diff options
| author | 2017-05-22 16:08:52 -0700 | |
|---|---|---|
| committer | 2018-06-28 14:33:23 -0700 | |
| commit | 8ad7a3b4f7875546f471f8955967da17c9728a00 (patch) | |
| tree | 2a5dbaf6752357b537fe956ec18f09ab3d8ee79f | |
| parent | d20a4d76c33cd6e609ad6b1b3cde09fdcbdde05c (diff) | |
ART: Improve Constructor.newInstance
Special-case InvokeMethod to be constructor-specific so as to avoid
unnecessary and duplicate checks. Refactor for some code sharing.
Reduces Constructor.newInstance for Integer by approximately 15%
(ten runs of 10000000 invocations, reporting mean per invocation,
host x86-64 optimizing, perf stat numbers are for complete run
and do not exclude, for example, setup and prologue GC):
Before After
(perf stat)
Instructions 2503.4 2149.8
Branches 450.8 384.4
Branch-misses 1.83 0.85
(time)
Time (ns) 335.17 278.58
Bug: 20269715
Test: mmma art
Test: m test-art-host
Change-Id: Id105e542a19d72efaace60ad39fcef5e42dde006
| -rw-r--r-- | runtime/native/java_lang_reflect_Constructor.cc | 11 | ||||
| -rw-r--r-- | runtime/reflection.cc | 134 | ||||
| -rw-r--r-- | runtime/reflection.h | 8 |
3 files changed, 116 insertions, 37 deletions
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc index a961cb2597..e54674f72b 100644 --- a/runtime/native/java_lang_reflect_Constructor.cc +++ b/runtime/native/java_lang_reflect_Constructor.cc @@ -60,6 +60,7 @@ static jobjectArray Constructor_getExceptionTypes(JNIEnv* env, jobject javaMetho static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) { ScopedFastNativeObjectAccess soa(env); ObjPtr<mirror::Constructor> m = soa.Decode<mirror::Constructor>(javaMethod); + ArtMethod* constructor_art_method = m->GetArtMethod(); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c(hs.NewHandle(m->GetDeclaringClass())); if (UNLIKELY(c->IsAbstract())) { @@ -100,18 +101,20 @@ static jobject Constructor_newInstance0(JNIEnv* env, jobject javaMethod, jobject } // String constructor is replaced by a StringFactory method in InvokeMethod. - if (c->IsStringClass()) { + if (UNLIKELY(c->IsStringClass())) { return InvokeMethod(soa, javaMethod, nullptr, javaArgs, 2); } ObjPtr<mirror::Object> receiver = movable ? c->AllocObject(soa.Self()) : c->AllocNonMovableObject(soa.Self()); - if (receiver == nullptr) { + if (UNLIKELY(receiver == nullptr)) { + DCHECK(soa.Self()->IsExceptionPending()); return nullptr; } jobject javaReceiver = soa.AddLocalReference<jobject>(receiver); - InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, 2); - // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod. + + InvokeConstructor(soa, constructor_art_method, receiver, javaArgs); + return javaReceiver; } diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 9e9c33caa8..646de757e0 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -457,6 +457,64 @@ void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); } +ALWAYS_INLINE +bool CheckArgsForInvokeMethod(ArtMethod* np_method, + ObjPtr<mirror::ObjectArray<mirror::Object>> objects) + REQUIRES_SHARED(Locks::mutator_lock_) { + const DexFile::TypeList* classes = np_method->GetParameterTypeList(); + uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); + uint32_t arg_count = (objects == nullptr) ? 0 : objects->GetLength(); + if (UNLIKELY(arg_count != classes_size)) { + ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d", + classes_size, arg_count).c_str()); + return false; + } + return true; +} + +ALWAYS_INLINE +bool InvokeMethodImpl(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* m, + ArtMethod* np_method, + ObjPtr<mirror::Object> receiver, + ObjPtr<mirror::ObjectArray<mirror::Object>> objects, + const char** shorty, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + // Invoke the method. + uint32_t shorty_len = 0; + *shorty = np_method->GetShorty(&shorty_len); + ArgArray arg_array(*shorty, shorty_len); + if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { + CHECK(soa.Self()->IsExceptionPending()); + return false; + } + + InvokeWithArgArray(soa, m, &arg_array, result, *shorty); + + // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. + if (soa.Self()->IsExceptionPending()) { + // If we get another exception when we are trying to wrap, then just use that instead. + ScopedLocalRef<jthrowable> th(soa.Env(), soa.Env()->ExceptionOccurred()); + soa.Self()->ClearException(); + jclass exception_class = soa.Env()->FindClass("java/lang/reflect/InvocationTargetException"); + if (exception_class == nullptr) { + soa.Self()->AssertPendingException(); + return false; + } + jmethodID mid = soa.Env()->GetMethodID(exception_class, "<init>", "(Ljava/lang/Throwable;)V"); + CHECK(mid != nullptr); + jobject exception_instance = soa.Env()->NewObject(exception_class, mid, th.get()); + if (exception_instance == nullptr) { + soa.Self()->AssertPendingException(); + return false; + } + soa.Env()->Throw(reinterpret_cast<jthrowable>(exception_instance)); + return false; + } + + return true; +} + } // anonymous namespace JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, @@ -632,12 +690,7 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM ObjPtr<mirror::ObjectArray<mirror::Object>> objects = soa.Decode<mirror::ObjectArray<mirror::Object>>(javaArgs); auto* np_method = m->GetInterfaceMethodIfProxy(kRuntimePointerSize); - const DexFile::TypeList* classes = np_method->GetParameterTypeList(); - uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); - uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0; - if (arg_count != classes_size) { - ThrowIllegalArgumentException(StringPrintf("Wrong number of arguments; expected %d, got %d", - classes_size, arg_count).c_str()); + if (!CheckArgsForInvokeMethod(np_method, objects)) { return nullptr; } @@ -661,39 +714,54 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaM // Invoke the method. JValue result; - uint32_t shorty_len = 0; - const char* shorty = np_method->GetShorty(&shorty_len); - ArgArray arg_array(shorty, shorty_len); - if (!arg_array.BuildArgArrayFromObjectArray(receiver, objects, np_method, soa.Self())) { - CHECK(soa.Self()->IsExceptionPending()); + const char* shorty; + if (!InvokeMethodImpl(soa, m, np_method, receiver, objects, &shorty, &result)) { return nullptr; } + return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result)); +} - InvokeWithArgArray(soa, m, &arg_array, &result, shorty); +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr<mirror::Object> receiver, + jobject javaArgs) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEndForInterpreter(true))) { + ThrowStackOverflowError(soa.Self()); + return; + } - // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. - if (soa.Self()->IsExceptionPending()) { - // If we get another exception when we are trying to wrap, then just use that instead. - ScopedLocalRef<jthrowable> th(soa.Env(), soa.Env()->ExceptionOccurred()); - soa.Self()->ClearException(); - jclass exception_class = soa.Env()->FindClass("java/lang/reflect/InvocationTargetException"); - if (exception_class == nullptr) { - soa.Self()->AssertPendingException(); - return nullptr; - } - jmethodID mid = soa.Env()->GetMethodID(exception_class, "<init>", "(Ljava/lang/Throwable;)V"); - CHECK(mid != nullptr); - jobject exception_instance = soa.Env()->NewObject(exception_class, mid, th.get()); - if (exception_instance == nullptr) { - soa.Self()->AssertPendingException(); - return nullptr; - } - soa.Env()->Throw(reinterpret_cast<jthrowable>(exception_instance)); - return nullptr; + if (kIsDebugBuild) { + CHECK(constructor->IsConstructor()); + + ObjPtr<mirror::Class> declaring_class = constructor->GetDeclaringClass(); + CHECK(declaring_class->IsInitialized()); + + // Calls to String.<init> should have been repplaced with with equivalent StringFactory calls. + CHECK(!declaring_class->IsStringClass()); + + // Check that the receiver is non-null and an instance of the field's declaring class. + CHECK(receiver != nullptr); + CHECK(VerifyObjectIsClass(receiver, declaring_class)); + CHECK_EQ(constructor, + receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(constructor, + kRuntimePointerSize)); } - // Box if necessary and return. - return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result)); + // Get our arrays of arguments and their types, and check they're the same size. + ObjPtr<mirror::ObjectArray<mirror::Object>> objects = + soa.Decode<mirror::ObjectArray<mirror::Object>>(javaArgs); + ArtMethod* np_method = constructor->GetInterfaceMethodIfProxy(kRuntimePointerSize); + if (!CheckArgsForInvokeMethod(np_method, objects)) { + return; + } + + // Invoke the constructor. + JValue result; + const char* shorty; + InvokeMethodImpl(soa, constructor, np_method, receiver, objects, &shorty, &result); } ObjPtr<mirror::Object> BoxPrimitive(Primitive::Type src_class, const JValue& value) { diff --git a/runtime/reflection.h b/runtime/reflection.h index 4391bcd60b..74580a21e0 100644 --- a/runtime/reflection.h +++ b/runtime/reflection.h @@ -92,6 +92,14 @@ jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, size_t num_frames = 1) REQUIRES_SHARED(Locks::mutator_lock_); +// Special-casing of the above. Assumes that the method is the correct constructor, the class is +// initialized, and that the receiver is an instance of the class. +void InvokeConstructor(const ScopedObjectAccessAlreadyRunnable& soa, + ArtMethod* constructor, + ObjPtr<mirror::Object> receiver, + jobject args) + REQUIRES_SHARED(Locks::mutator_lock_); + ALWAYS_INLINE bool VerifyObjectIsClass(ObjPtr<mirror::Object> o, ObjPtr<mirror::Class> c) REQUIRES_SHARED(Locks::mutator_lock_); |