@Override public String toString() {
String s = lookupClass().getSimpleName();
String lstr = lookupString(); int sl = lstr.indexOf('/'); if (sl >= 0) s += lstr.substring(sl);
ClassLoader cld = lookupClass().getClassLoader(); if (cld != THIS_LOADER) s += "/loader#"+numberOf(cld); return s;
}
/** Predict the success or failure of accessing this method. */ publicboolean willAccess(Method m) { Class<?> c1 = lookupClass(); Class<?> c2 = m.getDeclaringClass();
Module m1 = c1.getModule();
Module m2 = c2.getModule();
Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1; // unconditional has access to all public types/members of types that is in a package // are unconditionally exported if ((lookupModes & UNCONDITIONAL) != 0) { return m2.isExported(c2.getPackageName())
&& Modifier.isPublic(c2.getModifiers())
&& Modifier.isPublic(m.getModifiers());
}
// c1 and c2 are in different module if (m1 != m2 || m0 != m2) { return (lookupModes & PUBLIC) != 0
&& isModuleAccessible(c2)
&& Modifier.isPublic(c2.getModifiers())
&& Modifier.isPublic(m.getModifiers());
}
assert(m1 == m2 && prevLookupClass == null);
if (!willAccessClass(c2, false)) returnfalse;
LookupCase lc = this.in(c2); int modes1 = lc.lookupModes(); int modes2 = fixMods(m.getModifiers()); // allow private lookup on nestmates. Otherwise, privacy is strictly enforced if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
modes1 &= ~PRIVATE;
} // protected access is sometimes allowed if ((modes2 & PROTECTED) != 0) { int prev = modes2;
modes2 |= PACKAGE; // it acts like a package method also if ((lookupModes() & PROTECTED) != 0 &&
c2.isAssignableFrom(c1))
modes2 |= PUBLIC; // from a subclass, it acts like a public method also
} if (verbosity >= 2)
System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0); return (modes2 & modes1) != 0;
}
/** Predict the success or failure of accessing this class. */ publicboolean willAccessClass(Class<?> c2, boolean load) { Class<?> c1 = lookupClass(); if (load && c2.getClassLoader() != null) { if (c1.getClassLoader() == null) { // not visible returnfalse;
}
}
Module m1 = c1.getModule();
Module m2 = c2.getModule();
Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1; // unconditional has access to all public types that is in an unconditionally exported package if ((lookupModes & UNCONDITIONAL) != 0) { return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());
} // c1 and c2 are in different module if (m1 != m2 || m0 != m2) { return (lookupModes & PUBLIC) != 0
&& isModuleAccessible(c2)
&& Modifier.isPublic(c2.getModifiers());
}
assert(m1 == m2 && prevLookupClass == null);
LookupCase lc = this.in(c2); int modes1 = lc.lookupModes(); boolean r = false; if (modes1 == 0) {
r = false;
} else { if (Modifier.isPublic(c2.getModifiers())) { if ((modes1 & MODULE) != 0)
r = true; elseif ((modes1 & PUBLIC) != 0)
r = m1.isExported(c2.getPackageName());
} else { if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
r = true;
}
} if (verbosity >= 2) {
System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
} return r;
}
}
privatefinal TreeSet<LookupCase> CASES = new TreeSet<>(); privatefinal TreeMap<LookupCase,TreeSet<LookupCase>> CASE_EDGES = new TreeMap<>(); privatefinal ArrayList<ClassLoader> LOADERS = new ArrayList<>(); privatefinal ClassLoader THIS_LOADER = this.getClass().getClassLoader();
{ if (THIS_LOADER != null) LOADERS.add(THIS_LOADER); } // #1
private LookupCase lookupCase(String name) { for (LookupCase lc : CASES) { if (lc.toString().equals(name)) return lc;
} thrownew AssertionError(name);
}
privateint numberOf(ClassLoader cl) { if (cl == null) return0; int i = LOADERS.indexOf(cl); if (i < 0) {
i = LOADERS.size();
LOADERS.add(cl);
} return i+1;
}
privatevoid addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {
TreeSet<LookupCase> edges = CASE_EDGES.get(l2); if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>()); if (edges.add(l1)) { Class<?> c1 = l1.lookupClass(); assert(l2.lookupClass() == c2); // [A1] int m1 = l1.lookupModes(); int m2 = l2.lookupModes(); assert((m1 | m2) == m1); // [A2] (no elevation of access)
LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess); if (!expect.equals(l2))
System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
assertEquals(l2, expect);
}
}
privatevoid makeCases(Lookup[] originalLookups) { // make initial set of lookup test cases
CASES.clear(); LOADERS.clear(); CASE_EDGES.clear();
ArrayList<Class<?>> classes = new ArrayList<>(); for (Lookup l : originalLookups) {
CASES.add(new LookupCase(l));
classes.remove(l.lookupClass()); // no dups please
classes.add(l.lookupClass());
}
System.out.println("loaders = "+LOADERS); int rounds = 0; for (int lastCount = -1; lastCount != CASES.size(); ) {
lastCount = CASES.size(); // if CASES grow in the loop we go round again for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) { for (int mode : ACCESS_CASES) {
LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));
addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);
CASES.add(lc2);
} for (Class<?> c2 : classes) {
LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
addLookupEdge(lc1, c2, lc2, 0);
CASES.add(lc2);
}
}
rounds++;
}
System.out.println("filled in "+CASES.size()+" cases from "+originalLookups.length+" original cases in "+rounds+" rounds"); if (false) {
System.out.println("CASES: {"); for (LookupCase lc : CASES) {
System.out.println(lc);
Set<LookupCase> edges = CASE_EDGES.get(lc); if (edges != null) for (LookupCase prev : edges) {
System.out.println("\t"+prev);
}
}
System.out.println("}");
}
}
@Test publicvoid test() {
makeCases(lookups()); if (verbosity > 0) {
verbosity += 9;
Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
testOneAccess(lookupCase("AccessControlTest/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");
verbosity -= 9;
}
Set<Class<?>> targetClassesDone = new HashSet<>(); for (LookupCase targetCase : CASES) { Class<?> targetClass = targetCase.lookupClass(); if (!targetClassesDone.add(targetClass)) continue; // already saw this one
String targetPlace = placeName(targetClass); if (targetPlace == null) continue; // Object, String, not a target for (int targetAccess : ACCESS_CASES) { if (targetAccess == MODULE || targetAccess == UNCONDITIONAL) continue;
MethodType methodType = methodType(void.class);
Method method = targetMethod(targetClass, targetAccess, methodType); // Try to access target method from various contexts. for (LookupCase sourceCase : CASES) {
testOneAccess(sourceCase, method, "findClass");
testOneAccess(sourceCase, method, "accessClass");
testOneAccess(sourceCase, method, "find");
testOneAccess(sourceCase, method, "unreflect");
}
}
}
System.out.println("tested "+testCount+" access scenarios; "+testCountFails+" accesses were denied");
}
// This guy tests access from outside the package: /* packagetest.java.lang.invoke.AccessControlTest_subpkg; publicclassAcquaintance_remote{ publicstaticLookup[]lookups(){... } ... }
*/
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-10-11)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.