8173978: Lookup.in should allow teleporting from a lookup class in a named module without dropping all access
authormchung
Wed, 24 Jul 2019 16:46:42 -0700
changeset 57512 f557f260b787
parent 57511 00ae3b739184
child 57513 6073b2290c0a
8173978: Lookup.in should allow teleporting from a lookup class in a named module without dropping all access Reviewed-by: alanb, plevart
src/java.base/share/classes/java/lang/invoke/MemberName.java
src/java.base/share/classes/java/lang/invoke/MethodHandles.java
src/java.base/share/classes/sun/invoke/util/VerifyAccess.java
test/jdk/java/lang/invoke/AccessControlTest.java
test/jdk/java/lang/invoke/DropLookupModeTest.java
test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java
test/jdk/java/lang/invoke/modules/Driver1.java
test/jdk/java/lang/invoke/modules/Unnamed.java
test/jdk/java/lang/invoke/modules/Unnamed1.java
test/jdk/java/lang/invoke/modules/m1/p1/Main.java
test/jdk/java/lang/invoke/modules/m3/c1/C1.java
test/jdk/java/lang/invoke/modules/m3/c1/C2.java
test/jdk/java/lang/invoke/modules/m3/c2/C3.java
test/jdk/java/lang/invoke/modules/m3/jdk/test/ModuleAccessTest.java
test/jdk/java/lang/invoke/modules/m3/module-info.java
test/jdk/java/lang/invoke/modules/m4/d1/D1.java
test/jdk/java/lang/invoke/modules/m4/d1/D2.java
test/jdk/java/lang/invoke/modules/m4/d2/D3.java
test/jdk/java/lang/invoke/modules/m4/module-info.java
test/jdk/java/lang/invoke/modules/m5/e1/CrackM5Access.java
test/jdk/java/lang/invoke/modules/m5/e1/E1.java
test/jdk/java/lang/invoke/modules/m5/e1/E2.java
test/jdk/java/lang/invoke/modules/m5/e1/NonPublic.java
test/jdk/java/lang/invoke/modules/m5/e1/Statics.java
test/jdk/java/lang/invoke/modules/m5/e2/E3.java
test/jdk/java/lang/invoke/modules/m5/module-info.java
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -521,7 +521,7 @@
     public boolean isAccessibleFrom(Class<?> lookupClass) {
         int mode = (ALL_ACCESS|MethodHandles.Lookup.PACKAGE|MethodHandles.Lookup.MODULE);
         return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
-                                               lookupClass, mode);
+                                               lookupClass, null, mode);
     }
 
     /**
@@ -930,13 +930,21 @@
                 message += ", from public Lookup";
             } else {
                 Module m;
+                Class<?> plc;
                 if (from instanceof MethodHandles.Lookup) {
                     MethodHandles.Lookup lookup = (MethodHandles.Lookup)from;
+                    from = lookup.lookupClass();
                     m = lookup.lookupClass().getModule();
+                    plc = lookup.previousLookupClass();
                 } else {
-                    m = from.getClass().getModule();
+                    m = ((Class<?>)from).getModule();
+                    plc = null;
                 }
                 message += ", from " + from + " (" + m + ")";
+                if (plc != null) {
+                    message += ", previous lookup " +
+                        plc.getName() + " (" + plc.getModule() + ")";
+                }
             }
         }
         return new IllegalAccessException(message);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Jul 24 16:46:42 2019 -0700
@@ -125,7 +125,7 @@
 
     /**
      * Returns a {@link Lookup lookup object} which is trusted minimally.
-     * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes.
+     * 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>
@@ -134,14 +134,14 @@
      *
      * @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.
+     * 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)}.
-     * 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
@@ -156,64 +156,106 @@
     }
 
     /**
-     * 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
+     * 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>{@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>
+     * <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 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.
+     * 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 lookup the caller lookup object
+     * @param caller 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 IllegalArgumentException if {@code targetClass} is a primitive 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
+     * @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 lookup) throws IllegalAccessException {
+    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");
-        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);
+        // 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 ((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);
+                logger.logIfOpenedForIllegalAccess(caller, targetClass);
             }
         }
-        return new Lookup(targetClass);
+        return Lookup.newLookup(targetClass, newPreviousClass, newModes);
     }
 
     /**
@@ -533,6 +575,514 @@
      * 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
@@ -645,6 +1195,9 @@
         /** 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;
 
@@ -656,6 +1209,10 @@
          *  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;
 
@@ -680,7 +1237,7 @@
          */
         public static final int PACKAGE = Modifier.STATIC;
 
-        /** A single-bit mask representing {@code module} access (default access),
+        /** 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}.
@@ -688,6 +1245,10 @@
          *  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
          */
@@ -699,10 +1260,14 @@
          *  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
+         *  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()
@@ -713,24 +1278,55 @@
         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);
-            return (mods != 0) ? mods : (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;
@@ -774,48 +1370,69 @@
          * which in turn is called by a method not in this package.
          */
         Lookup(Class<?> lookupClass) {
-            this(lookupClass, FULL_POWER_MODES);
+            this(lookupClass, null, FULL_POWER_MODES);
             // make sure we haven't accidentally picked up a privileged class:
             checkUnprivilegedlookupClass(lookupClass);
         }
 
-        private Lookup(Class<?> lookupClass, int allowedModes) {
+        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 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 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.
+         * 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.
+         * 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 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.)
+         * <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
@@ -823,22 +1440,32 @@
          *
          * @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, FULL_POWER_MODES);
+                return new Lookup(requestedLookupClass, null, 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)
+            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;
-                else
-                    newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED);
+                }
+                // 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)) {
@@ -849,29 +1476,39 @@
                 && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
                 newModes &= ~(PRIVATE|PROTECTED);
             }
-            if ((newModes & PUBLIC) != 0
-                && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) {
+            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;
             }
-
-            checkUnprivilegedlookupClass(requestedLookupClass);
-            return new Lookup(requestedLookupClass, newModes);
+            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} and {@link #UNCONDITIONAL UNCONDITIONAL} are always
-         * dropped and so the resulting lookup mode will never have these access capabilities.
+         * {@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},
@@ -881,9 +1518,9 @@
          */
         public Lookup dropLookupMode(int modeToDrop) {
             int oldModes = lookupModes();
-            int newModes = oldModes & ~(modeToDrop | PROTECTED | UNCONDITIONAL);
+            int newModes = oldModes & ~(modeToDrop | PROTECTED);
             switch (modeToDrop) {
-                case PUBLIC: newModes &= ~(ALL_MODES); break;
+                case PUBLIC: newModes &= ~(FULL_POWER_MODES); break;
                 case MODULE: newModes &= ~(PACKAGE | PRIVATE); break;
                 case PACKAGE: newModes &= ~(PRIVATE); break;
                 case PROTECTED:
@@ -892,7 +1529,7 @@
                 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);
+            return newLookup(lookupClass(), previousLookupClass(), newModes);
         }
 
         /**
@@ -997,13 +1634,13 @@
         static { IMPL_NAMES.getClass(); }
 
         /** Package-private version of lookup which is trusted. */
-        static final Lookup IMPL_LOOKUP = new Lookup(Object.class, 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, (PUBLIC|UNCONDITIONAL));
+        static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL);
 
         private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
             String name = lookupClass.getName();
@@ -1013,6 +1650,8 @@
 
         /**
          * 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
@@ -1020,14 +1659,14 @@
          * 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 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".
+         * <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.
+         * 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}.
@@ -1047,20 +1686,25 @@
         @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|UNCONDITIONAL:
-                return cname  + "/publicLookup";
             case PUBLIC|MODULE:
                 return cname + "/module";
+            case PUBLIC|PACKAGE:
             case PUBLIC|MODULE|PACKAGE:
                 return cname + "/package";
-            case FULL_POWER_MODES & ~PROTECTED:
-                return cname + "/private";
+            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
@@ -1301,24 +1945,75 @@
         }
 
         /**
-         * 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.
+         * 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>
-         * The lookup context here is determined by the {@linkplain #lookupClass() lookup class} and the
-         * {@linkplain #lookupModes() lookup modes}.
+         * 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, using the allowed access
-         * modes.
+         * @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, allowedModes)) {
+            if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, prevLookupClass, allowedModes)) {
                 throw new MemberName(targetClass).makeAccessException("access violation", this);
             }
             checkSecurityManager(targetClass, null);
@@ -2083,7 +2778,7 @@
         boolean isClassAccessible(Class<?> refc) {
             Objects.requireNonNull(refc);
             Class<?> caller = lookupClassOrNull();
-            return caller == null || VerifyAccess.isClassAccessible(refc, caller, allowedModes);
+            return caller == null || VerifyAccess.isClassAccessible(refc, caller, prevLookupClass, allowedModes);
         }
 
         /** Check name for an illegal leading "&lt;" character. */
