changeset 57332:ad257875959a nestmates

[nestmates] Implement the following rules for hidden nestmate classes - Lookup::defineHiddenClass can optionally add a hidden class to the nest of the lookup class - Hidden classes cannot participate in static nest membership as they do not have names. NestHost/NestMembers attributes on hidden classes are ignored - After validation, a class with a bad NestHost belongs to a nest consisting only of itself (i.e. fallback to pretend that the attribute was not present) - After validation of the asymmetric NH/NM attributes (or after a bad NH is thrown away), access control effects of nesting structure are applied uniformly across the nest - A hidden class belongs to one nest determined at runtime. If added as a member to the nest of a lookup class LC, then the runtime nest host of the hidden class HC has the same runtime nest host as LC: a) LC has no NH attribute, LC is the runtime nest host of HC b) LC has a valid NH attribute (H), H is the runtime nest host of HC c) LC has a bad NH attribute, LC becomes the runtime nest of LC - LC::getNestHost == HC::getNestHost and LC::getNestMembers == HC::getNestMembers
author mchung
date Mon, 07 Oct 2019 17:29:47 -0700
parents 40df6530c76b
children 603407e5775f
files make/hotspot/symbols/symbols-unix src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/include/jvm.h src/hotspot/share/oops/instanceKlass.cpp src/hotspot/share/oops/instanceKlass.hpp src/hotspot/share/prims/jvm.cpp src/java.base/share/classes/java/lang/Class.java src/java.base/share/classes/java/lang/System.java src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java src/java.base/share/classes/java/lang/invoke/MethodHandles.java src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java src/java.base/share/native/libjava/Class.c test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java test/jdk/java/lang/invoke/lambda/superProtectedMethod/SuperMethodTest.java test/jdk/java/lang/invoke/lambda/superProtectedMethod/q/I.java test/jdk/java/lang/invoke/lambda/superProtectedMethod/q/J.java test/jdk/java/lang/invoke/nestmates/Invoker.java test/jdk/java/lang/invoke/nestmates/MyThreadLocal.java test/jdk/java/lang/invoke/nestmates/NestmateExtender.java test/jdk/java/lang/invoke/nestmates/NestmateTest.java test/jdk/java/lang/invoke/nestmates/TestNestmateTeleport.java test/jdk/java/lang/invoke/nestmates/src/p/C.java test/jdk/java/lang/invoke/nestmates/src/p/D.java test/jdk/java/lang/invoke/nestmates/src/p/E.java test/jdk/java/lang/invoke/nestmates/src/p/F.java
diffstat 31 files changed, 1021 insertions(+), 428 deletions(-) [+]
line wrap: on
line diff
--- a/make/hotspot/symbols/symbols-unix	Thu Sep 26 17:32:10 2019 +0000
+++ b/make/hotspot/symbols/symbols-unix	Mon Oct 07 17:29:47 2019 -0700
@@ -144,7 +144,6 @@
 JVM_IsHiddenClass
 JVM_IsInterface
 JVM_IsInterrupted
-JVM_IsNestHost
 JVM_IsPrimitiveClass
 JVM_IsSameClassPackage
 JVM_IsSupportedJNIVersion
--- a/src/hotspot/share/classfile/classFileParser.cpp	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Mon Oct 07 17:29:47 2019 -0700
@@ -4748,17 +4748,6 @@
     );
     return;
   }
-
-  // TBD: should this be an assert() ?
-  if (is_hidden() && (is_interface || is_abstract)) {
-    ResourceMark rm(THREAD);
-    Exceptions::fthrow(
-      THREAD_AND_LOCATION,
-      vmSymbols::java_lang_ClassFormatError(),
-      "Illegal class modifiers in hidden class %s: 0x%X",
-      _class_name->as_C_string(), flags);
-    return;
-  }
 }
 
 static bool has_illegal_visibility(jint flags) {
--- a/src/hotspot/share/include/jvm.h	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/hotspot/share/include/jvm.h	Mon Oct 07 17:29:47 2019 -0700
@@ -553,9 +553,6 @@
 /* Nestmates - since JDK 11 */
 
 JNIEXPORT jboolean JNICALL
-JVM_IsNestHost(JNIEnv *env, jclass current);
-
-JNIEXPORT jboolean JNICALL
 JVM_AreNestMates(JNIEnv *env, jclass current, jclass member);
 
 JNIEXPORT jclass JNICALL
--- a/src/hotspot/share/oops/instanceKlass.cpp	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/hotspot/share/oops/instanceKlass.cpp	Mon Oct 07 17:29:47 2019 -0700
@@ -211,19 +211,22 @@
   return false;
 }
 
