diff --git a/make/data/jdwp/jdwp.spec b/make/data/jdwp/jdwp.spec
index 258493edf97..a3377b61a09 100644
--- a/make/data/jdwp/jdwp.spec
+++ b/make/data/jdwp/jdwp.spec
@@ -70,7 +70,8 @@ JDWP "Java(tm) Debug Wire Protocol"
)
(Command AllClasses=3
"Returns reference types for all classes currently loaded by the "
- "target VM."
+ "target VM. "
+ "See JVM TI GetLoadedClasses."
(Out
)
(Reply
@@ -600,14 +601,9 @@ JDWP "Java(tm) Debug Wire Protocol"
(CommandSet ReferenceType=2
(Command Signature=1
- "Returns the JNI signature of a reference type. "
- "JNI signature formats are described in the "
- "Java Native Interface Specification"
- "
- "For primitive classes "
- "the returned signature is the signature of the corresponding primitive "
- "type; for example, \"I\" is returned as the signature of the class "
- "represented by java.lang.Integer.TYPE."
+ "Returns the type signature of a reference type. "
+ "Type signature formats are the same as specified in "
+ "JVM TI GetClassSignature."
(Out
(referenceType refType "The reference type ID.")
)
@@ -2266,11 +2262,12 @@ JDWP "Java(tm) Debug Wire Protocol"
)
(CommandSet ClassLoaderReference=14
(Command VisibleClasses=1
- "Returns a list of all classes which this class loader has "
- "been requested to load. This class loader is considered to be "
- "an initiating class loader for each class in the returned "
- "list. The list contains each "
- "reference type defined by this loader and any types for which "
+ "Returns a list of all classes which this class loader can find "
+ "by name via ClassLoader::loadClass, "
+ "Class::forName and bytecode linkage. That is, "
+ "all classes for which this class loader has been recorded as an "
+ "initiating loader. The list contains each "
+ "reference type created by this loader and any types for which "
"loading was delegated by this class loader to another class loader. "
"
"
"The visible class list has useful properties with respect to "
@@ -2280,6 +2277,8 @@ JDWP "Java(tm) Debug Wire Protocol"
"this class loader must be resolved to that single type. "
"
"
"No ordering of the returned list is guaranteed. "
+ "
"
+ "See JVM TI GetClassLoaderClasses. "
(Out
(classLoaderObject classLoaderObject "The class loader object ID. ")
)
diff --git a/make/hotspot/symbols/symbols-unix b/make/hotspot/symbols/symbols-unix
index 29efcc5b55f..50a16fafa68 100644
--- a/make/hotspot/symbols/symbols-unix
+++ b/make/hotspot/symbols/symbols-unix
@@ -142,6 +142,7 @@ JVM_Interrupt
JVM_InvokeMethod
JVM_IsArrayClass
JVM_IsConstructorIx
+JVM_IsHiddenClass
JVM_IsInterface
JVM_IsPrimitiveClass
JVM_IsRecord
@@ -151,6 +152,7 @@ JVM_IsThreadAlive
JVM_IsVMGeneratedMethodIx
JVM_LatestUserDefinedLoader
JVM_LoadLibrary
+JVM_LookupDefineClass
JVM_MaxMemory
JVM_MaxObjectInspectionAge
JVM_MonitorNotify
diff --git a/src/hotspot/share/aot/aotCodeHeap.cpp b/src/hotspot/share/aot/aotCodeHeap.cpp
index d0badea0a63..3b7676db715 100644
--- a/src/hotspot/share/aot/aotCodeHeap.cpp
+++ b/src/hotspot/share/aot/aotCodeHeap.cpp
@@ -1049,7 +1049,7 @@ bool AOTCodeHeap::reconcile_dynamic_klass(AOTCompiledMethod *caller, InstanceKla
InstanceKlass* dyno = InstanceKlass::cast(dyno_klass);
- if (!dyno->is_unsafe_anonymous()) {
+ if (!dyno->is_hidden() && !dyno->is_unsafe_anonymous()) {
if (_klasses_got[dyno_data->_got_index] != dyno) {
// compile-time class different from runtime class, fail and deoptimize
sweep_dependent_methods(holder_data);
diff --git a/src/hotspot/share/aot/aotLoader.cpp b/src/hotspot/share/aot/aotLoader.cpp
index d11b3cca955..63640e2080c 100644
--- a/src/hotspot/share/aot/aotLoader.cpp
+++ b/src/hotspot/share/aot/aotLoader.cpp
@@ -43,7 +43,7 @@ GrowableArray* AOTLoader::_libraries = new(ResourceObj::C_HEAP, mtCode)
#define FOR_ALL_AOT_LIBRARIES(lib) for (GrowableArrayIterator lib = libraries()->begin(); lib != libraries()->end(); ++lib)
void AOTLoader::load_for_klass(InstanceKlass* ik, Thread* thread) {
- if (ik->is_unsafe_anonymous()) {
+ if (ik->is_hidden() || ik->is_unsafe_anonymous()) {
// don't even bother
return;
}
@@ -58,7 +58,7 @@ void AOTLoader::load_for_klass(InstanceKlass* ik, Thread* thread) {
uint64_t AOTLoader::get_saved_fingerprint(InstanceKlass* ik) {
assert(UseAOT, "called only when AOT is enabled");
- if (ik->is_unsafe_anonymous()) {
+ if (ik->is_hidden() || ik->is_unsafe_anonymous()) {
// don't even bother
return 0;
}
diff --git a/src/hotspot/share/ci/ciField.cpp b/src/hotspot/share/ci/ciField.cpp
index 5c03b15f218..06fec63b020 100644
--- a/src/hotspot/share/ci/ciField.cpp
+++ b/src/hotspot/share/ci/ciField.cpp
@@ -223,9 +223,10 @@ static bool trust_final_non_static_fields(ciInstanceKlass* holder) {
holder->is_in_package("jdk/internal/foreign") || holder->is_in_package("jdk/incubator/foreign") ||
holder->is_in_package("java/lang"))
return true;
- // Trust VM unsafe anonymous classes. They are private API (jdk.internal.misc.Unsafe)
- // and can't be serialized, so there is no hacking of finals going on with them.
- if (holder->is_unsafe_anonymous())
+ // Trust hidden classes and VM unsafe anonymous classes. They are created via
+ // Lookup.defineHiddenClass or the private jdk.internal.misc.Unsafe API and
+ // can't be serialized, so there is no hacking of finals going on with them.
+ if (holder->is_hidden() || holder->is_unsafe_anonymous())
return true;
// Trust final fields in all boxed classes
if (holder->is_box_klass())
diff --git a/src/hotspot/share/ci/ciInstanceKlass.cpp b/src/hotspot/share/ci/ciInstanceKlass.cpp
index 26d8bd490ba..7412b6b5fe7 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.cpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.cpp
@@ -63,6 +63,7 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) :
_has_nonstatic_fields = ik->has_nonstatic_fields();
_has_nonstatic_concrete_methods = ik->has_nonstatic_concrete_methods();
_is_unsafe_anonymous = ik->is_unsafe_anonymous();
+ _is_hidden = ik->is_hidden();
_nonstatic_fields = NULL; // initialized lazily by compute_nonstatic_fields:
_has_injected_fields = -1;
_implementor = NULL; // we will fill these lazily
@@ -73,13 +74,13 @@ ciInstanceKlass::ciInstanceKlass(Klass* k) :
// InstanceKlass are created for both weak and strong metadata. Ensuring this metadata
// alive covers the cases where there are weak roots without performance cost.
oop holder = ik->klass_holder();
- if (ik->is_unsafe_anonymous()) {
+ if (ik->class_loader_data()->has_class_mirror_holder()) {
// Though ciInstanceKlass records class loader oop, it's not enough to keep
- // VM unsafe anonymous classes alive (loader == NULL). Klass holder should
+ // non-strong hidden classes and VM unsafe anonymous classes alive (loader == NULL). Klass holder should
// be used instead. It is enough to record a ciObject, since cached elements are never removed
// during ciObjectFactory lifetime. ciObjectFactory itself is created for
// every compilation and lives for the whole duration of the compilation.
- assert(holder != NULL, "holder of unsafe anonymous class is the mirror which is never null");
+ assert(holder != NULL, "holder of hidden or unsafe anonymous class is the mirror which is never null");
(void)CURRENT_ENV->get_object(holder);
}
@@ -123,6 +124,7 @@ ciInstanceKlass::ciInstanceKlass(ciSymbol* name,
_nonstatic_fields = NULL;
_has_injected_fields = -1;
_is_unsafe_anonymous = false;
+ _is_hidden = false;
_loader = loader;
_protection_domain = protection_domain;
_is_shared = false;
diff --git a/src/hotspot/share/ci/ciInstanceKlass.hpp b/src/hotspot/share/ci/ciInstanceKlass.hpp
index 09f2d520ace..9ecc3da6e03 100644
--- a/src/hotspot/share/ci/ciInstanceKlass.hpp
+++ b/src/hotspot/share/ci/ciInstanceKlass.hpp
@@ -56,6 +56,7 @@ private:
bool _has_nonstatic_fields;
bool _has_nonstatic_concrete_methods;
bool _is_unsafe_anonymous;
+ bool _is_hidden;
ciFlags _flags;
jint _nonstatic_field_size;
@@ -191,10 +192,14 @@ public:
return _has_nonstatic_concrete_methods;
}
- bool is_unsafe_anonymous() {
+ bool is_unsafe_anonymous() const {
return _is_unsafe_anonymous;
}
+ bool is_hidden() const {
+ return _is_hidden;
+ }
+
ciInstanceKlass* get_canonical_holder(int offset);
ciField* get_field_by_offset(int field_offset, bool is_static);
ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static);
diff --git a/src/hotspot/share/classfile/classFileParser.cpp b/src/hotspot/share/classfile/classFileParser.cpp
index 1092035733c..5388b4e4a96 100644
--- a/src/hotspot/share/classfile/classFileParser.cpp
+++ b/src/hotspot/share/classfile/classFileParser.cpp
@@ -1092,7 +1092,7 @@ public:
assert((int)_annotation_LIMIT <= (int)sizeof(_annotations_present) * BitsPerByte, "");
}
// If this annotation name has an ID, report it (or _none).
- ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name);
+ ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name, bool can_access_vm_annotations);
// Set the annotation name:
void set_annotation(ID id) {
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob");
@@ -1225,6 +1225,7 @@ static void parse_annotations(const ConstantPool* const cp,
const u1* buffer, int limit,
AnnotationCollector* coll,
ClassLoaderData* loader_data,
+ const bool can_access_vm_annotations,
TRAPS) {
assert(cp != NULL, "invariant");
@@ -1270,7 +1271,7 @@ static void parse_annotations(const ConstantPool* const cp,
}
// Here is where parsing particular annotations will take place.
- AnnotationCollector::ID id = coll->annotation_index(loader_data, aname);
+ AnnotationCollector::ID id = coll->annotation_index(loader_data, aname, can_access_vm_annotations);
if (AnnotationCollector::_unknown == id) continue;
coll->set_annotation(id);
@@ -1396,6 +1397,7 @@ void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs,
runtime_visible_annotations_length,
parsed_annotations,
_loader_data,
+ _can_access_vm_annotations,
CHECK);
cfs->skip_u1_fast(runtime_visible_annotations_length);
} else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
@@ -2059,12 +2061,13 @@ void ClassFileParser::throwIllegalSignature(const char* type,
AnnotationCollector::ID
AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
- const Symbol* name) {
+ const Symbol* name,
+ const bool can_access_vm_annotations) {
const vmSymbols::SID sid = vmSymbols::find_sid(name);
// Privileged code can use all annotations. Other code silently drops some.
- const bool privileged = loader_data->is_the_null_class_loader_data() ||
+ const bool privileged = loader_data->is_boot_class_loader_data() ||
loader_data->is_platform_class_loader_data() ||
- loader_data->is_unsafe_anonymous();
+ can_access_vm_annotations;
switch (sid) {
case vmSymbols::VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): {
if (_location != _in_method) break; // only allow for methods
@@ -2671,6 +2674,7 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
runtime_visible_annotations_length,
&parsed_annotations,
_loader_data,
+ _can_access_vm_annotations,
CHECK_NULL);
cfs->skip_u1_fast(runtime_visible_annotations_length);
} else if (method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
@@ -2865,6 +2869,10 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
if (parsed_annotations.has_any_annotations())
parsed_annotations.apply_to(methodHandle(THREAD, m));
+ if (is_hidden()) { // Mark methods in hidden classes as 'hidden'.
+ m->set_hidden(true);
+ }
+
// Copy annotations
copy_method_annotations(m->constMethod(),
runtime_visible_annotations,
@@ -3596,6 +3604,7 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
runtime_visible_annotations_length,
parsed_annotations,
_loader_data,
+ _can_access_vm_annotations,
CHECK);
cfs->skip_u1_fast(runtime_visible_annotations_length);
} else if (tag == vmSymbols::tag_runtime_invisible_annotations()) {
@@ -5592,7 +5601,9 @@ static void check_methods_for_intrinsics(const InstanceKlass* ik,
}
}
-InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook, TRAPS) {
+InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
+ const ClassInstanceInfo& cl_inst_info,
+ TRAPS) {
if (_klass != NULL) {
return _klass;
}
@@ -5600,7 +5611,11 @@ InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
InstanceKlass* const ik =
InstanceKlass::allocate_instance_klass(*this, CHECK_NULL);
- fill_instance_klass(ik, changed_by_loadhook, CHECK_NULL);
+ if (is_hidden()) {
+ mangle_hidden_class_name(ik);
+ }
+
+ fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
assert(_klass == ik, "invariant");
@@ -5626,7 +5641,10 @@ InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook,
return ik;
}
-void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loadhook, TRAPS) {
+void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
+ bool changed_by_loadhook,
+ const ClassInstanceInfo& cl_inst_info,
+ TRAPS) {
assert(ik != NULL, "invariant");
// Set name and CLD before adding to CLD
@@ -5662,6 +5680,11 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
// the parser onto the InstanceKlass*
apply_parsed_class_metadata(ik, _java_fields_count, CHECK);
+ // can only set dynamic nest-host after static nest information is set
+ if (cl_inst_info.dynamic_nest_host() != NULL) {
+ ik->set_nest_host(cl_inst_info.dynamic_nest_host(), THREAD);
+ }
+
// note that is not safe to use the fields in the parser from this point on
assert(NULL == _cp, "invariant");
assert(NULL == _fields, "invariant");
@@ -5686,11 +5709,11 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
ik->set_this_class_index(_this_class_index);
- if (is_unsafe_anonymous()) {
+ if (_is_hidden || is_unsafe_anonymous()) {
// _this_class_index is a CONSTANT_Class entry that refers to this
- // anonymous class itself. If this class needs to refer to its own methods or
- // fields, it would use a CONSTANT_MethodRef, etc, which would reference
- // _this_class_index. However, because this class is anonymous (it's
+ // hidden or anonymous class itself. If this class needs to refer to its own
+ // methods or fields, it would use a CONSTANT_MethodRef, etc, which would reference
+ // _this_class_index. However, because this class is hidden or anonymous (it's
// not stored in SystemDictionary), _this_class_index cannot be resolved
// with ConstantPool::klass_at_impl, which does a SystemDictionary lookup.
// Therefore, we must eagerly resolve _this_class_index now.
@@ -5706,6 +5729,9 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
assert (ik->is_unsafe_anonymous(), "should be the same");
ik->set_unsafe_anonymous_host(_unsafe_anonymous_host);
}
+ if (_is_hidden) {
+ ik->set_is_hidden();
+ }
// Set PackageEntry for this_klass
oop cl = ik->class_loader();
@@ -5785,6 +5811,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik, bool changed_by_loa
Handle(THREAD, _loader_data->class_loader()),
module_handle,
_protection_domain,
+ cl_inst_info.class_data(),
CHECK);
assert(_all_mirandas != NULL, "invariant");
@@ -5869,7 +5896,6 @@ void ClassFileParser::update_class_name(Symbol* new_class_name) {
_class_name->increment_refcount();
}
-
// For an unsafe anonymous class that is in the unnamed package, move it to its host class's
// package by prepending its host class's package name to its class name and setting
// its _class_name field.
@@ -5922,8 +5948,8 @@ void ClassFileParser::fix_unsafe_anonymous_class_name(TRAPS) {
}
static bool relax_format_check_for(ClassLoaderData* loader_data) {
- bool trusted = (loader_data->is_the_null_class_loader_data() ||
- SystemDictionary::is_platform_class_loader(loader_data->class_loader()));
+ bool trusted = loader_data->is_boot_class_loader_data() ||
+ loader_data->is_platform_class_loader_data();
bool need_verify =
// verifyAll
(BytecodeVerificationLocal && BytecodeVerificationRemote) ||
@@ -5935,17 +5961,16 @@ static bool relax_format_check_for(ClassLoaderData* loader_data) {
ClassFileParser::ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
- Handle protection_domain,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo* cl_info,
Publicity pub_level,
TRAPS) :
_stream(stream),
- _requested_name(name),
_class_name(NULL),
_loader_data(loader_data),
- _unsafe_anonymous_host(unsafe_anonymous_host),
- _cp_patches(cp_patches),
+ _unsafe_anonymous_host(cl_info->unsafe_anonymous_host()),
+ _cp_patches(cl_info->cp_patches()),
+ _is_hidden(cl_info->is_hidden()),
+ _can_access_vm_annotations(cl_info->can_access_vm_annotations()),
_num_patched_klasses(0),
_max_num_patched_klasses(0),
_orig_cp_size(0),
@@ -5976,7 +6001,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
_itable_size(0),
_num_miranda_methods(0),
_rt(REF_NONE),
- _protection_domain(protection_domain),
+ _protection_domain(cl_info->protection_domain()),
_access_flags(),
_pub_level(pub_level),
_bad_constant_seen(0),
@@ -6179,10 +6204,15 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
cp_size, CHECK);
_orig_cp_size = cp_size;
- if (int(cp_size) + _max_num_patched_klasses > 0xffff) {
- THROW_MSG(vmSymbols::java_lang_InternalError(), "not enough space for patched classes");
+ if (is_hidden()) { // Add a slot for hidden class name.
+ assert(_max_num_patched_klasses == 0, "Sanity check");
+ cp_size++;
+ } else {
+ if (int(cp_size) + _max_num_patched_klasses > 0xffff) {
+ THROW_MSG(vmSymbols::java_lang_InternalError(), "not enough space for patched classes");
+ }
+ cp_size += _max_num_patched_klasses;
}
- cp_size += _max_num_patched_klasses;
_cp = ConstantPool::allocate(_loader_data,
cp_size,
@@ -6233,36 +6263,67 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
assert(class_name_in_cp != NULL, "class_name can't be null");
- // Update _class_name to reflect the name in the constant pool
- update_class_name(class_name_in_cp);
-
// Don't need to check whether this class name is legal or not.
// It has been checked when constant pool is parsed.
// However, make sure it is not an array type.
if (_need_verify) {
- guarantee_property(_class_name->char_at(0) != JVM_SIGNATURE_ARRAY,
+ guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
"Bad class name in class file %s",
CHECK);
}
- // Checks if name in class file matches requested name
- if (_requested_name != NULL && _requested_name != _class_name) {
- ResourceMark rm(THREAD);
- Exceptions::fthrow(
- THREAD_AND_LOCATION,
- vmSymbols::java_lang_NoClassDefFoundError(),
- "%s (wrong name: %s)",
- _class_name->as_C_string(),
- _requested_name != NULL ? _requested_name->as_C_string() : "NoName"
- );
- return;
- }
+#ifdef ASSERT
+ // Basic sanity checks
+ assert(!(_is_hidden && (_unsafe_anonymous_host != NULL)), "mutually exclusive variants");
- // if this is an anonymous class fix up its name if it's in the unnamed
+ if (_unsafe_anonymous_host != NULL) {
+ assert(_class_name == vmSymbols::unknown_class_name(), "A named anonymous class???");
+ }
+ if (_is_hidden) {
+ assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name");
+ }
+#endif
+
+ // Update the _class_name as needed depending on whether this is a named,
+ // un-named, hidden or unsafe-anonymous class.
+
+ if (_is_hidden) {
+ assert(_class_name != NULL, "Unexpected null _class_name");
+#ifdef ASSERT
+ if (_need_verify) {
+ verify_legal_class_name(_class_name, CHECK);
+ }
+#endif
+
+ // NOTE: !_is_hidden does not imply "findable" as it could be an old-style
+ // "hidden" unsafe-anonymous class
+
+ // If this is an anonymous class fix up its name if it is in the unnamed
// package. Otherwise, throw IAE if it is in a different package than
// its host class.
- if (_unsafe_anonymous_host != NULL) {
+ } else if (_unsafe_anonymous_host != NULL) {
+ update_class_name(class_name_in_cp);
fix_unsafe_anonymous_class_name(CHECK);
+
+ } else {
+ // Check if name in class file matches given name
+ if (_class_name != class_name_in_cp) {
+ if (_class_name != vmSymbols::unknown_class_name()) {
+ ResourceMark rm(THREAD);
+ Exceptions::fthrow(THREAD_AND_LOCATION,
+ vmSymbols::java_lang_NoClassDefFoundError(),
+ "%s (wrong name: %s)",
+ class_name_in_cp->as_C_string(),
+ _class_name->as_C_string()
+ );
+ return;
+ } else {
+ // The class name was not known by the caller so we set it from
+ // the value in the CP.
+ update_class_name(class_name_in_cp);
+ }
+ // else nothing to do: the expected class name matches what is in the CP
+ }
}
// Verification prevents us from creating names with dots in them, this
@@ -6287,9 +6348,10 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
warning("DumpLoadedClassList and CDS are not supported in exploded build");
DumpLoadedClassList = NULL;
} else if (SystemDictionaryShared::is_sharing_possible(_loader_data) &&
+ !_is_hidden &&
_unsafe_anonymous_host == NULL) {
// Only dump the classes that can be stored into CDS archive.
- // Unsafe anonymous classes such as generated LambdaForm classes are also not included.
+ // Hidden and unsafe anonymous classes such as generated LambdaForm classes are also not included.
oop class_loader = _loader_data->class_loader();
ResourceMark rm(THREAD);
bool skip = false;
@@ -6384,6 +6446,35 @@ void ClassFileParser::parse_stream(const ClassFileStream* const stream,
// all bytes in stream read and parsed
}
+void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) {
+ ResourceMark rm;
+ // Construct hidden name from _class_name, "+", and &ik. Note that we can't
+ // use a '/' because that confuses finding the class's package. Also, can't
+ // use an illegal char such as ';' because that causes serialization issues
+ // and issues with hidden classes that create their own hidden classes.
+ char addr_buf[20];
+ jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik));
+ size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf);
+ char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len);
+ jio_snprintf(new_name, new_name_len, "%s+%s",
+ _class_name->as_C_string(), addr_buf);
+ update_class_name(SymbolTable::new_symbol(new_name));
+
+ // Add a Utf8 entry containing the hidden name.
+ assert(_class_name != NULL, "Unexpected null _class_name");
+ int hidden_index = _orig_cp_size; // this is an extra slot we added
+ _cp->symbol_at_put(hidden_index, _class_name);
+
+ // Update this_class_index's slot in the constant pool with the new Utf8 entry.
+ // We have to update the resolved_klass_index and the name_index together
+ // so extract the existing resolved_klass_index first.
+ CPKlassSlot cp_klass_slot = _cp->klass_slot_at(_this_class_index);
+ int resolved_klass_index = cp_klass_slot.resolved_klass_index();
+ _cp->unresolved_klass_at_put(_this_class_index, hidden_index, resolved_klass_index);
+ assert(_cp->klass_slot_at(_this_class_index).name_index() == _orig_cp_size,
+ "Bad name_index");
+}
+
void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream,
ConstantPool* cp,
TRAPS) {
diff --git a/src/hotspot/share/classfile/classFileParser.hpp b/src/hotspot/share/classfile/classFileParser.hpp
index 8945b29d05d..a6e48b1904d 100644
--- a/src/hotspot/share/classfile/classFileParser.hpp
+++ b/src/hotspot/share/classfile/classFileParser.hpp
@@ -37,6 +37,8 @@ template
class Array;
class ClassFileStream;
class ClassLoaderData;
+class ClassLoadInfo;
+class ClassInstanceInfo;
class CompressedLineNumberWriteStream;
class ConstMethod;
class FieldInfo;
@@ -109,11 +111,12 @@ class ClassFileParser {
typedef void unsafe_u2;
const ClassFileStream* _stream; // Actual input stream
- const Symbol* _requested_name;
Symbol* _class_name;
mutable ClassLoaderData* _loader_data;
const InstanceKlass* _unsafe_anonymous_host;
GrowableArray* _cp_patches; // overrides for CP entries
+ const bool _is_hidden;
+ const bool _can_access_vm_annotations;
int _num_patched_klasses;
int _max_num_patched_klasses;
int _orig_cp_size;
@@ -201,6 +204,8 @@ class ClassFileParser {
void parse_stream(const ClassFileStream* const stream, TRAPS);
+ void mangle_hidden_class_name(InstanceKlass* const ik);
+
void post_process_parsed_stream(const ClassFileStream* const stream,
ConstantPool* cp,
TRAPS);
@@ -208,7 +213,9 @@ class ClassFileParser {
void prepend_host_package_name(const InstanceKlass* unsafe_anonymous_host, TRAPS);
void fix_unsafe_anonymous_class_name(TRAPS);
- void fill_instance_klass(InstanceKlass* ik, bool cf_changed_in_CFLH, TRAPS);
+ void fill_instance_klass(InstanceKlass* ik, bool cf_changed_in_CFLH,
+ const ClassInstanceInfo& cl_inst_info, TRAPS);
+
void set_klass(InstanceKlass* instance);
void set_class_bad_constant_seen(short bad_constant);
@@ -527,21 +534,19 @@ class ClassFileParser {
FieldLayoutInfo* info,
TRAPS);
- void update_class_name(Symbol* new_name);
+ void update_class_name(Symbol* new_name);
public:
ClassFileParser(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
- Handle protection_domain,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo* cl_info,
Publicity pub_level,
TRAPS);
~ClassFileParser();
- InstanceKlass* create_instance_klass(bool cf_changed_in_CFLH, TRAPS);
+ InstanceKlass* create_instance_klass(bool cf_changed_in_CFLH, const ClassInstanceInfo& cl_inst_info, TRAPS);
const ClassFileStream* clone_stream() const;
@@ -557,6 +562,7 @@ class ClassFileParser {
u2 this_class_index() const { return _this_class_index; }
bool is_unsafe_anonymous() const { return _unsafe_anonymous_host != NULL; }
+ bool is_hidden() const { return _is_hidden; }
bool is_interface() const { return _access_flags.is_interface(); }
const InstanceKlass* unsafe_anonymous_host() const { return _unsafe_anonymous_host; }
diff --git a/src/hotspot/share/classfile/classLoader.cpp b/src/hotspot/share/classfile/classLoader.cpp
index 54df9eb739c..9d465eb6746 100644
--- a/src/hotspot/share/classfile/classLoader.cpp
+++ b/src/hotspot/share/classfile/classLoader.cpp
@@ -1257,13 +1257,12 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
+ ClassLoadInfo cl_info(protection_domain);
InstanceKlass* result = KlassFactory::create_from_stream(stream,
name,
loader_data,
- protection_domain,
- NULL, // unsafe_anonymous_host
- NULL, // cp_patches
+ cl_info,
THREAD);
if (HAS_PENDING_EXCEPTION) {
if (DumpSharedSpaces) {
@@ -1302,8 +1301,8 @@ void ClassLoader::record_result(InstanceKlass* ik, const ClassFileStream* stream
Arguments::assert_is_dumping_archive();
assert(stream != NULL, "sanity");
- if (ik->is_unsafe_anonymous()) {
- // We do not archive unsafe anonymous classes.
+ if (ik->is_hidden() || ik->is_unsafe_anonymous()) {
+ // We do not archive hidden or unsafe anonymous classes.
return;
}
diff --git a/src/hotspot/share/classfile/classLoaderData.cpp b/src/hotspot/share/classfile/classLoaderData.cpp
index 123571f85d5..4534d4fe1fc 100644
--- a/src/hotspot/share/classfile/classLoaderData.cpp
+++ b/src/hotspot/share/classfile/classLoaderData.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -126,16 +126,16 @@ void ClassLoaderData::initialize_name(Handle class_loader) {
_name_and_id = SymbolTable::new_symbol(cl_instance_name_and_id);
}
-ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_unsafe_anonymous) :
+ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool has_class_mirror_holder) :
_metaspace(NULL),
_metaspace_lock(new Mutex(Mutex::leaf+1, "Metaspace allocation lock", true,
Mutex::_safepoint_check_never)),
- _unloading(false), _is_unsafe_anonymous(is_unsafe_anonymous),
+ _unloading(false), _has_class_mirror_holder(has_class_mirror_holder),
_modified_oops(true), _accumulated_modified_oops(false),
// An unsafe anonymous class loader data doesn't have anything to keep
// it from being unloaded during parsing of the unsafe anonymous class.
// The null-class-loader should always be kept alive.
- _keep_alive((is_unsafe_anonymous || h_class_loader.is_null()) ? 1 : 0),
+ _keep_alive((has_class_mirror_holder || h_class_loader.is_null()) ? 1 : 0),
_claim(0),
_handles(),
_klasses(NULL), _packages(NULL), _modules(NULL), _unnamed_module(NULL), _dictionary(NULL),
@@ -150,13 +150,13 @@ ClassLoaderData::ClassLoaderData(Handle h_class_loader, bool is_unsafe_anonymous
initialize_name(h_class_loader);
}
- if (!is_unsafe_anonymous) {
- // The holder is initialized later for unsafe anonymous classes, and before calling anything
- // that call class_loader().
+ if (!has_class_mirror_holder) {
+ // The holder is initialized later for non-strong hidden classes and unsafe anonymous classes,
+ // and before calling anything that call class_loader().
initialize_holder(h_class_loader);
- // A ClassLoaderData created solely for an unsafe anonymous class should never have a
- // ModuleEntryTable or PackageEntryTable created for it. The defining package
+ // A ClassLoaderData created solely for a non-strong hidden class or unsafe anonymous class should
+ // never have a ModuleEntryTable or PackageEntryTable created for it. The defining package
// and module for an unsafe anonymous class will be found in its host class.
_packages = new PackageEntryTable(PackageEntryTable::_packagetable_entry_size);
if (h_class_loader.is_null()) {
@@ -291,20 +291,20 @@ bool ClassLoaderData::try_claim(int claim) {
}
}
-// Unsafe anonymous classes have their own ClassLoaderData that is marked to keep alive
+// Weak hidden and unsafe anonymous classes have their own ClassLoaderData that is marked to keep alive
// while the class is being parsed, and if the class appears on the module fixup list.
-// Due to the uniqueness that no other class shares the unsafe anonymous class' name or
-// ClassLoaderData, no other non-GC thread has knowledge of the unsafe anonymous class while
+// Due to the uniqueness that no other class shares the hidden or unsafe anonymous class' name or
+// ClassLoaderData, no other non-GC thread has knowledge of the hidden or unsafe anonymous class while
// it is being defined, therefore _keep_alive is not volatile or atomic.
void ClassLoaderData::inc_keep_alive() {
- if (is_unsafe_anonymous()) {
+ if (has_class_mirror_holder()) {
assert(_keep_alive > 0, "Invalid keep alive increment count");
_keep_alive++;
}
}
void ClassLoaderData::dec_keep_alive() {
- if (is_unsafe_anonymous()) {
+ if (has_class_mirror_holder()) {
assert(_keep_alive > 0, "Invalid keep alive decrement count");
_keep_alive--;
}
@@ -410,21 +410,21 @@ void ClassLoaderData::record_dependency(const Klass* k) {
// Do not need to record dependency if the dependency is to a class whose
// class loader data is never freed. (i.e. the dependency's class loader
- // is one of the three builtin class loaders and the dependency is not
- // unsafe anonymous.)
+ // is one of the three builtin class loaders and the dependency's class
+ // loader data has a ClassLoader holder, not a Class holder.)
if (to_cld->is_permanent_class_loader_data()) {
return;
}
oop to;
- if (to_cld->is_unsafe_anonymous()) {
- // Just return if an unsafe anonymous class is attempting to record a dependency
- // to itself. (Note that every unsafe anonymous class has its own unique class
+ if (to_cld->has_class_mirror_holder()) {
+ // Just return if a non-strong hidden class or unsafe anonymous class is attempting to record a dependency
+ // to itself. (Note that every non-strong hidden class or unsafe anonymous class has its own unique class
// loader data.)
if (to_cld == from_cld) {
return;
}
- // Unsafe anonymous class dependencies are through the mirror.
+ // Hidden and unsafe anonymous class dependencies are through the mirror.
to = k->java_mirror();
} else {
to = to_cld->class_loader();
@@ -572,7 +572,7 @@ const int _boot_loader_dictionary_size = 1009;
const int _default_loader_dictionary_size = 107;
Dictionary* ClassLoaderData::create_dictionary() {
- assert(!is_unsafe_anonymous(), "unsafe anonymous class loader data do not have a dictionary");
+ assert(!has_class_mirror_holder(), "class mirror holder cld does not have a dictionary");
int size;
bool resizable = false;
if (_the_null_class_loader_data == NULL) {
@@ -618,7 +618,7 @@ oop ClassLoaderData::holder_no_keepalive() const {
// Unloading support
bool ClassLoaderData::is_alive() const {
- bool alive = keep_alive() // null class loader and incomplete unsafe anonymous klasses.
+ bool alive = keep_alive() // null class loader and incomplete non-strong hidden class or unsafe anonymous class.
|| (_holder.peek() != NULL); // and not cleaned by the GC weak handle processing.
return alive;
@@ -716,13 +716,13 @@ ClassLoaderData::~ClassLoaderData() {
// Returns true if this class loader data is for the app class loader
// or a user defined system class loader. (Note that the class loader
-// data may be unsafe anonymous.)
+// data may have a Class holder.)
bool ClassLoaderData::is_system_class_loader_data() const {
return SystemDictionary::is_system_class_loader(class_loader());
}
// Returns true if this class loader data is for the platform class loader.
-// (Note that the class loader data may be unsafe anonymous.)
+// (Note that the class loader data may have a Class holder.)
bool ClassLoaderData::is_platform_class_loader_data() const {
return SystemDictionary::is_platform_class_loader(class_loader());
}
@@ -730,8 +730,8 @@ bool ClassLoaderData::is_platform_class_loader_data() const {
// Returns true if the class loader for this class loader data is one of
// the 3 builtin (boot application/system or platform) class loaders,
// including a user-defined system class loader. Note that if the class
-// loader data is for an unsafe anonymous class then it may get freed by a GC
-// even if its class loader is one of these loaders.
+// loader data is for a non-strong hidden class or unsafe anonymous class then it may
+// get freed by a GC even if its class loader is one of these loaders.
bool ClassLoaderData::is_builtin_class_loader_data() const {
return (is_boot_class_loader_data() ||
SystemDictionary::is_system_class_loader(class_loader()) ||
@@ -740,9 +740,9 @@ bool ClassLoaderData::is_builtin_class_loader_data() const {
// Returns true if this class loader data is a class loader data
// that is not ever freed by a GC. It must be the CLD for one of the builtin
-// class loaders and not the CLD for an unsafe anonymous class.
+// class loaders and not the CLD for a non-strong hidden class or unsafe anonymous class.
bool ClassLoaderData::is_permanent_class_loader_data() const {
- return is_builtin_class_loader_data() && !is_unsafe_anonymous();
+ return is_builtin_class_loader_data() && !has_class_mirror_holder();
}
ClassLoaderMetaspace* ClassLoaderData::metaspace_non_null() {
@@ -759,8 +759,8 @@ ClassLoaderMetaspace* ClassLoaderData::metaspace_non_null() {
if (this == the_null_class_loader_data()) {
assert (class_loader() == NULL, "Must be");
metaspace = new ClassLoaderMetaspace(_metaspace_lock, Metaspace::BootMetaspaceType);
- } else if (is_unsafe_anonymous()) {
- metaspace = new ClassLoaderMetaspace(_metaspace_lock, Metaspace::UnsafeAnonymousMetaspaceType);
+ } else if (has_class_mirror_holder()) {
+ metaspace = new ClassLoaderMetaspace(_metaspace_lock, Metaspace::ClassMirrorHolderMetaspaceType);
} else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {
metaspace = new ClassLoaderMetaspace(_metaspace_lock, Metaspace::ReflectionMetaspaceType);
} else {
@@ -877,8 +877,8 @@ void ClassLoaderData::free_deallocate_list_C_heap_structures() {
}
}
-// These CLDs are to contain unsafe anonymous classes used for JSR292
-ClassLoaderData* ClassLoaderData::unsafe_anonymous_class_loader_data(Handle loader) {
+// These CLDs are to contain non-strong hidden classes or unsafe anonymous classes used for JSR292
+ClassLoaderData* ClassLoaderData::has_class_mirror_holder_cld(Handle loader) {
// Add a new class loader data to the graph.
return ClassLoaderDataGraph::add(loader, true);
}
@@ -920,8 +920,8 @@ void ClassLoaderData::print_value_on(outputStream* out) const {
// loader data: 0xsomeaddr of 'bootstrap'
out->print("loader data: " INTPTR_FORMAT " of %s", p2i(this), loader_name_and_id());
}
- if (is_unsafe_anonymous()) {
- out->print(" unsafe anonymous");
+ if (_has_class_mirror_holder) {
+ out->print(" has a class holder");
}
}
@@ -931,7 +931,7 @@ void ClassLoaderData::print_value() const { print_value_on(tty); }
void ClassLoaderData::print_on(outputStream* out) const {
out->print("ClassLoaderData CLD: " PTR_FORMAT ", loader: " PTR_FORMAT ", loader_klass: %s {",
p2i(this), p2i(_class_loader.ptr_raw()), loader_name_and_id());
- if (is_unsafe_anonymous()) out->print(" unsafe anonymous");
+ if (has_class_mirror_holder()) out->print(" has a class holder");
if (claimed()) out->print(" claimed");
if (is_unloading()) out->print(" unloading");
out->print(" metaspace: " INTPTR_FORMAT, p2i(metaspace_or_null()));
@@ -951,8 +951,8 @@ void ClassLoaderData::verify() {
assert_locked_or_safepoint(_metaspace_lock);
oop cl = class_loader();
- guarantee(this == class_loader_data(cl) || is_unsafe_anonymous(), "Must be the same");
- guarantee(cl != NULL || this == ClassLoaderData::the_null_class_loader_data() || is_unsafe_anonymous(), "must be");
+ guarantee(this == class_loader_data(cl) || has_class_mirror_holder(), "Must be the same");
+ guarantee(cl != NULL || this == ClassLoaderData::the_null_class_loader_data() || has_class_mirror_holder(), "must be");
// Verify the integrity of the allocated space.
if (metaspace_or_null() != NULL) {
diff --git a/src/hotspot/share/classfile/classLoaderData.hpp b/src/hotspot/share/classfile/classLoaderData.hpp
index c7d81ad7811..1b898abcc3d 100644
--- a/src/hotspot/share/classfile/classLoaderData.hpp
+++ b/src/hotspot/share/classfile/classLoaderData.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -117,17 +117,20 @@ class ClassLoaderData : public CHeapObj {
// classes in the class loader are allocated.
Mutex* _metaspace_lock; // Locks the metaspace for allocations and setup.
bool _unloading; // true if this class loader goes away
- bool _is_unsafe_anonymous; // CLD is dedicated to one class and that class determines the CLDs lifecycle.
- // For example, an unsafe anonymous class.
+ bool _has_class_mirror_holder; // If true, CLD is dedicated to one class and that class determines
+ // the CLDs lifecycle. For example, a non-strong hidden class or an
+ // unsafe anonymous class. Arrays of these classes are also assigned
+ // to these class loader datas.
// Remembered sets support for the oops in the class loader data.
bool _modified_oops; // Card Table Equivalent (YC/CMS support)
bool _accumulated_modified_oops; // Mod Union Equivalent (CMS support)
int _keep_alive; // if this CLD is kept alive.
- // Used for unsafe anonymous classes and the boot class
- // loader. _keep_alive does not need to be volatile or
- // atomic since there is one unique CLD per unsafe anonymous class.
+ // Used for non-strong hidden classes, unsafe anonymous classes and the
+ // boot class loader. _keep_alive does not need to be volatile or
+ // atomic since there is one unique CLD per non-strong hidden class
+ // or unsafe anonymous class.
volatile int _claim; // non-zero if claimed, for example during GC traces.
// To avoid applying oop closure more than once.
@@ -162,7 +165,7 @@ class ClassLoaderData : public CHeapObj {
void set_next(ClassLoaderData* next) { _next = next; }
ClassLoaderData* next() const { return Atomic::load(&_next); }
- ClassLoaderData(Handle h_class_loader, bool is_unsafe_anonymous);
+ ClassLoaderData(Handle h_class_loader, bool has_class_mirror_holder);
~ClassLoaderData();
// The CLD are not placed in the Heap, so the Card Table or
@@ -231,7 +234,7 @@ class ClassLoaderData : public CHeapObj {
Mutex* metaspace_lock() const { return _metaspace_lock; }
- bool is_unsafe_anonymous() const { return _is_unsafe_anonymous; }
+ bool has_class_mirror_holder() const { return _has_class_mirror_holder; }
static void init_null_class_loader_data();
@@ -240,15 +243,15 @@ class ClassLoaderData : public CHeapObj {
}
// Returns true if this class loader data is for the system class loader.
- // (Note that the class loader data may be unsafe anonymous.)
+ // (Note that the class loader data may be for a non-strong hidden class or unsafe anonymous class)
bool is_system_class_loader_data() const;
// Returns true if this class loader data is for the platform class loader.
- // (Note that the class loader data may be unsafe anonymous.)
+ // (Note that the class loader data may be for a non-strong hidden class or unsafe anonymous class)
bool is_platform_class_loader_data() const;
// Returns true if this class loader data is for the boot class loader.
- // (Note that the class loader data may be unsafe anonymous.)
+ // (Note that the class loader data may be for a non-strong hidden class or unsafe anonymous class)
inline bool is_boot_class_loader_data() const;
bool is_builtin_class_loader_data() const;
@@ -269,7 +272,7 @@ class ClassLoaderData : public CHeapObj {
return _unloading;
}
- // Used to refcount an unsafe anonymous class's CLD in order to
+ // Used to refcount a non-strong hidden class's or unsafe anonymous class's CLD in order to
// indicate their aliveness.
void inc_keep_alive();
void dec_keep_alive();
@@ -313,7 +316,7 @@ class ClassLoaderData : public CHeapObj {
static ClassLoaderData* class_loader_data(oop loader);
static ClassLoaderData* class_loader_data_or_null(oop loader);
- static ClassLoaderData* unsafe_anonymous_class_loader_data(Handle loader);
+ static ClassLoaderData* has_class_mirror_holder_cld(Handle loader);
// Returns Klass* of associated class loader, or NULL if associated loader is 'bootstrap'.
// Also works if unloading.
diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
index bab44e3f2c7..971c495e70c 100644
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -192,7 +192,7 @@ bool ClassLoaderDataGraph::_metaspace_oom = false;
// Add a new class loader data node to the list. Assign the newly created
// ClassLoaderData into the java/lang/ClassLoader object as a hidden field
-ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool is_unsafe_anonymous) {
+ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool has_class_mirror_holder) {
assert_lock_strong(ClassLoaderDataGraph_lock);
@@ -200,7 +200,7 @@ ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool is_unsaf
// First check if another thread beat us to creating the CLD and installing
// it into the loader while we were waiting for the lock.
- if (!is_unsafe_anonymous && loader.not_null()) {
+ if (!has_class_mirror_holder && loader.not_null()) {
cld = java_lang_ClassLoader::loader_data_acquire(loader());
if (cld != NULL) {
return cld;
@@ -212,14 +212,14 @@ ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool is_unsaf
// loader oop in all collections, particularly young collections.
NoSafepointVerifier no_safepoints;
- cld = new ClassLoaderData(loader, is_unsafe_anonymous);
+ cld = new ClassLoaderData(loader, has_class_mirror_holder);
// First install the new CLD to the Graph.
cld->set_next(_head);
Atomic::release_store(&_head, cld);
// Next associate with the class_loader.
- if (!is_unsafe_anonymous) {
+ if (!has_class_mirror_holder) {
// Use OrderAccess, since readers need to get the loader_data only after
// it's added to the Graph
java_lang_ClassLoader::release_set_loader_data(loader(), cld);
@@ -237,9 +237,9 @@ ClassLoaderData* ClassLoaderDataGraph::add_to_graph(Handle loader, bool is_unsaf
return cld;
}
-ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool is_unsafe_anonymous) {
+ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool has_class_mirror_holder) {
MutexLocker ml(ClassLoaderDataGraph_lock);
- ClassLoaderData* loader_data = add_to_graph(loader, is_unsafe_anonymous);
+ ClassLoaderData* loader_data = add_to_graph(loader, has_class_mirror_holder);
return loader_data;
}
diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.hpp b/src/hotspot/share/classfile/classLoaderDataGraph.hpp
index 91fe4c343ac..ef466e102fd 100644
--- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -56,8 +56,8 @@ class ClassLoaderDataGraph : public AllStatic {
static volatile size_t _num_instance_classes;
static volatile size_t _num_array_classes;
- static ClassLoaderData* add_to_graph(Handle class_loader, bool is_unsafe_anonymous);
- static ClassLoaderData* add(Handle class_loader, bool is_unsafe_anonymous);
+ static ClassLoaderData* add_to_graph(Handle class_loader, bool has_class_mirror_holder);
+ static ClassLoaderData* add(Handle class_loader, bool has_class_mirror_holder);
public:
static ClassLoaderData* find_or_create(Handle class_loader);
@@ -76,7 +76,7 @@ class ClassLoaderDataGraph : public AllStatic {
// Walking classes through the ClassLoaderDataGraph include array classes. It also includes
// classes that are allocated but not loaded, classes that have errors, and scratch classes
// for redefinition. These classes are removed during the next class unloading.
- // Walking the ClassLoaderDataGraph also includes unsafe anonymous classes.
+ // Walking the ClassLoaderDataGraph also includes hidden and unsafe anonymous classes.
static void classes_do(KlassClosure* klass_closure);
static void classes_do(void f(Klass* const));
static void methods_do(void f(Method*));
diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp
index d42060bebb7..701c155da19 100644
--- a/src/hotspot/share/classfile/classLoaderExt.cpp
+++ b/src/hotspot/share/classfile/classLoaderExt.cpp
@@ -284,13 +284,12 @@ InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS)
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
Handle protection_domain;
+ ClassLoadInfo cl_info(protection_domain);
InstanceKlass* result = KlassFactory::create_from_stream(stream,
name,
loader_data,
- protection_domain,
- NULL, // unsafe_anonymous_host
- NULL, // cp_patches
+ cl_info,
THREAD);
if (HAS_PENDING_EXCEPTION) {
diff --git a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
index d24dd9a41f0..36e92ff60c6 100644
--- a/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
+++ b/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018 SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -129,7 +129,7 @@ public:
class LoaderTreeNode : public ResourceObj {
- // We walk the CLDG and, for each CLD which is non-unsafe_anonymous, add
+ // We walk the CLDG and, for each CLD which is findable, add
// a tree node.
// To add a node we need its parent node; if the parent node does not yet
// exist - because we have not yet encountered the CLD for the parent loader -
@@ -149,6 +149,9 @@ class LoaderTreeNode : public ResourceObj {
LoadedClassInfo* _anon_classes;
int _num_anon_classes;
+ LoadedClassInfo* _hidden_classes;
+ int _num_hidden_classes;
+
// In default view, similar tree nodes (same loader class, same name or no name)
// are folded into each other to make the output more readable.
// _num_folded contains the number of nodes which have been folded into this
@@ -177,6 +180,7 @@ class LoaderTreeNode : public ResourceObj {
if (_cld->is_the_null_class_loader_data()) {
st->print(" ");
} else {
+ assert(!_cld->has_class_mirror_holder(), "_cld must be the primary cld");
if (loader_name != NULL) {
st->print(" \"%s\",", loader_name->as_C_string());
}
@@ -220,7 +224,7 @@ class LoaderTreeNode : public ResourceObj {
if (print_classes) {
if (_classes != NULL) {
for (LoadedClassInfo* lci = _classes; lci; lci = lci->_next) {
- // Non-unsafe anonymous classes should live in the primary CLD of its loader
+ // non-strong hidden and unsafe anonymous classes should not live in the primary CLD of their loaders.
assert(lci->_cld == _cld, "must be");
branchtracker.print(st);
@@ -258,7 +262,8 @@ class LoaderTreeNode : public ResourceObj {
st->print("%*s ", indentation, "");
}
st->print("%s", lci->_klass->external_name());
- // For unsafe anonymous classes, also print CLD if verbose. Should be a different one than the primary CLD.
+ // For unsafe anonymous classes, also print CLD if verbose. Should
+ // be a different one than the primary CLD.
assert(lci->_cld != _cld, "must be");
if (verbose) {
st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
@@ -267,7 +272,35 @@ class LoaderTreeNode : public ResourceObj {
}
branchtracker.print(st);
st->print("%*s ", indentation, "");
- st->print_cr("(%u unsafe anonymous class%s)", _num_anon_classes, (_num_anon_classes == 1) ? "" : "es");
+ st->print_cr("(%u unsafe anonymous class%s)", _num_anon_classes,
+ (_num_anon_classes == 1) ? "" : "es");
+
+ // Empty line
+ branchtracker.print(st);
+ st->cr();
+ }
+
+ if (_hidden_classes != NULL) {
+ for (LoadedClassInfo* lci = _hidden_classes; lci; lci = lci->_next) {
+ branchtracker.print(st);
+ if (lci == _hidden_classes) { // first iteration
+ st->print("%*s ", indentation, "Hidden Classes:");
+ } else {
+ st->print("%*s ", indentation, "");
+ }
+ st->print("%s", lci->_klass->external_name());
+ // For non-strong hidden classes, also print CLD if verbose. Should be a
+ // different one than the primary CLD.
+ assert(lci->_cld != _cld, "must be");
+ if (verbose) {
+ st->print(" (Loader Data: " PTR_FORMAT ")", p2i(lci->_cld));
+ }
+ st->cr();
+ }
+ branchtracker.print(st);
+ st->print("%*s ", indentation, "");
+ st->print_cr("(%u hidden class%s)", _num_hidden_classes,
+ (_num_hidden_classes == 1) ? "" : "es");
// Empty line
branchtracker.print(st);
@@ -301,6 +334,7 @@ public:
LoaderTreeNode(const oop loader_oop)
: _loader_oop(loader_oop), _cld(NULL), _child(NULL), _next(NULL),
_classes(NULL), _num_classes(0), _anon_classes(NULL), _num_anon_classes(0),
+ _hidden_classes(NULL), _num_hidden_classes(0),
_num_folded(0)
{}
@@ -319,15 +353,25 @@ public:
_next = info;
}
- void add_classes(LoadedClassInfo* first_class, int num_classes, bool is_unsafe_anonymous) {
- LoadedClassInfo** p_list_to_add_to = is_unsafe_anonymous ? &_anon_classes : &_classes;
+ void add_classes(LoadedClassInfo* first_class, int num_classes, bool has_class_mirror_holder) {
+ LoadedClassInfo** p_list_to_add_to;
+ bool is_hidden = first_class->_klass->is_hidden();
+ if (has_class_mirror_holder) {
+ p_list_to_add_to = is_hidden ? &_hidden_classes : &_anon_classes;
+ } else {
+ p_list_to_add_to = &_classes;
+ }
// Search tail.
while ((*p_list_to_add_to) != NULL) {
p_list_to_add_to = &(*p_list_to_add_to)->_next;
}
*p_list_to_add_to = first_class;
- if (is_unsafe_anonymous) {
- _num_anon_classes += num_classes;
+ if (has_class_mirror_holder) {
+ if (is_hidden) {
+ _num_hidden_classes += num_classes;
+ } else {
+ _num_anon_classes += num_classes;
+ }
} else {
_num_classes += num_classes;
}
@@ -421,7 +465,7 @@ class LoaderInfoScanClosure : public CLDClosure {
LoadedClassCollectClosure lccc(cld);
const_cast(cld)->classes_do(&lccc);
if (lccc._num_classes > 0) {
- info->add_classes(lccc._list, lccc._num_classes, cld->is_unsafe_anonymous());
+ info->add_classes(lccc._list, lccc._num_classes, cld->has_class_mirror_holder());
}
}
@@ -481,7 +525,7 @@ public:
assert(info != NULL, "must be");
// Update CLD in node, but only if this is the primary CLD for this loader.
- if (cld->is_unsafe_anonymous() == false) {
+ if (cld->has_class_mirror_holder() == false) {
assert(info->cld() == NULL, "there should be only one primary CLD per loader");
info->set_cld(cld);
}
diff --git a/src/hotspot/share/classfile/classLoaderStats.cpp b/src/hotspot/share/classfile/classLoaderStats.cpp
index 2ba125f2f2d..f3adad46da5 100644
--- a/src/hotspot/share/classfile/classLoaderStats.cpp
+++ b/src/hotspot/share/classfile/classLoaderStats.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, 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
@@ -26,6 +26,7 @@
#include "classfile/classLoaderData.inline.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/classLoaderStats.hpp"
+#include "oops/objArrayKlass.hpp"
#include "oops/oop.inline.hpp"
#include "utilities/globalDefinitions.hpp"
@@ -59,7 +60,7 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
cls = *cls_ptr;
}
- if (!cld->is_unsafe_anonymous()) {
+ if (!cld->has_class_mirror_holder()) {
cls->_cld = cld;
}
@@ -71,8 +72,20 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
ClassStatsClosure csc;
cld->classes_do(&csc);
- if(cld->is_unsafe_anonymous()) {
- cls->_anon_classes_count += csc._num_classes;
+ bool is_hidden = false;
+ if(cld->has_class_mirror_holder()) {
+ // if cld has a class holder then it must be either hidden or unsafe anonymous.
+ Klass* k = cld->klasses();
+ // if it's an array class then need to see if bottom class is hidden.
+ if (k->is_array_klass()) {
+ k = ObjArrayKlass::cast(k)->bottom_klass();
+ }
+ is_hidden = k->is_hidden();
+ if (is_hidden) {
+ cls->_hidden_classes_count += csc._num_classes;
+ } else {
+ cls->_anon_classes_count += csc._num_classes;
+ }
} else {
cls->_classes_count = csc._num_classes;
}
@@ -80,9 +93,14 @@ void ClassLoaderStatsClosure::do_cld(ClassLoaderData* cld) {
ClassLoaderMetaspace* ms = cld->metaspace_or_null();
if (ms != NULL) {
- if(cld->is_unsafe_anonymous()) {
- cls->_anon_chunk_sz += ms->allocated_chunks_bytes();
- cls->_anon_block_sz += ms->allocated_blocks_bytes();
+ if(cld->has_class_mirror_holder()) {
+ if (is_hidden) {
+ cls->_hidden_chunk_sz += ms->allocated_chunks_bytes();
+ cls->_hidden_block_sz += ms->allocated_blocks_bytes();
+ } else {
+ cls->_anon_chunk_sz += ms->allocated_chunks_bytes();
+ cls->_anon_block_sz += ms->allocated_blocks_bytes();
+ }
} else {
cls->_chunk_sz = ms->allocated_chunks_bytes();
cls->_block_sz = ms->allocated_blocks_bytes();
@@ -121,6 +139,12 @@ bool ClassLoaderStatsClosure::do_entry(oop const& key, ClassLoaderStats* const&
cls->_anon_classes_count,
cls->_anon_chunk_sz, cls->_anon_block_sz);
}
+ if (cls->_hidden_classes_count > 0) {
+ _out->print_cr(SPACE SPACE SPACE " " UINTX_FORMAT_W(6) " " SIZE_FORMAT_W(8) " " SIZE_FORMAT_W(8) " + hidden classes",
+ "", "", "",
+ cls->_hidden_classes_count,
+ cls->_hidden_chunk_sz, cls->_hidden_block_sz);
+ }
return true;
}
diff --git a/src/hotspot/share/classfile/classLoaderStats.hpp b/src/hotspot/share/classfile/classLoaderStats.hpp
index 8e37b50a2f3..c6fac17a8be 100644
--- a/src/hotspot/share/classfile/classLoaderStats.hpp
+++ b/src/hotspot/share/classfile/classLoaderStats.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, 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
@@ -81,6 +81,10 @@ public:
size_t _anon_block_sz;
uintx _anon_classes_count;
+ size_t _hidden_chunk_sz;
+ size_t _hidden_block_sz;
+ uintx _hidden_classes_count;
+
ClassLoaderStats() :
_cld(0),
_class_loader(0),
@@ -90,7 +94,10 @@ public:
_classes_count(0),
_anon_chunk_sz(0),
_anon_block_sz(0),
- _anon_classes_count(0) {
+ _anon_classes_count(0),
+ _hidden_chunk_sz(0),
+ _hidden_block_sz(0),
+ _hidden_classes_count(0) {
}
};
diff --git a/src/hotspot/share/classfile/defaultMethods.cpp b/src/hotspot/share/classfile/defaultMethods.cpp
index 6fb83924a2a..a83f94d3644 100644
--- a/src/hotspot/share/classfile/defaultMethods.cpp
+++ b/src/hotspot/share/classfile/defaultMethods.cpp
@@ -918,7 +918,7 @@ static void switchover_constant_pool(BytecodeConstantPool* bpool,
ConstantPool* cp = bpool->create_constant_pool(CHECK);
if (cp != klass->constants()) {
// Copy resolved anonymous class into new constant pool.
- if (klass->is_unsafe_anonymous()) {
+ if (klass->is_unsafe_anonymous() || klass->is_hidden()) {
cp->klass_at_put(klass->this_class_index(), klass);
}
klass->class_loader_data()->add_to_deallocate_list(klass->constants());
diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp
index abd6c68d571..686dab0973e 100644
--- a/src/hotspot/share/classfile/dictionary.cpp
+++ b/src/hotspot/share/classfile/dictionary.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2020, 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
@@ -354,6 +354,7 @@ bool Dictionary::is_valid_protection_domain(unsigned int hash,
// since been unreferenced, so this entry should be cleared.
void Dictionary::clean_cached_protection_domains() {
assert_locked_or_safepoint(SystemDictionary_lock);
+ assert(!loader_data()->has_class_mirror_holder(), "cld should have a ClassLoader holder not a Class holder");
if (loader_data()->is_the_null_class_loader_data()) {
// Classes in the boot loader are not loaded with protection domains
@@ -482,6 +483,7 @@ void Dictionary::print_on(outputStream* st) const {
ResourceMark rm;
assert(loader_data() != NULL, "loader data should not be null");
+ assert(!loader_data()->has_class_mirror_holder(), "cld should have a ClassLoader holder not a Class holder");
st->print_cr("Java dictionary (table_size=%d, classes=%d, resizable=%s)",
table_size(), number_of_entries(), BOOL_TO_STR(_resizable));
st->print_cr("^ indicates that initiating loader is different from defining loader");
diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp
index f2f51be5b82..347437f8778 100644
--- a/src/hotspot/share/classfile/javaClasses.cpp
+++ b/src/hotspot/share/classfile/javaClasses.cpp
@@ -880,12 +880,13 @@ void java_lang_Class::fixup_mirror(Klass* k, TRAPS) {
k->clear_has_raw_archived_mirror();
}
}
- create_mirror(k, Handle(), Handle(), Handle(), CHECK);
+ create_mirror(k, Handle(), Handle(), Handle(), Handle(), CHECK);
}
void java_lang_Class::initialize_mirror_fields(Klass* k,
Handle mirror,
Handle protection_domain,
+ Handle classData,
TRAPS) {
// Allocate a simple java object for a lock.
// This needs to be a java object because during class initialization
@@ -898,6 +899,9 @@ void java_lang_Class::initialize_mirror_fields(Klass* k,
// Initialize static fields
InstanceKlass::cast(k)->do_local_static_fields(&initialize_static_field, mirror, CHECK);
+
+ // Set classData
+ set_class_data(mirror(), classData());
}
// Set the java.lang.Module module field in the java_lang_Class mirror
@@ -951,7 +955,8 @@ void java_lang_Class::allocate_fixup_lists() {
}
void java_lang_Class::create_mirror(Klass* k, Handle class_loader,
- Handle module, Handle protection_domain, TRAPS) {
+ Handle module, Handle protection_domain,
+ Handle classData, TRAPS) {
assert(k != NULL, "Use create_basic_type_mirror for primitive types");
assert(k->java_mirror() == NULL, "should only assign mirror once");
@@ -998,7 +1003,7 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader,
} else {
assert(k->is_instance_klass(), "Must be");
- initialize_mirror_fields(k, mirror, protection_domain, THREAD);
+ initialize_mirror_fields(k, mirror, protection_domain, classData, THREAD);
if (HAS_PENDING_EXCEPTION) {
// If any of the fields throws an exception like OOM remove the klass field
// from the mirror so GC doesn't follow it after the klass has been deallocated.
@@ -1424,6 +1429,14 @@ void java_lang_Class::set_signers(oop java_class, objArrayOop signers) {
java_class->obj_field_put(_signers_offset, (oop)signers);
}
+oop java_lang_Class::class_data(oop java_class) {
+ assert(_classData_offset != 0, "must be set");
+ return java_class->obj_field(_classData_offset);
+}
+void java_lang_Class::set_class_data(oop java_class, oop class_data) {
+ assert(_classData_offset != 0, "must be set");
+ java_class->obj_field_put(_classData_offset, class_data);
+}
void java_lang_Class::set_class_loader(oop java_class, oop loader) {
assert(_class_loader_offset != 0, "offsets should have been initialized");
@@ -1627,6 +1640,7 @@ int java_lang_Class::classRedefinedCount_offset = -1;
macro(_component_mirror_offset, k, "componentType", class_signature, false); \
macro(_module_offset, k, "module", module_signature, false); \
macro(_name_offset, k, "name", string_signature, false); \
+ macro(_classData_offset, k, "classData", object_signature, false);
void java_lang_Class::compute_offsets() {
if (offsets_computed) {
@@ -4295,6 +4309,7 @@ int java_lang_Class::_init_lock_offset;
int java_lang_Class::_signers_offset;
int java_lang_Class::_name_offset;
int java_lang_Class::_source_file_offset;
+int java_lang_Class::_classData_offset;
GrowableArray* java_lang_Class::_fixup_mirror_list = NULL;
GrowableArray* java_lang_Class::_fixup_module_field_list = NULL;
int java_lang_Throwable::backtrace_offset;
diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp
index 2a28724b80f..7766983fa58 100644
--- a/src/hotspot/share/classfile/javaClasses.hpp
+++ b/src/hotspot/share/classfile/javaClasses.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2020, 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
@@ -265,6 +265,7 @@ class java_lang_Class : AllStatic {
static int _component_mirror_offset;
static int _name_offset;
static int _source_file_offset;
+ static int _classData_offset;
static bool offsets_computed;
static int classRedefinedCount_offset;
@@ -276,7 +277,8 @@ class java_lang_Class : AllStatic {
static void set_protection_domain(oop java_class, oop protection_domain);
static void set_class_loader(oop java_class, oop class_loader);
static void set_component_mirror(oop java_class, oop comp_mirror);
- static void initialize_mirror_fields(Klass* k, Handle mirror, Handle protection_domain, TRAPS);
+ static void initialize_mirror_fields(Klass* k, Handle mirror, Handle protection_domain,
+ Handle classData, TRAPS);
static void set_mirror_module_field(Klass* K, Handle mirror, Handle module, TRAPS);
public:
static void allocate_fixup_lists();
@@ -284,7 +286,7 @@ class java_lang_Class : AllStatic {
// Instance creation
static void create_mirror(Klass* k, Handle class_loader, Handle module,
- Handle protection_domain, TRAPS);
+ Handle protection_domain, Handle classData, TRAPS);
static void fixup_mirror(Klass* k, TRAPS);
static oop create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS);
static void update_archived_primitive_mirror_native_pointers(oop archived_mirror) NOT_CDS_JAVA_HEAP_RETURN;
@@ -332,6 +334,8 @@ class java_lang_Class : AllStatic {
static oop component_mirror(oop java_class);
static objArrayOop signers(oop java_class);
static void set_signers(oop java_class, objArrayOop signers);
+ static oop class_data(oop java_class);
+ static void set_class_data(oop java_class, oop classData);
static oop class_loader(oop java_class);
static void set_module(oop java_class, oop module);
@@ -1144,16 +1148,20 @@ class java_lang_invoke_MemberName: AllStatic {
// Relevant integer codes (keep these in synch. with MethodHandleNatives.Constants):
enum {
- MN_IS_METHOD = 0x00010000, // method (not constructor)
- MN_IS_CONSTRUCTOR = 0x00020000, // constructor
- MN_IS_FIELD = 0x00040000, // field
- MN_IS_TYPE = 0x00080000, // nested type
- MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
- MN_REFERENCE_KIND_SHIFT = 24, // refKind
- MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
+ MN_IS_METHOD = 0x00010000, // method (not constructor)
+ MN_IS_CONSTRUCTOR = 0x00020000, // constructor
+ MN_IS_FIELD = 0x00040000, // field
+ MN_IS_TYPE = 0x00080000, // nested type
+ MN_CALLER_SENSITIVE = 0x00100000, // @CallerSensitive annotation detected
+ MN_REFERENCE_KIND_SHIFT = 24, // refKind
+ MN_REFERENCE_KIND_MASK = 0x0F000000 >> MN_REFERENCE_KIND_SHIFT,
// The SEARCH_* bits are not for MN.flags but for the matchFlags argument of MHN.getMembers:
- MN_SEARCH_SUPERCLASSES = 0x00100000, // walk super classes
- MN_SEARCH_INTERFACES = 0x00200000 // walk implemented interfaces
+ MN_SEARCH_SUPERCLASSES = 0x00100000, // walk super classes
+ MN_SEARCH_INTERFACES = 0x00200000, // walk implemented interfaces
+ MN_NESTMATE_CLASS = 0x00000001,
+ MN_HIDDEN_CLASS = 0x00000002,
+ MN_STRONG_LOADER_LINK = 0x00000004,
+ MN_ACCESS_VM_ANNOTATIONS = 0x00000008
};
// Accessors for code generation:
diff --git a/src/hotspot/share/classfile/klassFactory.cpp b/src/hotspot/share/classfile/klassFactory.cpp
index 13e9b9a6c96..8efe2b0ca0c 100644
--- a/src/hotspot/share/classfile/klassFactory.cpp
+++ b/src/hotspot/share/classfile/klassFactory.cpp
@@ -79,16 +79,18 @@ InstanceKlass* KlassFactory::check_shared_class_file_load_hook(
end_ptr - ptr,
cfs->source(),
ClassFileStream::verify);
+ ClassLoadInfo cl_info(protection_domain);
ClassFileParser parser(stream,
class_name,
loader_data,
- protection_domain,
- NULL,
- NULL,
+ &cl_info,
ClassFileParser::BROADCAST, // publicity level
CHECK_NULL);
- InstanceKlass* new_ik = parser.create_instance_klass(true /* changed_by_loadhook */,
+ const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
+ InstanceKlass* new_ik = parser.create_instance_klass(true, // changed_by_loadhook
+ *cl_inst_info, // dynamic_nest_host and classData
CHECK_NULL);
+
if (cached_class_file != NULL) {
new_ik->set_cached_class_file(cached_class_file);
}
@@ -165,9 +167,7 @@ static ClassFileStream* check_class_file_load_hook(ClassFileStream* stream,
InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
- Handle protection_domain,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo& cl_info,
TRAPS) {
assert(stream != NULL, "invariant");
assert(loader_data != NULL, "invariant");
@@ -183,12 +183,15 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
// increment counter
THREAD->statistical_info().incr_define_class_count();
- // Skip this processing for VM anonymous classes
- if (unsafe_anonymous_host == NULL) {
+ assert(!(cl_info.is_hidden() && (cl_info.unsafe_anonymous_host() != NULL)),
+ "hidden class has an anonymous host");
+
+ // Skip this processing for VM hidden or anonymous classes
+ if (!cl_info.is_hidden() && (cl_info.unsafe_anonymous_host() == NULL)) {
stream = check_class_file_load_hook(stream,
name,
loader_data,
- protection_domain,
+ cl_info.protection_domain(),
&cached_class_file,
CHECK_NULL);
}
@@ -196,14 +199,12 @@ InstanceKlass* KlassFactory::create_from_stream(ClassFileStream* stream,
ClassFileParser parser(stream,
name,
loader_data,
- protection_domain,
- unsafe_anonymous_host,
- cp_patches,
+ &cl_info,
ClassFileParser::BROADCAST, // publicity level
CHECK_NULL);
- InstanceKlass* result = parser.create_instance_klass(old_stream != stream, CHECK_NULL);
- assert(result == parser.create_instance_klass(old_stream != stream, THREAD), "invariant");
+ const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
+ InstanceKlass* result = parser.create_instance_klass(old_stream != stream, *cl_inst_info, CHECK_NULL);
if (result == NULL) {
return NULL;
diff --git a/src/hotspot/share/classfile/klassFactory.hpp b/src/hotspot/share/classfile/klassFactory.hpp
index 434fcd1faeb..97d49a52294 100644
--- a/src/hotspot/share/classfile/klassFactory.hpp
+++ b/src/hotspot/share/classfile/klassFactory.hpp
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+* Copyright (c) 2015, 2020, 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
@@ -30,6 +30,7 @@
class ClassFileStream;
class ClassLoaderData;
+class ClassLoadInfo;
template
class GrowableArray;
class Klass;
@@ -71,9 +72,7 @@ class KlassFactory : AllStatic {
static InstanceKlass* create_from_stream(ClassFileStream* stream,
Symbol* name,
ClassLoaderData* loader_data,
- Handle protection_domain,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo& cl_info,
TRAPS);
public:
static InstanceKlass* check_shared_class_file_load_hook(
diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp
index a966c0ae4c8..e394acff3db 100644
--- a/src/hotspot/share/classfile/moduleEntry.cpp
+++ b/src/hotspot/share/classfile/moduleEntry.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2020, 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
@@ -75,6 +75,7 @@ bool ModuleEntry::should_show_version() {
const char* loc = location()->as_C_string();
ClassLoaderData* cld = loader_data();
+ assert(!cld->has_class_mirror_holder(), "module's cld should have a ClassLoader holder not a Class holder");
if ((cld->is_the_null_class_loader_data() || cld->is_platform_class_loader_data()) &&
(strncmp(loc, "jrt:/java.", 10) == 0)) {
return false;
@@ -135,6 +136,7 @@ bool ModuleEntry::can_read(ModuleEntry* m) const {
// injecting dependencies that require the default read edges for resolution.
if (this->has_default_read_edges() && !m->is_named()) {
ClassLoaderData* cld = m->loader_data();
+ assert(!cld->has_class_mirror_holder(), "module's cld should have a ClassLoader holder not a Class holder");
if (cld->is_the_null_class_loader_data() || cld->is_system_class_loader_data()) {
return true; // default read edge
}
diff --git a/src/hotspot/share/classfile/moduleEntry.hpp b/src/hotspot/share/classfile/moduleEntry.hpp
index ed93ce59917..9240c0ebbcb 100644
--- a/src/hotspot/share/classfile/moduleEntry.hpp
+++ b/src/hotspot/share/classfile/moduleEntry.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2020, 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
@@ -110,7 +110,7 @@ public:
ClassLoaderData* loader_data() const { return _loader_data; }
void set_loader_data(ClassLoaderData* cld) {
- assert(!cld->is_unsafe_anonymous(), "Unexpected unsafe anonymous class loader data");
+ assert(!cld->has_class_mirror_holder(), "Unexpected has_class_mirror_holder cld");
_loader_data = cld;
}
diff --git a/src/hotspot/share/classfile/resolutionErrors.cpp b/src/hotspot/share/classfile/resolutionErrors.cpp
index 8aed61c4d35..45bb641ee95 100644
--- a/src/hotspot/share/classfile/resolutionErrors.cpp
+++ b/src/hotspot/share/classfile/resolutionErrors.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2020, 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
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/resolutionErrors.hpp"
+#include "memory/allocation.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/handles.inline.hpp"
@@ -42,6 +43,18 @@ void ResolutionErrorTable::add_entry(int index, unsigned int hash,
add_entry(index, entry);
}
+// add new entry to the table
+void ResolutionErrorTable::add_entry(int index, unsigned int hash,
+ const constantPoolHandle& pool, int cp_index,
+ const char* message)
+{
+ assert_locked_or_safepoint(SystemDictionary_lock);
+ assert(!pool.is_null() && message != NULL, "adding NULL obj");
+
+ ResolutionErrorEntry* entry = new_entry(hash, pool(), cp_index, message);
+ add_entry(index, entry);
+}
+
// find entry in the table
ResolutionErrorEntry* ResolutionErrorTable::find_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int cp_index)
@@ -59,9 +72,10 @@ ResolutionErrorEntry* ResolutionErrorTable::find_entry(int index, unsigned int h
}
void ResolutionErrorEntry::set_error(Symbol* e) {
- assert(e != NULL, "must set a value");
_error = e;
- _error->increment_refcount();
+ if (_error != NULL) {
+ _error->increment_refcount();
+ }
}
void ResolutionErrorEntry::set_message(Symbol* c) {
@@ -71,6 +85,10 @@ void ResolutionErrorEntry::set_message(Symbol* c) {
}
}
+void ResolutionErrorEntry::set_nest_host_error(const char* message) {
+ _nest_host_error = message;
+}
+
// create new error entry
ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, ConstantPool* pool,
int cp_index, Symbol* error,
@@ -80,17 +98,35 @@ ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, ConstantPool* po
entry->set_cp_index(cp_index);
entry->set_error(error);
entry->set_message(message);
+ entry->set_nest_host_error(NULL);
+
+ return entry;
+}
+
+// create new nest host error entry
+ResolutionErrorEntry* ResolutionErrorTable::new_entry(int hash, ConstantPool* pool,
+ int cp_index, const char* message)
+{
+ ResolutionErrorEntry* entry = (ResolutionErrorEntry*)Hashtable::new_entry(hash, pool);
+ entry->set_cp_index(cp_index);
+ entry->set_nest_host_error(message);
+ entry->set_error(NULL);
+ entry->set_message(NULL);
return entry;
}
void ResolutionErrorTable::free_entry(ResolutionErrorEntry *entry) {
// decrement error refcount
- assert(entry->error() != NULL, "error should be set");
- entry->error()->decrement_refcount();
+ if (entry->error() != NULL) {
+ entry->error()->decrement_refcount();
+ }
if (entry->message() != NULL) {
entry->message()->decrement_refcount();
}
+ if (entry->nest_host_error() != NULL) {
+ FREE_C_HEAP_ARRAY(char, entry->nest_host_error());
+ }
Hashtable::free_entry(entry);
}
diff --git a/src/hotspot/share/classfile/resolutionErrors.hpp b/src/hotspot/share/classfile/resolutionErrors.hpp
index 772e8df342b..932543413bf 100644
--- a/src/hotspot/share/classfile/resolutionErrors.hpp
+++ b/src/hotspot/share/classfile/resolutionErrors.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2020, 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
@@ -46,6 +46,8 @@ public:
ResolutionErrorEntry* new_entry(int hash, ConstantPool* pool, int cp_index,
Symbol* error, Symbol* message);
+ ResolutionErrorEntry* new_entry(int hash, ConstantPool* pool, int cp_index,
+ const char* message);
void free_entry(ResolutionErrorEntry *entry);
ResolutionErrorEntry* bucket(int i) {
@@ -64,6 +66,8 @@ public:
void add_entry(int index, unsigned int hash,
const constantPoolHandle& pool, int which, Symbol* error, Symbol* message);
+ void add_entry(int index, unsigned int hash,
+ const constantPoolHandle& pool, int which, const char* message);
// find error given the constant pool and constant pool index
ResolutionErrorEntry* find_entry(int index, unsigned int hash,
@@ -95,6 +99,7 @@ class ResolutionErrorEntry : public HashtableEntry {
int _cp_index;
Symbol* _error;
Symbol* _message;
+ const char* _nest_host_error;
public:
ConstantPool* pool() const { return literal(); }
@@ -108,6 +113,9 @@ class ResolutionErrorEntry : public HashtableEntry {
Symbol* message() const { return _message; }
void set_message(Symbol* c);
+ const char* nest_host_error() const { return _nest_host_error; }
+ void set_nest_host_error(const char* message);
+
ResolutionErrorEntry* next() const {
return (ResolutionErrorEntry*)HashtableEntry::next();
}
diff --git a/src/hotspot/share/classfile/symbolTable.cpp b/src/hotspot/share/classfile/symbolTable.cpp
index 17dfbc9599d..c2530653191 100644
--- a/src/hotspot/share/classfile/symbolTable.cpp
+++ b/src/hotspot/share/classfile/symbolTable.cpp
@@ -459,6 +459,8 @@ Symbol* SymbolTable::lookup_only_unicode(const jchar* name, int utf16_length,
void SymbolTable::new_symbols(ClassLoaderData* loader_data, const constantPoolHandle& cp,
int names_count, const char** names, int* lengths,
int* cp_indices, unsigned int* hashValues) {
+ // Note that c_heap will be true for non-strong hidden classes and unsafe anonymous classes
+ // even if their loader is the boot loader because they will have a different cld.
bool c_heap = !loader_data->is_the_null_class_loader_data();
for (int i = 0; i < names_count; i++) {
const char *name = names[i];
diff --git a/src/hotspot/share/classfile/systemDictionary.cpp b/src/hotspot/share/classfile/systemDictionary.cpp
index c1ac6a231c4..15a6f0af9b8 100644
--- a/src/hotspot/share/classfile/systemDictionary.cpp
+++ b/src/hotspot/share/classfile/systemDictionary.cpp
@@ -111,6 +111,46 @@ oop SystemDictionary::_java_platform_loader = NULL;
const int defaultProtectionDomainCacheSize = 1009;
+ClassLoadInfo::ClassLoadInfo() {
+ _protection_domain = Handle();
+ _unsafe_anonymous_host = NULL;
+ _cp_patches = NULL;
+ _class_hidden_info._dynamic_nest_host = NULL;
+ _class_hidden_info._class_data = Handle();
+ _is_hidden = false;
+ _is_strong_hidden = false;
+ _can_access_vm_annotations = false;
+}
+
+ClassLoadInfo::ClassLoadInfo(Handle protection_domain) {
+ _protection_domain = protection_domain;
+ _unsafe_anonymous_host = NULL;
+ _cp_patches = NULL;
+ _class_hidden_info._dynamic_nest_host = NULL;
+ _class_hidden_info._class_data = Handle();
+ _is_hidden = false;
+ _is_strong_hidden = false;
+ _can_access_vm_annotations = false;
+}
+
+ClassLoadInfo::ClassLoadInfo(Handle protection_domain,
+ const InstanceKlass* unsafe_anonymous_host,
+ GrowableArray* cp_patches,
+ InstanceKlass* dynamic_nest_host,
+ Handle class_data,
+ bool is_hidden,
+ bool is_strong_hidden,
+ bool can_access_vm_annotations) {
+ _protection_domain = protection_domain;
+ _unsafe_anonymous_host = unsafe_anonymous_host;
+ _cp_patches = cp_patches;
+ _class_hidden_info._dynamic_nest_host = dynamic_nest_host;
+ _class_hidden_info._class_data = class_data;
+ _is_hidden = is_hidden;
+ _is_strong_hidden = is_strong_hidden;
+ _can_access_vm_annotations = can_access_vm_annotations;
+}
+
// ----------------------------------------------------------------------------
// Java-level SystemLoader and PlatformLoader
@@ -822,7 +862,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
// class loaders holding the ObjectLock shouldn't find the class here
InstanceKlass* check = find_class(d_hash, name, dictionary);
if (check != NULL) {
- // Klass is already loaded, so return it after checking/adding protection domain
+ // Klass is already loaded, so return it after checking/adding protection domain
k = check;
class_has_been_loaded = true;
}
@@ -982,24 +1022,36 @@ Klass* SystemDictionary::find_instance_or_array_klass(Symbol* class_name,
// Note: this method is much like resolve_from_stream, but
// does not publish the classes via the SystemDictionary.
-// Handles unsafe_DefineAnonymousClass and redefineclasses
-// RedefinedClasses do not add to the class hierarchy
+// Handles Lookup.defineClass hidden, unsafe_DefineAnonymousClass
+// and redefineclasses. RedefinedClasses do not add to the class hierarchy.
InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
Handle class_loader,
- Handle protection_domain,
ClassFileStream* st,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo& cl_info,
TRAPS) {
EventClassLoad class_load_start_event;
ClassLoaderData* loader_data;
- if (unsafe_anonymous_host != NULL) {
- // Create a new CLD for an unsafe anonymous class, that uses the same class loader
- // as the unsafe_anonymous_host
- guarantee(unsafe_anonymous_host->class_loader() == class_loader(), "should be the same");
- loader_data = ClassLoaderData::unsafe_anonymous_class_loader_data(class_loader);
+
+ bool is_unsafe_anon_class = cl_info.unsafe_anonymous_host() != NULL;
+
+ if (is_unsafe_anon_class) {
+ // - for unsafe anonymous class: create a new CLD whith a class holder that uses
+ // the same class loader as the unsafe_anonymous_host.
+ guarantee(cl_info.unsafe_anonymous_host()->class_loader() == class_loader(),
+ "should be the same");
+ loader_data = ClassLoaderData::has_class_mirror_holder_cld(class_loader);
+ } else if (cl_info.is_hidden()) {
+ // - for hidden classes that are not strong: create a new CLD that has a class holder and
+ // whose loader is the Lookup class' loader.
+ // - for hidden class: add the class to the Lookup class' loader's CLD.
+ if (!cl_info.is_strong_hidden()) {
+ loader_data = ClassLoaderData::has_class_mirror_holder_cld(class_loader);
+ } else {
+ // This hidden class goes into the regular CLD pool for this loader.
+ loader_data = register_loader(class_loader);
+ }
} else {
loader_data = ClassLoaderData::class_loader_data(class_loader());
}
@@ -1015,15 +1067,16 @@ InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
InstanceKlass* k = KlassFactory::create_from_stream(st,
class_name,
loader_data,
- protection_domain,
- unsafe_anonymous_host,
- cp_patches,
+ cl_info,
CHECK_NULL);
- if (unsafe_anonymous_host != NULL && k != NULL) {
- // Unsafe anonymous classes must update ClassLoaderData holder (was unsafe_anonymous_host loader)
- // so that they can be unloaded when the mirror is no longer referenced.
- k->class_loader_data()->initialize_holder(Handle(THREAD, k->java_mirror()));
+ if ((cl_info.is_hidden() || is_unsafe_anon_class) && k != NULL) {
+ // Hidden classes that are not strong and unsafe anonymous classes must update
+ // ClassLoaderData holder so that they can be unloaded when the mirror is no
+ // longer referenced.
+ if (!cl_info.is_strong_hidden() || is_unsafe_anon_class) {
+ k->class_loader_data()->initialize_holder(Handle(THREAD, k->java_mirror()));
+ }
{
MutexLocker mu_r(THREAD, Compile_lock);
@@ -1036,12 +1089,14 @@ InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
// Rewrite and patch constant pool here.
k->link_class(CHECK_NULL);
- if (cp_patches != NULL) {
- k->constants()->patch_resolved_references(cp_patches);
+ if (cl_info.cp_patches() != NULL) {
+ k->constants()->patch_resolved_references(cl_info.cp_patches());
}
// If it's anonymous, initialize it now, since nobody else will.
- k->eager_initialize(CHECK_NULL);
+ if (is_unsafe_anon_class) {
+ k->eager_initialize(CHECK_NULL);
+ }
// notify jvmti
if (JvmtiExport::should_post_class_load()) {
@@ -1052,7 +1107,7 @@ InstanceKlass* SystemDictionary::parse_stream(Symbol* class_name,
post_class_load_event(&class_load_start_event, k, loader_data);
}
}
- assert(unsafe_anonymous_host != NULL || NULL == cp_patches,
+ assert(is_unsafe_anon_class || NULL == cl_info.cp_patches(),
"cp_patches only found with unsafe_anonymous_host");
return k;
@@ -1107,13 +1162,8 @@ InstanceKlass* SystemDictionary::resolve_from_stream(Symbol* class_name,
if (st->buffer() == NULL) {
return NULL;
}
- k = KlassFactory::create_from_stream(st,
- class_name,
- loader_data,
- protection_domain,
- NULL, // unsafe_anonymous_host
- NULL, // cp_patches
- CHECK_NULL);
+ ClassLoadInfo cl_info(protection_domain);
+ k = KlassFactory::create_from_stream(st, class_name, loader_data, cl_info, CHECK_NULL);
}
assert(k != NULL, "no klass created");
@@ -2327,6 +2377,42 @@ Symbol* SystemDictionary::find_resolution_error(const constantPoolHandle& pool,
}
}
+// Add an entry to resolution error table to record an error in resolving or
+// validating a nest host. This is used to construct informative error
+// messages when IllegalAccessError's occur. If an entry already exists it will
+// be updated with the nest host error message.
+void SystemDictionary::add_nest_host_error(const constantPoolHandle& pool,
+ int which,
+ const char* message) {
+ unsigned int hash = resolution_errors()->compute_hash(pool, which);
+ int index = resolution_errors()->hash_to_index(hash);
+ {
+ MutexLocker ml(Thread::current(), SystemDictionary_lock);
+ ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which);
+ if (entry != NULL) {
+ assert(entry->nest_host_error() == NULL, "Nest host error message already set!");
+ entry->set_nest_host_error(message);
+ } else {
+ resolution_errors()->add_entry(index, hash, pool, which, message);
+ }
+ }
+}
+
+// Lookup any nest host error
+const char* SystemDictionary::find_nest_host_error(const constantPoolHandle& pool, int which) {
+ unsigned int hash = resolution_errors()->compute_hash(pool, which);
+ int index = resolution_errors()->hash_to_index(hash);
+ {
+ MutexLocker ml(Thread::current(), SystemDictionary_lock);
+ ResolutionErrorEntry* entry = resolution_errors()->find_entry(index, hash, pool, which);
+ if (entry != NULL) {
+ return entry->nest_host_error();
+ } else {
+ return NULL;
+ }
+ }
+}
+
// Signature constraints ensure that callers and callees agree about
// the meaning of type names in their signatures. This routine is the
diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp
index 0af6858dd9a..e3623fa85df 100644
--- a/src/hotspot/share/classfile/systemDictionary.hpp
+++ b/src/hotspot/share/classfile/systemDictionary.hpp
@@ -34,6 +34,53 @@
#include "runtime/signature.hpp"
#include "utilities/hashtable.hpp"
+class ClassInstanceInfo : public StackObj {
+ private:
+ InstanceKlass* _dynamic_nest_host;
+ Handle _class_data;
+
+ public:
+ ClassInstanceInfo() {
+ _dynamic_nest_host = NULL;
+ _class_data = Handle();
+ }
+ ClassInstanceInfo(InstanceKlass* dynamic_nest_host, Handle class_data) {
+ _dynamic_nest_host = dynamic_nest_host;
+ _class_data = class_data;
+ }
+
+ InstanceKlass* dynamic_nest_host() const { return _dynamic_nest_host; }
+ Handle class_data() const { return _class_data; }
+ friend class ClassLoadInfo;
+};
+
+class ClassLoadInfo : public StackObj {
+ private:
+ Handle _protection_domain;
+ const InstanceKlass* _unsafe_anonymous_host;
+ GrowableArray* _cp_patches;
+ ClassInstanceInfo _class_hidden_info;
+ bool _is_hidden;
+ bool _is_strong_hidden;
+ bool _can_access_vm_annotations;
+
+ public:
+ ClassLoadInfo();
+ ClassLoadInfo(Handle protection_domain);
+ ClassLoadInfo(Handle protection_domain, const InstanceKlass* unsafe_anonymous_host,
+ GrowableArray* cp_patches, InstanceKlass* dynamic_nest_host,
+ Handle class_data, bool is_hidden, bool is_strong_hidden,
+ bool can_access_vm_annotations);
+
+ Handle protection_domain() const { return _protection_domain; }
+ const InstanceKlass* unsafe_anonymous_host() const { return _unsafe_anonymous_host; }
+ GrowableArray* cp_patches() const { return _cp_patches; }
+ const ClassInstanceInfo* class_hidden_info_ptr() const { return &_class_hidden_info; }
+ bool is_hidden() const { return _is_hidden; }
+ bool is_strong_hidden() const { return _is_strong_hidden; }
+ bool can_access_vm_annotations() const { return _can_access_vm_annotations; }
+};
+
// The dictionary in each ClassLoaderData stores all loaded classes, either
// initiatied by its class loader or defined by its class loader:
//
@@ -271,28 +318,13 @@ public:
bool is_superclass,
TRAPS);
- // Parse new stream. This won't update the dictionary or
- // class hierarchy, simply parse the stream. Used by JVMTI RedefineClasses.
- // Also used by Unsafe_DefineAnonymousClass
+ // Parse new stream. This won't update the dictionary or class
+ // hierarchy, simply parse the stream. Used by JVMTI RedefineClasses
+ // and by Unsafe_DefineAnonymousClass and jvm_lookup_define_class.
static InstanceKlass* parse_stream(Symbol* class_name,
Handle class_loader,
- Handle protection_domain,
ClassFileStream* st,
- TRAPS) {
- return parse_stream(class_name,
- class_loader,
- protection_domain,
- st,
- NULL, // unsafe_anonymous_host
- NULL, // cp_patches
- THREAD);
- }
- static InstanceKlass* parse_stream(Symbol* class_name,
- Handle class_loader,
- Handle protection_domain,
- ClassFileStream* st,
- const InstanceKlass* unsafe_anonymous_host,
- GrowableArray* cp_patches,
+ const ClassLoadInfo& cl_info,
TRAPS);
// Resolve from stream (called by jni_DefineClass and JVM_DefineClass)
@@ -530,6 +562,11 @@ public:
Symbol** message);
+ // Record a nest host resolution/validation error
+ static void add_nest_host_error(const constantPoolHandle& pool, int which,
+ const char* message);
+ static const char* find_nest_host_error(const constantPoolHandle& pool, int which);
+
static ProtectionDomainCacheEntry* cache_get(Handle protection_domain);
protected:
diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp
index da14cbae971..5b86621fc8f 100644
--- a/src/hotspot/share/classfile/systemDictionaryShared.cpp
+++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp
@@ -915,7 +915,7 @@ InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name,
if (!UseSharedSpaces) {
return NULL;
}
- if (class_name == NULL) { // don't do this for anonymous classes
+ if (class_name == NULL) { // don't do this for hidden and unsafe anonymous classes
return NULL;
}
if (class_loader.is_null() ||
@@ -1097,9 +1097,9 @@ void SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason)
}
bool SystemDictionaryShared::should_be_excluded(InstanceKlass* k) {
- if (k->class_loader_data()->is_unsafe_anonymous()) {
- warn_excluded(k, "Unsafe anonymous class");
- return true; // unsafe anonymous classes are not archived, skip
+ if (k->is_hidden() || k->is_unsafe_anonymous()) {
+ warn_excluded(k, "Hidden or Unsafe anonymous class");
+ return true; // hidden and unsafe anonymous classes are not archived, skip
}
if (k->is_in_error_state()) {
warn_excluded(k, "In error state");
diff --git a/src/hotspot/share/classfile/verificationType.cpp b/src/hotspot/share/classfile/verificationType.cpp
index 34027a7c252..a6ac7bc906b 100644
--- a/src/hotspot/share/classfile/verificationType.cpp
+++ b/src/hotspot/share/classfile/verificationType.cpp
@@ -48,11 +48,16 @@ VerificationType VerificationType::from_tag(u1 tag) {
bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Symbol* name,
Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) {
HandleMark hm(THREAD);
- Klass* this_class = SystemDictionary::resolve_or_fail(
+ Klass* this_class;
+ if (klass->is_hidden() && klass->name() == name) {
+ this_class = klass;
+ } else {
+ this_class = SystemDictionary::resolve_or_fail(
name, Handle(THREAD, klass->class_loader()),
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(this_class, klass);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(this_class, klass);
+ }
}
if (this_class->is_interface() && (!from_field_is_protected ||
@@ -65,11 +70,16 @@ bool VerificationType::resolve_and_check_assignability(InstanceKlass* klass, Sym
this_class == SystemDictionary::Cloneable_klass() ||
this_class == SystemDictionary::Serializable_klass();
} else if (from_is_object) {
- Klass* from_class = SystemDictionary::resolve_or_fail(
+ Klass* from_class;
+ if (klass->is_hidden() && klass->name() == from_name) {
+ from_class = klass;
+ } else {
+ from_class = SystemDictionary::resolve_or_fail(
from_name, Handle(THREAD, klass->class_loader()),
Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(from_class, klass);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(from_class, klass);
+ }
}
return from_class->is_subclass_of(this_class);
}
diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp
index e409ebf8cb9..9711662698b 100644
--- a/src/hotspot/share/classfile/verifier.cpp
+++ b/src/hotspot/share/classfile/verifier.cpp
@@ -2081,6 +2081,8 @@ Klass* ClassVerifier::load_class(Symbol* name, TRAPS) {
oop loader = current_class()->class_loader();
oop protection_domain = current_class()->protection_domain();
+ assert(name_in_supers(name, current_class()), "name should be a super class");
+
Klass* kls = SystemDictionary::resolve_or_fail(
name, Handle(THREAD, loader), Handle(THREAD, protection_domain),
true, THREAD);
diff --git a/src/hotspot/share/classfile/vmSymbols.cpp b/src/hotspot/share/classfile/vmSymbols.cpp
index 51cf1279505..6aa26f285b1 100644
--- a/src/hotspot/share/classfile/vmSymbols.cpp
+++ b/src/hotspot/share/classfile/vmSymbols.cpp
@@ -546,6 +546,7 @@ bool vmIntrinsics::is_disabled_by_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_isInterface:
case vmIntrinsics::_isArray:
case vmIntrinsics::_isPrimitive:
+ case vmIntrinsics::_isHidden:
case vmIntrinsics::_getSuperclass:
case vmIntrinsics::_Class_cast:
case vmIntrinsics::_getLength:
diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp
index 617fa7df991..94c632cf7d0 100644
--- a/src/hotspot/share/classfile/vmSymbols.hpp
+++ b/src/hotspot/share/classfile/vmSymbols.hpp
@@ -884,6 +884,8 @@
do_name( isArray_name, "isArray") \
do_intrinsic(_isPrimitive, java_lang_Class, isPrimitive_name, void_boolean_signature, F_RN) \
do_name( isPrimitive_name, "isPrimitive") \
+ do_intrinsic(_isHidden, java_lang_Class, isHidden_name, void_boolean_signature, F_RN) \
+ do_name( isHidden_name, "isHidden") \
do_intrinsic(_getSuperclass, java_lang_Class, getSuperclass_name, void_class_signature, F_RN) \
do_name( getSuperclass_name, "getSuperclass") \
do_intrinsic(_Class_cast, java_lang_Class, Class_cast_name, object_object_signature, F_R) \
diff --git a/src/hotspot/share/include/jvm.h b/src/hotspot/share/include/jvm.h
index a1b693edbba..6db1b264369 100644
--- a/src/hotspot/share/include/jvm.h
+++ b/src/hotspot/share/include/jvm.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2020, 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
@@ -388,6 +388,21 @@ JVM_DefineClassWithSource(JNIEnv *env, const char *name, jobject loader,
const jbyte *buf, jsize len, jobject pd,
const char *source);
+/*
+ * Define a class with the specified lookup class.
+ * lookup: Lookup class
+ * name: the name of the class
+ * 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; may be null
+ */
+JNIEXPORT jclass JNICALL
+JVM_LookupDefineClass(JNIEnv *env, jclass lookup, const char *name, const jbyte *buf,
+ jsize len, jobject pd, jboolean init, int flags, jobject classData);
+
/*
* Module support funcions
*/
@@ -473,6 +488,9 @@ JVM_IsArrayClass(JNIEnv *env, jclass cls);
JNIEXPORT jboolean JNICALL
JVM_IsPrimitiveClass(JNIEnv *env, jclass cls);
+JNIEXPORT jboolean JNICALL
+JVM_IsHiddenClass(JNIEnv *env, jclass cls);
+
JNIEXPORT jint JNICALL
JVM_GetClassModifiers(JNIEnv *env, jclass cls);
diff --git a/src/hotspot/share/interpreter/linkResolver.cpp b/src/hotspot/share/interpreter/linkResolver.cpp
index 389fd9b9761..cded39c86cb 100644
--- a/src/hotspot/share/interpreter/linkResolver.cpp
+++ b/src/hotspot/share/interpreter/linkResolver.cpp
@@ -535,6 +535,21 @@ Method* LinkResolver::lookup_polymorphic_method(const LinkInfo& link_info,
return NULL;
}
+static void print_nest_host_error_on(stringStream* ss, Klass* ref_klass, Klass* sel_klass, TRAPS) {
+ assert(ref_klass->is_instance_klass(), "must be");
+ assert(sel_klass->is_instance_klass(), "must be");
+ InstanceKlass* ref_ik = InstanceKlass::cast(ref_klass);
+ InstanceKlass* sel_ik = InstanceKlass::cast(sel_klass);
+ const char* nest_host_error_1 = ref_ik->nest_host_error(THREAD);
+ const char* nest_host_error_2 = sel_ik->nest_host_error(THREAD);
+ if (nest_host_error_1 != NULL || nest_host_error_2 != NULL) {
+ ss->print(", (%s%s%s)",
+ (nest_host_error_1 != NULL) ? nest_host_error_1 : "",
+ (nest_host_error_1 != NULL && nest_host_error_2 != NULL) ? ", " : "",
+ (nest_host_error_2 != NULL) ? nest_host_error_2 : "");
+ }
+}
+
void LinkResolver::check_method_accessability(Klass* ref_klass,
Klass* resolved_klass,
Klass* sel_klass,
@@ -567,24 +582,34 @@ void LinkResolver::check_method_accessability(Klass* ref_klass,
sel_klass,
flags,
true, false, CHECK);
- // Any existing exceptions that may have been thrown, for example LinkageErrors
- // from nest-host resolution, have been allowed to propagate.
+ // Any existing exceptions that may have been thrown
+ // have been allowed to propagate.
if (!can_access) {
ResourceMark rm(THREAD);
+ stringStream ss;
bool same_module = (sel_klass->module() == ref_klass->module());
- Exceptions::fthrow(
- THREAD_AND_LOCATION,
- vmSymbols::java_lang_IllegalAccessError(),
- "class %s tried to access %s%s%smethod '%s' (%s%s%s)",
- ref_klass->external_name(),
- sel_method->is_abstract() ? "abstract " : "",
- sel_method->is_protected() ? "protected " : "",
- sel_method->is_private() ? "private " : "",
- sel_method->external_name(),
- (same_module) ? ref_klass->joint_in_module_of_loader(sel_klass) : ref_klass->class_in_module_of_loader(),
- (same_module) ? "" : "; ",
- (same_module) ? "" : sel_klass->class_in_module_of_loader()
- );
+ ss.print("class %s tried to access %s%s%smethod '%s' (%s%s%s)",
+ ref_klass->external_name(),
+ sel_method->is_abstract() ? "abstract " : "",
+ sel_method->is_protected() ? "protected " : "",
+ sel_method->is_private() ? "private " : "",
+ sel_method->external_name(),
+ (same_module) ? ref_klass->joint_in_module_of_loader(sel_klass) : ref_klass->class_in_module_of_loader(),
+ (same_module) ? "" : "; ",
+ (same_module) ? "" : sel_klass->class_in_module_of_loader()
+ );
+
+ // For private access see if there was a problem with nest host
+ // resolution, and if so report that as part of the message.
+ if (sel_method->is_private()) {
+ print_nest_host_error_on(&ss, ref_klass, sel_klass, THREAD);
+ }
+
+ Exceptions::fthrow(THREAD_AND_LOCATION,
+ vmSymbols::java_lang_IllegalAccessError(),
+ "%s",
+ ss.as_string()
+ );
return;
}
}
@@ -903,19 +928,27 @@ void LinkResolver::check_field_accessability(Klass* ref_klass,
if (!can_access) {
bool same_module = (sel_klass->module() == ref_klass->module());
ResourceMark rm(THREAD);
- Exceptions::fthrow(
- THREAD_AND_LOCATION,
- vmSymbols::java_lang_IllegalAccessError(),
- "class %s tried to access %s%sfield %s.%s (%s%s%s)",
- ref_klass->external_name(),
- fd.is_protected() ? "protected " : "",
- fd.is_private() ? "private " : "",
- sel_klass->external_name(),
- fd.name()->as_C_string(),
- (same_module) ? ref_klass->joint_in_module_of_loader(sel_klass) : ref_klass->class_in_module_of_loader(),
- (same_module) ? "" : "; ",
- (same_module) ? "" : sel_klass->class_in_module_of_loader()
- );
+ stringStream ss;
+ ss.print("class %s tried to access %s%sfield %s.%s (%s%s%s)",
+ ref_klass->external_name(),
+ fd.is_protected() ? "protected " : "",
+ fd.is_private() ? "private " : "",
+ sel_klass->external_name(),
+ fd.name()->as_C_string(),
+ (same_module) ? ref_klass->joint_in_module_of_loader(sel_klass) : ref_klass->class_in_module_of_loader(),
+ (same_module) ? "" : "; ",
+ (same_module) ? "" : sel_klass->class_in_module_of_loader()
+ );
+ // For private access see if there was a problem with nest host
+ // resolution, and if so report that as part of the message.
+ if (fd.is_private()) {
+ print_nest_host_error_on(&ss, ref_klass, sel_klass, THREAD);
+ }
+ Exceptions::fthrow(THREAD_AND_LOCATION,
+ vmSymbols::java_lang_IllegalAccessError(),
+ "%s",
+ ss.as_string()
+ );
return;
}
}
diff --git a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
index fa40e07809a..4c359f07c8e 100644
--- a/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
+++ b/src/hotspot/share/jfr/instrumentation/jfrEventClassTransformer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2020, 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
@@ -1460,12 +1460,11 @@ static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStre
Handle pd(THREAD, ik->protection_domain());
Symbol* const class_name = ik->name();
const char* const klass_name = class_name != NULL ? class_name->as_C_string() : "";
+ ClassLoadInfo cl_info(pd);
ClassFileParser new_parser(stream,
class_name,
cld,
- pd,
- NULL, // host klass
- NULL, // cp_patches
+ &cl_info,
ClassFileParser::INTERNAL, // internal visibility
THREAD);
if (HAS_PENDING_EXCEPTION) {
@@ -1473,7 +1472,8 @@ static InstanceKlass* create_new_instance_klass(InstanceKlass* ik, ClassFileStre
CLEAR_PENDING_EXCEPTION;
return NULL;
}
- InstanceKlass* const new_ik = new_parser.create_instance_klass(false, THREAD);
+ const ClassInstanceInfo* cl_inst_info = cl_info.class_hidden_info_ptr();
+ InstanceKlass* const new_ik = new_parser.create_instance_klass(false, *cl_inst_info, THREAD);
if (HAS_PENDING_EXCEPTION) {
log_pending_exception(PENDING_EXCEPTION);
CLEAR_PENDING_EXCEPTION;
diff --git a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp
index ee6f8dd3e2e..2399275712a 100644
--- a/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp
+++ b/src/hotspot/share/jfr/leakprofiler/checkpoint/objectSampleDescription.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -162,10 +162,9 @@ void ObjectSampleDescription::write_class_name() {
if (k->is_instance_klass()) {
const InstanceKlass* ik = InstanceKlass::cast(k);
- if (ik->is_unsafe_anonymous()) {
+ if (ik->is_unsafe_anonymous() || ik->is_hidden()) {
return;
}
- assert(!ik->is_unsafe_anonymous(), "invariant");
const Symbol* name = ik->name();
if (name != NULL) {
write_text("Class Name: ");
diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml
index 81db849bdcc..f48c55c6f9e 100644
--- a/src/hotspot/share/jfr/metadata/metadata.xml
+++ b/src/hotspot/share/jfr/metadata/metadata.xml
@@ -219,6 +219,7 @@
stackTrace="true">
+
@@ -227,6 +228,7 @@
+
@@ -729,6 +731,11 @@
description="Total size of all allocated metaspace chunks for unsafe anonymous classes (each chunk has several blocks)" />
+
+
+
@@ -1135,6 +1142,7 @@
+
diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
index 526574146bc..066c1e8ede6 100644
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -483,6 +483,9 @@ public:
event.set_unsafeAnonymousClassCount(cls->_anon_classes_count);
event.set_unsafeAnonymousChunkSize(cls->_anon_chunk_sz);
event.set_unsafeAnonymousBlockSize(cls->_anon_block_sz);
+ event.set_hiddenClassCount(cls->_hidden_classes_count);
+ event.set_hiddenChunkSize(cls->_hidden_chunk_sz);
+ event.set_hiddenBlockSize(cls->_hidden_block_sz);
event.commit();
return true;
}
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
index fa5534b4c5e..71788c2b11c 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
@@ -137,7 +137,6 @@ static traceid method_id(KlassPtr klass, MethodPtr method) {
static traceid cld_id(CldPtr cld, bool leakp) {
assert(cld != NULL, "invariant");
- assert(!cld->is_unsafe_anonymous(), "invariant");
if (leakp) {
SET_LEAKP(cld);
} else {
@@ -163,6 +162,7 @@ static ClassLoaderData* get_cld(const Klass* klass) {
if (klass->is_objArray_klass()) {
klass = ObjArrayKlass::cast(klass)->bottom_klass();
}
+ if (klass->is_non_strong_hidden()) return NULL;
return is_unsafe_anonymous(klass) ?
InstanceKlass::cast(klass)->unsafe_anonymous_host()->class_loader_data() : klass->class_loader_data();
}
@@ -188,10 +188,12 @@ static int write_klass(JfrCheckpointWriter* writer, KlassPtr klass, bool leakp)
assert(_artifacts != NULL, "invariant");
assert(klass != NULL, "invariant");
writer->write(artifact_id(klass));
- writer->write(cld_id(get_cld(klass), leakp));
+ ClassLoaderData* cld = get_cld(klass);
+ writer->write(cld != NULL ? cld_id(cld, leakp) : 0);
writer->write(mark_symbol(klass, leakp));
writer->write(package_id(klass, leakp));
writer->write(get_flags(klass));
+ writer->write(klass->is_hidden());
return 1;
}
@@ -546,7 +548,6 @@ static void clear_modules() {
static int write_classloader(JfrCheckpointWriter* writer, CldPtr cld, bool leakp) {
assert(cld != NULL, "invariant");
- assert(!cld->is_unsafe_anonymous(), "invariant");
// class loader type
const Klass* class_loader_klass = cld->class_loader_klass();
if (class_loader_klass == NULL) {
@@ -604,7 +605,7 @@ class CLDCallback : public CLDClosure {
CLDCallback() {}
void do_cld(ClassLoaderData* cld) {
assert(cld != NULL, "invariant");
- if (cld->is_unsafe_anonymous()) {
+ if (cld->has_class_mirror_holder()) {
return;
}
do_class_loader_data(cld);
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp
index 541a4023ed6..657af666115 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -189,52 +189,54 @@ traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
* caller needs ResourceMark
*/
-uintptr_t JfrSymbolId::unsafe_anonymous_klass_name_hash(const InstanceKlass* ik) {
+uintptr_t JfrSymbolId::hidden_or_anon_klass_name_hash(const InstanceKlass* ik) {
assert(ik != NULL, "invariant");
- assert(ik->is_unsafe_anonymous(), "invariant");
+ assert(ik->is_unsafe_anonymous() || ik->is_hidden(), "invariant");
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
return (uintptr_t)mirror->identity_hash();
}
-static const char* create_unsafe_anonymous_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
+static const char* create_hidden_or_anon_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
assert(ik != NULL, "invariant");
- assert(ik->is_unsafe_anonymous(), "invariant");
+ assert(ik->is_unsafe_anonymous() || ik->is_hidden(), "invariant");
assert(hash != 0, "invariant");
- char* anonymous_symbol = NULL;
+ char* hidden_or_anon_symbol = NULL;
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
char hash_buf[40];
sprintf(hash_buf, "/" UINTX_FORMAT, hash);
const size_t hash_len = strlen(hash_buf);
const size_t result_len = ik->name()->utf8_length();
- anonymous_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
- ik->name()->as_klass_external_name(anonymous_symbol, (int)result_len + 1);
- assert(strlen(anonymous_symbol) == result_len, "invariant");
- strcpy(anonymous_symbol + result_len, hash_buf);
- assert(strlen(anonymous_symbol) == result_len + hash_len, "invariant");
- return anonymous_symbol;
+ hidden_or_anon_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
+ ik->name()->as_klass_external_name(hidden_or_anon_symbol, (int)result_len + 1);
+ assert(strlen(hidden_or_anon_symbol) == result_len, "invariant");
+ strcpy(hidden_or_anon_symbol + result_len, hash_buf);
+ assert(strlen(hidden_or_anon_symbol) == result_len + hash_len, "invariant");
+ return hidden_or_anon_symbol;
}
-bool JfrSymbolId::is_unsafe_anonymous_klass(const Klass* k) {
+bool JfrSymbolId::is_hidden_or_anon_klass(const Klass* k) {
assert(k != NULL, "invariant");
- return k->is_instance_klass() && ((const InstanceKlass*)k)->is_unsafe_anonymous();
+ return k->is_instance_klass() &&
+ (((const InstanceKlass*)k)->is_unsafe_anonymous() ||
+ ((const InstanceKlass*)k)->is_hidden());
}
-traceid JfrSymbolId::mark_unsafe_anonymous_klass_name(const InstanceKlass* ik, bool leakp) {
+traceid JfrSymbolId::mark_hidden_or_anon_klass_name(const InstanceKlass* ik, bool leakp) {
assert(ik != NULL, "invariant");
- assert(ik->is_unsafe_anonymous(), "invariant");
- const uintptr_t hash = unsafe_anonymous_klass_name_hash(ik);
- const char* const anonymous_klass_symbol = create_unsafe_anonymous_klass_symbol(ik, hash);
- return mark(hash, anonymous_klass_symbol, leakp);
+ assert(ik->is_unsafe_anonymous() || ik->is_hidden(), "invariant");
+ const uintptr_t hash = hidden_or_anon_klass_name_hash(ik);
+ const char* const hidden_or_anon_symbol = create_hidden_or_anon_klass_symbol(ik, hash);
+ return mark(hash, hidden_or_anon_symbol, leakp);
}
traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
assert(k != NULL, "invariant");
traceid symbol_id = 0;
- if (is_unsafe_anonymous_klass(k)) {
+ if (is_hidden_or_anon_klass(k)) {
assert(k->is_instance_klass(), "invariant");
- symbol_id = mark_unsafe_anonymous_klass_name((const InstanceKlass*)k, leakp);
+ symbol_id = mark_hidden_or_anon_klass_name((const InstanceKlass*)k, leakp);
}
if (0 == symbol_id) {
Symbol* const sym = k->name();
@@ -276,9 +278,9 @@ traceid JfrArtifactSet::bootstrap_name(bool leakp) {
return _symbol_id->bootstrap_name(leakp);
}
-traceid JfrArtifactSet::mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp) {
+traceid JfrArtifactSet::mark_hidden_or_anon_klass_name(const Klass* klass, bool leakp) {
assert(klass->is_instance_klass(), "invariant");
- return _symbol_id->mark_unsafe_anonymous_klass_name((const InstanceKlass*)klass, leakp);
+ return _symbol_id->mark_hidden_or_anon_klass_name((const InstanceKlass*)klass, leakp);
}
traceid JfrArtifactSet::mark(uintptr_t hash, const Symbol* sym, bool leakp) {
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp
index ede4c931d34..dd3141c2f1e 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -241,9 +241,9 @@ class JfrSymbolId : public JfrCHeapObj {
}
}
- traceid mark_unsafe_anonymous_klass_name(const InstanceKlass* k, bool leakp);
- bool is_unsafe_anonymous_klass(const Klass* k);
- uintptr_t unsafe_anonymous_klass_name_hash(const InstanceKlass* ik);
+ traceid mark_hidden_or_anon_klass_name(const InstanceKlass* k, bool leakp);
+ bool is_hidden_or_anon_klass(const Klass* k);
+ uintptr_t hidden_or_anon_klass_name_hash(const InstanceKlass* ik);
public:
JfrSymbolId();
@@ -304,7 +304,7 @@ class JfrArtifactSet : public JfrCHeapObj {
traceid mark(const Klass* klass, bool leakp);
traceid mark(const Symbol* symbol, bool leakp);
traceid mark(uintptr_t hash, const char* const str, bool leakp);
- traceid mark_unsafe_anonymous_klass_name(const Klass* klass, bool leakp);
+ traceid mark_hidden_or_anon_klass_name(const Klass* klass, bool leakp);
traceid bootstrap_name(bool leakp);
const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const;
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp
index 53564e8dca8..8a994d00b0f 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2020, 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
@@ -140,7 +140,7 @@ void JfrTraceId::assign(const PackageEntry* package) {
void JfrTraceId::assign(const ClassLoaderData* cld) {
assert(cld != NULL, "invariant");
- if (cld->is_unsafe_anonymous()) {
+ if (cld->has_class_mirror_holder()) {
cld->set_trace_id(0);
return;
}
diff --git a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
index 0dea7b36896..2802ac1bc8b 100644
--- a/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
+++ b/src/hotspot/share/jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp
@@ -114,7 +114,7 @@ inline traceid JfrTraceId::use(const PackageEntry* package) {
inline traceid JfrTraceId::use(const ClassLoaderData* cld) {
assert(cld != NULL, "invariant");
- return cld->is_unsafe_anonymous() ? 0 : set_used_and_get(cld);
+ return cld->has_class_mirror_holder() ? 0 : set_used_and_get(cld);
}
inline void JfrTraceId::set_leakp(const Klass* klass, const Method* method) {
diff --git a/src/hotspot/share/memory/metaspace.cpp b/src/hotspot/share/memory/metaspace.cpp
index 3c15ba3149d..ae541f287d5 100644
--- a/src/hotspot/share/memory/metaspace.cpp
+++ b/src/hotspot/share/memory/metaspace.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2020, 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
@@ -64,7 +64,7 @@ static const char* space_type_name(Metaspace::MetaspaceType t) {
switch (t) {
case Metaspace::StandardMetaspaceType: s = "Standard"; break;
case Metaspace::BootMetaspaceType: s = "Boot"; break;
- case Metaspace::UnsafeAnonymousMetaspaceType: s = "UnsafeAnonymous"; break;
+ case Metaspace::ClassMirrorHolderMetaspaceType: s = "ClassMirrorHolder"; break;
case Metaspace::ReflectionMetaspaceType: s = "Reflection"; break;
default: ShouldNotReachHere();
}
diff --git a/src/hotspot/share/memory/metaspace.hpp b/src/hotspot/share/memory/metaspace.hpp
index eda6b314c70..5861c562acb 100644
--- a/src/hotspot/share/memory/metaspace.hpp
+++ b/src/hotspot/share/memory/metaspace.hpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2020, 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
@@ -104,8 +104,8 @@ class Metaspace : public AllStatic {
ZeroMetaspaceType = 0,
StandardMetaspaceType = ZeroMetaspaceType,
BootMetaspaceType = StandardMetaspaceType + 1,
- UnsafeAnonymousMetaspaceType = BootMetaspaceType + 1,
- ReflectionMetaspaceType = UnsafeAnonymousMetaspaceType + 1,
+ ClassMirrorHolderMetaspaceType = BootMetaspaceType + 1,
+ ReflectionMetaspaceType = ClassMirrorHolderMetaspaceType + 1,
MetaspaceTypeCount
};
@@ -254,7 +254,7 @@ class ClassLoaderMetaspace : public CHeapObj {
// Initialize the first chunk for a Metaspace. Used for
// special cases such as the boot class loader, reflection
- // class loader and anonymous class loader.
+ // class loader and hidden class loader.
void initialize_first_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
metaspace::Metachunk* get_initialization_chunk(Metaspace::MetaspaceType type, Metaspace::MetadataType mdtype);
@@ -399,7 +399,7 @@ public:
rf_show_loaders = (1 << 0),
// Breaks report down by chunk type (small, medium, ...).
rf_break_down_by_chunktype = (1 << 1),
- // Breaks report down by space type (anonymous, reflection, ...).
+ // Breaks report down by space type (hidden, reflection, ...).
rf_break_down_by_spacetype = (1 << 2),
// Print details about the underlying virtual spaces.
rf_show_vslist = (1 << 3),
diff --git a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp
index 7bae7fc657c..99ca6570889 100644
--- a/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp
+++ b/src/hotspot/share/memory/metaspace/printCLDMetaspaceInfoClosure.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -104,7 +104,7 @@ void PrintCLDMetaspaceInfoClosure::do_cld(ClassLoaderData* cld) {
_out->print(UINTX_FORMAT_W(4) ": ", _num_loaders);
// Print "CLD for [,] instance of "
- // or "CLD for , loaded by [,] instance of "
+ // or "CLD for , loaded by [,] instance of "
ResourceMark rm;
const char* name = NULL;
@@ -128,8 +128,8 @@ void PrintCLDMetaspaceInfoClosure::do_cld(ClassLoaderData* cld) {
_out->print(" (unloading)");
}
_out->print(":");
- if (cld->is_unsafe_anonymous()) {
- _out->print(" , loaded by");
+ if (cld->has_class_mirror_holder()) {
+ _out->print(" , loaded by");
}
if (name != NULL) {
_out->print(" \"%s\"", name);
diff --git a/src/hotspot/share/memory/metaspace/spaceManager.cpp b/src/hotspot/share/memory/metaspace/spaceManager.cpp
index 6142afa35e7..236e8cbb328 100644
--- a/src/hotspot/share/memory/metaspace/spaceManager.cpp
+++ b/src/hotspot/share/memory/metaspace/spaceManager.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, 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
@@ -75,14 +75,14 @@ size_t SpaceManager::get_initial_chunk_size(Metaspace::MetaspaceType type) const
if (is_class()) {
switch (type) {
case Metaspace::BootMetaspaceType: requested = Metaspace::first_class_chunk_word_size(); break;
- case Metaspace::UnsafeAnonymousMetaspaceType: requested = ClassSpecializedChunk; break;
+ case Metaspace::ClassMirrorHolderMetaspaceType: requested = ClassSpecializedChunk; break;
case Metaspace::ReflectionMetaspaceType: requested = ClassSpecializedChunk; break;
default: requested = ClassSmallChunk; break;
}
} else {
switch (type) {
case Metaspace::BootMetaspaceType: requested = Metaspace::first_chunk_word_size(); break;
- case Metaspace::UnsafeAnonymousMetaspaceType: requested = SpecializedChunk; break;
+ case Metaspace::ClassMirrorHolderMetaspaceType: requested = SpecializedChunk; break;
case Metaspace::ReflectionMetaspaceType: requested = SpecializedChunk; break;
default: requested = SmallChunk; break;
}
@@ -114,15 +114,15 @@ size_t SpaceManager::calc_chunk_size(size_t word_size) {
// After that a medium chunk is preferred.
size_t chunk_word_size;
- // Special case for unsafe anonymous metadata space.
- // UnsafeAnonymous metadata space is usually small since it is used for
- // class loader data's whose life cycle is governed by one class such as an
- // unsafe anonymous class. The majority within 1K - 2K range and
+ // Special case for hidden metadata space.
+ // ClassMirrorHolder metadata space is usually small since it is used for
+ // class loader data's whose life cycle is governed by one class such as a
+ // non-strong hidden class or unsafe anonymous class. The majority within 1K - 2K range and
// rarely about 4K (64-bits JVM).
// Instead of jumping to SmallChunk after initial chunk exhausted, keeping allocation
// from SpecializeChunk up to _anon_or_delegating_metadata_specialize_chunk_limit (4)
// reduces space waste from 60+% to around 30%.
- if ((_space_type == Metaspace::UnsafeAnonymousMetaspaceType || _space_type == Metaspace::ReflectionMetaspaceType) &&
+ if ((_space_type == Metaspace::ClassMirrorHolderMetaspaceType || _space_type == Metaspace::ReflectionMetaspaceType) &&
_mdtype == Metaspace::NonClassType &&
num_chunks_by_type(SpecializedIndex) < anon_and_delegating_metadata_specialize_chunk_limit &&
word_size + Metachunk::overhead() <= SpecializedChunk) {
diff --git a/src/hotspot/share/memory/metaspaceTracer.cpp b/src/hotspot/share/memory/metaspaceTracer.cpp
index ccb043c5dab..824ab97c8bb 100644
--- a/src/hotspot/share/memory/metaspaceTracer.cpp
+++ b/src/hotspot/share/memory/metaspaceTracer.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, 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
@@ -62,10 +62,15 @@ void MetaspaceTracer::send_allocation_failure_event(ClassLoaderData *cld,
E event;
if (event.should_commit()) {
event.set_classLoader(cld);
- if (cld->is_unsafe_anonymous()) {
- event.set_unsafeAnonymousClassLoader(true);
- } else {
- event.set_unsafeAnonymousClassLoader(false);
+ event.set_unsafeAnonymousClassLoader(false); // initialize these
+ event.set_hiddenClassLoader(false);
+ if (cld->has_class_mirror_holder()) {
+ assert(cld->klasses() != NULL, "unexpected NULL for cld->klasses()");
+ if (cld->klasses()->is_non_strong_hidden()) {
+ event.set_hiddenClassLoader(true);
+ } else {
+ event.set_unsafeAnonymousClassLoader(true);
+ }
}
event.set_size(word_size * BytesPerWord);
event.set_metadataType((u1) mdtype);
diff --git a/src/hotspot/share/oops/arrayKlass.cpp b/src/hotspot/share/oops/arrayKlass.cpp
index 9063b454115..0c116801874 100644
--- a/src/hotspot/share/oops/arrayKlass.cpp
+++ b/src/hotspot/share/oops/arrayKlass.cpp
@@ -110,7 +110,7 @@ void ArrayKlass::complete_create_array_klass(ArrayKlass* k, Klass* super_klass,
assert((module_entry != NULL) || ((module_entry == NULL) && !ModuleEntryTable::javabase_defined()),
"module entry not available post " JAVA_BASE_NAME " definition");
oop module = (module_entry != NULL) ? module_entry->module() : (oop)NULL;
- java_lang_Class::create_mirror(k, Handle(THREAD, k->class_loader()), Handle(THREAD, module), Handle(), CHECK);
+ java_lang_Class::create_mirror(k, Handle(THREAD, k->class_loader()), Handle(THREAD, module), Handle(), Handle(), CHECK);
}
GrowableArray* ArrayKlass::compute_secondary_supers(int num_extra_slots,
diff --git a/src/hotspot/share/oops/instanceKlass.cpp b/src/hotspot/share/oops/instanceKlass.cpp
index ade036ac26a..b8151fa2b3f 100644
--- a/src/hotspot/share/oops/instanceKlass.cpp
+++ b/src/hotspot/share/oops/instanceKlass.cpp
@@ -31,6 +31,7 @@
#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"
@@ -134,6 +135,7 @@
#endif // ndef DTRACE_ENABLED
+
static inline bool is_class_loader(const Symbol* class_name,
const ClassFileParser& parser) {
assert(class_name != NULL, "invariant");
@@ -153,8 +155,11 @@ static inline bool is_class_loader(const Symbol* class_name,
return false;
}
-// called to verify that k is a member of this nest
+// 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);
@@ -175,7 +180,9 @@ bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const {
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);
+ 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;
@@ -186,15 +193,14 @@ bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const {
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);
+ // 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;
@@ -213,167 +219,210 @@ bool InstanceKlass::has_nest_member(InstanceKlass* k, TRAPS) const {
}
// 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
+// 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.
-InstanceKlass* InstanceKlass::nest_host(Symbol* validationException, TRAPS) {
+// 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) {
- // 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 (nest_host_k != NULL) {
+ return nest_host_k;
+ }
- 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());
- }
+ ResourceMark rm(THREAD);
- 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;
- }
+ // 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 we post the
- // requested exception type (if any) and return NULL
-
+ // 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
- 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;
+ } else {
+ error = "current type is not listed as a nest member";
}
- return nest_host_k;
+ } 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";
}
- 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);
+ // 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);
}
-
- 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);
}
+ } else {
+ log_trace(class, nestmates)("Type %s is not part of a nest: setting nest-host to self",
+ this->external_name());
}
- return nest_host_k;
+
+ // 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
+// 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. Resolution exceptions will be passed on by upper
- // layers. IncompatibleClassChangeErrors from membership validation failures
- // will also be passed through.
+ // the target class k.
- Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError();
- InstanceKlass* cur_host = nest_host(icce, CHECK_false);
+ InstanceKlass* cur_host = nest_host(CHECK_false);
if (cur_host == NULL) {
return false;
}
- Klass* k_nest_host = k->nest_host(icce, CHECK_false);
+ Klass* k_nest_host = k->nest_host(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());
- }
-
+ 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(parser.is_unsafe_anonymous()));
+ should_store_fingerprint(is_hidden_or_anonymous));
const Symbol* const class_name = parser.class_name();
assert(class_name != NULL, "invariant");
@@ -447,6 +496,7 @@ InstanceKlass::InstanceKlass(const ClassFileParser& parser, unsigned kind, Klass
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));
@@ -2276,7 +2326,7 @@ bool InstanceKlass::supers_have_passed_fingerprint_checks() {
return true;
}
-bool InstanceKlass::should_store_fingerprint(bool is_unsafe_anonymous) {
+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) {
@@ -2287,8 +2337,8 @@ bool InstanceKlass::should_store_fingerprint(bool is_unsafe_anonymous) {
// (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
+ 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
@@ -2581,6 +2631,7 @@ void InstanceKlass::release_C_heap_structures() {
// 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();
@@ -2631,6 +2682,15 @@ const char* InstanceKlass::signature_name() const {
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++];
@@ -2649,6 +2709,25 @@ ModuleEntry* InstanceKlass::module() const {
return unsafe_anonymous_host()->module();
}
+ if (is_hidden() &&
+ in_unnamed_package() &&
+ class_loader_data()->has_class_mirror_holder()) {
+ // For a non-strong 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();
@@ -2879,7 +2958,7 @@ InstanceKlass* InstanceKlass::compute_enclosing_class(bool* inner_is_member, TRA
*inner_is_member = true;
}
if (NULL == outer_klass) {
- // It may be unsafe anonymous; try for that.
+ // 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);
diff --git a/src/hotspot/share/oops/instanceKlass.hpp b/src/hotspot/share/oops/instanceKlass.hpp
index 2da21c08b74..7a1547a33ec 100644
--- a/src/hotspot/share/oops/instanceKlass.hpp
+++ b/src/hotspot/share/oops/instanceKlass.hpp
@@ -195,7 +195,10 @@ class InstanceKlass: public Klass {
// that is the nest-host of this class. This data has not been validated.
jushort _nest_host_index;
- // Resolved nest-host klass: either true nest-host or self if we are not nested.
+ // Resolved nest-host klass: either true nest-host or self if we are not
+ // nested, or an error occurred resolving or validating the nominated
+ // nest-host. Can also be set directly by JDK API's that establish nest
+ // relationships.
// By always being set it makes nest-member access checks simpler.
InstanceKlass* _nest_host;
@@ -469,6 +472,8 @@ class InstanceKlass: public Klass {
// nest-host index
jushort nest_host_index() const { return _nest_host_index; }
void set_nest_host_index(u2 i) { _nest_host_index = i; }
+ // dynamic nest member support
+ void set_nest_host(InstanceKlass* host, TRAPS);
// record components
Array* record_components() const { return _record_components; }
@@ -482,9 +487,13 @@ private:
bool has_nest_member(InstanceKlass* k, TRAPS) const;
public:
- // Returns nest-host class, resolving and validating it if needed
- // Returns NULL if an exception occurs during loading, or validation fails
- InstanceKlass* nest_host(Symbol* validationException, TRAPS);
+ // Used to construct informative IllegalAccessError messages at a higher level,
+ // if there was an issue resolving or validating the nest host.
+ // Returns NULL if there was no error.
+ const char* nest_host_error(TRAPS);
+ // Returns nest-host class, resolving and validating it if needed.
+ // Returns NULL if resolution is not possible from the calling context.
+ InstanceKlass* nest_host(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);
@@ -819,8 +828,8 @@ public:
}
bool supers_have_passed_fingerprint_checks();
- static bool should_store_fingerprint(bool is_unsafe_anonymous);
- bool should_store_fingerprint() const { return should_store_fingerprint(is_unsafe_anonymous()); }
+ static bool should_store_fingerprint(bool is_hidden_or_anonymous);
+ bool should_store_fingerprint() const { return should_store_fingerprint(is_hidden() || is_unsafe_anonymous()); }
bool has_stored_fingerprint() const;
uint64_t get_stored_fingerprint() const;
void store_fingerprint(uint64_t fingerprint);
diff --git a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
index ee3679c70aa..eaf5f61c4ae 100644
--- a/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
+++ b/src/hotspot/share/oops/instanceMirrorKlass.inline.hpp
@@ -1,4 +1,5 @@
-/* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
+/*
+ * Copyright (c) 2015, 2020, 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
@@ -51,10 +52,9 @@ void InstanceMirrorKlass::oop_oop_iterate(oop obj, OopClosureType* closure) {
Klass* klass = java_lang_Class::as_Klass_raw(obj);
// We'll get NULL for primitive mirrors.
if (klass != NULL) {
- if (klass->is_instance_klass() &&
- InstanceKlass::cast(klass)->is_unsafe_anonymous()) {
- // An unsafe anonymous class doesn't have its own class loader, so
- // when handling the java mirror for the class we need to make sure its class
+ if (klass->is_instance_klass() && klass->class_loader_data()->has_class_mirror_holder()) {
+ // A non-strong hidden class or an unsafe anonymous class doesn't have its own class loader,
+ // so when handling the java mirror for the class we need to make sure its class
// loader data is claimed, this is done by calling do_cld explicitly.
// For non-anonymous classes the call to do_cld is made when the class
// loader itself is handled.
diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp
index 2f2abe8e2ca..81f460f907f 100644
--- a/src/hotspot/share/oops/klass.cpp
+++ b/src/hotspot/share/oops/klass.cpp
@@ -608,7 +608,7 @@ void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protec
// gotten an OOM later but keep the mirror if it was created.
if (java_mirror() == NULL) {
log_trace(cds, mirror)("Recreate mirror for %s", external_name());
- java_lang_Class::create_mirror(this, loader, module_handle, protection_domain, CHECK);
+ java_lang_Class::create_mirror(this, loader, module_handle, protection_domain, Handle(), CHECK);
}
}
@@ -672,6 +672,20 @@ void Klass::check_array_allocation_length(int length, int max_length, TRAPS) {
}
}
+// Replace the last '+' char with '/'.
+static char* convert_hidden_name_to_java(Symbol* name) {
+ size_t name_len = name->utf8_length();
+ char* result = NEW_RESOURCE_ARRAY(char, name_len + 1);
+ name->as_klass_external_name(result, (int)name_len + 1);
+ for (int index = (int)name_len; index > 0; index--) {
+ if (result[index] == '+') {
+ result[index] = JVM_SIGNATURE_SLASH;
+ break;
+ }
+ }
+ return result;
+}
+
// In product mode, this function doesn't have virtual function calls so
// there might be some performance advantage to handling InstanceKlass here.
const char* Klass::external_name() const {
@@ -688,7 +702,14 @@ const char* Klass::external_name() const {
strcpy(result + name_len, addr_buf);
assert(strlen(result) == name_len + addr_len, "");
return result;
+
+ } else if (ik->is_hidden()) {
+ char* result = convert_hidden_name_to_java(name());
+ return result;
}
+ } else if (is_objArray_klass() && ObjArrayKlass::cast(this)->bottom_klass()->is_hidden()) {
+ char* result = convert_hidden_name_to_java(name());
+ return result;
}
if (name() == NULL) return "";
return name()->as_klass_external_name();
@@ -696,6 +717,18 @@ const char* Klass::external_name() const {
const char* Klass::signature_name() const {
if (name() == NULL) return "";
+ if (is_objArray_klass() && ObjArrayKlass::cast(this)->bottom_klass()->is_hidden()) {
+ size_t name_len = name()->utf8_length();
+ char* result = NEW_RESOURCE_ARRAY(char, name_len + 1);
+ name()->as_C_string(result, (int)name_len + 1);
+ for (int index = (int)name_len; index > 0; index--) {
+ if (result[index] == '+') {
+ result[index] = JVM_SIGNATURE_DOT;
+ break;
+ }
+ }
+ return result;
+ }
return name()->as_C_string();
}
diff --git a/src/hotspot/share/oops/klass.hpp b/src/hotspot/share/oops/klass.hpp
index d1e2d2bfc37..598d33d8ee6 100644
--- a/src/hotspot/share/oops/klass.hpp
+++ b/src/hotspot/share/oops/klass.hpp
@@ -615,6 +615,10 @@ protected:
void set_has_miranda_methods() { _access_flags.set_has_miranda_methods(); }
bool is_shared() const { return access_flags().is_shared_class(); } // shadows MetaspaceObj::is_shared)()
void set_is_shared() { _access_flags.set_is_shared_class(); }
+ bool is_hidden() const { return access_flags().is_hidden_class(); }
+ void set_is_hidden() { _access_flags.set_is_hidden_class(); }
+ bool is_non_strong_hidden() const { return access_flags().is_hidden_class() &&
+ class_loader_data()->has_class_mirror_holder(); }
bool is_cloneable() const;
void set_is_cloneable();
diff --git a/src/hotspot/share/oops/method.hpp b/src/hotspot/share/oops/method.hpp
index 21c390a1894..5b912ca6f5d 100644
--- a/src/hotspot/share/oops/method.hpp
+++ b/src/hotspot/share/oops/method.hpp
@@ -892,9 +892,10 @@ public:
_flags = x ? (_flags | _dont_inline) : (_flags & ~_dont_inline);
}
- bool is_hidden() {
+ bool is_hidden() const {
return (_flags & _hidden) != 0;
}
+
void set_hidden(bool x) {
_flags = x ? (_flags | _hidden) : (_flags & ~_hidden);
}
diff --git a/src/hotspot/share/opto/c2compiler.cpp b/src/hotspot/share/opto/c2compiler.cpp
index 4495409c2eb..9495ac2fb43 100644
--- a/src/hotspot/share/opto/c2compiler.cpp
+++ b/src/hotspot/share/opto/c2compiler.cpp
@@ -602,6 +602,7 @@ bool C2Compiler::is_intrinsic_supported(const methodHandle& method, bool is_virt
case vmIntrinsics::_isInterface:
case vmIntrinsics::_isArray:
case vmIntrinsics::_isPrimitive:
+ case vmIntrinsics::_isHidden:
case vmIntrinsics::_getSuperclass:
case vmIntrinsics::_getClassAccessFlags:
case vmIntrinsics::_floatToRawIntBits:
diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp
index d23fc03f465..de0efd13b91 100644
--- a/src/hotspot/share/opto/library_call.cpp
+++ b/src/hotspot/share/opto/library_call.cpp
@@ -186,6 +186,7 @@ class LibraryCallKit : public GraphKit {
int modifier_mask, int modifier_bits,
RegionNode* region);
Node* generate_interface_guard(Node* kls, RegionNode* region);
+ Node* generate_hidden_class_guard(Node* kls, RegionNode* region);
Node* generate_array_guard(Node* kls, RegionNode* region) {
return generate_array_guard_common(kls, region, false, false);
}
@@ -783,6 +784,7 @@ bool LibraryCallKit::try_to_inline(int predicate) {
case vmIntrinsics::_isInterface:
case vmIntrinsics::_isArray:
case vmIntrinsics::_isPrimitive:
+ case vmIntrinsics::_isHidden:
case vmIntrinsics::_getSuperclass:
case vmIntrinsics::_getClassAccessFlags: return inline_native_Class_query(intrinsic_id());
@@ -3084,6 +3086,9 @@ Node* LibraryCallKit::generate_access_flags_guard(Node* kls, int modifier_mask,
Node* LibraryCallKit::generate_interface_guard(Node* kls, RegionNode* region) {
return generate_access_flags_guard(kls, JVM_ACC_INTERFACE, 0, region);
}
+Node* LibraryCallKit::generate_hidden_class_guard(Node* kls, RegionNode* region) {
+ return generate_access_flags_guard(kls, JVM_ACC_IS_HIDDEN_CLASS, 0, region);
+}
//-------------------------inline_native_Class_query-------------------
bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) {
@@ -3119,6 +3124,9 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) {
prim_return_value = intcon(1);
expect_prim = true; // obviously
break;
+ case vmIntrinsics::_isHidden:
+ prim_return_value = intcon(0);
+ break;
case vmIntrinsics::_getSuperclass:
prim_return_value = null();
return_type = TypeInstPtr::MIRROR->cast_to_ptr_type(TypePtr::BotPTR);
@@ -3211,6 +3219,16 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) {
query_value = intcon(0); // "normal" path produces false
break;
+ case vmIntrinsics::_isHidden:
+ // (To verify this code sequence, check the asserts in JVM_IsHiddenClass.)
+ if (generate_hidden_class_guard(kls, region) != NULL)
+ // A guard was added. If the guard is taken, it was an hidden class.
+ phi->add_req(intcon(1));
+ // If we fall through, it's a plain class.
+ query_value = intcon(0);
+ break;
+
+
case vmIntrinsics::_getSuperclass:
// The rules here are somewhat unfortunate, but we can still do better
// with random logic than with a JNI call.
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 2245e5ef585..ca52fef5ff1 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -989,6 +989,154 @@ static jclass jvm_define_class_common(JNIEnv *env, const char *name,
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,
+ STRONG_LOADER_LINK = java_lang_invoke_MemberName::MN_STRONG_LOADER_LINK,
+ 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 strongly referenced from class loader.
+ */
+static jclass jvm_lookup_define_class(JNIEnv *env, jclass lookup, const char *name,
+ 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* lookup_k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(lookup));
+ // Lookup class must be a non-null instance
+ if (lookup_k == NULL) {
+ THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Lookup class is null");
+ }
+ assert(lookup_k->is_instance_klass(), "Lookup class must be an instance klass");
+
+ Handle class_loader (THREAD, lookup_k->class_loader());
+
+ bool is_nestmate = (flags & NESTMATE) == NESTMATE;
+ bool is_hidden = (flags & HIDDEN_CLASS) == HIDDEN_CLASS;
+ bool is_strong = (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK;
+ bool vm_annotations = (flags & ACCESS_VM_ANNOTATIONS) == ACCESS_VM_ANNOTATIONS;
+
+ InstanceKlass* host_class = NULL;
+ if (is_nestmate) {
+ host_class = InstanceKlass::cast(lookup_k)->nest_host(CHECK_NULL);
+ }
+
+ 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_strong ? "strong" : "weak",
+ 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_strong) {
+ THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "an ordinary class must be strongly referenced by its defining loader");
+ }
+ if (vm_annotations) {
+ THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "vm annotations only allowed for hidden classes");
+ }
+ if (flags != STRONG_LOADER_LINK) {
+ THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(),
+ err_msg("invalid flag 0x%x", 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 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);
+
+ Klass* defined_k;
+ InstanceKlass* ik = NULL;
+ if (!is_hidden) {
+ defined_k = SystemDictionary::resolve_from_stream(class_name,
+ class_loader,
+ protection_domain,
+ &st,
+ CHECK_NULL);
+
+ if (log_is_enabled(Debug, class, resolve) && defined_k != NULL) {
+ trace_class_resolution(defined_k);
+ }
+ ik = InstanceKlass::cast(defined_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_strong,
+ vm_annotations);
+ defined_k = SystemDictionary::parse_stream(class_name,
+ class_loader,
+ &st,
+ cl_info,
+ CHECK_NULL);
+ if (defined_k == NULL) {
+ THROW_MSG_0(vmSymbols::java_lang_Error(), "Failure to define a hidden class");
+ }
+
+ ik = InstanceKlass::cast(defined_k);
+
+ // 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.
+ ik->class_loader_data()->dec_keep_alive();
+
+ if (is_nestmate && log_is_enabled(Debug, class, nestmates)) {
+ 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");
+ }
+ }
+ assert(Reflection::is_same_class_package(lookup_k, defined_k),
+ "lookup class and defined class are in different packages");
+
+ if (init) {
+ ik->initialize(CHECK_NULL);
+ } else {
+ ik->link_class(CHECK_NULL);
+ }
+
+ return (jclass) JNIHandles::make_local(env, defined_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");
@@ -996,6 +1144,29 @@ JVM_ENTRY(jclass, JVM_DefineClass(JNIEnv *env, const char *name, jobject loader,
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
+ * 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, 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, 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");
@@ -1158,6 +1329,15 @@ JVM_ENTRY(jboolean, JVM_IsInterface(JNIEnv *env, jclass cls))
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");
@@ -1425,7 +1605,7 @@ JVM_ENTRY(jclass, JVM_GetDeclaringClass(JNIEnv *env, jclass ofClass))
= 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
@@ -1875,8 +2055,7 @@ JVM_ENTRY(jclass, JVM_GetNestHost(JNIEnv* env, jclass current))
Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
assert(c->is_instance_klass(), "must be");
InstanceKlass* ck = InstanceKlass::cast(c);
- // Don't post exceptions if validation fails
- InstanceKlass* host = ck->nest_host(NULL, THREAD);
+ InstanceKlass* host = ck->nest_host(THREAD);
return (jclass) (host == NULL ? NULL :
JNIHandles::make_local(THREAD, host->java_mirror()));
}
@@ -1886,62 +2065,77 @@ JVM_ENTRY(jobjectArray, JVM_GetNestMembers(JNIEnv* env, jclass current))
{
// current is not a primitive or array class
JVMWrapper("JVM_GetNestMembers");
+ ResourceMark rm(THREAD);
Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
assert(c->is_instance_klass(), "must be");
InstanceKlass* ck = InstanceKlass::cast(c);
- // Get the nest host for this nest - throw ICCE if validation fails
- Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError();
- InstanceKlass* host = ck->nest_host(icce, CHECK_NULL);
+ InstanceKlass* host = ck->nest_host(THREAD);
+ log_trace(class, nestmates)("Calling GetNestMembers for type %s with nest-host %s",
+ ck->external_name(), host->external_name());
{
JvmtiVMObjectAllocEventCollector oam;
Array* members = host->nest_members();
int length = members == NULL ? 0 : members->length();
+
+ log_trace(class, nestmates)(" - host has %d listed nest members", length);
+
// nest host is first in the array so make it one bigger
objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(),
length + 1, CHECK_NULL);
- objArrayHandle result (THREAD, r);
+ objArrayHandle result(THREAD, r);
result->obj_at_put(0, host->java_mirror());
if (length != 0) {
- int i;
- for (i = 0; i < length; i++) {
- int cp_index = members->at(i);
- Klass* k = host->constants()->klass_at(cp_index, CHECK_NULL);
- if (k->is_instance_klass()) {
- InstanceKlass* nest_host_k =
- InstanceKlass::cast(k)->nest_host(icce, CHECK_NULL);
- if (nest_host_k == host) {
- result->obj_at_put(i+1, k->java_mirror());
- }
- else {
- // k's nest host is legal but it isn't our host so
- // throw ICCE
- ResourceMark rm(THREAD);
- Exceptions::fthrow(THREAD_AND_LOCATION,
- icce,
- "Nest member %s in %s declares a different nest host of %s",
- k->external_name(),
- host->external_name(),
- nest_host_k->external_name()
- );
- return NULL;
- }
- }
- else {
- // we have a bad nest member entry - throw ICCE
- ResourceMark rm(THREAD);
- Exceptions::fthrow(THREAD_AND_LOCATION,
- icce,
- "Class %s can not be a nest member of %s",
- k->external_name(),
- host->external_name()
- );
- return NULL;
- }
+ int count = 0;
+ for (int i = 0; i < length; i++) {
+ int cp_index = members->at(i);
+ Klass* k = host->constants()->klass_at(cp_index, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) {
+ return NULL; // propagate VMEs
+ }
+ if (log_is_enabled(Trace, class, nestmates)) {
+ stringStream ss;
+ char* target_member_class = host->constants()->klass_name_at(cp_index)->as_C_string();
+ ss.print(" - resolution of nest member %s failed: ", target_member_class);
+ java_lang_Throwable::print(PENDING_EXCEPTION, &ss);
+ log_trace(class, nestmates)("%s", ss.as_string());
+ }
+ CLEAR_PENDING_EXCEPTION;
+ continue;
+ }
+ if (k->is_instance_klass()) {
+ InstanceKlass* ik = InstanceKlass::cast(k);
+ InstanceKlass* nest_host_k = ik->nest_host(CHECK_NULL);
+ if (nest_host_k == host) {
+ result->obj_at_put(count+1, k->java_mirror());
+ count++;
+ log_trace(class, nestmates)(" - [%d] = %s", count, ik->external_name());
+ } else {
+ log_trace(class, nestmates)(" - skipping member %s with different host %s",
+ ik->external_name(), nest_host_k->external_name());
+ }
+ } else {
+ log_trace(class, nestmates)(" - skipping member %s that is not an instance class",
+ k->external_name());
+ }
+ }
+ if (count < length) {
+ // we had invalid entries so we need to compact the array
+ log_trace(class, nestmates)(" - compacting array from length %d to %d",
+ length + 1, count + 1);
+
+ objArrayOop r2 = oopFactory::new_objArray(SystemDictionary::Class_klass(),
+ count + 1, CHECK_NULL);
+ objArrayHandle result2(THREAD, r2);
+ for (int i = 0; i < count + 1; i++) {
+ result2->obj_at_put(i, result->obj_at(i));
+ }
+ return (jobjectArray)JNIHandles::make_local(THREAD, result2());
}
}
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());
}
diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml
index 5f90917857c..1131bcfa1b3 100644
--- a/src/hotspot/share/prims/jvmti.xml
+++ b/src/hotspot/share/prims/jvmti.xml
@@ -1,7 +1,7 @@
-
-
-
-
-JDI Type Signatures
-
-
-
-
-
-
JDI Type Signatures
-
-
JDI Type Signatures
-
-
Type Signature
-
Java Type
-
-
-
Z
boolean
-
B
byte
-
C
char
-
S
short
-
I
int
-
J
long
-
F
float
-
D
double
-
Lfully-qualified-class
-;
-
fully-qualified-class
-
[type
-
-
type[]
-
-(arg-types )ret-type
-
-
method type (including constructors)
-
-
-
For example, the Java method:
-
long f (int n, String s, int[] arr);
-
has the following type signature:
-
(ILjava/lang/String;[I)J
-
-
-
diff --git a/src/jdk.jdi/share/classes/com/sun/jdi/event/ClassUnloadEvent.java b/src/jdk.jdi/share/classes/com/sun/jdi/event/ClassUnloadEvent.java
index 9819d133fb9..e236da0838a 100644
--- a/src/jdk.jdi/share/classes/com/sun/jdi/event/ClassUnloadEvent.java
+++ b/src/jdk.jdi/share/classes/com/sun/jdi/event/ClassUnloadEvent.java
@@ -42,12 +42,21 @@ import com.sun.jdi.VirtualMachine;
public interface ClassUnloadEvent extends Event {
/**
- * Returns the name of the class that has been unloaded.
+ * Returns the {@linkplain com.sun.jdi.Type#name() name of the class}
+ * that has been unloaded. The returned string may not be a
+ * binary name.
+ *
+ * @see Class#getName()
*/
public String className();
/**
- * Returns the JNI-style signature of the class that has been unloaded.
+ * Returns the {@linkplain com.sun.jdi.Type#signature() type signature of the class}
+ * that has been unloaded. The result is of the same
+ * form as the string returned by {@link Class#descriptorString()}.
+ * If this class can be described nominally, the returned string is a
+ * type descriptor conforming to JVMS {@jvms 4.3.2}; otherwise, the returned string
+ * is not a type descriptor.
*/
public String classSignature();
}
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/jdi/EventSetImpl.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/EventSetImpl.java
index 68f940ddae4..8c562a7c739 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/EventSetImpl.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/EventSetImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2020, 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
@@ -426,8 +426,18 @@ public class EventSetImpl extends ArrayList implements EventSet {
public String className() {
assert classSignature.startsWith("L") && classSignature.endsWith(";");
- return classSignature.substring(1, classSignature.length()-1)
- .replace('/', '.');
+
+ // trim leading "L" and trailing ";"
+ String name = classSignature.substring(1, classSignature.length() - 1);
+ int index = name.indexOf("."); // check if it is a hidden class
+ if (index < 0) {
+ return name.replace('/', '.');
+ } else {
+ // map the type descriptor from: "L" + N + "." + + ";"
+ // to class name: N.replace('/', '.') + "/" +
+ return name.substring(0, index).replace('/', '.')
+ + "/" + name.substring(index + 1);
+ }
}
public String classSignature() {
diff --git a/src/jdk.jdi/share/classes/com/sun/tools/jdi/JNITypeParser.java b/src/jdk.jdi/share/classes/com/sun/tools/jdi/JNITypeParser.java
index 1f6b5ef6afb..197c2d291d4 100644
--- a/src/jdk.jdi/share/classes/com/sun/tools/jdi/JNITypeParser.java
+++ b/src/jdk.jdi/share/classes/com/sun/tools/jdi/JNITypeParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2020, 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
@@ -43,38 +43,45 @@ public class JNITypeParser {
this.signature = signature;
}
- static String typeNameToSignature(String signature) {
+ static String typeNameToSignature(String typeName) {
StringBuilder sb = new StringBuilder();
- int firstIndex = signature.indexOf('[');
+ int firstIndex = typeName.indexOf('[');
int index = firstIndex;
while (index != -1) {
sb.append('[');
- index = signature.indexOf('[', index + 1);
+ index = typeName.indexOf('[', index + 1);
}
if (firstIndex != -1) {
- signature = signature.substring(0, firstIndex);
+ typeName = typeName.substring(0, firstIndex);
}
- if (signature.equals("boolean")) {
+ if (typeName.equals("boolean")) {
sb.append('Z');
- } else if (signature.equals("byte")) {
+ } else if (typeName.equals("byte")) {
sb.append('B');
- } else if (signature.equals("char")) {
+ } else if (typeName.equals("char")) {
sb.append('C');
- } else if (signature.equals("short")) {
+ } else if (typeName.equals("short")) {
sb.append('S');
- } else if (signature.equals("int")) {
+ } else if (typeName.equals("int")) {
sb.append('I');
- } else if (signature.equals("long")) {
+ } else if (typeName.equals("long")) {
sb.append('J');
- } else if (signature.equals("float")) {
+ } else if (typeName.equals("float")) {
sb.append('F');
- } else if (signature.equals("double")) {
+ } else if (typeName.equals("double")) {
sb.append('D');
} else {
sb.append('L');
- sb.append(signature.replace('.', '/'));
+ index = typeName.indexOf("/"); // check if it's a hidden class
+ if (index < 0) {
+ sb.append(typeName.replace('.', '/'));
+ } else {
+ sb.append(typeName.substring(0, index).replace('.', '/'));
+ sb.append(".");
+ sb.append(typeName.substring(index + 1));
+ }
sb.append(';');
}
@@ -203,7 +210,14 @@ public class JNITypeParser {
currentIndex);
String retVal = signature.substring(currentIndex,
endClass);
- retVal = retVal.replace('/','.');
+ int index = retVal.indexOf(".");
+ if (index < 0) {
+ retVal = retVal.replace('/', '.');
+ } else {
+ // hidden class
+ retVal = retVal.substring(0, index).replace('/', '.')
+ + "/" + retVal.substring(index + 1);
+ }
currentIndex = endClass + 1;
return retVal;
diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/util.c b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
index da439913071..429760218fa 100644
--- a/src/jdk.jdwp.agent/share/native/libjdwp/util.c
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/util.c
@@ -992,6 +992,10 @@ convertSignatureToClassname(char *convert)
char c = *p;
if (c == '/') {
*(p-1) = '.';
+ } else if (c == '.') {
+ // class signature of a hidden class is "Ljava/lang/Foo.1234;"
+ // map to "java.lang.Foo/1234"
+ *(p-1) = '/';
} else {
*(p-1) = c;
}
diff --git a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
index c9b592a2c28..173e618e440 100644
--- a/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
+++ b/src/jdk.unsupported/share/classes/sun/misc/Unsafe.java
@@ -30,6 +30,7 @@ import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
+import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.util.Set;
@@ -636,6 +637,12 @@ public final class Unsafe {
*/
@ForceInline
public long objectFieldOffset(Field f) {
+ if (f == null) {
+ throw new NullPointerException();
+ }
+ if (f.getDeclaringClass().isHidden()) {
+ throw new UnsupportedOperationException("can't get field offset on a hidden class: " + f);
+ }
return theInternalUnsafe.objectFieldOffset(f);
}
@@ -658,6 +665,12 @@ public final class Unsafe {
*/
@ForceInline
public long staticFieldOffset(Field f) {
+ if (f == null) {
+ throw new NullPointerException();
+ }
+ if (f.getDeclaringClass().isHidden()) {
+ throw new UnsupportedOperationException("can't get field offset on a hidden class: " + f);
+ }
return theInternalUnsafe.staticFieldOffset(f);
}
@@ -673,6 +686,12 @@ public final class Unsafe {
*/
@ForceInline
public Object staticFieldBase(Field f) {
+ if (f == null) {
+ throw new NullPointerException();
+ }
+ if (f.getDeclaringClass().isHidden()) {
+ throw new UnsupportedOperationException("can't get base address on a hidden class: " + f);
+ }
return theInternalUnsafe.staticFieldBase(f);
}
@@ -820,11 +839,16 @@ public final class Unsafe {
*
String: any object (not just a java.lang.String)
*
InterfaceMethodRef: (NYI) a method handle to invoke on that call site's arguments
*
+ *
+ * @deprecated Use the {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)}
+ * method.
+ *
* @param hostClass context for linkage, access control, protection domain, and class loader
* @param data bytes of a class file
* @param cpPatches where non-null entries exist, they replace corresponding CP entries in data
*/
@ForceInline
+ @Deprecated(since = "15", forRemoval = false)
public Class> defineAnonymousClass(Class> hostClass, byte[] data, Object[] cpPatches) {
return theInternalUnsafe.defineAnonymousClass(hostClass, data, cpPatches);
}
diff --git a/test/hotspot/gtest/memory/test_metaspace_allocation.cpp b/test/hotspot/gtest/memory/test_metaspace_allocation.cpp
index 3705d6e2a57..cc88575b6fe 100644
--- a/test/hotspot/gtest/memory/test_metaspace_allocation.cpp
+++ b/test/hotspot/gtest/memory/test_metaspace_allocation.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, SAP.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -102,9 +102,9 @@ protected:
_spaces[i].lock = new Mutex(Monitor::native, "gtest-MetaspaceAllocationTest-lock", false, Monitor::_safepoint_check_never);
ASSERT_TRUE(_spaces[i].lock != NULL);
}
- // Let every ~10th space be an unsafe anonymous one to test different allocation patterns.
+ // Let every ~10th space be a short-lived one to test different allocation patterns.
const Metaspace::MetaspaceType msType = (os::random() % 100 < 10) ?
- Metaspace::UnsafeAnonymousMetaspaceType : Metaspace::StandardMetaspaceType;
+ Metaspace::ClassMirrorHolderMetaspaceType : Metaspace::StandardMetaspaceType;
{
// Pull lock during space creation, since this is what happens in the VM too
// (see ClassLoaderData::metaspace_non_null(), which we mimick here).
diff --git a/test/hotspot/jtreg/ProblemList-graal.txt b/test/hotspot/jtreg/ProblemList-graal.txt
index 4b4287a93b6..ed057094673 100644
--- a/test/hotspot/jtreg/ProblemList-graal.txt
+++ b/test/hotspot/jtreg/ProblemList-graal.txt
@@ -241,3 +241,6 @@ org.graalvm.compiler.replacements.test.classfile.ClassfileBytecodeProviderTest
org.graalvm.compiler.core.test.deopt.CompiledMethodTest 8202955
org.graalvm.compiler.hotspot.test.ReservedStackAccessTest 8213567 windows-all
+
+org.graalvm.compiler.hotspot.test.CheckGraalIntrinsics 8219607 generic-all
+org.graalvm.compiler.hotspot.test.LambdaStableNameTest 8219607 generic-all
diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java
index 33ff2510df0..59beab7dfd8 100644
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestMetaAccessProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -68,7 +68,22 @@ public class TestMetaAccessProvider extends TypeUniverse {
metaAccess.encodeDeoptActionAndReason(DEOPT_ACTION, DEOPT_REASON, DEBUG_IDS[3]).asInt()
};
- private static boolean isUnsafeAnoymous(ResolvedJavaType type) {
+ private static boolean isHiddenClass(Class> cls) {
+ if (cls.isHidden()) {
+ return true;
+ }
+
+ // Check array of hidden type.
+ while (cls.getComponentType() != null) {
+ cls = cls.getComponentType();
+ }
+ if (cls.isHidden()) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isUnsafeAnonymous(ResolvedJavaType type) {
return type.getHostClass() != null;
}
@@ -77,7 +92,7 @@ public class TestMetaAccessProvider extends TypeUniverse {
for (Class> c : classes) {
ResolvedJavaType type = metaAccess.lookupJavaType(c);
assertNotNull(c.toString(), type);
- if (!isUnsafeAnoymous(type)) {
+ if (!isHiddenClass(c) && !isUnsafeAnonymous(type)) {
assertEquals(c.toString(), type.getName(), toInternalName(c.getName()));
assertEquals(c.toString(), type.getName(), toInternalName(type.toJavaName()));
assertEquals(c.toString(), c.getName(), type.toClassName());
@@ -98,11 +113,11 @@ public class TestMetaAccessProvider extends TypeUniverse {
ResolvedJavaType[] result = metaAccess.lookupJavaTypes(classes.toArray(new Class>[classes.size()]));
int counter = 0;
for (Class> aClass : classes) {
- if (!isUnsafeAnoymous(result[counter])) {
+ if (!isHiddenClass(aClass) && !isUnsafeAnonymous(result[counter])) {
assertEquals("Unexpected javaType: " + result[counter] + " while expecting of class: " + aClass, result[counter].toClassName(), aClass.getName());
}
counter++;
- }
+ }
}
@Test(expected = NullPointerException.class)
diff --git a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
index 349b05948c5..6a1d09c1198 100644
--- a/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
+++ b/test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2020, 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
@@ -25,7 +25,8 @@
* @test
* @requires vm.jvmci
* @library ../../../../../
- * @modules java.base/jdk.internal.reflect
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ * java.base/jdk.internal.reflect
* jdk.internal.vm.ci/jdk.vm.ci.meta
* jdk.internal.vm.ci/jdk.vm.ci.runtime
* jdk.internal.vm.ci/jdk.vm.ci.common
@@ -65,6 +66,7 @@ import java.util.Set;
import org.junit.Test;
+import jdk.internal.org.objectweb.asm.*;
import jdk.internal.reflect.ConstantPool;
import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
@@ -156,15 +158,26 @@ public class TestResolvedJavaType extends TypeUniverse {
}
}
+ private static Class> anonClass() throws Exception {
+ ClassWriter cw = new ClassWriter(0);
+ cw.visit(Opcodes.V1_8, Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, "Anon", null, "java/lang/Object", null);
+ FieldVisitor intField = cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "intField", "I", null, 0);
+ intField.visitEnd();
+ cw.visitEnd();
+ return unsafe.defineAnonymousClass(TypeUniverse.class, cw.toByteArray(), null);
+ }
+
@Test
- public void getHostClassTest() {
+ public void getHostClassTest() throws Exception {
+ ResolvedJavaType type = metaAccess.lookupJavaType(anonClass());
+ ResolvedJavaType host = type.getHostClass();
+ assertNotNull(host);
for (Class> c : classes) {
- ResolvedJavaType type = metaAccess.lookupJavaType(c);
- ResolvedJavaType host = type.getHostClass();
- if (!type.equals(predicateType)) {
- assertNull(host);
- } else {
- assertNotNull(host);
+ type = metaAccess.lookupJavaType(c);
+ host = type.getHostClass();
+ assertNull(host);
+ if (type.equals(predicateType)) {
+ assertTrue(c.isHidden());
}
}
@@ -176,9 +189,10 @@ public class TestResolvedJavaType extends TypeUniverse {
Supplier lambda = () -> () -> System.out.println("run");
ResolvedJavaType lambdaType = metaAccess.lookupJavaType(lambda.getClass());
ResolvedJavaType nestedLambdaType = metaAccess.lookupJavaType(lambda.get().getClass());
- assertNotNull(lambdaType.getHostClass());
- assertNotNull(nestedLambdaType.getHostClass());
- assertEquals(lambdaType.getHostClass(), nestedLambdaType.getHostClass());
+ assertNull(lambdaType.getHostClass());
+ assertTrue(lambda.getClass().isHidden());
+ assertNull(nestedLambdaType.getHostClass());
+ assertTrue(lambda.get().getClass().isHidden());
}
@Test
@@ -766,8 +780,8 @@ public class TestResolvedJavaType extends TypeUniverse {
if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(ConstantPool.class)) && f.getName().equals("constantPoolOop")) {
return true;
}
- if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class)) && f.getName().equals("classLoader")) {
- return true;
+ if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Class.class))) {
+ return f.getName().equals("classLoader") || f.getName().equals("classData");
}
if (f.getDeclaringClass().equals(metaAccess.lookupJavaType(Lookup.class))) {
return f.getName().equals("allowedModes") || f.getName().equals("lookupClass");
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/CastToParentTest.java b/test/hotspot/jtreg/runtime/HiddenClasses/CastToParentTest.java
new file mode 100644
index 00000000000..3ef0b528322
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/CastToParentTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test that a hidden class can be cast to its parent.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main CastToParentTest
+ */
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+// This test is based on vmTestbase/vm/mlvm/anonloader/func/castToGrandparent/Test.java
+public class CastToParentTest {
+
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass extends CastToParentTest { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+ public static void main(String[] args) throws Throwable {
+ Lookup lookup = MethodHandles.lookup();
+ Class> c = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ Object hiddenClassObj = c.newInstance();
+
+ // Cast hidden class to its parent.
+ CastToParentTest parentObj = (CastToParentTest)hiddenClassObj;
+
+ if (!parentObj.equals(hiddenClassObj)) {
+ throw new RuntimeException("Hidden class object cannot be cast to parent");
+ }
+
+ // Try to cast using a different mechanism.
+ new CastToParentTest().getClass().cast(hiddenClassObj);
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/DefineHiddenClass.java b/test/hotspot/jtreg/runtime/HiddenClasses/DefineHiddenClass.java
new file mode 100644
index 00000000000..a13b2b9aa5d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/DefineHiddenClass.java
@@ -0,0 +1,97 @@
+/*
+ * 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
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @build jdk.test.lib.JDKToolLauncher
+ * jdk.test.lib.process.ProcessTools
+ * jdk.test.lib.Utils
+ * @run main/othervm -Xverify:remote DefineHiddenClass
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.internal.misc.Unsafe;
+
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.Utils;
+
+/* package-private */ interface Test {
+ void test();
+}
+
+
+public class DefineHiddenClass {
+
+ static final Class> klass = DefineHiddenClass.class;
+ static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "hidden");
+ static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES, "hidden");
+
+ static void compileSources(String sourceFile) throws Throwable {
+ JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("javac");
+ launcher.addToolArg("-cp")
+ .addToolArg(Utils.TEST_CLASSES.toString())
+ .addToolArg("-d")
+ .addToolArg(CLASSES_DIR.toString())
+ .addToolArg(Paths.get(SRC_DIR.toString(), sourceFile).toString());
+
+ int exitCode = ProcessTools.executeCommand(launcher.getCommand())
+ .getExitValue();
+ if (exitCode != 0) {
+ throw new RuntimeException("Compilation of the test failed. "
+ + "Unexpected exit code: " + exitCode);
+ }
+ }
+
+ static byte[] readClassFile(String classFileName) throws Exception {
+ File classFile = new File(CLASSES_DIR + File.separator + classFileName);
+ try (FileInputStream in = new FileInputStream(classFile);
+ ByteArrayOutputStream out = new ByteArrayOutputStream())
+ {
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ return out.toByteArray();
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ compileSources("NameInString.java");
+ Lookup lookup = MethodHandles.lookup();
+ byte[] bytes = readClassFile("NameInString.class");
+ Class> c = lookup.defineHiddenClass(bytes, true, NESTMATE).lookupClass();
+ Test t = (Test) c.newInstance();
+ t.test();
+ }
+
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuper.java b/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuper.java
new file mode 100644
index 00000000000..27cc0293131
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuper.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2020, 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 8238550
+ * @summary Test that verifer assignability checks involving the 'this' pointer
+ * work ok for hidden classes.
+ * @compile FieldInSuperSub.jasm
+ * @run main/othervm -Xverify:remote FieldInSuper
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class FieldInSuper {
+
+ static final Path CLASSES_DIR = Paths.get(System.getProperty("test.classes"));
+ String hello = "hello";
+
+ static byte[] readClassFile(String classFileName) throws Exception {
+ File classFile = new File(CLASSES_DIR + File.separator + classFileName);
+ try (FileInputStream in = new FileInputStream(classFile);
+ ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ return out.toByteArray();
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ Lookup lookup = MethodHandles.lookup();
+ byte[] bytes = readClassFile("FieldInSuperSub.class");
+
+ // Define a hidden class that loads the 'this' pointer and then
+ // references field 'hello' using:
+ // getfield Field FieldInSuper.hello:"Ljava/lang/String;";
+ // This will cause the verifier to check that the 'this' pointer for
+ // the hidden class is assignable to FieldInSuper.
+ Class> c = lookup.defineHiddenClass(bytes, true, NESTMATE).lookupClass();
+ Object fss = c.newInstance();
+ c.getMethod("printMe").invoke(fss);
+ }
+
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuperSub.jasm b/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuperSub.jasm
new file mode 100644
index 00000000000..ab24f8084ee
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/FieldInSuperSub.jasm
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+super public class FieldInSuperSub extends FieldInSuper version 59:0 {
+
+ public Method "":"()V" stack 1 locals 1 {
+ aload_0;
+ invokespecial Method FieldInSuper."":"()V";
+ return;
+ }
+
+ // Change getfield to reference field 'hello' in super class.
+ public Method printMe:"()V" stack 2 locals 1 {
+ getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
+ aload_0;
+ getfield Field FieldInSuper.hello:"Ljava/lang/String;";
+ invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
+ return;
+ }
+
+} // end Class FieldInSuperSub
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/GCHiddenClass.java b/test/hotspot/jtreg/runtime/HiddenClasses/GCHiddenClass.java
new file mode 100644
index 00000000000..f269ae9c365
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/GCHiddenClass.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test that hidden classes get garbage collected.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main GCHiddenClass
+ */
+
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+// This test is based on test vmTestbase/vm/mlvm/anonloader/func/isGarbageCollected/Test.java
+public class GCHiddenClass {
+
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass { " +
+ " public TestClass() { " +
+ " System.out.println(\"Hello\"); " +
+ " } } ");
+
+ // A private method is great to keep hidden Class reference local to make it
+ // GCed on the next cycle
+ private PhantomReference> createClass(ReferenceQueue> refQueue) throws Exception {
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ return new PhantomReference>(cl, refQueue);
+ }
+
+ public boolean run() throws Exception {
+ ReferenceQueue> refQueue = new ReferenceQueue>();
+ PhantomReference> hiddenClassRef = createClass(refQueue);
+ System.gc();
+ Reference extends Class>> deletedObject = refQueue.remove();
+ return hiddenClassRef.equals(deletedObject);
+ }
+
+ public static void main(String[] args) throws Throwable {
+ GCHiddenClass gcHC = new GCHiddenClass();
+ if (!gcHC.run()) {
+ throw new RuntimeException("Test failed");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/HiddenClassStack.java b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenClassStack.java
new file mode 100644
index 00000000000..1f8b7c76429
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenClassStack.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test that stack tracing isn't broken if an exception is thrown
+ * in a hidden class.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main HiddenClassStack
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+// This test is based on vmTestbase/vm/mlvm/anonloader/func/classNameInStackTrace/Test.java
+public class HiddenClassStack {
+
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass { " +
+ " public TestClass() { " +
+ " throw new RuntimeException(\"boom\"); " +
+ " } } ");
+
+ public static void main(String[] args) throws Throwable {
+
+ // An exception is thrown by class loaded by lookup.defineHiddenClass().
+ // Verify that the exception's stack trace contains name of the current
+ // test class.
+ try {
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ Object obj = cl.newInstance();
+ throw new Exception("Expected RuntimeException not thrown");
+ } catch (RuntimeException e) {
+ if (!e.getMessage().contains("boom")) {
+ throw new RuntimeException("Wrong RuntimeException, e: " + e.toString());
+ }
+ ByteArrayOutputStream byteOS = new ByteArrayOutputStream();
+ PrintStream printStream = new PrintStream(byteOS);
+ e.printStackTrace(printStream);
+ printStream.close();
+ String stackTrace = byteOS.toString("ASCII");
+ if (!stackTrace.contains(HiddenClassStack.class.getName())) {
+ throw new RuntimeException("HiddenClassStack missing from stacktrace: " +
+ stackTrace);
+ }
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/HiddenDefMeths.java b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenDefMeths.java
new file mode 100644
index 00000000000..ee3460988a5
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenDefMeths.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Tests a hidden class that implements interfaces with default methods.
+ * @library /testlibrary
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ * java.management
+ * @run main HiddenDefMeths
+ */
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
+import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
+import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
+import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
+import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
+import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
+import static jdk.internal.org.objectweb.asm.Opcodes.V1_8;
+
+public class HiddenDefMeths {
+
+ interface Resource {
+ Pointer ptr();
+ }
+
+ interface Struct extends Resource {
+ StructPointer ptr();
+ }
+
+ interface Pointer { }
+
+ interface StructPointer extends Pointer { }
+
+ interface I extends Struct {
+ void m();
+ }
+
+ static String IMPL_PREFIX = "$$impl";
+ static String PTR_FIELD_NAME = "ptr";
+
+ // Generate a class similar to:
+ //
+ // public class HiddenDefMeths$I$$impl implements HiddenDefMeths$I, HiddenDefMeths$Struct {
+ //
+ // public HiddenDefMeths$StructPointer ptr;
+ //
+ // public HiddenDefMeths$I$$impl(HiddenDefMeths$StructPointer p) {
+ // ptr = p;
+ // }
+ //
+ // public HiddenDefMeths$StructPointer ptr() {
+ // return ptr;
+ // }
+ // }
+ //
+ byte[] generate(Class> iface) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+ String ifaceTypeName = Type.getInternalName(iface);
+ String proxyClassName = ifaceTypeName + IMPL_PREFIX;
+ // class definition
+ cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, proxyClassName,
+ desc(Object.class) + desc(ifaceTypeName) + desc(Struct.class),
+ name(Object.class),
+ new String[] { ifaceTypeName, name(Struct.class) });
+
+ cw.visitField(ACC_PUBLIC, PTR_FIELD_NAME, desc(StructPointer.class), desc(StructPointer.class), null);
+ cw.visitEnd();
+
+ // constructor
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "",
+ meth(desc(void.class), desc(StructPointer.class)),
+ meth(desc(void.class), desc(StructPointer.class)), null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, name(Object.class), "", meth(desc(void.class)), false);
+ mv.visitVarInsn(ALOAD, 1);
+ // Execution of this PUTFIELD instruction causes the bug's ClassNotFoundException.
+ mv.visitFieldInsn(PUTFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ // ptr() impl
+ mv = cw.visitMethod(ACC_PUBLIC, PTR_FIELD_NAME, meth(desc(StructPointer.class)),
+ meth(desc(StructPointer.class)), null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitFieldInsn(GETFIELD, proxyClassName, PTR_FIELD_NAME, desc(StructPointer.class));
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ String name(Class> clazz) {
+ if (clazz.isPrimitive()) {
+ throw new IllegalStateException();
+ } else if (clazz.isArray()) {
+ return desc(clazz);
+ } else {
+ return clazz.getName().replaceAll("\\.", "/");
+ }
+ }
+
+ String desc(Class> clazz) {
+ String mdesc = MethodType.methodType(clazz).toMethodDescriptorString();
+ return mdesc.substring(mdesc.indexOf(')') + 1);
+ }
+
+ String desc(String clazzName) {
+ return "L" + clazzName + ";";
+ }
+
+ String gen(String clazz, String... typeargs) {
+ return clazz.substring(0, clazz.length() - 1) + Stream.of(typeargs).collect(Collectors.joining("", "<", ">")) + ";";
+ }
+
+ String meth(String restype, String... argtypes) {
+ return Stream.of(argtypes).collect(Collectors.joining("", "(", ")")) + restype;
+ }
+
+ String meth(Method m) {
+ return MethodType.methodType(m.getReturnType(), m.getParameterTypes()).toMethodDescriptorString();
+ }
+
+ public static void main(String[] args) throws Throwable {
+ byte[] bytes = new HiddenDefMeths().generate(I.class);
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+ I i = (I)cl.getConstructors()[0].newInstance(new Object[] { null });
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/HiddenGetModule.java b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenGetModule.java
new file mode 100644
index 00000000000..aa9dbcf9030
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/HiddenGetModule.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test that a hidden class has the same module as its lookup class.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @compile pkg/HasNamedModule.java
+ * @run main/othervm HiddenGetModule
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import java.io.IOException;
+import java.lang.ModuleLayer;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class HiddenGetModule {
+
+ static byte unnamedKlassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+ public static ModuleFinder finderOf(ModuleDescriptor... descriptors) {
+
+ // Create a ModuleReference for each module
+ Map namesToReference = new HashMap<>();
+
+ for (ModuleDescriptor descriptor : descriptors) {
+ String name = descriptor.name();
+
+ URI uri = URI.create("module:/" + name);
+
+ ModuleReference mref = new ModuleReference(descriptor, uri) {
+ @Override
+ public ModuleReader open() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
+ namesToReference.put(name, mref);
+ }
+
+ return new ModuleFinder() {
+ @Override
+ public Optional find(String name) {
+ Objects.requireNonNull(name);
+ return Optional.ofNullable(namesToReference.get(name));
+ }
+ @Override
+ public Set findAll() {
+ return new HashSet<>(namesToReference.values());
+ }
+ };
+ }
+
+ public static void main(String[] args) throws Throwable {
+
+ // Test unnamed module.
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(unnamedKlassbuf, false, NESTMATE).lookupClass();
+ if (cl.getModule() != HiddenGetModule.class.getModule()) {
+ throw new RuntimeException("hidden class and lookup class have different unnamed modules");
+ }
+
+ // Test named module.
+ MyClassLoader myClassLoader = new MyClassLoader();
+
+ // Define a module named HiddenModule containing package pkg.
+ ModuleDescriptor descriptor = ModuleDescriptor.newModule("HiddenModule")
+ .requires("java.base")
+ .exports("pkg")
+ .build();
+
+ // Set up a ModuleFinder containing the module for this layer.
+ ModuleFinder finder = finderOf(descriptor);
+
+ // Resolves "HiddenModule"
+ Configuration cf = ModuleLayer.boot()
+ .configuration()
+ .resolve(finder, ModuleFinder.of(), Set.of("HiddenModule"));
+
+ // map module to class loader
+ Map map = new HashMap<>();
+ map.put("HiddenModule", myClassLoader);
+
+ // Create layer that contains HiddenModule
+ ModuleLayer layer = ModuleLayer.boot().defineModules(cf, map::get);
+
+ byte klassbuf[] = InMemoryJavaCompiler.compile("pkg.TestClass",
+ "package pkg; " +
+ "public class TestClass { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+ // Load the class and call the method that defines a hidden class and compares modules.
+ Class>c = Class.forName("pkg.HasNamedModule", true, myClassLoader);
+ if (c.getClassLoader() != myClassLoader) {
+ throw new RuntimeException("pkg.HasNamedModule defined by wrong classloader: " + c.getClassLoader());
+ }
+ Method m = c.getDeclaredMethod("compareModules", byte[].class);
+ m.invoke(null, klassbuf);
+ }
+
+
+ public static class MyClassLoader extends ClassLoader {
+
+ public static final String CLASS_NAME = "HasNamedModule";
+
+ static ByteBuffer readClassFile(String name) {
+ File f = new File(System.getProperty("test.classes", "."), name);
+ try (FileInputStream fin = new FileInputStream(f);
+ FileChannel fc = fin.getChannel()) {
+ return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ } catch (IOException e) {
+ throw new RuntimeException("Can't open file: " + name + ", " + e.toString());
+ }
+ }
+
+ protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ Class> c;
+ if (!name.contains(CLASS_NAME)) {
+ c = super.loadClass(name, resolve);
+ } else {
+ // should not delegate to the system class loader
+ c = findClass(name);
+ if (resolve) {
+ resolveClass(c);
+ }
+ }
+ return c;
+ }
+
+ protected Class> findClass(String name) throws ClassNotFoundException {
+ if (!name.contains(CLASS_NAME)) {
+ throw new ClassNotFoundException("Unexpected class: " + name);
+ }
+ return defineClass(name, readClassFile(name.replace(".", File.separator) + ".class"), null);
+ }
+ } /* MyClassLoader */
+
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/InstantiateHiddenClass.java b/test/hotspot/jtreg/runtime/HiddenClasses/InstantiateHiddenClass.java
new file mode 100644
index 00000000000..773588b11f9
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/InstantiateHiddenClass.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test issues with instantiating hidden classes.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main InstantiateHiddenClass
+ */
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+public class InstantiateHiddenClass {
+
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+ public static void main(String[] args) throws Throwable {
+
+ // Test that a hidden class cannot be found through its name.
+ try {
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ Class.forName(cl.getName()).newInstance();
+ throw new RuntimeException("Expected ClassNotFoundException not thrown");
+ } catch (ClassNotFoundException e ) {
+ // Test passed
+ }
+
+
+ // Create two hidden classes and instantiate an object from each of them.
+ // Verify that the references to these objects are different and references
+ // to their classes are not equal either.
+ Lookup lookup = MethodHandles.lookup();
+ Class> c1 = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ Class> c2 = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ Object o1 = c1.newInstance();
+ Object o2 = c2.newInstance();
+ if (o1 == o2) {
+ throw new RuntimeException("Objects should not be equal");
+ }
+ if (o1.getClass() == o2.getClass()) {
+ throw new RuntimeException("Classes should not be equal");
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/NestedHidden.java b/test/hotspot/jtreg/runtime/HiddenClasses/NestedHidden.java
new file mode 100644
index 00000000000..55d9fa9862d
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/NestedHidden.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Creates a hidden class inside of a hidden class.
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main p.NestedHidden
+ */
+
+package p;
+
+import java.lang.*;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+
+// Test that a hidden class can define its own hidden class by calling
+// lookup.defineHiddenClass().
+public class NestedHidden {
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("p.TestClass",
+ "package p; " +
+ "public class TestClass { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+ public static void main(String args[]) throws Exception {
+ // The hidden class calls lookup.defineHiddenClass(), creating a nested hidden class.
+ byte klassbuf2[] = InMemoryJavaCompiler.compile("p.TestClass2",
+ "package p; " +
+ "import java.lang.invoke.MethodHandles; " +
+ "import java.lang.invoke.MethodHandles.Lookup; " +
+ "import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*; " +
+ "public class TestClass2 { " +
+ " public static void doit() throws Throwable { " +
+ " Lookup lookup = MethodHandles.lookup(); " +
+ " Class> klass2 = lookup.defineHiddenClass(p.NestedHidden.klassbuf, true, NESTMATE).lookupClass(); " +
+ " Class[] dArgs = new Class[2]; " +
+ " dArgs[0] = String.class; " +
+ " dArgs[1] = String.class; " +
+ " try { " +
+ " klass2.getMethod(\"concat\", dArgs).invoke(null, \"CC\", \"DD\"); " +
+ " } catch (Throwable ex) { " +
+ " throw new RuntimeException(\"Exception: \" + ex.toString()); " +
+ " } " +
+ "} } ");
+
+ Lookup lookup = MethodHandles.lookup();
+ Class> klass2 = lookup.defineHiddenClass(klassbuf2, true, NESTMATE).lookupClass();
+ klass2.getMethod("doit").invoke(null);
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/StressHiddenClasses.java b/test/hotspot/jtreg/runtime/HiddenClasses/StressHiddenClasses.java
new file mode 100644
index 00000000000..60470402d97
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/StressHiddenClasses.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Stress defining hidden classes.
+ * @requires !vm.graal.enabled
+ * @library /test/lib
+ * @modules jdk.compiler
+ * @run main/othervm StressHiddenClasses
+ */
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+// This test is based on vmTestbase/vm/mlvm/anonloader/share/StressClassLoadingTest.java
+public class StressHiddenClasses {
+
+ private static final int PARSE_TIMEOUT = 0;
+ private static final int ITERATIONS = 40000;
+
+ static byte klassbuf[] = InMemoryJavaCompiler.compile("TestClass",
+ "public class TestClass { " +
+ " public static void concat(String one, String two) throws Throwable { " +
+ " System.out.println(one + two);" +
+ " } } ");
+
+
+ public void run() throws Exception {
+ for (int x = 0; x < ITERATIONS; x++) {
+ Thread parserThread = new Thread() {
+ public void run() {
+ try {
+ Lookup lookup = MethodHandles.lookup();
+ Class> c = lookup.defineHiddenClass(klassbuf, true, NESTMATE).lookupClass();
+ } catch (Throwable e) {
+ throw new RuntimeException("Unexpected exception: " + e.toString());
+ }
+ }
+ };
+
+ parserThread.start();
+ parserThread.join(PARSE_TIMEOUT);
+
+ if (parserThread.isAlive()) {
+ System.out.println("parser thread may be hung!");
+ StackTraceElement[] stack = parserThread.getStackTrace();
+ System.out.println("parser thread stack len: " + stack.length);
+ System.out.println(parserThread + " stack trace:");
+ for (int i = 0; i < stack.length; ++i) {
+ System.out.println(parserThread + "\tat " + stack[i]);
+ }
+
+ parserThread.join(); // Wait until either thread finishes or test times out.
+ }
+ }
+ }
+
+
+ public static void main(String[] args) throws Throwable {
+ StressHiddenClasses shc = new StressHiddenClasses();
+ shc.run();
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/TestHiddenClassUnloading.java b/test/hotspot/jtreg/runtime/HiddenClasses/TestHiddenClassUnloading.java
new file mode 100644
index 00000000000..c633151dc63
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/TestHiddenClassUnloading.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test unloading of hidden classes.
+ * @modules java.management
+ * @library /test/lib /
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:-BackgroundCompilation
+ * compiler.classUnloading.hiddenClass.TestHiddenClassUnloading
+ */
+
+package compiler.classUnloading.hiddenClass;
+
+import sun.hotspot.WhiteBox;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLConnection;
+import compiler.whitebox.CompilerWhiteBoxTest;
+
+// This is based on test compiler/classUnloading/anonymousClass/TestAnonymousClassUnloading.java
+public class TestHiddenClassUnloading {
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ /**
+ * We override hashCode here to be able to access this implementation
+ * via an Object reference (we cannot cast to TestHiddenClassUnloading).
+ */
+ @Override
+ public int hashCode() {
+ return 42;
+ }
+
+ /**
+ * Does some work by using the hiddenClass.
+ * @param hiddenClass Class performing some work (will be unloaded)
+ */
+ static private void doWork(Class> hiddenClass) throws InstantiationException, IllegalAccessException {
+ // Create a new instance
+ Object anon = hiddenClass.newInstance();
+ // We would like to call a method of hiddenClass here but we cannot cast because the class
+ // was loaded by a different class loader. One solution would be to use reflection but since
+ // we want C2 to implement the call as an IC we call Object::hashCode() here which actually
+ // calls hiddenClass::hashCode(). C2 will then implement this call as an IC.
+ if (anon.hashCode() != 42) {
+ new RuntimeException("Work not done");
+ }
+ }
+
+ /**
+ * Makes sure that method is compiled by forcing compilation if not yet compiled.
+ * @param m Method to be checked
+ */
+ static private void makeSureIsCompiled(Method m) {
+ // Make sure background compilation is disabled
+ if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) {
+ throw new RuntimeException("Background compilation enabled");
+ }
+
+ // Check if already compiled
+ if (!WHITE_BOX.isMethodCompiled(m)) {
+ // If not, try to compile it with C2
+ if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) {
+ // C2 compiler not available, try to compile with C1
+ WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE);
+ }
+ // Because background compilation is disabled, method should now be compiled
+ if(!WHITE_BOX.isMethodCompiled(m)) {
+ throw new RuntimeException(m + " not compiled");
+ }
+ }
+ }
+
+ /**
+ * This test creates stale Klass* metadata referenced by a compiled IC.
+ *
+ * The following steps are performed:
+ * (1) A hidden version of TestHiddenClassUnloading is loaded by a custom class loader
+ * (2) The method doWork that calls a method of the hidden class is compiled. The call
+ * is implemented as an IC referencing Klass* metadata of the hidden class.
+ * (3) Unloading of the hidden class is enforced. The IC now references dead metadata.
+ */
+ static public void main(String[] args) throws Exception {
+ // (1) Load a hidden version of this class using method lookup.defineHiddenClass().
+ String rn = TestHiddenClassUnloading.class.getSimpleName() + ".class";
+ URL classUrl = TestHiddenClassUnloading.class.getResource(rn);
+ URLConnection connection = classUrl.openConnection();
+
+ int length = connection.getContentLength();
+ byte[] classBytes = connection.getInputStream().readAllBytes();
+ if (length != -1 && classBytes.length != length) {
+ throw new IOException("Expected:" + length + ", actual: " + classBytes.length);
+ }
+
+ Lookup lookup = MethodHandles.lookup();
+ Class> hiddenClass = lookup.defineHiddenClass(classBytes, true, NESTMATE).lookupClass();
+
+ // (2) Make sure all paths of doWork are profiled and compiled
+ for (int i = 0; i < 100000; ++i) {
+ doWork(hiddenClass);
+ }
+
+ // Make sure doWork is compiled now
+ Method doWork = TestHiddenClassUnloading.class.getDeclaredMethod("doWork", Class.class);
+ makeSureIsCompiled(doWork);
+
+ // (3) Throw away reference to hiddenClass to allow unloading
+ hiddenClass = null;
+
+ // Force garbage collection to trigger unloading of hiddenClass
+ WHITE_BOX.fullGC();
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/hidden/NameInString.java b/test/hotspot/jtreg/runtime/HiddenClasses/hidden/NameInString.java
new file mode 100644
index 00000000000..6d507935d48
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/hidden/NameInString.java
@@ -0,0 +1,43 @@
+/*
+ * 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 that a string that is the same as its hidden class name does not
+ * get clobbered when the JVM changes the hidden class's name.
+ */
+public class NameInString implements Test {
+
+ private String realTest() {
+ return "NameInString";
+ }
+
+ public void test() {
+ String result = realTest();
+ // Make sure that the Utf8 constant pool entry for "NameInString" is okay.
+ if (!result.substring(0, 6).equals("NameIn") ||
+ !result.substring(6).equals("String")) {
+ throw new RuntimeException("'NameInString is bad: " + result);
+ }
+
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/HiddenClasses/pkg/HasNamedModule.java b/test/hotspot/jtreg/runtime/HiddenClasses/pkg/HasNamedModule.java
new file mode 100644
index 00000000000..e393fcbc52a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/HiddenClasses/pkg/HasNamedModule.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, 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 pkg;
+
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+
+// This class is used by test HiddenGetModule.java.
+public class HasNamedModule {
+
+ public static void compareModules(byte[] klassbuf) throws Throwable {
+ String moduleName = HasNamedModule.class.getModule().toString();
+ System.out.println("HasNamedModule module: " + moduleName);
+ Lookup lookup = MethodHandles.lookup();
+ Class> cl = lookup.defineHiddenClass(klassbuf, false, NESTMATE).lookupClass();
+ if (cl.getModule() != HasNamedModule.class.getModule()) {
+ System.out.println("HasNamedModule: " + moduleName +
+ ", hidden class module: " + cl.getModule());
+ throw new RuntimeException("hidden class and lookup class have different modules");
+ }
+ if (!moduleName.contains("HiddenModule")) {
+ throw new RuntimeException("wrong module name: " + moduleName);
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/Nestmates/membership/Helper.java b/test/hotspot/jtreg/runtime/Nestmates/membership/Helper.java
new file mode 100644
index 00000000000..2903f688b2a
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/Helper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+// Helper for TestNestHostErrorWithClassUnload.
+// This class is loaded into the new classloader to do the test and we invoke
+// its test() method reflectively. It has to be public to be accessible from
+// the un-named package of another loader.
+public class Helper {
+ public static void test() {
+ try {
+ P2.PackagedNestHost2.Member.m();
+ throw new RuntimeException("Call to P2.PackagedNestHost2.Member.m() succeeded unexpectedly!");
+ }
+ catch (IllegalAccessError iae) {
+ System.out.println("Got expected exception:" + iae);
+ }
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/Nestmates/membership/OtherPackage.java b/test/hotspot/jtreg/runtime/Nestmates/membership/OtherPackage.java
new file mode 100644
index 00000000000..ede167842e4
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/OtherPackage.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, 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 test;
+
+public class OtherPackage {
+}
diff --git a/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java b/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java
new file mode 100644
index 00000000000..07d75c79b23
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/TestDynamicNestmateMembership.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2018, 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
+ * @summary Test the rules for dynamic nest membership using the Lookup.defineHiddenClass API
+ * @compile OtherPackage.java
+ * @run main/othervm TestDynamicNestmateMembership
+ */
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.Path;
+import java.lang.invoke.MethodHandles;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+
+/* package */ class DynamicNestmate { }
+
+/* package */ class DynamicNestmate2 { }
+
+/* package */ class StaticHost {
+ static class StaticMember {
+ }
+}
+
+public class TestDynamicNestmateMembership {
+
+ static class Member {
+ static MethodHandles.Lookup getLookup() {
+ return MethodHandles.lookup();
+ }
+ }
+
+ static final String CLASSES = System.getProperty("test.classes");
+ static final Path CLASSES_DIR = Paths.get(CLASSES);
+
+ static byte[] getBytesForClass(String name) throws IOException {
+ Path classFile;
+ if (name.indexOf('.') > 0) {
+ // it's in a package
+ String[] paths = name.split("\\.");
+ classFile = CLASSES_DIR.resolve(paths[0]);
+ classFile = classFile.resolve(paths[1] + ".class");
+ }
+ else {
+ classFile = CLASSES_DIR.resolve(name + ".class");
+ }
+ return Files.readAllBytes(classFile);
+ }
+
+ static final Class> cls = TestDynamicNestmateMembership.class;
+ static final MethodHandles.Lookup main_lookup = MethodHandles.lookup();
+
+ public static void main(String[] args) throws Throwable {
+ test_validInjection();
+ test_hostIsMember();
+ test_otherPackage();
+ test_alreadyNestMember();
+ test_alreadyNestHost();
+ }
+
+ // Inject a valid class into the nest of the current class
+ static void test_validInjection() {
+ String name = "DynamicNestmate";
+ inject(name, null);
+ }
+
+ // Try to inject a class into a "host" that is itself a member.
+ // This is redirected at the defineClass level to the member's
+ // host and so will succeed.
+ static void test_hostIsMember() {
+ String name = "DynamicNestmate2";
+ inject(name, Member.getLookup(), null);
+ }
+
+ // Try to inject a class that has a static NestHost attribute
+ // No error since the nest host has been set when it's created.
+ // Static nest membership is effectively ignored.
+ static void test_alreadyNestMember() {
+ String name = "StaticHost$StaticMember";
+ inject(name, null);
+ }
+
+ // Try to inject a class that has the NestMembers attribute.
+ // No error since the nest host has been set when it's created.
+ // Static nest membership is effectively ignored.
+ static void test_alreadyNestHost() {
+ String name = "StaticHost";
+ inject(name, null);
+ }
+
+ // Try to inject a class that is in another package
+ static void test_otherPackage() {
+ String name = "test.OtherPackage";
+ inject(name, IllegalArgumentException.class);
+ }
+
+ static void inject(String name, Class extends Throwable> ex) {
+ inject(name, main_lookup, ex);
+ }
+
+ static void inject(String name, MethodHandles.Lookup lookup,
+ Class extends Throwable> ex) {
+ Class> target = lookup.lookupClass();
+ String action = "Injecting " + name + " into the nest of " +
+ target.getSimpleName();
+ try {
+ byte[] bytes = getBytesForClass(name);
+ Class> nestmate = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+ if (ex != null) {
+ throw new RuntimeException(action + " was expected to throw " +
+ ex.getSimpleName());
+ }
+ Class> actualHost = nestmate.getNestHost();
+ Class> expectedHost = target.getNestHost();
+ if (actualHost != expectedHost) {
+ throw new RuntimeException(action + " expected a nest-host of "
+ + expectedHost.getSimpleName() +
+ " but got " + actualHost.getSimpleName());
+ }
+ System.out.print("Ok: " + action + " succeeded: ");
+ if (actualHost != target) {
+ System.out.print("(re-directed to target's nest-host) ");
+ }
+ 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() + ":" +
+ t.getMessage());
+ }
+ else {
+ throw new RuntimeException(action + " got unexpected exception " +
+ t.getClass().getSimpleName(), t);
+ }
+ }
+ }
+
+}
+
diff --git a/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestHostErrorWithClassUnload.java b/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestHostErrorWithClassUnload.java
new file mode 100644
index 00000000000..8c5be5e7c84
--- /dev/null
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestHostErrorWithClassUnload.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary Test the ability to safely unload a class that has an error
+ * with its designated nest host. The nest host class must resolve
+ * successfully but fail validation. This tests a specific, otherwise
+ * untested, code path in ResolutionErrorTable::free_entry.
+ *
+ * @library /runtime/testlibrary
+ * @compile TestNestHostErrorWithClassUnload.java
+ * Helper.java
+ * PackagedNestHost.java
+ * PackagedNestHost2.java
+ * @compile PackagedNestHost2Member.jcod
+ *
+ * @run main/othervm -Xlog:class+unload=trace TestNestHostErrorWithClassUnload
+ */
+
+// Test setup:
+// PackagedNestHost.java defines P1.PackagedNestHost, which is referenced
+// by P2.PackageNestHost2.Member
+// PackagedNestHost2.java defines P2.PackagedNestHost2 and its nested Member
+// class.
+// PackagedNestHost2Member.jcod changes P2.PackagedNestHost.Member to claim
+// it is in the nest of P1.PackagedNestHost.
+// Helper.java is a helper class to run the test under the other classloader.
+// A separate class is used to avoid confusion because if a public nested
+// class were to be used, then the main test class would be loaded a second
+// time in the other loader, and also subsequently unloaded.
+//
+// We load all classes into a new classloader and then try to call
+// P2.PackagedNestHost.Member.m() which will be private at runtime. That will
+// trigger nest host resolution and validation and the invocation will fail
+// with IllegalAccessError. We then drop the classloader and invoke GC to force
+// the unloading of the classes. Not all GC configurations are guaranteed to
+// result in unloading, but that isn't essential for the test to succeed - we
+// know that when unloading does occur we have tested the desired code path.
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class TestNestHostErrorWithClassUnload {
+
+ static final MethodType INVOKE_T = MethodType.methodType(void.class);
+
+ public static void main(String[] args) throws Throwable {
+ ClassLoader cl = ClassUnloadCommon.newClassLoader();
+ Class> c = cl.loadClass("Helper");
+ MethodHandle mh = MethodHandles.lookup().findStatic(c, "test", INVOKE_T);
+ mh.invoke();
+ // now drop all references so we can trigger unloading
+ mh = null;
+ c = null;
+ cl = null;
+ ClassUnloadCommon.triggerUnloading();
+ }
+}
diff --git a/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestmateMembership.java b/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestmateMembership.java
index b1730b6724e..62bbcf7d5a7 100644
--- a/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestmateMembership.java
+++ b/test/hotspot/jtreg/runtime/Nestmates/membership/TestNestmateMembership.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -697,8 +697,8 @@ public class TestNestmateMembership {
check_expected(expected, msg);
}
- msg = "class TestNestmateMembership$CallerNoHost tried to access " +
- "private method 'void TestNestmateMembership$Target.m()'";
+ msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
+ "method 'void TestNestmateMembership$Target.m()'";
try {
CallerNoHost.invokeTarget();
throw new Error("Missing IllegalAccessError: " + msg);
@@ -720,19 +720,21 @@ public class TestNestmateMembership {
static void test_SelfHostInvoke() throws Throwable {
System.out.println("Testing for class that lists itself as nest-host");
String msg = "Type TestNestmateMembership$TargetSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member)";
try {
Caller.invokeTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetSelfHost with modifiers \"private static\"";
try {
Caller.invokeTargetSelfHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such method: TestNestmateMembership$TargetSelfHost.m()void/invokeStatic";
@@ -745,43 +747,42 @@ public class TestNestmateMembership {
}
msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.invokeTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.invokeTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
static void test_MissingHostInvoke() throws Throwable {
System.out.println("Testing for nest-host class that does not exist");
- String msg = "Unable to load nest-host class (NoTargetMissingHost) of " +
- "TestNestmateMembership$TargetMissingHost";
- String cause_msg = "NoTargetMissingHost";
+ String msg = "Nest host resolution of TestNestmateMembership$TargetMissingHost with host" +
+ " NoTargetMissingHost failed: java.lang.NoClassDefFoundError: NoTargetMissingHost";
try {
Caller.invokeTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class" +
+ " TestNestmateMembership$TargetMissingHost with modifiers \"private static\"";
try {
Caller.invokeTargetMissingHostReflectively();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
msg = "no such method: TestNestmateMembership$TargetMissingHost.m()void/invokeStatic";
try {
@@ -800,44 +801,42 @@ public class TestNestmateMembership {
check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of " +
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
+ msg = "Nest host resolution of TestNestmateMembership$CallerMissingHost with host" +
+ " NoCallerMissingHost failed: java.lang.NoClassDefFoundError: NoCallerMissingHost";
try {
CallerMissingHost.invokeTarget();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of "+
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
try {
CallerMissingHost.invokeTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
}
static void test_NotInstanceHostInvoke() throws Throwable {
System.out.println("Testing for nest-host class that is not an instance class");
String msg = "Type TestNestmateMembership$TargetNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
Caller.invokeTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class "+
+ "TestNestmateMembership$TargetNotInstanceHost with modifiers \"private static\"";
try {
Caller.invokeTargetNotInstanceHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such method: TestNestmateMembership$TargetNotInstanceHost.m()void/invokeStatic";
@@ -849,42 +848,42 @@ public class TestNestmateMembership {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a " +
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
CallerNotInstanceHost.invokeTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotInstanceHost.invokeTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
static void test_NotOurHostInvoke() throws Throwable {
System.out.println("Testing for nest-host class that does not list us in its nest");
- String msg = "Type TestNestmateMembership$TargetNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ String msg = "Type TestNestmateMembership$TargetNotOurHost (loader: 'app') is not a " +
+ "nest member of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.invokeTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotOurHost with modifiers \"private static\"";
try {
Caller.invokeTargetNotOurHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such method: TestNestmateMembership$TargetNotOurHost.m()void/invokeStatic";
@@ -897,21 +896,19 @@ public class TestNestmateMembership {
}
msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.invokeTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.invokeTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -919,19 +916,19 @@ public class TestNestmateMembership {
static void test_WrongPackageHostInvoke() {
System.out.println("Testing for nest-host and nest-member in different packages");
String msg = "Type P2.PackagedNestHost2$Member (loader: 'app') is not a nest member of " +
- "P1.PackagedNestHost (loader: 'app'): types are in different packages";
+ "type P1.PackagedNestHost (loader: 'app'): types are in different packages";
try {
P1.PackagedNestHost.doInvoke();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
try {
P2.PackagedNestHost2.Member.doInvoke();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -953,9 +950,9 @@ public class TestNestmateMembership {
"method 'void TestNestmateMembership$TargetNoHost.()'";
try {
Caller.newTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$Caller cannot access a member of class " +
@@ -980,18 +977,18 @@ public class TestNestmateMembership {
"method 'void TestNestmateMembership$Target.()'";
try {
CallerNoHost.newTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
"method 'void TestNestmateMembership$TargetNoHost.()'";
try {
CallerNoHost.newTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -999,19 +996,21 @@ public class TestNestmateMembership {
static void test_SelfHostConstruct() throws Throwable {
System.out.println("Testing for class that lists itself as nest-host");
String msg = "Type TestNestmateMembership$TargetSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.newTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetSelfHost with modifiers \"private\"";
try {
Caller.newTargetSelfHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such constructor: TestNestmateMembership$TargetSelfHost.()void/newInvokeSpecial";
@@ -1024,43 +1023,42 @@ public class TestNestmateMembership {
}
msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.newTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.newTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
static void test_MissingHostConstruct() throws Throwable {
System.out.println("Testing for nest-host class that does not exist");
- String msg = "Unable to load nest-host class (NoTargetMissingHost) of " +
- "TestNestmateMembership$TargetMissingHost";
- String cause_msg = "NoTargetMissingHost";
+ String msg = "Nest host resolution of TestNestmateMembership$TargetMissingHost with " +
+ "host NoTargetMissingHost failed: java.lang.NoClassDefFoundError: NoTargetMissingHost";
try {
Caller.newTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetMissingHost with modifiers \"private\"";
try {
Caller.newTargetMissingHostReflectively();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
msg = "no such constructor: TestNestmateMembership$TargetMissingHost.()void/newInvokeSpecial";
try {
@@ -1071,44 +1069,42 @@ public class TestNestmateMembership {
check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of " +
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
+ msg = "Nest host resolution of TestNestmateMembership$CallerMissingHost with host " +
+ "NoCallerMissingHost failed: java.lang.NoClassDefFoundError: NoCallerMissingHost";
try {
CallerMissingHost.newTarget();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of "+
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
try {
CallerMissingHost.newTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
}
static void test_NotInstanceHostConstruct() throws Throwable {
System.out.println("Testing for nest-host class that is not an instance class");
String msg = "Type TestNestmateMembership$TargetNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
Caller.newTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotInstanceHost with modifiers \"private\"";
try {
Caller.newTargetNotInstanceHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such constructor: TestNestmateMembership$TargetNotInstanceHost.()void/newInvokeSpecial";
@@ -1121,21 +1117,19 @@ public class TestNestmateMembership {
}
msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
CallerNotInstanceHost.newTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotInstanceHost.newTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1143,19 +1137,21 @@ public class TestNestmateMembership {
static void test_NotOurHostConstruct() throws Throwable {
System.out.println("Testing for nest-host class that does not list us in its nest");
String msg = "Type TestNestmateMembership$TargetNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.newTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotOurHost with modifiers \"private\"";
try {
Caller.newTargetNotOurHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "no such constructor: TestNestmateMembership$TargetNotOurHost.()void/newInvokeSpecial";
@@ -1168,21 +1164,21 @@ public class TestNestmateMembership {
}
msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.newTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.newTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1190,19 +1186,19 @@ public class TestNestmateMembership {
static void test_WrongPackageHostConstruct() {
System.out.println("Testing for nest-host and nest-member in different packages");
String msg = "Type P2.PackagedNestHost2$Member (loader: 'app') is not a nest member of " +
- "P1.PackagedNestHost (loader: 'app'): types are in different packages";
+ "type P1.PackagedNestHost (loader: 'app'): types are in different packages";
try {
P1.PackagedNestHost.doConstruct();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
try {
P2.PackagedNestHost2.Member.doConstruct();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1224,9 +1220,9 @@ public class TestNestmateMembership {
"field TestNestmateMembership$TargetNoHost.f";
try {
Caller.getFieldTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$Caller cannot access a member of class " +
@@ -1251,18 +1247,18 @@ public class TestNestmateMembership {
"field TestNestmateMembership$Target.f";
try {
CallerNoHost.getFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
"field TestNestmateMembership$TargetNoHost.f";
try {
CallerNoHost.getFieldTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1270,140 +1266,143 @@ public class TestNestmateMembership {
static void test_SelfHostGetField() throws Throwable {
System.out.println("Testing for class that lists itself as nest-host");
String msg = "Type TestNestmateMembership$TargetSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.getFieldTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetSelfHost with modifiers \"private static\"";
try {
Caller.getFieldTargetSelfHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetSelfHost.f/int/getStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.getFieldTargetSelfHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.getFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.getFieldTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
static void test_MissingHostGetField() throws Throwable {
System.out.println("Testing for nest-host class that does not exist");
- String msg = "Unable to load nest-host class (NoTargetMissingHost) of " +
- "TestNestmateMembership$TargetMissingHost";
- String cause_msg = "NoTargetMissingHost";
+ String msg = "Nest host resolution of TestNestmateMembership$TargetMissingHost with " +
+ "host NoTargetMissingHost failed: java.lang.NoClassDefFoundError: NoTargetMissingHost";
try {
Caller.getFieldTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetMissingHost with modifiers \"private static\"";
try {
Caller.getFieldTargetMissingHostReflectively();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetMissingHost.f/int/getStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.getFieldTargetMissingHostMH();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of " +
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
+ msg = "Nest host resolution of TestNestmateMembership$CallerMissingHost with " +
+ "host NoCallerMissingHost failed: java.lang.NoClassDefFoundError: NoCallerMissingHost";
try {
CallerMissingHost.getFieldTarget();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of "+
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
try {
CallerMissingHost.getFieldTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
}
static void test_NotInstanceHostGetField() throws Throwable {
System.out.println("Testing for nest-host class that is not an instance class");
String msg = "Type TestNestmateMembership$TargetNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
Caller.getFieldTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotInstanceHost with modifiers \"private static\"";
try {
Caller.getFieldTargetNotInstanceHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetNotInstanceHost.f/int/getStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.getFieldTargetNotInstanceHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
CallerNotInstanceHost.getFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotInstanceHost.getFieldTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1411,45 +1410,47 @@ public class TestNestmateMembership {
static void test_NotOurHostGetField() throws Throwable {
System.out.println("Testing for nest-host class that does not list us in its nest");
String msg = "Type TestNestmateMembership$TargetNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.getFieldTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotOurHost with modifiers \"private static\"";
try {
Caller.getFieldTargetNotOurHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetNotOurHost.f/int/getStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.getFieldTargetNotOurHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.getFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.getFieldTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1457,19 +1458,19 @@ public class TestNestmateMembership {
static void test_WrongPackageHostGetField() {
System.out.println("Testing for nest-host and nest-member in different packages");
String msg = "Type P2.PackagedNestHost2$Member (loader: 'app') is not a nest member of " +
- "P1.PackagedNestHost (loader: 'app'): types are in different packages";
+ "type P1.PackagedNestHost (loader: 'app'): types are in different packages";
try {
P1.PackagedNestHost.doGetField();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
try {
P2.PackagedNestHost2.Member.doGetField();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1489,9 +1490,9 @@ public class TestNestmateMembership {
"field TestNestmateMembership$TargetNoHost.f";
try {
Caller.putFieldTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$Caller cannot access a member of class " +
@@ -1516,18 +1517,18 @@ public class TestNestmateMembership {
"field TestNestmateMembership$Target.f";
try {
CallerNoHost.putFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
msg = "class TestNestmateMembership$CallerNoHost tried to access private " +
"field TestNestmateMembership$TargetNoHost.f";
try {
CallerNoHost.putFieldTargetNoHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1535,140 +1536,142 @@ public class TestNestmateMembership {
static void test_SelfHostPutField() throws Throwable {
System.out.println("Testing for class that lists itself as nest-host");
String msg = "Type TestNestmateMembership$TargetSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$TargetSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.putFieldTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetSelfHost with modifiers \"private static\"";
try {
Caller.putFieldTargetSelfHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetSelfHost.f/int/putStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.putFieldTargetSelfHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
+ " of type TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.putFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerSelfHost (loader: 'app') is not a nest member" +
- " of TestNestmateMembership$CallerSelfHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerSelfHost.putFieldTargetSelfHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
static void test_MissingHostPutField() throws Throwable {
System.out.println("Testing for nest-host class that does not exist");
- String msg = "Unable to load nest-host class (NoTargetMissingHost) of " +
- "TestNestmateMembership$TargetMissingHost";
- String cause_msg = "NoTargetMissingHost";
+ String msg = "Nest host resolution of TestNestmateMembership$TargetMissingHost with " +
+ "host NoTargetMissingHost failed: java.lang.NoClassDefFoundError: NoTargetMissingHost";
try {
Caller.putFieldTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetMissingHost with modifiers \"private static\"";
try {
Caller.putFieldTargetMissingHostReflectively();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetMissingHost.f/int/putStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.putFieldTargetMissingHostMH();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessException expected) {
+ check_expected(expected, msg);
}
-
- msg = "Unable to load nest-host class (NoCallerMissingHost) of " +
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
+ msg = "Nest host resolution of TestNestmateMembership$CallerMissingHost with host " +
+ "NoCallerMissingHost failed: java.lang.NoClassDefFoundError: NoCallerMissingHost";
try {
CallerMissingHost.putFieldTarget();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
- msg = "Unable to load nest-host class (NoCallerMissingHost) of "+
- "TestNestmateMembership$CallerMissingHost";
- cause_msg = "NoCallerMissingHost";
try {
CallerMissingHost.putFieldTargetMissingHost();
- throw new Error("Missing NoClassDefFoundError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (NoClassDefFoundError expected) {
- check_expected(expected, msg, cause_msg);
+ catch (IllegalAccessError expected) {
+ check_expected(expected, msg);
}
}
static void test_NotInstanceHostPutField() throws Throwable {
System.out.println("Testing for nest-host class that is not an instance class");
String msg = "Type TestNestmateMembership$TargetNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
Caller.putFieldTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotInstanceHost with modifiers \"private static\"";
try {
Caller.putFieldTargetNotInstanceHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetNotInstanceHost.f/int/putStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.putFieldTargetNotInstanceHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
+ "nest member of type [LInvalidNestHost; (loader: 'app'): host is not an instance class";
try {
CallerNotInstanceHost.putFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotInstanceHost (loader: 'app') is not a "+
- "nest member of [LInvalidNestHost; (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotInstanceHost.putFieldTargetNotInstanceHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1676,45 +1679,46 @@ public class TestNestmateMembership {
static void test_NotOurHostPutField() throws Throwable {
System.out.println("Testing for nest-host class that does not list us in its nest");
String msg = "Type TestNestmateMembership$TargetNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
Caller.putFieldTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
+ msg = "class TestNestmateMembership$Caller cannot access a member of class " +
+ "TestNestmateMembership$TargetNotOurHost with modifiers \"private static\"";
try {
Caller.putFieldTargetNotOurHostReflectively();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
+ msg = "member is private: TestNestmateMembership$TargetNotOurHost.f/int/putStatic, " +
+ "from class TestNestmateMembership$Caller";
try {
Caller.putFieldTargetNotOurHostMH();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessException: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessException expected) {
check_expected(expected, msg);
}
-
msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
+ " of type InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.putFieldTarget();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
- msg = "Type TestNestmateMembership$CallerNotOurHost (loader: 'app') is not a nest member" +
- " of InvalidNestHost (loader: 'app'): current type is not listed as a nest member";
try {
CallerNotOurHost.putFieldTargetNotOurHost();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1722,19 +1726,19 @@ public class TestNestmateMembership {
static void test_WrongPackageHostPutField() {
System.out.println("Testing for nest-host and nest-member in different packages");
String msg = "Type P2.PackagedNestHost2$Member (loader: 'app') is not a nest member of " +
- "P1.PackagedNestHost (loader: 'app'): types are in different packages";
+ "type P1.PackagedNestHost (loader: 'app'): types are in different packages";
try {
P1.PackagedNestHost.doPutField();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
try {
P2.PackagedNestHost2.Member.doPutField();
- throw new Error("Missing IncompatibleClassChangeError: " + msg);
+ throw new Error("Missing IllegalAccessError: " + msg);
}
- catch (IncompatibleClassChangeError expected) {
+ catch (IllegalAccessError expected) {
check_expected(expected, msg);
}
}
@@ -1745,31 +1749,8 @@ public class TestNestmateMembership {
if (!expected.getMessage().contains(msg)) {
throw new Error("Wrong " + expected.getClass().getSimpleName() +": \"" +
expected.getMessage() + "\" does not contain \"" +
- msg + "\"");
+ msg + "\"", expected);
}
System.out.println("OK - got expected exception: " + expected);
}
-
- static void check_expected(Throwable expected, String msg, String cause_msg) {
- if (!expected.getMessage().contains(msg)) {
- throw new Error("Wrong " + expected.getClass().getSimpleName() +": \"" +
- expected.getMessage() + "\" does not contain \"" +
- msg + "\"");
- }
- Throwable cause = expected.getCause();
- if (cause instanceof NoClassDefFoundError) {
- if (!cause.getMessage().contains(cause_msg)) {
- throw new Error(expected.getClass().getSimpleName() +
- " has wrong cause " + cause.getClass().getSimpleName() +": \"" +
- cause.getMessage() + "\" does not contain \"" +
- cause_msg + "\"");
- }
- }
- else throw new Error(expected.getClass().getSimpleName() +
- " has wrong cause " + cause.getClass().getSimpleName());
-
- System.out.println("OK - got expected exception: " + expected +
- " with cause " + cause);
- }
-
}
diff --git a/test/hotspot/jtreg/runtime/Nestmates/privateMethods/TestInvokeErrors.java b/test/hotspot/jtreg/runtime/Nestmates/privateMethods/TestInvokeErrors.java
index 5a86c542fee..031238c0adf 100644
--- a/test/hotspot/jtreg/runtime/Nestmates/privateMethods/TestInvokeErrors.java
+++ b/test/hotspot/jtreg/runtime/Nestmates/privateMethods/TestInvokeErrors.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2020, 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
@@ -74,8 +74,12 @@ public class TestInvokeErrors {
m.priv_invoke();
throw new Error("Unexpected success invoking MissingNestHost.priv_invoke");
}
- catch (NoClassDefFoundError ncdfe) {
- System.out.println("Got expected exception:" + ncdfe);
+ catch (IllegalAccessError iae) {
+ if (iae.getMessage().contains("java.lang.NoClassDefFoundError: NoSuchClass")) {
+ System.out.println("Got expected exception:" + iae);
+ } else {
+ throw new Error("Unexpected exception", iae);
+ }
}
}
}
@@ -105,11 +109,11 @@ public class TestInvokeErrors {
try {
Helper.doTest();
}
- catch (NoClassDefFoundError ncdfe) {
+ catch (IllegalAccessError iae) {
if (verifying)
- System.out.println("Got expected exception:" + ncdfe);
+ System.out.println("Got expected exception:" + iae);
else
- throw new Error("Unexpected error loading Helper class with verification disabled");
+ throw new Error("Unexpected error loading Helper class with verification disabled", iae);
}
}
}
diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderHierarchyTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderHierarchyTest.java
index 8fdfef53207..89f47630237 100644
--- a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderHierarchyTest.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderHierarchyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, SAP SE. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -27,8 +27,7 @@
* @summary Test of diagnostic command VM.classloaders
* @library /test/lib
* @modules java.base/jdk.internal.misc
- * java.compiler
- * java.management
+ * jdk.compiler
* jdk.internal.jvmstat/sun.jvmstat.monitor
* @run testng ClassLoaderHierarchyTest
*/
@@ -97,6 +96,7 @@ public class ClassLoaderHierarchyTest {
output.shouldMatch("Kevin.*TestClassLoader");
output.shouldMatch("Bill.*TestClassLoader");
output.shouldContain("TestClass2");
+ output.shouldContain("Hidden Classes:");
}
static class TestClassLoader extends ClassLoader {
diff --git a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java
index 60f94069986..61345ada76a 100644
--- a/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java
+++ b/test/hotspot/jtreg/serviceability/dcmd/vm/ClassLoaderStatsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2020, 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
@@ -29,7 +29,7 @@
* java.compiler
* java.management
* jdk.internal.jvmstat/sun.jvmstat.monitor
- * @run testng ClassLoaderStatsTest
+ * @run testng/othervm --add-exports=java.base/jdk.internal.misc=ALL-UNNAMED --add-exports=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED ClassLoaderStatsTest
*/
import org.testng.annotations.Test;
@@ -39,27 +39,34 @@ import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.dcmd.CommandExecutor;
import jdk.test.lib.dcmd.JMXExecutor;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import jdk.internal.misc.Unsafe;
+
public class ClassLoaderStatsTest {
+ // Expected output from VM.classloader_stats:
// ClassLoader Parent CLD* Classes ChunkSz BlockSz Type
- // 0x00000007c0215928 0x0000000000000000 0x0000000000000000 0 0 0 org.eclipse.osgi.baseadaptor.BaseAdaptor$1
- // 0x00000007c0009868 0x0000000000000000 0x00007fc52aebcc80 1 6144 3768 sun.reflect.DelegatingClassLoader
- // 0x00000007c0009868 0x0000000000000000 0x00007fc52b8916d0 1 6144 3688 sun.reflect.DelegatingClassLoader
- // 0x00000007c0009868 0x00000007c0038ba8 0x00007fc52afb8760 1 6144 3688 sun.reflect.DelegatingClassLoader
- // 0x00000007c0009868 0x0000000000000000 0x00007fc52afbb1a0 1 6144 3688 sun.reflect.DelegatingClassLoader
- // 0x0000000000000000 0x0000000000000000 0x00007fc523416070 5019 30060544 29956216
- // 455 1210368 672848 + unsafe anonymous classes
- // 0x00000007c016b5c8 0x00000007c0038ba8 0x00007fc52a995000 5 8192 5864 org.netbeans.StandardModule$OneModuleClassLoader
- // 0x00000007c0009868 0x00000007c016b5c8 0x00007fc52ac13640 1 6144 3896 sun.reflect.DelegatingClassLoader
+ // 0x0000000800bd3830 0x000000080037f468 0x00007f001c2ea170 1 10240 4672 ClassLoaderStatsTest$DummyClassLoader
+ // 1 2048 1080 + unsafe anonymous classes
+ // 1 2048 1088 + hidden classes
+ // 0x0000000000000000 0x0000000000000000 0x00007f00e852d190 1607 4628480 3931216
+ // 38 124928 85856 + hidden classes
+ // 0x00000008003b5508 0x0000000000000000 0x00007f001c2d4760 1 6144 4040 jdk.internal.reflect.DelegatingClassLoader
+ // 0x000000080037f468 0x000000080037ee80 0x00007f00e868e3f0 228 1368064 1286672 jdk.internal.loader.ClassLoaders$AppClassLoader
// ...
static Pattern clLine = Pattern.compile("0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*0x\\p{XDigit}*\\s*(\\d*)\\s*(\\d*)\\s*(\\d*)\\s*(.*)");
@@ -69,7 +76,7 @@ public class ClassLoaderStatsTest {
public void run(CommandExecutor executor) throws ClassNotFoundException {
- // create a classloader and load our special class
+ // create a classloader and load our special classes
dummyloader = new DummyClassLoader();
Class> c = Class.forName("TestClass", true, dummyloader);
if (c.getClassLoader() != dummyloader) {
@@ -82,9 +89,9 @@ public class ClassLoaderStatsTest {
String line = lines.next();
Matcher m = clLine.matcher(line);
if (m.matches()) {
- // verify that DummyClassLoader has loaded 1 class and 1 anonymous class
+ // verify that DummyClassLoader has loaded 1 class, 1 anonymous class, and 1 hidden class
if (m.group(4).equals("ClassLoaderStatsTest$DummyClassLoader")) {
- System.out.println("line: " + line);
+ System.out.println("DummyClassLoader line: " + line);
if (!m.group(1).equals("1")) {
Assert.fail("Should have loaded 1 class: " + line);
}
@@ -92,7 +99,10 @@ public class ClassLoaderStatsTest {
checkPositiveInt(m.group(3));
String next = lines.next();
- System.out.println("next: " + next);
+ System.out.println("DummyClassLoader next: " + next);
+ if (!next.contains("unsafe anonymous classes")) {
+ Assert.fail("Should have an anonymous class");
+ }
Matcher m1 = anonLine.matcher(next);
m1.matches();
if (!m1.group(1).equals("1")) {
@@ -100,6 +110,19 @@ public class ClassLoaderStatsTest {
}
checkPositiveInt(m1.group(2));
checkPositiveInt(m1.group(3));
+
+ next = lines.next();
+ System.out.println("DummyClassLoader next: " + next);
+ if (!next.contains("hidden classes")) {
+ Assert.fail("Should have a hidden class");
+ }
+ Matcher m2 = anonLine.matcher(next);
+ m2.matches();
+ if (!m2.group(1).equals("1")) {
+ Assert.fail("Should have loaded 1 hidden class, but found : " + m2.group(1));
+ }
+ checkPositiveInt(m2.group(2));
+ checkPositiveInt(m2.group(3));
}
}
}
@@ -117,8 +140,7 @@ public class ClassLoaderStatsTest {
static ByteBuffer readClassFile(String name)
{
- File f = new File(System.getProperty("test.classes", "."),
- name);
+ File f = new File(System.getProperty("test.classes", "."), name);
try (FileInputStream fin = new FileInputStream(f);
FileChannel fc = fin.getChannel())
{
@@ -163,10 +185,41 @@ public class ClassLoaderStatsTest {
}
}
+class HiddenClass { }
+
class TestClass {
+ private static final String HCName = "HiddenClass.class";
+ private static final String DIR = System.getProperty("test.classes");
+ static Unsafe unsafe = Unsafe.getUnsafe();
+
static {
- // force creation of anonymous class (for the lambdaform)
- Runnable r = () -> System.out.println("Hello");
- r.run();
+ try {
+ // Create a hidden non-strong class and an anonymous class.
+ byte[] klassBuf = readClassFile(DIR + File.separator + HCName);
+ Class> hc = defineHiddenClass(klassBuf);
+ Class ac = unsafe.defineAnonymousClass(TestClass.class, klassBuf, new Object[0]);
+ } catch (Throwable e) {
+ throw new RuntimeException("Unexpected exception in TestClass: " + e.getMessage());
+ }
+ }
+
+
+ static byte[] readClassFile(String classFileName) throws Exception {
+ File classFile = new File(classFileName);
+ try (FileInputStream in = new FileInputStream(classFile);
+ ByteArrayOutputStream out = new ByteArrayOutputStream())
+ {
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ return out.toByteArray();
+ }
+ }
+
+ static Class> defineHiddenClass(byte[] bytes) throws Exception {
+ Lookup lookup = MethodHandles.lookup();
+ Class> hc = lookup.defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+ return hc;
}
}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/P/Q/HiddenClassSigTest.java b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/P/Q/HiddenClassSigTest.java
new file mode 100644
index 00000000000..f04a841efcc
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/P/Q/HiddenClassSigTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2019, 2020, 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.misc
+ * jdk.compiler
+ * @compile HiddenClassSigTest.java
+ * @run main/othervm/native -agentlib:HiddenClassSigTest P.Q.HiddenClassSigTest
+ */
+
+package P.Q;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import jdk.test.lib.Utils;
+import jdk.test.lib.compiler.InMemoryJavaCompiler;
+
+
+interface Test {
+ String test(T t);
+}
+
+class HiddenClassSig implements Test {
+ private String realTest() { return "HiddenClassSig: "; }
+
+ public String test(T t) {
+ String str = realTest();
+ return str + t.toString();
+ }
+}
+
+public class HiddenClassSigTest {
+ private static void log(String str) { System.out.println(str); }
+
+ private static final String HCName = "P/Q/HiddenClassSig.class";
+ private static final String DIR = Utils.TEST_CLASSES;
+ private static final String LOG_PREFIX = "HiddenClassSigTest: ";
+
+ static native void checkHiddenClass(Class klass, String sig);
+ static native void checkHiddenClassArray(Class array, String sig);
+ static native boolean checkFailed();
+
+ static {
+ try {
+ System.loadLibrary("HiddenClassSigTest");
+ } catch (UnsatisfiedLinkError ule) {
+ System.err.println("Could not load HiddenClassSigTest library");
+ System.err.println("java.library.path: "
+ + System.getProperty("java.library.path"));
+ throw ule;
+ }
+ }
+
+ static byte[] readClassFile(String classFileName) throws Exception {
+ File classFile = new File(classFileName);
+ try (FileInputStream in = new FileInputStream(classFile);
+ ByteArrayOutputStream out = new ByteArrayOutputStream())
+ {
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ return out.toByteArray();
+ }
+ }
+
+ static Class> defineHiddenClass(String classFileName) throws Exception {
+ Lookup lookup = MethodHandles.lookup();
+ byte[] bytes = readClassFile(DIR + File.separator + classFileName);
+ Class> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
+ return hc;
+ }
+
+ static void logClassInfo(Class> klass) {
+ log("\n### Testing class: " + klass);
+ log(LOG_PREFIX + "isHidden: " + klass.isHidden());
+ log(LOG_PREFIX + "getName: " + klass.getName());
+ log(LOG_PREFIX + "typeName: " + klass.getTypeName());
+ log(LOG_PREFIX + "toString: " + klass.toString());
+ log(LOG_PREFIX + "toGenStr: " + klass.toGenericString());
+ log(LOG_PREFIX + "elem type: " + klass.componentType());
+ }
+
+ private static final String HC_NAME = "P.Q.HiddenClassSig";
+ private static final String HC_SUFFIX_REGEX = "0x[0-9a-f]+";
+ static boolean checkName(Class> klass, String name, String toString) {
+ boolean failed = false;
+ String regex = "";
+ Class> c = klass;
+ while (c.isArray()) {
+ regex = "\\[" + regex;
+ c = c.componentType();
+ }
+ if (klass.isArray()) {
+ regex += "L" + HC_NAME + "/" + HC_SUFFIX_REGEX + ";";
+ } else {
+ regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
+ }
+ if (!name.matches(regex)) {
+ log("Test FAIL: result of Class::getName" + " \"" + name + "\" does not match " + regex);
+ failed = true;
+ }
+ if (!toString.matches("class " + regex)) {
+ log("Test FAIL: result of Class::toString" + " \"" + name + "\" does not match " + regex);
+ failed = true;
+ }
+ return failed;
+ }
+
+ static boolean checkTypeName(Class> klass, String name) {
+ boolean failed = false;
+ String regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
+ Class> c = klass;
+ while (c.isArray()) {
+ c = c.componentType();
+ regex = regex + "\\[\\]";
+ }
+ if (!name.matches(regex)) {
+ log("Test FAIL: result of Class::getTypeName" + " \"" + name + "\" does not match " + regex);
+ failed = true;
+ }
+ return failed;
+ }
+
+ static boolean checkGenericString(Class> klass, String name) {
+ boolean failed = false;
+ Class> c = klass;
+ String regex = HC_NAME + "/" + HC_SUFFIX_REGEX + "";
+ if (!klass.isArray()) {
+ regex = "class " + regex;
+ }
+ while (c.isArray()) {
+ c = c.componentType();
+ regex = regex + "\\[\\]";
+ }
+ if (!name.matches(regex)) {
+ log("Test FAIL: result of Class::toGenericString" + " \"" + name + "\" does not match " + regex);
+ failed = true;
+ }
+ return failed;
+ }
+
+ static boolean checkDescriptorString(Class> klass, String name) {
+ boolean failed = false;
+ String regex = "L" + HC_NAME.replace('.', '/') + "." + HC_SUFFIX_REGEX + ";";
+ Class> c = klass;
+ while (c.isArray()) {
+ regex = "\\[" + regex;
+ c = c.componentType();
+ }
+ if (!name.matches(regex)) {
+ log("Test FAIL: result of Class::descriptorString" + " \"" + name + "\" does not match " + regex);
+ failed = true;
+ }
+ return failed;
+ }
+
+ static boolean testClass(Class> klass) {
+ boolean failed = false;
+ logClassInfo(klass);
+
+ failed |= checkName(klass, klass.getName(), klass.toString());
+ failed |= checkTypeName(klass, klass.getTypeName());
+ failed |= checkGenericString(klass, klass.toGenericString());
+ failed |= checkDescriptorString(klass, klass.descriptorString());
+
+ if (klass.isArray() && klass.isHidden()) {
+ log("Test FAIL: an array class is never hidden");
+ failed = true;
+ }
+ if (klass.isArray()) {
+ checkHiddenClassArray(klass, klass.descriptorString());
+ } else {
+ checkHiddenClass(klass, klass.descriptorString());
+ }
+ return failed;
+ }
+
+ public static void main(String args[]) throws Exception {
+ log(LOG_PREFIX + "started");
+ Class> hc = defineHiddenClass(HCName);
+ String baseName = ("" + hc).substring("class ".length());
+
+ Test t = (Test)hc.newInstance();
+ String str = t.test("Test generic hidden class");
+ log(LOG_PREFIX + "hc.test() returned string: " + str);
+
+ boolean failed = testClass(hc);
+
+ Class> hcArr = hc.arrayType();
+ failed |= testClass(hcArr);
+
+ Class> hcArrArr = hcArr.arrayType();
+ failed |= testClass(hcArrArr);
+
+ if (failed) {
+ throw new RuntimeException("FAIL: failed status from java part");
+ }
+ if (checkFailed()) {
+ throw new RuntimeException("FAIL: failed status from native agent");
+ }
+ log(LOG_PREFIX + "finished");
+ }
+}
diff --git a/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp
new file mode 100644
index 00000000000..002d7d4761c
--- /dev/null
+++ b/test/hotspot/jtreg/serviceability/jvmti/HiddenClass/libHiddenClassSigTest.cpp
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2019, 2020, 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.
+ */
+
+#include
+#include "jvmti.h"
+
+extern "C" {
+
+static const char* EXP_INTERF_SIG = "LP/Q/Test;";
+static const char* SIG_START = "LP/Q/HiddenClassSig";
+static const size_t SIG_START_LEN = strlen(SIG_START);
+static const int ACC_INTERFACE = 0x0200; // Interface class modifiers bit
+
+static jvmtiEnv *jvmti = NULL;
+static jint class_load_count = 0;
+static bool failed = false;
+
+#define LOG0(str) { printf(str); fflush(stdout); }
+#define LOG1(str, arg) { printf(str, arg); fflush(stdout); }
+#define LOG2(str, arg1, arg2) { printf(str, arg1, arg2); fflush(stdout); }
+
+#define CHECK_JVMTI_ERROR(jni, err, msg) \
+ if (err != JVMTI_ERROR_NONE) { \
+ LOG1("CHECK_JVMTI_ERROR: JVMTI function returned error: %d\n", err); \
+ jni->FatalError(msg); \
+ return; \
+ }
+
+/* Return the jmethodID of j.l.Class.isHidden() method. */
+static jmethodID
+is_hidden_mid(JNIEnv* jni) {
+ char* csig = NULL;
+ jint count = 0;
+ jmethodID *methods = NULL;
+ jclass clazz = jni->FindClass("java/lang/Class");
+ if (clazz == NULL) {
+ jni->FatalError("is_hidden_mid: Error: FindClass returned NULL for java/lang/Class\n");
+ return NULL;
+ }
+
+ // find the jmethodID of j.l.Class.isHidden() method
+ jmethodID mid = jni->GetMethodID(clazz, "isHidden", "()Z");
+ if (mid == NULL) {
+ jni->FatalError("is_hidden_mid: Error in jni GetMethodID: Cannot find j.l.Class.isHidden method\n");
+ }
+ return mid;
+}
+
+/* Return true if the klass is hidden. */
+static bool
+is_hidden(JNIEnv* jni, jclass klass) {
+ static jmethodID is_hid_mid = NULL;
+
+ if (is_hid_mid == NULL) {
+ is_hid_mid = is_hidden_mid(jni);
+ }
+ // invoke j.l.Class.isHidden() method
+ return jni->CallBooleanMethod(klass, is_hid_mid);
+}
+
+/* Check the class signature matches the expected. */
+static void
+check_class_signature(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, bool is_hidden, const char* exp_sig) {
+ jint class_modifiers = 0;
+ char* sig = NULL;
+ char* gsig = NULL;
+ jvmtiError err;
+
+ err = jvmti->GetClassSignature(klass, &sig, &gsig);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class: Error in JVMTI GetClassSignature");
+
+ LOG1("check_class_signature: class with sig: %s\n", sig);
+ LOG1("check_class_signature: class with gsig: %s\n", gsig);
+
+ if (strcmp(sig, exp_sig) != 0) {
+ LOG2("check_class_signature: FAIL: Hidden class signature %s does not match expected: %s\n", sig, exp_sig);
+ failed = true;
+ }
+ if (is_hidden && gsig == NULL) {
+ LOG0("check_class_signature: FAIL: unexpected NULL generic signature for hidden class\n");
+ failed = true;
+ }
+}
+
+/* Test hidden class flags: it should not be interface, array nor modifiable. */
+static void
+check_hidden_class_flags(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
+ jint modifiers = 0;
+ jboolean flag = false;
+ jvmtiError err;
+
+ err = jvmti->GetClassModifiers(klass, &modifiers);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI GetClassModifiers");
+ LOG1("check_hidden_class_flags: hidden class modifiers: 0x%x\n", modifiers);
+ if ((modifiers & ACC_INTERFACE) != 0) {
+ LOG0("check_hidden_class_flags: FAIL: unexpected ACC_INTERFACE bit in hidden class modifiers\n");
+ failed = true;
+ return;
+ }
+
+ err = jvmti->IsInterface(klass, &flag);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsInterface");
+ if (flag) {
+ LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be interface\n");
+ failed = true;
+ return;
+ }
+
+ err = jvmti->IsArrayClass(klass, &flag);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsArrayClass");
+ if (flag) {
+ LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be array\n");
+ failed = true;
+ return;
+ }
+ err = jvmti->IsModifiableClass(klass, &flag);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsModifiableClass");
+ if (flag) {
+ LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be modifiable\n");
+ failed = true;
+ }
+}
+
+/* Test GetClassLoaderClasses: it should not return any hidden classes. */
+static void
+check_hidden_class_loader(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
+ jint count = 0;
+ jobject loader = NULL;
+ jclass* loader_classes = NULL;
+ jboolean found = false;
+ jvmtiError err;
+
+ err = jvmti->GetClassLoader(klass, &loader);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoader");
+
+ err = jvmti->GetClassLoaderClasses(loader, &count, &loader_classes);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoaderClasses");
+
+ for (int idx = 0; idx < count; idx++) {
+ char* sig = NULL;
+ jclass kls = loader_classes[idx];
+
+ if (!is_hidden(jni, kls)) {
+ continue;
+ }
+ err = jvmti->GetClassSignature(kls, &sig, NULL);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassSignature");
+
+ LOG1("check_hidden_class_loader: FAIL: JVMTI GetClassLoaderClasses returned hidden class: %s\n", sig);
+ failed = true;
+ return;
+ }
+ LOG0("check_hidden_class_loader: not found hidden class in its loader classes as expected\n");
+}
+
+/* Test the hidden class implements expected interface. */
+static void
+check_hidden_class_impl_interf(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
+ char* sig = NULL;
+ jint count = 0;
+ jclass* interfaces = NULL;
+ jvmtiError err;
+
+ // check that hidden class implements just one interface
+ err = jvmti->GetImplementedInterfaces(klass, &count, &interfaces);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetImplementedInterfaces");
+ if (count != 1) {
+ LOG1("check_hidden_class_impl_interf: FAIL: implemented interfaces count: %d, expected to be 1\n", count);
+ failed = true;
+ return;
+ }
+
+ // check the interface signature is matching the expected
+ err = jvmti->GetClassSignature(interfaces[0], &sig, NULL);
+ CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface");
+
+ if (strcmp(sig, EXP_INTERF_SIG) != 0) {
+ LOG2("check_hidden_class_impl_interf: FAIL: implemented interface signature: %s, expected to be: %s\n",
+ sig, EXP_INTERF_SIG);
+ failed = true;
+ }
+}
+
+/* Test hidden class. */
+static void
+check_hidden_class(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, const char* exp_sig) {
+ char* source_file_name = NULL;
+
+ LOG1("\n### Native agent: check_hidden_class started: class: %s\n", exp_sig);
+
+ check_class_signature(jvmti, jni, klass, true /* not hidden */, exp_sig);
+ if (failed) return;
+
+ check_hidden_class_flags(jvmti, jni, klass);
+ if (failed) return;
+
+ check_hidden_class_loader(jvmti, jni, klass);
+ if (failed) return;
+
+ check_hidden_class_impl_interf(jvmti, jni, klass);
+ if (failed) return;
+
+ LOG0("### Native agent: check_hidden_class finished\n");
+}
+
+/* Test hidden class array. */
+static void
+check_hidden_class_array(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass_array, const char* exp_sig) {
+ char* source_file_name = NULL;
+
+ LOG1("\n### Native agent: check_hidden_class_array started: array: %s\n", exp_sig);
+
+ check_class_signature(jvmti, jni, klass_array, false /* is hidden */, exp_sig);
+ if (failed) return;
+
+ LOG0("### Native agent: check_hidden_class_array finished\n");
+}
+
+/* Enable CLASS_LOAD event notification mode. */
+static void JNICALL
+VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
+ jvmtiError err;
+
+ printf("VMInit event: SIG_START: %s, SIG_START_LEN: %d\n", SIG_START, (int)SIG_START_LEN);
+ fflush(stdout);
+
+ err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
+ CHECK_JVMTI_ERROR(jni, err, "VMInit event: Error in enabling ClassLoad events notification");
+}
+
+/* Check CLASS_LOAD event is generated for the given hidden class. */
+static void JNICALL
+ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
+ char* sig = NULL;
+ char* gsig = NULL;
+ char* src_name = NULL;
+ jvmtiError err;
+
+ err = jvmti->GetClassSignature(klass, &sig, &gsig);
+ CHECK_JVMTI_ERROR(jni, err, "ClassLoad event: Error in JVMTI GetClassSignature");
+
+ if (strlen(sig) > strlen(SIG_START) &&
+ strncmp(sig, SIG_START, SIG_START_LEN) == 0 &&
+ is_hidden(jni, klass)) {
+ class_load_count++;
+ if (gsig == NULL) {
+ LOG0("ClassLoad event: FAIL: GetClassSignature returned NULL generic signature for hidden class\n");
+ failed = true;
+ }
+ LOG1("ClassLoad event: hidden class with sig: %s\n", sig);
+ LOG1("ClassLoad event: hidden class with gsig: %s\n", gsig);
+ }
+}
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
+ jvmtiEventCallbacks callbacks;
+ jvmtiCapabilities caps;
+ jvmtiError err;
+
+ LOG0("Agent_OnLoad: started\n");
+ if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
+ LOG0("Agent_OnLoad: Error in GetEnv in obtaining jvmtiEnv*\n");
+ failed = true;
+ return JNI_ERR;
+ }
+
+ // set required event callbacks
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.ClassLoad = &ClassLoad;
+ callbacks.VMInit = &VMInit;
+
+ err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
+ if (err != JVMTI_ERROR_NONE) {
+ LOG1("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
+ failed = true;
+ return JNI_ERR;
+ }
+
+ // add required capabilities
+ memset(&caps, 0, sizeof(caps));
+ caps.can_get_source_file_name = 1;
+ err = jvmti->AddCapabilities(&caps);
+ if (err != JVMTI_ERROR_NONE) {
+ LOG1("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
+ failed = true;
+ return JNI_ERR;
+ }
+
+ // enable VM_INIT event notification mode
+ err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
+ if (err != JVMTI_ERROR_NONE) {
+ LOG1("Agent_OnLoad: Error in JVMTI SetEventNotificationMode: %d\n", err);
+ failed = true;
+ return JNI_ERR;
+ }
+
+ LOG0("Agent_OnLoad: finished\n");
+ return JNI_OK;
+}
+
+/* Native method: checkHiddenClass(). */
+JNIEXPORT void JNICALL
+Java_P_Q_HiddenClassSigTest_checkHiddenClass(JNIEnv *jni, jclass klass, jclass hidden_klass, jstring exp_sig_str) {
+ const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL);
+
+ if (exp_sig == NULL) {
+ jni->FatalError("check_hidden_class: Error: JNI GetStringChars returned NULL for jstring\n");
+ return;
+ }
+ check_hidden_class(jvmti, jni, hidden_klass, exp_sig);
+
+ jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
+}
+
+/* Native method: checkHiddenClassArray(). */
+JNIEXPORT void JNICALL
+Java_P_Q_HiddenClassSigTest_checkHiddenClassArray(JNIEnv *jni, jclass klass, jclass hidden_klass_array, jstring exp_sig_str) {
+ const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL);
+
+ if (exp_sig == NULL) {
+ jni->FatalError("check_hidden_class_array: Error: JNI GetStringChars returned NULL for jstring\n");
+ return;
+ }
+ check_hidden_class_array(jvmti, jni, hidden_klass_array, exp_sig);
+
+ jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
+}
+
+/* Native method: checkFailed(). */
+JNIEXPORT jboolean JNICALL
+Java_P_Q_HiddenClassSigTest_checkFailed(JNIEnv *jni, jclass klass) {
+ if (class_load_count == 0) {
+ LOG0("Native Agent: missed ClassLoad event for hidden class\n");
+ failed = true;
+ }
+ return failed;
+}
+
+} // extern "C"
diff --git a/test/jdk/java/lang/instrument/HiddenClass/HiddenClassAgent.java b/test/jdk/java/lang/instrument/HiddenClass/HiddenClassAgent.java
new file mode 100644
index 00000000000..17eb8033230
--- /dev/null
+++ b/test/jdk/java/lang/instrument/HiddenClass/HiddenClassAgent.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2020, 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.instrument
+ * jdk.compiler
+ * @build jdk.test.lib.compiler.CompilerUtils
+ * jdk.test.lib.Utils *
+ * @run shell ../MakeJAR3.sh HiddenClassAgent 'Can-Retransform-Classes: true'
+ * @run main/othervm/native -javaagent:HiddenClassAgent.jar HiddenClassApp
+ */
+
+import java.lang.instrument.*;
+
+/* Test that Instrumentation getAllLoadedClasses includes
+ * hidden classes but getInitiatedClasses does not.
+ * Check that all hidden classes are non-retransformable.
+ */
+public class HiddenClassAgent extends Thread {
+ private static volatile boolean completed = false;
+ private static volatile boolean failed = false;
+ private static volatile boolean hiddenClassLoaded = false;
+
+ private static Instrumentation instr = null;
+ private static Object monitor = new Object();
+
+ static void log(String str) { System.err.println(str); }
+ public static boolean failed() { return failed; }
+
+ public static
+ boolean checkWaitForCompleteness() {
+ try {
+ synchronized (monitor) {
+ while (!completed) {
+ monitor.wait(100);
+ }
+ }
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ log("HiddenClassAgent: waitCheckForCompletness: Caught InterruptedException: " + ex);
+ }
+ return completed;
+ }
+
+ public static
+ void setHiddenClassLoaded() {
+ synchronized (monitor) {
+ hiddenClassLoaded = true;
+ monitor.notifyAll();
+ }
+ }
+
+ private static
+ void waitForHiddenClassLoad() {
+ try {
+ synchronized (monitor) {
+ while (!hiddenClassLoaded) {
+ monitor.wait(100);
+ }
+ }
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ log("HiddenClassAgent: waitForHiddenClassLoad: Caught InterruptedException: " + ex);
+ }
+ }
+
+ private static
+ ClassLoader testGetAllLoadedClasses() {
+ boolean hiddenClassFound = false;
+ ClassLoader loader = null;
+ Class>[] classes = instr.getAllLoadedClasses();
+
+ for (int i = 0; i < classes.length; i++) {
+ Class klass = classes[i];
+
+ if (!klass.isHidden() || !klass.getName().contains("HiddenClass/")) {
+ continue;
+ }
+ log("HiddenClassAgent: getAllLoadedClasses returned hidden class: " + klass);
+ hiddenClassFound = true;
+ loader = klass.getClassLoader();
+ log("HiddenClassAgent: class loader of hidden class: " + loader);
+
+ try {
+ instr.retransformClasses(klass);
+ log("HiddenClassAgent: FAIL: hidden class is retransformable: " + klass);
+ failed = true;
+ } catch (UnmodifiableClassException e) {
+ log("HiddenClassAgent: Got expected UnmodifiableClassException for class: " + klass);
+ } catch (Throwable e) {
+ log("HiddenClassAgent: FAIL: unexpected throwable in hidden class retransform: " + klass);
+ log("HiddenClassAgent: got Throwable" + e);
+ failed = true;
+ }
+ }
+ if (!hiddenClassFound) {
+ log("HiddenClassAgent: FAIL: a hidden class is not found in getAllLoadedClasses list");
+ failed = true;
+ }
+ return loader;
+ }
+
+ private static
+ void testGetInitiatedClasses(ClassLoader loader) {
+ Class>[] classes = instr.getInitiatedClasses(loader);
+ for (int i = 0; i < classes.length; i++) {
+ Class klass = classes[i];
+
+ if (klass.isHidden()) {
+ log("HiddenClassAgent: FAIL: getInitiatedClasses returned hidden class: " + klass);
+ failed = true;
+ return;
+ }
+ }
+ log("HiddenClassAgent: getInitiatedClasses returned no hidden classes as expected");
+ }
+
+ public static void
+ premain(String agentArgs, Instrumentation instrumentation) {
+ instr = instrumentation;
+ Thread agentThread = new HiddenClassAgent();
+ agentThread.start();
+ }
+
+ public void run () {
+ log("HiddenClassAgent: started");
+ waitForHiddenClassLoad();
+
+ // Test getAllLoadedClasses
+ ClassLoader loader = testGetAllLoadedClasses();
+
+ // Test getInitiatedClasses
+ testGetInitiatedClasses(null);
+ if (loader != null) {
+ testGetInitiatedClasses(loader);
+ }
+
+ synchronized (monitor) {
+ completed = true;
+ monitor.notifyAll();
+ }
+ log("HiddenClassAgent: finished");
+ }
+}
diff --git a/test/jdk/java/lang/instrument/HiddenClass/HiddenClassApp.java b/test/jdk/java/lang/instrument/HiddenClass/HiddenClassApp.java
new file mode 100644
index 00000000000..600c9c7ff87
--- /dev/null
+++ b/test/jdk/java/lang/instrument/HiddenClass/HiddenClassApp.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import jdk.test.lib.compiler.CompilerUtils;
+import jdk.test.lib.Utils;
+
+
+interface Test {
+ void test();
+}
+
+public class HiddenClassApp {
+ static void log(String str) { System.out.println(str); }
+
+ static final String HCName = "HiddenClass.java";
+ static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "hidden");
+ static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES, "hidden");
+
+ static void compileSources(String sourceFile) throws Throwable {
+ boolean ok = CompilerUtils.compile(SRC_DIR.resolve(sourceFile), CLASSES_DIR,
+ "-cp", Utils.TEST_CLASSES.toString());
+ if (!ok){
+ throw new RuntimeException("HiddenClassApp: Compilation of the test failed. ");
+ }
+ }
+
+ static byte[] readClassFile(String classFileName) throws Exception {
+ File classFile = new File(CLASSES_DIR + File.separator + classFileName);
+ try (FileInputStream in = new FileInputStream(classFile);
+ ByteArrayOutputStream out = new ByteArrayOutputStream())
+ {
+ int b;
+ while ((b = in.read()) != -1) {
+ out.write(b);
+ }
+ return out.toByteArray();
+ }
+ }
+
+ static Class> defineHiddenClass(String name) throws Exception {
+ Lookup lookup = MethodHandles.lookup();
+ byte[] bytes = readClassFile(name + ".class");
+ Class> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
+ return hc;
+ }
+
+ public static void main(String args[]) throws Exception {
+ log("HiddenClassApp: started");
+ try {
+ compileSources(HCName);
+ log("HiddenClassApp: compiled " + HCName);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ throw new Exception("HiddenClassApp: Failed to compile " + HCName);
+ }
+
+ Class> c = defineHiddenClass("HiddenClass");
+ log("HiddenClassApp: Defined HiddenClass with name: " + c.getName());
+ HiddenClassAgent.setHiddenClassLoaded();
+
+ Test t = (Test) c.newInstance();
+ t.test();
+ log("HiddenClassApp: Tested HiddenClass");
+
+ if (!HiddenClassAgent.checkWaitForCompleteness()) {
+ throw new Exception("HiddenClassApp: FAIL: HiddenClassAgent did not complete");
+ }
+ if (HiddenClassAgent.failed()) {
+ throw new Exception("HiddenClassApp: FAIL: HiddenClassAgent failed");
+ }
+ log("HiddenClassApp: finished");
+ }
+}
diff --git a/test/jdk/java/lang/instrument/HiddenClass/hidden/HiddenClass.java b/test/jdk/java/lang/instrument/HiddenClass/hidden/HiddenClass.java
new file mode 100644
index 00000000000..7c253d0fcb5
--- /dev/null
+++ b/test/jdk/java/lang/instrument/HiddenClass/hidden/HiddenClass.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class HiddenClass implements Test {
+ private String realTest() {
+ return this.getClass().getName();
+ }
+
+ public void test() {
+ String name = realTest();
+ if (name.indexOf("HiddenClass") == -1) {
+ throw new RuntimeException("'HiddenClass string is bad: " + name);
+ }
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
new file mode 100644
index 00000000000..75732a8ef21
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/BasicTest.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2019, 2020, 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
+ * @modules java.base/jdk.internal.org.objectweb.asm
+ * jdk.compiler
+ * @library /test/lib
+ * @build jdk.test.lib.Utils
+ * jdk.test.lib.compiler.CompilerUtils
+ * @run testng/othervm --enable-preview BasicTest
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.Type;
+import jdk.test.lib.compiler.CompilerUtils;
+import jdk.test.lib.Utils;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static org.testng.Assert.*;
+
+interface HiddenTest {
+ void test();
+}
+
+public class BasicTest {
+
+ private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
+ private static final Path CLASSES_DIR = Paths.get("classes");
+ private static final Path CLASSES_10_DIR = Paths.get("classes_10");
+
+ private static byte[] hiddenClassBytes;
+
+ @BeforeTest
+ static void setup() throws IOException {
+ compileSources(SRC_DIR, CLASSES_DIR,
+ "--enable-preview", "-source", String.valueOf(Runtime.version().feature()));
+ hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenClass.class"));
+
+ // 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 {
+ Stream ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
+ 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);
+ }
+ }
+
+ 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);
+ singletonNest(hc);
+ return hc;
+ }
+
+ // basic test on a hidden class
+ @Test
+ public void hiddenClass() throws Throwable {
+ HiddenTest t = (HiddenTest)defineHiddenClass("HiddenClass").newInstance();
+ t.test();
+
+ // sanity check
+ Class> c = t.getClass();
+ Class>[] intfs = c.getInterfaces();
+ assertTrue(c.isHidden());
+ assertFalse(c.isPrimitive());
+ assertTrue(intfs.length == 1);
+ assertTrue(intfs[0] == HiddenTest.class);
+ assertTrue(c.getCanonicalName() == null);
+
+ String hcName = "HiddenClass";
+ String hcSuffix = "0x[0-9a-f]+";
+ assertTrue(c.getName().matches(hcName + "/" + hcSuffix));
+ assertTrue(c.descriptorString().matches("L" + hcName + "." + hcSuffix + ";"), c.descriptorString());
+
+ // test array of hidden class
+ testHiddenArray(c);
+
+ // test setAccessible
+ checkSetAccessible(c, "realTest");
+ checkSetAccessible(c, "test");
+ }
+
+ // primitive class is not a hidden class
+ @Test
+ public void primitiveClass() {
+ assertFalse(int.class.isHidden());
+ assertFalse(String.class.isHidden());
+ }
+
+ private void testHiddenArray(Class> type) throws Exception {
+ // array of hidden class
+ Object array = Array.newInstance(type, 2);
+ Class> arrayType = array.getClass();
+ assertTrue(arrayType.isArray());
+ assertTrue(Array.getLength(array) == 2);
+ assertFalse(arrayType.isHidden());
+
+ String hcName = "HiddenClass";
+ String hcSuffix = "0x[0-9a-f]+";
+ assertTrue(arrayType.getName().matches("\\[" + "L" + hcName + "/" + hcSuffix + ";"));
+ assertTrue(arrayType.descriptorString().matches("\\[" + "L" + hcName + "." + hcSuffix + ";"));
+
+ assertTrue(arrayType.getComponentType().isHidden());
+ assertTrue(arrayType.getComponentType() == type);
+ Object t = type.newInstance();
+ Array.set(array, 0, t);
+ Object o = Array.get(array, 0);
+ assertTrue(o == t);
+ }
+
+ private void checkSetAccessible(Class> c, String name, Class>... ptypes) throws Exception {
+ Method m = c.getDeclaredMethod(name, ptypes);
+ assertTrue(m.trySetAccessible());
+ m.setAccessible(true);
+ }
+
+ // Define a hidden class that uses lambda
+ // This verifies LambdaMetaFactory supports the caller which is a hidden class
+ @Test
+ public void testLambda() throws Throwable {
+ HiddenTest t = (HiddenTest)defineHiddenClass("Lambda").newInstance();
+ try {
+ t.test();
+ } catch (Error e) {
+ if (!e.getMessage().equals("thrown by " + t.getClass().getName())) {
+ throw e;
+ }
+ }
+ }
+
+ // Verify the nest host and nest members of a hidden class and hidden nestmate class
+ @Test
+ public void testHiddenNestHost() throws Throwable {
+ byte[] hc1 = hiddenClassBytes;
+ Lookup lookup1 = lookup().defineHiddenClass(hc1, false);
+ Class> host = lookup1.lookupClass();
+
+ byte[] hc2 = Files.readAllBytes(CLASSES_DIR.resolve("Lambda.class"));
+ Lookup lookup2 = lookup1.defineHiddenClass(hc2, false, NESTMATE);
+ Class> member = lookup2.lookupClass();
+
+ // test nest membership and reflection API
+ assertTrue(host.isNestmateOf(member));
+ assertTrue(host.getNestHost() == host);
+ // getNestHost and getNestMembers return the same value when calling
+ // on a nest member and the nest host
+ assertTrue(member.getNestHost() == host.getNestHost());
+ assertTrue(Arrays.equals(member.getNestMembers(), host.getNestMembers()));
+ // getNestMembers includes the nest host that can be a hidden class but
+ // only includes static nest members
+ assertTrue(host.getNestMembers().length == 1);
+ assertTrue(host.getNestMembers()[0] == host);
+ }
+
+ @DataProvider(name = "hiddenClasses")
+ private Object[][] hiddenClasses() {
+ return new Object[][] {
+ new Object[] { "HiddenInterface", false },
+ new Object[] { "AbstractClass", false },
+ // a hidden annotation is useless because it cannot be referenced by any class
+ new Object[] { "HiddenAnnotation", false },
+ // class file with bad NestHost, NestMembers and InnerClasses or EnclosingMethod attribute
+ // define them as nestmate to verify Class::getNestHost and getNestMembers
+ new Object[] { "Outer", true },
+ new Object[] { "Outer$Inner", true },
+ new Object[] { "EnclosingClass", true },
+ new Object[] { "EnclosingClass$1", true },
+ };
+ }
+
+ /*
+ * Test that class file bytes that can be defined as a normal class
+ * can be successfully created as a hidden class even it might not
+ * make sense as a hidden class. For example, a hidden annotation
+ * is not useful as it cannot be referenced and an outer/inner class
+ * when defined as a hidden effectively becomes a final top-level class.
+ */
+ @Test(dataProvider = "hiddenClasses")
+ public void defineHiddenClass(String name, boolean nestmate) throws Exception {
+ byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
+ Class> hc;
+ Class> host;
+ if (nestmate) {
+ hc = lookup().defineHiddenClass(bytes, false, NESTMATE).lookupClass();
+ host = lookup().lookupClass().getNestHost();
+ } else {
+ hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ host = hc;
+ }
+ assertTrue(hc.getNestHost() == host);
+ assertTrue(hc.getNestMembers().length == 1);
+ assertTrue(hc.getNestMembers()[0] == host);
+ }
+
+ @DataProvider(name = "emptyClasses")
+ private Object[][] emptyClasses() {
+ return new Object[][] {
+ new Object[] { "EmptyHiddenSynthetic", ACC_SYNTHETIC },
+ new Object[] { "EmptyHiddenEnum", ACC_ENUM },
+ new Object[] { "EmptyHiddenAbstractClass", ACC_ABSTRACT },
+ new Object[] { "EmptyHiddenInterface", ACC_ABSTRACT|ACC_INTERFACE },
+ new Object[] { "EmptyHiddenAnnotation", ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE },
+ };
+ }
+
+ /*
+ * Test if an empty class with valid access flags can be created as a hidden class
+ * as long as it does not violate the restriction of a hidden class.
+ *
+ * A meaningful enum type defines constants of that enum type. So
+ * enum class containing constants of its type should not be a hidden
+ * class.
+ */
+ @Test(dataProvider = "emptyClasses")
+ public void emptyHiddenClass(String name, int accessFlags) throws Exception {
+ byte[] bytes = (accessFlags == ACC_ENUM) ? classBytes(name, Enum.class, accessFlags)
+ : classBytes(name, accessFlags);
+ Class> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ switch (accessFlags) {
+ case ACC_SYNTHETIC:
+ assertTrue(hc.isSynthetic());
+ assertFalse(hc.isEnum());
+ assertFalse(hc.isAnnotation());
+ assertFalse(hc.isInterface());
+ break;
+ case ACC_ENUM:
+ assertFalse(hc.isSynthetic());
+ assertTrue(hc.isEnum());
+ assertFalse(hc.isAnnotation());
+ assertFalse(hc.isInterface());
+ break;
+ case ACC_ABSTRACT:
+ assertFalse(hc.isSynthetic());
+ assertFalse(hc.isEnum());
+ assertFalse(hc.isAnnotation());
+ assertFalse(hc.isInterface());
+ break;
+ case ACC_ABSTRACT|ACC_INTERFACE:
+ assertFalse(hc.isSynthetic());
+ assertFalse(hc.isEnum());
+ assertFalse(hc.isAnnotation());
+ assertTrue(hc.isInterface());
+ break;
+ case ACC_ANNOTATION|ACC_ABSTRACT|ACC_INTERFACE:
+ assertFalse(hc.isSynthetic());
+ assertFalse(hc.isEnum());
+ assertTrue(hc.isAnnotation());
+ assertTrue(hc.isInterface());
+ break;
+ default:
+ throw new IllegalArgumentException("unexpected access flag: " + accessFlags);
+ }
+ assertTrue(hc.isHidden());
+ assertTrue(hc.getModifiers() == (ACC_PUBLIC|accessFlags));
+ assertFalse(hc.isLocalClass());
+ assertFalse(hc.isMemberClass());
+ assertFalse(hc.isAnonymousClass());
+ assertFalse(hc.isArray());
+ }
+
+ // These class files can't be defined as hidden classes
+ @DataProvider(name = "cantBeHiddenClasses")
+ private Object[][] cantBeHiddenClasses() {
+ return new Object[][] {
+ // a hidden class can't be a field's declaring type
+ // enum class with static final HiddenEnum[] $VALUES:
+ new Object[] { "HiddenEnum" },
+ // supertype of this class is a hidden class
+ new Object[] { "HiddenSuper" },
+ // a record class whose equals(HiddenRecord, Object) method
+ // refers to a hidden class in the parameter type and fails
+ // verification. Perhaps this method signature should be reconsidered.
+ new Object[] { "HiddenRecord" },
+ };
+ }
+
+ /*
+ * These class files
+ */
+ @Test(dataProvider = "cantBeHiddenClasses", expectedExceptions = NoClassDefFoundError.class)
+ public void failToDeriveAsHiddenClass(String name) throws Exception {
+ byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(name + ".class"));
+ Class> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ }
+
+ /*
+ * A hidden class can be successfully created but fails to be reflected
+ * if it refers to its own type in the descriptor.
+ * e.g. Class::getMethods resolves the declaring type of fields,
+ * parameter types and return type.
+ */
+ @Test
+ public void hiddenCantReflect() throws Throwable {
+ HiddenTest t = (HiddenTest)defineHiddenClass("HiddenCantReflect").newInstance();
+ t.test();
+
+ Class> c = t.getClass();
+ Class>[] intfs = c.getInterfaces();
+ assertTrue(intfs.length == 1);
+ assertTrue(intfs[0] == HiddenTest.class);
+
+ try {
+ // this would cause loading of class HiddenCantReflect and NCDFE due
+ // to error during verification
+ c.getDeclaredMethods();
+ } catch (NoClassDefFoundError e) {
+ Throwable x = e.getCause();
+ if (x == null || !(x instanceof ClassNotFoundException && x.getMessage().contains("HiddenCantReflect"))) {
+ throw e;
+ }
+ }
+ }
+
+ @Test(expectedExceptions = {IllegalArgumentException.class})
+ public void cantDefineModule() throws Throwable {
+ Path src = Paths.get("module-info.java");
+ Path dir = CLASSES_DIR.resolve("m");
+ Files.write(src, List.of("module m {}"), StandardCharsets.UTF_8);
+ compileSources(src, dir);
+
+ byte[] bytes = Files.readAllBytes(dir.resolve("module-info.class"));
+ lookup().defineHiddenClass(bytes, false);
+ }
+
+ @Test(expectedExceptions = {IllegalArgumentException.class})
+ public void cantDefineClassInAnotherPackage() throws Throwable {
+ Path src = Paths.get("ClassInAnotherPackage.java");
+ Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
+ compileSources(src, CLASSES_DIR);
+
+ byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("p").resolve("ClassInAnotherPackage.class"));
+ lookup().defineHiddenClass(bytes, false);
+ }
+
+ @Test(expectedExceptions = {IllegalAccessException.class})
+ public void lessPrivilegedLookup() throws Throwable {
+ Lookup lookup = lookup().dropLookupMode(Lookup.PRIVATE);
+ lookup.defineHiddenClass(hiddenClassBytes, false);
+ }
+
+ @DataProvider(name = "nestedTypesOrAnonymousClass")
+ private Object[][] nestedTypesOrAnonymousClass() {
+ return new Object[][] {
+ // class file with bad InnerClasses or EnclosingMethod attribute
+ new Object[] { "Outer", null },
+ new Object[] { "Outer$Inner", "Outer" },
+ new Object[] { "EnclosingClass", null },
+ new Object[] { "EnclosingClass$1", "EnclosingClass" },
+ };
+ }
+
+ @Test(dataProvider = "nestedTypesOrAnonymousClass")
+ public void hasInnerClassesOrEnclosingMethodAttribute(String className, String badDeclaringClassName) throws Throwable {
+ byte[] bytes = Files.readAllBytes(CLASSES_10_DIR.resolve(className + ".class"));
+ Class> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ hiddenClassWithBadAttribute(hc, badDeclaringClassName);
+ }
+
+ // define a hidden class with static nest membership
+ @Test
+ public void hasStaticNestHost() throws Exception {
+ byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer$Inner.class"));
+ Class> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ hiddenClassWithBadAttribute(hc, "Outer");
+ }
+
+ @Test
+ public void hasStaticNestMembers() throws Throwable {
+ byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("Outer.class"));
+ Class> hc = lookup().defineHiddenClass(bytes, false).lookupClass();
+ assertHiddenClass(hc);
+ assertTrue(hc.getNestHost() == hc);
+ Class>[] members = hc.getNestMembers();
+ assertTrue(members.length == 1 && members[0] == hc);
+ }
+
+ // a hidden class with bad InnerClasses or EnclosingMethod attribute
+ private void hiddenClassWithBadAttribute(Class> hc, String badDeclaringClassName) {
+ assertTrue(hc.isHidden());
+ assertTrue(hc.getCanonicalName() == null);
+ assertTrue(hc.getName().contains("/"));
+
+ if (badDeclaringClassName == null) {
+ // the following reflection API assumes a good name in InnerClasses
+ // or EnclosingMethod attribute can successfully be resolved.
+ assertTrue(hc.getSimpleName().length() > 0);
+ assertFalse(hc.isAnonymousClass());
+ assertFalse(hc.isLocalClass());
+ assertFalse(hc.isMemberClass());
+ } else {
+ declaringClassNotFound(hc, badDeclaringClassName);
+ }
+
+ // validation of nest membership
+ assertTrue(hc.getNestHost() == hc);
+ // validate the static nest membership
+ Class>[] members = hc.getNestMembers();
+ assertTrue(members.length == 1 && members[0] == hc);
+ }
+
+ // Class::getSimpleName, Class::isMemberClass
+ private void declaringClassNotFound(Class> c, String cn) {
+ try {
+ // fail to find declaring/enclosing class
+ c.isMemberClass();
+ assertTrue(false);
+ } catch (NoClassDefFoundError e) {
+ if (!e.getMessage().equals(cn)) {
+ throw e;
+ }
+ }
+ try {
+ // fail to find declaring/enclosing class
+ c.getSimpleName();
+ assertTrue(false);
+ } catch (NoClassDefFoundError e) {
+ if (!e.getMessage().equals(cn)) {
+ throw e;
+ }
+ }
+ }
+
+ private static void singletonNest(Class> hc) {
+ assertTrue(hc.getNestHost() == hc);
+ assertTrue(hc.getNestMembers().length == 1);
+ assertTrue(hc.getNestMembers()[0] == hc);
+ }
+
+ private static void assertHiddenClass(Class> hc) {
+ assertTrue(hc.isHidden());
+ assertTrue(hc.getCanonicalName() == null);
+ assertTrue(hc.getName().contains("/"));
+ assertFalse(hc.isAnonymousClass());
+ assertFalse(hc.isLocalClass());
+ assertFalse(hc.isMemberClass());
+ assertFalse(hc.getSimpleName().isEmpty()); // sanity check
+ }
+
+ private static byte[] classBytes(String classname, int accessFlags) {
+ return classBytes(classname, Object.class, accessFlags);
+ }
+
+ private static byte[] classBytes(String classname, Class> supertType, int accessFlags) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+ cw.visit(V14, ACC_PUBLIC|accessFlags, classname, null, Type.getInternalName(supertType), null);
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java
new file mode 100644
index 00000000000..7bba0090c0b
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/HiddenNestmateTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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 HiddenNestmateTest
+ * @run testng/othervm HiddenNestmateTest
+ */
+
+import java.lang.invoke.*;
+import java.lang.invoke.MethodHandles.Lookup;
+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;
+
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import static java.lang.invoke.MethodHandles.Lookup.*;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+import static org.testng.Assert.*;
+
+public class HiddenNestmateTest {
+ private static final byte[] bytes = classBytes("HiddenInjected");
+
+ private static void assertNestmate(Lookup lookup) {
+ assertTrue((lookup.lookupModes() & PRIVATE) != 0);
+ assertTrue((lookup.lookupModes() & MODULE) != 0);
+
+ Class> hiddenClass = lookup.lookupClass();
+ Class> nestHost = hiddenClass.getNestHost();
+ assertTrue(hiddenClass.isHidden());
+ assertTrue(nestHost == MethodHandles.lookup().lookupClass());
+
+ // 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 a hidden class to have no access to private members of another class
+ */
+ @Test
+ public void hiddenClass() throws Throwable {
+ // define a hidden class
+ Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false);
+ Class> c = lookup.lookupClass();
+ assertTrue(lookup.hasFullPrivilegeAccess());
+ assertTrue(c.getNestHost() == c); // host of its own nest
+ assertTrue(c.isHidden());
+
+ // invoke int test(HiddenNestmateTest o) via MethodHandle
+ MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
+ MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
+ try {
+ int x = (int) mh.bindTo(ctor.invoke()).invokeExact(this);
+ throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
+ } catch (IllegalAccessError e) {}
+
+ // invoke int test(HiddenNestmateTest o)
+ try {
+ int x1 = testInjectedClass(c);
+ throw new RuntimeException("should fail when accessing HiddenNestmateTest.privMethod()");
+ } catch (IllegalAccessError e) {}
+ }
+
+ /*
+ * Test a hidden class to have access to private members of its nestmates
+ */
+ @Test
+ public void hiddenNestmate() throws Throwable {
+ // define a hidden nestmate class
+ Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
+ Class> c = lookup.lookupClass();
+ assertNestmate(lookup);
+
+ // invoke int test(HiddenNestmateTest o) via MethodHandle
+ MethodHandle ctor = lookup.findConstructor(c, MethodType.methodType(void.class));
+ MethodHandle mh = lookup.findVirtual(c, "test", MethodType.methodType(int.class, HiddenNestmateTest.class));
+ int x = (int)mh.bindTo(ctor.invoke()).invokeExact( this);
+ assertTrue(x == privMethod());
+
+ // invoke int test(HiddenNestmateTest o)
+ int x1 = testInjectedClass(c);
+ assertTrue(x1 == privMethod());
+ }
+
+ /*
+ * Test a hidden class created with NESTMATE and STRONG option is a nestmate
+ */
+ @Test
+ public void hiddenStrongClass() throws Throwable {
+ // define a hidden class strongly referenced by the class loader
+ Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE, STRONG);
+ assertNestmate(lookup);
+ }
+
+ /*
+ * Fail to create a hidden class if dropping PRIVATE lookup mode
+ */
+ @Test(expectedExceptions = IllegalAccessException.class)
+ public void noPrivateLookupAccess() throws Throwable {
+ Lookup lookup = MethodHandles.lookup().dropLookupMode(Lookup.PRIVATE);
+ lookup.defineHiddenClass(bytes, false, NESTMATE);
+ }
+
+ public void teleportToNestmate() throws Throwable {
+ Lookup lookup = MethodHandles.lookup().defineHiddenClass(bytes, false, NESTMATE);
+ assertNestmate(lookup);
+
+ // Teleport to a hidden nestmate
+ Lookup lc = MethodHandles.lookup().in(lookup.lookupClass());
+ assertTrue((lc.lookupModes() & PRIVATE) != 0);
+ Lookup lc2 = lc.defineHiddenClass(bytes, false, NESTMATE);
+ assertNestmate(lc2);
+ }
+
+ /*
+ * Fail to create a hidden class in a different package from the lookup class' package
+ */
+ @Test(expectedExceptions = IllegalArgumentException.class)
+ public void notSamePackage() throws Throwable {
+ MethodHandles.lookup().defineHiddenClass(classBytes("p/HiddenInjected"), false, NESTMATE);
+ }
+
+ /*
+ * invoke int test(HiddenNestmateTest o) method defined in the injected class
+ */
+ private int testInjectedClass(Class> c) throws Throwable {
+ try {
+ Method m = c.getMethod("test", HiddenNestmateTest.class);
+ return (int) m.invoke(c.newInstance(), this);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+
+ private static byte[] classBytes(String classname) {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
+ MethodVisitor mv;
+
+ cw.visit(V12, ACC_FINAL, classname, null, "java/lang/Object", null);
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V");
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ {
+ // access a private member of the nest host class
+ mv = cw.visitMethod(ACC_PUBLIC, "test", "(LHiddenNestmateTest;)I", null, null);
+ mv.visitCode();
+ mv.visitVarInsn(ALOAD, 0);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitMethodInsn(INVOKEVIRTUAL, "HiddenNestmateTest", "privMethod", "()I");
+ mv.visitInsn(IRETURN);
+ mv.visitMaxs(0, 0);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+
+ private int privMethod() { return 1234; }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java
new file mode 100644
index 00000000000..9e3c8a8b318
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/LambdaNestedInnerTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2019, 2020, 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
+ * @summary define a lambda proxy class whose target class has an invalid
+ * nest membership
+ * @run testng/othervm p.LambdaNestedInnerTest
+ */
+
+package p;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+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.Collectors;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static org.testng.Assert.*;
+
+public class LambdaNestedInnerTest {
+ private static final String INNER_CLASSNAME = "p.LambdaNestedInnerTest$Inner";
+ private static final String DIR = "missingOuter";
+ public static class Inner implements Runnable {
+ // generate lambda proxy class
+ private Runnable lambda1 = this::doit;
+
+ @Override
+ public void run() {
+ // validate the lambda proxy class
+ Class> lambdaProxyClass = lambda1.getClass();
+ assertTrue(lambdaProxyClass.isHidden());
+ 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));
+ 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() {
+ }
+ }
+
+ @BeforeTest
+ public void setup() throws IOException {
+ String filename = INNER_CLASSNAME.replace('.', File.separatorChar) + ".class";
+ Path src = Paths.get(System.getProperty("test.classes"), filename);
+ Path dest = Paths.get(DIR, filename);
+ Files.createDirectories(dest.getParent());
+ Files.copy(src, dest, REPLACE_EXISTING);
+ }
+
+ @Test
+ public void test() throws Exception {
+ Class> inner = Class.forName(INNER_CLASSNAME);
+ // inner class is a nest member of LambdaNestedInnerTest
+ Class> nestHost = inner.getNestHost();
+ assertTrue(nestHost == LambdaNestedInnerTest.class);
+ Set> members = Arrays.stream(nestHost.getNestMembers()).collect(Collectors.toSet());
+ assertEquals(members, Set.of(nestHost, inner, TestLoader.class));
+
+ // spin lambda proxy hidden class
+ Runnable runnable = (Runnable) inner.newInstance();
+ runnable.run();
+ }
+
+ @Test
+ public void nestHostNotExist() throws Exception {
+ URL[] urls = new URL[] { Paths.get(DIR).toUri().toURL() };
+ URLClassLoader loader = new URLClassLoader(urls, null);
+ Class> inner = loader.loadClass(INNER_CLASSNAME);
+ assertTrue(inner.getClassLoader() == loader);
+ assertTrue(inner.getNestHost() == inner); // linkage error ignored
+
+ Runnable runnable = (Runnable) inner.newInstance();
+ // this validates the lambda proxy class
+ runnable.run();
+ }
+
+ /*
+ * Tests IncompatibleClassChangeError thrown since the true nest host is not
+ * in the same runtime package as the hidden class
+ */
+ @Test
+ public void nestHostNotSamePackage() throws Exception {
+ URL[] urls = new URL[] { Paths.get(DIR).toUri().toURL() };
+ TestLoader loader = new TestLoader(urls);
+
+ Class> inner = loader.loadClass(INNER_CLASSNAME);
+ assertTrue(inner.getClassLoader() == loader);
+ assertTrue(inner.getNestHost() == inner); // linkage error ignored.
+
+ Runnable runnable = (Runnable) inner.newInstance();
+ // this validates the lambda proxy class
+ runnable.run();
+ }
+
+ static class TestLoader extends URLClassLoader {
+ TestLoader(URL[] urls) {
+ super(urls, TestLoader.class.getClassLoader());
+ }
+ public Class> loadClass(String name) throws ClassNotFoundException {
+ if (INNER_CLASSNAME.equals(name)) {
+ return findClass(name);
+ } else {
+ // delegate to its parent
+ return loadClass(name, false);
+ }
+ }
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefField.java b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefField.java
new file mode 100644
index 00000000000..c88ab193659
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefField.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/*
+ * The classfile for this class will be used to define a hidden class
+ * The load of this class will fail because hidden classes cannot
+ * use their name in field signatures.
+ */
+public class SelfRefField implements Test {
+
+ SelfRefField other = null;
+
+ private void realTest() {
+ other = this; // field signature test
+ }
+
+ public void test() {
+ realTest();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefMethod.java b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefMethod.java
new file mode 100644
index 00000000000..1d30a35a35c
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfRefMethod.java
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/*
+ * The classfile for this class will be used to define a hidden class
+ * The load of this class will fail because hidden classes cannot
+ * use their names in method signatures.
+ */
+public class SelfRefMethod implements Test {
+
+ private void realTest() {
+ SelfRefMethod local = this;
+ set_other(local); // method signature test
+ }
+
+ private void set_other(SelfRefMethod t) {
+ }
+
+ public void test() {
+ realTest();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/SelfReferenceDescriptor.java b/test/jdk/java/lang/invoke/defineHiddenClass/SelfReferenceDescriptor.java
new file mode 100644
index 00000000000..5490561fccf
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/SelfReferenceDescriptor.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2019, 2020, 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
+ * @modules jdk.compiler
+ * @library /test/lib
+ * @build jdk.test.lib.Utils
+ * jdk.test.lib.compiler.CompilerUtils
+ * SelfReferenceDescriptor
+ * @run main/othervm -Xverify:remote SelfReferenceDescriptor
+ * @summary Test that a hidden class cannot be referenced in descriptor
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import static java.lang.invoke.MethodHandles.lookup;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.test.lib.compiler.CompilerUtils;
+
+import jdk.test.lib.Utils;
+
+/* package-private */ interface Test {
+ void test();
+}
+
+public class SelfReferenceDescriptor {
+
+ private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC);
+ private static final Path CLASSES_DIR = Paths.get("classes");
+
+ static void compileSources(Path sourceFile, String... options) throws IOException {
+ Stream ops = Stream.of("-cp", Utils.TEST_CLASSES + File.pathSeparator + CLASSES_DIR);
+ if (options != null && options.length > 0) {
+ ops = Stream.concat(ops, Arrays.stream(options));
+ }
+ if (!CompilerUtils.compile(sourceFile, CLASSES_DIR, ops.toArray(String[]::new))) {
+ throw new RuntimeException("Compilation of the test failed: " + sourceFile);
+ }
+ }
+
+ // Test that a hidden class cannot use its own name in a field
+ // signature.
+ public static void hiddenClassInFieldDescriptor() throws Exception {
+ compileSources(SRC_DIR.resolve("SelfRefField.java"));
+ Path path = CLASSES_DIR.resolve("SelfRefField.class");
+ byte[] bytes = Files.readAllBytes(path);
+ try {
+ lookup().defineHiddenClass(bytes, false, NESTMATE);
+ throw new RuntimeException("expected NCDFE in defining SelfRefField hidden class");
+ } catch (NoClassDefFoundError e) {
+ if (!e.getMessage().contains("SelfRefField")) throw e;
+ }
+ }
+
+ // Test that a hidden class cannot use its own name in a method
+ // signature.
+ public static void hiddenClassInMethodDescriptor() throws Exception {
+ compileSources(SRC_DIR.resolve("SelfRefMethod.java"));
+ Path path = CLASSES_DIR.resolve("SelfRefMethod.class");
+ byte[] bytes = Files.readAllBytes(path);
+ try {
+ lookup().defineHiddenClass(bytes, false, NESTMATE);
+ throw new RuntimeException("expected NCDFE in defining SelfRefMethod hidden class");
+ } catch (NoClassDefFoundError e) {
+ if (!e.getMessage().contains("SelfRefMethod")) throw e;
+ }
+ }
+
+ public static void main(String... args) throws Exception {
+ hiddenClassInMethodDescriptor();
+ hiddenClassInFieldDescriptor();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/TypeDescriptorTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/TypeDescriptorTest.java
new file mode 100644
index 00000000000..e60cb8a5782
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/TypeDescriptorTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2020, 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 8242013
+ * @run testng/othervm test.TypeDescriptorTest
+ * @summary Test TypeDescriptor::descriptorString for hidden classes which
+ * cannot be used to produce ConstantDesc via ClassDesc or
+ * MethodTypeDesc factory methods
+ */
+
+package test;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.constant.*;
+import java.lang.invoke.*;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Array;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import static java.lang.invoke.MethodType.*;
+
+import org.testng.annotations.Test;
+import org.testng.annotations.DataProvider;
+import static org.testng.Assert.*;
+
+public class TypeDescriptorTest {
+ private static final Lookup HC_LOOKUP = defineHiddenClass();
+ private static final Class> HC = HC_LOOKUP.lookupClass();
+ private static Lookup defineHiddenClass() {
+ String classes = System.getProperty("test.classes");
+ try {
+ byte[] bytes = Files.readAllBytes(Paths.get(classes, "test/HiddenClass.class"));
+ return MethodHandles.lookup().defineHiddenClass(bytes, true);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @DataProvider(name = "constables")
+ private Object[][] constables() throws Exception {
+ Class> hcArray = Array.newInstance(HC, 1).getClass();
+ return new Object[][] {
+ new Object[] { HC },
+ new Object[] { hcArray },
+ new Object[] { methodType(HC) },
+ new Object[] { methodType(void.class, HC) },
+ new Object[] { methodType(void.class, HC, int.class) },
+ new Object[] { HC_LOOKUP.findStatic(HC, "m", methodType(void.class)) },
+ new Object[] { HC_LOOKUP.findStaticVarHandle(HC, "f", Object.class) }
+ };
+ }
+
+ /*
+ * Hidden classes have no nominal descriptor.
+ * Constable::describeConstable returns empty optional.
+ */
+ @Test(dataProvider = "constables")
+ public void noNominalDescriptor(Constable constable) {
+ assertTrue(constable.describeConstable().isEmpty());
+ }
+
+ /*
+ * ClassDesc factory methods throws IAE with the name or descriptor string
+ * from a hidden class
+ */
+ @Test
+ public void testClassDesc() {
+ try {
+ ClassDesc.ofDescriptor(HC.descriptorString());
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+
+ try {
+ ClassDesc.ofDescriptor(HC.getName());
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ try {
+ ClassDesc.of(HC.getPackageName(), HC.getSimpleName());
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ try {
+ ClassDesc.of(HC.getName());
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ }
+
+ @DataProvider(name = "typeDescriptors")
+ private Object[][] typeDescriptors() throws Exception {
+ Class> hcArray = Array.newInstance(HC, 1, 1).getClass();
+ return new Object[][] {
+ new Object[] { HC, "Ltest/HiddenClass.0x[0-9a-f]+;"},
+ new Object[] { hcArray, "\\[\\[Ltest/HiddenClass.0x[0-9a-f]+;"},
+ new Object[] { methodType(HC), "\\(\\)Ltest/HiddenClass.0x[0-9a-f]+;" },
+ new Object[] { methodType(void.class, HC), "\\(Ltest/HiddenClass.0x[0-9a-f]+;\\)V" },
+ new Object[] { methodType(void.class, HC, int.class, Object.class), "\\(Ltest/HiddenClass.0x[0-9a-f]+;ILjava/lang/Object;\\)V" }
+ };
+ }
+
+ /*
+ * Hidden classes have no nominal type descriptor
+ */
+ @Test(dataProvider = "typeDescriptors")
+ public void testTypeDescriptor(TypeDescriptor td, String regex) throws Exception {
+ String desc = td.descriptorString();
+ assertTrue(desc.matches(regex));
+
+ if (td instanceof Class) {
+ try {
+ ClassDesc.ofDescriptor(desc);
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ } else if (td instanceof MethodType) {
+ try {
+ MethodTypeDesc.ofDescriptor(desc);
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ }
+ }
+
+ @DataProvider(name = "methodTypes")
+ private Object[][] methodTypes() throws Exception {
+ Class> hcArray = Array.newInstance(HC, 1, 1).getClass();
+ return new Object[][] {
+ new Object[] { methodType(HC), "\\(\\)Ltest/HiddenClass.0x[0-9a-f]+;" },
+ new Object[] { methodType(void.class, hcArray), "\\(\\[\\[Ltest/HiddenClass.0x[0-9a-f]+;\\)V" },
+ new Object[] { methodType(void.class, int.class, HC), "\\(ILtest/HiddenClass.0x[0-9a-f]+;\\)V" }
+ };
+ }
+
+ /*
+ * Test MethodType::toMethodDescriptorString with MethodType referencing to hidden class
+ */
+ @Test(dataProvider = "methodTypes")
+ public void testToMethodDescriptorString(MethodType mtype, String regex) throws Exception {
+ String desc = mtype.toMethodDescriptorString();
+ assertTrue(desc.matches(regex));
+
+ try {
+ MethodType.fromMethodDescriptorString(desc, TypeDescriptorTest.class.getClassLoader());
+ assertFalse(true);
+ } catch (IllegalArgumentException e) {}
+ }
+}
+
+class HiddenClass {
+ private static final Object f = new Object();
+ public static void m() {
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java
new file mode 100644
index 00000000000..c3cdb2b7095
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/UnloadingTest.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2020, 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
+ * @summary verify if the hidden class is unloaded when the class loader is GC'ed
+ * @modules jdk.compiler
+ * @library /test/lib/
+ * @build jdk.test.lib.util.ForceGC
+ * @run testng/othervm UnloadingTest
+ */
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import jdk.test.lib.util.ForceGC;
+
+import jdk.test.lib.compiler.CompilerUtils;
+import jdk.test.lib.Utils;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
+import static org.testng.Assert.*;
+
+public class UnloadingTest {
+ private static final Path CLASSES_DIR = Paths.get("classes");
+ private static byte[] hiddenClassBytes;
+
+ @BeforeTest
+ static void setup() throws IOException {
+ Path src = Paths.get(Utils.TEST_SRC, "src", "LookupHelper.java");
+ if (!CompilerUtils.compile(src, CLASSES_DIR)) {
+ throw new RuntimeException("Compilation of the test failed: " + src);
+ }
+
+ hiddenClassBytes = Files.readAllBytes(CLASSES_DIR.resolve("LookupHelper.class"));
+ }
+
+ /*
+ * Test that a hidden class is unloaded while the loader remains strongly reachable
+ */
+ @Test
+ public void unloadable() throws Exception {
+ TestLoader loader = new TestLoader();
+ Class> helper = Class.forName("LookupHelper", true, loader);
+ Method m = helper.getMethod("getLookup");
+ Lookup lookup = (Lookup)m.invoke(null);
+ HiddenClassUnloader unloader = createHiddenClass(lookup, false);
+ // the hidden class should be unloaded
+ unloader.unload();
+
+ // loader is strongly reachable
+ Reference.reachabilityFence(loader);
+ }
+
+ /*
+ * Test that a hidden class is not unloaded when the loader is strongly reachable
+ */
+ @Test
+ public void notUnloadable() throws Exception {
+ TestLoader loader = new TestLoader();
+ Class> helper = Class.forName("LookupHelper", true, loader);
+ Method m = helper.getMethod("getLookup");
+ Lookup lookup = (Lookup)m.invoke(null);
+ HiddenClassUnloader unloader = createHiddenClass(lookup, true);
+ assertFalse(unloader.tryUnload()); // hidden class is not unloaded
+
+ // loader is strongly reachable
+ Reference.reachabilityFence(loader);
+ }
+
+ /*
+ * Create a nest of two hidden classes.
+ * They can be unloaded even the loader is strongly reachable
+ */
+ @Test
+ public void hiddenClassNest() throws Exception {
+ TestLoader loader = new TestLoader();
+ Class> helper = Class.forName("LookupHelper", true, loader);
+ Method m = helper.getMethod("getLookup");
+ Lookup lookup = (Lookup)m.invoke(null);
+ HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, false);
+
+ // keep a strong reference to the nest member class
+ Class> member = unloaders[1].weakRef.get();
+ assertTrue(member != null);
+ // nest host and member will not be unloaded
+ assertFalse(unloaders[0].tryUnload());
+ assertFalse(unloaders[1].tryUnload());
+
+ // clear the reference to the nest member
+ Reference.reachabilityFence(member);
+ member = null;
+
+ // nest host and member will be unloaded
+ unloaders[0].unload();
+ unloaders[1].unload();
+
+ // loader is strongly reachable
+ Reference.reachabilityFence(loader);
+ }
+
+ /*
+ * Create a nest with a hidden class nest host and strong nest member.
+ * Test that both are not unloaded
+ */
+ @Test
+ public void hiddenClassNestStrongMember() throws Exception {
+ TestLoader loader = new TestLoader();
+ Class> helper = Class.forName("LookupHelper", true, loader);
+ Method m = helper.getMethod("getLookup");
+ Lookup lookup = (Lookup)m.invoke(null);
+ HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, false, true);
+ assertFalse(unloaders[0].tryUnload()); // nest host cannot be unloaded
+ assertFalse(unloaders[1].tryUnload()); // nest member cannot be unloaded
+
+ // loader is strongly reachable
+ Reference.reachabilityFence(loader);
+ }
+
+ /*
+ * Create a nest with a strong hidden nest host and a hidden class member.
+ * The nest member can be unloaded whereas the nest host will not be unloaded.
+ */
+ @Test
+ public void hiddenClassNestStrongHost() throws Exception {
+ TestLoader loader = new TestLoader();
+ Class> helper = Class.forName("LookupHelper", true, loader);
+ Method m = helper.getMethod("getLookup");
+ Lookup lookup = (Lookup)m.invoke(null);
+ HiddenClassUnloader[] unloaders = createNestOfTwoHiddenClasses(lookup, true, false);
+ assertFalse(unloaders[0].tryUnload()); // nest host cannot be unloaded
+ unloaders[1].unload();
+
+ // loader is strongly reachable
+ Reference.reachabilityFence(loader);
+ }
+
+ /*
+ * Create a HiddenClassUnloader that holds a weak reference to the newly created
+ * hidden class.
+ */
+ static HiddenClassUnloader createHiddenClass(Lookup lookup, boolean strong) throws Exception {
+ Class> hc;
+ if (strong) {
+ hc = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG).lookupClass();
+ } else {
+ hc = lookup.defineHiddenClass(hiddenClassBytes, false).lookupClass();
+ }
+ assertTrue(hc.getClassLoader() == lookup.lookupClass().getClassLoader());
+ return new HiddenClassUnloader(hc);
+ }
+
+ /*
+ * Create an array of HiddenClassUnloader with two elements: the first element
+ * is for the nest host and the second element is for the nest member.
+ */
+ static HiddenClassUnloader[] createNestOfTwoHiddenClasses(Lookup lookup, boolean strongHost, boolean strongMember) throws Exception {
+ Lookup hostLookup;
+ if (strongHost) {
+ hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false, STRONG);
+ } else {
+ hostLookup = lookup.defineHiddenClass(hiddenClassBytes, false);
+ }
+ Class> host = hostLookup.lookupClass();
+ Class> member;
+ if (strongMember) {
+ member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE, STRONG).lookupClass();
+ } else {
+ member = hostLookup.defineHiddenClass(hiddenClassBytes, false, NESTMATE).lookupClass();
+ }
+ assertTrue(member.getNestHost() == host);
+ return new HiddenClassUnloader[] { new HiddenClassUnloader(host), new HiddenClassUnloader(member) };
+ }
+
+ static class HiddenClassUnloader {
+ private final WeakReference> weakRef;
+ private HiddenClassUnloader(Class> hc) {
+ assertTrue(hc.isHidden());
+ this.weakRef = new WeakReference<>(hc);
+ }
+
+ void unload() {
+ // Force garbage collection to trigger unloading of class loader and native library
+ ForceGC gc = new ForceGC();
+ assertTrue(gc.await(() -> weakRef.get() == null));
+
+ if (weakRef.get() != null) {
+ throw new RuntimeException("loader " + " not unloaded!");
+ }
+ }
+
+ boolean tryUnload() {
+ ForceGC gc = new ForceGC();
+ return gc.await(() -> weakRef.get() == null);
+ }
+ }
+
+ static class TestLoader extends URLClassLoader {
+ static URL[] toURLs() {
+ try {
+ return new URL[] { CLASSES_DIR.toUri().toURL() };
+ } catch (MalformedURLException e) {
+ throw new Error(e);
+ }
+ }
+
+ static AtomicInteger counter = new AtomicInteger();
+ TestLoader() {
+ super("testloader-" + counter.addAndGet(1), toURLs(), ClassLoader.getSystemClassLoader());
+ }
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/UnreflectTest.java b/test/jdk/java/lang/invoke/defineHiddenClass/UnreflectTest.java
new file mode 100644
index 00000000000..f1bf40c7906
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/UnreflectTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2020, 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
+ * @compile src/Fields.java
+ * @run testng/othervm UnreflectTest
+ * @summary Test Lookup::unreflectSetter and Lookup::unreflectVarHandle
+ */
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+public class UnreflectTest {
+ static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
+ static final Class> hiddenClass = defineHiddenClass();
+ private static Class> defineHiddenClass() {
+ String classes = System.getProperty("test.classes");
+ Path cf = Paths.get(classes, "Fields.class");
+ try {
+ byte[] bytes = Files.readAllBytes(cf);
+ return MethodHandles.lookup().defineHiddenClass(bytes, true).lookupClass();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /*
+ * Test Lookup::unreflectSetter and Lookup::unreflectVarHandle that
+ * can write the value of a non-static final field in a normal class
+ */
+ @Test
+ public void testFieldsInNormalClass() throws Throwable {
+ // despite the name "HiddenClass", this class is loaded by the
+ // class loader as non-hidden class
+ Class> c = Fields.class;
+ Fields o = new Fields();
+ assertFalse(c.isHidden());
+ readOnlyAccessibleObject(c, "STATIC_FINAL", null, true);
+ readWriteAccessibleObject(c, "STATIC_NON_FINAL", null, false);
+ readWriteAccessibleObject(c, "FINAL", o, true);
+ readWriteAccessibleObject(c, "NON_FINAL", o, false);
+ }
+
+ /*
+ * Test Lookup::unreflectSetter and Lookup::unreflectVarHandle that
+ * has NO write the value of a non-static final field in a hidden class
+ */
+ @Test
+ public void testFieldsInHiddenClass() throws Throwable {
+ assertTrue(hiddenClass.isHidden());
+ Object o = hiddenClass.newInstance();
+ readOnlyAccessibleObject(hiddenClass, "STATIC_FINAL", null, true);
+ readWriteAccessibleObject(hiddenClass, "STATIC_NON_FINAL", null, false);
+ readOnlyAccessibleObject(hiddenClass, "FINAL", o, true);
+ readWriteAccessibleObject(hiddenClass, "NON_FINAL", o, false);
+ }
+
+ /*
+ * Verify read-only access via unreflectSetter and unreflectVarHandle
+ */
+ private static void readOnlyAccessibleObject(Class> c, String name, Object o, boolean isFinal) throws Throwable {
+ Field f = c.getDeclaredField(name);
+ int modifier = f.getModifiers();
+ if (isFinal) {
+ assertTrue(Modifier.isFinal(modifier));
+ } else {
+ assertFalse(Modifier.isFinal(modifier));
+ }
+ assertTrue(f.trySetAccessible());
+
+ // Field object with read-only access
+ MethodHandle mh = LOOKUP.unreflectGetter(f);
+ Object value = Modifier.isStatic(modifier) ? mh.invoke() : mh.invoke(o);
+ assertTrue(value == f.get(o));
+ try {
+ LOOKUP.unreflectSetter(f);
+ assertTrue(false, "should fail to unreflect a setter for " + name);
+ } catch (IllegalAccessException e) {
+ }
+
+ VarHandle vh = LOOKUP.unreflectVarHandle(f);
+ if (isFinal) {
+ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.SET));
+ } else {
+ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET));
+ }
+ }
+
+ private static void readWriteAccessibleObject(Class> c, String name, Object o, boolean isFinal) throws Throwable {
+ Field f = c.getDeclaredField(name);
+ int modifier = f.getModifiers();
+ if (isFinal) {
+ assertTrue(Modifier.isFinal(modifier));
+ } else {
+ assertFalse(Modifier.isFinal(modifier));
+ }
+ assertTrue(f.trySetAccessible());
+
+ // Field object with read-write access
+ MethodHandle mh = MethodHandles.lookup().unreflectGetter(f);
+ Object value = Modifier.isStatic(modifier) ? mh.invoke() : mh.invoke(o);
+ assertTrue(value == f.get(o));
+ try {
+ MethodHandle setter = MethodHandles.lookup().unreflectSetter(f);
+ if (Modifier.isStatic(modifier)) {
+ setter.invokeExact(value);
+ } else {
+ setter.invoke(o, value);
+ }
+ } catch (IllegalAccessException e) {
+ throw e;
+ }
+
+ VarHandle vh = LOOKUP.unreflectVarHandle(f);
+ if (isFinal) {
+ assertFalse(vh.isAccessModeSupported(VarHandle.AccessMode.SET));
+ } else {
+ assertTrue(vh.isAccessModeSupported(VarHandle.AccessMode.SET));
+ }
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/AbstractClass.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/AbstractClass.java
new file mode 100644
index 00000000000..0cccba04979
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/AbstractClass.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public abstract class AbstractClass {
+ abstract void test();
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/EnclosingClass.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/EnclosingClass.java
new file mode 100644
index 00000000000..ceded918f6f
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/EnclosingClass.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class EnclosingClass {
+ public static void run() {
+ Runnable r = new Runnable() {
+ public void run() {}
+ };
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/Fields.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/Fields.java
new file mode 100644
index 00000000000..7fd257b7645
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/Fields.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class Fields {
+ static final Object STATIC_FINAL = new Object();
+ static Object STATIC_NON_FINAL = new Object();
+ final Object FINAL = new Object();
+ Object NON_FINAL = new Object();
+
+ public String name() {
+ return this.getClass().getName();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenAnnotation.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenAnnotation.java
new file mode 100644
index 00000000000..68b52263b82
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenAnnotation.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+import java.lang.annotation.*;
+import static java.lang.annotation.ElementType.*;
+
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
+@Retention(RetentionPolicy.SOURCE)
+public @interface HiddenAnnotation {
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenCantReflect.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenCantReflect.java
new file mode 100644
index 00000000000..290c7392949
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenCantReflect.java
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+/*
+ * The classfile does not fail verification but would fail when
+ * getMethod
+ */
+public class HiddenCantReflect implements HiddenTest {
+
+ HiddenCantReflect other = null;
+
+ private String realTest() {
+ Object o = other;
+ HiddenCantReflect local = this;
+ local = other;
+ local = (HiddenCantReflect) o;
+ local = new HiddenCantReflect();
+
+ set_other(null);
+
+ local = getThis();
+
+ set_other_maybe(new Object());
+ set_other_maybe(this);
+ return "HiddenCantReflect";
+ }
+
+ private HiddenCantReflect getThis() {
+ return null;
+ }
+
+ private void set_other(HiddenCantReflect t) {
+ other = t;
+ }
+
+ private void set_other_maybe(Object o) {
+ if (o instanceof HiddenCantReflect) {
+ }
+ }
+
+ public void test() {
+ String result = realTest();
+ // Make sure that the Utf8 constant pool entry for "HiddenCantReflect" is okay.
+ if (!result.substring(0, 7).equals("HiddenC") ||
+ !result.substring(7).equals("antReflect")) {
+ throw new RuntimeException("'HiddenCantReflect string is bad: " + result);
+ }
+
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClass.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClass.java
new file mode 100644
index 00000000000..20dadb0e539
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClass.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+/*
+ * The classfile for this class will be loaded directly and used to define
+ * a hidden class.
+ */
+public class HiddenClass implements HiddenTest {
+
+ HiddenClass other = null;
+
+ private String realTest() {
+ Object o = other;
+ HiddenClass local = this;
+ local = other;
+ local = (HiddenClass) o;
+ local = new HiddenClass();
+
+ set_other_maybe(new Object());
+ set_other_maybe(this);
+ return "HiddenClass";
+ }
+
+ private void set_other_maybe(Object o) {
+ if (o instanceof HiddenClass) {
+ }
+ }
+
+ public void test() {
+ String result = realTest();
+ // Make sure that the Utf8 constant pool entry for "HiddenClass" is okay.
+ if (!result.substring(0, 7).equals("HiddenC") ||
+ !result.substring(7).equals("lass")) {
+ throw new RuntimeException("'HiddenClass string is bad: " + result);
+ }
+
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClassThrow.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClassThrow.java
new file mode 100644
index 00000000000..b9806547ffb
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenClassThrow.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * The classfile for this class will be loaded directly and used to define
+ * a hidden class.
+ */
+public class HiddenClassThrow implements HiddenTest {
+
+ public void test() {
+ throw new RuntimeException(this.getClass().getName());
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenEnum.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenEnum.java
new file mode 100644
index 00000000000..d56f870ee91
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenEnum.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+// fail to create HiddenEnum as a hidden class as it fails verification
+// initializes the static final HiddenEnum[] $VALUES field
+public enum HiddenEnum {
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenInterface.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenInterface.java
new file mode 100644
index 00000000000..248952084e1
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenInterface.java
@@ -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.
+ */
+
+/*
+ * The classfile for this class will be used to define a hidden interface.
+ * This class will fail to be created as a hidden class because hidden classes
+ * cannot user their name in field signatures.
+ */
+public interface HiddenInterface {
+ default void test() {
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenRecord.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenRecord.java
new file mode 100644
index 00000000000..2cd41da1f2c
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenRecord.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020, 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.
+ */
+
+// fail to create HiddenRecord as a hidden class
+// verification fails in the BSM to invoke equals(HiddenRecord, Object) method
+record HiddenRecord(int i) { }
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenSuper.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenSuper.java
new file mode 100644
index 00000000000..3b0afba038d
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/HiddenSuper.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class HiddenSuper extends HiddenClass implements HiddenTest {
+ private void realTest() {
+ }
+
+ public void test() {
+ realTest();
+ }
+}
diff --git a/test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java b/test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java
new file mode 100644
index 00000000000..bcdf2d6ef69
--- /dev/null
+++ b/test/jdk/java/lang/invoke/defineHiddenClass/src/Lambda.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+import java.util.function.Function;
+
+public class Lambda implements HiddenTest {
+ public void test() {
+ Function