--- old/src/hotspot/share/prims/jvm.cpp 2019-12-03 19:37:10.000000000 -0800 +++ new/src/hotspot/share/prims/jvm.cpp 2019-12-03 19:37:10.000000000 -0800 @@ -983,6 +983,150 @@ return (jclass) JNIHandles::make_local(env, k->java_mirror()); } +enum { + NESTMATE = java_lang_invoke_MemberName::MN_NESTMATE_CLASS, + HIDDEN_CLASS = java_lang_invoke_MemberName::MN_HIDDEN_CLASS, + WEAK_CLASS = java_lang_invoke_MemberName::MN_WEAK_CLASS, + ACCESS_VM_ANNOTATIONS = java_lang_invoke_MemberName::MN_ACCESS_VM_ANNOTATIONS +}; + +/* + * Define a class with the specified flags that indicates if it's a nestmate, + * 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, + jboolean init, int flags, jobject classData, TRAPS) { + assert(THREAD->is_Java_thread(), "must be a JavaThread"); + JavaThread* jt = (JavaThread*) THREAD; + ResourceMark rm(THREAD); + + Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(lookup)); + // Lookup class must be a non-null instance + if (k == NULL) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); + } + assert(k->is_instance_klass(), "Lookup class must be an instance klass"); + + jboolean is_nestmate = (flags & NESTMATE) == NESTMATE; + jboolean is_hidden = (flags & HIDDEN_CLASS) == HIDDEN_CLASS; + jboolean is_weak = (flags & WEAK_CLASS) == WEAK_CLASS; + jboolean vm_annotations = (flags & ACCESS_VM_ANNOTATIONS) == ACCESS_VM_ANNOTATIONS; + + InstanceKlass* host_class = NULL; + if (is_nestmate) { + host_class = InstanceKlass::cast(k)->runtime_nest_host(CHECK_NULL); + } + + if (log_is_enabled(Info, class, nestmates)) { + log_info(class, nestmates)("LookupDefineClass: %s - %s%s, %s, %s, %s", + name, + is_nestmate ? "with dynamic nest-host " : "non-nestmate", + is_nestmate ? host_class->external_name() : "", + is_hidden ? "hidden" : "not hidden", + is_weak ? "weak" : "strong", + vm_annotations ? "with vm annotations" : "without vm annotation"); + } + + if (!is_hidden) { + // classData is only applicable for hidden classes + if (classData != NULL) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "classData is only applicable for hidden classes"); + } + if (is_nestmate) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "dynamic nestmate is only applicable for hidden classes"); + } + if (is_weak) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "weak class is only applicable for hidden classes"); + } + if (vm_annotations) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for hidden classes"); + } + if (flags) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), + err_msg("flag 0x%x can only be set for hidden classes", flags)); + } + } + + + // 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; + if (name != NULL) { + const int str_len = (int)strlen(name); + if (str_len > Symbol::max_length()) { + // It's impossible to create this class; the name cannot fit + // into the constant pool. + Exceptions::fthrow(THREAD_AND_LOCATION, + vmSymbols::java_lang_NoClassDefFoundError(), + "Class name exceeds maximum length of %d: %s", + Symbol::max_length(), + name); + return 0; + } + class_name = SymbolTable::new_symbol(name, str_len); + } + + Handle class_loader (THREAD, JNIHandles::resolve(loader)); + Handle protection_domain (THREAD, JNIHandles::resolve(pd)); + const char* source = is_nestmate ? host_class->external_name() : "__JVM_LookupDefineClass__"; + ClassFileStream st((u1*)buf, len, source, ClassFileStream::verify); + + if (!is_hidden) { + k = SystemDictionary::resolve_from_stream(class_name, + class_loader, + protection_domain, + &st, + CHECK_NULL); + + if (log_is_enabled(Debug, class, resolve) && k != NULL) { + trace_class_resolution(k); + } + } else { // hidden + Handle classData_h(THREAD, JNIHandles::resolve(classData)); + ClassLoadInfo cl_info(protection_domain, + NULL, // unsafe_anonymous_host + NULL, // cp_patches + host_class, + classData_h, + is_hidden, + is_weak, + vm_annotations); + k = SystemDictionary::parse_stream(class_name, + class_loader, + &st, + cl_info, + CHECK_NULL); + if (k == NULL) { + THROW_MSG_0(vmSymbols::java_lang_Error(), "Failure to define a hidden class"); + } + + // The hidden class loader data has been artificially been kept alive to + // this point. The mirror and any instances of this class have to keep + // it alive afterwards. + InstanceKlass::cast(k)->class_loader_data()->dec_keep_alive(); + + if (is_nestmate && log_is_enabled(Debug, class, nestmates)) { + InstanceKlass* ik = InstanceKlass::cast(k); + ModuleEntry* module = ik->module(); + const char * module_name = module->is_named() ? module->name()->as_C_string() : UNNAMED_MODULE; + log_debug(class, nestmates)("Dynamic nestmate: %s/%s, nest_host %s, %s", + module_name, + ik->external_name(), + host_class->external_name(), + ik->is_hidden() ? "is hidden" : "is not hidden"); + } + } + + InstanceKlass* ik = InstanceKlass::cast(k); + if (init) { + ik->initialize(CHECK_NULL); + } else { + ik->link_class(CHECK_NULL); + } + + return (jclass) JNIHandles::make_local(env, k->java_mirror()); +} JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd)) JVMWrapper("JVM_DefineClass"); @@ -990,6 +1134,30 @@ return jvm_define_class_common(env, name, loader, buf, len, pd, NULL, THREAD); JVM_END +/* + * Define a class with the specified lookup class. + * lookup: Lookup class + * name: the name of the class + * loader: defining class loader + * buf: class bytes + * len: length of class bytes + * pd: protection domain + * init: initialize the class + * flags: properties of the class + * classData: private static pre-initialized field + */ +JVM_ENTRY(jclass, JVM_LookupDefineClass(JNIEnv *env, jclass lookup, const char *name, jobject loader, + const jbyte *buf, jsize len, jobject pd, jboolean initialize, int flags, jobject classData)) + JVMWrapper("JVM_LookupDefineClass"); + + if (lookup == NULL) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null"); + } + + assert(buf != NULL, "buf must not be NULL"); + + return jvm_lookup_define_class(env, lookup, name, loader, buf, len, pd, initialize, flags, classData, THREAD); +JVM_END JVM_ENTRY(jclass, JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len, jobject pd, const char *source)) JVMWrapper("JVM_DefineClassWithSource"); @@ -1152,6 +1320,15 @@ return result; JVM_END +JVM_ENTRY(jboolean, JVM_IsHiddenClass(JNIEnv *env, jclass cls)) + JVMWrapper("JVM_IsHiddenClass"); + oop mirror = JNIHandles::resolve_non_null(cls); + if (java_lang_Class::is_primitive(mirror)) { + return JNI_FALSE; + } + Klass* k = java_lang_Class::as_Klass(mirror); + return k->is_hidden(); +JVM_END JVM_ENTRY(jobjectArray, JVM_GetClassSigners(JNIEnv *env, jclass cls)) JVMWrapper("JVM_GetClassSigners"); @@ -1419,7 +1596,7 @@ = InstanceKlass::cast(java_lang_Class::as_Klass(JNIHandles::resolve_non_null(ofClass)) )->compute_enclosing_class(&inner_is_member, CHECK_NULL); if (outer_klass == NULL) return NULL; // already a top-level class - if (!inner_is_member) return NULL; // an anonymous class (inside a method) + if (!inner_is_member) return NULL; // a hidden or unsafe anonymous class (inside a method) return (jclass) JNIHandles::make_local(env, outer_klass->java_mirror()); } JVM_END @@ -1865,7 +2042,8 @@ ResourceMark rm(THREAD); Exceptions::fthrow(THREAD_AND_LOCATION, icce, - "Nest member %s in %s declares a different nest host of %s", + "%s.getNestMembers: Nest member %s in %s declares a different nest host of %s", + c->external_name(), k->external_name(), host->external_name(), nest_host_k->external_name() @@ -1887,7 +2065,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()); }