@@ -2220,7 +2915,7 @@
             int requestedModes = fixmods(mods);  // adjust 0 => PACKAGE
             if ((requestedModes & allowedModes) != 0) {
                 if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
-                                                    mods, lookupClass(), allowedModes))
+                                                    mods, lookupClass(), previousLookupClass(), allowedModes))
                     return;
             } else {
                 // Protected members can also be checked as if they were package-private.
@@ -2239,9 +2934,10 @@
                                (defc == refc ||
                                 Modifier.isPublic(refc.getModifiers())));
             if (!classOK && (allowedModes & PACKAGE) != 0) {
-                classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), FULL_POWER_MODES) &&
+                // 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(), FULL_POWER_MODES)));
+                            VerifyAccess.isClassAccessible(refc, lookupClass(), null, FULL_POWER_MODES)));
             }
             if (!classOK)
                 return "class is not public";
--- a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java	Wed Jul 24 16:46:42 2019 -0700
@@ -88,18 +88,20 @@
      * @param defc the class in which the proposed member is actually defined
      * @param mods modifier flags for the proposed member
      * @param lookupClass the class for which the access check is being made
+     * @param prevLookupClass the class for which the access check is being made
+     * @param allowedModes allowed modes
      * @return true iff the accessing class can access such a member
      */
     public static boolean isMemberAccessible(Class<?> refc,  // symbolic ref class
                                              Class<?> defc,  // actual def class
                                              int      mods,  // actual member mods
                                              Class<?> lookupClass,
+                                             Class<?> prevLookupClass,
                                              int      allowedModes) {
         if (allowedModes == 0)  return false;
-        assert((allowedModes & PUBLIC) != 0 &&
-               (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+        assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
         // The symbolic reference class (refc) must always be fully verified.
-        if (!isClassAccessible(refc, lookupClass, allowedModes)) {
+        if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {
             return false;
         }
         // Usually refc and defc are the same, but verify defc also in case they differ.
@@ -109,6 +111,7 @@
 
         switch (mods & ALL_ACCESS_MODES) {
         case PUBLIC:
+            assert (allowedModes & PUBLIC) != 0 || (allowedModes & UNCONDITIONAL_ALLOWED) != 0;
             return true;  // already checked above
         case PROTECTED:
             assert !defc.isInterface(); // protected members aren't allowed in interfaces
@@ -175,14 +178,23 @@
      * package that is exported to the module that contains D.
      * <li>C and D are members of the same runtime package.
      * </ul>
+     *
      * @param refc the symbolic reference class to which access is being checked (C)
      * @param lookupClass the class performing the lookup (D)
+     * @param prevLookupClass the class from which the lookup was teleported or null
+     * @param allowedModes allowed modes
      */
-    public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
+    public static boolean isClassAccessible(Class<?> refc,
+                                            Class<?> lookupClass,
+                                            Class<?> prevLookupClass,
                                             int allowedModes) {
         if (allowedModes == 0)  return false;
-        assert((allowedModes & PUBLIC) != 0 &&
-               (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+        assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+
+        if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
+            isSamePackage(lookupClass, refc))
+            return true;
+
         int mods = getClassModifiers(refc);
         if (isPublic(mods)) {
 
@@ -195,37 +207,62 @@
                 return true;
             }
 
-            // trivially allow
-            if ((allowedModes & MODULE_ALLOWED) != 0 &&
-                (lookupModule == refModule))
+            // allow access to public types in all unconditionally exported packages
+            if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {
+                return refModule.isExported(refc.getPackageName());
+            }
+
+            if (lookupModule == refModule && prevLookupClass == null) {
+                // allow access to all public types in lookupModule
+                if ((allowedModes & MODULE_ALLOWED) != 0)
+                    return true;
+
+                assert (allowedModes & PUBLIC) != 0;
+                return refModule.isExported(refc.getPackageName());
+            }
+
+            // cross-module access
+            // 1. refc is in different module from lookupModule, or
+            // 2. refc is in lookupModule and a different module from prevLookupModule
+            Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()
+                                                              : null;
+            assert refModule != lookupModule || refModule != prevLookupModule;
+            if (isModuleAccessible(refc, lookupModule, prevLookupModule))
                 return true;
 
-            // check readability when UNCONDITIONAL not allowed
-            if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0)
-                || lookupModule.canRead(refModule)) {
-
-                // check that refc is in an exported package
-                if ((allowedModes & MODULE_ALLOWED) != 0) {
-                    if (refModule.isExported(refc.getPackageName(), lookupModule))
-                        return true;
-                } else {
-                    // exported unconditionally
-                    if (refModule.isExported(refc.getPackageName()))
-                        return true;
-                }
-
-                // not exported but allow access during VM initialization
-                // because java.base does not have its exports setup
-                if (!jdk.internal.misc.VM.isModuleSystemInited())
-                    return true;
-            }
+            // not exported but allow access during VM initialization
+            // because java.base does not have its exports setup
+            if (!jdk.internal.misc.VM.isModuleSystemInited())
+                return true;
 
             // public class not accessible to lookupClass
             return false;
         }
-        if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
-            isSamePackage(lookupClass, refc))
-            return true;
+
+        return false;
+    }
+
+    /*
+     * Tests if a class or interface REFC is accessible to m1 and m2 where m2
+     * may be null.
+     *
+     * A class or interface REFC in m is accessible to m1 and m2 if and only if
+     * both m1 and m2 read m and m exports the package of REFC at least to
+     * both m1 and m2.
+     */
+    public static boolean isModuleAccessible(Class<?> refc,  Module m1, Module m2) {
+        Module refModule = refc.getModule();
+        assert refModule != m1 || refModule != m2;
+        int mods = getClassModifiers(refc);
+        if (isPublic(mods)) {
+            if (m1.canRead(refModule) && (m2 == null || m2.canRead(refModule))) {
+                String pn = refc.getPackageName();
+
+                // refc is exported package to at least both m1 and m2
+                if (refModule.isExported(pn, m1) && (m2 == null || refModule.isExported(pn, m2)))
+                    return true;
+            }
+        }
         return false;
     }
 
--- a/test/jdk/java/lang/invoke/AccessControlTest.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/test/jdk/java/lang/invoke/AccessControlTest.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,6 @@
 import java.lang.reflect.*;
 import java.lang.reflect.Modifier;
 import java.util.*;
-import org.testng.*;
 import org.testng.annotations.*;
 
 import static java.lang.invoke.MethodHandles.*;
@@ -62,23 +61,28 @@
     private class LookupCase implements Comparable<LookupCase> {
         final Lookup   lookup;
         final Class<?> lookupClass;
+        final Class<?> prevLookupClass;
         final int      lookupModes;
         public LookupCase(Lookup lookup) {
             this.lookup = lookup;
             this.lookupClass = lookup.lookupClass();
+            this.prevLookupClass = lookup.previousLookupClass();
             this.lookupModes = lookup.lookupModes();
+
             assert(lookupString().equals(lookup.toString()));
             numberOf(lookupClass().getClassLoader()); // assign CL#
         }
-        public LookupCase(Class<?> lookupClass, int lookupModes) {
+        public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {
             this.lookup = null;
             this.lookupClass = lookupClass;
+            this.prevLookupClass = prevLookupClass;
             this.lookupModes = lookupModes;
             numberOf(lookupClass().getClassLoader()); // assign CL#
         }
 
-        public final Class<?> lookupClass() { return lookupClass; }
-        public final int      lookupModes() { return lookupModes; }
+        public final Class<?> lookupClass()     { return lookupClass; }
+        public final Class<?> prevLookupClass() { return prevLookupClass; }
+        public final int      lookupModes()     { return lookupModes; }
 
         public Lookup lookup() { lookup.getClass(); return lookup; }
 
@@ -86,12 +90,24 @@
         public int compareTo(LookupCase that) {
             Class<?> c1 = this.lookupClass();
             Class<?> c2 = that.lookupClass();
+            Class<?> p1 = this.prevLookupClass();
+            Class<?> p2 = that.prevLookupClass();
             if (c1 != c2) {
                 int cmp = c1.getName().compareTo(c2.getName());
                 if (cmp != 0)  return cmp;
                 cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
                 assert(cmp != 0);
                 return cmp;
+            } else if (p1 != p2){
+                if (p1 == null)
+                    return 1;
+                else if (p2 == null)
+                    return -1;
+                int cmp = p1.getName().compareTo(p2.getName());
+                if (cmp != 0)  return cmp;
+                cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());
+                assert(cmp != 0);
+                return cmp;
             }
             return -(this.lookupModes() - that.lookupModes());
         }
@@ -102,6 +118,7 @@
         }
         public boolean equals(LookupCase that) {
             return (this.lookupClass() == that.lookupClass() &&
+                    this.prevLookupClass() == that.prevLookupClass() &&
                     this.lookupModes() == that.lookupModes());
         }
 
@@ -113,20 +130,25 @@
         /** Simulate all assertions in the spec. for Lookup.toString. */
         private String lookupString() {
             String name = lookupClass.getName();
+            if (prevLookupClass != null)
+                name += "/" + prevLookupClass.getName();
             String suffix = "";
             if (lookupModes == 0)
                 suffix = "/noaccess";
             else if (lookupModes == PUBLIC)
                 suffix = "/public";
-             else if (lookupModes == (PUBLIC|UNCONDITIONAL))
+             else if (lookupModes == UNCONDITIONAL)
                 suffix = "/publicLookup";
             else if (lookupModes == (PUBLIC|MODULE))
                 suffix = "/module";
-            else if (lookupModes == (PUBLIC|MODULE|PACKAGE))
+            else if (lookupModes == (PUBLIC|PACKAGE)
+                     || lookupModes == (PUBLIC|MODULE|PACKAGE))
                 suffix = "/package";
-            else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
+            else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)
+                    || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
                 suffix = "/private";
-            else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
+            else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
+                     || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
                 suffix = "";
             else
                 suffix = "/#"+Integer.toHexString(lookupModes);
@@ -138,41 +160,50 @@
          * Creates a lookup on the specified new lookup class.
          * [A1] The resulting object will report the specified
          * class as its own {@link #lookupClass lookupClass}.
-         * <p>
          * [A2] 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> [A3] If the old lookup class is in a 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 publicLookup, in which case
-         * public access is not lost.
-         * <li> [A4] If the old lookup class is in an unnamed module, and the new
-         * lookup class is a different module then module access is lost.
-         * <li> [A5] If the new lookup class differs from the old one then UNCONDITIONAL
-         * is lost. If the new lookup class is not within the same package member as the
-         * old one, protected members will not be accessible by virtue of inheritance.
+         * [A3] If the new lookup class is in a different module from the old one,
+         * i.e. {@link #MODULE MODULE} access is lost.
+         * [A4] 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.
+         * [A5] 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> [A6] If the new lookup class is in a different package than the old one,
-         * protected and default (package) members will not be accessible.
-         * <li> [A7] If the new lookup class is not within the same package member
-         * as the old one, private members will not be accessible.
-         * <li> [A8] If the new lookup class is not accessible to the old lookup class,
-         * then no members, not even public members, will be accessible.
-         * <li> [A9] (In all other cases, public members will continue to be accessible.)
-         * </ul>
+         * [A6] 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.
+         * [A7] 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.
+         * <p>
+         * The new previous lookup class is chosen as follows:
+         * [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
+         * the new previous lookup class is {@code null}.
+         * [A9] 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.
+         * [A10] 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.
+         *
          * Other than the above cases, the new lookup will have the same
-         * access capabilities as the original. [A10]
+         * access capabilities as the original. [A11]
          * <hr>
          */
         public LookupCase in(Class<?> c2) {
             Class<?> c1 = lookupClass();
-            int m1 = lookupModes();
+            Module m1 = c1.getModule();
+            Module m2 = c2.getModule();
+            Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();
+            int modes1 = lookupModes();
             int changed = 0;
             // for the purposes of access control then treat classes in different unnamed
             // modules as being in the same module.
-            boolean sameModule = (c1.getModule() == c2.getModule()) ||
-                                 (!c1.getModule().isNamed() && !c2.getModule().isNamed());
+            boolean sameModule = (m1 == m2) ||
+                                 (!m1.isNamed() && !m2.isNamed());
             boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
                                    c1.getPackageName().equals(c2.getPackageName()));
             boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
@@ -180,40 +211,85 @@
             assert(samePackage  || !sameTopLevel);
             assert(sameTopLevel || !sameClass);
             boolean accessible = sameClass;
-            if ((m1 & PACKAGE) != 0)  accessible |= samePackage;
-            if ((m1 & PUBLIC ) != 0)  accessible |= (c2.getModifiers() & PUBLIC) != 0;
-            if (!sameModule) {
-                if (c1.getModule().isNamed() && (m1 & UNCONDITIONAL) == 0) {
-                    accessible = false;  // [A3]
-                } else {
-                    changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED);    // [A3] [A4]
-                }
+
+            if ((modes1 & PACKAGE) != 0)  accessible |= samePackage;
+            if ((modes1 & PUBLIC ) != 0)  {
+                if (isModuleAccessible(c2))
+                    accessible |= (c2.getModifiers() & PUBLIC) != 0;
+                else
+                    accessible = false;
+            }
+            if ((modes1 & UNCONDITIONAL) != 0) {
+                if (m2.isExported(c2.getPackageName()))
+                    accessible |= (c2.getModifiers() & PUBLIC) != 0;
+                else
+                    accessible = false;
             }
             if (!accessible) {
-                // Different package and no access to c2; lose all access.
-                changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED);  // [A8]
+                // no access to c2; lose all access.
+                changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL);  // [A6]
+            }
+            if (m2 != m1 && m0 != m1) {
+                // hop to a third module; lose all access
+                changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED);  // [A7]
+            }
+            if (!sameModule) {
+                changed |= MODULE;  // [A3]
             }
             if (!samePackage) {
                 // Different package; loose PACKAGE and lower access.
-                changed |= (PACKAGE|PRIVATE|PROTECTED);  // [A6]
+                changed |= (PACKAGE|PRIVATE|PROTECTED);  // [A4]
             }
             if (!sameTopLevel) {
                 // Different top-level class.  Lose PRIVATE and PROTECTED access.
-                changed |= (PRIVATE|PROTECTED);  // [A5] [A7]
+                changed |= (PRIVATE|PROTECTED);  // [A5]
             }
-            if (!sameClass) {
-                changed |= (UNCONDITIONAL);     // [A5]
-            } else {
-                assert(changed == 0);       // [A10] (no deprivation if same class)
+            if (sameClass) {
+                assert(changed == 0);       // [A11] (no deprivation if same class)
             }
-            if (accessible)  assert((changed & PUBLIC) == 0);  // [A9]
-            int m2 = m1 & ~changed;
-            LookupCase l2 = new LookupCase(c2, m2);
-            assert(l2.lookupClass() == c2); // [A1]
-            assert((m1 | m2) == m1);        // [A2] (no elevation of access)
+
+            if (accessible)  assert((changed & PUBLIC) == 0);
+            int modes2 = modes1 & ~changed;
+            Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]
+            if ((modes1 & UNCONDITIONAL) != 0) plc = null;      // [A8]
+            LookupCase l2 = new LookupCase(c2, plc, modes2);
+            assert(l2.lookupClass() == c2);         // [A1]
+            assert((modes1 | modes2) == modes1);    // [A2] (no elevation of access)
+            assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
             return l2;
         }
 
+        LookupCase dropLookupMode(int modeToDrop) {
+            int oldModes = lookupModes();
+            int newModes = oldModes & ~(modeToDrop | PROTECTED);
+            switch (modeToDrop) {
+                case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); 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
+            LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);
+            assert((oldModes | newModes) == oldModes);    // [A2] (no elevation of access)
+            assert(l2.prevLookupClass() == null || (newModes & MODULE) == 0);
+            return l2;
+        }
+
+        boolean isModuleAccessible(Class<?> c) {
+            Module m1 = lookupClass().getModule();
+            Module m2 = c.getModule();
+            Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;
+            String pn = c.getPackageName();
+            boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);
+            if (m1 != m0) {
+                accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);
+            }
+            return accessible;
+        }
+
         @Override
         public String toString() {
             String s = lookupClass().getSimpleName();
@@ -229,33 +305,48 @@
         public boolean 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());
