< prev index next >

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Print this page

        

*** 123,149 **** return new Lookup(caller); } /** * Returns a {@link Lookup lookup object} which is trusted minimally. ! * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes. * It can only be used to create method handles to public members of * public classes in packages that are exported unconditionally. * <p> * As a matter of pure convention, the {@linkplain Lookup#lookupClass() lookup class} * of this lookup object will be {@link java.lang.Object}. * * @apiNote The use of Object is conventional, and because the lookup modes are * limited, there is no special access provided to the internals of Object, its package ! * or its module. Consequently, the lookup context of this lookup object will be the ! * bootstrap class loader, which means it cannot find user classes. * * <p style="font-size:smaller;"> * <em>Discussion:</em> * The lookup class can be changed to any other class {@code C} using an expression of the form * {@link Lookup#in publicLookup().in(C.class)}. - * but may change the lookup context by virtue of changing the class loader. * A public lookup object is always subject to * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>. * Also, it cannot access * <a href="MethodHandles.Lookup.html#callsens">caller sensitive methods</a>. * @return a lookup object which is trusted minimally --- 123,149 ---- return new Lookup(caller); } /** * Returns a {@link Lookup lookup object} which is trusted minimally. ! * The lookup has the {@code UNCONDITIONAL} mode. * It can only be used to create method handles to public members of * public classes in packages that are exported unconditionally. * <p> * As a matter of pure convention, the {@linkplain Lookup#lookupClass() lookup class} * of this lookup object will be {@link java.lang.Object}. * * @apiNote The use of Object is conventional, and because the lookup modes are * limited, there is no special access provided to the internals of Object, its package ! * or its module. This public lookup object or other lookup object with ! * {@code UNCONDITIONAL} mode assumes readability. Consequently, the lookup class ! * is not used to determine the lookup context. * * <p style="font-size:smaller;"> * <em>Discussion:</em> * The lookup class can be changed to any other class {@code C} using an expression of the form * {@link Lookup#in publicLookup().in(C.class)}. * A public lookup object is always subject to * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>. * Also, it cannot access * <a href="MethodHandles.Lookup.html#callsens">caller sensitive methods</a>. * @return a lookup object which is trusted minimally
*** 154,221 **** public static Lookup publicLookup() { return Lookup.PUBLIC_LOOKUP; } /** ! * Returns a {@link Lookup lookup object} with full capabilities to emulate all ! * supported bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc"> ! * private access</a>, on a target class. ! * This method checks that a caller, specified as a {@code Lookup} object, is allowed to ! * do <em>deep reflection</em> on the target class. If {@code m1} is the module containing ! * the {@link Lookup#lookupClass() lookup class}, and {@code m2} is the module containing ! * the target class, then this check ensures that * <ul> ! * <li>{@code m1} {@link Module#canRead reads} {@code m2}.</li> ! * <li>{@code m2} {@link Module#isOpen(String,Module) opens} the package containing ! * the target class to at least {@code m1}.</li> ! * <li>The lookup has the {@link Lookup#MODULE MODULE} lookup mode.</li> * </ul> * <p> ! * If there is a security manager, its {@code checkPermission} method is called to ! * check {@code ReflectPermission("suppressAccessChecks")}. ! * @apiNote The {@code MODULE} lookup mode serves to authenticate that the lookup object ! * was created by code in the caller module (or derived from a lookup object originally ! * created by the caller). A lookup object with the {@code MODULE} lookup mode can be ! * shared with trusted parties without giving away {@code PRIVATE} and {@code PACKAGE} ! * access to the caller. * @param targetClass the target class ! * @param lookup the caller lookup object * @return a lookup object for the target class, with private access ! * @throws IllegalArgumentException if {@code targetClass} is a primitve type or array class * @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null} - * @throws IllegalAccessException if the access check specified above fails * @throws SecurityException if denied by the security manager * @since 9 * @spec JPMS * @see Lookup#dropLookupMode */ ! public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(ACCESS_PERMISSION); if (targetClass.isPrimitive()) throw new IllegalArgumentException(targetClass + " is a primitive class"); if (targetClass.isArray()) throw new IllegalArgumentException(targetClass + " is an array class"); ! Module targetModule = targetClass.getModule(); ! Module callerModule = lookup.lookupClass().getModule(); if (!callerModule.canRead(targetModule)) throw new IllegalAccessException(callerModule + " does not read " + targetModule); if (targetModule.isNamed()) { String pn = targetClass.getPackageName(); assert !pn.isEmpty() : "unnamed package cannot be in named module"; if (!targetModule.isOpen(pn, callerModule)) throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule); } ! if ((lookup.lookupModes() & Lookup.MODULE) == 0) ! throw new IllegalAccessException("lookup does not have MODULE lookup mode"); if (!callerModule.isNamed() && targetModule.isNamed()) { IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger(); if (logger != null) { ! logger.logIfOpenedForIllegalAccess(lookup, targetClass); } } ! return new Lookup(targetClass); } /** * Performs an unchecked "crack" of a * <a href="MethodHandleInfo.html#directmh">direct method handle</a>. --- 154,263 ---- public static Lookup publicLookup() { return Lookup.PUBLIC_LOOKUP; } /** ! * Returns a {@link Lookup lookup} object on a target class to emulate all supported ! * bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc"> private access</a>. ! * The returned lookup object can provide access to classes in modules and packages, ! * and members of those classes, outside the normal rules of Java access control, ! * instead conforming to the more permissive rules for modular <em>deep reflection</em>. ! * <p> ! * A caller, specified as a {@code Lookup} object, in module {@code M1} is ! * allowed to do deep reflection on module {@code M2} and package of the target class ! * if and only if all of the following conditions are {@code true}: * <ul> ! * <li>If there is a security manager, its {@code checkPermission} method is ! * called to check {@code ReflectPermission("suppressAccessChecks")} and ! * that must return normally. ! * <li>The caller lookup object must have the {@link Lookup#MODULE MODULE} lookup mode. ! * (This is because otherwise there would be no way to ensure the original lookup ! * creator was a member of any particular module, and so any subsequent checks ! * for readability and qualified exports would become ineffective.) ! * <li>The caller lookup object must have {@link Lookup#PRIVATE PRIVATE} access. ! * (This is because an application intending to share intra-module access ! * using {@link Lookup#MODULE MODULE} alone will inadvertently also share ! * deep reflection to its own module.) ! * <li>The target class must be a proper class, not a primitive or array class. ! * (Thus, {@code M2} is well-defined.) ! * <li>If the caller module {@code M1} differs from ! * the target module {@code M2} then both of the following must be true: ! * <ul> ! * <li>{@code M1} {@link Module#canRead reads} {@code M2}.</li> ! * <li>{@code M2} {@link Module#isOpen(String,Module) opens} the package ! * containing the target class to at least {@code M1}.</li> ! * </ul> * </ul> * <p> ! * If any of the above checks is violated, this method fails with an ! * exception. ! * <p> ! * Otherwise, if {@code M1} and {@code M2} are the same module, this method ! * returns a {@code Lookup} on {@code targetClass} with full capabilities and ! * {@code null} previous lookup class. ! * <p> ! * Otherwise, {@code M1} and {@code M2} are two different modules. This method ! * returns a {@code Lookup} on {@code targetClass} that records ! * the lookup class of the caller as the new previous lookup class and ! * drops {@code MODULE} access from the full capabilities mode. ! * * @param targetClass the target class ! * @param caller the caller lookup object * @return a lookup object for the target class, with private access ! * @throws IllegalArgumentException if {@code targetClass} is a primitive type or array class * @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null} * @throws SecurityException if denied by the security manager + * @throws IllegalAccessException if any of the other access checks specified above fails * @since 9 * @spec JPMS * @see Lookup#dropLookupMode + * @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a> */ ! public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(ACCESS_PERMISSION); if (targetClass.isPrimitive()) throw new IllegalArgumentException(targetClass + " is a primitive class"); if (targetClass.isArray()) throw new IllegalArgumentException(targetClass + " is an array class"); ! // Ensure that we can reason accurately about private and module access. ! if ((caller.lookupModes() & Lookup.PRIVATE) == 0) ! throw new IllegalAccessException("caller does not have PRIVATE lookup mode"); ! if ((caller.lookupModes() & Lookup.MODULE) == 0) ! throw new IllegalAccessException("caller does not have MODULE lookup mode"); ! ! // previous lookup class is never set if it has MODULE access ! assert caller.previousLookupClass() == null; ! ! Class<?> callerClass = caller.lookupClass(); ! Module callerModule = callerClass.getModule(); // M1 ! Module targetModule = targetClass.getModule(); // M2 ! Class<?> newPreviousClass = null; ! int newModes = Lookup.FULL_POWER_MODES; ! ! if (targetModule != callerModule) { if (!callerModule.canRead(targetModule)) throw new IllegalAccessException(callerModule + " does not read " + targetModule); if (targetModule.isNamed()) { String pn = targetClass.getPackageName(); assert !pn.isEmpty() : "unnamed package cannot be in named module"; if (!targetModule.isOpen(pn, callerModule)) throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule); } ! ! // M2 != M1, set previous lookup class to M1 and drop MODULE access ! newPreviousClass = callerClass; ! newModes &= ~Lookup.MODULE; ! } ! if (!callerModule.isNamed() && targetModule.isNamed()) { IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger(); if (logger != null) { ! logger.logIfOpenedForIllegalAccess(caller, targetClass); } } ! return Lookup.newLookup(targetClass, newPreviousClass, newModes); } /** * Performs an unchecked "crack" of a * <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
*** 531,540 **** --- 573,1090 ---- * Each of these permissions is a consequence of the fact that a lookup object * with private access can be securely traced back to an originating class, * whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions * can be reliably determined and emulated by method handles. * + * <h2><a id="cross-module-lookup"></a>Cross-module lookups</h2> + * When a lookup class in one module {@code M1} accesses a class in another module + * {@code M2}, extra access checking is performed beyond the access mode bits. + * A {@code Lookup} with {@link #PUBLIC} mode and a lookup class in {@code M1} + * can access public types in {@code M2} when {@code M2} is readable to {@code M1} + * and when the type is in a package of {@code M2} that is exported to + * at least {@code M1}. + * <p> + * A {@code Lookup} on {@code C} can also <em>teleport</em> to a target class + * via {@link #in(Class) Lookup.in} and {@link MethodHandles#privateLookupIn(Class, Lookup) + * MethodHandles.privateLookupIn} methods. + * Teleporting across modules will always record the original lookup class as + * the <em>{@linkplain #previousLookupClass() previous lookup class}</em> + * and drops {@link Lookup#MODULE MODULE} access. + * If the target class is in the same module as the lookup class {@code C}, + * then the target class becomes the new lookup class + * and there is no change to the previous lookup class. + * If the target class is in a different module from {@code M1} ({@code C}'s module), + * {@code C} becomes the new previous lookup class + * and the target class becomes the new lookup class. + * In that case, if there was already a previous lookup class in {@code M0}, + * and it differs from {@code M1} and {@code M2}, then the resulting lookup + * drops all privileges. + * For example, + * <blockquote><pre> + * {@code + * Lookup lookup = MethodHandles.lookup(); // in class C + * Lookup lookup2 = lookup.in(D.class); + * MethodHandle mh = lookup2.findStatic(E.class, "m", MT); + * }</pre></blockquote> + * <p> + * The {@link #lookup()} factory method produces a {@code Lookup} object + * with {@code null} previous lookup class. + * {@link Lookup#in lookup.in(D.class)} transforms the {@code lookup} on class {@code C} + * to class {@code D} without elevation of privileges. + * If {@code C} and {@code D} are in the same module, + * {@code lookup2} records {@code D} as the new lookup class and keeps the + * same previous lookup class as the original {@code lookup}, or + * {@code null} if not present. + * <p> + * When a {@code Lookup} teleports from a class + * in one nest to another nest, {@code PRIVATE} access is dropped. + * When a {@code Lookup} teleports from a class in one package to + * another package, {@code PACKAGE} access is dropped. + * When a {@code Lookup} teleports from a class in one module to another module, + * {@code MODULE} access is dropped. + * Teleporting across modules drops the ability to access non-exported classes + * in both the module of the new lookup class and the module of the old lookup class + * and the resulting {@code Lookup} remains only {@code PUBLIC} access. + * A {@code Lookup} can teleport back and forth to a class in the module of + * the lookup class and the module of the previous class lookup. + * Teleporting across modules can only decrease access but cannot increase it. + * Teleporting to some third module drops all accesses. + * <p> + * In the above example, if {@code C} and {@code D} are in different modules, + * {@code lookup2} records {@code D} as its lookup class and + * {@code C} as its previous lookup class and {@code lookup2} has only + * {@code PUBLIC} access. {@code lookup2} can teleport to other class in + * {@code C}'s module and {@code D}'s module. + * If class {@code E} is in a third module, {@code lookup2.in(E.class)} creates + * a {@code Lookup} on {@code E} with no access and {@code lookup2}'s lookup + * class {@code D} is recorded as its previous lookup class. + * <p> + * Teleporting across modules restricts access to the public types that + * both the lookup class and the previous lookup class can equally access + * (see below). + * <p> + * {@link MethodHandles#privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn(T.class, lookup)} + * can be used to teleport a {@code lookup} from class {@code C} to class {@code T} + * and create a new {@code Lookup} with <a href="#privcc">private access</a> + * if the lookup class is allowed to do <em>deep reflection</em> on {@code T}. + * The {@code lookup} must have {@link #MODULE} and {@link #PRIVATE} access + * to call {@code privateLookupIn}. + * A {@code lookup} on {@code C} in module {@code M1} is allowed to do deep reflection + * on all classes in {@code M1}. If {@code T} is in {@code M1}, {@code privateLookupIn} + * produces a new {@code Lookup} on {@code T} with full capabilities. + * A {@code lookup} on {@code C} is also allowed + * to do deep reflection on {@code T} in another module {@code M2} if + * {@code M1} reads {@code M2} and {@code M2} {@link Module#isOpen(String,Module) opens} + * the package containing {@code T} to at least {@code M1}. + * {@code T} becomes the new lookup class and {@code C} becomes the new previous + * lookup class and {@code MODULE} access is dropped from the resulting {@code Lookup}. + * The resulting {@code Lookup} can be used to do member lookup or teleport + * to another lookup class by calling {@link #in Lookup::in}. But + * it cannot be used to obtain another private {@code Lookup} by calling + * {@link MethodHandles#privateLookupIn(Class, Lookup) privateLookupIn} + * because it has no {@code MODULE} access. + * + * <h2><a id="module-access-check"></a>Cross-module access checks</h2> + * + * A {@code Lookup} with {@link #PUBLIC} or with {@link #UNCONDITIONAL} mode + * allows cross-module access. The access checking is performed with respect + * to both the lookup class and the previous lookup class if present. + * <p> + * A {@code Lookup} with {@link #UNCONDITIONAL} mode can access public type + * in all modules when the type is in a package that is {@linkplain Module#isExported(String) + * exported unconditionally}. + * <p> + * If a {@code Lookup} on {@code LC} in {@code M1} has no previous lookup class, + * the lookup with {@link #PUBLIC} mode can access all public types in modules + * that are readable to {@code M1} and the type is in a package that is exported + * at least to {@code M1}. + * <p> + * If a {@code Lookup} on {@code LC} in {@code M1} has a previous lookup class + * {@code PLC} on {@code M0}, the lookup with {@link #PUBLIC} mode can access + * the intersection of all public types that are accessible to {@code M1} + * with all public types that are accessible to {@code M0}. {@code M0} + * reads {@code M1} and hence the set of accessible types includes: + * + * <table class="striped"> + * <caption style="display:none"> + * Public types in the following packages are accessible to the + * lookup class and the previous lookup class. + * </caption> + * <thead> + * <tr> + * <th scope="col">Equally accessible types to {@code M0} and {@code M1}</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M1}</th> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M0} if {@code M1} reads {@code M0}</th> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">unconditional-exported packages from a third module {@code M2} + * if both {@code M0} and {@code M1} read {@code M2}</th> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">qualified-exported packages from {@code M1} to {@code M0}</th> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">qualified-exported packages from {@code M0} to {@code M1} + * if {@code M1} reads {@code M0}</th> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">qualified-exported packages from a third module {@code M2} to + * both {@code M0} and {@code M1} if both {@code M0} and {@code M1} read {@code M2}</th> + * </tr> + * </tbody> + * </table> + * + * <h2><a id="access-modes"></a>Access modes</h2> + * + * The table below shows the access modes of a {@code Lookup} produced by + * any of the following factory or transformation methods: + * <ul> + * <li>{@link #lookup() MethodHandles.lookup()}</li> + * <li>{@link #publicLookup() MethodHandles.publicLookup()}</li> + * <li>{@link #privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn}</li> + * <li>{@link Lookup#in}</li> + * <li>{@link Lookup#dropLookupMode(int)}</li> + * </ul> + * + * <table class="striped"> + * <caption style="display:none"> + * Access mode summary + * </caption> + * <thead> + * <tr> + * <th scope="col">Lookup object</th> + * <th style="text-align:center">protected</th> + * <th style="text-align:center">private</th> + * <th style="text-align:center">package</th> + * <th style="text-align:center">module</th> + * <th style="text-align:center">public</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <th scope="row" style="text-align:left">{@code CL = MethodHandles.lookup()} in {@code C}</th> + * <td style="text-align:center">PRO</td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">{@code CL.in(C1)} same package</th> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">{@code CL.in(C1)} same module</th> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <th scope="row" style="text-align:left">{@code CL.in(D)} different module</th> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code CL.in(D).in(C)} hop back to module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI1 = privateLookupIn(C1,CL)}</td> + * <td style="text-align:center">PRO</td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1a = privateLookupIn(C,PRI1)}</td> + * <td style="text-align:center">PRO</td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.in(C1)} same package</td> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.in(C1)} different package</td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.in(D)} different module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI1.dropLookupMode(PROTECTED)}</td> + * <td></td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.dropLookupMode(PRIVATE)}</td> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.dropLookupMode(PACKAGE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.dropLookupMode(MODULE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code PRI1.dropLookupMode(PUBLIC)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * <tr> + * <td>{@code PRI2 = privateLookupIn(D,CL)}</td> + * <td style="text-align:center">PRO</td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code privateLookupIn(D,PRI1)}</td> + * <td style="text-align:center">PRO</td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code privateLookupIn(C,PRI2)} fails</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">IAE</td> + * </tr> + * <tr> + * <td>{@code PRI2.in(D2)} same package</td> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.in(D2)} different package</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.in(C1)} hop back to module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.in(E)} hop to third module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * </tr> + * <tr> + * <td>{@code PRI2.dropLookupMode(PROTECTED)}</td> + * <td></td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.dropLookupMode(PRIVATE)}</td> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.dropLookupMode(PACKAGE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.dropLookupMode(MODULE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">2R</td> + * </tr> + * <tr> + * <td>{@code PRI2.dropLookupMode(PUBLIC)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * </tr> + * <tr> + * <td>{@code CL.dropLookupMode(PROTECTED)}</td> + * <td></td> + * <td style="text-align:center">PRI</td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code CL.dropLookupMode(PRIVATE)}</td> + * <td></td> + * <td></td> + * <td style="text-align:center">PAC</td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code CL.dropLookupMode(PACKAGE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">MOD</td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code CL.dropLookupMode(MODULE)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">1R</td> + * </tr> + * <tr> + * <td>{@code CL.dropLookupMode(PUBLIC)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * </tr> + * <tr> + * <td>{@code PUB = publicLookup()}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">U</td> + * </tr> + * <tr> + * <td>{@code PUB.in(D)} different module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">U</td> + * </tr> + * <tr> + * <td>{@code PUB.in(D).in(E)} third module</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">U</td> + * </tr> + * <tr> + * <td>{@code PUB.dropLookupMode(UNCONDITIONAL)}</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * </tr> + * <tr> + * <td>{@code privateLookupIn(C1,PUB)} fails</td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">IAE</td> + * </tr> + * <tr> + * <td>{@code ANY.in(X)}, for inaccessible <code>X</code></td> + * <td></td> + * <td></td> + * <td></td> + * <td></td> + * <td style="text-align:center">none</td> + * </tr> + * </tbody> + * </table> + * + * <p> + * Notes: + * <ul> + * <li>Class {@code C} and class {@code C1} are in module {@code M1}, + * but {@code D} and {@code D2} are in module {@code M2}, and {@code E} + * is in module {@code M3}. {@code X} stands for class which is inaccessible + * to the lookup. {@code ANY} stands for any of the example lookups.</li> + * <li>{@code PRO} indicates {@link #PROTECTED} bit set, + * {@code PRI} indicates {@link #PRIVATE} bit set, + * {@code PAC} indicates {@link #PACKAGE} bit set, + * {@code MOD} indicates {@link #MODULE} bit set, + * {@code 1R} and {@code 2R} indicate {@link #PUBLIC} bit set, + * {@code U} indicates {@link #UNCONDITIONAL} bit set, + * {@code IAE} indicates {@code IllegalAccessException} thrown.</li> + * <li>Public access comes in three kinds: + * <ul> + * <li>unconditional ({@code U}): the lookup assumes readability. + * The lookup has {@code null} previous lookup class. + * <li>one-module-reads ({@code 1R}): the module access checking is + * performed with respect to the lookup class. The lookup has {@code null} + * previous lookup class. + * <li>two-module-reads ({@code 2R}): the module access checking is + * performed with respect to the lookup class and the previous lookup class. + * The lookup has a non-null previous lookup class which is in a + * different module from the current lookup class. + * </ul> + * <li>Any attempt to reach a third module loses all access.</li> + * <li>If a target class {@code X} is not accessible to {@code Lookup::in} + * all access modes are dropped.</li> + * </ul> + * * <h2><a id="secmgr"></a>Security manager interactions</h2> * Although bytecode instructions can only refer to classes in * a related class loader, this API can search for methods in any * class, as long as a reference to its {@code Class} object is * available. Such cross-loader references are also possible with the
*** 643,652 **** --- 1193,1205 ---- public static final class Lookup { /** The class on behalf of whom the lookup is being performed. */ private final Class<?> lookupClass; + /** previous lookup class */ + private final Class<?> prevLookupClass; + /** The allowed sorts of members which may be looked up (PUBLIC, etc.). */ private final int allowedModes; static { Reflection.registerFieldsToFilter(Lookup.class, Set.of("lookupClass", "allowedModes"));
*** 654,663 **** --- 1207,1220 ---- /** A single-bit mask representing {@code public} access, * which may contribute to the result of {@link #lookupModes lookupModes}. * The value, {@code 0x01}, happens to be the same as the value of the * {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}. + * <p> + * A {@code Lookup} with this lookup mode performs cross-module access check + * with respect to the {@linkplain #lookupClass() lookup class} and + * {@linkplain #previousLookupClass() previous lookup class} if present. */ public static final int PUBLIC = Modifier.PUBLIC; /** A single-bit mask representing {@code private} access, * which may contribute to the result of {@link #lookupModes lookupModes}.
*** 678,695 **** * The value is {@code 0x08}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. */ public static final int PACKAGE = Modifier.STATIC; ! /** A single-bit mask representing {@code module} access (default access), * which may contribute to the result of {@link #lookupModes lookupModes}. * The value is {@code 0x10}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} * with this lookup mode can access all public types in the module of the * lookup class and public types in packages exported by other modules * to the module of the lookup class. * @since 9 * @spec JPMS */ public static final int MODULE = PACKAGE << 1; --- 1235,1256 ---- * The value is {@code 0x08}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. */ public static final int PACKAGE = Modifier.STATIC; ! /** A single-bit mask representing {@code module} access, * which may contribute to the result of {@link #lookupModes lookupModes}. * The value is {@code 0x10}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} * with this lookup mode can access all public types in the module of the * lookup class and public types in packages exported by other modules * to the module of the lookup class. + * <p> + * If this lookup mode is set, the {@linkplain #previousLookupClass() + * previous lookup class} is always {@code null}. + * * @since 9 * @spec JPMS */ public static final int MODULE = PACKAGE << 1;
*** 697,738 **** * which may contribute to the result of {@link #lookupModes lookupModes}. * The value is {@code 0x20}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. * A {@code Lookup} with this lookup mode assumes {@linkplain * java.lang.Module#canRead(java.lang.Module) readability}. ! * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup} ! * with this lookup mode can access all public members of public types ! * of all modules where the type is in a package that is {@link * java.lang.Module#isExported(String) exported unconditionally}. * @since 9 * @spec JPMS * @see #publicLookup() */ public static final int UNCONDITIONAL = PACKAGE << 2; private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL); private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL); private static final int TRUSTED = -1; private static int fixmods(int mods) { mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL); ! return (mods != 0) ? mods : (PACKAGE | MODULE | UNCONDITIONAL); } /** Tells which class is performing the lookup. It is this class against * which checks are performed for visibility and access permissions. * <p> * The class implies a maximum level of access permission, * but the permissions may be additionally limited by the bitmask * {@link #lookupModes lookupModes}, which controls whether non-public members * can be accessed. * @return the lookup class, on behalf of which this lookup object finds members */ public Class<?> lookupClass() { return lookupClass; } // This is just for calling out to MethodHandleImpl. private Class<?> lookupClassOrNull() { return (allowedModes == TRUSTED) ? null : lookupClass; } --- 1258,1334 ---- * which may contribute to the result of {@link #lookupModes lookupModes}. * The value is {@code 0x20}, which does not correspond meaningfully to * any particular {@linkplain java.lang.reflect.Modifier modifier bit}. * A {@code Lookup} with this lookup mode assumes {@linkplain * java.lang.Module#canRead(java.lang.Module) readability}. ! * This lookup mode can access all public members of public types ! * of all modules when the type is in a package that is {@link * java.lang.Module#isExported(String) exported unconditionally}. + * + * <p> + * If this lookup mode is set, the {@linkplain #previousLookupClass() + * previous lookup class} is always {@code null}. + * * @since 9 * @spec JPMS * @see #publicLookup() */ public static final int UNCONDITIONAL = PACKAGE << 2; private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE | MODULE | UNCONDITIONAL); private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL); private static final int TRUSTED = -1; + /* + * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL + * Adjust 0 => PACKAGE + */ private static int fixmods(int mods) { mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL); ! if (Modifier.isPublic(mods)) ! mods |= UNCONDITIONAL; ! return (mods != 0) ? mods : PACKAGE; } /** Tells which class is performing the lookup. It is this class against * which checks are performed for visibility and access permissions. * <p> + * If this lookup object has a {@linkplain #previousLookupClass() previous lookup class}, + * access checks are performed against both the lookup class and the previous lookup class. + * <p> * The class implies a maximum level of access permission, * but the permissions may be additionally limited by the bitmask * {@link #lookupModes lookupModes}, which controls whether non-public members * can be accessed. * @return the lookup class, on behalf of which this lookup object finds members + * @see <a href="#cross-module-lookup">Cross-module lookups</a> */ public Class<?> lookupClass() { return lookupClass; } + /** Reports a lookup class in another module that this lookup object + * was previously teleported from, or {@code null}. + * <p> + * A {@code Lookup} object produced by the factory methods, such as the + * {@link #lookup() lookup()} and {@link #publicLookup() publicLookup()} method, + * has {@code null} previous lookup class. + * A {@code Lookup} object has a non-null previous lookup class + * when this lookup was teleported from an old lookup class + * in one module to a new lookup class in another module. + * + * @return the lookup class in another module that this lookup object was + * previously teleported from, or {@code null} + * @since 14 + * @see #in(Class) + * @see MethodHandles#privateLookupIn(Class, Lookup) + * @see <a href="#cross-module-lookup">Cross-module lookups</a> + */ + public Class<?> previousLookupClass() { + return prevLookupClass; + } + // This is just for calling out to MethodHandleImpl. private Class<?> lookupClassOrNull() { return (allowedModes == TRUSTED) ? null : lookupClass; }
*** 772,900 **** * for method handle creation. * Must be called by from a method in this package, * which in turn is called by a method not in this package. */ Lookup(Class<?> lookupClass) { ! this(lookupClass, FULL_POWER_MODES); // make sure we haven't accidentally picked up a privileged class: checkUnprivilegedlookupClass(lookupClass); } ! private Lookup(Class<?> lookupClass, int allowedModes) { this.lookupClass = lookupClass; this.allowedModes = allowedModes; } /** * Creates a lookup on the specified new lookup class. * The resulting object will report the specified * class as its own {@link #lookupClass() lookupClass}. * <p> * However, the resulting {@code Lookup} object is guaranteed * to have no more access capabilities than the original. * In particular, access capabilities can be lost as follows:<ul> ! * <li>If the old lookup class is in a {@link Module#isNamed() named} module, and ! * the new lookup class is in a different module {@code M}, then no members, not ! * even public members in {@code M}'s exported packages, will be accessible. ! * The exception to this is when this lookup is {@link #publicLookup() ! * publicLookup}, in which case {@code PUBLIC} access is not lost. ! * <li>If the old lookup class is in an unnamed module, and the new lookup class ! * is a different module then {@link #MODULE MODULE} access is lost. ! * <li>If the new lookup class differs from the old one then {@code UNCONDITIONAL} is lost. * <li>If the new lookup class is in a different package ! * than the old one, protected and default (package) members will not be accessible. * <li>If the new lookup class is not within the same package member * as the old one, private members will not be accessible, and protected members ! * will not be accessible by virtue of inheritance. * (Protected members may continue to be accessible because of package sharing.) ! * <li>If the new lookup class is not accessible to the old lookup class, ! * then no members, not even public members, will be accessible. ! * (In all other cases, public members will continue to be accessible.) * </ul> * <p> * The resulting lookup's capabilities for loading classes * (used during {@link #findClass} invocations) * are determined by the lookup class' loader, * which may change due to this operation. ! * * @param requestedLookupClass the desired lookup class for the new lookup object * @return a lookup object which reports the desired lookup class, or the same object * if there is no change * @throws NullPointerException if the argument is null * * @revised 9 * @spec JPMS */ public Lookup in(Class<?> requestedLookupClass) { Objects.requireNonNull(requestedLookupClass); if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all ! return new Lookup(requestedLookupClass, FULL_POWER_MODES); if (requestedLookupClass == this.lookupClass) return this; // keep same capabilities int newModes = (allowedModes & FULL_POWER_MODES); ! if (!VerifyAccess.isSameModule(this.lookupClass, requestedLookupClass)) { ! // Need to drop all access when teleporting from a named module to another ! // module. The exception is publicLookup where PUBLIC is not lost. ! if (this.lookupClass.getModule().isNamed() ! && (this.allowedModes & UNCONDITIONAL) == 0) newModes = 0; ! else newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED); } if ((newModes & PACKAGE) != 0 && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) { newModes &= ~(PACKAGE|PRIVATE|PROTECTED); } // Allow nestmate lookups to be created without special privilege: if ((newModes & PRIVATE) != 0 && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) { newModes &= ~(PRIVATE|PROTECTED); } ! if ((newModes & PUBLIC) != 0 ! && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) { // The requested class it not accessible from the lookup class. // No permissions. newModes = 0; } ! ! checkUnprivilegedlookupClass(requestedLookupClass); ! return new Lookup(requestedLookupClass, newModes); } - /** * Creates a lookup on the same lookup class which this lookup object * finds members, but with a lookup mode that has lost the given lookup mode. * The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE * MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}. ! * {@link #PROTECTED PROTECTED} and {@link #UNCONDITIONAL UNCONDITIONAL} are always ! * dropped and so the resulting lookup mode will never have these access capabilities. * When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE} * or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will * not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC} * is dropped then the resulting lookup has no access. * @param modeToDrop the lookup mode to drop * @return a lookup object which lacks the indicated mode, or the same object if there is no change * @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC}, * {@code MODULE}, {@code PACKAGE}, {@code PROTECTED}, {@code PRIVATE} or {@code UNCONDITIONAL} * @see MethodHandles#privateLookupIn * @since 9 */ public Lookup dropLookupMode(int modeToDrop) { int oldModes = lookupModes(); ! int newModes = oldModes & ~(modeToDrop | PROTECTED | UNCONDITIONAL); switch (modeToDrop) { ! case PUBLIC: newModes &= ~(ALL_MODES); break; case MODULE: newModes &= ~(PACKAGE | PRIVATE); break; case PACKAGE: newModes &= ~(PRIVATE); break; case PROTECTED: case PRIVATE: case UNCONDITIONAL: break; default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop"); } if (newModes == oldModes) return this; // return self if no change ! return new Lookup(lookupClass(), newModes); } /** * Defines a class to the same class loader and in the same runtime package and * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's --- 1368,1537 ---- * for method handle creation. * Must be called by from a method in this package, * which in turn is called by a method not in this package. */ Lookup(Class<?> lookupClass) { ! this(lookupClass, null, FULL_POWER_MODES); // make sure we haven't accidentally picked up a privileged class: checkUnprivilegedlookupClass(lookupClass); } ! private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) { ! assert prevLookupClass == null || ((allowedModes & MODULE) == 0 ! && prevLookupClass.getModule() != lookupClass.getModule()); ! this.lookupClass = lookupClass; + this.prevLookupClass = prevLookupClass; this.allowedModes = allowedModes; } + private static Lookup newLookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) { + // make sure we haven't accidentally picked up a privileged class: + checkUnprivilegedlookupClass(lookupClass); + return new Lookup(lookupClass, prevLookupClass, allowedModes); + } + /** * Creates a lookup on the specified new lookup class. * The resulting object will report the specified * class as its own {@link #lookupClass() lookupClass}. + * * <p> * However, the resulting {@code Lookup} object is guaranteed * to have no more access capabilities than the original. * In particular, access capabilities can be lost as follows:<ul> ! * <li>If the new lookup class is in a different module from the old one, ! * i.e. {@link #MODULE MODULE} access is lost. * <li>If the new lookup class is in a different package ! * than the old one, protected and default (package) members will not be accessible, ! * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost. * <li>If the new lookup class is not within the same package member * as the old one, private members will not be accessible, and protected members ! * will not be accessible by virtue of inheritance, ! * i.e. {@link #PRIVATE PRIVATE} access is lost. * (Protected members may continue to be accessible because of package sharing.) ! * <li>If the new lookup class is not ! * {@linkplain #accessClass(Class) accessible} to this lookup, ! * then no members, not even public members, will be accessible ! * i.e. all access modes are lost. ! * <li>If the new lookup class, the old lookup class and the previous lookup class ! * are all in different modules i.e. teleporting to a third module, ! * all access modes are lost. * </ul> * <p> + * The new previous lookup class is chosen as follows: + * <ul> + * <li>If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit, + * the new previous lookup class is {@code null}. + * <li>If the new lookup class is in the same module as the old lookup class, + * the new previous lookup class is the old previous lookup class. + * <li>If the new lookup class is in a different module from the old lookup class, + * the new previous lookup class is the the old lookup class. + *</ul> + * <p> * The resulting lookup's capabilities for loading classes * (used during {@link #findClass} invocations) * are determined by the lookup class' loader, * which may change due to this operation. ! * <p> * @param requestedLookupClass the desired lookup class for the new lookup object * @return a lookup object which reports the desired lookup class, or the same object * if there is no change * @throws NullPointerException if the argument is null * * @revised 9 * @spec JPMS + * @see #accessClass(Class) + * @see <a href="#cross-module-lookup">Cross-module lookups</a> */ public Lookup in(Class<?> requestedLookupClass) { Objects.requireNonNull(requestedLookupClass); if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all ! return new Lookup(requestedLookupClass, null, FULL_POWER_MODES); if (requestedLookupClass == this.lookupClass) return this; // keep same capabilities int newModes = (allowedModes & FULL_POWER_MODES); ! Module fromModule = this.lookupClass.getModule(); ! Module targetModule = requestedLookupClass.getModule(); ! Class<?> plc = this.previousLookupClass(); ! if ((this.allowedModes & UNCONDITIONAL) != 0) { ! assert plc == null; ! newModes = UNCONDITIONAL; ! } else if (fromModule != targetModule) { ! if (plc != null && !VerifyAccess.isSameModule(plc, requestedLookupClass)) { ! // allow hopping back and forth between fromModule and plc's module ! // but not the third module newModes = 0; ! } ! // drop MODULE access newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED); + // teleport from this lookup class + plc = this.lookupClass; } if ((newModes & PACKAGE) != 0 && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) { newModes &= ~(PACKAGE|PRIVATE|PROTECTED); } // Allow nestmate lookups to be created without special privilege: if ((newModes & PRIVATE) != 0 && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) { newModes &= ~(PRIVATE|PROTECTED); } ! if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0 ! && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, this.prevLookupClass, allowedModes)) { // The requested class it not accessible from the lookup class. // No permissions. newModes = 0; } ! return newLookup(requestedLookupClass, plc, newModes); } /** * Creates a lookup on the same lookup class which this lookup object * finds members, but with a lookup mode that has lost the given lookup mode. * The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE * MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}. ! * {@link #PROTECTED PROTECTED} is always ! * dropped and so the resulting lookup mode will never have this access capability. * When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE} * or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will * not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC} + * is dropped then the resulting lookup has no access. If {@code UNCONDITIONAL} * is dropped then the resulting lookup has no access. + * + * @apiNote + * A lookup with {@code PACKAGE} but not {@code PRIVATE} mode can safely + * delegate non-public access within the package of the lookup class without + * conferring private access. A lookup with {@code MODULE} but not + * {@code PACKAGE} mode can safely delegate {@code PUBLIC} access within + * the module of the lookup class without conferring package access. + * A lookup with a {@linkplain #previousLookupClass() previous lookup class} + * (and {@code PUBLIC} but not {@code MODULE} mode) can safely delegate access + * to public classes accessible to both the module of the lookup class + * and the module of the previous lookup class. + * * @param modeToDrop the lookup mode to drop * @return a lookup object which lacks the indicated mode, or the same object if there is no change * @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC}, * {@code MODULE}, {@code PACKAGE}, {@code PROTECTED}, {@code PRIVATE} or {@code UNCONDITIONAL} * @see MethodHandles#privateLookupIn * @since 9 */ public Lookup dropLookupMode(int modeToDrop) { int oldModes = lookupModes(); ! int newModes = oldModes & ~(modeToDrop | PROTECTED); switch (modeToDrop) { ! case PUBLIC: newModes &= ~(FULL_POWER_MODES); break; case MODULE: newModes &= ~(PACKAGE | PRIVATE); break; case PACKAGE: newModes &= ~(PRIVATE); break; case PROTECTED: case PRIVATE: case UNCONDITIONAL: break; default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop"); } if (newModes == oldModes) return this; // return self if no change ! return newLookup(lookupClass(), previousLookupClass(), newModes); } /** * Defines a class to the same class loader and in the same runtime package and * {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
*** 995,1035 **** // Make sure outer class is initialized first. static { IMPL_NAMES.getClass(); } /** Package-private version of lookup which is trusted. */ ! static final Lookup IMPL_LOOKUP = new Lookup(Object.class, TRUSTED); /** Version of lookup which is trusted minimally. * It can only be used to create method handles to publicly accessible * members in packages that are exported unconditionally. */ ! static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, (PUBLIC|UNCONDITIONAL)); private static void checkUnprivilegedlookupClass(Class<?> lookupClass) { String name = lookupClass.getName(); if (name.startsWith("java.lang.invoke.")) throw newIllegalArgumentException("illegal lookupClass: "+lookupClass); } /** * Displays the name of the class from which lookups are to be made. * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.) * If there are restrictions on the access permitted to this lookup, * this is indicated by adding a suffix to the class name, consisting * of a slash and a keyword. The keyword represents the strongest * allowed access, and is chosen as follows: * <ul> * <li>If no access is allowed, the suffix is "/noaccess". * <li>If only public access to types in exported packages is allowed, the suffix is "/public". - * <li>If only public access and unconditional access are allowed, the suffix is "/publicLookup". * <li>If only public and module access are allowed, the suffix is "/module". ! * <li>If only public, module and package access are allowed, the suffix is "/package". ! * <li>If only public, module, package, and private access are allowed, the suffix is "/private". * </ul> ! * If none of the above cases apply, it is the case that full ! * access (public, module, package, private, and protected) is allowed. * In this case, no suffix is added. * This is true only of an object obtained originally from * {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}. * Objects created by {@link java.lang.invoke.MethodHandles.Lookup#in Lookup.in} * always have restricted access, and will display a suffix. --- 1632,1674 ---- // Make sure outer class is initialized first. static { IMPL_NAMES.getClass(); } /** Package-private version of lookup which is trusted. */ ! static final Lookup IMPL_LOOKUP = new Lookup(Object.class, null, TRUSTED); /** Version of lookup which is trusted minimally. * It can only be used to create method handles to publicly accessible * members in packages that are exported unconditionally. */ ! static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL); private static void checkUnprivilegedlookupClass(Class<?> lookupClass) { String name = lookupClass.getName(); if (name.startsWith("java.lang.invoke.")) throw newIllegalArgumentException("illegal lookupClass: "+lookupClass); } /** * Displays the name of the class from which lookups are to be made. + * followed with "/" and the name of the {@linkplain #previousLookupClass() + * previous lookup class} if present. * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.) * If there are restrictions on the access permitted to this lookup, * this is indicated by adding a suffix to the class name, consisting * of a slash and a keyword. The keyword represents the strongest * allowed access, and is chosen as follows: * <ul> * <li>If no access is allowed, the suffix is "/noaccess". + * <li>If only unconditional access is allowed, the suffix is "/publicLookup". * <li>If only public access to types in exported packages is allowed, the suffix is "/public". * <li>If only public and module access are allowed, the suffix is "/module". ! * <li>If public and package access are allowed, the suffix is "/package". ! * <li>If public, package, and private access are allowed, the suffix is "/private". * </ul> ! * If none of the above cases apply, it is the case that full access ! * (public, module, package, private, and protected) is allowed. * In this case, no suffix is added. * This is true only of an object obtained originally from * {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}. * Objects created by {@link java.lang.invoke.MethodHandles.Lookup#in Lookup.in} * always have restricted access, and will display a suffix.
*** 1045,1068 **** * @spec JPMS */ @Override public String toString() { String cname = lookupClass.getName(); switch (allowedModes) { case 0: // no privileges return cname + "/noaccess"; case PUBLIC: return cname + "/public"; - case PUBLIC|UNCONDITIONAL: - return cname + "/publicLookup"; case PUBLIC|MODULE: return cname + "/module"; case PUBLIC|MODULE|PACKAGE: return cname + "/package"; ! case FULL_POWER_MODES & ~PROTECTED: return cname + "/private"; case FULL_POWER_MODES: return cname; case TRUSTED: return "/trusted"; // internal only; not exported default: // Should not happen, but it's a bitfield... cname = cname + "/" + Integer.toHexString(allowedModes); --- 1684,1712 ---- * @spec JPMS */ @Override public String toString() { String cname = lookupClass.getName(); + if (prevLookupClass != null) + cname += "/" + prevLookupClass.getName(); switch (allowedModes) { case 0: // no privileges return cname + "/noaccess"; + case UNCONDITIONAL: + return cname + "/publicLookup"; case PUBLIC: return cname + "/public"; case PUBLIC|MODULE: return cname + "/module"; + case PUBLIC|PACKAGE: case PUBLIC|MODULE|PACKAGE: return cname + "/package"; ! case FULL_POWER_MODES & (~PROTECTED): ! case FULL_POWER_MODES & ~(PROTECTED|MODULE): return cname + "/private"; case FULL_POWER_MODES: + case FULL_POWER_MODES & (~MODULE): return cname; case TRUSTED: return "/trusted"; // internal only; not exported default: // Should not happen, but it's a bitfield... cname = cname + "/" + Integer.toHexString(allowedModes);
*** 1299,1326 **** Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader()); return accessClass(targetClass); } /** ! * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The ! * static initializer of the class is not run. * <p> ! * The lookup context here is determined by the {@linkplain #lookupClass() lookup class} and the ! * {@linkplain #lookupModes() lookup modes}. * ! * @param targetClass the class to be access-checked * * @return the class that has been access-checked ! * ! * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access ! * modes. * @exception SecurityException if a security manager is present and it * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @since 9 */ public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException { ! if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) { throw new MemberName(targetClass).makeAccessException("access violation", this); } checkSecurityManager(targetClass, null); return targetClass; } --- 1943,2021 ---- Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader()); return accessClass(targetClass); } /** ! * Determines if a class can be accessed from the lookup context defined by ! * this {@code Lookup} object. The static initializer of the class is not run. * <p> ! * If the {@code targetClass} is in the same module as the lookup class, ! * the lookup class is {@code LC} in module {@code M1} and ! * the previous lookup class is in module {@code M0} or ! * {@code null} if not present, ! * {@code targetClass} is accessible if and only if one of the following is true: ! * <ul> ! * <li>If this lookup has {@link #PRIVATE} access, {@code targetClass} is ! * {@code LC} or other class in the same nest of {@code LC}.</li> ! * <li>If this lookup has {@link #PACKAGE} access, {@code targetClass} is ! * in the same runtime package of {@code LC}.</li> ! * <li>If this lookup has {@link #MODULE} access, {@code targetClass} is ! * a public type in {@code M1}.</li> ! * <li>If this lookup has {@link #PUBLIC} access, {@code targetClass} is ! * a public type in a package exported by {@code M1} to at least {@code M0} ! * if the previous lookup class is present; otherwise, {@code targetClass} ! * is a public type in a package exported by {@code M1} unconditionally.</li> ! * </ul> * ! * <p> ! * Otherwise, if this lookup has {@link #UNCONDITIONAL} access, this lookup ! * can access public types in all modules when the type is in a package ! * that is exported unconditionally. ! * <p> ! * Otherwise, the target class is in a different module from {@code lookupClass}, ! * and if this lookup does not have {@code PUBLIC} access, {@code lookupClass} ! * is inaccessible. ! * <p> ! * Otherwise, if this lookup has no {@linkplain #previousLookupClass() previous lookup class}, ! * {@code M1} is the module containing {@code lookupClass} and ! * {@code M2} is the module containing {@code targetClass}, ! * then {@code targetClass} is accessible if and only if ! * <ul> ! * <li>{@code M1} reads {@code M2}, and ! * <li>{@code targetClass} is public and in a package exported by ! * {@code M2} at least to {@code M1}. ! * </ul> ! * <p> ! * Otherwise, if this lookup has a {@linkplain #previousLookupClass() previous lookup class}, ! * {@code M1} and {@code M2} are as before, and {@code M0} is the module ! * containing the previous lookup class, then {@code targetClass} is accessible ! * if and only if one of the following is true: ! * <ul> ! * <li>{@code targetClass} is in {@code M0} and {@code M1} ! * {@linkplain Module#reads reads} {@code M0} and the type is ! * in a package that is exported to at least {@code M1}. ! * <li>{@code targetClass} is in {@code M1} and {@code M0} ! * {@linkplain Module#reads reads} {@code M1} and the type is ! * in a package that is exported to at least {@code M0}. ! * <li>{@code targetClass} is in a third module {@code M2} and both {@code M0} ! * and {@code M1} reads {@code M2} and the type is in a package ! * that is exported to at least both {@code M0} and {@code M2}. ! * </ul> ! * <p> ! * Otherwise, {@code targetClass} is not accessible. * + * @param targetClass the class to be access-checked * @return the class that has been access-checked ! * @throws IllegalAccessException if the class is not accessible from the lookup class ! * and previous lookup class, if present, using the allowed access modes. * @exception SecurityException if a security manager is present and it * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a> * @since 9 + * @see <a href="#cross-module-lookup">Cross-module lookups</a> */ public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException { ! if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, prevLookupClass, allowedModes)) { throw new MemberName(targetClass).makeAccessException("access violation", this); } checkSecurityManager(targetClass, null); return targetClass; }
*** 2081,2091 **** } boolean isClassAccessible(Class<?> refc) { Objects.requireNonNull(refc); Class<?> caller = lookupClassOrNull(); ! return caller == null || VerifyAccess.isClassAccessible(refc, caller, allowedModes); } /** Check name for an illegal leading "&lt;" character. */ void checkMethodName(byte refKind, String name) throws NoSuchMethodException { if (name.startsWith("<") && refKind != REF_newInvokeSpecial) --- 2776,2786 ---- } boolean isClassAccessible(Class<?> refc) { Objects.requireNonNull(refc); Class<?> caller = lookupClassOrNull(); ! return caller == null || VerifyAccess.isClassAccessible(refc, caller, prevLookupClass, allowedModes); } /** Check name for an illegal leading "&lt;" character. */ void checkMethodName(byte refKind, String name) throws NoSuchMethodException { if (name.startsWith("<") && refKind != REF_newInvokeSpecial)
*** 2218,2228 **** MethodHandleNatives.refKindIsSetter(refKind)) throw m.makeAccessException("unexpected set of a final field", this); int requestedModes = fixmods(mods); // adjust 0 => PACKAGE if ((requestedModes & allowedModes) != 0) { if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(), ! mods, lookupClass(), allowedModes)) return; } else { // Protected members can also be checked as if they were package-private. if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0 && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass())) --- 2913,2923 ---- MethodHandleNatives.refKindIsSetter(refKind)) throw m.makeAccessException("unexpected set of a final field", this); int requestedModes = fixmods(mods); // adjust 0 => PACKAGE if ((requestedModes & allowedModes) != 0) { if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(), ! mods, lookupClass(), previousLookupClass(), allowedModes)) return; } else { // Protected members can also be checked as if they were package-private. if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0 && VerifyAccess.isSamePackage(m.getDeclaringClass(), lookupClass()))
*** 2237,2249 **** // check the class first: boolean classOK = (Modifier.isPublic(defc.getModifiers()) && (defc == refc || Modifier.isPublic(refc.getModifiers()))); if (!classOK && (allowedModes & PACKAGE) != 0) { ! classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), FULL_POWER_MODES) && (defc == refc || ! VerifyAccess.isClassAccessible(refc, lookupClass(), FULL_POWER_MODES))); } if (!classOK) return "class is not public"; if (Modifier.isPublic(mods)) return "access to public member failed"; // (how?, module not readable?) --- 2932,2945 ---- // check the class first: boolean classOK = (Modifier.isPublic(defc.getModifiers()) && (defc == refc || Modifier.isPublic(refc.getModifiers()))); if (!classOK && (allowedModes & PACKAGE) != 0) { ! // ignore previous lookup class to check if default package access ! classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), null, FULL_POWER_MODES) && (defc == refc || ! VerifyAccess.isClassAccessible(refc, lookupClass(), null, FULL_POWER_MODES))); } if (!classOK) return "class is not public"; if (Modifier.isPublic(mods)) return "access to public member failed"; // (how?, module not readable?)
< prev index next >