< prev index next >

src/hotspot/share/oops/instanceKlass.cpp

Print this page
rev 58565 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: duke
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com

*** 29,38 **** --- 29,39 ---- #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.inline.hpp" #include "classfile/javaClasses.hpp" #include "classfile/moduleEntry.hpp" + #include "classfile/resolutionErrors.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmSymbols.hpp"
*** 132,141 **** --- 133,143 ---- #define DTRACE_CLASSINIT_PROBE(type, thread_type) #define DTRACE_CLASSINIT_PROBE_WAIT(type, thread_type, wait) #endif // ndef DTRACE_ENABLED + static inline bool is_class_loader(const Symbol* class_name, const ClassFileParser& parser) { assert(class_name != NULL, "invariant"); if (class_name == vmSymbols::java_lang_ClassLoader()) {
*** 151,162 **** } } return false; } ! // called to verify that k is a member of this nest bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const { if (_nest_members == NULL || _nest_members == Universe::the_empty_short_array()) { if (log_is_enabled(Trace, class, nestmates)) { ResourceMark rm(THREAD); log_trace(class, nestmates)("Checked nest membership of %s in non-nest-host class %s", k->external_name(), this->external_name()); --- 153,167 ---- } } return false; } ! // private: called to verify that k is a static member of this nest. ! // We know that k is an instance class in the same package and hence the ! // same classloader. bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const { + assert(!is_hidden(), "unexpected hidden class"); if (_nest_members == NULL || _nest_members == Universe::the_empty_short_array()) { if (log_is_enabled(Trace, class, nestmates)) { ResourceMark rm(THREAD); log_trace(class, nestmates)("Checked nest membership of %s in non-nest-host class %s", k->external_name(), this->external_name());
*** 173,202 **** // Check for a resolved cp entry , else fall back to a name check. // We don't want to resolve any class other than the one being checked. for (int i = 0; i < _nest_members->length(); i++) { int cp_index = _nest_members->at(i); if (_constants->tag_at(cp_index).is_klass()) { ! Klass* k2 = _constants->klass_at(cp_index, CHECK_false); if (k2 == k) { log_trace(class, nestmates)("- class is listed at nest_members[%d] => cp[%d]", i, cp_index); return true; } } else { Symbol* name = _constants->klass_name_at(cp_index); if (name == k->name()) { log_trace(class, nestmates)("- Found it at nest_members[%d] => cp[%d]", i, cp_index); ! // Names match so check actual klass - this may trigger class loading if ! // it doesn't match (though that should be impossible). But to be safe we ! // have to check for a compiler thread executing here. ! if (!THREAD->can_call_java() && !_constants->tag_at(cp_index).is_klass()) { ! log_trace(class, nestmates)("- validation required resolution in an unsuitable thread"); ! return false; ! } ! ! Klass* k2 = _constants->klass_at(cp_index, CHECK_false); if (k2 == k) { log_trace(class, nestmates)("- class is listed as a nest member"); return true; } else { --- 178,208 ---- // Check for a resolved cp entry , else fall back to a name check. // We don't want to resolve any class other than the one being checked. for (int i = 0; i < _nest_members->length(); i++) { int cp_index = _nest_members->at(i); if (_constants->tag_at(cp_index).is_klass()) { ! Klass* k2 = _constants->klass_at(cp_index, THREAD); ! assert(!HAS_PENDING_EXCEPTION || PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass()), ! "Exceptions should not be possible here"); if (k2 == k) { log_trace(class, nestmates)("- class is listed at nest_members[%d] => cp[%d]", i, cp_index); return true; } } else { Symbol* name = _constants->klass_name_at(cp_index); if (name == k->name()) { log_trace(class, nestmates)("- Found it at nest_members[%d] => cp[%d]", i, cp_index); ! // Names match so check actual klass. This may trigger class loading if ! // it doesn't match though that should be impossible as it means one classloader ! // has defined two different classes with the same name! A compiler thread won't be ! // able to perform that loading but we can't exclude the compiler threads from ! // executing this logic. But it should actually be impossible to trigger loading here. ! Klass* k2 = _constants->klass_at(cp_index, THREAD); ! assert(!HAS_PENDING_EXCEPTION || PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass()), ! "Exceptions should not be possible here"); if (k2 == k) { log_trace(class, nestmates)("- class is listed as a nest member"); return true; } else {
*** 211,381 **** log_trace(class, nestmates)("- class is NOT a nest member!"); return false; } // Return nest-host class, resolving, validating and saving it if needed. ! // In cases where this is called from a thread that can not do classloading // (such as a native JIT thread) then we simply return NULL, which in turn // causes the access check to return false. Such code will retry the access ! // from a more suitable environment later. ! InstanceKlass* InstanceKlass::nest_host(Symbol* validationException, TRAPS) { InstanceKlass* nest_host_k = _nest_host; ! if (nest_host_k == NULL) { ! // need to resolve and save our nest-host class. This could be attempted ! // concurrently but as the result is idempotent and we don't use the class ! // then we do not need any synchronization beyond what is implicitly used ! // during class loading. if (_nest_host_index != 0) { // we have a real nest_host // Before trying to resolve check if we're in a suitable context if (!THREAD->can_call_java() && !_constants->tag_at(_nest_host_index).is_klass()) { - if (log_is_enabled(Trace, class, nestmates)) { - ResourceMark rm(THREAD); log_trace(class, nestmates)("Rejected resolution of nest-host of %s in unsuitable thread", this->external_name()); ! } ! return NULL; } - if (log_is_enabled(Trace, class, nestmates)) { - ResourceMark rm(THREAD); log_trace(class, nestmates)("Resolving nest-host of %s using cp entry for %s", this->external_name(), _constants->klass_name_at(_nest_host_index)->as_C_string()); - } 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 ! ResourceMark rm(THREAD); ! char buf[200]; ! CLEAR_PENDING_EXCEPTION; ! jio_snprintf(buf, sizeof(buf), ! "Unable to load nest-host class (%s) of %s", ! _constants->klass_name_at(_nest_host_index)->as_C_string(), ! this->external_name()); ! log_trace(class, nestmates)("%s - NoClassDefFoundError", buf); ! THROW_MSG_CAUSE_NULL(vmSymbols::java_lang_NoClassDefFoundError(), buf, exc_h); ! } ! // All other exceptions pass through (OOME, StackOverflowError, LinkageErrors etc). ! return NULL; } // A valid nest-host is an instance class in the current package that lists this ! // class as a nest member. If any of these conditions are not met we post the ! // requested exception type (if any) and return NULL ! const char* error = NULL; // JVMS 5.4.4 indicates package check comes first if (is_same_class_package(k)) { - // Now check actual membership. We can't be a member if our "host" is // not an instance class. if (k->is_instance_klass()) { nest_host_k = InstanceKlass::cast(k); ! ! bool is_member = nest_host_k->has_nest_member(this, CHECK_NULL); if (is_member) { ! // save resolved nest-host value ! _nest_host = nest_host_k; - if (log_is_enabled(Trace, class, nestmates)) { - ResourceMark rm(THREAD); log_trace(class, nestmates)("Resolved nest-host of %s to %s", this->external_name(), k->external_name()); - } return nest_host_k; } } - error = "current type is not listed as a nest member"; } else { error = "types are in different packages"; } ! if (log_is_enabled(Trace, class, nestmates)) { ! ResourceMark rm(THREAD); ! log_trace(class, nestmates) ! ("Type %s (loader: %s) is not a nest member of " ! "resolved type %s (loader: %s): %s", this->external_name(), this->class_loader_data()->loader_name_and_id(), k->external_name(), k->class_loader_data()->loader_name_and_id(), error); } - - if (validationException != NULL && THREAD->can_call_java()) { - ResourceMark rm(THREAD); - Exceptions::fthrow(THREAD_AND_LOCATION, - validationException, - "Type %s (loader: %s) is not a nest member of %s (loader: %s): %s", - this->external_name(), - this->class_loader_data()->loader_name_and_id(), - k->external_name(), - k->class_loader_data()->loader_name_and_id(), - error - ); } - return NULL; } else { - if (log_is_enabled(Trace, class, nestmates)) { - ResourceMark rm(THREAD); log_trace(class, nestmates)("Type %s is not part of a nest: setting nest-host to self", this->external_name()); } ! // save resolved nest-host value return (_nest_host = this); } } ! return nest_host_k; } // check if 'this' and k are nestmates (same nest_host), or k is our nest_host, // or we are k's nest_host - all of which is covered by comparing the two ! // resolved_nest_hosts 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 ! // will also be passed through. ! Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError(); ! InstanceKlass* cur_host = nest_host(icce, CHECK_false); if (cur_host == NULL) { return false; } ! Klass* k_nest_host = k->nest_host(icce, CHECK_false); if (k_nest_host == NULL) { return false; } bool access = (cur_host == k_nest_host); - if (log_is_enabled(Trace, class, nestmates)) { ResourceMark rm(THREAD); log_trace(class, nestmates)("Class %s does %shave nestmate access to %s", this->external_name(), access ? "" : "NOT ", k->external_name()); - } - return access; } InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser, TRAPS) { const int size = InstanceKlass::size(parser.vtable_size(), parser.itable_size(), nonstatic_oop_map_size(parser.total_oop_map_count()), parser.is_interface(), parser.is_unsafe_anonymous(), ! should_store_fingerprint(parser.is_unsafe_anonymous())); const Symbol* const class_name = parser.class_name(); assert(class_name != NULL, "invariant"); ClassLoaderData* loader_data = parser.loader_data(); assert(loader_data != NULL, "invariant"); --- 217,430 ---- log_trace(class, nestmates)("- class is NOT a nest member!"); return false; } // Return nest-host class, resolving, validating and saving it if needed. ! // In cases where this is called from a thread that cannot do classloading // (such as a native JIT thread) then we simply return NULL, which in turn // causes the access check to return false. Such code will retry the access ! // from a more suitable environment later. Otherwise the _nest_host is always ! // set once this method returns. ! // Any errors from nest-host resolution must be preserved so they can be queried ! // from higher-level access checking code, and reported as part of access checking ! // exceptions. ! // VirtualMachineErrors are propagated with a NULL return. ! // Under any conditions where the _nest_host can be set to non-NULL the resulting ! // value of it and, if applicable, the nest host resolution/validation error, ! // are idempotent. ! InstanceKlass* InstanceKlass::nest_host(TRAPS) { InstanceKlass* nest_host_k = _nest_host; ! if (nest_host_k != NULL) { ! return nest_host_k; ! } ! ! ResourceMark rm(THREAD); ! ! // need to resolve and save our nest-host class. if (_nest_host_index != 0) { // we have a real nest_host // Before trying to resolve check if we're in a suitable context if (!THREAD->can_call_java() && !_constants->tag_at(_nest_host_index).is_klass()) { log_trace(class, nestmates)("Rejected resolution of nest-host of %s in unsuitable thread", this->external_name()); ! return NULL; // sentinel to say "try again from a different context" } log_trace(class, nestmates)("Resolving nest-host of %s using cp entry for %s", this->external_name(), _constants->klass_name_at(_nest_host_index)->as_C_string()); Klass* k = _constants->klass_at(_nest_host_index, THREAD); if (HAS_PENDING_EXCEPTION) { ! if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) { ! return NULL; // propagate VMEs } + stringStream ss; + char* target_host_class = _constants->klass_name_at(_nest_host_index)->as_C_string(); + ss.print("Nest host resolution of %s with host %s failed: ", + this->external_name(), target_host_class); + java_lang_Throwable::print(PENDING_EXCEPTION, &ss); + const char* msg = ss.as_string(true /* on C-heap */); + constantPoolHandle cph(THREAD, constants()); + SystemDictionary::add_nest_host_error(cph, _nest_host_index, msg); + CLEAR_PENDING_EXCEPTION; + log_trace(class, nestmates)("%s", msg); + } else { // A valid nest-host is an instance class in the current package that lists this ! // class as a nest member. If any of these conditions are not met the class is ! // its own nest-host. const char* error = NULL; // JVMS 5.4.4 indicates package check comes first if (is_same_class_package(k)) { // Now check actual membership. We can't be a member if our "host" is // not an instance class. if (k->is_instance_klass()) { nest_host_k = InstanceKlass::cast(k); ! bool is_member = nest_host_k->has_nest_member(this, THREAD); ! // exception is rare, perhaps impossible ! if (!HAS_PENDING_EXCEPTION) { if (is_member) { ! _nest_host = nest_host_k; // save resolved nest-host value log_trace(class, nestmates)("Resolved nest-host of %s to %s", this->external_name(), k->external_name()); return nest_host_k; + } else { + error = "current type is not listed as a nest member"; } + } else { + if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) { + return NULL; // propagate VMEs + } + stringStream ss; + ss.print("exception on member check: "); + java_lang_Throwable::print(PENDING_EXCEPTION, &ss); + error = ss.as_string(); + } + } else { + error = "host is not an instance class"; } } else { error = "types are in different packages"; } ! // something went wrong, so record what and log it ! { ! stringStream ss; ! ss.print("Type %s (loader: %s) is not a nest member of type %s (loader: %s): %s", this->external_name(), this->class_loader_data()->loader_name_and_id(), k->external_name(), k->class_loader_data()->loader_name_and_id(), error); + const char* msg = ss.as_string(true /* on C-heap */); + constantPoolHandle cph(THREAD, constants()); + SystemDictionary::add_nest_host_error(cph, _nest_host_index, msg); + log_trace(class, nestmates)("%s", msg); } } } else { log_trace(class, nestmates)("Type %s is not part of a nest: setting nest-host to self", this->external_name()); } ! ! // Either not in an explicit nest, or else an error occurred, so ! // the nest-host is set to `this`. Any thread that sees this assignment ! // will also see any setting of nest_host_error(), if applicable. return (_nest_host = this); + } + + // 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().defineHiddenClass(bytes_for_X, NESTMATE); + // results in: + // class_of_X.set_nest_host(lookup().lookupClass().getNestHost()) + // If it has an explicit _nest_host_index or _nest_members, these will be ignored. + // We also know the "host" is a valid nest-host in the same package so we can + // assert some of those facts. + void InstanceKlass::set_nest_host(InstanceKlass* host, TRAPS) { + assert(is_hidden(), "must be a hidden class"); + assert(host != NULL, "NULL nest host specified"); + assert(_nest_host == NULL, "current class has resolved nest-host"); + assert(nest_host_error(THREAD) == NULL, "unexpected nest host resolution error exists: %s", + nest_host_error(THREAD)); + assert((host->_nest_host == NULL && host->_nest_host_index == 0) || + (host->_nest_host == host), "proposed host is not a valid nest-host"); + // Can't assert this as package is not set yet: + // assert(is_same_class_package(host), "proposed host is in wrong package"); + + if (log_is_enabled(Trace, class, nestmates)) { + ResourceMark rm(THREAD); + const char* msg = ""; + // a hidden class does not expect a statically defined nest-host + if (_nest_host_index > 0) { + msg = "(the NestHost attribute in the current class is ignored)"; + } else if (_nest_members != NULL && _nest_members != Universe::the_empty_short_array()) { + msg = "(the NestMembers attribute in the current class is ignored)"; } + log_trace(class, nestmates)("Injected type %s into the nest of %s %s", + this->external_name(), + host->external_name(), + msg); } ! // set dynamic nest host ! _nest_host = host; ! // Record dependency to keep nest host from being unloaded before this class. ! ClassLoaderData* this_key = class_loader_data(); ! this_key->record_dependency(host); } // check if 'this' and k are nestmates (same nest_host), or k is our nest_host, // or we are k's nest_host - all of which is covered by comparing the two ! // resolved_nest_hosts. ! // Any exceptions (i.e. VMEs) are propagated. 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. ! InstanceKlass* cur_host = nest_host(CHECK_false); if (cur_host == NULL) { return false; } ! Klass* k_nest_host = k->nest_host(CHECK_false); if (k_nest_host == NULL) { return false; } bool access = (cur_host == k_nest_host); ResourceMark rm(THREAD); log_trace(class, nestmates)("Class %s does %shave nestmate access to %s", this->external_name(), access ? "" : "NOT ", k->external_name()); return access; } + const char* InstanceKlass::nest_host_error(TRAPS) { + if (_nest_host_index == 0) { + return NULL; + } else { + constantPoolHandle cph(THREAD, constants()); + return SystemDictionary::find_nest_host_error(cph, (int)_nest_host_index); + } + } + InstanceKlass* InstanceKlass::allocate_instance_klass(const ClassFileParser& parser, TRAPS) { + bool is_hidden_or_anonymous = parser.is_hidden() || parser.is_unsafe_anonymous(); const int size = InstanceKlass::size(parser.vtable_size(), parser.itable_size(), nonstatic_oop_map_size(parser.total_oop_map_count()), parser.is_interface(), parser.is_unsafe_anonymous(), ! should_store_fingerprint(is_hidden_or_anonymous)); const Symbol* const class_name = parser.class_name(); assert(class_name != NULL, "invariant"); ClassLoaderData* loader_data = parser.loader_data(); assert(loader_data != NULL, "invariant");
*** 445,454 **** --- 494,504 ---- _reference_type(parser.reference_type()) { set_vtable_length(parser.vtable_size()); set_kind(kind); set_access_flags(parser.access_flags()); + if (parser.is_hidden()) set_is_hidden(); set_is_unsafe_anonymous(parser.is_unsafe_anonymous()); set_layout_helper(Klass::instance_layout_helper(parser.layout_size(), false)); assert(NULL == _methods, "underlying memory not zeroed?");
*** 2274,2296 **** } return true; } ! bool InstanceKlass::should_store_fingerprint(bool is_unsafe_anonymous) { #if INCLUDE_AOT // We store the fingerprint into the InstanceKlass only in the following 2 cases: if (CalculateClassFingerprint) { // (1) We are running AOT to generate a shared library. return true; } if (Arguments::is_dumping_archive()) { // (2) We are running -Xshare:dump or -XX:ArchiveClassesAtExit to create a shared archive return true; } ! if (UseAOT && is_unsafe_anonymous) { ! // (3) We are using AOT code from a shared library and see an unsafe anonymous class return true; } #endif // In all other cases we might set the _misc_has_passed_fingerprint_check bit, --- 2324,2346 ---- } return true; } ! bool InstanceKlass::should_store_fingerprint(bool is_hidden_or_anonymous) { #if INCLUDE_AOT // We store the fingerprint into the InstanceKlass only in the following 2 cases: if (CalculateClassFingerprint) { // (1) We are running AOT to generate a shared library. return true; } if (Arguments::is_dumping_archive()) { // (2) We are running -Xshare:dump or -XX:ArchiveClassesAtExit to create a shared archive return true; } ! if (UseAOT && is_hidden_or_anonymous) { ! // (3) We are using AOT code from a shared library and see a hidden or unsafe anonymous class return true; } #endif // In all other cases we might set the _misc_has_passed_fingerprint_check bit,
*** 2578,2587 **** --- 2628,2638 ---- } #endif // Decrement symbol reference counts associated with the unloaded class. if (_name != NULL) _name->decrement_refcount(); + // unreference array name derived from this class name (arrays of an unloaded // class can't be referenced anymore). if (_array_name != NULL) _array_name->decrement_refcount(); FREE_C_HEAP_ARRAY(char, _source_debug_extension); }
*** 2628,2637 **** --- 2679,2697 ---- // Add the actual class name for (int src_index = 0; src_index < src_length; ) { dest[dest_index++] = src[src_index++]; } + if (is_hidden()) { // Replace the last '+' with a '.'. + for (int index = (int)src_length; index > 0; index--) { + if (dest[index] == '+') { + dest[index] = JVM_SIGNATURE_DOT; + break; + } + } + } + // If we have a hash, append it for (int hash_index = 0; hash_index < hash_len; ) { dest[dest_index++] = hash_buf[hash_index++]; }
*** 2646,2655 **** --- 2706,2734 ---- if (is_unsafe_anonymous()) { assert(unsafe_anonymous_host() != NULL, "unsafe anonymous class must have a host class"); return unsafe_anonymous_host()->module(); } + if (is_hidden() && + in_unnamed_package() && + class_loader_data()->has_class_mirror_holder()) { + // For a weak hidden class defined to an unnamed package, + // its (class held) CLD will not have an unnamed module created for it. + // Two choices to find the correct ModuleEntry: + // 1. If hidden class is within a nest, use nest host's module + // 2. Find the unnamed module off from the class loader + // For now option #2 is used since a nest host is not set until + // after the instance class is created in jvm_lookup_define_class(). + if (class_loader_data()->is_boot_class_loader_data()) { + return ClassLoaderData::the_null_class_loader_data()->unnamed_module(); + } else { + oop module = java_lang_ClassLoader::unnamedModule(class_loader_data()->class_loader()); + assert(java_lang_Module::is_instance(module), "Not an instance of java.lang.Module"); + return java_lang_Module::module_entry(module); + } + } + // Class is in a named package if (!in_unnamed_package()) { return _package_entry->module(); }
*** 2856,2866 **** Klass* ok = i_cp->klass_at(ooff, CHECK_NULL); outer_klass = InstanceKlass::cast(ok); *inner_is_member = true; } if (NULL == outer_klass) { ! // It may be unsafe anonymous; try for that. int encl_method_class_idx = enclosing_method_class_index(); if (encl_method_class_idx != 0) { Klass* ok = i_cp->klass_at(encl_method_class_idx, CHECK_NULL); outer_klass = InstanceKlass::cast(ok); *inner_is_member = false; --- 2935,2945 ---- Klass* ok = i_cp->klass_at(ooff, CHECK_NULL); outer_klass = InstanceKlass::cast(ok); *inner_is_member = true; } if (NULL == outer_klass) { ! // It may be a local or anonymous class; try for that. int encl_method_class_idx = enclosing_method_class_index(); if (encl_method_class_idx != 0) { Klass* ok = i_cp->klass_at(encl_method_class_idx, CHECK_NULL); outer_klass = InstanceKlass::cast(ok); *inner_is_member = false;
< prev index next >