Mercurial > hg > graal-jvmci-8
diff src/share/vm/prims/unsafe.cpp @ 431:a45484ea312d
6653858: dynamic languages need to be able to load anonymous classes
Summary: low-level privileged sun.misc.Unsafe.defineAnonymousClass
Reviewed-by: kvn
author | jrose |
---|---|
date | Wed, 12 Nov 2008 22:33:26 -0800 |
parents | 1ee8caae33af |
children | df6caf649ff7 |
line wrap: on
line diff
--- a/src/share/vm/prims/unsafe.cpp Wed Nov 12 11:01:31 2008 -0800 +++ b/src/share/vm/prims/unsafe.cpp Wed Nov 12 22:33:26 2008 -0800 @@ -837,6 +837,163 @@ } 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 @@ {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 @@ } } } + 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)) {