daniellansun commented on code in PR #2845:
URL: https://github.com/apache/groovy/pull/2845#discussion_r3889440631


##########
src/main/java/org/codehaus/groovy/classgen/asm/BytecodeHelper.java:
##########
@@ -466,12 +467,41 @@ private static void writeGenericsBoundType(StringBuilder 
ret, ClassNode printTyp
             ret.append(printType.getGenericsTypes()[0].getName());
             ret.append(";");
         } else {
-            ret.append(getTypeDescription(printType, false));
-            addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
+            writeParameterizedClass(ret, printType);
             if (!isPrimitiveType(printType)) ret.append(";");
         }
     }
 
+    /**
+     * Writes a class type and its type arguments, using the JLS 4.5 nested 
form
+     * {@code LOuter&lt;...&gt;.Inner&lt;...&gt;} when an enclosing rare type 
is present.
+     */
+    private static void writeParameterizedClass(StringBuilder ret, ClassNode 
printType) {
+        ClassNode owner = printType.getOuterClassType();
+        if (owner != null) {
+            writeParameterizedClass(ret, owner);
+            ret.append('.');
+            ret.append(innerClassSimpleName(printType, owner));
+            addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
+            return;
+        }
+        ret.append(getTypeDescription(printType, false));
+        addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
+    }
+
+    private static String innerClassSimpleName(final ClassNode inner, final 
ClassNode owner) {
+        String innerName = inner.getName();
+        String ownerName = owner.getName();
+        if (innerName.startsWith(ownerName) && innerName.length() > 
ownerName.length()) {
+            char sep = innerName.charAt(ownerName.length());
+            if (sep == '.' || sep == '$') {
+                return innerName.substring(ownerName.length() + 
1).replace('$', '.');
+            }
+        }
+        int dot = Math.max(innerName.lastIndexOf('.'), 
innerName.lastIndexOf('$'));
+        return dot < 0 ? innerName : innerName.substring(dot + 1);

Review Comment:
   1. The two spellings are parser vs resolver, not two different nesting rules.
      A rare type is first recorded as `Outer.Inner`; after resolve,
      `ClassNode.getName()` follows the redirect and is `Outer$Inner`. Anonymous
      classes (`Foo` / `Foo$1`) already match the owner prefix, so they take the
      same path as `Outer$Inner` — there is no separate “plain name” case for
      `Foo$1`. Both `.` and `$` after a matching owner prefix are treated as
      nesting separators. We did not fold the parser onto `$` in this follow-up:
      classgen already sees the binary name via `getName()`, and accepting both
      separators is cheaper than changing how the parser names unresolved types.
   
   2. `Bar.X` with owner `Foo` is not a well-formed rare type (that would be
      `Foo$Bar$X`). The fallback is the last identifier only (`.X`), never extra
      qualification of the owner. It does **not** emit `Foo.Bar.X`.
   
      Tests assert the JVMS 4.7.9.1 strings: `Outer.Inner` and `Outer$Inner` 
both
      emit `L…Outer<…>.Inner<…>;`, `Foo$1` emits `L…Foo<…>.1;`, `Bar.X` emits
      `L…Foo<…>.X;`, and `Foo` / `FooBar` (prefix but not a nesting separator)
      emits `L…Foo<…>.FooBar;`. Nested form is used only when the enclosing type
      is parameterized; a raw enclosing type keeps the binary name
      (`Outer$Inner<…>`).
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to