+            }
 
-            // publicLookup has access to all public types/members of types in unnamed modules
-            if ((lookupModes & UNCONDITIONAL) != 0
-                && (lookupModes & PUBLIC) != 0
-                && !c2.getModule().isNamed()
-                && Modifier.isPublic(c2.getModifiers())
-                && Modifier.isPublic(m.getModifiers()))
-                return true;
+            // 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))
+                return false;
 
             LookupCase lc = this.in(c2);
-            int m1 = lc.lookupModes();
-            int m2 = fixMods(m.getModifiers());
+            int modes1 = lc.lookupModes();
+            int modes2 = fixMods(m.getModifiers());
             // allow private lookup on nestmates. Otherwise, privacy is strictly enforced
-            if (c1 != c2 && ((m2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
-                m1 &= ~PRIVATE;
+            if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
+                modes1 &= ~PRIVATE;
             }
             // protected access is sometimes allowed
-            if ((m2 & PROTECTED) != 0) {
-                int prev = m2;
-                m2 |= PACKAGE;  // it acts like a package method also
+            if ((modes2 & PROTECTED) != 0) {
+                int prev = modes2;
+                modes2 |= PACKAGE;  // it acts like a package method also
                 if ((lookupModes() & PROTECTED) != 0 &&
                     c2.isAssignableFrom(c1))
-                    m2 |= PUBLIC;  // from a subclass, it acts like a public method also
+                    modes2 |= PUBLIC;  // from a subclass, it acts like a public method also
             }
             if (verbosity >= 2)
-                System.out.format("%s willAccess %s m1=0x%h m2=0x%h => %s%n", this, lc, m1, m2, ((m2 & m1) != 0));
-            return (m2 & m1) != 0;
+                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. */
@@ -268,24 +359,36 @@
                 }
             }
 
-            // publicLookup has access to all public types/members of types in unnamed modules
-            if ((lookupModes & UNCONDITIONAL) != 0
-                && (lookupModes & PUBLIC) != 0
-                && (!c2.getModule().isNamed())
-                && Modifier.isPublic(c2.getModifiers()))
-                return true;
+            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 m1 = lc.lookupModes();
+            int modes1 = lc.lookupModes();
             boolean r = false;
-            if (m1 == 0) {
+            if (modes1 == 0) {
                 r = false;
             } else {
-                int m2 = fixMods(c2.getModifiers());
-                if ((m2 & PUBLIC) != 0) {
-                    r = true;
-                } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
-                    r = true;
+                if (Modifier.isPublic(c2.getModifiers())) {
+                    if ((modes1 & MODULE) != 0)
+                        r = true;
+                    else if ((modes1 & PUBLIC) != 0)
+                        r = m1.isExported(c2.getPackageName());
+                } else {
+                    if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
+                        r = true;
                 }
             }
             if (verbosity >= 2) {
@@ -328,7 +431,7 @@
         return i+1;
     }
 
-    private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2) {
+    private void 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)) {
@@ -337,7 +440,7 @@
             int m1 = l1.lookupModes();
             int m2 = l2.lookupModes();
             assert((m1 | m2) == m1);        // [A2] (no elevation of access)
-            LookupCase expect = l1.in(c2);
+            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);
@@ -358,9 +461,14 @@
         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);