-bool InstanceKlass::is_nest_host() {
-  if (_nest_host_index != 0) {
-    // A member of other class's nest if NestHost attribute is present
-    // Do not do class resolution
-    return JNI_FALSE;
+InstanceKlass* InstanceKlass::runtime_nest_host(TRAPS) {
+  // TODO: nest_host returns NULL if validation fails.  Need to follow up
+  // the specification when to evaluate the runtime nest host.  Right now
+  // it's only determined when a dynamic nestmate is added.
+  InstanceKlass* nest_host_k = nest_host(NULL, CHECK_NULL);
+  if (nest_host_k == NULL) {
+    assert(_nest_host == NULL, "should fail to validate NestNost");
+    // drop the static nest information; set dynamic nest to this class
+    if (log_is_enabled(Trace, class, nestmates)) {
+      ResourceMark rm(THREAD);
+      log_trace(class, nestmates)("Fail to validate static nest host of %s: setting nest-host to self",
+                                  this->external_name());
+    }
+    _nest_host = nest_host_k = this;
   }
-
-  InstanceKlass* nest_host_k = _nest_host;
-  if (nest_host_k == NULL) {
-    // the host of its own nest
-    return JNI_TRUE;
-  }
-  return nest_host_k == this;
+  return nest_host_k;
 }
 
 // Return nest-host class, resolving, validating and saving it if needed.
@@ -259,8 +262,13 @@
       Klass* k = _constants->klass_at(_nest_host_index, THREAD);
       if (HAS_PENDING_EXCEPTION) {
         Handle exc_h = Handle(THREAD, PENDING_EXCEPTION);
-        if (exc_h->is_a(SystemDictionary::NoClassDefFoundError_klass())) {
-          // throw a new CDNFE with the original as its cause, and a clear msg
+        if (validationException == NULL && exc_h->is_a(SystemDictionary::LinkageError_klass())) {
+          // clear exception if fails to resolve the nest host class
+          CLEAR_PENDING_EXCEPTION;
+        }
+        // throw a new NCDFE with the original as its cause, and a clear msg
+        if (exc_h->is_a(SystemDictionary::NoClassDefFoundError_klass()) && validationException != NULL) {
+          // throw a new NCDFE with the original as its cause, and a clear msg
           ResourceMark rm(THREAD);
           char buf[200];
           CLEAR_PENDING_EXCEPTION;
@@ -349,7 +357,7 @@
 // Dynamic nest member support: set this class's nest host to the given class.
 // This occurs as part of the class definition, as soon as the instanceKlass
 // has been created and doesn't require further resolution. The code:
-//    lookup().defineClass(bytes_for_X, NESTMATE);
+//    lookup().defineHiddenClass(bytes_for_X, NESTMATE);
 // results in:
 //    class_of_X.set_nest_host(lookup().lookupClass().getNestHost())
 // So we know that current class is "pristine" and its _nest_host must be NULL.
@@ -397,7 +405,7 @@
 bool InstanceKlass::has_nestmate_access_to(InstanceKlass* k, TRAPS) {
 
   assert(this != k, "this should be handled by higher-level code");
-
+  
   // Per JVMS 5.4.4 we first resolve and validate the current class, then
   // the target class k. Resolution exceptions will be passed on by upper
   // layers. IncompatibleClassChangeErrors from membership validation failures
--- a/src/hotspot/share/oops/instanceKlass.hpp	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/hotspot/share/oops/instanceKlass.hpp	Mon Oct 07 17:29:47 2019 -0700
@@ -457,8 +457,9 @@
   InstanceKlass* nest_host(Symbol* validationException, TRAPS);
   // Check if this klass is a nestmate of k - resolves this nest-host and k's
   bool has_nestmate_access_to(InstanceKlass* k, TRAPS);
-  // Check if this klass is the host of a nest
-  bool is_nest_host();
+  // Returns the runtime nest host.  If static nest host is valid, set the nest host;
+  // otherwise this klass is the host of a nest; all errors are ignored
+  InstanceKlass* runtime_nest_host(TRAPS);
 
   enum InnerClassAttributeOffset {
     // From http://mirror.eng/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814
--- a/src/hotspot/share/prims/jvm.cpp	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/hotspot/share/prims/jvm.cpp	Mon Oct 07 17:29:47 2019 -0700
@@ -981,11 +981,7 @@
 
 /*
  * Define a class with the specified flags that indicates if it's a nestmate,
- * hidden, or weakly reachable from class loader.
- *
- * Same class may be defined by multiple threads at the same time.
- * Should the VM keep the classData (the one successfully defined the class)
- * as if a private static field is declared in the class?
+ * hidden, or weakly referenced from class loader.
  */
 static jclass jvm_lookup_define_class(JNIEnv *env, jclass lookup, const char *name,
                                       jobject loader, const jbyte *buf, jsize len, jobject pd,
@@ -1008,20 +1004,7 @@
 
   InstanceKlass* host_class = NULL;
   if (is_nestmate) {
-    host_class = InstanceKlass::cast(k);
-    if (!host_class->is_nest_host()) {
-      THROW_MSG_0(vmSymbols::java_lang_IllegalAccessException(), "lookup class is not the nest host");
-    }
-  }
-
-  // classData is only applicable for hidden classes
-  if (classData != NULL && !is_hidden) {
-    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "classData is only applicable for hidden classes");
-  }
-
-  // vm_annotations only allowed for hidden classes
-  if (vm_annotations && !is_hidden) {
-    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for weak hidden classes");
+    host_class = InstanceKlass::cast(k)->runtime_nest_host(CHECK_NULL);
   }
 
   if (log_is_enabled(Info, class, nestmates)) {
@@ -1034,6 +1017,16 @@
                                vm_annotations ? "with vm annotations" : "without vm annotation");
   }
 
+  // classData is only applicable for hidden classes
+  if (classData != NULL && !is_hidden) {
+    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "classData is only applicable for hidden classes");
+  }
+
+  // vm_annotations only allowed for hidden classes
+  if (vm_annotations && !is_hidden) {
+    THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for weak hidden classes");
+  }
+
   // Since exceptions can be thrown, class initialization can take place
   // if name is NULL no check for class name in .class stream has to be made.
   TempNewSymbol class_name = NULL;
@@ -1954,18 +1947,6 @@
 }
 JVM_END
 
-JVM_ENTRY(jboolean, JVM_IsNestHost(JNIEnv *env, jclass current))
-{
-  JVMWrapper("JVM_IsNestHost");
-  Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
-  if (c->is_instance_klass()) {
-    InstanceKlass* ik = InstanceKlass::cast(c);
-    return ik->is_nest_host();
-  }
-  return JNI_FALSE;
-}
-JVM_END
-
 JVM_ENTRY(jboolean, JVM_AreNestMates(JNIEnv *env, jclass current, jclass member))
 {
   JVMWrapper("JVM_AreNestMates");
@@ -2053,7 +2034,7 @@
       }
     }
     else {
-      assert(host == ck, "must be singleton nest");
+      assert(host == ck || ck->is_hidden(), "must be singleton nest or dynamic nestmate");
     }
     return (jobjectArray)JNIHandles::make_local(THREAD, result());
   }
--- a/src/java.base/share/classes/java/lang/Class.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/Class.java	Mon Oct 07 17:29:47 2019 -0700
@@ -328,10 +328,7 @@
      * @exception LinkageError if the linkage fails
      * @exception ExceptionInInitializerError if the initialization provoked
      *            by this method fails
-     * @exception ClassNotFoundException if the class cannot be located, or
-     *            the class is {@linkplain #isHiddenClass() hidden}
-     *
-     * @see Class#isHiddenClass
+     * @exception ClassNotFoundException if the class cannot be located
      */
     @CallerSensitive
     public static Class<?> forName(String className)
@@ -390,8 +387,7 @@
      * @exception ExceptionInInitializerError if the initialization provoked
      *            by this method fails
      * @exception ClassNotFoundException if the class cannot be located by
-     *            the specified class loader, or
-     *            the class is {@linkplain #isHiddenClass() hidden}
+     *            the specified class loader
      * @exception SecurityException
      *            if a security manager is present, and the {@code loader} is
      *            {@code null}, and the caller's class loader is not
@@ -404,7 +400,6 @@
      * @jls 12.2 Loading of Classes and Interfaces
      * @jls 12.3 Linking of Classes and Interfaces
      * @jls 12.4 Initialization of Classes and Interfaces
-     * @see       java.lang.Class#isHiddenClass
      * @since     1.2
      */
     @CallerSensitive
@@ -466,8 +461,7 @@
      * @param  name     The <a href="ClassLoader.html#binary-name">binary name</a>
      *                  of the class
      * @return {@code Class} object of the given name defined in the given module;
-     *         {@code null} if not found or the class is defined in
-     *         the given module but {@linkplain #isHiddenClass() hidden}
+     *         {@code null} if not found
      *
      * @throws NullPointerException if the given module or name is {@code null}
      *
@@ -485,7 +479,6 @@
      *
      * @jls 12.2 Loading of Classes and Interfaces
      * @jls 12.3 Linking of Classes and Interfaces
-     * @see       java.lang.Class#isHiddenClass
      * @since 9
      * @spec JPMS
      */
@@ -3920,8 +3913,6 @@
 
     private native Class<?> getNestHost0();
 
-    /* package-private */ native boolean isNestHost();
-
     /**
      * Returns the nest host of the <a href=#nest>nest</a> to which the class
      * or interface represented by this {@code Class} object belongs.
@@ -3935,19 +3926,17 @@
      * that the represented entity belongs to the nest consisting only of
      * itself, and is the nest host.
      *
-     * <p> If this class is a {@linkplain #isHiddenClass() hidden class}
-     * created by calling
-     * {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
-     * Lookup::defineHiddenClass} with
-     * {@link MethodHandles.Lookup.ClassOption#NESTMATE NESTMATE} class option,
-     * then this method returns the lookup class of the lookup object creating
-     * it as this hidden class was added as a member to the nest of that lookup class.
-     *
      * <p>If there is a {@linkplain LinkageError linkage error} accessing
      * the nest host, or if this class or interface is not enumerated as
      * a member of the nest by the nest host, then it is considered to belong
      * to its own nest and {@code this} is returned as the host.
      *
+     * <p>A {@linkplain Class#isHiddenClass() hidden class} can be dynamically
+     * added as a member of an existing nest of another class by calling
+     * {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
+     * Lookup::defineHiddenClass} with {@link MethodHandles.Lookup.ClassOption#NESTMATE
+     * NESTMATE} option.
+     *
      * @apiNote A {@code class} file of version 55.0 or greater may record the
      * host of the nest to which it belongs by using the {@code NestHost}
      * attribute (JVMS 4.7.28). Alternatively, a {@code class} file of
@@ -3956,6 +3945,9 @@
      * {@code NestMembers} attribute (JVMS 4.7.29).
      * A {@code class} file of version 54.0 or lower does not use these
      * attributes.
+     * A hidden class cannot attempt to claim nest membership statically
+     * as it cannot be referred by name.  The {@code NestHost} and
+     * {@code NestMembers} attributes are erroneous and therefore ignored.
      *
      * @return the nest host of this class or interface
      *
@@ -3976,18 +3968,16 @@
         if (isPrimitive() || isArray()) {
             return this;
         }
-        Class<?> host;
-        try {
+
+        Class<?> host = this.nest;
+        if (host == null) {
             host = getNestHost0();
-        } catch (LinkageError e) {
-            // if we couldn't load our nest-host then we
+            // if null then nest membership validation failed, so we
             // act as-if we have no nest-host attribute
-            return this;
-        }
-        // if null then nest membership validation failed, so we
-        // act as-if we have no nest-host attribute
-        if (host == null || host == this) {
-            return this;
+            if (host == null || host == this) {
+                return this.nest = this;
+            }
+            this.nest = host;
         }
         // returning a different class requires a security check
         SecurityManager sm = System.getSecurityManager();
@@ -3998,6 +3988,9 @@
         return host;
     }
 
+    // keep a strong reference to the nest host
+    private transient Class<?> nest;
+
     /**
      * Determines if the given {@code Class} is a nestmate of the
      * class or interface represented by this {@code Class} object.
@@ -4018,11 +4011,8 @@
             c.isPrimitive() || c.isArray()) {
             return false;
         }
-        try {
-            return getNestHost0() == c.getNestHost0();
-        } catch (LinkageError e) {
-            return false;
-        }
+
+        return getNestHost() == c.getNestHost();
     }
 
     private native Class<?>[] getNestMembers0();
--- a/src/java.base/share/classes/java/lang/System.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/System.java	Mon Oct 07 17:29:47 2019 -0700
@@ -2289,10 +2289,6 @@
                 ClassLoader.loadLibrary(caller, library, false);
             }
 
-            public boolean isNestHost(Class<?> c) {
-                return c.isNestHost();
-            }
-
             public ProtectionDomain protectionDomain(Class<?> c) {
                 return c.protectionDomain();
             }
--- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Mon Oct 07 17:29:47 2019 -0700
@@ -314,8 +314,7 @@
         }
         try {
             // this class is linked at the indy callsite; so define a hidden nestmate
-            Class<?> nestHost = caller.lookupClass().getNestHost();
-            return caller.in(nestHost).defineHiddenClass(classBytes, true, NESTMATE).lookupClass();
+            return caller.defineHiddenClass(classBytes, true, NESTMATE).lookupClass();
         } catch (IllegalAccessException e) {
             throw new LambdaConversionException("Exception defining lambda proxy class", e);
         }
--- a/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Oct 07 17:29:47 2019 -0700
@@ -44,6 +44,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Stream;
 
 import static java.lang.invoke.LambdaForm.BasicType;
@@ -314,8 +315,8 @@
      * Extract the MemberName of a newly-defined method.
      */
     private MemberName loadMethod(byte[] classFile) {
-        Class<?> invokerClass = LOOKUP.lookupDefineClass(className(), classFile,
-                HIDDEN_CLASS|WEAK_CLASS|ACCESS_VM_ANNOTATIONS, true, classDataValues());
+        Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(classFile, Set.of(ClassOption.WEAK))
+                                      .defineClass(true, classDataValues());
         return resolveInvokerMember(invokerClass, invokerName, invokerType);
     }
 
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Oct 07 17:29:47 2019 -0700
@@ -1178,7 +1178,7 @@
                  */
                 String name = targetClass.getName() + "$$InjectedInvoker";
                 Class<?> invokerClass = new Lookup(targetClass)
-                        .lookupDefineClass(name, INJECTED_INVOKER_TEMPLATE, HIDDEN_CLASS, true, null);
+                        .makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, HIDDEN_CLASS).defineClass(true);
                 assert checkInjectedInvoker(targetClass, invokerClass);
                 return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
             } catch (ReflectiveOperationException ex) {
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Oct 07 17:29:47 2019 -0700
@@ -29,8 +29,6 @@
 import jdk.internal.access.SharedSecrets;
 import jdk.internal.module.IllegalAccessLogger;
 import jdk.internal.org.objectweb.asm.ClassReader;
-import jdk.internal.org.objectweb.asm.ClassVisitor;
-import jdk.internal.org.objectweb.asm.Opcodes;
 import jdk.internal.reflect.CallerSensitive;
 import jdk.internal.reflect.Reflection;
 import jdk.internal.vm.annotation.ForceInline;
@@ -1597,35 +1595,7 @@
             if ((lookupModes() & PACKAGE) == 0)
                 throw new IllegalAccessException("Lookup does not have PACKAGE access");
             assert (lookupModes() & (MODULE|PUBLIC)) != 0;
-
-            // parse class bytes to get class name (in internal form)
-            bytes = bytes.clone();
-            String name;
-            try {
-                ClassReader reader = new ClassReader(bytes);
-                name = reader.getClassName();
-            } catch (RuntimeException e) {
-                // ASM exceptions are poorly specified
-                ClassFormatError cfe = new ClassFormatError();
-                cfe.initCause(e);
-                throw cfe;
-            }
-
-            // get package and class name in binary form
-            String cn, pn;
-            int index = name.lastIndexOf('/');
-            if (index == -1) {
-                cn = name;
-                pn = "";
-            } else {
-                cn = name.replace('/', '.');
-                pn = cn.substring(0, index);
-            }
-            if (!pn.equals(lookupClass.getPackageName())) {
-                throw new IllegalArgumentException("Class not in same package as lookup class");
-            }
-
-            return lookupDefineClass(cn, bytes, 0, false, null);
+            return makeClassDefiner(bytes.clone()).defineClass(false);
         }
 
         private void checkDefineClassPermission() {
@@ -1644,8 +1614,8 @@
          * {@linkplain java.security.ProtectionDomain protection domain} as this
          * lookup's {@linkplain #lookupClass() lookup class}.
          * The {@code options} parameter specifies the class options if the
-         * hidden class is created as a {@linkplain ClassOption#NESTMATE
-         * nestmate} of this lookup's lookup class and/or is {@linkplain ClassOption#WEAK
+         * hidden class is created as a {@linkplain ClassOption#NESTMATE nestmate}
+         * of this lookup's lookup class and/or is {@linkplain ClassOption#WEAK
          * weakly referenced} by its defining class loader.
          *
          * <p> The hidden class is initialized if the {@code initialize} parameter is
@@ -1671,6 +1641,12 @@
          *     A hidden class is not {@linkplain java.lang.instrument.Instrumentation#isModifiableClass(Class)
          *     modifiable} by Java agents or tool agents using
          *     the <a href="{@docRoot}/../specs/jvmti.html">JVM Tool Interface</a>.
+         * <li>Serialization:
+         *     The default serialization mechanism records the name of a class in
+         *     its serialized form and finds the class by name during deserialization.
+         *     A <em>serializable</em> hidden class requires a custom serialization
+         *     mechanism in order to ensure that instances are properly serialized
+         *     and deserialized.
          * </ul>
          *
          * <p> The {@linkplain #lookupModes() lookup modes} for this lookup must
@@ -1679,12 +1655,7 @@
          *
          * <p> If {@code options} has {@link ClassOption#NESTMATE NESTMATE}, then
          * this method creates the hidden class as a member of the nest of
-         * this lookup's lookup class.  The lookup class must be the host
-         * of a nest.  Together with the {@code PRIVATE} and {@code MODULE}
-         * lookup mode, it ensures that the nest host (the lookup class) authorizes
-         * the membership of the hidden class being defined.
-         * If the lookup class is a member of some other class' nest, that is
-         * not the host of a nest, then {@code IllegalAccessException} will be thrown.
+         * this lookup's lookup class.
          *
          * <p> If {@code options} has {@link ClassOption#WEAK WEAK}, then
          * the hidden class is weakly referenced from its defining class loader
@@ -1693,18 +1664,6 @@
          * <p> The {@code bytes} parameter is the class bytes of a valid class file
          * (as defined by the <em>The Java Virtual Machine Specification</em>)
          * with a class name in the same package as the lookup class.
-         * The name of a hidden class cannot be referenced in other class and
-         * therefore:
-         * <ul>
-         * <li>It must be a top-level class.</li>
-         * <li>It cannot be an abstract class or an interface.</li>
-         * <li>It cannot be an enclosing class.</li>
-         * <li>It cannot be a superclass.</li>
-         * <li>It cannot be in any static nest membership, i.e. the class bytes
-         *     must not contain the {@code NestHost}, {@code NestMembers}.</li>
-         * </ul>
-         * If any of the above checks is violated, {@code IllegalArgumentException}
-         * will be thrown.
          *
          * @param bytes the class bytes
          * @param initialize if {@code true} the class will be initialized.
@@ -1712,14 +1671,8 @@
          * @return the {@code Lookup} object on the hidden class
          *
          * @throws IllegalArgumentException the bytes are for a class in a different package
-         *                                  to the lookup class; or the bytes contain {@code NestHost},
-         *                                  {@code NestMembers}, {@code EnclosingMethod} attribute, or
-         *                                  {@code classes} entries in the {@code InnerClasses} attribute
-         *                                  indicating that this hidden class is an enclosing class
-         *                                  or a nested class
-         * @throws IllegalAccessException   if this lookup does not have {@code PRIVATE} and {@code MODULE} access;
-         *                                  or if the hidden class is defined as a {@linkplain ClassOption#NESTMATE nestmate}
-         *                                  and this lookup's lookup class is not the nest host
+         *                                  to the lookup class
+         * @throws IllegalAccessException   if this lookup does not have {@code PRIVATE} and {@code MODULE} access
          * @throws LinkageError             if the class is malformed ({@code ClassFormatError}), cannot be
          *                                  verified ({@code VerifyError}), is already defined,
          *                                  or another linkage error occurs
@@ -1743,8 +1696,7 @@
             }
 
             Set<ClassOption> opts = (options != null && options.length > 0) ? Set.of(options) : Set.of();
-            HiddenClassDefiner cfd = new HiddenClassDefiner(bytes.clone(), opts);
-            Class<?> c = cfd.defineClass(initialize, null);
+            Class<?> c =  makeHiddenClassDefiner(bytes.clone(), opts).defineClass(initialize, null);
             return new Lookup(c, null, FULL_POWER_MODES);
         }
 
@@ -1774,14 +1726,8 @@
          * @return the {@code Lookup} object on the hidden class
          *
          * @throws IllegalArgumentException the bytes are for a class in a different package
-         *                                  to the lookup class; or the bytes contain {@code NestHost},
-         *                                  {@code NestMembers}, {@code EnclosingMethod} attribute, or
-         *                                  {@code classes} entries in the {@code InnerClasses} attribute
-         *                                  indicating that this hidden class is an enclosing class
-         *                                  or a nested class
-         * @throws IllegalAccessException   if this lookup does not have {@code PRIVATE} and {@code MODULE} access;
-         *                                  or if the hidden class is defined as a {@linkplain ClassOption#NESTMATE nestmate}
-         *                                  and this lookup's lookup class is not the nest host
+         *                                  to the lookup class
+         * @throws IllegalAccessException   if this lookup does not have {@code PRIVATE} and {@code MODULE} access
          * @throws LinkageError             if the class is malformed ({@code ClassFormatError}), cannot be
          *                                  verified ({@code VerifyError}), is already defined,
          *                                  or another linkage error occurs
@@ -1807,8 +1753,7 @@
             }
 
             Set<ClassOption> opts = (options != null && options.length > 0) ? Set.of(options) : Set.of();
-            HiddenClassDefiner cfd = new HiddenClassDefiner(bytes.clone(), opts);
-            Class<?> c = cfd.defineClass(true, classData);
+            Class<?> c = makeHiddenClassDefiner(bytes.clone(), opts).defineClass(true, classData);
             return new Lookup(c, null, FULL_POWER_MODES);
         }
 
@@ -1860,43 +1805,93 @@
             return (T) MethodHandleNatives.classData(lookupClass);
         }
 
-        class HiddenClassDefiner extends ClassVisitor {
+        private ClassDefiner makeClassDefiner(byte[] bytes) {
+            return new ClassDefiner(this, bytes, Set.of(), 0);
+        }
+
+        ClassDefiner makeHiddenClassDefiner(byte[] bytes, Set<ClassOption> options) {
+            return new ClassDefiner(this, bytes, options, HIDDEN_CLASS);
+        }
+
+        /**
+         * This method is only called by MethodHandleImpl.BindCaller.makeInjectedInvoker.
+         *
+         * @param name the name of the class and the name in the class bytes is ignored.
+         * @param bytes class bytes
+         * @param flags class flags
+         * @return ClassDefiner
+         */
+        ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, int flags) {
+            return new ClassDefiner(this, name, bytes, flags | HIDDEN_CLASS);
+        }
+
+        static class ClassDefiner {
+            private final Lookup lookup;
+            private final String name;
             private final byte[] bytes;
             private final int classFlags;
-            private String name;
-
-            HiddenClassDefiner(byte[] bytes, Set<ClassOption> options) throws IllegalAccessException {
-                super(Opcodes.ASM7);
+
+            // caller should make a defensive copy of the arguments if needed
+            // before calling this constructor
+            private ClassDefiner(Lookup lookup, byte[] bytes, Set<ClassOption> options, int flags) {
+                this.lookup = lookup;
                 this.bytes = bytes;
-                this.classFlags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
-                if (options.contains(ClassOption.NESTMATE) && !JLA.isNestHost(lookupClass)) {
-                    throw new IllegalAccessException(lookupClass.getName() + " is not a nest host");
+                this.classFlags = flags | ClassOption.optionsToFlag(options);
+                this.name = className(bytes);
+
+                int index = name.lastIndexOf('.');
+                String pn = (index == -1) ? "" : name.substring(0, index);
+                if (!pn.equals(lookup.lookupClass().getPackageName())) {
+                    throw newIllegalArgumentException(name + " not in same package as lookup class: " +
+                            lookup.lookupClass().getName());
                 }
             }
 
+            // skip package name check
+            private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) {
+                this.lookup = lookup;
+                this.bytes = bytes;
+                this.classFlags = flags;
+                this.name = name;
+            }
+
+            String className() {
+                return name;
+            }
+
+            Class<?> defineClass(boolean initialize) {
+                return defineClass(initialize, null);
+            }
+
+            /**
+             * Defines the class of the given bytes and the given classData.
+             * If {@code initialize} parameter is true, then the class will be initialized.
+             *
+             * @param initialize true if the class to be initialized
+             * @param classData classData or null
+             * @return the class
+             *
+             * @throws LinkageError linkage error
+             */
             Class<?> defineClass(boolean initialize, Object classData) {
-                parseClassFile();
-                return Lookup.this.lookupDefineClass(className(), bytes, classFlags, initialize, classData);
+                assert (initialize || classData == null);  // initialize must be true if classData is non-null
+                Class<?> lookupClass = lookup.lookupClass();
+                ClassLoader loader = lookupClass.getClassLoader();
+                ProtectionDomain pd = (loader != null) ? lookup.lookupClassProtectionDomain() : null;
+                Class<?> c = JLA.defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData);
+                assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost();
+                return c;
             }
 
-            void parseClassFile() {
+            private boolean isNestmate() {
+                return (classFlags & NESTMATE_CLASS) != 0;
+            }
+
+            private static String className(byte[] bytes) {
                 try {
                     ClassReader reader = new ClassReader(bytes);
-                    this.name = reader.getClassName();
-                    int accessFlags = reader.getAccess();
-                    if (Modifier.isInterface(accessFlags) || Modifier.isAbstract(accessFlags)) {
-                        throw newIllegalArgumentException("can't define " +
-                                (Modifier.isInterface(accessFlags)
-                                    ? "a hidden interface" : "an abstract hidden class"));
-                    }
-                    // check package name
-                    int index = name.lastIndexOf('/');
-                    String cn = className();
-                    String pn = (index == -1) ? "" : cn.substring(0, index);
-                    if (!pn.equals(lookupClass.getPackageName())) {
-                        throw newIllegalArgumentException(cn + " not in same package as lookup class: " + lookupClass.getName());
-                    }
-                    reader.accept(this, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+                    String name = reader.getClassName();
+                    return name.replace('/', '.');
                 } catch (IllegalArgumentException e) {
                     throw e;
                 } catch (RuntimeException e) {
@@ -1906,64 +1901,6 @@
                     throw cfe;
                 }
             }
-
-            String className() {
-                return name.replace('/', '.');
-            }
-
-            @Override
-            public void visitNestHost(String nestHost) {
-                throw newIllegalArgumentException("can't define a hidden class with NestHost attribute");
-            }
-
-            @Override
-            public void visitNestMember(final String nestMember) {
-                throw newIllegalArgumentException("can't define a hidden class with NestMembers attribute");
-            }
-
-            @Override
-            public void visitOuterClass(String owner, String name, String desc) {
-                throw newIllegalArgumentException("can't define a hidden class with EnclosingMethod attribute");
-            }
-
-            @Override
-            public void visitInnerClass(String cn, String outerName, String innerName, int access) {
-                if (name.equals(cn)) {
-                    throw newIllegalArgumentException(name + " is a nested class");
-                }
-                if (name.equals(outerName)) {
-                    throw newIllegalArgumentException(name + " contains class members " + cn);
-                }
-                if (innerName == null) {
-                    throw newIllegalArgumentException(name + " encloses an anonymous class " + cn);
-                }
-            }
-        }
-
-        /**
-         * Defines the class of the given bytes and the given classData.
-         * If {@code initialize} parameter is true, then the class will be initialized.
-         *
-         * This method is also called by InvokerBytecodeGenerator and
-         * BindCaller.makeInjectedInvoker
-         *
-         * @param name the name of the class if it's a non-hidden class; the prefix
-         *             to be used as VM assigned class name of a hidden class
-         * @param bytes class file bytes
-         * @param flags VM flags to indicate it's a hidden, nestmate, weak class
-         *              or access to VM annotations
-         * @param initialize true if the class to be initialized
-         * @param classData classData or null
-         * @return the class
-         *
-         * @throws LinkageError linkage error
-         */
-        Class<?> lookupDefineClass(String name, byte[] bytes, int flags, boolean initialize, Object classData) {
-            assert (initialize || classData == null);  // initialize must be true if classData is non-null
-            ClassLoader loader = lookupClass.getClassLoader();
-            ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null;
-            Class<?> clazz = JLA.defineClass(loader, lookupClass, name, bytes, pd, initialize, flags, classData);
-            return clazz;
         }
 
         private ProtectionDomain lookupClassProtectionDomain() {
@@ -2287,13 +2224,11 @@
          * <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
          * @throws LinkageError if the linkage fails
          * @throws ClassNotFoundException if the class cannot be loaded by the lookup class' loader
-         *                                or the class is {@linkplain Class#isHiddenClass hidden}
          * @throws IllegalAccessException if the class is not accessible, using the allowed access modes.
          *
          * @jls 12.2 Loading of Classes and Interfaces
          * @jls 12.3 Linking of Classes and Interfaces
          * @since 9
-         * @see Class#isHiddenClass
          */
         public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
             Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader());
@@ -3609,9 +3544,10 @@
 
         /**
          * The set of class options that specify whether a hidden class created by
-         * {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...)} method
-         * is added as a nest member of a lookup class and whether it is weakly
-         * referenced by its defining class loader.
+         * {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
+         * Lookup::defineHiddenMethod} method is dynamically added as
+         * a new member to the nest of a lookup class and whether a hidden class
+         * is weakly referenced by its defining class loader.
          *
          * @since 14
          */
--- a/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/StringConcatFactory.java	Mon Oct 07 17:29:47 2019 -0700
@@ -872,6 +872,7 @@
                     null,
                     null);
 
+            // use of @ForceInline no longer has any effect
             mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
             mv.visitCode();
 
@@ -1141,8 +1142,7 @@
 
             byte[] classBytes = cw.toByteArray();
             try {
-                // Need to investigate why this class uses @ForceInline.
-                Class<?> innerClass = lookup.lookupDefineClass(className, classBytes, HIDDEN_CLASS|ACCESS_VM_ANNOTATIONS, true, null);
+                Class<?> innerClass = lookup.defineHiddenClass(classBytes,true).lookupClass();
                 dumpIfEnabled(className, classBytes);
                 return lookup.findStatic(innerClass, METHOD_NAME, args);
             } catch (Exception e) {
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java	Mon Oct 07 17:29:47 2019 -0700
@@ -331,16 +331,6 @@
     void loadLibrary(Class<?> caller, String library);
 
     /**
-     * Returns true if this class is the host of a nest.
-     *
-     * If the NestHost attribute is present in the given Class,
-     * then it is not the nest host and the class indicated by
-     * the NestHost attribute is not accessed.  This method
-     * should not throw any exception.
-     */
-    boolean isNestHost(Class<?> c);
-
-    /**
      * Get protection domain of the given Class
      */
     ProtectionDomain protectionDomain(Class<?> c);
--- a/src/java.base/share/native/libjava/Class.c	Thu Sep 26 17:32:10 2019 +0000
+++ b/src/java.base/share/native/libjava/Class.c	Mon Oct 07 17:29:47 2019 -0700
@@ -60,7 +60,6 @@
     {"setSigners",       "([" OBJ ")V",     (void *)&JVM_SetClassSigners},
     {"isArray",          "()Z",             (void *)&JVM_IsArrayClass},
     {"isHiddenClass",    "()Z",             (void *)&JVM_IsHiddenClass},
-    {"isNestHost",       "()Z",             (void *)&JVM_IsNestHost},
     {"isPrimitive",      "()Z",             (void *)&JVM_IsPrimitiveClass},
     {"getModifiers",     "()I",             (void *)&JVM_GetClassModifiers},
     {"getDeclaredFields0","(Z)[" FLD,       (void *)&JVM_GetClassDeclaredFields},
--- a/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java	Mon Oct 07 17:29:47 2019 -0700
@@ -94,16 +94,20 @@
         inject(name, Member.getLookup(), null);
     }
 
-    // Try to inject a class that is already part of another nest
+    // Try to inject a class that has the NestHost attribute.  It is
+    // erroneous to name a hidden class in the static nest membership
+    // and so NestHost attribute is ignored.
     static void test_alreadyNestMember() {
         String name = "StaticHost$StaticMember";
-        inject(name, IllegalArgumentException.class);
+        inject(name, null);
     }
 
-    // Try to inject a class that is already a nest host
+    // Try to inject a class that has the NestMembers attribute.  It is
+    // erroneous to name a hidden class in the static nest membership
+    // and so NestMembers attribute is ignored.
     static void test_alreadyNestHost() {
         String name = "StaticHost";
-        inject(name, IllegalArgumentException.class);
+        inject(name, null);
     }
 
     // Try to inject a class that is in another package
@@ -121,10 +125,9 @@
         Class<?> target = lookup.lookupClass();
         String action = "Injecting " + name + " into the nest of " +
             target.getSimpleName();
-        MethodHandles.Lookup nestLookup = lookup.in(target.getNestHost());
         try {
             byte[] bytes = getBytesForClass(name);
-            Class<?> nestmate = nestLookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+            Class<?> nestmate = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
             if (ex != null) {
                 throw new RuntimeException(action + " was expected to throw " +
                                            ex.getSimpleName());
@@ -140,10 +143,9 @@
             if (actualHost != target) {
                 System.out.print("(re-directed to target's nest-host) ");
             }
-            System.out.println("Nesthost of " + nestmate.getSimpleName() +
-                               " is " + actualHost.getSimpleName());
-        }
-        catch (Throwable t) {
+            System.out.println("Nesthost of " + nestmate.getName() +
+                               " is " + actualHost.getName());
+        } catch (Throwable t) {
             if (t.getClass() == ex) {
                 System.out.println("Ok: " + action + " got expected exception: " +
                                    t.getClass().getSimpleName() + ":" +
--- a/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java	Mon Oct 07 17:29:47 2019 -0700
@@ -35,10 +35,12 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.invoke.MethodHandles.Lookup;
 import static java.lang.invoke.MethodHandles.lookup;
 
 import java.lang.reflect.Array;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -46,15 +48,14 @@
 import java.util.stream.Stream;
 
 import jdk.test.lib.compiler.CompilerUtils;
-
 import jdk.test.lib.Utils;
 
 import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 import static org.testng.Assert.*;
 
-/* package-private */ interface HiddenTest {
-  void test();
+interface HiddenTest {
+    void test();
 }
 
 public class BasicTest {
@@ -65,9 +66,11 @@
 
     @BeforeTest
     static void setup() throws IOException {
-        if (!CompilerUtils.compile(SRC_DIR, CLASSES_DIR, false, "-cp", Utils.TEST_CLASSES)) {
-            throw new RuntimeException("Compilation of the test failed");
-        }
+        compileSources(SRC_DIR, CLASSES_DIR);
+
+        // compile with --release 10 with no NestHost and NestMembers attribute
+        compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
+        compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
     }
 
     static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
@@ -80,15 +83,14 @@
         }
     }
 
-    static byte[] readClassFile(String classFileName) throws IOException {
-        return Files.readAllBytes(CLASSES_DIR.resolve(classFileName));
+    static Class<?> defineHiddenClass(String name) throws Exception {
+        byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
+        Class<?> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+        assertHiddenClass(hc);
+        return hc;
     }
 
-    static Class<?> defineHiddenClass(String name) throws Exception {
-        byte[] bytes = readClassFile(name + ".class");
-        return lookup().defineHiddenClass(bytes, false).lookupClass();
-    }
-
+    // basic test on a hidden class
     @Test
     public void hiddenClass() throws Throwable {
         HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
@@ -111,7 +113,7 @@
     }
 
     @Test
-    public void testIsHiddenClass() {
+    public void primitiveClass() {
         assertFalse(int.class.isHiddenClass());
         assertFalse(String.class.isHiddenClass());
     }
@@ -138,6 +140,7 @@
         m.setAccessible(true);
     }
 
+    // define a hidden class that uses lambda whic
     @Test
     public void testLambda() throws Throwable {
         HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
@@ -151,6 +154,22 @@
     }
 
     @Test
+    public void testHiddenNestHost() throws Throwable {
+        byte[] hc1 = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
+        Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
+        byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
+        Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, Lookup.ClassOption.NESTMATE);
+        Class<?> host = lookup1.lookupClass();
+        Class<?> member = lookup2.lookupClass();
+        assertTrue(host.getNestHost() == host);
+        assertTrue(member.getNestHost() == host.getNestHost());
+        assertTrue(host.isNestmateOf(member));
+        assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers()));
+        assertTrue(host.getNestMembers().length == 1);
+        assertTrue(host.getNestMembers()[0] == host);
+    }
+
+    @Test
     public void hiddenCantReflect() throws Throwable {
         HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
         t.test();
@@ -171,15 +190,21 @@
         }
     }
 
-    @Test(expectedExceptions = IllegalArgumentException.class)
+    @Test
     public void hiddenInterface() throws Exception {
-        defineHiddenClass("HiddenInterface");
+        Class<?> hc = defineHiddenClass("HiddenInterface");
+        assertTrue(hc.isInterface());
     }
 
+    @Test
+    public void abstractHiddenClass() throws Exception {
+        Class<?> hc = defineHiddenClass("AbstractClass");
+        assertTrue(Modifier.isAbstract(hc.getModifiers()));
+    }
 
-    @Test(expectedExceptions = IllegalArgumentException.class)
-    public void abstractHiddenClass() throws Exception {
-        defineHiddenClass("AbstractClass");
+    @Test
+    public void hiddenOuterClass() throws Throwable {
+        defineHiddenClass("Outer");
     }
 
     @Test(expectedExceptions = NoClassDefFoundError.class)
@@ -188,55 +213,56 @@
     }
 
     @Test
-    public void hiddenNestmates() throws Throwable {
+    public void hasStaticNestMembership() throws Exception {
+        byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
+        Class<?> c = lookup().defineHiddenClass(bytes, false).lookupClass();
+        declaringClassNotFound(c, "Outer");
+    }
+
+    @Test
+    public void hasInnerClassesAttribute() throws Throwable {
+        byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer.class"));
+        Class<?> c = lookup().defineHiddenClass(bytes, false).lookupClass();
+        assertTrue(c.getSimpleName().startsWith("Outer"));
+
+        bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer$Inner.class"));
+        c = lookup().defineHiddenClass(bytes, false).lookupClass();
+        declaringClassNotFound(c, "Outer");
+    }
+
+    @Test
+    public void anonymousClass() throws Throwable {
+        byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass.class"));
+        Class<?> c = lookup().defineHiddenClass(bytes, false).lookupClass();
+        assertTrue(c.getSimpleName().startsWith("EnclosingClass"));
+
+        bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass$1.class"));
+        c = lookup().defineHiddenClass(bytes, false).lookupClass();
+        declaringClassNotFound(c, "EnclosingClass");
+    }
+
+    private void declaringClassNotFound(Class<?> c, String cn) {
         try {
-            defineHiddenClass("Outer");
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("NestMembers attribute")) throw e;
-        }
-
-        try {
-            defineHiddenClass("Outer$Inner");
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("NestHost attribute")) throw e;
+            // fail to find declaring/enclosing class
+            c.getSimpleName();
+            assertTrue(false);
+        } catch (NoClassDefFoundError e) {
+            if (!e.getMessage().equals(cn)) {
+                throw e;
+            }
         }
     }
 
-    @Test
-    public void hiddenNestedClass() throws Throwable {
-        // compile with --release 10 with no NestHost and NestMembers attribute
-        compileSources(SRC_DIR.resolve("Outer.java"), CLASSES_10_DIR, "--release", "10");
-        try {
-            byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer.class"));
-            lookup().defineHiddenClass(bytes, false);
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("Outer$Inner")) throw e;
-        }
-
-        try {
-            byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("Outer$Inner.class"));
-            lookup().defineHiddenClass(bytes, false);
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("Outer$Inner")) throw e;
-        }
-    }
-
-    @Test
-    public void hiddenAnonymous() throws Throwable {
-        // compile with --release 10 with no NestHost and NestMembers attribute
-        compileSources(SRC_DIR.resolve("EnclosingClass.java"), CLASSES_10_DIR, "--release", "10");
-        try {
-            byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass.class"));
-            lookup().defineHiddenClass(bytes, false);
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("EnclosingClass$1")) throw e;
-        }
-
-        try {
-            byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve("EnclosingClass$1.class"));
-            lookup().defineHiddenClass(bytes, false);
-        } catch (IllegalArgumentException e) {
-            if (!e.getMessage().contains("EnclosingMethod attribute")) throw e;
-        }
+    private static void assertHiddenClass(Class<?> hc) {
+        assertTrue(hc.isHiddenClass());
+        assertTrue(hc.getNestHost() == hc);
+        assertTrue(hc.getNestMembers().length == 1);
+        assertTrue(hc.getNestMembers()[0] == hc);
+        assertTrue(hc.getCanonicalName() == null);
+        assertTrue(hc.getName().contains("/"));  // implementation-specific
+        assertFalse(hc.isAnonymousClass());
+        assertFalse(hc.isLocalClass());
+        assertFalse(hc.isMemberClass());
+        assertFalse(hc.getSimpleName().isEmpty()); // sanity check
     }
 }
--- a/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java	Mon Oct 07 17:29:47 2019 -0700
@@ -34,6 +34,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.stream.Stream;
+import java.util.Arrays;
 
 import jdk.internal.org.objectweb.asm.*;
 import org.testng.annotations.Test;
@@ -59,6 +60,7 @@
         // hidden nestmate is not listed in the return array of getNestMembers
         assertTrue(Stream.of(nestHost.getNestMembers()).noneMatch(k -> k == hiddenClass));
         assertTrue(hiddenClass.isNestmateOf(lookup.lookupClass()));
+        assertTrue(Arrays.equals(hiddenClass.getNestMembers(), nestHost.getNestMembers()));
     }
 
     @Test
