--- a/hotspot/src/share/vm/prims/unsafe.cpp Wed Nov 12 11:01:31 2008 -0800
+++ b/hotspot/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)) {