+                    addLookupEdge(lc1, c2, lc2, 0);
                     CASES.add(lc2);
                 }
             }
@@ -386,8 +494,8 @@
         if (verbosity > 0) {
             verbosity += 9;
             Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
-            testOneAccess(lookupCase("AccessControlTest/public"),  pro_in_self, "find");
-            testOneAccess(lookupCase("Remote_subclass/public"),    pro_in_self, "find");
+            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;
         }
@@ -398,6 +506,8 @@
             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.
@@ -457,7 +567,6 @@
     }
 
     static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
-        assert targetAccess != MODULE;
         String methodName = accessName(targetAccess)+placeName(targetClass);
         if (verbosity >= 2)
             System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
@@ -491,10 +600,13 @@
         assert(false);
         return "?";
     }
-    // MODULE not a test case at this time
     private static final int[] ACCESS_CASES = {
-        PUBLIC, PACKAGE, PRIVATE, PROTECTED
+        PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL
     };
+    /*
+     * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
+     * Adjust 0 => PACKAGE
+     */
     /** Return one of the ACCESS_CASES. */
     static int fixMods(int mods) {
         mods &= (PUBLIC|PRIVATE|PROTECTED);
--- a/test/jdk/java/lang/invoke/DropLookupModeTest.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/test/jdk/java/lang/invoke/DropLookupModeTest.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -106,37 +106,39 @@
         assertTrue(lookup.lookupModes() == 0);
     }
 
-    /**
-     * Test dropLookupMode on the public Lookup.
-     */
-    public void testPublicLookup() {
-        final Lookup publicLookup = MethodHandles.publicLookup();
-        final Class<?> lc = publicLookup.lookupClass();
-        assertTrue(publicLookup.lookupModes() == (PUBLIC|UNCONDITIONAL));
-
-        Lookup lookup = publicLookup.dropLookupMode(PRIVATE);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == PUBLIC);
-
-        lookup = publicLookup.dropLookupMode(PROTECTED);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == PUBLIC);
+    @DataProvider(name = "unconditionals")
+    public Object[][] unconditionals() {
+        Lookup publicLookup = MethodHandles.publicLookup();
+        return new Object[][] {
+            { publicLookup, Object.class },
+            { publicLookup.in(String.class), String.class },
+            { publicLookup.in(DropLookupModeTest.class), DropLookupModeTest.class },
+        };
+    }
 
-        lookup = publicLookup.dropLookupMode(PACKAGE);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == PUBLIC);
-
-        lookup = publicLookup.dropLookupMode(MODULE);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == PUBLIC);
+    /**
+     * Test dropLookupMode on the lookup with public lookup
+     * and UNCONDITIONAL
+     */
+    @Test(dataProvider = "unconditionals")
+    public void testUnconditionalLookup(Lookup unconditionalLookup, Class<?> expected) {
+        assertTrue(unconditionalLookup.lookupModes() == UNCONDITIONAL);
 
-        lookup = publicLookup.dropLookupMode(PUBLIC);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == 0);
+        assertPublicLookup(unconditionalLookup.dropLookupMode(PRIVATE), expected);
+        assertPublicLookup(unconditionalLookup.dropLookupMode(PROTECTED), expected);
+        assertPublicLookup(unconditionalLookup.dropLookupMode(PACKAGE), expected);
+        assertPublicLookup(unconditionalLookup.dropLookupMode(MODULE), expected);
+        assertPublicLookup(unconditionalLookup.dropLookupMode(PUBLIC), expected);
 
-        lookup = publicLookup.dropLookupMode(UNCONDITIONAL);
-        assertTrue(lookup.lookupClass() == lc);
-        assertTrue(lookup.lookupModes() == PUBLIC);
+        // drop all access
+        Lookup lookup = unconditionalLookup.dropLookupMode(UNCONDITIONAL);
+        assertTrue(lookup.lookupClass() == expected);
+        assertTrue(lookup.lookupModes() == 0);
+    }
+
+    private void assertPublicLookup(Lookup lookup, Class<?> expected) {
+        assertTrue(lookup.lookupClass() == expected);
+        assertTrue(lookup.lookupModes() == UNCONDITIONAL);
     }
 
     @DataProvider(name = "badInput")
@@ -157,4 +159,4 @@
         MethodHandles.lookup().dropLookupMode(modeToDrop);
     }
 