@@ -117,12 +119,11 @@
         lookup.defineHiddenClass(bytes, false, NESTMATE);
     }
 
-    @Test(expectedExceptions = IllegalAccessException.class)
     public void teleportToNestmate() throws Throwable {
         Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
         assertNestmate(lookup);
 
-        // Teleport to a nestmate
+        // Teleport to a hidden nestmate
         Lookup lc =  MethodHandles.lookup().in(lookup.lookupClass());
         assertTrue((lc.lookupModes() & PRIVATE) != 0);
         Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
--- a/test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java	Mon Oct 07 17:29:47 2019 -0700
@@ -33,7 +33,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.lang.invoke.LambdaConversionException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.file.Files;
@@ -54,18 +53,30 @@
     private static final String DIR = "missingOuter";
     public static class Inner implements Runnable {
         // generate lambda proxy class
-        private Runnable lambda1 = () -> {
-        };
+        private Runnable lambda1 = this::doit;
+
         @Override
         public void run() {
-            Runnable r = lambda1;
-            r.run();
             // validate the lambda proxy class
-            Class<?> lambdaProxyClass = r.getClass();
+            Class<?> lambdaProxyClass = lambda1.getClass();
             assertTrue(lambdaProxyClass.isHiddenClass());
-            System.out.println(lambdaProxyClass.getNestHost() + " vs " + this.getClass());
+            System.out.format("%s nest host %s nestmate of Inner class %s%n",
+                    lambdaProxyClass, lambdaProxyClass.getNestHost(),
+                    lambdaProxyClass.isNestmateOf(Inner.class));
+            assertTrue(lambdaProxyClass.getNestHost() == Inner.class.getNestHost());
+            assertTrue(Arrays.equals(lambdaProxyClass.getNestMembers(), Inner.class.getNestMembers()));
             assertTrue(lambdaProxyClass.isNestmateOf(Inner.class));
-            assertTrue(lambdaProxyClass.getNestHost() == Inner.class.getNestHost());
+            lambda1.run();
+        }
+
+        // testng may not be visible to this class
+        private static void assertTrue(boolean x) {
+            if (!x) {
+                throw new AssertionError("expected true but found false");
+            }
+        }
+
+        private void doit() {
         }
     }
 
@@ -92,9 +103,6 @@
         runnable.run();
     }
 
