daniellansun commented on code in PR #3227:
URL: https://github.com/apache/groovy/pull/3227#discussion_r4550638414
##########
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java:
##########
@@ -791,6 +792,141 @@ private void ambiguousClass(final ClassNode type, final
ClassNode iType, final S
}
}
+ /**
+ * Binds a simple name from on-demand imports.
+ * <p>
+ * A source import shadows every default import, including implicit
+ * {@code java.lang.*} and star imports added by a compilation customizer.
+ * Source static-import-on-demand, source type-import-on-demand, and
+ * packages expanded from a source {@code import module} are one sequence
+ * in source order. The first accessible type in that sequence is bound.
+ * A later import does not create an ambiguity, and an inaccessible type
+ * is skipped so it does not hide a later accessible type.
+ * <p>
+ * Customizer on-demand imports have no source location. They are tried
+ * after every source on-demand import and before the language defaults,
+ * so a configured star can still supply {@code Date} or {@code String}
+ * without tying with {@code java.lang} or {@code java.util}. Within that
+ * tier, static stars precede package stars because the two are stored
+ * separately. An explicit single-type import is resolved earlier and
+ * therefore wins over every on-demand import.
+ *
+ * @return {@code true} if the name was bound
+ */
+ private boolean resolveFromOnDemandImports(final ClassNode type, final
String name, final ModuleNode module) {
+ OnDemandImports order = onDemandImports(module);
+ for (ImportNode importNode : order.declared) {
+ if (bindOnDemand(type, name, importNode)) return true;
+ }
+ for (ImportNode importNode : order.customizer) {
+ if (bindOnDemand(type, name, importNode)) return true;
+ }
+ return false;
+ }
+
+ private boolean bindOnDemand(final ClassNode type, final String name,
final ImportNode importNode) {
+ ClassNode outer = importNode.getType();
+ if (outer != null) {
+ ClassNode tmp = new ConstructedNestedClass(outer, name);
+ if (resolve(tmp, false, false, true) &&
Modifier.isStatic(tmp.getModifiers())
Review Comment:
`outer != null` covers both `import static Type.*` and `import Type.*` when
that name resolves to a class (`visitClass` then stores the class on the
import). Both forms look up a member type, and only a static member type is in
scope, the same rule as a single static import. A non-static inner class is not
part of that star. A member type of an interface is implicitly static, so
`Map.Entry` still matches.
##########
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java:
##########
@@ -791,6 +792,141 @@ private void ambiguousClass(final ClassNode type, final
ClassNode iType, final S
}
}
+ /**
+ * Binds a simple name from on-demand imports.
+ * <p>
+ * A source import shadows every default import, including implicit
+ * {@code java.lang.*} and star imports added by a compilation customizer.
+ * Source static-import-on-demand, source type-import-on-demand, and
+ * packages expanded from a source {@code import module} are one sequence
+ * in source order. The first accessible type in that sequence is bound.
+ * A later import does not create an ambiguity, and an inaccessible type
+ * is skipped so it does not hide a later accessible type.
+ * <p>
+ * Customizer on-demand imports have no source location. They are tried
+ * after every source on-demand import and before the language defaults,
+ * so a configured star can still supply {@code Date} or {@code String}
+ * without tying with {@code java.lang} or {@code java.util}. Within that
+ * tier, static stars precede package stars because the two are stored
+ * separately. An explicit single-type import is resolved earlier and
+ * therefore wins over every on-demand import.
+ *
+ * @return {@code true} if the name was bound
+ */
+ private boolean resolveFromOnDemandImports(final ClassNode type, final
String name, final ModuleNode module) {
+ OnDemandImports order = onDemandImports(module);
+ for (ImportNode importNode : order.declared) {
+ if (bindOnDemand(type, name, importNode)) return true;
+ }
+ for (ImportNode importNode : order.customizer) {
+ if (bindOnDemand(type, name, importNode)) return true;
+ }
+ return false;
+ }
+
+ private boolean bindOnDemand(final ClassNode type, final String name,
final ImportNode importNode) {
+ ClassNode outer = importNode.getType();
+ if (outer != null) {
+ ClassNode tmp = new ConstructedNestedClass(outer, name);
+ if (resolve(tmp, false, false, true) &&
Modifier.isStatic(tmp.getModifiers())
+ && isStarImportAccessible(tmp, outer)) {
+ type.setRedirect(tmp.redirect());
+ return true;
+ }
+ return false;
+ }
+ String packageName = importNode.getPackageName();
+ if (packageName == null) return false;
+ ClassNode tmp = new ConstructedClassWithPackage(packageName, name);
+ if (resolve(tmp, false, false, true) && isStarImportAccessible(tmp,
null)) {
+ type.setRedirect(tmp.redirect());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Source on-demand imports in source order, then customizer on-demand
+ * imports. Computed once per module; import lists are fixed before
resolve.
+ */
+ private static OnDemandImports onDemandImports(final ModuleNode module) {
+ OnDemandImports cached = module.getNodeMetaData(OnDemandImports.class);
+ if (cached != null) return cached;
+
+ List<PositionedImport> positioned = new ArrayList<>();
+ int sequence = 0;
+ for (ImportNode node : module.getStaticStarImports().values()) {
+ if (node.getLineNumber() > 0) {
+ positioned.add(new PositionedImport(node.getLineNumber(),
node.getColumnNumber(), sequence++, node));
+ }
+ }
+ for (ImportNode node : module.getStarImports()) {
+ if (node.getLineNumber() > 0) {
+ positioned.add(new PositionedImport(node.getLineNumber(),
node.getColumnNumber(), sequence++, node));
+ }
+ }
+ List<ImportNode> pendingModule = new ArrayList<>();
+ for (ImportNode node : module.getModuleStarImports()) {
+ pendingModule.add(node);
+ if (node.getLineNumber() > 0) {
+ int line = node.getLineNumber();
+ int column = node.getColumnNumber();
+ for (ImportNode part : pendingModule) {
+ positioned.add(new PositionedImport(line, column,
sequence++, part));
+ }
+ pendingModule.clear();
+ }
+ }
+ positioned.sort((a, b) -> {
+ int byLine = Integer.compare(a.line, b.line);
+ if (byLine != 0) return byLine;
+ int byColumn = Integer.compare(a.column, b.column);
+ if (byColumn != 0) return byColumn;
+ return Integer.compare(a.sequence, b.sequence);
+ });
+
+ List<ImportNode> declared = new ArrayList<>(positioned.size());
+ for (PositionedImport positionedImport : positioned) {
+ declared.add(positionedImport.node);
+ }
+ List<ImportNode> customizer = new ArrayList<>();
+ for (ImportNode node : module.getStaticStarImports().values()) {
+ if (node.getLineNumber() <= 0) customizer.add(node);
Review Comment:
`List.sort` is stable, so the sequence key was redundant and is gone. A
single list of the raw nodes would not preserve `import module` order: that
statement expands to one star per exported package, and only the last of those
nodes carries the statement's line and column. The earlier packages have no
position of their own and must inherit that position, in export order, among
the source imports. Sorting by the stored line numbers and then moving every
non-positive line to the end would put those packages in the customizer tier.
Customizer imports (line `<= 0`) stay after every source on-demand import. The
lists are small, so they are no longer stored as module metadata.
##########
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java:
##########
@@ -1195,6 +1289,72 @@ protected Expression transformPropertyExpression(final
PropertyExpression pe) {
return isTopLevelProperty ? correctClassClassChain(xe) : xe;
}
+ /**
+ * Whether a type found through a star import, a module import, or a
default
+ * import is accessible from {@link #currentClass}.
+ * <p>
+ * Public types are visible. A package-private type stays visible in its
own
+ * package, a protected member type stays visible to subclasses of its
enclosing
+ * type, and a private member type stays visible inside the enclosing
nest. A
+ * public member of an inaccessible enclosing type is not visible, so a
star
+ * import cannot bind it.
+ * <p>
+ * Single-type imports are not filtered. An explicit {@code import
p.Hidden}
+ * or a fully-qualified name still resolves, matching long-standing Groovy
+ * behaviour, and {@code Class.forName} is unaffected. Static field imports
+ * already apply an access check ({@link StaticImportVisitor}, since
Groovy 5).
+ *
+ * @param type a resolved candidate
+ * @param knownOuter the type named by {@code import some.Type.*} when
+ * {@code type} is a binary member type that does not
expose
+ * {@link ClassNode#getOuterClass()}
+ * @return {@code true} if the star import may bind {@code type}
+ */
+ private boolean isStarImportAccessible(final ClassNode type, final
ClassNode knownOuter) {
+ ClassNode outer = type.getOuterClass();
+ if (outer == null) {
+ outer = knownOuter;
Review Comment:
No. It is the type named by `import Type.*` or `import static Type.*`. A
package star, a default import, and the recursive walk pass null. If
`getOuterClass()` is already set, that value is used and `knownOuter` is not.
##########
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java:
##########
@@ -1195,6 +1289,72 @@ protected Expression transformPropertyExpression(final
PropertyExpression pe) {
return isTopLevelProperty ? correctClassClassChain(xe) : xe;
}
+ /**
+ * Whether a type found through a star import, a module import, or a
default
+ * import is accessible from {@link #currentClass}.
+ * <p>
+ * Public types are visible. A package-private type stays visible in its
own
+ * package, a protected member type stays visible to subclasses of its
enclosing
+ * type, and a private member type stays visible inside the enclosing
nest. A
+ * public member of an inaccessible enclosing type is not visible, so a
star
+ * import cannot bind it.
+ * <p>
+ * Single-type imports are not filtered. An explicit {@code import
p.Hidden}
+ * or a fully-qualified name still resolves, matching long-standing Groovy
+ * behaviour, and {@code Class.forName} is unaffected. Static field imports
+ * already apply an access check ({@link StaticImportVisitor}, since
Groovy 5).
+ *
+ * @param type a resolved candidate
+ * @param knownOuter the type named by {@code import some.Type.*} when
+ * {@code type} is a binary member type that does not
expose
+ * {@link ClassNode#getOuterClass()}
+ * @return {@code true} if the star import may bind {@code type}
+ */
+ private boolean isStarImportAccessible(final ClassNode type, final
ClassNode knownOuter) {
+ ClassNode outer = type.getOuterClass();
+ if (outer == null) {
+ outer = knownOuter;
+ }
+ if (outer == null) {
+ outer = binaryEnclosing(type);
Review Comment:
A member type read from bytecode is an ordinary class node, not an
`InnerClassNode`. `InnerClassNode` is what the parser builds for a source
nested class, and only that sets `getOuterClass()`. Turning every loaded member
type into an `InnerClassNode` would change enclosing-instance handling
everywhere `getOuterClass()` is used. `binaryEnclosing` reads the enclosing
binary name for this check only. For `import Type.*` the caller already passes
that type as `knownOuter`.
##########
src/main/java/org/codehaus/groovy/control/ResolveVisitor.java:
##########
@@ -1195,6 +1289,72 @@ protected Expression transformPropertyExpression(final
PropertyExpression pe) {
return isTopLevelProperty ? correctClassClassChain(xe) : xe;
}
+ /**
+ * Whether a type found through a star import, a module import, or a
default
+ * import is accessible from {@link #currentClass}.
+ * <p>
+ * Public types are visible. A package-private type stays visible in its
own
+ * package, a protected member type stays visible to subclasses of its
enclosing
+ * type, and a private member type stays visible inside the enclosing
nest. A
+ * public member of an inaccessible enclosing type is not visible, so a
star
+ * import cannot bind it.
+ * <p>
+ * Single-type imports are not filtered. An explicit {@code import
p.Hidden}
+ * or a fully-qualified name still resolves, matching long-standing Groovy
+ * behaviour, and {@code Class.forName} is unaffected. Static field imports
+ * already apply an access check ({@link StaticImportVisitor}, since
Groovy 5).
+ *
+ * @param type a resolved candidate
+ * @param knownOuter the type named by {@code import some.Type.*} when
+ * {@code type} is a binary member type that does not
expose
+ * {@link ClassNode#getOuterClass()}
+ * @return {@code true} if the star import may bind {@code type}
+ */
+ private boolean isStarImportAccessible(final ClassNode type, final
ClassNode knownOuter) {
+ ClassNode outer = type.getOuterClass();
+ if (outer == null) {
+ outer = knownOuter;
+ }
+ if (outer == null) {
+ outer = binaryEnclosing(type);
+ }
+ if (outer != null && outer != type && !isStarImportAccessible(outer,
null)) {
+ return false;
Review Comment:
`isResolved()` does not have to be true. `getOuterClass()` is non-null only
for a source `InnerClassNode`, and that enclosing node is the class from the
parse, not a `ClassHelper.make` placeholder (those are created public). A
binary enclosing type is the resolved import type or the class the resolver
returned, so its flags come from the class file. A decompiled class also
reports `isResolved() == false`, but its modifiers are the `InnerClasses` flags.
There was a related hole: `@PackageScope` runs later in semantic analysis
than resolve, so a source class is still synthetic public when the modifier is
read. A synthetic-public class annotated `@PackageScope` (no argument, or an
argument that includes `CLASS`) is now treated as package-private. Bytecode
already has the flag removed. Same-package access is unchanged, and
`@PackageScope(FIELDS)` does not hide the class.
##########
src/test/groovy/bugs/Groovy11916.groovy:
##########
@@ -0,0 +1,727 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import groovy.lang.GroovyClassLoader
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.Phases
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.Test
+
+import java.lang.reflect.Modifier
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+
+/**
+ * Star imports, module imports, and default imports resolve only accessible
+ * types. A source import shadows those defaults, including {@code java.lang}.
+ * Two source star imports bind the first accessible type in source order.
+ * An explicit single-type import, a fully qualified name, and
+ * {@code Class.forName} still name a type that is not public.
+ */
+final class Groovy11916 {
+
+ private static GroovyClassLoader types
+
+ @BeforeAll
+ static void compilePackagePrivateTypes() {
+ types = compile(Groovy11916.classLoader, [
+ 'p/Types': '''
+ package p
+
+ @groovy.transform.PackageScope
+ class Hidden {}
+
+ class Visible {}
+
+ @groovy.transform.PackageScope
+ class Outer {
+ public static class Inner {}
+ }
+
+ class Protector {
+ protected static class Secret {}
+ }
+
+ class Priv {
+ private static class Secret {}
+ public static class Shown {}
+ }
+
+ @groovy.transform.PackageScope
+ class Collision {}
+ '''
+ ])
+ assert !Modifier.isPublic(types.loadClass('p.Hidden').modifiers)
+ assert !Modifier.isPublic(types.loadClass('p.Collision').modifiers)
+ assert Modifier.isPublic(types.loadClass('p.Visible').modifiers)
+ assert !Modifier.isPublic(types.loadClass('p.Outer').modifiers)
+ assert Modifier.isPublic(types.loadClass('p.Outer$Inner').modifiers)
+ assert
Modifier.isProtected(types.loadClass('p.Protector$Secret').modifiers)
+ assert Modifier.isPrivate(types.loadClass('p.Priv$Secret').modifiers)
Review Comment:
Those asserts lock the fixture the later tests assume: `Hidden` and
`Collision` are not public, `Visible` is, `Outer` is not while `Inner` is,
`Protector.Secret` is protected, and `Priv.Secret` is private. If
`@PackageScope` failed to clear public, or an inner class had the wrong flag,
the later tests would pass or fail for the wrong reason.
##########
src/test/groovy/bugs/Groovy11916.groovy:
##########
@@ -0,0 +1,727 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package bugs
+
+import groovy.lang.GroovyClassLoader
+import org.codehaus.groovy.control.CompilationUnit
+import org.codehaus.groovy.control.CompilerConfiguration
+import org.codehaus.groovy.control.Phases
+import org.codehaus.groovy.control.customizers.ImportCustomizer
+import org.junit.jupiter.api.BeforeAll
+import org.junit.jupiter.api.Test
+
+import java.lang.reflect.Modifier
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+
+/**
+ * Star imports, module imports, and default imports resolve only accessible
+ * types. A source import shadows those defaults, including {@code java.lang}.
+ * Two source star imports bind the first accessible type in source order.
+ * An explicit single-type import, a fully qualified name, and
+ * {@code Class.forName} still name a type that is not public.
+ */
+final class Groovy11916 {
+
+ private static GroovyClassLoader types
+
+ @BeforeAll
+ static void compilePackagePrivateTypes() {
+ types = compile(Groovy11916.classLoader, [
+ 'p/Types': '''
+ package p
+
+ @groovy.transform.PackageScope
+ class Hidden {}
+
+ class Visible {}
+
+ @groovy.transform.PackageScope
+ class Outer {
+ public static class Inner {}
+ }
+
+ class Protector {
+ protected static class Secret {}
+ }
+
+ class Priv {
+ private static class Secret {}
+ public static class Shown {}
+ }
+
+ @groovy.transform.PackageScope
+ class Collision {}
+ '''
+ ])
+ assert !Modifier.isPublic(types.loadClass('p.Hidden').modifiers)
+ assert !Modifier.isPublic(types.loadClass('p.Collision').modifiers)
+ assert Modifier.isPublic(types.loadClass('p.Visible').modifiers)
+ assert !Modifier.isPublic(types.loadClass('p.Outer').modifiers)
+ assert Modifier.isPublic(types.loadClass('p.Outer$Inner').modifiers)
+ assert
Modifier.isProtected(types.loadClass('p.Protector$Secret').modifiers)
+ assert Modifier.isPrivate(types.loadClass('p.Priv$Secret').modifiers)
+ }
+
+ private static GroovyClassLoader compile(ClassLoader parent, Map<String,
String> sources, CompilerConfiguration config = null) {
+ def loader = new GroovyClassLoader(parent, config)
+ def unit = new CompilationUnit(config, null, loader)
+ sources.each { name, text -> unit.addSource(name + '.groovy', text) }
+ unit.compile(Phases.CLASS_GENERATION)
+ unit.classes.sort { it.name }.each { groovyClass ->
+ loader.defineClass(groovyClass.name, groovyClass.bytes)
+ }
+ loader
+ }
+
+ private static void putType(Map<String, String> sources, String pkg,
String simpleName, String state) {
+ sources[pkg + '/Marker'] = "package ${pkg}\nclass Marker {}"
+ if (state == 'p') {
+ sources[pkg + '/' + simpleName] = "package ${pkg}\nclass
${simpleName} {}"
+ } else if (state == 'k') {
+ sources[pkg + '/' + simpleName] = "package
${pkg}\[email protected]\nclass ${simpleName} {}"
+ }
+ }
+
+ private static String useSource(String pkg, String imports, String
simpleName) {
+ """\
+package ${pkg}
+${imports}
+class Use {
+ ${simpleName} field
+ static Class which() { ${simpleName} }
+}
+"""
+ }
+
+ /** First public type in source order. {@code p} public, {@code k}
package-private, {@code m} missing. */
+ private static String firstStar(String a, String b, String c) {
+ if (a == 'p') return 'pa.X'
+ if (b == 'p') return 'pb.X'
+ if (c == 'p') return 'pc.X'
+ return 'unresolved'
+ }
+
+ private static String classifyUse(Map<String, String> sources, String
className, CompilerConfiguration config = null) {
+ // Dependency types are compiled first so package-private modifiers are
+ // already on the bytecode when the importing compilation unit is
resolved.
+ def deps = sources.findAll { name, text -> !name.endsWith('/Use') }
+ def use = sources.findAll { name, text -> name.endsWith('/Use') }
+ try {
+ def parent = deps ? compile(Groovy11916.classLoader, deps) :
Groovy11916.classLoader
+ def loader = compile(parent, use, config)
+ return
loader.loadClass(className).getDeclaredMethod('which').invoke(null).name
+ } catch (Exception e) {
+ String text = ''
+ for (Throwable t = e; t != null; t = t.cause) {
+ if (t.message != null) text += t.message + '\n'
+ }
+ if (text.contains('ambiguous')) return 'ambiguous'
+ if (text.contains('unable to resolve')) return 'unresolved'
+ if (text.contains('not public')) return 'not-public'
+ def snippet = text.readLines().collect { it.trim() }.findAll { it
}.take(4).join(' | ')
+ return 'other:' + (snippet ?: e.getClass().simpleName)
+ }
+ }
+
+ @Test
+ void testStarImportDoesNotResolvePackagePrivateJdkType() {
+ assert !Modifier.isPublic(Class.forName('java.util.TimSort').modifiers)
+
+ def err = shouldFail '''
+ import java.util.*
+ def seen = null instanceof TimSort
+ '''
+ assert err.message.contains('unable to resolve class TimSort')
+
+ // A bare name in a script is a dynamic property when it is not a type.
+ // It must not bind the inaccessible class (that binding is what throws
+ // IllegalAccessError for a later instanceof / checkcast).
+ err = shouldFail '''
+ import java.util.*
+ assert TimSort.name == 'java.util.TimSort'
+ '''
+ assert err instanceof MissingPropertyException
+ }
+
+ @Test
+ void testDefaultImportDoesNotResolvePackagePrivateJdkType() {
+ def err = shouldFail '''
+ def seen = null instanceof TimSort
+ '''
+ assert err.message.contains('unable to resolve class TimSort')
+ }
+
+ @Test
+ void testModuleImportDoesNotResolvePackagePrivateJdkType() {
+ def err = shouldFail '''
+ import module java.base
+ def seen = null instanceof TimSort
+ '''
+ assert err.message.contains('unable to resolve class TimSort')
+ }
+
+ @Test
+ void testStarImportStillResolvesPublicTypes() {
+ assertScript '''
+ import java.util.*
+ assert ArrayList.name == 'java.util.ArrayList'
+ assert List.name == 'java.util.List'
+ assert new ArrayList() instanceof List
+ '''
+ assertScript '''
+ import java.util.Map.*
+ Entry e = [a:1].entrySet().first()
+ assert e.key == 'a'
+ '''
+ assertScript '''
+ import static java.util.Map.*
+ Entry e = [a:1].entrySet().first()
+ assert e instanceof Entry
+ '''
+ assertScript '''
+ import module java.base
+ assert ArrayList.name == 'java.util.ArrayList'
+ assert LocalDate.name == 'java.time.LocalDate'
+ '''
+ }
+
+ @Test
+ void testExplicitReferenceToPackagePrivateTypeStillResolves() {
+ assertScript '''
+ import java.util.TimSort
+ assert TimSort.name == 'java.util.TimSort'
Review Comment:
`TimSort` is package-private, and this script does not perform an
access-checked linkage. Because the class is not public, the class expression
is not an `ldc` of `java.util.TimSort`; that linkage would throw
`IllegalAccessError`. It is loaded with `Class.forName`, and `.name` is
`Class.getName()`. `Class.forName` does not access-check the class, which is
also why the `Class.forName('java.util.TimSort')` assertion in the same test
runs. A non-null `instanceof`, a non-null cast, and `new` do throw
`IllegalAccessError`. `null instanceof TimSort` does not: the JVM does not
resolve the type for a null operand. The test is asserting the explicit-import
path, which still resolves, and the operation it runs is the class-handle path.
The star-import filter is what stops a simple name from being compiled into a
linkage that fails.
--
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]