-}
\ No newline at end of file
+}
--- a/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -82,6 +82,7 @@
     }
 
     // Invoke MethodHandles.privateLookupIn with a reduced-power caller
+    @Test(expectedExceptions = {IllegalAccessException.class})
     public void testReducedAccessCallerSameModule() throws Throwable {
         Lookup caller = MethodHandles.lookup().dropLookupMode(PACKAGE);
         assertTrue((caller.lookupModes() & PRIVATE) == 0);
@@ -89,12 +90,6 @@
         assertTrue((caller.lookupModes() & MODULE) != 0);
 
         Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, caller);
-        assertTrue(lookup.lookupClass() == nonPublicType);
-        assertTrue(lookup.hasPrivateAccess());
-
-        // use it
-        MethodHandle mh = lookup.findStaticGetter(nonPublicType, "obj", Object.class);
-        Object obj = mh.invokeExact();
     }
 
     // Invoke MethodHandles.privateLookupIn with the public lookup as caller
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Driver1.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8173978
+ * @build m3/* m4/* m5/* Unnamed Unnamed1
+ * @run testng/othervm m3/jdk.test.ModuleAccessTest
+ * @summary Basic test case for module access checks and Lookup.in and
+ *          MethodHandles.privateLookupIn
+ */
--- a/test/jdk/java/lang/invoke/modules/Unnamed.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/test/jdk/java/lang/invoke/modules/Unnamed.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,4 +21,11 @@
  * questions.
  */
 
