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<...>.Inner<...>} 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]