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
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 9e9c33c..646de75 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -457,6 +457,64 @@
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 @@
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,41 +714,56 @@
// 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;
}
-
- 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 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;
- }
-
- // Box if necessary and return.
return soa.AddLocalReference<jobject>(BoxPrimitive(Primitive::GetType(shorty[0]), result));
}
+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;
+ }
+
+ 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));
+ }
+
+ // 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) {
if (src_class == Primitive::kPrimNot) {
return MakeObjPtr(value.GetL());