8238358: Implementation of JEP 371: Hidden Classes
Co-authored-by: Lois Foltan <lois.foltan@oracle.com> Co-authored-by: David Holmes <david.holmes@oracle.com> Co-authored-by: Harold Seigel <harold.seigel@oracle.com> Co-authored-by: Serguei Spitsyn <serguei.spitsyn@oracle.com> Co-authored-by: Alex Buckley <alex.buckley@oracle.com> Co-authored-by: Jamsheed Mohammed C M <jamsheed.c.m@oracle.com> Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com> Co-authored-by: Amy Lu <amy.lu@oracle.com> Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, sspitsyn, vromero
This commit is contained in:
parent
642041adbc
commit
7cc1371059
make
src
hotspot/share
aot
ci
classfile
classFileParser.cppclassFileParser.hppclassLoader.cppclassLoaderData.cppclassLoaderData.hppclassLoaderDataGraph.cppclassLoaderDataGraph.hppclassLoaderExt.cppclassLoaderHierarchyDCmd.cppclassLoaderStats.cppclassLoaderStats.hppdefaultMethods.cppdictionary.cppjavaClasses.cppjavaClasses.hppklassFactory.cppklassFactory.hppmoduleEntry.cppmoduleEntry.hppresolutionErrors.cppresolutionErrors.hppsymbolTable.cppsystemDictionary.cppsystemDictionary.hppsystemDictionaryShared.cppverificationType.cppverifier.cppvmSymbols.cppvmSymbols.hpp
include
interpreter
jfr
instrumentation
leakprofiler/checkpoint
metadata
periodic
recorder/checkpoint/types
memory
oops
arrayKlass.cppinstanceKlass.cppinstanceKlass.hppinstanceMirrorKlass.inline.hppklass.cppklass.hppmethod.hpp
opto
prims
runtime
utilities
java.base/share
classes
java/lang
Class.javaClassLoader.javaStringConcatHelper.javaSystem.java
invoke
AbstractValidatingLambdaMetafactory.javaGenerateJLIClassesHelper.javaInnerClassLambdaMetafactory.javaInvokerBytecodeGenerator.javaMethodHandleImpl.javaMethodHandleNatives.javaMethodHandles.javaMethodType.javaStringConcatFactory.javaTypeDescriptor.java
reflect
jdk/internal
access
reflect
sun/invoke/util
native/libjava
java.instrument/share/classes/java/lang/instrument
jdk.compiler/share/classes/com/sun/tools/javac
@ -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 <a href=\"../jvmti.html#GetLoadedClasses\">JVM TI GetLoadedClasses</a>."
|
||||
(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 "
|
||||
"<a href=\"../jni/index.html\">Java Native Interface Specification</a>"
|
||||
"<p>
|
||||
"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 "
|
||||
"<a href=\"../jvmti.html#GetClassSignature\">JVM TI GetClassSignature</a>."
|
||||
(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 <i>initiating</i> 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 <code>ClassLoader::loadClass</code>, "
|
||||
"<code>Class::forName</code> and bytecode linkage. That is, "
|
||||
"all classes for which this class loader has been recorded as an "
|
||||
"<i>initiating</i> 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. "
|
||||
"<p>"
|
||||
"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. "
|
||||
"<p>"
|
||||
"No ordering of the returned list is guaranteed. "
|
||||
"<p>"
|
||||
"See <a href=\"../jvmti.html#GetClassLoaderClasses\">JVM TI GetClassLoaderClasses</a>. "
|
||||
(Out
|
||||
(classLoaderObject classLoaderObject "The class loader object ID. ")
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -43,7 +43,7 @@ GrowableArray<AOTLib*>* AOTLoader::_libraries = new(ResourceObj::C_HEAP, mtCode)
|
||||
#define FOR_ALL_AOT_LIBRARIES(lib) for (GrowableArrayIterator<AOTLib*> 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;
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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<Handle>* 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) {
|
||||
|
@ -37,6 +37,8 @@ template <typename T>
|
||||
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<Handle>* _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<Handle>* 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; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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<mtClass> {
|
||||
// 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<mtClass> {
|
||||
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<mtClass> {
|
||||
|
||||
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<mtClass> {
|
||||
}
|
||||
|
||||
// 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<mtClass> {
|
||||
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<mtClass> {
|
||||
|
||||
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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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*));
|
||||
|
@ -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) {
|
||||
|
@ -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(" <bootstrap>");
|
||||
} 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<ClassLoaderData*>(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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
|
@ -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<Klass*>* java_lang_Class::_fixup_mirror_list = NULL;
|
||||
GrowableArray<Klass*>* java_lang_Class::_fixup_module_field_list = NULL;
|
||||
int java_lang_Throwable::backtrace_offset;
|
||||
|
@ -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:
|
||||
|
@ -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<Handle>* 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;
|
||||
|
@ -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 <typename>
|
||||
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<Handle>* cp_patches,
|
||||
const ClassLoadInfo& cl_info,
|
||||
TRAPS);
|
||||
public:
|
||||
static InstanceKlass* check_shared_class_file_load_hook(
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<ConstantPool*, mtClass>::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<ConstantPool*, mtClass>::free_entry(entry);
|
||||
}
|
||||
|
||||
|
@ -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<ConstantPool*, mtClass> {
|
||||
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<ConstantPool*, mtClass> {
|
||||
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<ConstantPool*, mtClass>::next();
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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<Handle>* 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<Handle>* 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
|
||||
|
@ -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<Handle>* _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<Handle>* 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<Handle>* 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<Handle>* 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:
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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) \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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: ");
|
||||
|
@ -219,6 +219,7 @@
|
||||
stackTrace="true">
|
||||
<Field type="ClassLoader" name="classLoader" label="Class Loader" />
|
||||
<Field type="boolean" name="unsafeAnonymousClassLoader" label="Unsafe Anonymous Class Loader" />
|
||||
<Field type="boolean" name="hiddenClassLoader" label="Hidden Class Loader" />
|
||||
<Field type="ulong" contentType="bytes" name="size" label="Size" />
|
||||
<Field type="MetadataType" name="metadataType" label="Metadata Type" />
|
||||
<Field type="MetaspaceObjectType" name="metaspaceObjectType" label="Metaspace Object Type" />
|
||||
@ -227,6 +228,7 @@
|
||||
<Event name="MetaspaceOOM" category="Java Virtual Machine, GC, Metaspace" label="Metaspace Out of Memory" startTime="false" stackTrace="true">
|
||||
<Field type="ClassLoader" name="classLoader" label="Class Loader" />
|
||||
<Field type="boolean" name="unsafeAnonymousClassLoader" label="Unsafe Anonymous Class Loader" />
|
||||
<Field type="boolean" name="hiddenClassLoader" label="Hidden Class Loader" />
|
||||
<Field type="ulong" contentType="bytes" name="size" label="Size" />
|
||||
<Field type="MetadataType" name="metadataType" label="Metadata Type" />
|
||||
<Field type="MetaspaceObjectType" name="metaspaceObjectType" label="Metaspace Object Type" />
|
||||
@ -729,6 +731,11 @@
|
||||
description="Total size of all allocated metaspace chunks for unsafe anonymous classes (each chunk has several blocks)" />
|
||||
<Field type="ulong" contentType="bytes" name="unsafeAnonymousBlockSize" label="Total Unsafe Anonymous Classes Block Size"
|
||||
description="Total size of all allocated metaspace blocks for unsafe anonymous classes (each chunk has several blocks)" />
|
||||
<Field type="long" name="hiddenClassCount" label="Hidden Classes" description="Number of hidden classes" />
|
||||
<Field type="ulong" contentType="bytes" name="hiddenChunkSize" label="Total Hidden Classes Chunk Size"
|
||||
description="Total size of all allocated metaspace chunks for hidden classes (each chunk has several blocks)" />
|
||||
<Field type="ulong" contentType="bytes" name="hiddenBlockSize" label="Total Hidden Classes Block Size"
|
||||
description="Total size of all allocated metaspace blocks for hidden classes (each chunk has several blocks)" />
|
||||
</Event>
|
||||
|
||||
<Event name="SymbolTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="Symbol Table Statistics" period="everyChunk">
|
||||
@ -1135,6 +1142,7 @@
|
||||
<Field type="Symbol" name="name" label="Name" />
|
||||
<Field type="Package" name="package" label="Package" />
|
||||
<Field type="int" name="modifiers" label="Access Modifiers" />
|
||||
<Field type="boolean" name="hidden" label="Hidden" />
|
||||
</Type>
|
||||
|
||||
<Type name="ClassLoader" label="Java Class Loader">
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<bool>(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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<mtClass> {
|
||||
|
||||
// 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),
|
||||
|
@ -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 [<loader name>,] instance of <loader class name>"
|
||||
// or "CLD for <anonymous class>, loaded by [<loader name>,] instance of <loader class name>"
|
||||
// or "CLD for <hidden or anonymous class>, loaded by [<loader name>,] instance of <loader class name>"
|
||||
|
||||
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(" <anonymous class>, loaded by");
|
||||
if (cld->has_class_mirror_holder()) {
|
||||
_out->print(" <hidden or anonymous class>, loaded by");
|
||||
}
|
||||
if (name != NULL) {
|
||||
_out->print(" \"%s\"", name);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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<Klass*>* ArrayKlass::compute_secondary_supers(int num_extra_slots,
|
||||
|
@ -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);
|
||||
|
@ -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<RecordComponent*>* 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);
|
||||
|
@ -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.
|
||||
|
@ -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 "<unknown>";
|
||||
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 "<unknown>";
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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<u2>* 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());
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<?xml-stylesheet type="text/xsl" href="jvmti.xsl"?>
|
||||
<!--
|
||||
Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
Copyright (c) 2002, 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
|
||||
@ -6846,10 +6846,6 @@ class C2 extends C1 implements I2 {
|
||||
</category>
|
||||
|
||||
<category id="class" label="Class">
|
||||
|
||||
<intro>
|
||||
</intro>
|
||||
|
||||
<function id="GetLoadedClasses" jkernel="yes" num="78">
|
||||
<synopsis>Get Loaded Classes</synopsis>
|
||||
<description>
|
||||
@ -6858,9 +6854,26 @@ class C2 extends C1 implements I2 {
|
||||
<code>class_count_ptr</code>, and the array itself via
|
||||
<code>classes_ptr</code>.
|
||||
<p/>
|
||||
Array classes of all types (including arrays of primitive types) are
|
||||
included in the returned list. Primitive classes (for example,
|
||||
<code>java.lang.Integer.TYPE</code>) are <i>not</i> included in this list.
|
||||
A class or interface creation can be triggered by one of the following:
|
||||
<ul>
|
||||
<li>By loading and deriving a class from a <code>class</code> file representation
|
||||
using a class loader (see <vmspec chapter="5.3"/>).</li>
|
||||
<li>By invoking <externallink id="../api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass(byte[],boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...)">Lookup::defineHiddenClass</externallink>
|
||||
that creates a hidden class or interface from a <code>class</code> file representation.</li>
|
||||
<li>By invoking methods in certain Java SE Platform APIs such as reflection.</li>
|
||||
</ul>
|
||||
<p/>
|
||||
An array class is created directly by the Java virtual machine. The creation
|
||||
can be triggered by using class loaders or by invoking methods in certain
|
||||
Java SE Platform APIs such as reflection.
|
||||
<p/>
|
||||
The returned list includes all classes and interfaces, including
|
||||
<externallink id="../api/java.base/java/lang/Class.html#isHidden()">
|
||||
hidden classes or interfaces</externallink>,
|
||||
and also array classes of all types
|
||||
(including arrays of primitive types).
|
||||
Primitive classes (for example, <code>java.lang.Integer.TYPE</code>) are
|
||||
<i>not</i> included in the returned list.
|
||||
</description>
|
||||
<origin>jvmdi</origin>
|
||||
<capabilities>
|
||||
@ -6887,15 +6900,27 @@ class C2 extends C1 implements I2 {
|
||||
<function id="GetClassLoaderClasses" jkernel="yes" num="79">
|
||||
<synopsis>Get Classloader Classes</synopsis>
|
||||
<description>
|
||||
Returns an array of those classes for which this class loader has
|
||||
been recorded as an initiating loader. Each
|
||||
class in the returned array was created by this class loader,
|
||||
Returns an array of all classes which this class loader
|
||||
can find by name via
|
||||
<externallink id="../api/java.base/java/lang/ClassLoader.html#loadClass(java.lang.String,boolean)">ClassLoader::loadClass</externallink>,
|
||||
<externallink id="../api/java.base/java/lang/Class.html#forName(java.lang.String,boolean,java.lang.ClassLoader)">Class::forName</externallink> and bytecode linkage.
|
||||
That is, all classes for which <code>initiating_loader</code>
|
||||
has been recorded as an initiating loader.
|
||||
Each class in the returned array was created by this class loader,
|
||||
either by defining it directly or by delegation to another class loader.
|
||||
See <vmspec chapter="5.3"/>.
|
||||
<p/>
|
||||
The returned list does not include
|
||||
<externallink id="../api/java.base/java/lang/Class.html#isHidden()">hidden
|
||||
classes or interfaces</externallink> or array classes whose
|
||||
element type is a hidden class or interface as they cannot be discovered
|
||||
by any class loader.
|
||||
<p/>
|
||||
The number of classes in the array is returned via
|
||||
<code>class_count_ptr</code>, and the array itself via
|
||||
<code>classes_ptr</code>.
|
||||
<p/>
|
||||
See <externallink id="../api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass(byte[],boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...)">Lookup::defineHiddenClass</externallink>.
|
||||
</description>
|
||||
<origin>jvmdi</origin>
|
||||
<capabilities>
|
||||
@ -6931,15 +6956,37 @@ class C2 extends C1 implements I2 {
|
||||
<function id="GetClassSignature" phase="start" num="48">
|
||||
<synopsis>Get Class Signature</synopsis>
|
||||
<description>
|
||||
For the class indicated by <code>klass</code>, return the
|
||||
<externallink id="jni/types.html#type-signatures">JNI
|
||||
type signature</externallink>
|
||||
and the generic signature of the class.
|
||||
For example, <code>java.util.List</code> is <code>"Ljava/util/List;"</code>
|
||||
and <code>int[]</code> is <code>"[I"</code>
|
||||
The returned name for primitive classes
|
||||
is the type signature character of the corresponding primitive type.
|
||||
For example, <code>java.lang.Integer.TYPE</code> is <code>"I"</code>.
|
||||
Return the name and the generic signature of the class indicated by <code>klass</code>.
|
||||
<p/>
|
||||
If the class is a class or interface, then:
|
||||
<ul>
|
||||
<li>If the class or interface is not <externallink id="../api/java.base/java/lang/Class.html#isHidden()">hidden</externallink>,
|
||||
then the returned name is the <externallink id="jni/types.html#type-signatures">
|
||||
JNI type signature</externallink>.
|
||||
For example, java.util.List is "Ljava/util/List;"
|
||||
</li>
|
||||
<li>If the class or interface is <externallink id="../api/java.base/java/lang/Class.html#isHidden()">hidden</externallink>,
|
||||
then the returned name is a string of the form:
|
||||
<code>"L" + N + "." + S + ";"</code>
|
||||
where <code>N</code> is the binary name encoded in internal form (JVMS 4.2.1)
|
||||
indicated by the <code>class</code> file passed to
|
||||
<externallink id="../api/java.base/java/lang/invoke/MethodHandles.Lookup.html#defineHiddenClass(byte[],boolean,java.lang.invoke.MethodHandles.Lookup.ClassOption...)">Lookup::defineHiddenClass</externallink>,
|
||||
and <code>S</code> is an unqualified name.
|
||||
The returned name is not a type descriptor and does not conform to JVMS 4.3.2.
|
||||
For example, com.foo.Foo/AnySuffix is "Lcom/foo/Foo.AnySuffix;"
|
||||
</li>
|
||||
</ul>
|
||||
<p/>
|
||||
If the class indicated by <code>klass</code> represents an array class, then
|
||||
the returned name is a string consisting of one or more "<code>[</code>" characters
|
||||
representing the depth of the array nesting, followed by the class signature
|
||||
of the element type. For example the class signature of java.lang.String[] is
|
||||
"[Ljava/lang/String;" and that of int[] is "[I".
|
||||
<p/>
|
||||
If the class indicated by <code>klass</code> represents primitive type or <code>void</code>,
|
||||
then the returned name is the <externallink id="jni/types.html#type-signatures">
|
||||
type signature character of the corresponding primitive type</externallink>.
|
||||
For example, java.lang.Integer.TYPE is "I".
|
||||
</description>
|
||||
<origin>jvmdiClone</origin>
|
||||
<capabilities>
|
||||
@ -12712,13 +12759,16 @@ myInit() {
|
||||
<event label="Class Load"
|
||||
id="ClassLoad" const="JVMTI_EVENT_CLASS_LOAD" filtered="thread" phase="start" num="55">
|
||||
<description>
|
||||
A class load event is generated when a class is first loaded. The order
|
||||
of class load events generated by a particular thread are guaranteed
|
||||
to match the order of class loading within that thread.
|
||||
A class load event is generated
|
||||
<functionlink id="GetLoadedClasses">when a class or interface is created.</functionlink>.
|
||||
<p/>
|
||||
Array class creation does not generate a class load event.
|
||||
The creation of a primitive class (for example, java.lang.Integer.TYPE)
|
||||
does not generate a class load event.
|
||||
<p/>
|
||||
The order of class load events generated by a particular thread is guaranteed
|
||||
to match the order of class loading within that thread.
|
||||
<p/>
|
||||
This event is sent at an early stage in loading the class. As
|
||||
a result the class should be used carefully. Note, for example,
|
||||
that methods and fields are not yet loaded, so queries for methods,
|
||||
|
@ -150,8 +150,8 @@ bool VM_RedefineClasses::doit_prologue() {
|
||||
}
|
||||
|
||||
oop mirror = JNIHandles::resolve_non_null(_class_defs[i].klass);
|
||||
// classes for primitives and arrays and vm unsafe anonymous classes cannot be redefined
|
||||
// check here so following code can assume these classes are InstanceKlass
|
||||
// classes for primitives, arrays, hidden and vm unsafe anonymous classes
|
||||
// cannot be redefined.
|
||||
if (!is_modifiable_class(mirror)) {
|
||||
_res = JVMTI_ERROR_UNMODIFIABLE_CLASS;
|
||||
return false;
|
||||
@ -293,8 +293,9 @@ bool VM_RedefineClasses::is_modifiable_class(oop klass_mirror) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot redefine or retransform an unsafe anonymous class.
|
||||
if (InstanceKlass::cast(k)->is_unsafe_anonymous()) {
|
||||
// Cannot redefine or retransform a hidden or an unsafe anonymous class.
|
||||
if (InstanceKlass::cast(k)->is_hidden() ||
|
||||
InstanceKlass::cast(k)->is_unsafe_anonymous()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -1239,11 +1240,12 @@ jvmtiError VM_RedefineClasses::load_new_class_versions(TRAPS) {
|
||||
// load hook event.
|
||||
state->set_class_being_redefined(the_class, _class_load_kind);
|
||||
|
||||
ClassLoadInfo cl_info(protection_domain);
|
||||
InstanceKlass* scratch_class = SystemDictionary::parse_stream(
|
||||
the_class_sym,
|
||||
the_class_loader,
|
||||
protection_domain,
|
||||
&st,
|
||||
cl_info,
|
||||
THREAD);
|
||||
// Clear class_being_redefined just to be sure.
|
||||
state->clear_class_being_redefined();
|
||||
|
@ -1111,6 +1111,10 @@ void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm,
|
||||
template(java_lang_invoke_MemberName,MN_SEARCH_INTERFACES) \
|
||||
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \
|
||||
template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \
|
||||
template(java_lang_invoke_MemberName,MN_NESTMATE_CLASS) \
|
||||
template(java_lang_invoke_MemberName,MN_HIDDEN_CLASS) \
|
||||
template(java_lang_invoke_MemberName,MN_STRONG_LOADER_LINK) \
|
||||
template(java_lang_invoke_MemberName,MN_ACCESS_VM_ANNOTATIONS) \
|
||||
/*end*/
|
||||
|
||||
#define IGNORE_REQ(req_expr) /* req_expr */
|
||||
@ -1527,7 +1531,7 @@ static JNINativeMethod MHN_methods[] = {
|
||||
{CC "clearCallSiteContext", CC "(" CTX ")V", FN_PTR(MHN_clearCallSiteContext)},
|
||||
{CC "staticFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_staticFieldOffset)},
|
||||
{CC "staticFieldBase", CC "(" MEM ")" OBJ, FN_PTR(MHN_staticFieldBase)},
|
||||
{CC "getMemberVMInfo", CC "(" MEM ")" OBJ, FN_PTR(MHN_getMemberVMInfo)}
|
||||
{CC "getMemberVMInfo", CC "(" MEM ")" OBJ, FN_PTR(MHN_getMemberVMInfo)}
|
||||
};
|
||||
|
||||
static JNINativeMethod MH_methods[] = {
|
||||
|
@ -850,12 +850,19 @@ Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
|
||||
ClassFileStream st(class_bytes, class_bytes_length, host_source, ClassFileStream::verify);
|
||||
|
||||
Symbol* no_class_name = NULL;
|
||||
ClassLoadInfo cl_info(host_domain,
|
||||
InstanceKlass::cast(host_klass),
|
||||
cp_patches,
|
||||
NULL, // dynamic_nest_host
|
||||
Handle(), // classData
|
||||
false, // is_hidden
|
||||
false, // is_strong_hidden
|
||||
true); // can_access_vm_annotations
|
||||
|
||||
Klass* anonk = SystemDictionary::parse_stream(no_class_name,
|
||||
host_loader,
|
||||
host_domain,
|
||||
&st,
|
||||
InstanceKlass::cast(host_klass),
|
||||
cp_patches,
|
||||
cl_info,
|
||||
CHECK_NULL);
|
||||
if (anonk == NULL) {
|
||||
return NULL;
|
||||
|
@ -711,7 +711,7 @@ bool Reflection::is_same_class_package(const Klass* class1, const Klass* class2)
|
||||
// Checks that the 'outer' klass has declared 'inner' as being an inner klass. If not,
|
||||
// throw an incompatible class change exception
|
||||
// If inner_is_member, require the inner to be a member of the outer.
|
||||
// If !inner_is_member, require the inner to be unsafe anonymous (a non-member).
|
||||
// If !inner_is_member, require the inner to be hidden or unsafe anonymous (non-members).
|
||||
// Caller is responsible for figuring out in advance which case must be true.
|
||||
void Reflection::check_for_inner_class(const InstanceKlass* outer, const InstanceKlass* inner,
|
||||
bool inner_is_member, TRAPS) {
|
||||
|
@ -517,6 +517,7 @@ typedef HashtableEntry<InstanceKlass*, mtClass> KlassHashtableEntry;
|
||||
nonstatic_field(ClassLoaderData, _class_loader, OopHandle) \
|
||||
nonstatic_field(ClassLoaderData, _next, ClassLoaderData*) \
|
||||
volatile_nonstatic_field(ClassLoaderData, _klasses, Klass*) \
|
||||
nonstatic_field(ClassLoaderData, _has_class_mirror_holder, bool) \
|
||||
volatile_nonstatic_field(ClassLoaderData, _dictionary, Dictionary*) \
|
||||
\
|
||||
static_ptr_volatile_field(ClassLoaderDataGraph, _head, ClassLoaderData*) \
|
||||
|
@ -66,6 +66,7 @@ enum {
|
||||
JVM_ACC_IS_CLONEABLE_FAST = (int)0x80000000,// True if klass implements the Cloneable interface and can be optimized in generated code
|
||||
JVM_ACC_HAS_FINAL_METHOD = 0x01000000, // True if klass has final method
|
||||
JVM_ACC_IS_SHARED_CLASS = 0x02000000, // True if klass is shared
|
||||
JVM_ACC_IS_HIDDEN_CLASS = 0x04000000, // True if klass is hidden
|
||||
|
||||
// Klass* and Method* flags
|
||||
JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000,
|
||||
@ -149,6 +150,7 @@ class AccessFlags {
|
||||
bool has_final_method () const { return (_flags & JVM_ACC_HAS_FINAL_METHOD ) != 0; }
|
||||
bool is_cloneable_fast () const { return (_flags & JVM_ACC_IS_CLONEABLE_FAST ) != 0; }
|
||||
bool is_shared_class () const { return (_flags & JVM_ACC_IS_SHARED_CLASS ) != 0; }
|
||||
bool is_hidden_class () const { return (_flags & JVM_ACC_IS_HIDDEN_CLASS ) != 0; }
|
||||
|
||||
// Klass* and Method* flags
|
||||
bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; }
|
||||
@ -221,6 +223,7 @@ class AccessFlags {
|
||||
void set_is_cloneable_fast() { atomic_set_bits(JVM_ACC_IS_CLONEABLE_FAST); }
|
||||
void set_has_miranda_methods() { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS); }
|
||||
void set_is_shared_class() { atomic_set_bits(JVM_ACC_IS_SHARED_CLASS); }
|
||||
void set_is_hidden_class() { atomic_set_bits(JVM_ACC_IS_HIDDEN_CLASS); }
|
||||
|
||||
public:
|
||||
// field flags
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
#include "runtime/os.inline.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
#include "runtime/vm_version.hpp"
|
||||
#include "utilities/defaultStream.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
@ -368,10 +369,16 @@ void stringStream::reset() {
|
||||
zero_terminate();
|
||||
}
|
||||
|
||||
char* stringStream::as_string() const {
|
||||
char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos + 1);
|
||||
char* stringStream::as_string(bool c_heap) const {
|
||||
char* copy = c_heap ?
|
||||
NEW_C_HEAP_ARRAY(char, buffer_pos + 1, mtInternal) : NEW_RESOURCE_ARRAY(char, buffer_pos + 1);
|
||||
strncpy(copy, buffer, buffer_pos);
|
||||
copy[buffer_pos] = 0; // terminating null
|
||||
if (c_heap) {
|
||||
// Need to ensure our content is written to memory before we return
|
||||
// the pointer to it.
|
||||
OrderAccess::storestore();
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
@ -215,7 +215,8 @@ class stringStream : public outputStream {
|
||||
size_t size() const { return buffer_pos; }
|
||||
const char* base() const { return buffer; }
|
||||
void reset();
|
||||
char* as_string() const;
|
||||
// copy to a resource, or C-heap, array as requested
|
||||
char* as_string(bool c_heap = false) const;
|
||||
};
|
||||
|
||||
class fileStream : public outputStream {
|
||||
|
@ -28,6 +28,7 @@ package java.lang;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.constant.ClassDesc;
|
||||
import java.lang.invoke.TypeDescriptor;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.io.IOException;
|
||||
@ -63,8 +64,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
@ -100,16 +99,42 @@ import sun.reflect.misc.ReflectUtil;
|
||||
* keyword {@code void} are also represented as {@code Class} objects.
|
||||
*
|
||||
* <p> {@code Class} has no public constructor. Instead a {@code Class}
|
||||
* object is constructed automatically by the Java Virtual Machine
|
||||
* when a class loader invokes one of the
|
||||
* {@link ClassLoader#defineClass(String,byte[], int,int) defineClass} methods
|
||||
* and passes the bytes of a {@code class} file.
|
||||
* object is constructed automatically by the Java Virtual Machine when
|
||||
* a class is derived from the bytes of a {@code class} file through
|
||||
* the invocation of one of the following methods:
|
||||
* <ul>
|
||||
* <li> {@link ClassLoader#defineClass(String, byte[], int, int) ClassLoader::defineClass}
|
||||
* <li> {@link java.lang.invoke.MethodHandles.Lookup#defineClass(byte[])
|
||||
* java.lang.invoke.MethodHandles.Lookup::defineClass}
|
||||
* <li> {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* java.lang.invoke.MethodHandles.Lookup::defineHiddenClass}
|
||||
* </ul>
|
||||
*
|
||||
* <p> The methods of class {@code Class} expose many characteristics of a
|
||||
* class or interface. Most characteristics are derived from the {@code class}
|
||||
* file that the class loader passed to the Java Virtual Machine. A few
|
||||
* characteristics are determined by the class loading environment at run time,
|
||||
* such as the module returned by {@link #getModule() getModule()}.
|
||||
* file that the class loader passed to the Java Virtual Machine or
|
||||
* from the {@code class} file passed to {@code Lookup::defineClass}
|
||||
* or {@code Lookup::defineHiddenClass}.
|
||||
* A few characteristics are determined by the class loading environment
|
||||
* at run time, such as the module returned by {@link #getModule() getModule()}.
|
||||
*
|
||||
* <p> The following example uses a {@code Class} object to print the
|
||||
* class name of an object:
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* void printClassName(Object obj) {
|
||||
* System.out.println("The class of " + obj +
|
||||
* " is " + obj.getClass().getName());
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* It is also possible to get the {@code Class} object for a named
|
||||
* type (or for {@code void}) using a <i>class literal</i>.
|
||||
* For example:
|
||||
*
|
||||
* <blockquote>
|
||||
* {@code System.out.println("The name of class Foo is: "+Foo.class.getName());}
|
||||
* </blockquote>
|
||||
*
|
||||
* <p> Some methods of class {@code Class} expose whether the declaration of
|
||||
* a class or interface in Java source code was <em>enclosed</em> within
|
||||
@ -128,24 +153,33 @@ import sun.reflect.misc.ReflectUtil;
|
||||
* other members are the classes and interfaces whose declarations are
|
||||
* enclosed within the top-level class declaration.
|
||||
*
|
||||
* <p> The following example uses a {@code Class} object to print the
|
||||
* class name of an object:
|
||||
* <p> A class or interface created by the invocation of
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass} is a {@linkplain Class#isHidden() <em>hidden</em>}
|
||||
* class or interface.
|
||||
* All kinds of class, including enum types and record types, may be
|
||||
* hidden classes; all kinds of interface, including annotation types,
|
||||
* may be hidden interfaces.
|
||||
*
|
||||
* <blockquote><pre>
|
||||
* void printClassName(Object obj) {
|
||||
* System.out.println("The class of " + obj +
|
||||
* " is " + obj.getClass().getName());
|
||||
* }
|
||||
* </pre></blockquote>
|
||||
* The {@linkplain #getName() name of a hidden class or interface} is
|
||||
* not a <a href="ClassLoader.html#binary-name">binary name</a>,
|
||||
* which means the following:
|
||||
* <ul>
|
||||
* <li>A hidden class or interface cannot be referenced by the constant pools
|
||||
* of other classes and interfaces.
|
||||
* <li>A hidden class or interface cannot be described in
|
||||
* {@linkplain java.lang.constant.ConstantDesc <em>nominal form</em>} by
|
||||
* {@link #describeConstable() Class::describeConstable},
|
||||
* {@link ClassDesc#of(String) ClassDesc::of}, or
|
||||
* {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}.
|
||||
* <li>A hidden class or interface cannot be discovered by {@link #forName Class::forName}
|
||||
* or {@link ClassLoader#loadClass(String, boolean) ClassLoader::loadClass}.
|
||||
* </ul>
|
||||
*
|
||||
* <p> It is also possible to get the {@code Class} object for a named
|
||||
* type (or for void) using a class literal. See Section {@jls
|
||||
* 15.8.2} of <cite>The Java™ Language Specification</cite>.
|
||||
* For example:
|
||||
*
|
||||
* <blockquote>
|
||||
* {@code System.out.println("The name of class Foo is: " + Foo.class.getName());}
|
||||
* </blockquote>
|
||||
* A hidden class or interface is never an array class, but may be
|
||||
* the element type of an array. In all other respects, the fact that
|
||||
* a class or interface is hidden has no bearing on the characteristics
|
||||
* exposed by the methods of class {@code Class}.
|
||||
*
|
||||
* @param <T> the type of the class modeled by this {@code Class}
|
||||
* object. For example, the type of {@code String.class} is {@code
|
||||
@ -155,6 +189,7 @@ import sun.reflect.misc.ReflectUtil;
|
||||
* @author unascribed
|
||||
* @see java.lang.ClassLoader#defineClass(byte[], int, int)
|
||||
* @since 1.0
|
||||
* @jls 15.8.2 Class Literals
|
||||
*/
|
||||
public final class Class<T> implements java.io.Serializable,
|
||||
GenericDeclaration,
|
||||
@ -186,9 +221,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||
/**
|
||||
* Converts the object to a string. The string representation is the
|
||||
* string "class" or "interface", followed by a space, and then by the
|
||||
* fully qualified name of the class in the format returned by
|
||||
* {@code getName}. If this {@code Class} object represents a
|
||||
* primitive type, this method returns the name of the primitive type. If
|
||||
* name of the class in the format returned by {@code getName}.
|
||||
* If this {@code Class} object represents a primitive type,
|
||||
* this method returns the name of the primitive type. If
|
||||
* this {@code Class} object represents void this method returns
|
||||
* "void". If this {@code Class} object represents an array type,
|
||||
* this method returns "class " followed by {@code getName}.
|
||||
@ -745,11 +780,12 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this class is a synthetic class;
|
||||
* returns {@code false} otherwise.
|
||||
* @return {@code true} if and only if this class is a synthetic class as
|
||||
* defined by <cite>The Java™ Language Specification</cite>.
|
||||
* Returns {@code true} if and only if this class has the synthetic modifier
|
||||
* bit set.
|
||||
*
|
||||
* @return {@code true} if and only if this class has the synthetic modifier bit set
|
||||
* @jls 13.1 The Form of a Binary
|
||||
* @jvms 4.1 The {@code ClassFile} Structure
|
||||
* @since 1.5
|
||||
*/
|
||||
public boolean isSynthetic() {
|
||||
@ -758,22 +794,26 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
/**
|
||||
* Returns the name of the entity (class, interface, array class,
|
||||
* primitive type, or void) represented by this {@code Class} object,
|
||||
* as a {@code String}.
|
||||
* primitive type, or void) represented by this {@code Class} object.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a reference type that is
|
||||
* not an array type then the binary name of the class is
|
||||
* returned, as specified by <cite>The Java™ Language
|
||||
* Specification</cite>.
|
||||
* <p> If this {@code Class} object represents a class or interface,
|
||||
* not an array class, then:
|
||||
* <ul>
|
||||
* <li> If the class or interface is not {@linkplain #isHidden() hidden},
|
||||
* then the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* of the class or interface is returned.
|
||||
* <li> If the class or interface is hidden, then the result is a string
|
||||
* of the form: {@code N + '/' + <suffix>}
|
||||
* where {@code N} is the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* indicated by the {@code class} file passed to
|
||||
* {@link java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}, and {@code <suffix>} is an unqualified name.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents a primitive type or void, then the
|
||||
* name returned is a {@code String} equal to the Java language
|
||||
* keyword corresponding to the primitive type or void.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a class of arrays, then the internal
|
||||
* form of the name consists of the name of the element type preceded by
|
||||
* one or more '{@code [}' characters representing the depth of the array
|
||||
* nesting. The encoding of element type names is as follows:
|
||||
* <p> If this {@code Class} object represents an array class, then
|
||||
* the result is a string consisting of one or more '{@code [}' characters
|
||||
* representing the depth of the array nesting, followed by the element
|
||||
* type as encoded using the following table:
|
||||
*
|
||||
* <blockquote><table class="striped">
|
||||
* <caption style="display:none">Element types and encodings</caption>
|
||||
@ -781,21 +821,22 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* <tr><th scope="col"> Element Type <th scope="col"> Encoding
|
||||
* </thead>
|
||||
* <tbody style="text-align:left">
|
||||
* <tr><th scope="row"> boolean <td style="text-align:center"> Z
|
||||
* <tr><th scope="row"> byte <td style="text-align:center"> B
|
||||
* <tr><th scope="row"> char <td style="text-align:center"> C
|
||||
* <tr><th scope="row"> class or interface
|
||||
* <td style="text-align:center"> L<i>classname</i>;
|
||||
* <tr><th scope="row"> double <td style="text-align:center"> D
|
||||
* <tr><th scope="row"> float <td style="text-align:center"> F
|
||||
* <tr><th scope="row"> int <td style="text-align:center"> I
|
||||
* <tr><th scope="row"> long <td style="text-align:center"> J
|
||||
* <tr><th scope="row"> short <td style="text-align:center"> S
|
||||
* <tr><th scope="row"> {@code boolean} <td style="text-align:center"> {@code Z}
|
||||
* <tr><th scope="row"> {@code byte} <td style="text-align:center"> {@code B}
|
||||
* <tr><th scope="row"> {@code char} <td style="text-align:center"> {@code C}
|
||||
* <tr><th scope="row"> class or interface with <a href="ClassLoader.html#binary-name">binary name</a> <i>N</i>
|
||||
* <td style="text-align:center"> {@code L}<em>N</em>{@code ;}
|
||||
* <tr><th scope="row"> {@code double} <td style="text-align:center"> {@code D}
|
||||
* <tr><th scope="row"> {@code float} <td style="text-align:center"> {@code F}
|
||||
* <tr><th scope="row"> {@code int} <td style="text-align:center"> {@code I}
|
||||
* <tr><th scope="row"> {@code long} <td style="text-align:center"> {@code J}
|
||||
* <tr><th scope="row"> {@code short} <td style="text-align:center"> {@code S}
|
||||
* </tbody>
|
||||
* </table></blockquote>
|
||||
*
|
||||
* <p> The class or interface name <i>classname</i> is the binary name of
|
||||
* the class specified above.
|
||||
* <p> If this {@code Class} object represents a primitive type or {@code void},
|
||||
* then the result is a string with the same spelling as the Java language
|
||||
* keyword which corresponds to the primitive type or {@code void}.
|
||||
*
|
||||
* <p> Examples:
|
||||
* <blockquote><pre>
|
||||
@ -809,8 +850,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* returns "[[[[[[[I"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return the name of the class or interface
|
||||
* @return the name of the class, interface, or other entity
|
||||
* represented by this {@code Class} object.
|
||||
* @jls 13.1 The Form of a Binary
|
||||
*/
|
||||
public String getName() {
|
||||
String name = this.name;
|
||||
@ -888,6 +930,14 @@ public final class Class<T> implements java.io.Serializable,
|
||||
// will throw NoSuchFieldException
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
// Set by VM
|
||||
private transient Object classData;
|
||||
|
||||
// package-private
|
||||
Object getClassData() {
|
||||
return classData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@code TypeVariable} objects that represent the
|
||||
* type variables declared by the generic declaration represented by this
|
||||
@ -900,7 +950,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* @throws java.lang.reflect.GenericSignatureFormatError if the generic
|
||||
* signature of this generic declaration does not conform to
|
||||
* the format specified in section {@jvms 4.7.9} of
|
||||
* <cite>The Java™ Virtual Machine Specification</cite>,
|
||||
* <cite>The Java™ Virtual Machine Specification</cite>
|
||||
* @since 1.5
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -1023,10 +1073,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
public String getPackageName() {
|
||||
String pn = this.packageName;
|
||||
if (pn == null) {
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
if (c.isPrimitive()) {
|
||||
pn = "java.lang";
|
||||
} else {
|
||||
@ -1184,6 +1231,20 @@ public final class Class<T> implements java.io.Serializable,
|
||||
|
||||
private final Class<?> componentType;
|
||||
|
||||
/*
|
||||
* Returns the {@code Class} representing the element type of an array class.
|
||||
* If this class does not represent an array class, then this method returns
|
||||
* {@code null}.
|
||||
*/
|
||||
private Class<?> elementType() {
|
||||
if (!isArray()) return null;
|
||||
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Java language modifiers for this class or interface, encoded
|
||||
@ -1614,11 +1675,17 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical name of the underlying class as defined
|
||||
* by <cite>The Java™ Language Specification</cite>, section
|
||||
* {@jls 6.7}. Returns null if the underlying class does not have
|
||||
* a canonical name (i.e., if it is a local or anonymous class or
|
||||
* an array whose component type does not have a canonical name).
|
||||
* Returns the canonical name of the underlying class as
|
||||
* defined by <cite>The Java™ Language Specification</cite>.
|
||||
* Returns {@code null} if the underlying class does not have a canonical
|
||||
* name. Classes without canonical names include:
|
||||
* <ul>
|
||||
* <li>a {@linkplain #isLocalClass() local class}
|
||||
* <li>a {@linkplain #isAnonymousClass() anonymous class}
|
||||
* <li>a {@linkplain #isHidden() hidden class}
|
||||
* <li>an array whose component type does not have a canonical name</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return the canonical name of the underlying class if it exists, and
|
||||
* {@code null} otherwise.
|
||||
* @since 1.5
|
||||
@ -1640,7 +1707,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
else
|
||||
return ReflectionData.NULL_SENTINEL;
|
||||
}
|
||||
if (isLocalOrAnonymousClass())
|
||||
if (isHidden() || isLocalOrAnonymousClass())
|
||||
return ReflectionData.NULL_SENTINEL;
|
||||
Class<?> enclosingClass = getEnclosingClass();
|
||||
if (enclosingClass == null) { // top level class
|
||||
@ -1657,6 +1724,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* Returns {@code true} if and only if the underlying class
|
||||
* is an anonymous class.
|
||||
*
|
||||
* @apiNote
|
||||
* An anonymous class is not a {@linkplain #isHidden() hidden class}.
|
||||
*
|
||||
* @return {@code true} if and only if this class is an anonymous class.
|
||||
* @since 1.5
|
||||
*/
|
||||
@ -2882,6 +2952,11 @@ public final class Class<T> implements java.io.Serializable,
|
||||
if (sm != null) {
|
||||
sm.checkPermission(SecurityConstants.GET_PD_PERMISSION);
|
||||
}
|
||||
return protectionDomain();
|
||||
}
|
||||
|
||||
// package-private
|
||||
java.security.ProtectionDomain protectionDomain() {
|
||||
java.security.ProtectionDomain pd = getProtectionDomain0();
|
||||
if (pd == null) {
|
||||
if (allPermDomain == null) {
|
||||
@ -2896,7 +2971,6 @@ public final class Class<T> implements java.io.Serializable,
|
||||
return pd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ProtectionDomain of this class.
|
||||
*/
|
||||
@ -2968,10 +3042,7 @@ public final class Class<T> implements java.io.Serializable,
|
||||
*/
|
||||
private String resolveName(String name) {
|
||||
if (!name.startsWith("/")) {
|
||||
Class<?> c = this;
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
String baseName = c.getPackageName();
|
||||
if (baseName != null && !baseName.isEmpty()) {
|
||||
name = baseName.replace('.', '/') + "/" + name;
|
||||
@ -4042,30 +4113,23 @@ public final class Class<T> implements java.io.Serializable,
|
||||
/**
|
||||
* Returns the nest host of the <a href=#nest>nest</a> to which the class
|
||||
* or interface represented by this {@code Class} object belongs.
|
||||
* Every class and interface is a member of exactly one nest.
|
||||
* A class or interface that is not recorded as belonging to a nest
|
||||
* belongs to the nest consisting only of itself, and is the nest
|
||||
* host.
|
||||
* Every class and interface belongs to exactly one nest.
|
||||
*
|
||||
* <p>Each of the {@code Class} objects representing array types,
|
||||
* primitive types, and {@code void} returns {@code this} to indicate
|
||||
* that the represented entity belongs to the nest consisting only of
|
||||
* If the nest host of this class or interface has previously
|
||||
* been determined, then this method returns the nest host.
|
||||
* If the nest host of this class or interface has
|
||||
* not previously been determined, then this method determines the nest
|
||||
* host using the algorithm of JVMS 5.4.4, and returns it.
|
||||
*
|
||||
* Often, a class or interface belongs to a nest consisting only of itself,
|
||||
* in which case this method returns {@code this} to indicate that the class
|
||||
* or interface is the nest host.
|
||||
*
|
||||
* <p>If this {@code Class} object represents a primitive type, an array type,
|
||||
* or {@code void}, then this method returns {@code this},
|
||||
* indicating that the represented entity belongs to the nest consisting only of
|
||||
* itself, and is the nest host.
|
||||
*
|
||||
* <p>If there is a {@linkplain LinkageError linkage error} accessing
|
||||
* the nest host, or if this class or interface is not enumerated as
|
||||
* a member of the nest by the nest host, then it is considered to belong
|
||||
* to its own nest and {@code this} is returned as the host.
|
||||
*
|
||||
* @apiNote A {@code class} file of version 55.0 or greater may record the
|
||||
* host of the nest to which it belongs by using the {@code NestHost}
|
||||
* attribute (JVMS {@jvms 4.7.28}). Alternatively, a {@code class} file of
|
||||
* version 55.0 or greater may act as a nest host by enumerating the nest's
|
||||
* other members with the
|
||||
* {@code NestMembers} attribute (JVMS {@jvms 4.7.29}).
|
||||
* A {@code class} file of version 54.0 or lower does not use these
|
||||
* attributes.
|
||||
*
|
||||
* @return the nest host of this class or interface
|
||||
*
|
||||
* @throws SecurityException
|
||||
@ -4085,17 +4149,9 @@ public final class Class<T> implements java.io.Serializable,
|
||||
if (isPrimitive() || isArray()) {
|
||||
return this;
|
||||
}
|
||||
Class<?> host;
|
||||
try {
|
||||
host = getNestHost0();
|
||||
} catch (LinkageError e) {
|
||||
// if we couldn't load our nest-host then we
|
||||
// act as-if we have no nest-host attribute
|
||||
return this;
|
||||
}
|
||||
// if null then nest membership validation failed, so we
|
||||
// act as-if we have no nest-host attribute
|
||||
if (host == null || host == this) {
|
||||
|
||||
Class<?> host = getNestHost0();
|
||||
if (host == this) {
|
||||
return this;
|
||||
}
|
||||
// returning a different class requires a security check
|
||||
@ -4127,11 +4183,8 @@ public final class Class<T> implements java.io.Serializable,
|
||||
c.isPrimitive() || c.isArray()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return getNestHost0() == c.getNestHost0();
|
||||
} catch (LinkageError e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getNestHost() == c.getNestHost();
|
||||
}
|
||||
|
||||
private native Class<?>[] getNestMembers0();
|
||||
@ -4140,39 +4193,47 @@ public final class Class<T> implements java.io.Serializable,
|
||||
* Returns an array containing {@code Class} objects representing all the
|
||||
* classes and interfaces that are members of the nest to which the class
|
||||
* or interface represented by this {@code Class} object belongs.
|
||||
* The {@linkplain #getNestHost() nest host} of that nest is the zeroth
|
||||
* element of the array. Subsequent elements represent any classes or
|
||||
* interfaces that are recorded by the nest host as being members of
|
||||
* the nest; the order of such elements is unspecified. Duplicates are
|
||||
* permitted.
|
||||
* If the nest host of that nest does not enumerate any members, then the
|
||||
* array has a single element containing {@code this}.
|
||||
*
|
||||
* <p>Each of the {@code Class} objects representing array types,
|
||||
* primitive types, and {@code void} returns an array containing only
|
||||
* First, this method obtains the {@linkplain #getNestHost() nest host},
|
||||
* {@code H}, of the nest to which the class or interface represented by
|
||||
* this {@code Class} object belongs. The zeroth element of the returned
|
||||
* array is {@code H}.
|
||||
*
|
||||
* Then, for each class or interface {@code C} which is recorded by {@code H}
|
||||
* as being a member of its nest, this method attempts to obtain the {@code Class}
|
||||
* object for {@code C} (using {@linkplain #getClassLoader() the defining class
|
||||
* loader} of the current {@code Class} object), and then obtains the
|
||||
* {@linkplain #getNestHost() nest host} of the nest to which {@code C} belongs.
|
||||
* The classes and interfaces which are recorded by {@code H} as being members
|
||||
* of its nest, and for which {@code H} can be determined as their nest host,
|
||||
* are indicated by subsequent elements of the returned array. The order of
|
||||
* such elements is unspecified. Duplicates are permitted.
|
||||
*
|
||||
* <p>If this {@code Class} object represents a primitive type, an array type,
|
||||
* or {@code void}, then this method returns a single-element array containing
|
||||
* {@code this}.
|
||||
*
|
||||
* <p>This method validates that, for each class or interface which is
|
||||
* recorded as a member of the nest by the nest host, that class or
|
||||
* interface records itself as a member of that same nest. Any exceptions
|
||||
* that occur during this validation are rethrown by this method.
|
||||
* @apiNote
|
||||
* The returned array includes only the nest members recorded in the {@code NestMembers}
|
||||
* attribute, and not any hidden classes that were added to the nest via
|
||||
* {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}.
|
||||
*
|
||||
* @return an array of all classes and interfaces in the same nest as
|
||||
* this class
|
||||
* this class or interface
|
||||
*
|
||||
* @throws LinkageError
|
||||
* If there is any problem loading or validating a nest member or
|
||||
* its nest host
|
||||
* @throws SecurityException
|
||||
* If any returned class is not the current class, and
|
||||
* if a security manager, <i>s</i>, is present and the caller's
|
||||
* class loader is not the same as or an ancestor of the class
|
||||
* loader for that returned class and invocation of {@link
|
||||
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
|
||||
* denies access to the package of that returned class
|
||||
* If any returned class is not the current class, and
|
||||
* if a security manager, <i>s</i>, is present and the caller's
|
||||
* class loader is not the same as or an ancestor of the class
|
||||
* loader for that returned class and invocation of {@link
|
||||
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
|
||||
* denies access to the package of that returned class
|
||||
*
|
||||
* @since 11
|
||||
* @see #getNestHost()
|
||||
* @jvms 4.7.28 The {@code NestHost} Attribute
|
||||
* @jvms 4.7.29 The {@code NestMembers} Attribute
|
||||
*/
|
||||
@CallerSensitive
|
||||
public Class<?>[] getNestMembers() {
|
||||
@ -4196,13 +4257,57 @@ public final class Class<T> implements java.io.Serializable,
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type descriptor string for this class.
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #forName};
|
||||
* Returns the descriptor string of the entity (class, interface, array class,
|
||||
* primitive type, or {@code void}) represented by this {@code Class} object.
|
||||
*
|
||||
* <p> If this {@code Class} object represents a class or interface,
|
||||
* not an array class, then:
|
||||
* <ul>
|
||||
* <li> If the class or interface is not {@linkplain Class#isHidden() hidden},
|
||||
* then the result is a field descriptor (JVMS {@jvms 4.3.2})
|
||||
* for the class or interface. Calling
|
||||
* {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
|
||||
* with the result descriptor string produces a {@link ClassDesc ClassDesc}
|
||||
* describing this class or interface.
|
||||
* <li> If the class or interface is {@linkplain Class#isHidden() hidden},
|
||||
* then the result is a string of the form:
|
||||
* <blockquote>
|
||||
* {@code "L" +} <em>N</em> {@code + "." + <suffix> + ";"}
|
||||
* </blockquote>
|
||||
* where <em>N</em> is the <a href="ClassLoader.html#binary-name">binary name</a>
|
||||
* encoded in internal form indicated by the {@code class} file passed to
|
||||
* {@link MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClass}, and {@code <suffix>} is an unqualified name.
|
||||
* A hidden class or interface has no {@linkplain ClassDesc nominal descriptor}.
|
||||
* The result string is not a type descriptor.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents an array class, then
|
||||
* the result is a string consisting of one or more '{@code [}' characters
|
||||
* representing the depth of the array nesting, followed by the
|
||||
* descriptor string of the element type.
|
||||
* <ul>
|
||||
* <li> If the element type is not a {@linkplain Class#isHidden() hidden} class
|
||||
* or interface, then this array class can be described nominally.
|
||||
* Calling {@link ClassDesc#ofDescriptor(String) ClassDesc::ofDescriptor}
|
||||
* with the result descriptor string produces a {@link ClassDesc ClassDesc}
|
||||
* describing this array class.
|
||||
* <li> If the element type is a {@linkplain Class#isHidden() hidden} class or
|
||||
* interface, then this array class cannot be described nominally.
|
||||
* The result string is not a type descriptor.
|
||||
* </ul>
|
||||
*
|
||||
* <p> If this {@code Class} object represents a primitive type or
|
||||
* {@code void}, then the result is a field descriptor string which
|
||||
* is a one-letter code corresponding to a primitive type or {@code void}
|
||||
* ({@code "B", "C", "D", "F", "I", "J", "S", "Z", "V"}) (JVMS {@jvms 4.3.2}).
|
||||
*
|
||||
* @apiNote
|
||||
* This is not a strict inverse of {@link #forName};
|
||||
* distinct classes which share a common name but have different class loaders
|
||||
* will have identical descriptor strings.
|
||||
*
|
||||
* @return the type descriptor representation
|
||||
* @return the descriptor string for this {@code Class} object
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @since 12
|
||||
*/
|
||||
@ -4210,10 +4315,15 @@ public final class Class<T> implements java.io.Serializable,
|
||||
public String descriptorString() {
|
||||
if (isPrimitive())
|
||||
return Wrapper.forPrimitiveType(this).basicTypeString();
|
||||
else if (isArray()) {
|
||||
|
||||
if (isArray()) {
|
||||
return "[" + componentType.descriptorString();
|
||||
}
|
||||
else {
|
||||
} else if (isHidden()) {
|
||||
String name = getName();
|
||||
int index = name.indexOf('/');
|
||||
return "L" + name.substring(0, index).replace('.', '/')
|
||||
+ "." + name.substring(index+1) + ";";
|
||||
} else {
|
||||
return "L" + getName().replace('.', '/') + ";";
|
||||
}
|
||||
}
|
||||
@ -4256,6 +4366,20 @@ public final class Class<T> implements java.io.Serializable,
|
||||
*/
|
||||
@Override
|
||||
public Optional<ClassDesc> describeConstable() {
|
||||
return Optional.of(ClassDesc.ofDescriptor(descriptorString()));
|
||||
}
|
||||
Class<?> c = isArray() ? elementType() : this;
|
||||
return c.isHidden() ? Optional.empty()
|
||||
: Optional.of(ClassDesc.ofDescriptor(descriptorString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if and only if the underlying class is a hidden class.
|
||||
*
|
||||
* @return {@code true} if and only if this class is a hidden class.
|
||||
*
|
||||
* @since 15
|
||||
* @see MethodHandles.Lookup#defineHiddenClass
|
||||
*/
|
||||
@HotSpotIntrinsicCandidate
|
||||
public native boolean isHidden();
|
||||
|
||||
}
|
||||
|
@ -1115,6 +1115,29 @@ public abstract class ClassLoader {
|
||||
int off, int len, ProtectionDomain pd,
|
||||
String source);
|
||||
|
||||
/**
|
||||
* Defines a class of the given flags via Lookup.defineClass.
|
||||
*
|
||||
* @param loader the defining loader
|
||||
* @param lookup nest host of the Class to be defined
|
||||
* @param name the binary name or {@code null} if not findable
|
||||
* @param b class bytes
|
||||
* @param off the start offset in {@code b} of the class bytes
|
||||
* @param len the length of the class bytes
|
||||
* @param pd protection domain
|
||||
* @param initialize initialize the class
|
||||
* @param flags flags
|
||||
* @param classData class data
|
||||
*/
|
||||
static native Class<?> defineClass0(ClassLoader loader,
|
||||
Class<?> lookup,
|
||||
String name,
|
||||
byte[] b, int off, int len,
|
||||
ProtectionDomain pd,
|
||||
boolean initialize,
|
||||
int flags,
|
||||
Object classData);
|
||||
|
||||
// true if the name is null or has the potential to be a valid binary name
|
||||
private boolean checkName(String name) {
|
||||
if ((name == null) || (name.isEmpty()))
|
||||
|
@ -28,6 +28,10 @@ package java.lang;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
/**
|
||||
* Helper for string concatenation. These methods are mostly looked up with private lookups
|
||||
* from {@link java.lang.invoke.StringConcatFactory}, and used in {@link java.lang.invoke.MethodHandle}
|
||||
@ -466,4 +470,13 @@ final class StringConcatHelper {
|
||||
return String.COMPACT_STRINGS ? LATIN1 : UTF16;
|
||||
}
|
||||
|
||||
static MethodHandle lookupStatic(String name, MethodType methodType) {
|
||||
try {
|
||||
return MethodHandles.lookup().findStatic(StringConcatHelper.class, name, methodType);
|
||||
} catch (NoSuchMethodException|IllegalAccessException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,8 @@ import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Executable;
|
||||
@ -2175,6 +2177,10 @@ public final class System {
|
||||
public Class<?> defineClass(ClassLoader loader, String name, byte[] b, ProtectionDomain pd, String source) {
|
||||
return ClassLoader.defineClass1(loader, name, b, 0, b.length, pd, source);
|
||||
}
|
||||
public Class<?> defineClass(ClassLoader loader, Class<?> lookup, String name, byte[] b, ProtectionDomain pd,
|
||||
boolean initialize, int flags, Object classData) {
|
||||
return ClassLoader.defineClass0(loader, lookup, name, b, 0, b.length, pd, initialize, flags, classData);
|
||||
}
|
||||
public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
|
||||
return cl.findBootstrapClassOrNull(name);
|
||||
}
|
||||
@ -2257,6 +2263,18 @@ public final class System {
|
||||
public void setCause(Throwable t, Throwable cause) {
|
||||
t.setCause(cause);
|
||||
}
|
||||
|
||||
public ProtectionDomain protectionDomain(Class<?> c) {
|
||||
return c.protectionDomain();
|
||||
}
|
||||
|
||||
public MethodHandle stringConcatHelper(String name, MethodType methodType) {
|
||||
return StringConcatHelper.lookupStatic(name, methodType);
|
||||
}
|
||||
|
||||
public Object classData(Class<?> c) {
|
||||
return c.getClassData();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
* System.out.printf(">>> %s\n", iii.foo(44));
|
||||
* }}
|
||||
*/
|
||||
final MethodHandles.Lookup caller; // The caller's lookup context
|
||||
final Class<?> targetClass; // The class calling the meta-factory via invokedynamic "class X"
|
||||
final MethodType invokedType; // The type of the invoked method "(CC)II"
|
||||
final Class<?> samBase; // The type of the returned instance "interface JJ"
|
||||
@ -120,6 +121,7 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
"Invalid caller: %s",
|
||||
caller.lookupClass().getName()));
|
||||
}
|
||||
this.caller = caller;
|
||||
this.targetClass = caller.lookupClass();
|
||||
this.invokedType = invokedType;
|
||||
|
||||
@ -143,8 +145,20 @@ import static sun.invoke.util.Wrapper.isWrapperType;
|
||||
case REF_invokeSpecial:
|
||||
// JDK-8172817: should use referenced class here, but we don't know what it was
|
||||
this.implClass = implInfo.getDeclaringClass();
|
||||
this.implKind = REF_invokeSpecial;
|
||||
this.implIsInstanceMethod = true;
|
||||
|
||||
// Classes compiled prior to dynamic nestmate support invokes a private instance
|
||||
// method with REF_invokeSpecial.
|
||||
//
|
||||
// invokespecial should only be used to invoke private nestmate constructors.
|
||||
// The lambda proxy class will be defined as a nestmate of targetClass.
|
||||
// If the method to be invoked is an instance method of targetClass, then
|
||||
// convert to use invokevirtual or invokeinterface.
|
||||
if (targetClass == implClass && !implInfo.getName().equals("<init>")) {
|
||||
this.implKind = implClass.isInterface() ? REF_invokeInterface : REF_invokeVirtual;
|
||||
} else {
|
||||
this.implKind = REF_invokeSpecial;
|
||||
}
|
||||
break;
|
||||
case REF_invokeStatic:
|
||||
case REF_newInvokeSpecial:
|
||||
|
@ -196,24 +196,20 @@ class GenerateJLIClassesHelper {
|
||||
private static byte[] generateCodeBytesForLFs(String className,
|
||||
String[] names, LambdaForm[] forms) {
|
||||
|
||||
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(Opcodes.V1_8, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
className, null, InvokerBytecodeGenerator.INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null);
|
||||
|
||||
for (int i = 0; i < forms.length; i++) {
|
||||
addMethod(className, names[i], forms[i],
|
||||
forms[i].methodType(), cw);
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, names[i], forms[i], forms[i].methodType());
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
}
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static void addMethod(String className, String methodName, LambdaForm form,
|
||||
MethodType type, ClassWriter cw) {
|
||||
InvokerBytecodeGenerator g
|
||||
= new InvokerBytecodeGenerator(className, methodName, form, type);
|
||||
g.setClassWriter(cw);
|
||||
g.addMethod();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private static LambdaForm makeReinvokerFor(MethodType type) {
|
||||
|
@ -27,13 +27,14 @@ package java.lang.invoke;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.*;
|
||||
import sun.invoke.util.BytecodeDescriptor;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
|
||||
import java.io.FilePermission;
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.LinkedHashSet;
|
||||
@ -41,6 +42,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.STRONG;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -50,13 +53,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
* @see LambdaMetafactory
|
||||
*/
|
||||
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
||||
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private static final int CLASSFILE_VERSION = 52;
|
||||
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
||||
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
||||
private static final String NAME_CTOR = "<init>";
|
||||
private static final String NAME_FACTORY = "get$Lambda";
|
||||
|
||||
//Serialization support
|
||||
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
||||
@ -64,13 +64,17 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
||||
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
|
||||
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
|
||||
private static final String DESCR_SET_IMPL_METHOD = "(Ljava/lang/invoke/MethodHandle;)V";
|
||||
|
||||
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
||||
private static final String NAME_METHOD_READ_OBJECT = "readObject";
|
||||
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
|
||||
private static final String NAME_FIELD_IMPL_METHOD = "protectedImplMethod";
|
||||
|
||||
private static final String DESCR_CLASS = "Ljava/lang/Class;";
|
||||
private static final String DESCR_STRING = "Ljava/lang/String;";
|
||||
private static final String DESCR_OBJECT = "Ljava/lang/Object;";
|
||||
private static final String DESCR_METHOD_HANDLE = "Ljava/lang/invoke/MethodHandle;";
|
||||
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
||||
= "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
|
||||
+ DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
|
||||
@ -78,8 +82,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
|
||||
private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
|
||||
|
||||
private static final String DESCR_HIDDEN = "Ljdk/internal/vm/annotation/Hidden;";
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
// Used to ensure that each spun class name is unique
|
||||
@ -108,6 +110,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
private final String[] argNames; // Generated names for the constructor arguments
|
||||
private final String[] argDescs; // Type descriptors for the constructor arguments
|
||||
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
|
||||
private final boolean useImplMethodHandle; // use MethodHandle invocation instead of symbolic bytecode invocation
|
||||
|
||||
/**
|
||||
* General meta-factory constructor, supporting both standard cases and
|
||||
@ -163,7 +166,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
implMethodName = implInfo.getName();
|
||||
implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
|
||||
constructorType = invokedType.changeReturnType(Void.TYPE);
|
||||
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||
lambdaClassName = lambdaClassName(targetClass);
|
||||
useImplMethodHandle = !implClass.getPackageName().equals(implInfo.getDeclaringClass().getPackageName())
|
||||
&& !Modifier.isPublic(implInfo.getModifiers());
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
if (parameterCount > 0) {
|
||||
@ -178,6 +183,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
}
|
||||
}
|
||||
|
||||
private static String lambdaClassName(Class<?> targetClass) {
|
||||
String name = targetClass.getName();
|
||||
if (targetClass.isHidden()) {
|
||||
// use the original class name
|
||||
name = name.replace('/', '_');
|
||||
}
|
||||
return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CallSite. Generate a class file which implements the functional
|
||||
* interface, define the class, if there are no parameters create an instance
|
||||
@ -217,20 +231,14 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
try {
|
||||
Object inst = ctrs[0].newInstance();
|
||||
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (!disableEagerInitialization) {
|
||||
UNSAFE.ensureClassInitialized(innerClass);
|
||||
}
|
||||
return new ConstantCallSite(
|
||||
MethodHandles.Lookup.IMPL_LOOKUP
|
||||
.findStatic(innerClass, NAME_FACTORY, invokedType));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
|
||||
return new ConstantCallSite(mh.asType(invokedType));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new LambdaConversionException("Exception finding constructor", e);
|
||||
}
|
||||
}
|
||||
@ -283,14 +291,9 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
generateConstructor();
|
||||
|
||||
if (invokedType.parameterCount() != 0 || disableEagerInitialization) {
|
||||
generateFactory();
|
||||
}
|
||||
|
||||
// Forward the SAM method
|
||||
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
|
||||
samMethodType.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation(DESCR_HIDDEN, true);
|
||||
new ForwardingMethodGenerator(mv).generate(samMethodType);
|
||||
|
||||
// Forward the bridges
|
||||
@ -298,11 +301,18 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
for (MethodType mt : additionalBridges) {
|
||||
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
|
||||
mt.toMethodDescriptorString(), null, null);
|
||||
mv.visitAnnotation(DESCR_HIDDEN, true);
|
||||
new ForwardingMethodGenerator(mv).generate(mt);
|
||||
}
|
||||
}
|
||||
|
||||
if (useImplMethodHandle) {
|
||||
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_STATIC,
|
||||
NAME_FIELD_IMPL_METHOD,
|
||||
DESCR_METHOD_HANDLE,
|
||||
null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
if (isSerializable)
|
||||
generateSerializationFriendlyMethods();
|
||||
else if (accidentallySerializable)
|
||||
@ -313,7 +323,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
// Define the generated class in this VM.
|
||||
|
||||
final byte[] classBytes = cw.toByteArray();
|
||||
|
||||
// If requested, dump out to a file for debugging purposes
|
||||
if (dumper != null) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
@ -327,28 +336,26 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
// createDirectories may need it
|
||||
new PropertyPermission("user.dir", "read"));
|
||||
}
|
||||
|
||||
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the factory method for the class
|
||||
*/
|
||||
private void generateFactory() {
|
||||
MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
|
||||
m.visitCode();
|
||||
m.visitTypeInsn(NEW, lambdaClassName);
|
||||
m.visitInsn(Opcodes.DUP);
|
||||
int parameterCount = invokedType.parameterCount();
|
||||
for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
|
||||
Class<?> argType = invokedType.parameterType(typeIndex);
|
||||
m.visitVarInsn(getLoadOpcode(argType), varIndex);
|
||||
varIndex += getParameterSize(argType);
|
||||
try {
|
||||
// this class is linked at the indy callsite; so define a hidden nestmate
|
||||
Lookup lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
|
||||
if (useImplMethodHandle) {
|
||||
// If the target class invokes a method reference this::m which is
|
||||
// resolved to a protected method inherited from a superclass in a different
|
||||
// package, the target class does not have a bridge and this method reference
|
||||
// has been changed from public to protected after the target class was compiled.
|
||||
// This lambda proxy class has no access to the resolved method.
|
||||
// So this workaround by passing the live implMethod method handle
|
||||
// to the proxy class to invoke directly.
|
||||
MethodHandle mh = lookup.findStaticSetter(lookup.lookupClass(), NAME_FIELD_IMPL_METHOD, MethodHandle.class);
|
||||
mh.invokeExact(implMethod);
|
||||
}
|
||||
return lookup.lookupClass();
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new LambdaConversionException("Exception defining lambda proxy class", e);
|
||||
} catch (Throwable t) {
|
||||
throw new InternalError(t);
|
||||
}
|
||||
m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
|
||||
m.visitInsn(ARETURN);
|
||||
m.visitMaxs(-1, -1);
|
||||
m.visitEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -464,6 +471,10 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
visitTypeInsn(NEW, implMethodClassName);
|
||||
visitInsn(DUP);
|
||||
}
|
||||
if (useImplMethodHandle) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
visitFieldInsn(GETSTATIC, lambdaClassName, NAME_FIELD_IMPL_METHOD, DESCR_METHOD_HANDLE);
|
||||
}
|
||||
for (int i = 0; i < argNames.length; i++) {
|
||||
visitVarInsn(ALOAD, 0);
|
||||
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
||||
@ -471,11 +482,16 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
convertArgumentTypes(methodType);
|
||||
|
||||
// Invoke the method we want to forward to
|
||||
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
||||
implMethodName, implMethodDesc,
|
||||
implClass.isInterface());
|
||||
|
||||
if (useImplMethodHandle) {
|
||||
MethodType mtype = implInfo.getMethodType().insertParameterTypes(0, implClass);
|
||||
visitMethodInsn(INVOKEVIRTUAL, "java/lang/invoke/MethodHandle",
|
||||
"invokeExact", mtype.descriptorString(), false);
|
||||
} else {
|
||||
// Invoke the method we want to forward to
|
||||
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
||||
implMethodName, implMethodDesc,
|
||||
implClass.isInterface());
|
||||
}
|
||||
// Convert the return value (if any) and return it
|
||||
// Note: if adapting from non-void to void, the 'return'
|
||||
// instruction will pop the unneeded result
|
||||
|
@ -26,6 +26,7 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.FieldVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
@ -42,6 +43,7 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.BasicType;
|
||||
@ -49,6 +51,7 @@ import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
|
||||
/**
|
||||
* Code generation backend for LambdaForm.
|
||||
@ -67,6 +70,8 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
private static final String LOOP_CLAUSES = MHI + "$LoopClauses";
|
||||
private static final String MHARY2 = "[[L" + MH + ";";
|
||||
private static final String MH_SIG = "L" + MH + ";";
|
||||
|
||||
|
||||
private static final String LF_SIG = "L" + LF + ";";
|
||||
private static final String LFN_SIG = "L" + LFN + ";";
|
||||
@ -92,6 +97,7 @@ class InvokerBytecodeGenerator {
|
||||
/** ASM bytecode generation. */
|
||||
private ClassWriter cw;
|
||||
private MethodVisitor mv;
|
||||
private final List<ClassData> classData = new ArrayList<>();
|
||||
|
||||
/** Single element internal class name lookup cache. */
|
||||
private Class<?> lastClass;
|
||||
@ -99,6 +105,15 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
private static final MemberName.Factory MEMBERNAME_FACTORY = MemberName.getFactory();
|
||||
private static final Class<?> HOST_CLASS = LambdaForm.class;
|
||||
private static final MethodHandles.Lookup LOOKUP = lookup();
|
||||
|
||||
private static MethodHandles.Lookup lookup() {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(HOST_CLASS, IMPL_LOOKUP);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Main constructor; other constructors delegate to this one. */
|
||||
private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
|
||||
@ -221,41 +236,52 @@ class InvokerBytecodeGenerator {
|
||||
return className;
|
||||
}
|
||||
|
||||
class CpPatch {
|
||||
final int index;
|
||||
public static class ClassData {
|
||||
final String name;
|
||||
final String desc;
|
||||
final Object value;
|
||||
CpPatch(int index, Object value) {
|
||||
this.index = index;
|
||||
|
||||
ClassData(String name, String desc, Object value) {
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String name() { return name; }
|
||||
public String toString() {
|
||||
return "CpPatch/index="+index+",value="+value;
|
||||
return name + ",value="+value;
|
||||
}
|
||||
}
|
||||
|
||||
private final ArrayList<CpPatch> cpPatches = new ArrayList<>();
|
||||
String classData(Object arg) {
|
||||
String desc;
|
||||
if (arg instanceof Class) {
|
||||
desc = "Ljava/lang/Class;";
|
||||
} else if (arg instanceof MethodHandle) {
|
||||
desc = MH_SIG;
|
||||
} else if (arg instanceof LambdaForm) {
|
||||
desc = LF_SIG;
|
||||
} else {
|
||||
desc = "Ljava/lang/Object;";
|
||||
}
|
||||
|
||||
private int cph = 0; // for counting constant placeholders
|
||||
|
||||
String constantPlaceholder(Object arg) {
|
||||
String cpPlaceholder = "CONSTANT_PLACEHOLDER_" + cph++;
|
||||
if (DUMP_CLASS_FILES) cpPlaceholder += " <<" + debugString(arg) + ">>";
|
||||
// TODO check if arg is already in the constant pool
|
||||
// insert placeholder in CP and remember the patch
|
||||
int index = cw.newConst((Object) cpPlaceholder);
|
||||
cpPatches.add(new CpPatch(index, arg));
|
||||
return cpPlaceholder;
|
||||
Class<?> c = arg.getClass();
|
||||
while (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
// unique static variable name
|
||||
String name = "_DATA_" + c.getSimpleName() + "_" + classData.size();
|
||||
ClassData cd = new ClassData(name, desc, arg);
|
||||
classData.add(cd);
|
||||
return cd.name();
|
||||
}
|
||||
|
||||
Object[] cpPatches(byte[] classFile) {
|
||||
int size = getConstantPoolSize(classFile);
|
||||
Object[] res = new Object[size];
|
||||
for (CpPatch p : cpPatches) {
|
||||
if (p.index >= size)
|
||||
throw new InternalError("in cpool["+size+"]: "+p+"\n"+Arrays.toString(Arrays.copyOf(classFile, 20)));
|
||||
res[p.index] = p.value;
|
||||
List<Object> classDataValues() {
|
||||
Object[] data = new Object[classData.size()];
|
||||
for (int i = 0; i < classData.size(); i++) {
|
||||
data[i] = classData.get(i).value;
|
||||
}
|
||||
return res;
|
||||
return List.of(data);
|
||||
}
|
||||
|
||||
private static String debugString(Object arg) {
|
||||
@ -288,19 +314,11 @@ class InvokerBytecodeGenerator {
|
||||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = loadAndInitializeInvokerClass(classFile, cpPatches(classFile));
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(classFile)
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define a given class as anonymous class in the runtime system.
|
||||
*/
|
||||
private static Class<?> loadAndInitializeInvokerClass(byte[] classBytes, Object[] patches) {
|
||||
Class<?> invokerClass = UNSAFE.defineAnonymousClass(HOST_CLASS, classBytes, patches);
|
||||
UNSAFE.ensureClassInitialized(invokerClass); // Make sure the class is initialized; VM might complain.
|
||||
return invokerClass;
|
||||
}
|
||||
|
||||
private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
|
||||
MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
|
||||
try {
|
||||
@ -316,7 +334,8 @@ class InvokerBytecodeGenerator {
|
||||
*/
|
||||
private ClassWriter classFilePrologue() {
|
||||
final int NOT_ACC_PUBLIC = 0; // not ACC_PUBLIC
|
||||
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
|
||||
setClassWriter(cw);
|
||||
cw.visit(Opcodes.V1_8, NOT_ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
|
||||
CLASS_PREFIX + className, null, INVOKER_SUPER_NAME, null);
|
||||
cw.visitSource(SOURCE_PREFIX + className, null);
|
||||
@ -336,6 +355,51 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
private String className() {
|
||||
return CLASS_PREFIX + className;
|
||||
}
|
||||
|
||||
private void clinit() {
|
||||
clinit(cw, className(), classData);
|
||||
}
|
||||
|
||||
/*
|
||||
* <clinit> to initialize the static final fields with the live class data
|
||||
* LambdaForms can't use condy due to bootstrapping issue.
|
||||
*/
|
||||
static void clinit(ClassWriter cw, String className, List<ClassData> classData) {
|
||||
if (classData.isEmpty())
|
||||
return;
|
||||
|
||||
for (ClassData p : classData) {
|
||||
// add the static field
|
||||
FieldVisitor fv = cw.visitField(Opcodes.ACC_STATIC|Opcodes.ACC_FINAL, p.name, p.desc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
|
||||
MethodVisitor mv = cw.visitMethod(Opcodes.ACC_STATIC, "<clinit>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitLdcInsn(Type.getType("L" + className + ";"));
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/invoke/MethodHandleNatives",
|
||||
"classData", "(Ljava/lang/Class;)Ljava/lang/Object;", false);
|
||||
// we should optimize one single element case that does not need to create a List
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, "java/util/List");
|
||||
mv.visitVarInsn(Opcodes.ASTORE, 0);
|
||||
int index = 0;
|
||||
for (ClassData p : classData) {
|
||||
// initialize the static field
|
||||
mv.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
emitIconstInsn(mv, index++);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List",
|
||||
"get", "(I)Ljava/lang/Object;", true);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, p.desc.substring(1, p.desc.length()-1));
|
||||
mv.visitFieldInsn(Opcodes.PUTSTATIC, className, p.name, p.desc);
|
||||
}
|
||||
mv.visitInsn(Opcodes.RETURN);
|
||||
mv.visitMaxs(2, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
/*
|
||||
* Low-level emit helpers.
|
||||
*/
|
||||
@ -408,6 +472,10 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
|
||||
private void emitIconstInsn(final int cst) {
|
||||
emitIconstInsn(mv, cst);
|
||||
}
|
||||
|
||||
private static void emitIconstInsn(MethodVisitor mv, int cst) {
|
||||
if (cst >= -1 && cst <= 5) {
|
||||
mv.visitInsn(Opcodes.ICONST_0 + cst);
|
||||
} else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
|
||||
@ -577,8 +645,7 @@ class InvokerBytecodeGenerator {
|
||||
String sig = getInternalName(cls);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, sig);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(cls));
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, CLS);
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(cls), "Ljava/lang/Class;");
|
||||
mv.visitInsn(Opcodes.SWAP);
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CLS, "cast", LL_SIG, false);
|
||||
if (Object[].class.isAssignableFrom(cls))
|
||||
@ -737,6 +804,7 @@ class InvokerBytecodeGenerator {
|
||||
private byte[] generateCustomizedCodeBytes() {
|
||||
classFilePrologue();
|
||||
addMethod();
|
||||
clinit();
|
||||
bogusMethod(lambdaForm);
|
||||
|
||||
final byte[] classFile = toByteArray();
|
||||
@ -764,14 +832,14 @@ class InvokerBytecodeGenerator {
|
||||
mv.visitAnnotation(DONTINLINE_SIG, true);
|
||||
}
|
||||
|
||||
constantPlaceholder(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
|
||||
classData(lambdaForm); // keep LambdaForm instance & its compiled form lifetime tightly coupled.
|
||||
|
||||
if (lambdaForm.customized != null) {
|
||||
// Since LambdaForm is customized for a particular MethodHandle, it's safe to substitute
|
||||
// receiver MethodHandle (at slot #0) with an embedded constant and use it instead.
|
||||
// It enables more efficient code generation in some situations, since embedded constants
|
||||
// are compile-time constants for JIT compiler.
|
||||
mv.visitLdcInsn(constantPlaceholder(lambdaForm.customized));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(lambdaForm.customized), MH_SIG);
|
||||
mv.visitTypeInsn(Opcodes.CHECKCAST, MH);
|
||||
assert(checkActualReceiver()); // expects MethodHandle on top of the stack
|
||||
mv.visitVarInsn(Opcodes.ASTORE, localsMap[0]);
|
||||
@ -901,7 +969,7 @@ class InvokerBytecodeGenerator {
|
||||
// push receiver
|
||||
MethodHandle target = name.function.resolvedHandle();
|
||||
assert(target != null) : name.exprString();
|
||||
mv.visitLdcInsn(constantPlaceholder(target));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(target), MH_SIG);
|
||||
emitReferenceCast(MethodHandle.class, target);
|
||||
} else {
|
||||
// load receiver
|
||||
@ -957,7 +1025,9 @@ class InvokerBytecodeGenerator {
|
||||
return false; // inner class of some sort
|
||||
if (cls.getClassLoader() != MethodHandle.class.getClassLoader())
|
||||
return false; // not on BCP
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
|
||||
if (cls.isHidden())
|
||||
return false;
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
|
||||
return false;
|
||||
if (!isStaticallyInvocableType(member.getMethodOrFieldType()))
|
||||
return false;
|
||||
@ -981,14 +1051,16 @@ class InvokerBytecodeGenerator {
|
||||
if (cls == Object.class)
|
||||
return true;
|
||||
if (MethodHandle.class.isAssignableFrom(cls)) {
|
||||
assert(!ReflectUtil.isVMAnonymousClass(cls));
|
||||
assert(!cls.isHidden());
|
||||
return true;
|
||||
}
|
||||
while (cls.isArray())
|
||||
cls = cls.getComponentType();
|
||||
if (cls.isPrimitive())
|
||||
return true; // int[].class, for example
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: switch to supported API once it is added
|
||||
if (cls.isHidden())
|
||||
return false;
|
||||
if (ReflectUtil.isVMAnonymousClass(cls)) // FIXME: Unsafe::defineAnonymousClass to be removed
|
||||
return false;
|
||||
// could use VerifyAccess.isClassAccessible but the following is a safe approximation
|
||||
if (cls.getClassLoader() != Object.class.getClassLoader())
|
||||
@ -1060,7 +1132,7 @@ class InvokerBytecodeGenerator {
|
||||
}
|
||||
assert(java.lang.reflect.Array.getLength(emptyArray) == 0);
|
||||
assert(emptyArray.getClass() == rtype); // exact typing
|
||||
mv.visitLdcInsn(constantPlaceholder(emptyArray));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(emptyArray), "Ljava/lang/Object;");
|
||||
emitReferenceCast(rtype, emptyArray);
|
||||
return;
|
||||
}
|
||||
@ -1623,7 +1695,7 @@ class InvokerBytecodeGenerator {
|
||||
if (Wrapper.isWrapperType(arg.getClass()) && bptype != L_TYPE) {
|
||||
emitConst(arg);
|
||||
} else {
|
||||
mv.visitLdcInsn(constantPlaceholder(arg));
|
||||
mv.visitFieldInsn(Opcodes.GETSTATIC, className(), classData(arg), "Ljava/lang/Object;");
|
||||
emitImplicitConversion(L_TYPE, ptype, arg);
|
||||
}
|
||||
}
|
||||
@ -1815,6 +1887,7 @@ class InvokerBytecodeGenerator {
|
||||
emitReturnInsn(basicType(rtype));
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
bogusMethod(invokerType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
@ -1883,6 +1956,7 @@ class InvokerBytecodeGenerator {
|
||||
emitReturnInsn(L_TYPE); // NOTE: NamedFunction invokers always return a reference value.
|
||||
|
||||
methodEpilogue();
|
||||
clinit();
|
||||
bogusMethod(dstType);
|
||||
|
||||
final byte[] classFile = cw.toByteArray();
|
||||
|
@ -27,7 +27,6 @@ package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
@ -40,6 +39,7 @@ import sun.invoke.util.ValueConversions;
|
||||
import sun.invoke.util.VerifyType;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
@ -1157,10 +1157,24 @@ abstract class MethodHandleImpl {
|
||||
return restoreToType(bccInvoker.bindTo(vamh), mh, hostClass);
|
||||
}
|
||||
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> hostClass) {
|
||||
private static MethodHandle makeInjectedInvoker(Class<?> targetClass) {
|
||||
try {
|
||||
Class<?> invokerClass = UNSAFE.defineAnonymousClass(hostClass, INJECTED_INVOKER_TEMPLATE, null);
|
||||
assert checkInjectedInvoker(hostClass, invokerClass);
|
||||
/*
|
||||
* The invoker class defined to the same class loader as the lookup class
|
||||
* but in an unnamed package so that the class bytes can be cached and
|
||||
* reused for any @CSM.
|
||||
*
|
||||
* @CSM must be public and exported if called by any module.
|
||||
*/
|
||||
String name = targetClass.getName() + "$$InjectedInvoker";
|
||||
if (targetClass.isHidden()) {
|
||||
// use the original class name
|
||||
name = name.replace('/', '_');
|
||||
}
|
||||
Class<?> invokerClass = new Lookup(targetClass)
|
||||
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE)
|
||||
.defineClass(true);
|
||||
assert checkInjectedInvoker(targetClass, invokerClass);
|
||||
return IMPL_LOOKUP.findStatic(invokerClass, "invoke_V", INVOKER_MT);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw uncaughtException(ex);
|
||||
@ -1256,10 +1270,6 @@ abstract class MethodHandleImpl {
|
||||
"(Ljava/lang/invoke/MethodHandle;[Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
null, null);
|
||||
|
||||
// Suppress invoker method in stack traces.
|
||||
AnnotationVisitor av0 = mv.visitAnnotation(InvokerBytecodeGenerator.HIDDEN_SIG, true);
|
||||
av0.visitEnd();
|
||||
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
@ -137,6 +139,15 @@ class MethodHandleNatives {
|
||||
REF_newInvokeSpecial = 8,
|
||||
REF_invokeInterface = 9,
|
||||
REF_LIMIT = 10;
|
||||
|
||||
/**
|
||||
* Flags for Lookup.ClassOptions
|
||||
*/
|
||||
static final int
|
||||
NESTMATE_CLASS = 0x00000001,
|
||||
HIDDEN_CLASS = 0x00000002,
|
||||
STRONG_LOADER_LINK = 0x00000004,
|
||||
ACCESS_VM_ANNOTATIONS = 0x00000008;
|
||||
}
|
||||
|
||||
static boolean refKindIsValid(int refKind) {
|
||||
@ -659,4 +670,13 @@ class MethodHandleNatives {
|
||||
return (definingClass.isAssignableFrom(symbolicRefClass) || // Msym overrides Mdef
|
||||
symbolicRefClass.isInterface()); // Mdef implements Msym
|
||||
}
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
/*
|
||||
* A convenient method for LambdaForms to get the class data of a given class.
|
||||
* LambdaForms cannot use condy via MethodHandles.classData
|
||||
*/
|
||||
static Object classData(Class<?> c) {
|
||||
return JLA.classData(c);
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,12 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -45,8 +48,6 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -219,6 +220,10 @@ public class MethodHandles {
|
||||
* @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a>
|
||||
*/
|
||||
public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException {
|
||||
if (caller.allowedModes == Lookup.TRUSTED) {
|
||||
return new Lookup(targetClass);
|
||||
}
|
||||
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
|
||||
if (targetClass.isPrimitive())
|
||||
@ -262,6 +267,57 @@ public class MethodHandles {
|
||||
return Lookup.newLookup(targetClass, newPreviousClass, newModes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <em>class data</em> associated with the lookup class
|
||||
* of the specified {@code Lookup} object, or {@code null}.
|
||||
*
|
||||
* <p> Classes can be created with class data by calling
|
||||
* {@link Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
|
||||
* Lookup::defineHiddenClassWithClassData}.
|
||||
* A hidden class with a class data behaves as if the hidden class
|
||||
* has a private static final unnamed field pre-initialized with
|
||||
* the class data and this method is equivalent as if calling
|
||||
* {@link ConstantBootstraps#getStaticFinal(Lookup, String, Class)} to
|
||||
* obtain the value of such field corresponding to the class data.
|
||||
*
|
||||
* <p> The {@linkplain Lookup#lookupModes() lookup modes} for this lookup
|
||||
* must have {@link Lookup#ORIGINAL ORIGINAL} access in order to retrieve
|
||||
* the class data.
|
||||
*
|
||||
* @apiNote
|
||||
* This method can be called as a bootstrap method for a dynamically computed
|
||||
* constant. A framework can create a hidden class with class data, for
|
||||
* example that can be {@code List.of(o1, o2, o3....)} containing more than
|
||||
* one live object. The class data is accessible only to the lookup object
|
||||
* created by the original caller but inaccessible to other members
|
||||
* in the same nest. If a framework passes security sensitive live objects
|
||||
* to a hidden class via class data, it is recommended to load the value
|
||||
* of class data as a dynamically computed constant instead of storing
|
||||
* the live objects in private fields which are accessible to other
|
||||
* nestmates.
|
||||
*
|
||||
* @param <T> the type to cast the class data object to
|
||||
* @param caller the lookup context describing the class performing the
|
||||
* operation (normally stacked by the JVM)
|
||||
* @param name ignored
|
||||
* @param type the type of the class data
|
||||
* @return the value of the class data if present in the lookup class;
|
||||
* otherwise {@code null}
|
||||
* @throws IllegalAccessException if the lookup context does not have
|
||||
* original caller access
|
||||
* @throws ClassCastException if the class data cannot be converted to
|
||||
* the specified {@code type}
|
||||
* @see Lookup#defineHiddenClassWithClassData(byte[], Object, Lookup.ClassOption...)
|
||||
* @since 15
|
||||
*/
|
||||
static <T> T classData(Lookup caller, String name, Class<T> type) throws IllegalAccessException {
|
||||
if (!caller.hasFullPrivilegeAccess()) {
|
||||
throw new IllegalAccessException(caller + " does not have full privilege access");
|
||||
}
|
||||
Object classData = MethodHandleNatives.classData(caller.lookupClass);
|
||||
return type.cast(classData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an unchecked "crack" of a
|
||||
* <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
|
||||
@ -517,7 +573,7 @@ public class MethodHandles {
|
||||
* that the receiver argument must match both the resolved method <em>and</em>
|
||||
* the current class. Again, this requirement is enforced by narrowing the
|
||||
* type of the leading parameter to the resulting method handle.
|
||||
* (See the Java Virtual Machine Specification, section {@jmvs 4.10.1.9}.)
|
||||
* (See the Java Virtual Machine Specification, section {@jvms 4.10.1.9}.)
|
||||
* <p>
|
||||
* The JVM represents constructors and static initializer blocks as internal methods
|
||||
* with special names ({@code "<init>"} and {@code "<clinit>"}).
|
||||
@ -1400,8 +1456,6 @@ public class MethodHandles {
|
||||
*/
|
||||
Lookup(Class<?> lookupClass) {
|
||||
this(lookupClass, null, FULL_POWER_MODES);
|
||||
// make sure we haven't accidentally picked up a privileged class:
|
||||
checkUnprivilegedlookupClass(lookupClass);
|
||||
}
|
||||
|
||||
private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
|
||||
@ -1508,7 +1562,7 @@ public class MethodHandles {
|
||||
}
|
||||
// Allow nestmate lookups to be created without special privilege:
|
||||
if ((newModes & PRIVATE) != 0
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
|
||||
newModes &= ~(PRIVATE|PROTECTED);
|
||||
}
|
||||
if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0
|
||||
@ -1577,9 +1631,12 @@ public class MethodHandles {
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a class to the same class loader and in the same runtime package and
|
||||
* Creates a class or interface from {@code bytes}
|
||||
* with the same class loader and in the same runtime package and
|
||||
* {@linkplain java.security.ProtectionDomain protection domain} as this lookup's
|
||||
* {@linkplain #lookupClass() lookup class}.
|
||||
* {@linkplain #lookupClass() lookup class} as if calling
|
||||
* {@link ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
|
||||
* ClassLoader::defineClass}.
|
||||
*
|
||||
* <p> The {@linkplain #lookupModes() lookup modes} for this lookup must include
|
||||
* {@link #PACKAGE PACKAGE} access as default (package) members will be
|
||||
@ -1602,11 +1659,12 @@ public class MethodHandles {
|
||||
*
|
||||
* @param bytes the class bytes
|
||||
* @return the {@code Class} object for the class
|
||||
* @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws IllegalArgumentException the bytes are for a class in a different package
|
||||
* to the lookup class
|
||||
* @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
|
||||
* @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be
|
||||
* verified ({@code VerifyError}), is already defined, or another linkage error occurs
|
||||
* @throws VerifyError if the newly created class cannot be verified
|
||||
* @throws LinkageError if the newly created class cannot be linked for any other reason
|
||||
* @throws SecurityException if a security manager is present and it
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws NullPointerException if {@code bytes} is {@code null}
|
||||
@ -1617,66 +1675,537 @@ public class MethodHandles {
|
||||
* @see ClassLoader#defineClass(String,byte[],int,int,ProtectionDomain)
|
||||
*/
|
||||
public Class<?> defineClass(byte[] bytes) throws IllegalAccessException {
|
||||
ensureDefineClassPermission();
|
||||
if ((lookupModes() & PACKAGE) == 0)
|
||||
throw new IllegalAccessException("Lookup does not have PACKAGE access");
|
||||
return makeClassDefiner(bytes.clone()).defineClass(false);
|
||||
}
|
||||
|
||||
private void ensureDefineClassPermission() {
|
||||
if (allowedModes == TRUSTED) return;
|
||||
|
||||
if (!hasFullPrivilegeAccess()) {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null)
|
||||
sm.checkPermission(new RuntimePermission("defineClass"));
|
||||
}
|
||||
if ((lookupModes() & PACKAGE) == 0)
|
||||
throw new IllegalAccessException("Lookup does not have PACKAGE access");
|
||||
}
|
||||
|
||||
// parse class bytes to get class name (in internal form)
|
||||
bytes = bytes.clone();
|
||||
String name;
|
||||
/**
|
||||
* The set of class options that specify whether a hidden class created by
|
||||
* {@link Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
|
||||
* Lookup::defineHiddenClass} method is dynamically added as a new member
|
||||
* to the nest of a lookup class and/or whether a hidden class has
|
||||
* a strong relationship with the class loader marked as its defining loader.
|
||||
*
|
||||
* @since 15
|
||||
*/
|
||||
public enum ClassOption {
|
||||
/**
|
||||
* Specifies that a hidden class be added to {@linkplain Class#getNestHost nest}
|
||||
* of a lookup class as a nestmate.
|
||||
*
|
||||
* <p> A hidden nestmate class has access to the private members of all
|
||||
* classes and interfaces in the same nest.
|
||||
*
|
||||
* @see Class#getNestHost()
|
||||
*/
|
||||
NESTMATE(NESTMATE_CLASS),
|
||||
|
||||
/**
|
||||
* Specifies that a hidden class has a <em>strong</em>
|
||||
* relationship with the class loader marked as its defining loader,
|
||||
* as a normal class or interface has with its own defining loader.
|
||||
* This means that the hidden class may be unloaded if and only if
|
||||
* its defining loader is not reachable and thus may be reclaimed
|
||||
* by a garbage collector (JLS 12.7).
|
||||
*
|
||||
* <p> By default, a hidden class or interface may be unloaded
|
||||
* even if the class loader that is marked as its defining loader is
|
||||
* <a href="../ref/package.html#reachability">reachable</a>.
|
||||
|
||||
*
|
||||
* @jls 12.7 Unloading of Classes and Interfaces
|
||||
*/
|
||||
STRONG(STRONG_LOADER_LINK);
|
||||
|
||||
/* the flag value is used by VM at define class time */
|
||||
private final int flag;
|
||||
ClassOption(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
static int optionsToFlag(Set<ClassOption> options) {
|
||||
int flags = 0;
|
||||
for (ClassOption cp : options) {
|
||||
flags |= cp.flag;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <em>hidden</em> class or interface from {@code bytes},
|
||||
* returning a {@code Lookup} on the newly created class or interface.
|
||||
*
|
||||
* <p> Ordinarily, a class or interface {@code C} is created by a class loader,
|
||||
* which either defines {@code C} directly or delegates to another class loader.
|
||||
* A class loader defines {@code C} directly by invoking
|
||||
* {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
|
||||
* ClassLoader::defineClass}, which causes the Java Virtual Machine
|
||||
* to derive {@code C} from a purported representation in {@code class} file format.
|
||||
* In situations where use of a class loader is undesirable, a class or interface
|
||||
* {@code C} can be created by this method instead. This method is capable of
|
||||
* defining {@code C}, and thereby creating it, without invoking
|
||||
* {@code ClassLoader::defineClass}.
|
||||
* Instead, this method defines {@code C} as if by arranging for
|
||||
* the Java Virtual Machine to derive a nonarray class or interface {@code C}
|
||||
* from a purported representation in {@code class} file format
|
||||
* using the following rules:
|
||||
*
|
||||
* <ol>
|
||||
* <li> The {@linkplain #lookupModes() lookup modes} for this {@code Lookup}
|
||||
* must include {@linkplain #hasFullPrivilegeAccess() full privilege} access.
|
||||
* This level of access is needed to create {@code C} in the module
|
||||
* of the lookup class of this {@code Lookup}.</li>
|
||||
*
|
||||
* <li> The purported representation in {@code bytes} must be a {@code ClassFile}
|
||||
* structure of a supported major and minor version. The major and minor version
|
||||
* may differ from the {@code class} file version of the lookup class of this
|
||||
* {@code Lookup}.</li>
|
||||
*
|
||||
* <li> The value of {@code this_class} must be a valid index in the
|
||||
* {@code constant_pool} table, and the entry at that index must be a valid
|
||||
* {@code CONSTANT_Class_info} structure. Let {@code N} be the binary name
|
||||
* encoded in internal form that is specified by this structure. {@code N} must
|
||||
* denote a class or interface in the same package as the lookup class.</li>
|
||||
*
|
||||
* <li> Let {@code CN} be the string {@code N + "." + <suffix>},
|
||||
* where {@code <suffix>} is an unqualified name.
|
||||
*
|
||||
* <p> Let {@code newBytes} be the {@code ClassFile} structure given by
|
||||
* {@code bytes} with an additional entry in the {@code constant_pool} table,
|
||||
* indicating a {@code CONSTANT_Utf8_info} structure for {@code CN}, and
|
||||
* where the {@code CONSTANT_Class_info} structure indicated by {@code this_class}
|
||||
* refers to the new {@code CONSTANT_Utf8_info} structure.
|
||||
*
|
||||
* <p> Let {@code L} be the defining class loader of the lookup class of this {@code Lookup}.
|
||||
*
|
||||
* <p> {@code C} is derived with name {@code CN}, class loader {@code L}, and
|
||||
* purported representation {@code newBytes} as if by the rules of JVMS {@jvms 5.3.5},
|
||||
* with the following adjustments:
|
||||
* <ul>
|
||||
* <li> The constant indicated by {@code this_class} is permitted to specify a name
|
||||
* that includes a single {@code "."} character, even though this is not a valid
|
||||
* binary class or interface name in internal form.</li>
|
||||
*
|
||||
* <li> The Java Virtual Machine marks {@code L} as the defining class loader of {@code C},
|
||||
* but no class loader is recorded as an initiating class loader of {@code C}.</li>
|
||||
*
|
||||
* <li> {@code C} is considered to have the same runtime
|
||||
* {@linkplain Class#getPackage() package}, {@linkplain Class#getModule() module}
|
||||
* and {@linkplain java.security.ProtectionDomain protection domain}
|
||||
* as the lookup class of this {@code Lookup}.
|
||||
* <li> Let {@code GN} be the binary name obtained by taking {@code N}
|
||||
* (a binary name encoded in internal form) and replacing ASCII forward slashes with
|
||||
* ASCII periods. For the instance of {@link java.lang.Class} representing {@code C}:
|
||||
* <ul>
|
||||
* <li> {@link Class#getName()} returns the string {@code GN + "/" + <suffix>},
|
||||
* even though this is not a valid binary class or interface name.</li>
|
||||
* <li> {@link Class#descriptorString()} returns the string
|
||||
* {@code "L" + N + "." + <suffix> + ";"},
|
||||
* even though this is not a valid type descriptor name.</li>
|
||||
* <li> {@link Class#describeConstable()} returns an empty optional as {@code C}
|
||||
* cannot be described in {@linkplain java.lang.constant.ClassDesc nominal form}.</li>
|
||||
* </ul>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* <p> After {@code C} is derived, it is linked by the Java Virtual Machine.
|
||||
* Linkage occurs as specified in JVMS {@jvms 5.4.3}, with the following adjustments:
|
||||
* <ul>
|
||||
* <li> During verification, whenever it is necessary to load the class named
|
||||
* {@code CN}, the attempt succeeds, producing class {@code C}. No request is
|
||||
* made of any class loader.</li>
|
||||
*
|
||||
* <li> On any attempt to resolve the entry in the run-time constant pool indicated
|
||||
* by {@code this_class}, the symbolic reference is considered to be resolved to
|
||||
* {@code C} and resolution always succeeds immediately.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> If the {@code initialize} parameter is {@code true},
|
||||
* then {@code C} is initialized by the Java Virtual Machine.
|
||||
*
|
||||
* <p> The newly created class or interface {@code C} serves as the
|
||||
* {@linkplain #lookupClass() lookup class} of the {@code Lookup} object
|
||||
* returned by this method. {@code C} is <em>hidden</em> in the sense that
|
||||
* no other class or interface can refer to {@code C} via a constant pool entry.
|
||||
* That is, a hidden class or interface cannot be named as a supertype, a field type,
|
||||
* a method parameter type, or a method return type by any other class.
|
||||
* This is because a hidden class or interface does not have a binary name, so
|
||||
* there is no internal form available to record in any class's constant pool.
|
||||
* A hidden class or interface is not discoverable by {@link Class#forName(String, boolean, ClassLoader)},
|
||||
* {@link ClassLoader#loadClass(String, boolean)}, or {@link #findClass(String)}, and
|
||||
* is not {@linkplain java.lang.instrument.Instrumentation#isModifiableClass(Class)
|
||||
* modifiable} by Java agents or tool agents using the <a href="{@docRoot}/../specs/jvmti.html">
|
||||
* JVM Tool Interface</a>.
|
||||
*
|
||||
* <p> A class or interface created by
|
||||
* {@linkplain ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)
|
||||
* a class loader} has a strong relationship with that class loader.
|
||||
* That is, every {@code Class} object contains a reference to the {@code ClassLoader}
|
||||
* that {@linkplain Class#getClassLoader() defined it}.
|
||||
* This means that a class created by a class loader may be unloaded if and
|
||||
* only if its defining loader is not reachable and thus may be reclaimed
|
||||
* by a garbage collector (JLS 12.7).
|
||||
*
|
||||
* By default, however, a hidden class or interface may be unloaded even if
|
||||
* the class loader that is marked as its defining loader is
|
||||
* <a href="../ref/package.html#reachability">reachable</a>.
|
||||
* This behavior is useful when a hidden class or interface serves multiple
|
||||
* classes defined by arbitrary class loaders. In other cases, a hidden
|
||||
* class or interface may be linked to a single class (or a small number of classes)
|
||||
* with the same defining loader as the hidden class or interface.
|
||||
* In such cases, where the hidden class or interface must be coterminous
|
||||
* with a normal class or interface, the {@link ClassOption#STRONG STRONG}
|
||||
* option may be passed in {@code options}.
|
||||
* This arranges for a hidden class to have the same strong relationship
|
||||
* with the class loader marked as its defining loader,
|
||||
* as a normal class or interface has with its own defining loader.
|
||||
*
|
||||
* If {@code STRONG} is not used, then the invoker of {@code defineHiddenClass}
|
||||
* may still prevent a hidden class or interface from being
|
||||
* unloaded by ensuring that the {@code Class} object is reachable.
|
||||
*
|
||||
* <p> The unloading characteristics are set for each hidden class when it is
|
||||
* defined, and cannot be changed later. An advantage of allowing hidden classes
|
||||
* to be unloaded independently of the class loader marked as their defining loader
|
||||
* is that a very large number of hidden classes may be created by an application.
|
||||
* In contrast, if {@code STRONG} is used, then the JVM may run out of memory,
|
||||
* just as if normal classes were created by class loaders.
|
||||
*
|
||||
* <p> Classes and interfaces in a nest are allowed to have mutual access to
|
||||
* their private members. The nest relationship is determined by
|
||||
* the {@code NestHost} attribute (JVMS {@jvms 4.7.28}) and
|
||||
* the {@code NestMembers} attribute (JVMS {@jvms 4.7.29}) in a {@code class} file.
|
||||
* By default, a hidden class belongs to a nest consisting only of itself
|
||||
* because a hidden class has no binary name.
|
||||
* The {@link ClassOption#NESTMATE NESTMATE} option can be passed in {@code options}
|
||||
* to create a hidden class or interface {@code C} as a member of a nest.
|
||||
* The nest to which {@code C} belongs is not based on any {@code NestHost} attribute
|
||||
* in the {@code ClassFile} structure from which {@code C} was derived.
|
||||
* Instead, the following rules determine the nest host of {@code C}:
|
||||
* <ul>
|
||||
* <li>If the nest host of the lookup class of this {@code Lookup} has previously
|
||||
* been determined, then let {@code H} be the nest host of the lookup class.
|
||||
* Otherwise, the nest host of the lookup class is determined using the
|
||||
* algorithm in JVMS {@jvms 5.4.4}, yielding {@code H}.</li>
|
||||
* <li>The nest host of {@code C} is determined to be {@code H},
|
||||
* the nest host of the lookup class.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p> A hidden class or interface may be serializable, but this requires a custom
|
||||
* serialization mechanism in order to ensure that instances are properly serialized
|
||||
* and deserialized. The default serialization mechanism supports only classes and
|
||||
* interfaces that are discoverable by their class name.
|
||||
*
|
||||
* @param bytes the bytes that make up the class data,
|
||||
* in the format of a valid {@code class} file as defined by
|
||||
* <cite>The Java Virtual Machine Specification</cite>.
|
||||
* @param initialize if {@code true} the class will be initialized.
|
||||
* @param options {@linkplain ClassOption class options}
|
||||
* @return the {@code Lookup} object on the hidden class
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Lookup} does not have
|
||||
* {@linkplain #hasFullPrivilegeAccess() full privilege} access
|
||||
* @throws SecurityException if a security manager is present and it
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
* @throws IncompatibleClassChangeError if the class or interface named as
|
||||
* the direct superclass of {@code C} is in fact an interface, or if any of the classes
|
||||
* or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
|
||||
* @throws ClassCircularityError if any of the superclasses or superinterfaces of
|
||||
* {@code C} is {@code C} itself
|
||||
* @throws VerifyError if the newly created class cannot be verified
|
||||
* @throws LinkageError if the newly created class cannot be linked for any other reason
|
||||
* @throws NullPointerException if any parameter is {@code null}
|
||||
*
|
||||
* @since 15
|
||||
* @see Class#isHidden()
|
||||
* @jvms 4.2.1 Binary Class and Interface Names
|
||||
* @jvms 4.2.2 Unqualified Names
|
||||
* @jvms 4.7.28 The {@code NestHost} Attribute
|
||||
* @jvms 4.7.29 The {@code NestMembers} Attribute
|
||||
* @jvms 5.4.3.1 Class and Interface Resolution
|
||||
* @jvms 5.4.4 Access Control
|
||||
* @jvms 5.3.5 Deriving a {@code Class} from a {@code class} File Representation
|
||||
* @jvms 5.4 Linking
|
||||
* @jvms 5.5 Initialization
|
||||
* @jls 12.7 Unloading of Classes and Interfaces
|
||||
*/
|
||||
public Lookup defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
Objects.requireNonNull(bytes);
|
||||
Objects.requireNonNull(options);
|
||||
|
||||
ensureDefineClassPermission();
|
||||
if (!hasFullPrivilegeAccess()) {
|
||||
throw new IllegalAccessException(this + " does not have full privilege access");
|
||||
}
|
||||
|
||||
return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false).defineClassAsLookup(initialize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <em>hidden</em> class or interface from {@code bytes} with associated
|
||||
* {@linkplain MethodHandles#classData(Lookup, String, Class) class data},
|
||||
* returning a {@code Lookup} on the newly created class or interface.
|
||||
*
|
||||
* <p> This method is equivalent to calling
|
||||
* {@link #defineHiddenClass(byte[], boolean, ClassOption...) defineHiddenClass(bytes, true, options)}
|
||||
* as if the hidden class has a private static final unnamed field whose value
|
||||
* is initialized to {@code classData} right before the class initializer is
|
||||
* executed. The newly created class is linked and initialized by the Java
|
||||
* Virtual Machine.
|
||||
*
|
||||
* <p> The {@link MethodHandles#classData(Lookup, String, Class) MethodHandles::classData}
|
||||
* method can be used to retrieve the {@code classData}.
|
||||
*
|
||||
* @param bytes the class bytes
|
||||
* @param classData pre-initialized class data
|
||||
* @param options {@linkplain ClassOption class options}
|
||||
* @return the {@code Lookup} object on the hidden class
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Lookup} does not have
|
||||
* {@linkplain #hasFullPrivilegeAccess() full privilege} access
|
||||
* @throws SecurityException if a security manager is present and it
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
* @throws IncompatibleClassChangeError if the class or interface named as
|
||||
* the direct superclass of {@code C} is in fact an interface, or if any of the classes
|
||||
* or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
|
||||
* @throws ClassCircularityError if any of the superclasses or superinterfaces of
|
||||
* {@code C} is {@code C} itself
|
||||
* @throws VerifyError if the newly created class cannot be verified
|
||||
* @throws LinkageError if the newly created class cannot be linked for any other reason
|
||||
* @throws NullPointerException if any parameter is {@code null}
|
||||
*
|
||||
* @since 15
|
||||
* @see Lookup#defineHiddenClass(byte[], boolean, ClassOption...)
|
||||
* @see Class#isHidden()
|
||||
*/
|
||||
/* package-private */ Lookup defineHiddenClassWithClassData(byte[] bytes, Object classData, ClassOption... options)
|
||||
throws IllegalAccessException
|
||||
{
|
||||
Objects.requireNonNull(bytes);
|
||||
Objects.requireNonNull(classData);
|
||||
Objects.requireNonNull(options);
|
||||
|
||||
ensureDefineClassPermission();
|
||||
if (!hasFullPrivilegeAccess()) {
|
||||
throw new IllegalAccessException(this + " does not have full privilege access");
|
||||
}
|
||||
|
||||
return makeHiddenClassDefiner(bytes.clone(), Set.of(options), false)
|
||||
.defineClassAsLookup(true, classData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates the given bytes to be a class or interface and the class name
|
||||
* is in the same package as the lookup class.
|
||||
*
|
||||
* This method returns the class name.
|
||||
*/
|
||||
private String validateAndGetClassName(byte[] bytes) {
|
||||
try {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
name = reader.getClassName();
|
||||
if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) {
|
||||
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
|
||||
}
|
||||
String name = reader.getClassName().replace('/', '.');
|
||||
int index = name.lastIndexOf('.');
|
||||
String pn = (index == -1) ? "" : name.substring(0, index);
|
||||
if (!pn.equals(lookupClass.getPackageName())) {
|
||||
throw newIllegalArgumentException(name + " not in same package as lookup class: " +
|
||||
lookupClass.getName());
|
||||
}
|
||||
return name;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
// ASM exceptions are poorly specified
|
||||
ClassFormatError cfe = new ClassFormatError();
|
||||
cfe.initCause(e);
|
||||
throw cfe;
|
||||
}
|
||||
}
|
||||
|
||||
// get package and class name in binary form
|
||||
String cn, pn;
|
||||
int index = name.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
cn = name;
|
||||
pn = "";
|
||||
} else {
|
||||
cn = name.replace('/', '.');
|
||||
pn = cn.substring(0, index);
|
||||
}
|
||||
if (!pn.equals(lookupClass.getPackageName())) {
|
||||
throw new IllegalArgumentException("Class not in same package as lookup class");
|
||||
|
||||
/*
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a normal class
|
||||
* from the given bytes.
|
||||
*
|
||||
* Caller should make a defensive copy of the arguments if needed
|
||||
* before calling this factory method.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
*/
|
||||
private ClassDefiner makeClassDefiner(byte[] bytes) {
|
||||
return new ClassDefiner(this, validateAndGetClassName(bytes), bytes, STRONG_LOADER_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes. The name must be in the same package as the lookup class.
|
||||
*
|
||||
* Caller should make a defensive copy of the arguments if needed
|
||||
* before calling this factory method.
|
||||
*
|
||||
* @param bytes class bytes
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes.
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
|
||||
return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, Set.of(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes and options.
|
||||
* The name must be in the same package as the lookup class.
|
||||
*
|
||||
* Caller should make a defensive copy of the arguments if needed
|
||||
* before calling this factory method.
|
||||
*
|
||||
* @param bytes class bytes
|
||||
* @param options class options
|
||||
* @param accessVmAnnotations true to give the hidden class access to VM annotations
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes and options
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(byte[] bytes,
|
||||
Set<ClassOption> options,
|
||||
boolean accessVmAnnotations) {
|
||||
return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, options, accessVmAnnotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes. No package name check on the given name.
|
||||
*
|
||||
* @param name fully-qualified name that specifies the prefix of the hidden class
|
||||
* @param bytes class bytes
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes.
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
|
||||
return makeHiddenClassDefiner(name, bytes, Set.of(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes and options. No package name check on the given name.
|
||||
*
|
||||
* @param name the name of the class and the name in the class bytes is ignored.
|
||||
* @param bytes class bytes
|
||||
* @param options class options
|
||||
* @param accessVmAnnotations true to give the hidden class access to VM annotations
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name,
|
||||
byte[] bytes,
|
||||
Set<ClassOption> options,
|
||||
boolean accessVmAnnotations) {
|
||||
int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
|
||||
if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
|
||||
// jdk.internal.vm.annotations are permitted for classes
|
||||
// defined to boot loader and platform loader
|
||||
flags |= ACCESS_VM_ANNOTATIONS;
|
||||
}
|
||||
|
||||
// invoke the class loader's defineClass method
|
||||
ClassLoader loader = lookupClass.getClassLoader();
|
||||
ProtectionDomain pd = (loader != null) ? lookupClassProtectionDomain() : null;
|
||||
String source = "__Lookup_defineClass__";
|
||||
Class<?> clazz = SharedSecrets.getJavaLangAccess().defineClass(loader, cn, bytes, pd, source);
|
||||
return clazz;
|
||||
return new ClassDefiner(this, name, bytes, flags);
|
||||
}
|
||||
|
||||
static class ClassDefiner {
|
||||
private final Lookup lookup;
|
||||
private final String name;
|
||||
private final byte[] bytes;
|
||||
private final int classFlags;
|
||||
|
||||
private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) {
|
||||
assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
|
||||
this.lookup = lookup;
|
||||
this.bytes = bytes;
|
||||
this.classFlags = flags;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String className() {
|
||||
return name;
|
||||
}
|
||||
|
||||
Class<?> defineClass(boolean initialize) {
|
||||
return defineClass(initialize, null);
|
||||
}
|
||||
|
||||
Lookup defineClassAsLookup(boolean initialize) {
|
||||
Class<?> c = defineClass(initialize, null);
|
||||
return new Lookup(c, null, FULL_POWER_MODES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the class of the given bytes and the given classData.
|
||||
* If {@code initialize} parameter is true, then the class will be initialized.
|
||||
*
|
||||
* @param initialize true if the class to be initialized
|
||||
* @param classData classData or null
|
||||
* @return the class
|
||||
*
|
||||
* @throws LinkageError linkage error
|
||||
*/
|
||||
Class<?> defineClass(boolean initialize, Object classData) {
|
||||
Class<?> lookupClass = lookup.lookupClass();
|
||||
ClassLoader loader = lookupClass.getClassLoader();
|
||||
ProtectionDomain pd = (loader != null) ? lookup.lookupClassProtectionDomain() : null;
|
||||
Class<?> c = JLA.defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData);
|
||||
assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost();
|
||||
return c;
|
||||
}
|
||||
|
||||
Lookup defineClassAsLookup(boolean initialize, Object classData) {
|
||||
// initialize must be true if classData is non-null
|
||||
assert classData == null || initialize == true;
|
||||
Class<?> c = defineClass(initialize, classData);
|
||||
return new Lookup(c, null, FULL_POWER_MODES);
|
||||
}
|
||||
|
||||
private boolean isNestmate() {
|
||||
return (classFlags & NESTMATE_CLASS) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ProtectionDomain lookupClassProtectionDomain() {
|
||||
ProtectionDomain pd = cachedProtectionDomain;
|
||||
if (pd == null) {
|
||||
cachedProtectionDomain = pd = protectionDomain(lookupClass);
|
||||
cachedProtectionDomain = pd = JLA.protectionDomain(lookupClass);
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
|
||||
private ProtectionDomain protectionDomain(Class<?> clazz) {
|
||||
PrivilegedAction<ProtectionDomain> pa = clazz::getProtectionDomain;
|
||||
return AccessController.doPrivileged(pa);
|
||||
}
|
||||
|
||||
// cached protection domain
|
||||
private volatile ProtectionDomain cachedProtectionDomain;
|
||||
|
||||
|
||||
// Make sure outer class is initialized first.
|
||||
static { IMPL_NAMES.getClass(); }
|
||||
|
||||
@ -1689,6 +2218,8 @@ public class MethodHandles {
|
||||
*/
|
||||
static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL);
|
||||
|
||||
static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
|
||||
String name = lookupClass.getName();
|
||||
if (name.startsWith("java.lang.invoke."))
|
||||
@ -1749,7 +2280,7 @@ public class MethodHandles {
|
||||
return cname + "/package";
|
||||
case FULL_POWER_MODES & (~PROTECTED):
|
||||
case FULL_POWER_MODES & ~(PROTECTED|MODULE):
|
||||
return cname + "/private";
|
||||
return cname + "/private";
|
||||
case FULL_POWER_MODES:
|
||||
case FULL_POWER_MODES & (~MODULE):
|
||||
return cname;
|
||||
@ -2639,8 +3170,13 @@ return mh1;
|
||||
|
||||
private MethodHandle unreflectField(Field f, boolean isSetter) throws IllegalAccessException {
|
||||
MemberName field = new MemberName(f, isSetter);
|
||||
if (isSetter && field.isStatic() && field.isFinal())
|
||||
throw field.makeAccessException("static final field has no write access", this);
|
||||
if (isSetter && field.isFinal()) {
|
||||
if (field.isStatic()) {
|
||||
throw field.makeAccessException("static final field has no write access", this);
|
||||
} else if (field.getDeclaringClass().isHidden()){
|
||||
throw field.makeAccessException("final field in a hidden class has no write access", this);
|
||||
}
|
||||
}
|
||||
assert(isSetter
|
||||
? MethodHandleNatives.refKindIsSetter(field.getReferenceKind())
|
||||
: MethodHandleNatives.refKindIsGetter(field.getReferenceKind()));
|
||||
@ -3201,7 +3737,8 @@ return mh1;
|
||||
}
|
||||
refc = lookupClass();
|
||||
}
|
||||
return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(), this.allowedModes == TRUSTED);
|
||||
return VarHandles.makeFieldHandle(getField, refc, getField.getFieldType(),
|
||||
this.allowedModes == TRUSTED && !getField.getDeclaringClass().isHidden());
|
||||
}
|
||||
/** Check access and get the requested constructor. */
|
||||
private MethodHandle getDirectConstructor(Class<?> refc, MemberName ctor) throws IllegalAccessException {
|
||||
|
@ -99,6 +99,36 @@ import static java.lang.invoke.MethodType.fromDescriptor;
|
||||
* all classes named in the descriptor must be accessible, and will be loaded.
|
||||
* (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.)
|
||||
* This loading may occur at any time before the {@code MethodType} object is first derived.
|
||||
* <p>
|
||||
* <b><a id="descriptor">Nominal Descriptors</a></b>
|
||||
* <p>
|
||||
* A {@code MethodType} can be described in {@linkplain MethodTypeDesc nominal form}
|
||||
* if and only if all of the parameter types and return type can be described
|
||||
* with a {@link Class#describeConstable() nominal descriptor} represented by
|
||||
* {@link ClassDesc}. If a method type can be described norminally, then:
|
||||
* <ul>
|
||||
* <li>The method type has a {@link MethodTypeDesc nominal descriptor}
|
||||
* returned by {@link #describeConstable() MethodType::describeConstable}.</li>
|
||||
* <li>The descriptor string returned by
|
||||
* {@link #descriptorString() MethodType::descriptorString} or
|
||||
* {@link #toMethodDescriptorString() MethodType::toMethodDescriptorString}
|
||||
* for the method type is a method descriptor (JVMS {@jvms 4.3.3}).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* If any of the parameter types or return type cannot be described
|
||||
* nominally, i.e. {@link Class#describeConstable() Class::describeConstable}
|
||||
* returns an empty optional for that type,
|
||||
* then the method type cannot be described nominally:
|
||||
* <ul>
|
||||
* <li>The method type has no {@link MethodTypeDesc nominal descriptor} and
|
||||
* {@link #describeConstable() MethodType::describeConstable} returns
|
||||
* an empty optional.</li>
|
||||
* <li>The descriptor string returned by
|
||||
* {@link #descriptorString() MethodType::descriptorString} or
|
||||
* {@link #toMethodDescriptorString() MethodType::toMethodDescriptorString}
|
||||
* for the method type is not a type descriptor.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
*/
|
||||
@ -1140,7 +1170,9 @@ class MethodType
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a bytecode descriptor representation of the method type.
|
||||
* Returns a descriptor string for the method type. This method
|
||||
* is equivalent to calling {@link #descriptorString() MethodType::descriptorString}.
|
||||
*
|
||||
* <p>
|
||||
* Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
|
||||
* Two distinct classes which share a common name but have different class loaders
|
||||
@ -1150,7 +1182,9 @@ class MethodType
|
||||
* generate bytecodes that process method handles and {@code invokedynamic}.
|
||||
* {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
|
||||
* because the latter requires a suitable class loader argument.
|
||||
* @return the bytecode type descriptor representation
|
||||
* @return the descriptor string for this method type
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
public String toMethodDescriptorString() {
|
||||
String desc = methodDescriptor;
|
||||
@ -1162,11 +1196,28 @@ class MethodType
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a field type descriptor string for this type
|
||||
* Returns a descriptor string for this method type.
|
||||
*
|
||||
* @return the descriptor string
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* <p>
|
||||
* If this method type can be <a href="#descriptor">described nominally</a>,
|
||||
* then the result is a method type descriptor (JVMS {@jvms 4.3.3}).
|
||||
* {@link MethodTypeDesc MethodTypeDesc} for this method type
|
||||
* can be produced by calling {@link MethodTypeDesc#ofDescriptor(String)
|
||||
* MethodTypeDesc::ofDescriptor} with the result descriptor string.
|
||||
* <p>
|
||||
* If this method type cannot be <a href="#descriptor">described nominally</a>
|
||||
* and the result is a string of the form:
|
||||
* <blockquote>{@code "(<parameter-descriptors>)<return-descriptor>"}</blockquote>
|
||||
* where {@code <parameter-descriptors>} is the concatenation of the
|
||||
* {@linkplain Class#descriptorString() descriptor string} of all
|
||||
* of the parameter types and the {@linkplain Class#descriptorString() descriptor string}
|
||||
* of the return type. No {@link java.lang.constant.MethodTypeDesc MethodTypeDesc}
|
||||
* can be produced from the result string.
|
||||
*
|
||||
* @return the descriptor string for this method type
|
||||
* @since 12
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
@Override
|
||||
public String descriptorString() {
|
||||
@ -1179,12 +1230,13 @@ class MethodType
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a nominal descriptor for this instance, if one can be
|
||||
* Returns a nominal descriptor for this instance, if one can be
|
||||
* constructed, or an empty {@link Optional} if one cannot be.
|
||||
*
|
||||
* @return An {@link Optional} containing the resulting nominal descriptor,
|
||||
* or an empty {@link Optional} if one cannot be constructed.
|
||||
* @since 12
|
||||
* @see <a href="#descriptor">Nominal Descriptor for {@code MethodType}</a>
|
||||
*/
|
||||
@Override
|
||||
public Optional<MethodTypeDesc> describeConstable() {
|
||||
|
@ -25,7 +25,8 @@
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.access.JavaLangAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
@ -42,6 +43,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.lookup;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.ClassOption.*;
|
||||
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
|
||||
/**
|
||||
@ -133,6 +137,8 @@ public final class StringConcatFactory {
|
||||
*/
|
||||
private static final Strategy DEFAULT_STRATEGY = Strategy.MH_INLINE_SIZED_EXACT;
|
||||
|
||||
private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
|
||||
|
||||
private enum Strategy {
|
||||
/**
|
||||
* Bytecode generator, calling into {@link java.lang.StringBuilder}.
|
||||
@ -189,8 +195,6 @@ public final class StringConcatFactory {
|
||||
*/
|
||||
private static final ProxyClassesDumper DUMPER;
|
||||
|
||||
private static final Class<?> STRING_HELPER;
|
||||
|
||||
static {
|
||||
// In case we need to double-back onto the StringConcatFactory during this
|
||||
// static initialization, make sure we have the reasonable defaults to complete
|
||||
@ -202,12 +206,6 @@ public final class StringConcatFactory {
|
||||
// DEBUG = false; // implied
|
||||
// DUMPER = null; // implied
|
||||
|
||||
try {
|
||||
STRING_HELPER = Class.forName("java.lang.StringConcatHelper");
|
||||
} catch (Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
final String strategy =
|
||||
VM.getSavedProperty("java.lang.invoke.stringConcat");
|
||||
CACHE_ENABLE = Boolean.parseBoolean(
|
||||
@ -718,25 +716,10 @@ public final class StringConcatFactory {
|
||||
|
||||
private static String getClassName(Class<?> hostClass) throws StringConcatException {
|
||||
/*
|
||||
The generated class is in the same package as the host class as
|
||||
it's the implementation of the string concatenation for the host class.
|
||||
|
||||
When cache is enabled, we want to cache as much as we can.
|
||||
|
||||
However, there are two peculiarities:
|
||||
|
||||
a) The generated class should stay within the same package as the
|
||||
host class, to allow Unsafe.defineAnonymousClass access controls
|
||||
to work properly. JDK may choose to fail with IllegalAccessException
|
||||
when accessing a VM anonymous class with non-privileged callers,
|
||||
see JDK-8058575.
|
||||
|
||||
b) If we mark the stub with some prefix, say, derived from the package
|
||||
name because of (a), we can technically use that stub in other packages.
|
||||
But the call stack traces would be extremely puzzling to unsuspecting users
|
||||
and profiling tools: whatever stub wins the race, would be linked in all
|
||||
similar callsites.
|
||||
|
||||
Therefore, we set the class prefix to match the host class package, and use
|
||||
the prefix as the cache key too. This only affects BC_* strategies, and only when
|
||||
cache is enabled.
|
||||
*/
|
||||
|
||||
switch (STRATEGY) {
|
||||
@ -745,9 +728,11 @@ public final class StringConcatFactory {
|
||||
case BC_SB_SIZED_EXACT: {
|
||||
if (CACHE_ENABLE) {
|
||||
String pkgName = hostClass.getPackageName();
|
||||
return (pkgName != null && !pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
|
||||
return (!pkgName.isEmpty() ? pkgName.replace('.', '/') + "/" : "") + "Stubs$$StringConcat";
|
||||
} else {
|
||||
return hostClass.getName().replace('.', '/') + "$$StringConcat";
|
||||
String name = hostClass.isHidden() ? hostClass.getName().replace('/', '_')
|
||||
: hostClass.getName();
|
||||
return name.replace('.', '/') + "$$StringConcat";
|
||||
}
|
||||
}
|
||||
case MH_SB_SIZED:
|
||||
@ -819,7 +804,7 @@ public final class StringConcatFactory {
|
||||
* chain javac would otherwise emit. This strategy uses only the public API,
|
||||
* and comes as the baseline for the current JDK behavior. On other words,
|
||||
* this strategy moves the javac generated bytecode to runtime. The
|
||||
* generated bytecode is loaded via Unsafe.defineAnonymousClass, but with
|
||||
* generated bytecode is loaded via Lookup::defineClass, but with
|
||||
* the caller class coming from the BSM -- in other words, the protection
|
||||
* guarantees are inherited from the method where invokedynamic was
|
||||
* originally called. This means, among other things, that the bytecode is
|
||||
@ -848,7 +833,6 @@ public final class StringConcatFactory {
|
||||
* private String API.
|
||||
*/
|
||||
private static final class BytecodeStringBuilderStrategy {
|
||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
static final int CLASSFILE_VERSION = 52;
|
||||
static final String METHOD_NAME = "concat";
|
||||
|
||||
@ -861,7 +845,7 @@ public final class StringConcatFactory {
|
||||
|
||||
cw.visit(CLASSFILE_VERSION,
|
||||
ACC_SUPER + ACC_PUBLIC + ACC_FINAL + ACC_SYNTHETIC,
|
||||
className, // Unsafe.defineAnonymousClass would append an unique ID
|
||||
className,
|
||||
null,
|
||||
"java/lang/Object",
|
||||
null
|
||||
@ -874,6 +858,7 @@ public final class StringConcatFactory {
|
||||
null,
|
||||
null);
|
||||
|
||||
// use of @ForceInline no longer has any effect
|
||||
mv.visitAnnotation("Ljdk/internal/vm/annotation/ForceInline;", true);
|
||||
mv.visitCode();
|
||||
|
||||
@ -1143,11 +1128,9 @@ public final class StringConcatFactory {
|
||||
|
||||
byte[] classBytes = cw.toByteArray();
|
||||
try {
|
||||
Class<?> hostClass = lookup.lookupClass();
|
||||
Class<?> innerClass = UNSAFE.defineAnonymousClass(hostClass, classBytes, null);
|
||||
UNSAFE.ensureClassInitialized(innerClass);
|
||||
dumpIfEnabled(innerClass.getName(), classBytes);
|
||||
return Lookup.IMPL_LOOKUP.findStatic(innerClass, METHOD_NAME, args);
|
||||
Class<?> innerClass = lookup.defineHiddenClass(classBytes, true, STRONG).lookupClass();
|
||||
dumpIfEnabled(className, classBytes);
|
||||
return lookup.findStatic(innerClass, METHOD_NAME, args);
|
||||
} catch (Exception e) {
|
||||
dumpIfEnabled(className + "$$FAILED", classBytes);
|
||||
throw new StringConcatException("Exception while spinning the class", e);
|
||||
@ -1270,8 +1253,8 @@ public final class StringConcatFactory {
|
||||
* computation on MethodHandle combinators. The computation is built with
|
||||
* public MethodHandle APIs, resolved from a public Lookup sequence, and
|
||||
* ends up calling the public StringBuilder API. Therefore, this strategy
|
||||
* does not use any private API at all, even the Unsafe.defineAnonymousClass,
|
||||
* since everything is handled under cover by java.lang.invoke APIs.
|
||||
* does not use any private API at all since everything is handled under
|
||||
* cover by java.lang.invoke APIs.
|
||||
*
|
||||
* <p><b>{@link Strategy#MH_SB_SIZED_EXACT}: "MethodHandles StringBuilder,
|
||||
* sized exactly".</b>
|
||||
@ -1283,7 +1266,6 @@ public final class StringConcatFactory {
|
||||
* private String API.
|
||||
*/
|
||||
private static final class MethodHandleStringBuilderStrategy {
|
||||
|
||||
private MethodHandleStringBuilderStrategy() {
|
||||
// no instantiation
|
||||
}
|
||||
@ -1461,6 +1443,8 @@ public final class StringConcatFactory {
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static final Lookup MHSBS_LOOKUP = lookup();
|
||||
|
||||
private static final ConcurrentMap<Integer, MethodHandle> SUMMERS;
|
||||
|
||||
// This one is deliberately non-lambdified to optimize startup time:
|
||||
@ -1474,9 +1458,9 @@ public final class StringConcatFactory {
|
||||
// unroll some initial sizes.
|
||||
Class<?>[] cls = new Class<?>[cnt];
|
||||
Arrays.fill(cls, int.class);
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
|
||||
return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, cls);
|
||||
} else {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
|
||||
return lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class, "sum", int.class, int.class, int[].class)
|
||||
.asCollector(int[].class, cnt - 1);
|
||||
}
|
||||
}
|
||||
@ -1491,8 +1475,8 @@ public final class StringConcatFactory {
|
||||
STRING_LENGTH = lookupVirtual(publicLookup, String.class, "length", int.class);
|
||||
BUILDER_TO_STRING = lookupVirtual(publicLookup, StringBuilder.class, "toString", String.class);
|
||||
if (DEBUG) {
|
||||
BUILDER_TO_STRING_CHECKED = lookupStatic(MethodHandles.Lookup.IMPL_LOOKUP,
|
||||
MethodHandleStringBuilderStrategy.class, "toStringChecked", String.class, StringBuilder.class);
|
||||
BUILDER_TO_STRING_CHECKED = lookupStatic(MHSBS_LOOKUP, MethodHandleStringBuilderStrategy.class,
|
||||
"toStringChecked", String.class, StringBuilder.class);
|
||||
} else {
|
||||
BUILDER_TO_STRING_CHECKED = null;
|
||||
}
|
||||
@ -1516,8 +1500,6 @@ public final class StringConcatFactory {
|
||||
* that requires porting if there are private JDK changes occur.
|
||||
*/
|
||||
private static final class MethodHandleInlineCopyStrategy {
|
||||
static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
||||
|
||||
private MethodHandleInlineCopyStrategy() {
|
||||
// no instantiation
|
||||
}
|
||||
@ -1736,8 +1718,9 @@ public final class StringConcatFactory {
|
||||
private static final Function<Class<?>, MethodHandle> PREPEND = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "prepend", long.class, long.class, byte[].class,
|
||||
String.class, Wrapper.asPrimitiveType(c), String.class);
|
||||
return JLA.stringConcatHelper("prepend",
|
||||
methodType(long.class, long.class, byte[].class,
|
||||
String.class, Wrapper.asPrimitiveType(c), String.class));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1745,8 +1728,7 @@ public final class StringConcatFactory {
|
||||
private static final Function<Class<?>, MethodHandle> MIX = new Function<>() {
|
||||
@Override
|
||||
public MethodHandle apply(Class<?> c) {
|
||||
return lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "mix", long.class, long.class,
|
||||
Wrapper.asPrimitiveType(c));
|
||||
return JLA.stringConcatHelper("mix", methodType(long.class, long.class, Wrapper.asPrimitiveType(c)));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1759,7 +1741,7 @@ public final class StringConcatFactory {
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandle initCoder = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "initialCoder", long.class);
|
||||
MethodHandle initCoder = JLA.stringConcatHelper("initialCoder", methodType(long.class));
|
||||
INITIAL_CODER = (long) initCoder.invoke();
|
||||
} catch (Throwable e) {
|
||||
throw new AssertionError(e);
|
||||
@ -1768,9 +1750,9 @@ public final class StringConcatFactory {
|
||||
PREPENDERS = new ConcurrentHashMap<>();
|
||||
MIXERS = new ConcurrentHashMap<>();
|
||||
|
||||
SIMPLE = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "simpleConcat", String.class, Object.class, Object.class);
|
||||
NEW_STRING = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newString", String.class, byte[].class, long.class);
|
||||
NEW_ARRAY = lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "newArray", byte[].class, long.class);
|
||||
SIMPLE = JLA.stringConcatHelper("simpleConcat", methodType(String.class, Object.class, Object.class));
|
||||
NEW_STRING = JLA.stringConcatHelper("newString", methodType(String.class, byte[].class, long.class));
|
||||
NEW_ARRAY = JLA.stringConcatHelper( "newArray", methodType(byte[].class, long.class));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1784,7 +1766,7 @@ public final class StringConcatFactory {
|
||||
}
|
||||
|
||||
private static final MethodHandle OBJECT_INSTANCE =
|
||||
lookupStatic(Lookup.IMPL_LOOKUP, STRING_HELPER, "stringOf", String.class, Object.class);
|
||||
JLA.stringConcatHelper("stringOf", methodType(String.class, Object.class));
|
||||
|
||||
private static class FloatStringifiers {
|
||||
private static final MethodHandle FLOAT_INSTANCE =
|
||||
|
@ -27,19 +27,24 @@ package java.lang.invoke;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An entity that has a field or method type descriptor
|
||||
*
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
* An entity that has a type descriptor.
|
||||
*
|
||||
* @since 12
|
||||
*/
|
||||
public interface TypeDescriptor {
|
||||
/**
|
||||
* Return the type descriptor string for this instance, which must be either
|
||||
* a field type descriptor (JVMS 4.3.2) or method type descriptor (JVMS 4.3.3).
|
||||
* Returns the descriptor string for this {@code TypeDescriptor} object.
|
||||
*
|
||||
* @return the type descriptor
|
||||
* If this {@code TypeDescriptor} object can be described in nominal form,
|
||||
* then this method returns a type descriptor as specified in JVMS {@jvms 4.3}.
|
||||
* The result descriptor string can be used to produce
|
||||
* a {@linkplain java.lang.constant.ConstantDesc nominal descriptor}.
|
||||
*
|
||||
* Otherwise, the result string is not a type descriptor.
|
||||
* No {@linkplain java.lang.constant.ConstantDesc nominal descriptor}
|
||||
* can be produced from the result string.
|
||||
*
|
||||
* @return the descriptor string for this {@code TypeDescriptor} object
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
* @jvms 4.3.3 Method Descriptors
|
||||
*/
|
||||
@ -47,7 +52,10 @@ public interface TypeDescriptor {
|
||||
|
||||
|
||||
/**
|
||||
* An entity that has a field type descriptor
|
||||
* An entity that has a field type descriptor.
|
||||
* Field descriptors conforming to JVMS {@jvms 4.3.2} can be described
|
||||
* nominally via {@link Class#describeConstable Class::describeConstable};
|
||||
* otherwise they cannot be described nominally.
|
||||
*
|
||||
* @param <F> the class implementing {@linkplain TypeDescriptor.OfField}
|
||||
* @jvms 4.3.2 Field Descriptors
|
||||
@ -86,6 +94,9 @@ public interface TypeDescriptor {
|
||||
|
||||
/**
|
||||
* An entity that has a method type descriptor
|
||||
* Method descriptors conforming to JVMS {@jvms 4.3.3} can be described
|
||||
* nominally via {@link MethodType#describeConstable MethodType::describeConstable};
|
||||
* otherwise they cannot be described nominally.
|
||||
*
|
||||
* @param <F> the type representing field type descriptors
|
||||
* @param <M> the class implementing {@linkplain TypeDescriptor.OfMethod}
|
||||
|
@ -176,6 +176,12 @@ public class AccessibleObject implements AnnotatedElement {
|
||||
* to the caller and the package containing the declaring class is not open
|
||||
* to the caller's module. </p>
|
||||
*
|
||||
* <p> This method cannot be used to enable {@linkplain Field#set <em>write</em>}
|
||||
* access to a final field declared in a {@linkplain Class#isHidden() hidden class},
|
||||
* since such fields are not modifiable. The {@code accessible} flag when
|
||||
* {@code true} suppresses Java language access control checks to only
|
||||
* enable {@linkplain Field#get <em>read</em>} access to such fields.
|
||||
*
|
||||
* <p> If there is a security manager, its
|
||||
* {@code checkPermission} method is first called with a
|
||||
* {@code ReflectPermission("suppressAccessChecks")} permission.
|
||||
|
@ -721,10 +721,19 @@ class Field extends AccessibleObject implements Member {
|
||||
* the underlying field is inaccessible, the method throws an
|
||||
* {@code IllegalAccessException}.
|
||||
*
|
||||
* <p>If the underlying field is final, the method throws an
|
||||
* {@code IllegalAccessException} unless {@code setAccessible(true)}
|
||||
* has succeeded for this {@code Field} object
|
||||
* and the field is non-static. Setting a final field in this way
|
||||
* <p>If the underlying field is final, this {@code Field} object has
|
||||
* <em>write</em> access if and only if the following conditions are met:
|
||||
* <ul>
|
||||
* <li>{@link #setAccessible(boolean) setAccessible(true)} has succeeded for
|
||||
* this {@code Field} object;</li>
|
||||
* <li>the field is non-static; and</li>
|
||||
* <li>the field's declaring class is not a {@linkplain Class#isHidden()
|
||||
* hidden class}.</li>
|
||||
* </ul>
|
||||
* If any of the above checks is not met, this method throws an
|
||||
* {@code IllegalAccessException}.
|
||||
*
|
||||
* <p> Setting a final field in this way
|
||||
* is meaningful only during deserialization or reconstruction of
|
||||
* instances of classes with blank final fields, before they are
|
||||
* made available for access by other parts of a program. Use in
|
||||
@ -756,7 +765,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -791,7 +801,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -827,7 +838,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -863,7 +875,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -899,7 +912,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -935,7 +949,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -971,7 +986,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -1007,7 +1023,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
@ -1043,7 +1060,8 @@ class Field extends AccessibleObject implements Member {
|
||||
*
|
||||
* @throws IllegalAccessException if this {@code Field} object
|
||||
* is enforcing Java language access control and the underlying
|
||||
* field is either inaccessible or final.
|
||||
* field is either inaccessible or final;
|
||||
* or if this {@code Field} object has no write access.
|
||||
* @throws IllegalArgumentException if the specified object is not an
|
||||
* instance of the class or interface declaring the underlying
|
||||
* field (or a subclass or implementor thereof),
|
||||
|
@ -26,6 +26,9 @@
|
||||
package jdk.internal.access;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Executable;
|
||||
import java.lang.reflect.Method;
|
||||
@ -147,6 +150,14 @@ public interface JavaLangAccess {
|
||||
*/
|
||||
Class<?> defineClass(ClassLoader cl, String name, byte[] b, ProtectionDomain pd, String source);
|
||||
|
||||
/**
|
||||
* Defines a class with the given name to a class loader with
|
||||
* the given flags and class data.
|
||||
*
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineClass
|
||||
*/
|
||||
Class<?> defineClass(ClassLoader cl, Class<?> lookup, String name, byte[] b, ProtectionDomain pd, boolean initialize, int flags, Object classData);
|
||||
|
||||
/**
|
||||
* Returns a class loaded by the bootstrap class loader.
|
||||
*/
|
||||
@ -311,4 +322,21 @@ public interface JavaLangAccess {
|
||||
* @param cause set t's cause to new value
|
||||
*/
|
||||
void setCause(Throwable t, Throwable cause);
|
||||
|
||||
/**
|
||||
* Get protection domain of the given Class
|
||||
*/
|
||||
ProtectionDomain protectionDomain(Class<?> c);
|
||||
|
||||
/**
|
||||
* Get a method handle of string concat helper method
|
||||
*/
|
||||
MethodHandle stringConcatHelper(String name, MethodType methodType);
|
||||
|
||||
/*
|
||||
* Get the class data associated with the given class.
|
||||
* @param c the class
|
||||
* @see java.lang.invoke.MethodHandles.Lookup#defineHiddenClass(byte[], boolean, MethodHandles.Lookup.ClassOption...)
|
||||
*/
|
||||
Object classData(Class<?> c);
|
||||
}
|
||||
|
@ -25,9 +25,10 @@
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/** Used only for the first few invocations of a Constructor;
|
||||
afterward, switches to bytecode-based implementation */
|
||||
|
||||
@ -49,6 +50,7 @@ class NativeConstructorAccessorImpl extends ConstructorAccessorImpl {
|
||||
// because that kind of class can't be referred to by name, hence can't
|
||||
// be found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
&& !c.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
ConstructorAccessorImpl acc = (ConstructorAccessorImpl)
|
||||
new MethodAccessorGenerator().
|
||||
|
@ -25,9 +25,10 @@
|
||||
|
||||
package jdk.internal.reflect;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import sun.reflect.misc.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
|
||||
/** Used only for the first few invocations of a Method; afterward,
|
||||
switches to bytecode-based implementation */
|
||||
|
||||
@ -47,6 +48,7 @@ class NativeMethodAccessorImpl extends MethodAccessorImpl {
|
||||
// that kind of class can't be referred to by name, hence can't be
|
||||
// found from the generated bytecode.
|
||||
if (++numInvocations > ReflectionFactory.inflationThreshold()
|
||||
&& !method.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
MethodAccessorImpl acc = (MethodAccessorImpl)
|
||||
new MethodAccessorGenerator().
|
||||
|
@ -51,7 +51,7 @@ public class Reflection {
|
||||
fieldFilterMap = Map.of(
|
||||
Reflection.class, ALL_MEMBERS,
|
||||
AccessibleObject.class, ALL_MEMBERS,
|
||||
Class.class, Set.of("classLoader"),
|
||||
Class.class, Set.of("classLoader", "classData"),
|
||||
ClassLoader.class, ALL_MEMBERS,
|
||||
Constructor.class, ALL_MEMBERS,
|
||||
Field.class, ALL_MEMBERS,
|
||||
|
@ -200,7 +200,8 @@ public class ReflectionFactory {
|
||||
method = root;
|
||||
}
|
||||
|
||||
if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
if (noInflation && !method.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateMethod(method.getDeclaringClass(),
|
||||
method.getName(),
|
||||
@ -244,7 +245,8 @@ public class ReflectionFactory {
|
||||
return new BootstrapConstructorAccessorImpl(c);
|
||||
}
|
||||
|
||||
if (noInflation && !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
if (noInflation && !c.getDeclaringClass().isHidden()
|
||||
&& !ReflectUtil.isVMAnonymousClass(c.getDeclaringClass())) {
|
||||
return new MethodAccessorGenerator().
|
||||
generateConstructor(c.getDeclaringClass(),
|
||||
c.getParameterTypes(),
|
||||
|
@ -35,7 +35,7 @@ class UnsafeFieldAccessorFactory {
|
||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
||||
boolean isVolatile = Modifier.isVolatile(field.getModifiers());
|
||||
boolean isQualified = isFinal || isVolatile;
|
||||
boolean isReadOnly = isFinal && (isStatic || !override);
|
||||
boolean isReadOnly = isFinal && (isStatic || !override || field.getDeclaringClass().isHidden());
|
||||
if (isStatic) {
|
||||
// This code path does not guarantee that the field's
|
||||
// declaring class has been initialized, but it must be
|
||||
|
@ -154,11 +154,7 @@ public class BytecodeDescriptor {
|
||||
} else if (t == Object.class) {
|
||||
sb.append("Ljava/lang/Object;");
|
||||
} else {
|
||||
boolean lsemi = (!t.isArray());
|
||||
if (lsemi) sb.append('L');
|
||||
sb.append(t.getName().replace('.', '/'));
|
||||
if (lsemi) sb.append(';');
|
||||
sb.append(t.descriptorString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ static JNINativeMethod methods[] = {
|
||||
{"getSigners", "()[" OBJ, (void *)&JVM_GetClassSigners},
|
||||
{"setSigners", "([" OBJ ")V", (void *)&JVM_SetClassSigners},
|
||||
{"isArray", "()Z", (void *)&JVM_IsArrayClass},
|
||||
{"isHidden", "()Z", (void *)&JVM_IsHiddenClass},
|
||||
{"isPrimitive", "()Z", (void *)&JVM_IsPrimitiveClass},
|
||||
{"getModifiers", "()I", (void *)&JVM_GetClassModifiers},
|
||||
{"getDeclaredFields0","(Z)[" FLD, (void *)&JVM_GetClassDeclaredFields},
|
||||
|
@ -207,6 +207,66 @@ Java_java_lang_ClassLoader_defineClass2(JNIEnv *env,
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jclass JNICALL
|
||||
Java_java_lang_ClassLoader_defineClass0(JNIEnv *env,
|
||||
jclass cls,
|
||||
jobject loader,
|
||||
jclass lookup,
|
||||
jstring name,
|
||||
jbyteArray data,
|
||||
jint offset,
|
||||
jint length,
|
||||
jobject pd,
|
||||
jboolean initialize,
|
||||
jint flags,
|
||||
jobject classData)
|
||||
{
|
||||
jbyte *body;
|
||||
char *utfName;
|
||||
jclass result = 0;
|
||||
char buf[128];
|
||||
|
||||
if (data == NULL) {
|
||||
JNU_ThrowNullPointerException(env, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Work around 4153825. malloc crashes on Solaris when passed a
|
||||
* negative size.
|
||||
*/
|
||||
if (length < 0) {
|
||||
JNU_ThrowArrayIndexOutOfBoundsException(env, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
body = (jbyte *)malloc(length);
|
||||
if (body == 0) {
|
||||
JNU_ThrowOutOfMemoryError(env, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*env)->GetByteArrayRegion(env, data, offset, length, body);
|
||||
|
||||
if ((*env)->ExceptionOccurred(env))
|
||||
goto free_body;
|
||||
|
||||
if (name != NULL) {
|
||||
utfName = getUTF(env, name, buf, sizeof(buf));
|
||||
if (utfName == NULL) {
|
||||
goto free_body;
|
||||
}
|
||||
fixClassname(utfName);
|
||||
} else {
|
||||
utfName = NULL;
|
||||
}
|
||||
|
||||
return JVM_LookupDefineClass(env, lookup, utfName, body, length, pd, initialize, flags, classData);
|
||||
|
||||
free_body:
|
||||
free(body);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns NULL if class not found.
|
||||
*/
|
||||
|
@ -387,6 +387,9 @@ public interface Instrumentation {
|
||||
|
||||
/**
|
||||
* Returns an array of all classes currently loaded by the JVM.
|
||||
* The returned array includes all classes and interfaces, including
|
||||
* {@linkplain Class#isHidden hidden classes or interfaces}, and array classes
|
||||
* of all types.
|
||||
*
|
||||
* @return an array containing all the classes loaded by the JVM, zero-length if there are none
|
||||
*/
|
||||
@ -395,12 +398,21 @@ public interface Instrumentation {
|
||||
getAllLoadedClasses();
|
||||
|
||||
/**
|
||||
* Returns an array of all classes for which <code>loader</code> is an initiating loader.
|
||||
* If the supplied loader is <code>null</code>, classes initiated by the bootstrap class
|
||||
* loader are returned.
|
||||
* Returns an array of all classes which {@code loader} can find by name
|
||||
* via {@link ClassLoader#loadClass(String, boolean) ClassLoader::loadClass},
|
||||
* {@link Class#forName(String) Class::forName} and bytecode linkage.
|
||||
* That is, all classes for which {@code loader} has been recorded as
|
||||
* an initiating loader. If the supplied {@code loader} is {@code null},
|
||||
* classes that the bootstrap class loader can find by name are returned.
|
||||
* <p>
|
||||
* The returned array does not include {@linkplain Class#isHidden()
|
||||
* hidden classes or interfaces} or array classes whose
|
||||
* {@linkplain Class#componentType() element type} is a
|
||||
* {@linkplain Class#isHidden() hidden class or interface}.
|
||||
* as they cannot be discovered by any class loader.
|
||||
*
|
||||
* @param loader the loader whose initiated class list will be returned
|
||||
* @return an array containing all the classes for which loader is an initiating loader,
|
||||
* @return an array containing all classes which {@code loader} can find by name;
|
||||
* zero-length if there are none
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
|
@ -68,12 +68,14 @@ import com.sun.tools.javac.comp.Modules;
|
||||
import com.sun.tools.javac.main.Arguments;
|
||||
import com.sun.tools.javac.main.JavaCompiler;
|
||||
import com.sun.tools.javac.model.JavacElements;
|
||||
import com.sun.tools.javac.platform.PlatformDescription;
|
||||
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
|
||||
import com.sun.tools.javac.tree.JCTree.LetExpr;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.DefinedBy;
|
||||
import com.sun.tools.javac.util.DefinedBy.Api;
|
||||
import com.sun.tools.javac.util.Log;
|
||||
import com.sun.tools.javac.util.Options;
|
||||
|
||||
/**
|
||||
* A pool of reusable JavacTasks. When a task is no valid anymore, it is returned to the pool,
|
||||
@ -252,6 +254,7 @@ public class JavacTaskPool {
|
||||
drop(JavacTask.class);
|
||||
drop(JavacTrees.class);
|
||||
drop(JavacElements.class);
|
||||
drop(PlatformDescription.class);
|
||||
|
||||
if (ht.get(Log.logKey) instanceof ReusableLog) {
|
||||
//log already inited - not first round
|
||||
@ -266,6 +269,7 @@ public class JavacTaskPool {
|
||||
Annotate.instance(this).newRound();
|
||||
CompileStates.instance(this).clear();
|
||||
MultiTaskListener.instance(this).clear();
|
||||
Options.instance(this).clear();
|
||||
|
||||
//find if any of the roots have redefined java.* classes
|
||||
Symtab syms = Symtab.instance(this);
|
||||
|
@ -1231,10 +1231,14 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
||||
|
||||
public static class RootPackageSymbol extends PackageSymbol {
|
||||
public final MissingInfoHandler missingInfoHandler;
|
||||
public final boolean allowPrivateInvokeVirtual;
|
||||
|
||||
public RootPackageSymbol(Name name, Symbol owner, MissingInfoHandler missingInfoHandler) {
|
||||
public RootPackageSymbol(Name name, Symbol owner,
|
||||
MissingInfoHandler missingInfoHandler,
|
||||
boolean allowPrivateInvokeVirtual) {
|
||||
super(name, owner);
|
||||
this.missingInfoHandler = missingInfoHandler;
|
||||
this.allowPrivateInvokeVirtual = allowPrivateInvokeVirtual;
|
||||
}
|
||||
|
||||
}
|
||||
@ -2311,7 +2315,7 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
||||
} else {
|
||||
if (refSym.isStatic()) {
|
||||
return ClassFile.REF_invokeStatic;
|
||||
} else if ((refSym.flags() & PRIVATE) != 0) {
|
||||
} else if ((refSym.flags() & PRIVATE) != 0 && !allowPrivateInvokeVirtual()) {
|
||||
return ClassFile.REF_invokeSpecial;
|
||||
} else if (refSym.enclClass().isInterface()) {
|
||||
return ClassFile.REF_invokeInterface;
|
||||
@ -2322,6 +2326,13 @@ public abstract class Symbol extends AnnoConstruct implements PoolConstant, Elem
|
||||
}
|
||||
}
|
||||
|
||||
private boolean allowPrivateInvokeVirtual() {
|
||||
Symbol rootPack = this;
|
||||
while (rootPack != null && !(rootPack instanceof RootPackageSymbol)) {
|
||||
rootPack = rootPack.owner;
|
||||
}
|
||||
return rootPack != null && ((RootPackageSymbol) rootPack).allowPrivateInvokeVirtual;
|
||||
}
|
||||
@Override
|
||||
public int poolTag() {
|
||||
return ClassFile.CONSTANT_MethodHandle;
|
||||
|
@ -54,6 +54,7 @@ import com.sun.tools.javac.code.Type.MethodType;
|
||||
import com.sun.tools.javac.code.Type.UnknownType;
|
||||
import com.sun.tools.javac.code.Types.UniqueType;
|
||||
import com.sun.tools.javac.comp.Modules;
|
||||
import com.sun.tools.javac.jvm.Target;
|
||||
import com.sun.tools.javac.util.Assert;
|
||||
import com.sun.tools.javac.util.Context;
|
||||
import com.sun.tools.javac.util.Convert;
|
||||
@ -389,7 +390,10 @@ public class Symtab {
|
||||
|
||||
MissingInfoHandler missingInfoHandler = MissingInfoHandler.instance(context);
|
||||
|
||||
rootPackage = new RootPackageSymbol(names.empty, null, missingInfoHandler);
|
||||
Target target = Target.instance(context);
|
||||
rootPackage = new RootPackageSymbol(names.empty, null,
|
||||
missingInfoHandler,
|
||||
target.runtimeUseNestAccess());
|
||||
|
||||
// create the basic builtin symbols
|
||||
unnamedModule = new ModuleSymbol(names.empty, null) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user