-    /*
-     * Test NoClassDefFoundError thrown if the true nest host is not found.
-     */
     @Test
     public void nestHostNotExist() throws Exception {
         URL[] urls = new URL[] { Paths.get(DIR).toUri().toURL() };
@@ -103,18 +111,9 @@
         assertTrue(inner.getClassLoader() == loader);
         assertTrue(inner.getNestHost() == inner);   // linkage error ignored
 
-        try {
-            Runnable runnable = (Runnable) inner.newInstance();
-            assertTrue(false);
-        } catch (BootstrapMethodError e) {
-            lambdaConversionFailed(e);
-        }
-    }
-
-    private static void lambdaConversionFailed(BootstrapMethodError bme) {
-        assertTrue(bme.getCause() instanceof LambdaConversionException);
-        IllegalAccessException iae = (IllegalAccessException)bme.getCause().getCause();
-        assertTrue(iae.getMessage().equals("p.LambdaNestedInnerTest$Inner is not a nest host"));
+        Runnable runnable = (Runnable) inner.newInstance();
+        // this validates the lambda proxy class
+        runnable.run();
     }
 
     /*
@@ -130,12 +129,9 @@
         assertTrue(inner.getClassLoader() == loader);
         assertTrue(inner.getNestHost() == inner);   // linkage error ignored.
 
-        try {
-            Runnable runnable = (Runnable) inner.newInstance();
-            assertTrue(false);
-        } catch (BootstrapMethodError e) {
-            lambdaConversionFailed(e);
-        }
+        Runnable runnable = (Runnable) inner.newInstance();
+        // this validates the lambda proxy class
+        runnable.run();
     }
 
     static class TestLoader extends URLClassLoader {
@@ -152,5 +148,3 @@
         }
     }
 }
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/lambda/superProtectedMethod/SuperMethodTest.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,66 @@
+/*
+ * 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 8227415
+ * @run main p.SuperMethodTest
+ * @summary method reference to a protected method inherited from its
+ *          superclass in a different package must be accessed via
+ *          a bridge method.  Lambda proxy class has no access to it.
+ */
+
+package p;
+
+import q.I;
+import q.J;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.function.Function;
+
+public class SuperMethodTest  {
+    public static void main(String... args) {
+        Sub_I sub = new Sub_I();
+        sub.test(Paths.get("test"));
+    }
+
+    public static class Sub_J extends J {
+        Sub_J(Function<Path,String> function) {
+            super(function);
+        }
+    }
+
+    public static class Sub_I extends I {
+        public void test(Path path) {
+            /*
+             * The method reference to an inherited protected method
+             * in another package is desugared with REF_invokeVirtual on
+             * a bridge method to invoke protected q.I::filename method
+             */
+            Sub_J c = new Sub_J(this::filename);
+            c.check(path);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/lambda/superProtectedMethod/q/I.java	Mon Oct 07 17:29:47 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.
+ */
+
+package q;
+
+import java.nio.file.Path;
+
+public class I {
+    protected String filename(Path file) {
+        return file.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/lambda/superProtectedMethod/q/J.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,39 @@
+/*
+ * 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 q;
+
+import java.nio.file.Path;
+import java.util.function.Function;
+
+public class J {
+    protected final Function<Path,String> fileReader;
+
+    public J(Function<Path,String> fileReader) {
+        this.fileReader = fileReader;
+    }
+
+    public void check(Path file) {
+        fileReader.apply(file);
+    }
+}
--- a/test/jdk/java/lang/invoke/nestmates/Invoker.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/Invoker.java	Mon Oct 07 17:29:47 2019 -0700
@@ -24,6 +24,7 @@
 import java.lang.invoke.*;
 import static java.lang.invoke.MethodType.*;
 
+// this serves as a trampoline class to invoke a method handle
 public class Invoker implements Runnable {
     static final MethodHandle MH;
     static {
--- a/test/jdk/java/lang/invoke/nestmates/MyThreadLocal.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/MyThreadLocal.java	Mon Oct 07 17:29:47 2019 -0700
@@ -108,7 +108,7 @@
                                     MyThreadLocal.class);
     }
 
-    static class Inner2 {
+    static class SuperMethodHandleFactory {
         // dynamic version of superInitInner with MH, and no bridge
         static MethodHandle superInitInnerMH() throws ReflectiveOperationException {
             Lookup L = lookup();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/NestmateExtender.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,132 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 p;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Set;
+
+import jdk.internal.org.objectweb.asm.*;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+public class NestmateExtender {
+
+    // the input stream to read the original module-info.class
+    private final InputStream in;
+
+    // the value of the ModuleMainClass attribute
+    private String host;
+
+    // the value for the ModuleTarget attribute
+    private Set<String> members;
+
+
+    private NestmateExtender(InputStream in) {
+        this.in = in;
+    }
+
+    /**
+     * Sets the nest host in the NestHost attribute
+     */
+    public NestmateExtender nestHost(String host) {
+        this.host = host;
+        return this;
+    }
+
+    /**
+     * Sets the nest members in the NestMembers attribute
+     */
+    public NestmateExtender nestMembers(Set<String> members) {
+        this.members = members;
+        return this;
+    }
+
+    /**
+     * Outputs the modified class file to the given output stream.
+     * Once this method has been called then the NestmateExtender object should
+     * be discarded.
+     */
+    public void write(OutputStream out) throws IOException {
+        // emit to the output stream
+        out.write(toByteArray());
+    }
+
+    /**
+     * Returns the bytes of the modified class file.
+     * Once this method has been called then the NestmateExtender object should
+     * be discarded.
+     */
+    public byte[] toByteArray() throws IOException {
+        if (host != null && members != null) {
+            throw new IllegalStateException("cannot set both the nest host and members");
+        }
+        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
+                                         + ClassWriter.COMPUTE_FRAMES);
+
+        ClassReader cr = new ClassReader(in);
+        ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
+            @Override
+            public void visitNestHost(final String nestHost) {
+                throw new IllegalArgumentException("should not have NestHost attribute: " + nestHost);
+            }
+            @Override
+            public void visitNestMember(final String nestMember) {
+                throw new IllegalArgumentException("should not have NestMembers attribute: " + nestMember);
+            }
+            @Override
+            public MethodVisitor visitMethod(final int access,
+                                             final String name,
+                                             final String descriptor,
+                                             final String signature,
+                                             final String[] exceptions) {
+                int modifiers = access;
+                if (name.equals("name") || name.equals("lookup")) {
+                    modifiers = (access & ~ACC_PUBLIC) | ACC_PRIVATE;
+                }
+                return super.visitMethod(modifiers, name, descriptor, signature, exceptions);
+            }
+        };
+        cr.accept(cv, 0);
+
+        // add NestHost or NestMembers attributes
+        if (host != null) {
+            cw.visitNestHost(host);
+        }
+
+        if (members != null) {
+            for (String name : members) {
+                cw.visitNestMember(name);
+            }
+        }
+
+        return cw.toByteArray();
+    }
+
+    public static NestmateExtender newExtender(InputStream in) {
+        return new NestmateExtender(in);
+    }
+}
--- a/test/jdk/java/lang/invoke/nestmates/NestmateTest.java	Thu Sep 26 17:32:10 2019 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/NestmateTest.java	Mon Oct 07 17:29:47 2019 -0700
@@ -44,78 +44,104 @@
 // See also http://mail.openjdk.java.net/pipermail/valhalla-spec-experts/2016-January/000071.html
 public class NestmateTest {
     /*
-     * Teleporting from a lookup to a nestmate produces a full-power lookup
+     * 1) This lookup is not in the same nest of MyThreadLocal
+     * 2) Teleport to a member of MyThreadLocal's nest will drop PRIVATE access
      */
-    private static void assertThreadLocalNestmate(Class<?> member) throws Throwable {
-        Lookup lookup = MyThreadLocal.lookup();
-        Lookup lookup2 = lookup.in(member);
-        assertTrue(lookup.lookupClass().isNestmateOf(member));
-        assertTrue(lookup2.hasPrivateAccess());
+    @Test
+    public void test() throws Throwable {
+        Class<?> c = MyThreadLocal.SuperMethodHandleFactory.class;
 
-        // can invoke a private method
-        MethodHandle mh = lookup2.findStatic(MyThreadLocal.class, "testNestmateAccess", methodType(void.class));
-        mh.invokeExact();
+        Lookup L1 = lookup();
+        assertFalse(L1.lookupClass().isNestmateOf(MyThreadLocal.class));
+        assertTrue(MyThreadLocal.class.isNestmateOf(c));
 
-        try {
-            // no access to teleport to a protected method in its super class in a different package
-            lookup2.findSpecial(ThreadLocal.class, "initialValue",
-                                methodType(Object.class),
-                                MyThreadLocal.class);
-            assertTrue(false);
-        } catch (IllegalAccessException e) {}
-
-        // cross-package teleport
-        Lookup l = lookup2.in(ThreadLocal.class);
-        assertTrue(l.lookupModes() == PUBLIC);
+        Lookup L2 = L1.in(c);
+        assertTrue(L2.lookupClass() == c);
+        assertTrue((L2.lookupModes() & PRIVATE) == 0);
     }
 
     @Test
-    public void test() throws Throwable {
-        // not a nestmate of this class
-        Lookup L = lookup().in(MyThreadLocal.Inner.class);
-        assertTrue(L.lookupClass() == MyThreadLocal.Inner.class);
-        assertTrue((L.lookupModes() & PRIVATE) == 0);
+    public void intraNestTeleport() throws Throwable {
+        Class<?> host = MyThreadLocal.class;
+        Class<?> member = MyThreadLocal.SuperMethodHandleFactory.class;
+        Lookup L1 = MyThreadLocal.lookup();         // Lookup on MyThreadLocal
+        Lookup L2 = L1.in(member);                  // teleport to a member
+        assertTrue(L2.lookupClass().isNestmateOf(L1.lookupClass()));
+        assertTrue(L2.hasPrivateAccess());          // full-power lookup
 
-        // nestmate of MyThreadLocal
-        assertThreadLocalNestmate(MyThreadLocal.Inner.class);
+        // access to a private method of its nest host
+        MethodHandle mh = L2.findStatic(host, "testNestmateAccess", methodType(void.class));
+        mh.invokeExact();
+
+        // L1 has access to a protected method in its superclass in a different package
+        // because host (specialCaller) must be identical to the lookup class of Lookup object
+        assertTrue(L1.lookupClass() == host);
+        L1.findSpecial(ThreadLocal.class, "initialValue",
+                       methodType(Object.class),
+                       host);
+
+        try {
+            // L2 has no access to a protected method in its superclass in a different package
+            // as L2's lookup class != host
+            assertFalse(L2.lookupClass() == host);
+            L2.findSpecial(ThreadLocal.class, "initialValue",
+                           methodType(Object.class),
+                           host);
+            assertTrue(false);
+        } catch (IllegalAccessException e) {
+            e.printStackTrace();
+        }
+
+        // cross-package teleport: has only PUBLIC access
+        Lookup L3 = L2.in(ThreadLocal.class);
+        assertTrue(L3.lookupModes() == PUBLIC);
     }
 
-    // dynamic invocation of super::initialValue
+    /*
+     * Dynamic invocation of super::initialValue method handle.
+     */
     @Test
-    public void superInitMethodHandle() throws Throwable {
+    public void invokeSuperInitMethodHandle() throws Throwable {
         MyThreadLocal tl = new MyThreadLocal();
         MethodHandle mh = tl.superInitMH();
         mh = mh.bindTo(tl);
         assertTrue(mh.invokeExact() == null);
     }
 
-    // dynamic invocation of super::initialValue from a nestmate class
+    /*
+     * Dynamic invocation of super::initialValue from a nestmate class
+     */
     @Test
-    public void superInitInnerMethodHandle() throws Throwable {
-        MyThreadLocal tl = new MyThreadLocal();
-        MethodHandle mh = MyThreadLocal.Inner2.superInitInnerMH();
-        invokeMH(tl, mh);
+    public void invokeFromHiddenClass() throws Throwable {
+        MethodHandle mh = MyThreadLocal.SuperMethodHandleFactory.superInitInnerMH();
+        invokeMH(new MyThreadLocal(), mh);
     }
 
     /*
      * Test the dynamic invocation of ThreadLocal::initialValue where
      * a bridge method may not exist.
      *
-     * Spins a hidden nestmate class that invokes ThreadLocal::initialValue.
+     * ThreadLocal::initialValue can only be produced by calling findSpecial
+     * on the Lookup on MyThreadLocal with the specialCaller == MyThreadLocal.
+     *
+     * Spins a hidden nestmate class that invokes a given method handle
+     * to invoke ThreadLocal::initialValue on a given ThreadLocal instance.
+     *
      * It's a nestmate of MyThreadLocal which may or may not have the
      * bridge method.  Even it has the bridge method, a library may not
      * know about the synthetic method name to invoke.
      */
     private static void invokeMH(ThreadLocal tl, MethodHandle mh) throws Throwable {
+        // define a hidden class with a given MethodHandle
         Path path = Paths.get(System.getProperty("test.classes"), "Invoker.class");
         byte[] bytes = Files.readAllBytes(path);
         Lookup lookup = MyThreadLocal.lookup()
                 .defineHiddenClassWithClassData(bytes, mh.bindTo(tl), ClassOption.NESTMATE);
         Class<?> hiddenClass = lookup.lookupClass();
         assertTrue(hiddenClass.isHiddenClass());
-        assertThreadLocalNestmate(hiddenClass);
 
-        // run it
+        // ThreadLocal::initialValue can only be produced by calling findSpecial
+        // on the Lookup on MyThreadLocal with the specialCaller == MyThreadLocal
         MethodHandle ctor = lookup.findConstructor(lookup.lookupClass(), methodType(void.class));
         ctor = ctor.asType(methodType(Runnable.class));
         Runnable r = (Runnable)ctor.invokeExact();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/TestNestmateTeleport.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,162 @@
+/*
+ * 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
+ * @library /test/lib
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ * @build jdk.test.lib.Utils
+ *        jdk.test.lib.compiler.CompilerUtils
+ *        p.NestmateExtender p.TestNestmateTeleport
+ * @run main/othervm p.TestNestmateTeleport
+ */
+
+package p;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import jdk.test.lib.compiler.CompilerUtils;
+import jdk.test.lib.Utils;
+
+import static java.lang.invoke.MethodHandles.lookup;
+
+/*
+ * Class C, D, E are in the same nest.  No InnerClasses attribute.
+ *
+ * C and D and E can access private members of C and D and E symbolically
+ * C is a subclass of ThreadLocal and C does not have a bridge method.
+ *
+ * A full power lookup on C can access private members of C and D and E,
+ * i.e. equivalent to the bytecode behavior
+ *
+ * E can access C symbolically but it does not have access to
+ * the protected method C::initialValue which is inherited from
+ * another package (ThreadLocal::initialValue).
+ *
+ * A full-power lookup on C can findSpecial to get the method handle
+ * for ThreadLocal::initialValue with specialCaller == C.class
+ * (specialCaller must be identical to the lookup class).
+ */
+public class TestNestmateTeleport {
+    private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
+    private static final Path CLASSES = Paths.get("classes");
+    private static final Path DEST = Paths.get("dest");
+
+    public static void main(String... args) throws Exception {
+        Files.createDirectories(CLASSES);
+        Files.createDirectories(DEST.resolve("p"));
+        compileSources(SRC_DIR, CLASSES);
+
+        // add NestHost and NestMembers attribute to class file of C, D, E
+        // change the name() method from public to private
+        String host = "p/C";
+        Set<String> members = Set.of("p/D", "p/E");
+        Class<?> c = lookup().defineClass(addNestMembers(CLASSES.resolve("p/C.class"), DEST.resolve("p/C.class"), members));
+        Class<?> d = lookup().defineClass(addNestHost(CLASSES.resolve("p/D.class"), DEST.resolve("p/D.class"), host));
+        Class<?> e = lookup().defineClass(addNestHost(CLASSES.resolve("p/E.class"), DEST.resolve("p/E.class"), host));
+
+        checkClass(c, "C", c);
+        checkClass(d, "D", c);
+        checkClass(e, "E", c);
+
+        // C::test invokes private D::name and E::name method
+        Method m1 = c.getMethod("test");
+        m1.invoke(null);
+
+        // E::test invokes private C::accessD method and
+        // C::initialValue protected method inherited from java.lang.ThreadLocal
+        Method m2 = e.getMethod("test");
+        m2.invoke(null);
+
+        Class<?> f = lookup().defineClass(Files.readAllBytes(CLASSES.resolve("p/F.class")));
+        Class<?> fInner = lookup().defineClass(Files.readAllBytes(CLASSES.resolve("p/F$Inner.class")));
+        // Inner::test invokes private F::name method and
+        // F::initialValue protected method inherited from java.lang.ThreadLocal
+        Method m3 = fInner.getMethod("test");
+        m3.invoke(null);
+    }
+
+    static void checkClass(Class<?> c, String expected, Class<?> host) throws Exception {
+        if (Class.forName(c.getName()) != c) {
+            throw new RuntimeException("mismatched " + c);
+        }
+        Class<?> h = c.getNestHost();
+        if (h != host) {
+            throw new RuntimeException("mismatched host " + h.getName() + " expected " + host.getName());
+        }
+
+        Method m = c.getDeclaredMethod("name");
+        if (!Modifier.isPrivate(m.getModifiers())) {
+            throw new RuntimeException("expected private method: " + m);
+        }
+
+        m.setAccessible(true);
+        String name = (String)m.invoke(c.newInstance());
+        if (!expected.equals(name)) {
+            throw new RuntimeException("expected " + expected + " got " + name);
+        }
+    }
+
+    static void compileSources(Path sourceFile, Path dest, String... options) throws IOException {
+        Stream<String> ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES);
+        if (options != null && options.length > 0) {
+            ops = Stream.concat(ops, Arrays.stream(options));
+        }
+        if (!CompilerUtils.compile(sourceFile, dest, ops.toArray(String[]::new))) {
+            throw new RuntimeException("Compilation of the test failed: " + sourceFile);
+        }
+    }
+
+    private static byte[] addNestHost(Path source, Path dest, String host) throws IOException {
+        try (InputStream in = Files.newInputStream(source);
+             OutputStream out = Files.newOutputStream(dest)) {
+            NestmateExtender extender = NestmateExtender.newExtender(in);
+            extender.nestHost(host);
+            byte[] bytes = extender.toByteArray();
+            out.write(bytes);
+            return bytes;
+        }
+    }
+
+    private static byte[] addNestMembers(Path source, Path dest, Set<String> members) throws IOException {
+        try (InputStream in = Files.newInputStream(source);
+             OutputStream out = Files.newOutputStream(dest)) {
+            NestmateExtender extender = NestmateExtender.newExtender(in);
+            extender.nestMembers(members);
+            byte[] bytes = extender.toByteArray();
+            out.write(bytes);
+            return bytes;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/src/p/C.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,62 @@
+/*
+ * 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 p;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class C extends ThreadLocal {
+    public static Lookup lookup() {
+        return MethodHandles.lookup();
+    }
+
+    public String name() {
+        return "C";
+    }
+
+    public static void test() {
+        accessD();
+        accessE();
+    }
+
+    private static void accessD() {
+        D d = new D();
+        System.out.println("invoking D.name() = " + d.name());
+        if (!d.name().equals("D")) {
+            throw new AssertionError("unexpected " + d.name());
+        }
+    }
+
+    private static void accessE() {
+        E e = new E();
+        System.out.println("invoking E.name() = " + e.name());
+        if (!e.name().equals("E")) {
+            throw new AssertionError("unexpected " + e.name());
+        }
+    }
+
+    public static void main(String... args) {
+        test();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/src/p/D.java	Mon Oct 07 17:29:47 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 p;
+public class D {
+    public String name() {
+        return "D";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/src/p/E.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,61 @@
+/*
+ * 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 p;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class E {
+    public String name() {
+        return "E";
+    }
+    public static void test() throws Throwable {
+        assertTrue(E.class.isNestmateOf(C.class));
+
+        // E is a nestmate of C and E's lookup can find private member of C
+        Lookup lookup = MethodHandles.lookup();
+        MethodHandle mh1 = lookup.findStatic(C.class, "accessD", methodType(void.class));
+        mh1.invokeExact();
+
+        // should it allow Lookup::in teleporting to a nest member and give full-power lookup?
+        // calling findSpecial: lookupClass must be identical to specialCaller i.e. C
+        lookup = lookup.in(C.class);
+        assertTrue(lookup.hasPrivateAccess());
+        MethodHandle mh2 = lookup.findSpecial(ThreadLocal.class, "initialValue",
+                                              methodType(Object.class),
+                                              C.class);
+        C c = new C();
+        System.out.println("invoking protected ThreadLocal::initialValue");
+        Object o = mh2.invokeExact(c);
+    }
+
+    static void assertTrue(boolean v) {
+        if (!v) {
+            throw new AssertionError("expected true but got " + v);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/nestmates/src/p/F.java	Mon Oct 07 17:29:47 2019 -0700
@@ -0,0 +1,74 @@
+/*
+ * 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 p;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import static java.lang.invoke.MethodType.methodType;
+
+public class F extends ThreadLocal {
+    private String name() {
+        return "F";
+    }
+
+    public static class Inner {
+        public static void test() throws Throwable {
+            // Inner is a nestmate of F
+            assertTrue(Inner.class.isNestmateOf(F.class));
+
+            F f = new F();
+            // Inner is a nestmate of F and this lookup can find private member of F
+            Lookup lookup = MethodHandles.lookup();
+            MethodHandle mh1 = lookup.findVirtual(F.class, "name", methodType(String.class));
+            String n = (String)mh1.invokeExact(f);
+            assertTrue(n.equals("F"));
+
+            // Lookup::in teleporting to a class member and give full-power lookup
+            // calling findSpecial: lookupClass must be identical to specialCaller i.e. F
+            lookup = lookup.in(F.class);
+            assertTrue(lookup.hasPrivateAccess());
+            MethodHandle mh2 = lookup.findSpecial(ThreadLocal.class, "initialValue",
+                                                  methodType(Object.class),
+                                                  F.class);
+            System.out.println("invoking protected ThreadLocal::initialValue");
+            Object o = mh2.invokeExact(f);
+        }
+    }
+
+    public static void test() throws Throwable {
+        Inner.test();
+    }
+
+    static void assertTrue(boolean v) {
+        if (!v) {
+            throw new AssertionError("expected true but got " + v);
+        }
+    }
+
+    public static void main(String... args) throws Throwable {
+        test();
+    }
+}