diff options
author | Mathieu Chartier <mathieuc@google.com> | 2015-04-24 16:55:16 -0700 |
---|---|---|
committer | Andreas Gampe <agampe@google.com> | 2015-04-25 22:08:16 +0000 |
commit | f36cb5f65cb150151aa40b23937e2b0ad75cc546 (patch) | |
tree | 25365c69a089bc491a4b070ee3403be964cfc606 | |
parent | 33984b4d6b79b2d65258e69506e5669be704db82 (diff) | |
download | android_art-f36cb5f65cb150151aa40b23937e2b0ad75cc546.tar.gz android_art-f36cb5f65cb150151aa40b23937e2b0ad75cc546.tar.bz2 android_art-f36cb5f65cb150151aa40b23937e2b0ad75cc546.zip |
Move Class.newInstance to native
Avoids 1 allocation and several JNI transitions.
Before:
Class_classNewInstance: 4462.39 ns; σ=39.42 ns @ 3 trials
After:
Class_classNewInstance: 1073.39 ns; σ=24.14 ns @ 10 trials
Bug: 20269715
Bug: 20566996
Change-Id: Icd52155ce79a978a4d869855bfdfd7735abd8187
-rw-r--r-- | compiler/jit/jit_compiler.cc | 2 | ||||
-rw-r--r-- | runtime/native/java_lang_Class.cc | 74 | ||||
-rw-r--r-- | runtime/native/java_lang_reflect_Constructor.cc | 53 | ||||
-rw-r--r-- | runtime/reflection.cc | 32 | ||||
-rw-r--r-- | runtime/reflection.h | 9 |
5 files changed, 126 insertions, 44 deletions
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 6a085482ff..7c400ee82c 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -62,7 +62,7 @@ extern "C" bool jit_compile_method(void* handle, mirror::ArtMethod* method, Thre JitCompiler::JitCompiler() : total_time_(0) { auto* pass_manager_options = new PassManagerOptions; - pass_manager_options->SetDisablePassList("GVN,DCE"); + pass_manager_options->SetDisablePassList("GVN,DCE,GVNCleanup"); compiler_options_.reset(new CompilerOptions( CompilerOptions::kDefaultCompilerFilter, CompilerOptions::kDefaultHugeMethodThreshold, diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index b0d923b10c..48a8bc76a0 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -29,6 +29,7 @@ #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" +#include "reflection.h" #include "scoped_thread_state_change.h" #include "scoped_fast_native_object_access.h" #include "ScopedLocalRef.h" @@ -391,8 +392,8 @@ static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, nullptr; } -jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, - jboolean publicOnly) { +static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, + jboolean publicOnly) { ScopedFastNativeObjectAccess soa(env); StackHandleScope<5> hs(soa.Self()); auto* klass = DecodeClass(soa, javaThis); @@ -457,6 +458,74 @@ jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, return soa.AddLocalReference<jobjectArray>(ret.Get()); } +static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<4> hs(soa.Self()); + auto klass = hs.NewHandle(DecodeClass(soa, javaThis)); + if (UNLIKELY(klass->GetPrimitiveType() != 0 || klass->IsInterface() || klass->IsArrayClass() || + klass->IsAbstract())) { + soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", + "%s cannot be instantiated", PrettyClass(klass.Get()).c_str()); + return nullptr; + } + auto caller = hs.NewHandle<mirror::Class>(nullptr); + // Verify that we can access the class. + if (!klass->IsPublic()) { + caller.Assign(GetCallingClass(soa.Self(), 1)); + if (caller.Get() != nullptr && !caller->CanAccess(klass.Get())) { + soa.Self()->ThrowNewExceptionF( + "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s", + PrettyClass(klass.Get()).c_str(), PrettyClass(caller.Get()).c_str()); + return nullptr; + } + } + auto* constructor = klass->GetDeclaredConstructor( + soa.Self(), NullHandle<mirror::ObjectArray<mirror::Class>>()); + if (UNLIKELY(constructor == nullptr)) { + soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", + "%s has no zero argument constructor", + PrettyClass(klass.Get()).c_str()); + return nullptr; + } + auto receiver = hs.NewHandle(klass->AllocObject(soa.Self())); + if (UNLIKELY(receiver.Get() == nullptr)) { + soa.Self()->AssertPendingOOMException(); + return nullptr; + } + // Verify that we can access the constructor. + auto* declaring_class = constructor->GetDeclaringClass(); + if (!constructor->IsPublic()) { + if (caller.Get() == nullptr) { + caller.Assign(GetCallingClass(soa.Self(), 1)); + } + if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess( + soa.Self(), receiver.Get(), declaring_class, constructor->GetAccessFlags(), + caller.Get()))) { + soa.Self()->ThrowNewExceptionF( + "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s", + PrettyMethod(constructor).c_str(), PrettyClass(caller.Get()).c_str()); + return nullptr; + } + } + // Ensure that we are initialized. + if (UNLIKELY(!declaring_class->IsInitialized())) { + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized( + soa.Self(), hs.NewHandle(declaring_class), true, true)) { + soa.Self()->AssertPendingException(); + return nullptr; + } + } + // Invoke the constructor. + JValue result; + uint32_t args[1] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(receiver.Get())) }; + constructor->Invoke(soa.Self(), args, sizeof(args), &result, "V"); + if (UNLIKELY(soa.Self()->IsExceptionPending())) { + return nullptr; + } + // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. + return soa.AddLocalReference<jobject>(receiver.Get()); +} + static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, classForName, "!(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"), @@ -474,6 +543,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Class, getNameNative, "!()Ljava/lang/String;"), NATIVE_METHOD(Class, getProxyInterfaces, "!()[Ljava/lang/Class;"), NATIVE_METHOD(Class, getPublicDeclaredFields, "!()[Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, newInstance, "!()Ljava/lang/Object;"), }; void register_java_lang_Class(JNIEnv* env) { diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index c33f81a211..04d2e5e324 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -29,29 +29,43 @@ namespace art { -static ALWAYS_INLINE inline jobject NewInstanceHelper( - JNIEnv* env, jobject javaMethod, jobjectArray javaArgs, size_t num_frames) { +/* + * We get here through Constructor.newInstance(). The Constructor object + * would not be available if the constructor weren't public (per the + * definition of Class.getConstructor), so we can skip the method access + * check. We can also safely assume the constructor isn't associated + * with an interface, array, or primitive class. If this is coming from + * native, it is OK to avoid access checks since JNI does not enforce them. + */ +static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); mirror::Method* m = soa.Decode<mirror::Method*>(javaMethod); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); if (UNLIKELY(c->IsAbstract())) { - soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", - "Can't instantiate %s %s", + soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;", "Can't instantiate %s %s", c->IsInterface() ? "interface" : "abstract class", PrettyDescriptor(c.Get()).c_str()); return nullptr; } - + // Verify that we can access the class (only for debug since the above comment). + if (kIsDebugBuild && !c->IsPublic()) { + auto* caller = GetCallingClass(soa.Self(), 1); + // If caller is null, then we called from JNI, just avoid the check since JNI avoids most + // access checks anyways. TODO: Investigate if this the correct behavior. + if (caller != nullptr && !caller->CanAccess(c.Get())) { + soa.Self()->ThrowNewExceptionF( + "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s", + PrettyClass(c.Get()).c_str(), PrettyClass(caller).c_str()); + return nullptr; + } + } if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(soa.Self(), c, true, true)) { DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } - bool movable = true; - if (!kMovingMethods && c->IsArtMethodClass()) { - movable = false; - } else if (!kMovingClasses && c->IsClassClass()) { + if (!kMovingClasses && c->IsClassClass()) { movable = false; } mirror::Object* receiver = @@ -59,33 +73,14 @@ static ALWAYS_INLINE inline jobject NewInstanceHelper( if (receiver == nullptr) { return nullptr; } - jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); - InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, num_frames); - + InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 1); // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. return javaReceiver; } -/* - * We get here through Constructor.newInstance(). The Constructor object - * would not be available if the constructor weren't public (per the - * definition of Class.getConstructor), so we can skip the method access - * check. We can also safely assume the constructor isn't associated - * with an interface, array, or primitive class. - */ -static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { - return NewInstanceHelper(env, javaMethod, javaArgs, 1); -} - -static jobject Constructor_newInstanceTwoFrames(JNIEnv* env, jobject javaMethod, - jobjectArray javaArgs) { - return NewInstanceHelper(env, javaMethod, javaArgs, 2); -} - static JNINativeMethod gMethods[] = { NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"), - NATIVE_METHOD(Constructor, newInstanceTwoFrames, "!([Ljava/lang/Object;)Ljava/lang/Object;"), }; void register_java_lang_reflect_Constructor(JNIEnv* env) { diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 3099094ed4..a2ce0cbf12 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -799,40 +799,48 @@ bool UnboxPrimitiveForField(mirror::Object* o, mirror::Class* dst_class, ArtFiel return UnboxPrimitive(o, dst_class, f, unboxed_value); } -bool UnboxPrimitiveForResult(mirror::Object* o, - mirror::Class* dst_class, JValue* unboxed_value) { +bool UnboxPrimitiveForResult(mirror::Object* o, mirror::Class* dst_class, JValue* unboxed_value) { return UnboxPrimitive(o, dst_class, nullptr, unboxed_value); } +mirror::Class* GetCallingClass(Thread* self, size_t num_frames) { + NthCallerVisitor visitor(self, num_frames); + visitor.WalkStack(); + return visitor.caller != nullptr ? visitor.caller->GetDeclaringClass() : nullptr; +} + bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) { if ((access_flags & kAccPublic) != 0) { return true; } - NthCallerVisitor visitor(self, num_frames); - visitor.WalkStack(); - if (UNLIKELY(visitor.caller == nullptr)) { + auto* klass = GetCallingClass(self, num_frames); + if (UNLIKELY(klass == nullptr)) { // The caller is an attached native thread. return false; } - mirror::Class* caller_class = visitor.caller->GetDeclaringClass(); - if (caller_class == declaring_class) { + *calling_class = klass; + return VerifyAccess(self, obj, declaring_class, access_flags, klass); +} + +bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, + uint32_t access_flags, mirror::Class* calling_class) { + if (calling_class == declaring_class) { return true; } ScopedAssertNoThreadSuspension sants(self, "verify-access"); - *calling_class = caller_class; if ((access_flags & kAccPrivate) != 0) { return false; } if ((access_flags & kAccProtected) != 0) { - if (obj != nullptr && !obj->InstanceOf(caller_class) && - !declaring_class->IsInSamePackage(caller_class)) { + if (obj != nullptr && !obj->InstanceOf(calling_class) && + !declaring_class->IsInSamePackage(calling_class)) { return false; - } else if (declaring_class->IsAssignableFrom(caller_class)) { + } else if (declaring_class->IsAssignableFrom(calling_class)) { return true; } } - return declaring_class->IsInSamePackage(caller_class); + return declaring_class->IsInSamePackage(calling_class); } void InvalidReceiverError(mirror::Object* o, mirror::Class* c) { diff --git a/runtime/reflection.h b/runtime/reflection.h index c63f858129..6305d68bd5 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -77,6 +77,15 @@ bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_cl uint32_t access_flags, mirror::Class** calling_class, size_t num_frames) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +// This version takes a known calling class. +bool VerifyAccess(Thread* self, mirror::Object* obj, mirror::Class* declaring_class, + uint32_t access_flags, mirror::Class* calling_class) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + +// Get the calling class by using a stack visitor, may return null for unattached native threads. +mirror::Class* GetCallingClass(Thread* self, size_t num_frames) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void InvalidReceiverError(mirror::Object* o, mirror::Class* c) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); |