mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
6653858: dynamic languages need to be able to load anonymous classes
Low-level privileged sun.misc.Unsafe.defineAnonymousClass Reviewed-by: kvn
This commit is contained in:
parent
015a08b3e8
commit
849e0ffb04
19 changed files with 607 additions and 37 deletions
|
|
@ -837,6 +837,163 @@ UNSAFE_ENTRY(jclass, Unsafe_DefineClass1(JNIEnv *env, jobject unsafe, jstring na
|
|||
}
|
||||
UNSAFE_END
|
||||
|
||||
#define DAC_Args CLS"[B["OBJ
|
||||
// define a class but do not make it known to the class loader or system dictionary
|
||||
// - host_class: supplies context for linkage, access control, protection domain, and class loader
|
||||
// - data: bytes of a class file, a raw memory address (length gives the number of bytes)
|
||||
// - cp_patches: where non-null entries exist, they replace corresponding CP entries in data
|
||||
|
||||
// When you load an anonymous class U, it works as if you changed its name just before loading,
|
||||
// to a name that you will never use again. Since the name is lost, no other class can directly
|
||||
// link to any member of U. Just after U is loaded, the only way to use it is reflectively,
|
||||
// through java.lang.Class methods like Class.newInstance.
|
||||
|
||||
// Access checks for linkage sites within U continue to follow the same rules as for named classes.
|
||||
// The package of an anonymous class is given by the package qualifier on the name under which it was loaded.
|
||||
// An anonymous class also has special privileges to access any member of its host class.
|
||||
// This is the main reason why this loading operation is unsafe. The purpose of this is to
|
||||
// allow language implementations to simulate "open classes"; a host class in effect gets
|
||||
// new code when an anonymous class is loaded alongside it. A less convenient but more
|
||||
// standard way to do this is with reflection, which can also be set to ignore access
|
||||
// restrictions.
|
||||
|
||||
// Access into an anonymous class is possible only through reflection. Therefore, there
|
||||
// are no special access rules for calling into an anonymous class. The relaxed access
|
||||
// rule for the host class is applied in the opposite direction: A host class reflectively
|
||||
// access one of its anonymous classes.
|
||||
|
||||
// If you load the same bytecodes twice, you get two different classes. You can reload
|
||||
// the same bytecodes with or without varying CP patches.
|
||||
|
||||
// By using the CP patching array, you can have a new anonymous class U2 refer to an older one U1.
|
||||
// The bytecodes for U2 should refer to U1 by a symbolic name (doesn't matter what the name is).
|
||||
// The CONSTANT_Class entry for that name can be patched to refer directly to U1.
|
||||
|
||||
// This allows, for example, U2 to use U1 as a superclass or super-interface, or as
|
||||
// an outer class (so that U2 is an anonymous inner class of anonymous U1).
|
||||
// It is not possible for a named class, or an older anonymous class, to refer by
|
||||
// name (via its CP) to a newer anonymous class.
|
||||
|
||||
// CP patching may also be used to modify (i.e., hack) the names of methods, classes,
|
||||
// or type descriptors used in the loaded anonymous class.
|
||||
|
||||
// Finally, CP patching may be used to introduce "live" objects into the constant pool,
|
||||
// instead of "dead" strings. A compiled statement like println((Object)"hello") can
|
||||
// be changed to println(greeting), where greeting is an arbitrary object created before
|
||||
// the anonymous class is loaded. This is useful in dynamic languages, in which
|
||||
// various kinds of metaobjects must be introduced as constants into bytecode.
|
||||
// Note the cast (Object), which tells the verifier to expect an arbitrary object,
|
||||
// not just a literal string. For such ldc instructions, the verifier uses the
|
||||
// type Object instead of String, if the loaded constant is not in fact a String.
|
||||
|
||||
static oop
|
||||
Unsafe_DefineAnonymousClass_impl(JNIEnv *env,
|
||||
jclass host_class, jbyteArray data, jobjectArray cp_patches_jh,
|
||||
HeapWord* *temp_alloc,
|
||||
TRAPS) {
|
||||
|
||||
if (UsePerfData) {
|
||||
ClassLoader::unsafe_defineClassCallCounter()->inc();
|
||||
}
|
||||
|
||||
if (data == NULL) {
|
||||
THROW_0(vmSymbols::java_lang_NullPointerException());
|
||||
}
|
||||
|
||||
jint length = typeArrayOop(JNIHandles::resolve_non_null(data))->length();
|
||||
jint word_length = (length + sizeof(HeapWord)-1) / sizeof(HeapWord);
|
||||
HeapWord* body = NEW_C_HEAP_ARRAY(HeapWord, word_length);
|
||||
if (body == NULL) {
|
||||
THROW_0(vmSymbols::java_lang_OutOfMemoryError());
|
||||
}
|
||||
|
||||
// caller responsible to free it:
|
||||
(*temp_alloc) = body;
|
||||
|
||||
{
|
||||
jbyte* array_base = typeArrayOop(JNIHandles::resolve_non_null(data))->byte_at_addr(0);
|
||||
Copy::conjoint_words((HeapWord*) array_base, body, word_length);
|
||||
}
|
||||
|
||||
u1* class_bytes = (u1*) body;
|
||||
int class_bytes_length = (int) length;
|
||||
if (class_bytes_length < 0) class_bytes_length = 0;
|
||||
if (class_bytes == NULL
|
||||
|| host_class == NULL
|
||||
|| length != class_bytes_length)
|
||||
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
|
||||
|
||||
objArrayHandle cp_patches_h;
|
||||
if (cp_patches_jh != NULL) {
|
||||
oop p = JNIHandles::resolve_non_null(cp_patches_jh);
|
||||
if (!p->is_objArray())
|
||||
THROW_0(vmSymbols::java_lang_IllegalArgumentException());
|
||||
cp_patches_h = objArrayHandle(THREAD, (objArrayOop)p);
|
||||
}
|
||||
|
||||
KlassHandle host_klass(THREAD, java_lang_Class::as_klassOop(JNIHandles::resolve_non_null(host_class)));
|
||||
const char* host_source = host_klass->external_name();
|
||||
Handle host_loader(THREAD, host_klass->class_loader());
|
||||
Handle host_domain(THREAD, host_klass->protection_domain());
|
||||
|
||||
GrowableArray<Handle>* cp_patches = NULL;
|
||||
if (cp_patches_h.not_null()) {
|
||||
int alen = cp_patches_h->length();
|
||||
for (int i = alen-1; i >= 0; i--) {
|
||||
oop p = cp_patches_h->obj_at(i);
|
||||
if (p != NULL) {
|
||||
Handle patch(THREAD, p);
|
||||
if (cp_patches == NULL)
|
||||
cp_patches = new GrowableArray<Handle>(i+1, i+1, Handle());
|
||||
cp_patches->at_put(i, patch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClassFileStream st(class_bytes, class_bytes_length, (char*) host_source);
|
||||
|
||||
instanceKlassHandle anon_klass;
|
||||
{
|
||||
symbolHandle no_class_name;
|
||||
klassOop anonk = SystemDictionary::parse_stream(no_class_name,
|
||||
host_loader, host_domain,
|
||||
&st, host_klass, cp_patches,
|
||||
CHECK_NULL);
|
||||
if (anonk == NULL) return NULL;
|
||||
anon_klass = instanceKlassHandle(THREAD, anonk);
|
||||
}
|
||||
|
||||
// let caller initialize it as needed...
|
||||
|
||||
return anon_klass->java_mirror();
|
||||
}
|
||||
|
||||
UNSAFE_ENTRY(jclass, Unsafe_DefineAnonymousClass(JNIEnv *env, jobject unsafe, jclass host_class, jbyteArray data, jobjectArray cp_patches_jh))
|
||||
{
|
||||
UnsafeWrapper("Unsafe_DefineAnonymousClass");
|
||||
ResourceMark rm(THREAD);
|
||||
|
||||
HeapWord* temp_alloc = NULL;
|
||||
|
||||
jobject res_jh = NULL;
|
||||
|
||||
{ oop res_oop = Unsafe_DefineAnonymousClass_impl(env,
|
||||
host_class, data, cp_patches_jh,
|
||||
&temp_alloc, THREAD);
|
||||
if (res_oop != NULL)
|
||||
res_jh = JNIHandles::make_local(env, res_oop);
|
||||
}
|
||||
|
||||
// try/finally clause:
|
||||
if (temp_alloc != NULL) {
|
||||
FREE_C_HEAP_ARRAY(HeapWord, temp_alloc);
|
||||
}
|
||||
|
||||
return (jclass) res_jh;
|
||||
}
|
||||
UNSAFE_END
|
||||
|
||||
|
||||
|
||||
UNSAFE_ENTRY(void, Unsafe_MonitorEnter(JNIEnv *env, jobject unsafe, jobject jobj))
|
||||
UnsafeWrapper("Unsafe_MonitorEnter");
|
||||
|
|
@ -1292,6 +1449,9 @@ JNINativeMethod memcopy_methods_15[] = {
|
|||
{CC"copyMemory", CC"("ADR ADR"J)V", FN_PTR(Unsafe_CopyMemory)}
|
||||
};
|
||||
|
||||
JNINativeMethod anonk_methods[] = {
|
||||
{CC"defineAnonymousClass", CC"("DAC_Args")"CLS, FN_PTR(Unsafe_DefineAnonymousClass)},
|
||||
};
|
||||
|
||||
#undef CC
|
||||
#undef FN_PTR
|
||||
|
|
@ -1354,6 +1514,15 @@ JVM_ENTRY(void, JVM_RegisterUnsafeMethods(JNIEnv *env, jclass unsafecls))
|
|||
}
|
||||
}
|
||||
}
|
||||
if (AnonymousClasses) {
|
||||
env->RegisterNatives(unsafecls, anonk_methods, sizeof(anonk_methods)/sizeof(JNINativeMethod));
|
||||
if (env->ExceptionOccurred()) {
|
||||
if (PrintMiscellaneous && (Verbose || WizardMode)) {
|
||||
tty->print_cr("Warning: SDK 1.7 Unsafe.defineClass (anonymous version) not found.");
|
||||
}
|
||||
env->ExceptionClear();
|
||||
}
|
||||
}
|
||||
int status = env->RegisterNatives(unsafecls, methods, sizeof(methods)/sizeof(JNINativeMethod));
|
||||
if (env->ExceptionOccurred()) {
|
||||
if (PrintMiscellaneous && (Verbose || WizardMode)) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue