diff --git a/libs/entitlement/tools/jdk-api-extractor/src/main/java/org/elasticsearch/entitlement/tools/jdkapi/JdkApiExtractor.java b/libs/entitlement/tools/jdk-api-extractor/src/main/java/org/elasticsearch/entitlement/tools/jdkapi/JdkApiExtractor.java index 1ca6b66602188..b4f5e4dd1a636 100644 --- a/libs/entitlement/tools/jdk-api-extractor/src/main/java/org/elasticsearch/entitlement/tools/jdkapi/JdkApiExtractor.java +++ b/libs/entitlement/tools/jdk-api-extractor/src/main/java/org/elasticsearch/entitlement/tools/jdkapi/JdkApiExtractor.java @@ -46,13 +46,13 @@ public class JdkApiExtractor { // exclude both final and non-final variants of these private static final Set EXCLUDES = Set.of( new AccessibleMethod("toString", "()Ljava/lang/String;", true, false, false), + new AccessibleMethod("toString", "()Ljava/lang/String;", true, true, false), new AccessibleMethod("hashCode", "()I", true, false, false), + new AccessibleMethod("hashCode", "()I", true, true, false), new AccessibleMethod("equals", "(Ljava/lang/Object;)Z", true, false, false), - new AccessibleMethod("close", "()V", true, false, false), - new AccessibleMethod("toString", "()Ljava/lang/String;", true, true, false), - new AccessibleMethod("hashCode", "()I", true, false, true), new AccessibleMethod("equals", "(Ljava/lang/Object;)Z", true, true, false), - new AccessibleMethod("close", "()V", true, false, true) + new AccessibleMethod("close", "()V", true, false, false), + new AccessibleMethod("close", "()V", true, true, false) ); private static String DEPRECATIONS_ONLY = "--deprecations-only"; @@ -66,7 +66,7 @@ public static void main(String[] args) throws IOException { final Map moduleNameByClass = new HashMap<>(); final Map> accessibleImplementationsByClass = new TreeMap<>(ModuleClass.COMPARATOR); - final Map> accessibleForOverridesByClass = new TreeMap<>(ModuleClass.COMPARATOR); + final Map> inheritableAccessByClass = new TreeMap<>(ModuleClass.COMPARATOR); final Map> deprecationsByClass = new TreeMap<>(ModuleClass.COMPARATOR); final Map> exportsByModule = Utils.findModuleExports(); @@ -84,7 +84,7 @@ public static void main(String[] args) throws IOException { moduleNameByClass, exportsByModule, accessibleImplementationsByClass, - accessibleForOverridesByClass, + inheritableAccessByClass, deprecationsByClass ); Predicate modulePredicate = Utils.DEFAULT_MODULE_PREDICATE.or( @@ -206,11 +206,11 @@ static class AccessibleClassVisitor extends ClassVisitor { private final Map moduleNameByClass; private final Map> exportsByModule; private final Map> accessibleImplementationsByClass; - private final Map> accessibleForOverridesByClass; + private final Map> inheritableAccessByClass; private final Map> deprecationsByClass; private Set accessibleImplementations; - private Set accessibleForOverrides; + private Set inheritableAccess; private Set deprecations; private ModuleClass moduleClass; @@ -223,14 +223,14 @@ static class AccessibleClassVisitor extends ClassVisitor { Map moduleNameByClass, Map> exportsByModule, Map> accessibleImplementationsByClass, - Map> accessibleForOverridesByClass, + Map> inheritableAccessByClass, Map> deprecationsByClass ) { super(ASM9); this.moduleNameByClass = moduleNameByClass; this.exportsByModule = exportsByModule; this.accessibleImplementationsByClass = accessibleImplementationsByClass; - this.accessibleForOverridesByClass = accessibleForOverridesByClass; + this.inheritableAccessByClass = inheritableAccessByClass; this.deprecationsByClass = deprecationsByClass; } @@ -240,13 +240,13 @@ private static Set newSortedSet() { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { - final Set currentAccessibleForOverrides = newSortedSet(); + final Set currentInheritedAccess = newSortedSet(); if (superName != null) { var superModuleClass = getModuleClass(superName); if (accessibleImplementationsByClass.containsKey(superModuleClass) == false) { visitSuperClass(superName); } - currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(superModuleClass, emptySet())); + currentInheritedAccess.addAll(inheritableAccessByClass.getOrDefault(superModuleClass, emptySet())); } if (interfaces != null && interfaces.length > 0) { for (var interfaceName : interfaces) { @@ -254,7 +254,7 @@ public void visit(int version, int access, String name, String signature, String if (accessibleImplementationsByClass.containsKey(interfaceModuleClass) == false) { visitInterface(interfaceName); } - currentAccessibleForOverrides.addAll(accessibleForOverridesByClass.getOrDefault(interfaceModuleClass, emptySet())); + currentInheritedAccess.addAll(inheritableAccessByClass.getOrDefault(interfaceModuleClass, emptySet())); } } // only initialize local state AFTER visiting all dependencies above! @@ -264,7 +264,7 @@ public void visit(int version, int access, String name, String signature, String this.isPublicClass = (access & ACC_PUBLIC) != 0; this.isFinalClass = (access & ACC_FINAL) != 0; this.isDeprecatedClass = (access & ACC_DEPRECATED) != 0; - this.accessibleForOverrides = currentAccessibleForOverrides; + this.inheritableAccess = currentInheritedAccess; this.accessibleImplementations = newSortedSet(); this.deprecations = newSortedSet(); } @@ -289,7 +289,7 @@ private Set getModuleExports(String module) { public void visitEnd() { super.visitEnd(); if (accessibleImplementationsByClass.put(moduleClass, unmodifiableSet(accessibleImplementations)) != null - || accessibleForOverridesByClass.put(moduleClass, unmodifiableSet(accessibleForOverrides)) != null + || inheritableAccessByClass.put(moduleClass, unmodifiableSet(inheritableAccess)) != null || deprecationsByClass.put(moduleClass, unmodifiableSet(deprecations)) != null) { throw new IllegalStateException("Class " + moduleClass.clazz + " was already visited!"); } @@ -337,18 +337,21 @@ public final MethodVisitor visitMethod(int access, String name, String descripto var method = new AccessibleMethod(name, descriptor, isPublic, isFinal, isStatic); if (isPublicClass && isExported && EXCLUDES.contains(method) == false) { - // class is public and exported, for final classes skip non-public methods + // class is public and exported, to be accessible outside the JDK the method must be either: + // - public or + // - protected if not a final class if (isPublic || isFinalClass == false) { accessibleImplementations.add(method); - // if not static, the method is accessible for overrides - if (isStatic == false) { - accessibleForOverrides.add(method); + // if public and not static, the method can be accessible on non-public and non-exported subclasses, + // but skip constructors + if (isPublic && isStatic == false && name.equals("") == false) { + inheritableAccess.add(method); } if (isDeprecatedClass || isDeprecated) { deprecations.add(method); } } - } else if (accessibleForOverrides.contains(method)) { + } else if (inheritableAccess.contains(method)) { accessibleImplementations.add(method); if (isDeprecatedClass || isDeprecated) { deprecations.add(method);