-public class Unnamed { }
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class Unnamed {
+    public static Lookup lookup() {
+        return MethodHandles.lookup();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Unnamed1.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class Unnamed1 { }
--- a/test/jdk/java/lang/invoke/modules/m1/p1/Main.java	Wed Jul 24 10:22:11 2019 -0400
+++ b/test/jdk/java/lang/invoke/modules/m1/p1/Main.java	Wed Jul 24 16:46:42 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -45,7 +45,7 @@
     private Class<?> p2_Type2;        // m1, not exported
     private Class<?> q1_Type1;        // m2, exported
     private Class<?> q2_Type2;        // m2, not exported
-    private Class<?> x500NameClass;   // java.base, not exported
+    private Class<?> signalClass;     // java.base, not exported
     private Class<?> unnamedClass;    // class in unnamed module
 
     @BeforeTest
@@ -55,7 +55,7 @@
             p2_Type2 = Class.forName("p2.Type2");
             q1_Type1 = Class.forName("q1.Type1");
             q2_Type2 = Class.forName("q2.Type2");
-            x500NameClass = Class.forName("sun.security.x509.X500Name");
+            signalClass = Class.forName("jdk.internal.misc.Signal");
             unnamedClass = Class.forName("Unnamed");
         } catch (ClassNotFoundException e) {
             throw new AssertionError(e);
@@ -105,7 +105,7 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class); // [A2]
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); // [A3]
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class); // [A3]
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);  // [A3]
@@ -130,7 +130,7 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);
@@ -139,51 +139,70 @@
     /**
      * Hop to lookup class in another named module
      *
-     * [A0] has no access
+     * [A0] has PUBLIC access if accessible; otherwise no access
+     * [A1] old lookup class becomes previous lookup class
      */
     public void testFromNamedToNamedModule() throws Exception {
+        // m2/q1_Type1 is accessible to m1 whereas m2/q_Type2 is not accessible
         Lookup lookup = MethodHandles.lookup().in(q1_Type1);
-        assertTrue(lookup.lookupModes() == 0); // [A0]
+        assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
+        assertTrue(lookup.previousLookupClass() == Main.class); // [A1]
+
+        Lookup lookup2 = MethodHandles.lookup().in(q2_Type2);
+        assertTrue(lookup2.lookupModes() == 0);      // [A0]
+        assertTrue(lookup2.previousLookupClass() == Main.class); // [A1]
 
         // m1
         findConstructorExpectingIAE(lookup, p1_Type1, void.class);
         findConstructorExpectingIAE(lookup, p2_Type2, void.class);
 
+        findConstructorExpectingIAE(lookup2, p1_Type1, void.class);
+        findConstructorExpectingIAE(lookup2, p2_Type2, void.class);
+
         // m2
-        findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+        findConstructor(lookup, q1_Type1, void.class);  // m2/q1 is exported
         findConstructorExpectingIAE(lookup, q2_Type2, void.class);
 
+        findConstructorExpectingIAE(lookup2, q1_Type1, void.class);
+        findConstructorExpectingIAE(lookup2, q2_Type2, void.class);
+
         // java.base
-        findConstructorExpectingIAE(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructor(lookup, Object.class, void.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+        findConstructorExpectingIAE(lookup2, Object.class, void.class);
+        findConstructorExpectingIAE(lookup2, signalClass, void.class, String.class);
 
         // unnamed
         findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+
+        findConstructorExpectingIAE(lookup2, unnamedClass, void.class);
+
     }
 
     /**
      * Hop to lookup class in an unnamed module
      *
-     * [A0] has no access
+     * [A0] has PUBLIC access
      */
     public void testFromNamedToUnnamedModule() throws Exception {
         Lookup lookup = MethodHandles.lookup().in(unnamedClass);
-        assertTrue(lookup.lookupModes() == 0); // [A0]
+        assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
 
         // m1
-        findConstructorExpectingIAE(lookup, p1_Type1, void.class);
+        findConstructor(lookup, p1_Type1, void.class);      // p1 is exported
         findConstructorExpectingIAE(lookup, p2_Type2, void.class);
 
         // m2
-        findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+        findConstructor(lookup, q1_Type1, void.class);
         findConstructorExpectingIAE(lookup, q2_Type2, void.class);
 
         // java.base
-        findConstructorExpectingIAE(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructor(lookup, Object.class, void.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
-        findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+        findConstructor(lookup, unnamedClass, void.class);
     }
 
     /**
@@ -206,7 +225,7 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);
@@ -215,11 +234,11 @@
     /**
      * MethodHandles.publicLookup()
      *
-     * [A0] has PUBLIC|UNCONDITIONAL access
+     * [A0] has UNCONDITIONAL access
      */
     public void testPublicLookup() throws Exception {
         Lookup lookup = MethodHandles.publicLookup();
-        assertTrue(lookup.lookupModes() == (PUBLIC|UNCONDITIONAL)); // A0
+        assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
 
         // m1
         findConstructor(lookup, p1_Type1, void.class);
@@ -231,7 +250,7 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);
@@ -239,36 +258,12 @@
 
     /**
      * Hop from publicLookup to accessible type in java.base
+     *
+     * [A0] has UNCONDITIONAL access
      */
     public void testPublicLookupToBaseModule() throws Exception {
         Lookup lookup = MethodHandles.publicLookup().in(String.class);
-        assertTrue(lookup.lookupModes() == PUBLIC); // A0
-
-        // m1
-        findConstructorExpectingIAE(lookup, p1_Type1, void.class);
-        findConstructorExpectingIAE(lookup, p2_Type2, void.class);
-
-        // m2
-        findConstructorExpectingIAE(lookup, q1_Type1, void.class);
-        findConstructorExpectingIAE(lookup, q2_Type2, void.class);
-
-        // java.base
-        findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
-
-        // unnamed
-        findConstructorExpectingIAE(lookup, unnamedClass, void.class);
-    }
-
-
-    /**
-     * Hop from publicLookup to accessible type in named module.
-     *
-     * [A0] has PUBLIC access
-     */
-    public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
-        Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
-        assertTrue(lookup.lookupModes() == PUBLIC); // A0
+        assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
 
         // m1
         findConstructor(lookup, p1_Type1, void.class);
@@ -280,7 +275,33 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+        // unnamed
+        findConstructor(lookup, unnamedClass, void.class);
+    }
+
+
+    /**
+     * Hop from publicLookup to accessible type in named module.
+     *
+     * [A0] has UNCONDITIONAL access
+     */
+    public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
+        Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
+        assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
+
+        // m1
+        findConstructor(lookup, p1_Type1, void.class);
+        findConstructorExpectingIAE(lookup, p2_Type2, void.class);
+
+        // m2
+        findConstructor(lookup, q1_Type1, void.class);
+        findConstructorExpectingIAE(lookup, q2_Type2, void.class);
+
+        // java.base
+        findConstructor(lookup, Object.class, void.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);
@@ -305,7 +326,7 @@
 
         // java.base
         findConstructorExpectingIAE(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructorExpectingIAE(lookup, unnamedClass, void.class);
@@ -314,11 +335,11 @@
     /**
      * Teleport from publicLookup to public type in unnamed module
      *
-     * [A0] has PUBLIC access
+     * [A0] has UNCONDITIONAL access
      */
     public void testPublicLookupToUnnamedModule() throws Exception {
         Lookup lookup = MethodHandles.publicLookup().in(unnamedClass);
-        assertTrue(lookup.lookupModes() == PUBLIC); // A0
+        assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
 
         // m1
         findConstructor(lookup, p1_Type1, void.class);
@@ -330,7 +351,7 @@
 
         // java.base
         findConstructor(lookup, Object.class, void.class);
-        findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+        findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
 
         // unnamed
         findConstructor(lookup, unnamedClass, void.class);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C1.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package c1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class C1 {
+    public C1() { }
+
+    public static Lookup lookup() {
+        return MethodHandles.lookup();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C2.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package c1;
+
+public class C2 {
+    public C2() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c2/C3.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package c2;
+
+public class C3{
+    public C3() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/jdk/test/ModuleAccessTest.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.test;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import e1.CrackM5Access;
+
+import static java.lang.invoke.MethodHandles.Lookup.*;
+import static org.testng.Assert.*;
+
+public class ModuleAccessTest {
+    static ModuleLookup m3;
+    static ModuleLookup m4;
+    static ModuleLookup m5;
+    static Map<String, ModuleLookup> moduleLookupMap = new HashMap<>();
+    static Lookup privLookupIn;
+    static Lookup privLookupIn2;
+    static Lookup unnamedLookup;
+    static Class<?> unnamed;
+    static Class<?> unnamed1;
+
+    @BeforeTest
+    public void setup() throws Exception {
+        m3 = new ModuleLookup("m3", 'C');
+        m4 = new ModuleLookup("m4", 'D');
+        m5 = new ModuleLookup("m5", 'E');
+        moduleLookupMap.put(m3.name(), m3);
+        moduleLookupMap.put(m4.name(), m4);
+        moduleLookupMap.put(m5.name(), m5);
+
+        privLookupIn = MethodHandles.privateLookupIn(m3.type2, m3.lookup);
+        privLookupIn2 = MethodHandles.privateLookupIn(m4.type1, m3.lookup);
+
+        unnamed = Class.forName("Unnamed");
+        unnamed1 = Class.forName("Unnamed1");
+        unnamedLookup = (Lookup)unnamed.getMethod("lookup").invoke(null);
+
+        // m5 reads m3
+        CrackM5Access.addReads(m3.module);
+        CrackM5Access.addReads(unnamed.getModule());
+    }
+
+    @DataProvider(name = "samePackage")
+    public Object[][] samePackage() throws Exception {
+        return new Object[][] {
+            { m3.lookup,     m3.type2 },
+            { privLookupIn,  m3.type1 },
+            { privLookupIn2, m4.type2 },
+            { unnamedLookup, unnamed1 }
+        };
+    }
+
+    /**
+     * Test lookup.in(T) where T is in the same package of the lookup class.
+     *
+     * [A0] targetClass becomes the lookup class
+     * [A1] no change in previous lookup class
+     * [A2] PROTECTED and PRIVATE are dropped
+     */
+    @Test(dataProvider = "samePackage")
+    public void testLookupInSamePackage(Lookup lookup, Class<?> targetClass) throws Exception {
+        Class<?> lookupClass = lookup.lookupClass();
+        Lookup lookup2 = lookup.in(targetClass);
+
+        assertTrue(lookupClass.getPackage() == targetClass.getPackage());
+        assertTrue(lookupClass.getModule() == targetClass.getModule());
+        assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
+        assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());  // [A1]
+        assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));  // [A2]
+    }
+
+    @DataProvider(name = "sameModule")
+    public Object[][] sameModule() throws Exception {
+        return new Object[][] {
+            { m3.lookup,     m3.type3},
+            { privLookupIn,  m3.type3},
+            { privLookupIn2, m4.type3}
+        };
+    }
+
+    /**
+     * Test lookup.in(T) where T is in the same module but different package from the lookup class.
+     *
+     * [A0] targetClass becomes the lookup class
+     * [A1] no change in previous lookup class
+     * [A2] PROTECTED, PRIVATE and PACKAGE are dropped
+     */
+    @Test(dataProvider = "sameModule")
+    public void testLookupInSameModule(Lookup lookup, Class<?> targetClass) throws Exception {
+        Class<?> lookupClass = lookup.lookupClass();
+        Lookup lookup2 = lookup.in(targetClass);
+
+        assertTrue(lookupClass.getPackage() != targetClass.getPackage());
+        assertTrue(lookupClass.getModule() == targetClass.getModule());
+        assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
+        assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());  // [A1]
+        assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE))); // [A2]
+    }
+
+    @DataProvider(name = "anotherModule")
+    public Object[][] anotherModule() throws Exception {
+        return new Object[][] {
+            { m3.lookup, m4.type1, m5, m5.accessibleTypesTo(m3.module, m4.module) },
+            { m4.lookup, m5.type2, m3, m3.accessibleTypesTo(m4.module, m5.module) },
+            { m3.lookup, m5.type1, m4, m4.accessibleTypesTo(m3.module, m5.module) },
+            { m5.lookup, unnamed,  m3, m3.accessibleTypesTo(m5.module, unnamed.getModule()) },
+        };
+    }
+
+    /**
+     * Test lookup.in(T) where T is in a different module from the lookup class.
+     *
+     * [A0] targetClass becomes the lookup class
+     * [A1] lookup class becomes previous lookup class
+     * [A2] PROTECTED, PRIVATE, PACKAGE, and MODULE are dropped
+     * [A3] no access to module internal types in m0 and m1
+     * [A4] if m1 reads m0, can access public types in m0; otherwise no access.
+     * [A5] can access public types in m1 exported to m0
+     * [A6] can access public types in m2 exported to m0 and m1
+     */
+    @Test(dataProvider = "anotherModule")
+    public void testLookupInAnotherModule(Lookup lookup, Class<?> targetClass,
+                                          ModuleLookup m2, Set<Class<?>> otherTypes) throws Exception {
+        Class<?> lookupClass = lookup.lookupClass();
+        Module m0 = lookupClass.getModule();
+        Module m1 = targetClass.getModule();
+
+        assertTrue(m0 != m1);
+        assertTrue(m0.canRead(m1));
+        assertTrue(m1.isExported(targetClass.getPackageName(), m0));
+
+        Lookup lookup2 = lookup.in(targetClass);
+        assertTrue(lookup2.lookupClass() == targetClass);   // [A0]
+        assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());  // [A1]
+        assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));  // [A2]
+
+        // [A3] no access to module internal type in m0
+        // [A4] if m1 reads m0,
+        // [A4]   no access to public types exported from m0 unconditionally
+        // [A4]   no access to public types exported from m0
+        ModuleLookup ml0 = moduleLookupMap.get(m0.getName());
+        if (m1.canRead(m0)) {
+            for (Class<?> type : ml0.unconditionalExports()) {
+                testAccess(lookup2, type);
+            }
+            for (Class<?> type : ml0.qualifiedExportsTo(m1)) {
+                testAccess(lookup2, type);
+            }
+        } else {
+            findConstructorExpectingIAE(lookup2, ml0.type1, void.class);
+            findConstructorExpectingIAE(lookup2, ml0.type2, void.class);
+            findConstructorExpectingIAE(lookup2, ml0.type3, void.class);
+        }
+
+        // [A5] can access public types exported from m1 unconditionally
+        // [A5] can access public types exported from m1 to m0
+        if (m1.isNamed()) {
+            ModuleLookup ml1 = moduleLookupMap.get(m1.getName());
+            assertTrue(ml1.unconditionalExports().size() + ml1.qualifiedExportsTo(m0).size() > 0);
+            for (Class<?> type : ml1.unconditionalExports()) {
+                testAccess(lookup2, type);
+            }
+            for (Class<?> type : ml1.qualifiedExportsTo(m0)) {
+                testAccess(lookup2, type);
+            }
+        } else {
+            // unnamed module
+            testAccess(lookup2, unnamed1);
+        }
+
+        // [A5] can access public types exported from m2 unconditionally
+        // [A5] can access public types exported from m2 to m0 and m1
+        for (Class<?> type : otherTypes) {
+            assertTrue(type.getModule() == m2.module);
+            testAccess(lookup2, type);
+        }
+
+        // test inaccessible types
+        for (Class<?> type : Set.of(m2.type1, m2.type2, m2.type3)) {
+            if (!otherTypes.contains(type)) {
+                // type is accessible to this lookup
+                try {
+                    lookup2.accessClass(type);
+                    assertTrue(false);
+                } catch (IllegalAccessException e) {}
+
+                findConstructorExpectingIAE(lookup2, type, void.class);
+            }
+        }
+    }
+
+    public void testAccess(Lookup lookup, Class<?> type) throws Exception {
+        // type is accessible to this lookup
+        assertTrue(lookup.accessClass(type) == type);
+
+        // can find constructor
+        findConstructor(lookup, type, void.class);
+
+        Module m0 = lookup.previousLookupClass().getModule();
+        Module m1 = lookup.lookupClass().getModule();
+        Module m2 = type.getModule();
+
+        assertTrue(m0 != m1 && m0 != null);
+        assertTrue((lookup.lookupModes() & MODULE) == 0);
+        assertTrue(m0 != m2 || m1 != m2);
+
+        MethodHandles.Lookup lookup2 = lookup.in(type);
+        if (m2 == m1) {
+            // the same module of the lookup class
+            assertTrue(lookup2.lookupClass() == type);
+            assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());
+        } else if (m2 == m0) {
+            // hop back to the module of the previous lookup class
+            assertTrue(lookup2.lookupClass() == type);
+            assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+        } else {
+            // hop to a third module
+            assertTrue(lookup2.lookupClass() == type);
+            assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+            assertTrue(lookup2.lookupModes() == 0);
+        }
+    }
+
+    @DataProvider(name = "thirdModule")
+    public Object[][] thirdModule() throws Exception {
+        return new Object[][] {
+            { m3.lookup, m4.type1, m5.type1},
+            { m3.lookup, m4.type2, m5.type1},
+            { unnamedLookup, m3.type1, m4.type1 },
+        };
+    }
+
+    /**
+     * Test lookup.in(c1).in(c2) where c1 is in second module and c2 is in a third module.
+     *
+     * [A0] c2 becomes the lookup class
+     * [A1] c1 becomes previous lookup class
+     * [A2] all access bits are dropped
+     */
+    @Test(dataProvider = "thirdModule")
+    public void testLookupInThirdModule(Lookup lookup, Class<?> c1, Class<?> c2) throws Exception {
+        Class<?> c0 = lookup.lookupClass();
+        Module m0 = c0.getModule();
+        Module m1 = c1.getModule();
+        Module m2 = c2.getModule();
+
+        assertTrue(m0 != m1 && m0 != m2 && m1 != m2);
+        assertTrue(m0.canRead(m1) && m0.canRead(m2));
+        assertTrue(m1.canRead(m2));
+        assertTrue(m1.isExported(c1.getPackageName(), m0));
+        assertTrue(m2.isExported(c2.getPackageName(), m0) && m2.isExported(c2.getPackageName(), m1));
+
+        Lookup lookup1 = lookup.in(c1);
+        assertTrue(lookup1.lookupClass() == c1);
+        assertTrue(lookup1.previousLookupClass() == c0);
+        assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));
+
+        Lookup lookup2 = lookup1.in(c2);
+        assertTrue(lookup2.lookupClass() == c2);                    // [A0]
+        assertTrue(lookup2.previousLookupClass() == c1);            // [A1]
+        assertTrue(lookup2.lookupModes() == 0, lookup2.toString()); // [A2]
+    }
+
+    @DataProvider(name = "privLookupIn")
+    public Object[][] privLookupIn() throws Exception {
+        return new Object[][] {
+            { m3.lookup,  m4.type1 },
+            { m3.lookup,  m5.type1 },
+            { m4.lookup,  m5.type2 },
+            { m5.lookup,  m3.type3 },
+            { m5.lookup,  unnamed  }
+        };
+    }
+
+    /**
+     * Test privateLookupIn(T, lookup) where T is in another module
+     *
+     * [A0] full capabilities except MODULE bit
+     * [A1] target class becomes the lookup class
+     * [A2] the lookup class becomes previous lookup class
+     * [A3] IAE thrown if lookup has no MODULE access
+     */
+    @Test(dataProvider = "privLookupIn")
+    public void testPrivateLookupIn(Lookup lookup, Class<?> targetClass) throws Exception {
+        Module m0 = lookup.lookupClass().getModule();
+        Module m1 = targetClass.getModule();
+
+        // privateLookupIn from m0 to m1
+        assertTrue(m0 != m1);
+        assertTrue(m1.isOpen(targetClass.getPackageName(), m0));
+        Lookup privLookup1 = MethodHandles.privateLookupIn(targetClass, lookup);
+        assertTrue(privLookup1.lookupModes() == (PROTECTED|PRIVATE|PACKAGE|PUBLIC));  // [A0]
+        assertTrue(privLookup1.lookupClass() == targetClass);                    // [A1]
+        assertTrue(privLookup1.previousLookupClass() == lookup.lookupClass());   // [A2]
+
+        // privLookup1 has no MODULE access; can't do privateLookupIn
+        try {
+            Lookup privLookup2 = MethodHandles.privateLookupIn(targetClass, privLookup1); // [A3]
+            assertFalse(privLookup2 != null);
+        } catch (IllegalAccessException e) {}
+    }
+
+    /**
+     * Test member access from the Lookup returned from privateLookupIn
+     */
+    @Test
+    public void testPrivateLookupAccess() throws Exception {
+        Class<?> staticsClass = e1.Statics.class;
+        Lookup privLookup1 = MethodHandles.privateLookupIn(staticsClass, m4.lookup);
+        assertTrue((privLookup1.lookupModes() & MODULE) == 0);
+        assertTrue(privLookup1.lookupClass() == staticsClass);
+        assertTrue(privLookup1.previousLookupClass() == m4.lookup.lookupClass());
+
+        // access private member and default package member in m5
+        MethodType mtype = MethodType.methodType(void.class);
+        MethodHandle mh1 = privLookup1.findStatic(staticsClass, "privateMethod", mtype);
+        MethodHandle mh2 = privLookup1.findStatic(staticsClass, "packageMethod", mtype);
+
+        // access public member in exported types from m5 to m4
+        findConstructor(privLookup1, m5.type1, void.class);
+        // no access to public member in non-exported types to m5
+        findConstructorExpectingIAE(privLookup1, m5.type3, void.class);
+
+        // no access to public types in m4 since m5 does not read m4
+        assertFalse(m5.module.canRead(m4.module));
+        findConstructorExpectingIAE(privLookup1, m4.type1, void.class);
+
+        // teleport from a privateLookup to another class in the same package
+        // lose private access
+        Lookup privLookup2 = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+        Lookup lookup = privLookup2.in(staticsClass);
+        assertTrue((lookup.lookupModes() & PRIVATE) == 0);
+        MethodHandle mh3 = lookup.findStatic(staticsClass, "packageMethod", mtype);
+        try {
+            lookup.findStatic(staticsClass, "privateMethod", mtype);
+            assertTrue(false);
+        } catch (IllegalAccessException e) {}
+    }
+
+    /**
+     * Test member access from the Lookup returned from privateLookupIn and
+     * the lookup mode after dropLookupMode
+     */
+    @Test
+    public void testDropLookupMode() throws Exception {
+        Lookup lookup = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+        assertTrue((lookup.lookupModes() & MODULE) == 0);
+
+        Lookup lookup1 = lookup.dropLookupMode(PRIVATE);
+        assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));
+        Lookup lookup2 = lookup.dropLookupMode(PACKAGE);
+        assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+        Lookup lookup3 = lookup.dropLookupMode(MODULE);
+        assertTrue(lookup3.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+        Lookup lookup4 = lookup.dropLookupMode(PUBLIC);
+        assertTrue(lookup4.lookupModes() == 0);
+
+    }
+
+    /**
+     * Test no access to a public member on a non-public class
+     */
+    @Test
+    public void testPrivateLookupOnNonPublicType() throws Exception {
+        // privateLookup in a non-public type
+        Class<?> nonPUblicType = Class.forName("e1.NonPublic");
+        Lookup privLookup = MethodHandles.privateLookupIn(nonPUblicType, m4.lookup);
+        MethodType mtype = MethodType.methodType(void.class);
+        MethodHandle mh1 = privLookup.findStatic(nonPUblicType, "publicStatic", mtype);
+
+        // drop MODULE access i.e. only PUBLIC access
+        Lookup lookup = privLookup.dropLookupMode(MODULE);
+        assertTrue(lookup.lookupModes() == PUBLIC);
+        try {
+            MethodHandle mh2 = lookup.findStatic(nonPUblicType, "publicStatic", mtype);
+            assertFalse(mh2 != null);
+        } catch (IllegalAccessException e) {}
+    }
+
+    @Test
+    public void testPublicLookup() {
+        Lookup publicLookup = MethodHandles.publicLookup();
+        Lookup pub1 = publicLookup.in(m3.type1);
+        Lookup pub2 = pub1.in(java.lang.String.class);
+        Lookup pub3 = pub2.in(java.lang.management.ThreadMXBean.class);
+        Lookup pub4 = pub3.dropLookupMode(UNCONDITIONAL);
+
+        assertTrue(publicLookup.lookupClass() == Object.class);
+        assertTrue(publicLookup.lookupModes() == UNCONDITIONAL);
+        assertTrue(pub1.lookupClass() == m3.type1);
+        assertTrue(pub1.lookupModes() == UNCONDITIONAL);
+        assertTrue(pub2.lookupClass() == String.class);
+        assertTrue(pub2.lookupModes() == UNCONDITIONAL);
+        assertTrue(pub3.lookupClass() == java.lang.management.ThreadMXBean.class);
+        assertTrue(pub3.lookupModes() == UNCONDITIONAL);
+        assertTrue(pub4.lookupModes() == 0);
+
+        // publicLookup has no MODULE access; can't do privateLookupIn
+        try {
+            Lookup pub5 = MethodHandles.privateLookupIn(m4.type1, pub1);
+            assertFalse(pub5 != null);
+        } catch (IllegalAccessException e) {}
+    }
+
+    static class ModuleLookup {
+        private final Module module;
+        private final Set<String> packages;
+        private final Lookup lookup;
+        private final Class<?> type1;
+        private final Class<?> type2;
+        private final Class<?> type3;
+
+        ModuleLookup(String mn, char c) throws Exception {
+            this.module = ModuleLayer.boot().findModule(mn).orElse(null);
+            assertNotNull(this.module);
+            this.packages = module.getDescriptor().packages();
+            assertTrue(packages.size() <= 3);
+            Lookup lookup = null;
+            Class<?> type1 = null;
+            Class<?> type2 = null;
+            Class<?> type3 = null;
+            for (String pn : packages) {
+                char n = pn.charAt(pn.length() - 1);
+                switch (n) {
+                    case '1':
+                        type1 = Class.forName(pn + "." + c + "1");
+                        type2 = Class.forName(pn + "." + c + "2");
+                        Method m = type1.getMethod("lookup");
+                        lookup = (Lookup) m.invoke(null);
+                        break;
+                    case '2':
+                        type3 = Class.forName(pn + "." + c + "3");
+                        break;
+
+                    default:
+                }
+            }
+            this.lookup = lookup;
+            this.type1 = type1;
+            this.type2 = type2;
+            this.type3 = type3;
+        }
+
+        String name() {
+            return module.getName();
+        }
+
+        /*
+         * Returns the set of types that are unconditionally exported.
+         */
+        Set<Class<?>> unconditionalExports() {
+            return Stream.of(type1, type2, type3)
+                         .filter(c -> module.isExported(c.getPackageName()))
+                         .collect(Collectors.toSet());
+        }
+
+        /*
+         * Returns the set of types that are qualifiedly exported to the specified
+         * caller module
+         */
+        Set<Class<?>> qualifiedExportsTo(Module caller) {
+            if (caller.canRead(this.module)) {
+                return Stream.of(type1, type2, type3)
+                             .filter(c -> !module.isExported(c.getPackageName())
+                                          && module.isExported(c.getPackageName(), caller))
+                             .collect(Collectors.toSet());
+            } else {
+                return Set.of();
+            }
+        }
+
+        /*
+         * Returns the set of types that are qualifiedly exported to the specified
+         * caller module
+         */
+        Set<Class<?>> accessibleTypesTo(Module m0, Module m1) {
+            if (m0.canRead(this.module) && m1.canRead(this.module)) {
+                return Stream.of(type1, type2, type3)
+                             .filter(c -> module.isExported(c.getPackageName(), m0)
+                                          && module.isExported(c.getPackageName(), m1))
+                             .collect(Collectors.toSet());
+            } else {
+                return Set.of();
+            }
+        }
+
+        /*
+         * Returns the set of types that are open to the specified caller
+         * unconditionally or qualifiedly.
+         */
+        Set<Class<?>> opensTo(Module caller) {
+            if (caller.canRead(this.module)) {
+                return Stream.of(type1, type2, type3)
+                             .filter(c -> module.isOpen(c.getPackageName(), caller))
+                             .collect(Collectors.toSet());
+            } else {
+                return Set.of();
+            }
+        }
+
+        public String toString() {
+            return module.toString();
+        }
+    }
+
+    /**
+     * Invokes Lookup findConstructor with a method type constructed from the
+     * given return and parameter types, expecting IllegalAccessException to be
+     * thrown.
+     */
+    static void findConstructorExpectingIAE(Lookup lookup,
+                                            Class<?> clazz,
+                                            Class<?> rtype,
+                                            Class<?>... ptypes) throws Exception {
+        try {
+            MethodHandle mh = findConstructor(lookup, clazz, rtype, ptypes);
+            assertTrue(false);
+        } catch (IllegalAccessException expected) { }
+    }
+
+    /**
+     * Invokes Lookup findConstructor with a method type constructored from the
+     * given return and parameter types.
+     */
+    static MethodHandle findConstructor(Lookup lookup,
+                                        Class<?> clazz,
+                                        Class<?> rtype,
+                                        Class<?>... ptypes) throws Exception {
+        MethodType mt = MethodType.methodType(rtype, ptypes);
+        return lookup.findConstructor(clazz, mt);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/module-info.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m3 {
+    requires m4;
+    requires m5;
+    requires testng;
+    requires java.management;
+    exports c1;
+    opens c2 to m5;
+    exports jdk.test;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D1.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package d1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class D1 {
+    public D1() { }
+
+    public static Lookup lookup() {
+        return MethodHandles.lookup();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D2.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package d1;
+
+public class D2 {
+    public D2() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d2/D3.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package d2;
+
+public class D3 {
+    public D3() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/module-info.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m4 {
+    requires m5;
+    opens d1;
+    exports d2 to m3;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/CrackM5Access.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e1;
+
+public class CrackM5Access {
+    private static void privateMethod() { }
+
+    static void packageMethod() { }
+
+    public static void addReads(Module m) {
+        CrackM5Access.class.getModule().addReads(m);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E1.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class E1 {
+    public E1() { }
+
+    public static Lookup lookup() {
+        return MethodHandles.lookup();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E2.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e1;
+
+public class E2 {
+    public E2() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/NonPublic.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e1;
+
+class NonPublic {
+    public static void publicStatic() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/Statics.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e1;
+
+public class Statics {
+    private static void privateMethod() { }
+
+    static void packageMethod() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e2/E3.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package e2;
+
+public class E3 {
+    public E3() { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/module-info.java	Wed Jul 24 16:46:42 2019 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+module m5 {
+    exports e1 to m3;
+    opens e1 to m3, m4;
+    exports e2 to m3;
+}