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:
John R Rose 2008-11-12 22:33:26 -08:00
parent 015a08b3e8
commit 849e0ffb04
19 changed files with 607 additions and 37 deletions

View file

@ -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)) {