diff options
author | Ian Rogers <irogers@google.com> | 2014-03-13 23:45:53 -0700 |
---|---|---|
committer | Ian Rogers <irogers@google.com> | 2014-03-14 11:28:10 -0700 |
commit | 53b8b09fc80329539585dcf43657bc5f4ecefdff (patch) | |
tree | cac0f82fbb89bd907104e3fed6c36203e11a3de0 /runtime/reflection.cc | |
parent | 0dea9872082bc3e576ed6cefed86b0d6c0c45ffd (diff) | |
download | art-53b8b09fc80329539585dcf43657bc5f4ecefdff.tar.gz art-53b8b09fc80329539585dcf43657bc5f4ecefdff.tar.bz2 art-53b8b09fc80329539585dcf43657bc5f4ecefdff.zip |
Refactor reflective method invocation.
Move invocation code out of JNI internal into reflection, including ArgArray
code. Make reflective invocation use the ArgArray to build arguments rather
than allocating a jvalue[] and unboxing arguments into that.
Move reflection part of jni_internal_test into reflection_test.
Make greater use of fast JNI.
Change-Id: Ib381372df5f9a83679e30e7275de24fa0e6b1057
Diffstat (limited to 'runtime/reflection.cc')
-rw-r--r-- | runtime/reflection.cc | 481 |
1 files changed, 450 insertions, 31 deletions
diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 0bfa70f279..43105571c9 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -19,7 +19,6 @@ #include "class_linker.h" #include "common_throws.h" #include "dex_file-inl.h" -#include "invoke_arg_array_builder.h" #include "jni_internal.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" @@ -29,12 +28,440 @@ #include "mirror/object_array-inl.h" #include "object_utils.h" #include "scoped_thread_state_change.h" +#include "stack.h" #include "well_known_classes.h" namespace art { -jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject javaReceiver, - jobject javaArgs) { +class ArgArray { + public: + explicit ArgArray(const char* shorty, uint32_t shorty_len) + : shorty_(shorty), shorty_len_(shorty_len), num_bytes_(0) { + size_t num_slots = shorty_len + 1; // +1 in case of receiver. + if (LIKELY((num_slots * 2) < kSmallArgArraySize)) { + // We can trivially use the small arg array. + arg_array_ = small_arg_array_; + } else { + // Analyze shorty to see if we need the large arg array. + for (size_t i = 1; i < shorty_len; ++i) { + char c = shorty[i]; + if (c == 'J' || c == 'D') { + num_slots++; + } + } + if (num_slots <= kSmallArgArraySize) { + arg_array_ = small_arg_array_; + } else { + large_arg_array_.reset(new uint32_t[num_slots]); + arg_array_ = large_arg_array_.get(); + } + } + } + + uint32_t* GetArray() { + return arg_array_; + } + + uint32_t GetNumBytes() { + return num_bytes_; + } + + void Append(uint32_t value) { + arg_array_[num_bytes_ / 4] = value; + num_bytes_ += 4; + } + + void Append(mirror::Object* obj) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Append(StackReference<mirror::Object>::FromMirrorPtr(obj).AsVRegValue()); + } + + void AppendWide(uint64_t value) { + // For ARM and MIPS portable, align wide values to 8 bytes (ArgArray starts at offset of 4). +#if defined(ART_USE_PORTABLE_COMPILER) && (defined(__arm__) || defined(__mips__)) + if (num_bytes_ % 8 == 0) { + num_bytes_ += 4; + } +#endif + arg_array_[num_bytes_ / 4] = value; + arg_array_[(num_bytes_ / 4) + 1] = value >> 32; + num_bytes_ += 8; + } + + void AppendFloat(float value) { + jvalue jv; + jv.f = value; + Append(jv.i); + } + + void AppendDouble(double value) { + jvalue jv; + jv.d = value; + AppendWide(jv.j); + } + + void BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, va_list ap) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + for (size_t i = 1; i < shorty_len_; ++i) { + switch (shorty_[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + Append(va_arg(ap, jint)); + break; + case 'F': + AppendFloat(va_arg(ap, jdouble)); + break; + case 'L': + Append(soa.Decode<mirror::Object*>(va_arg(ap, jobject))); + break; + case 'D': + AppendDouble(va_arg(ap, jdouble)); + break; + case 'J': + AppendWide(va_arg(ap, jlong)); + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + void BuildArgArray(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver, + jvalue* args) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { + switch (shorty_[i]) { + case 'Z': + Append(args[args_offset].z); + break; + case 'B': + Append(args[args_offset].b); + break; + case 'C': + Append(args[args_offset].c); + break; + case 'S': + Append(args[args_offset].s); + break; + case 'I': + case 'F': + Append(args[args_offset].i); + break; + case 'L': + Append(soa.Decode<mirror::Object*>(args[args_offset].l)); + break; + case 'D': + case 'J': + AppendWide(args[args_offset].j); + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + void BuildArgArrayFromFrame(ShadowFrame* shadow_frame, uint32_t arg_offset) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // Set receiver if non-null (method is not static) + size_t cur_arg = arg_offset; + if (!shadow_frame->GetMethod()->IsStatic()) { + Append(shadow_frame->GetVReg(cur_arg)); + cur_arg++; + } + for (size_t i = 1; i < shorty_len_; ++i) { + switch (shorty_[i]) { + case 'Z': + case 'B': + case 'C': + case 'S': + case 'I': + case 'F': + case 'L': + Append(shadow_frame->GetVReg(cur_arg)); + cur_arg++; + break; + case 'D': + case 'J': + AppendWide(shadow_frame->GetVRegLong(cur_arg)); + cur_arg++; + cur_arg++; + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } + } + } + + static void ThrowIllegalPrimitiveArgumentException(const char* expected, + const StringPiece& found_descriptor) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ThrowIllegalArgumentException(nullptr, + StringPrintf("Invalid primitive conversion from %s to %s", expected, + PrettyDescriptor(found_descriptor.as_string()).c_str()).c_str()); + } + + bool BuildArgArray(const ScopedObjectAccess& soa, mirror::Object* receiver, + mirror::ObjectArray<mirror::Object>* args, MethodHelper& mh) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::TypeList* classes = mh.GetParameterTypeList(); + // Set receiver if non-null (method is not static) + if (receiver != nullptr) { + Append(receiver); + } + for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { + mirror::Object* arg = args->Get(args_offset); + if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) { + mirror::Class* dst_class = + mh.GetClassFromTypeIdx(classes->GetTypeItem(args_offset).type_idx_); + if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { + ThrowIllegalArgumentException(nullptr, + StringPrintf("method %s argument %d has type %s, got %s", + PrettyMethod(mh.GetMethod(), false).c_str(), + args_offset + 1, // Humans don't count from 0. + PrettyDescriptor(dst_class).c_str(), + PrettyTypeOf(arg).c_str()).c_str()); + return false; + } + } + +#define DO_FIRST_ARG(match_descriptor, get_fn, append) { \ + const StringPiece src_descriptor(arg != nullptr \ + ? ClassHelper(arg->GetClass<>()).GetDescriptor() \ + : "null"); \ + if (LIKELY(src_descriptor == match_descriptor)) { \ + mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \ + append(primitive_field-> get_fn(arg)); + +#define DO_ARG(match_descriptor, get_fn, append) \ + } else if (LIKELY(src_descriptor == match_descriptor)) { \ + mirror::ArtField* primitive_field = arg->GetClass()->GetIFields()->Get(0); \ + append(primitive_field-> get_fn(arg)); + +#define DO_FAIL(expected) \ + } else { \ + if (arg->GetClass<>()->IsPrimitive()) { \ + ThrowIllegalPrimitiveArgumentException(expected, src_descriptor); \ + } else { \ + ThrowIllegalArgumentException(nullptr, \ + StringPrintf("method %s argument %d has type %s, got %s", \ + PrettyMethod(mh.GetMethod(), false).c_str(), \ + args_offset + 1, \ + expected, \ + PrettyTypeOf(arg).c_str()).c_str()); \ + } \ + return false; \ + } } + + switch (shorty_[i]) { + case 'L': + Append(arg); + break; + case 'Z': + DO_FIRST_ARG("Ljava/lang/Boolean;", GetBoolean, Append) + DO_FAIL("boolean") + break; + case 'B': + DO_FIRST_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("byte") + break; + case 'C': + DO_FIRST_ARG("Ljava/lang/Character;", GetChar, Append) + DO_FAIL("char") + break; + case 'S': + DO_FIRST_ARG("Ljava/lang/Short;", GetShort, Append) + DO_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("short") + break; + case 'I': + DO_FIRST_ARG("Ljava/lang/Integer;", GetInt, Append) + DO_ARG("Ljava/lang/Character;", GetChar, Append) + DO_ARG("Ljava/lang/Short;", GetShort, Append) + DO_ARG("Ljava/lang/Byte;", GetByte, Append) + DO_FAIL("int") + break; + case 'J': + DO_FIRST_ARG("Ljava/lang/Long;", GetLong, AppendWide) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendWide) + DO_ARG("Ljava/lang/Character;", GetChar, AppendWide) + DO_ARG("Ljava/lang/Short;", GetShort, AppendWide) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendWide) + DO_FAIL("long") + break; + case 'F': + DO_FIRST_ARG("Ljava/lang/Float;", GetFloat, AppendFloat) + DO_ARG("Ljava/lang/Long;", GetLong, AppendFloat) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendFloat) + DO_ARG("Ljava/lang/Character;", GetChar, AppendFloat) + DO_ARG("Ljava/lang/Short;", GetShort, AppendFloat) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendFloat) + DO_FAIL("float") + break; + case 'D': + DO_FIRST_ARG("Ljava/lang/Double;", GetDouble, AppendDouble) + DO_ARG("Ljava/lang/Float;", GetFloat, AppendDouble) + DO_ARG("Ljava/lang/Long;", GetLong, AppendDouble) + DO_ARG("Ljava/lang/Integer;", GetInt, AppendDouble) + DO_ARG("Ljava/lang/Character;", GetChar, AppendDouble) + DO_ARG("Ljava/lang/Short;", GetShort, AppendDouble) + DO_ARG("Ljava/lang/Byte;", GetByte, AppendDouble) + DO_FAIL("double") + break; +#ifndef NDEBUG + default: + LOG(FATAL) << "Unexpected shorty character: " << shorty_[i]; +#endif + } +#undef DO_FIRST_ARG +#undef DO_ARG +#undef DO_FAIL + } + return true; + } + + private: + enum { kSmallArgArraySize = 16 }; + const char* const shorty_; + const uint32_t shorty_len_; + uint32_t num_bytes_; + uint32_t* arg_array_; + uint32_t small_arg_array_[kSmallArgArraySize]; + UniquePtr<uint32_t[]> large_arg_array_; +}; + +static void CheckMethodArguments(mirror::ArtMethod* m, uint32_t* args) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + const DexFile::TypeList* params = MethodHelper(m).GetParameterTypeList(); + if (params == nullptr) { + return; // No arguments so nothing to check. + } + uint32_t offset = 0; + uint32_t num_params = params->Size(); + size_t error_count = 0; + if (!m->IsStatic()) { + offset = 1; + } + for (uint32_t i = 0; i < num_params; i++) { + uint16_t type_idx = params->GetTypeItem(i).type_idx_; + mirror::Class* param_type = MethodHelper(m).GetClassFromTypeIdx(type_idx); + if (param_type == nullptr) { + Thread* self = Thread::Current(); + CHECK(self->IsExceptionPending()); + LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: " + << MethodHelper(m).GetTypeDescriptorFromTypeIdx(type_idx) << "\n" + << self->GetException(nullptr)->Dump(); + self->ClearException(); + ++error_count; + } else if (!param_type->IsPrimitive()) { + // TODO: check primitives are in range. + mirror::Object* argument = reinterpret_cast<mirror::Object*>(args[i + offset]); + if (argument != nullptr && !argument->InstanceOf(param_type)) { + LOG(ERROR) << "JNI ERROR (app bug): attempt to pass an instance of " + << PrettyTypeOf(argument) << " as argument " << (i + 1) + << " to " << PrettyMethod(m); + ++error_count; + } + } else if (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble()) { + offset++; + } + } + if (error_count > 0) { + // TODO: pass the JNI function name (such as "CallVoidMethodV") through so we can call JniAbort + // with an argument. + JniAbortF(nullptr, "bad arguments passed to %s (see above for details)", + PrettyMethod(m).c_str()); + } +} + +static mirror::ArtMethod* FindVirtualMethod(mirror::Object* receiver, + mirror::ArtMethod* method) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(method); +} + + +static void InvokeWithArgArray(const ScopedObjectAccessUnchecked& soa, mirror::ArtMethod* method, + ArgArray* arg_array, JValue* result, const char* shorty) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + uint32_t* args = arg_array->GetArray(); + if (UNLIKELY(soa.Env()->check_jni)) { + CheckMethodArguments(method, args); + } + method->Invoke(soa.Self(), args, arg_array->GetNumBytes(), result, shorty); +} + +JValue InvokeWithVarArgs(const ScopedObjectAccess& soa, jobject obj, jmethodID mid, va_list args) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + mirror::ArtMethod* method = soa.DecodeMethod(mid); + mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj); + MethodHelper mh(method); + JValue result; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArray(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); + return result; +} + +JValue InvokeWithJValues(const ScopedObjectAccessUnchecked& soa, mirror::Object* receiver, + jmethodID mid, jvalue* args) { + mirror::ArtMethod* method = soa.DecodeMethod(mid); + MethodHelper mh(method); + JValue result; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArray(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); + return result; +} + +JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccess& soa, + mirror::Object* receiver, jmethodID mid, jvalue* args) { + mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); + MethodHelper mh(method); + JValue result; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArray(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); + return result; +} + +JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccess& soa, + jobject obj, jmethodID mid, va_list args) { + mirror::Object* receiver = soa.Decode<mirror::Object*>(obj); + mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); + MethodHelper mh(method); + JValue result; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArray(soa, receiver, args); + InvokeWithArgArray(soa, method, &arg_array, &result, mh.GetShorty()); + return result; +} + +void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, + MethodHelper& mh, JValue* result) { + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); + shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, + mh.GetShorty()); +} + +jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, + jobject javaReceiver, jobject javaArgs) { jmethodID mid = soa.Env()->FromReflectedMethod(javaMethod); mirror::ArtMethod* m = soa.DecodeMethod(mid); @@ -47,17 +474,16 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject declaring_class = sirt_c.get(); } - mirror::Object* receiver = NULL; + mirror::Object* receiver = nullptr; if (!m->IsStatic()) { // Check that the receiver is non-null and an instance of the field's declaring class. receiver = soa.Decode<mirror::Object*>(javaReceiver); - if (!VerifyObjectInClass(receiver, declaring_class)) { + if (!VerifyObjectIsClass(receiver, declaring_class)) { return NULL; } // Find the actual implementation of the virtual method. m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m); - mid = soa.EncodeMethod(m); } // Get our arrays of arguments and their types, and check they're the same size. @@ -65,8 +491,8 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject soa.Decode<mirror::ObjectArray<mirror::Object>*>(javaArgs); MethodHelper mh(m); const DexFile::TypeList* classes = mh.GetParameterTypeList(); - uint32_t classes_size = classes == NULL ? 0 : classes->Size(); - uint32_t arg_count = (objects != NULL) ? objects->GetLength() : 0; + uint32_t classes_size = (classes == nullptr) ? 0 : classes->Size(); + uint32_t arg_count = (objects != nullptr) ? objects->GetLength() : 0; if (arg_count != classes_size) { ThrowIllegalArgumentException(NULL, StringPrintf("Wrong number of arguments; expected %d, got %d", @@ -74,22 +500,15 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject return NULL; } - // Translate javaArgs to a jvalue[]. - UniquePtr<jvalue[]> args(new jvalue[arg_count]); - JValue* decoded_args = reinterpret_cast<JValue*>(args.get()); - for (uint32_t i = 0; i < arg_count; ++i) { - mirror::Object* arg = objects->Get(i); - mirror::Class* dst_class = mh.GetClassFromTypeIdx(classes->GetTypeItem(i).type_idx_); - if (!UnboxPrimitiveForArgument(arg, dst_class, decoded_args[i], m, i)) { - return NULL; - } - if (!dst_class->IsPrimitive()) { - args[i].l = soa.AddLocalReference<jobject>(arg); - } + // Invoke the method. + JValue result; + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); + if (!arg_array.BuildArgArray(soa, receiver, objects, mh)) { + CHECK(soa.Self()->IsExceptionPending()); + return nullptr; } - // Invoke the method. - JValue value(InvokeWithJValues(soa, javaReceiver, mid, args.get())); + InvokeWithArgArray(soa, m, &arg_array, &result, mh.GetShorty()); // Wrap any exception with "Ljava/lang/reflect/InvocationTargetException;" and return early. if (soa.Self()->IsExceptionPending()) { @@ -103,10 +522,11 @@ jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod, jobject } // Box if necessary and return. - return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), value)); + return soa.AddLocalReference<jobject>(BoxPrimitive(mh.GetReturnType()->GetPrimitiveType(), + result)); } -bool VerifyObjectInClass(mirror::Object* o, mirror::Class* c) { +bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c) { if (o == NULL) { ThrowNullPointerException(NULL, "null receiver"); return false; @@ -218,6 +638,10 @@ mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { if (src_class == Primitive::kPrimNot) { return value.GetL(); } + if (src_class == Primitive::kPrimVoid) { + // There's no such thing as a void field, and void methods invoked via reflection return null. + return nullptr; + } jmethodID m = NULL; const char* shorty; @@ -254,20 +678,15 @@ mirror::Object* BoxPrimitive(Primitive::Type src_class, const JValue& value) { m = WellKnownClasses::java_lang_Short_valueOf; shorty = "LS"; break; - case Primitive::kPrimVoid: - // There's no such thing as a void field, and void methods invoked via reflection return null. - return nullptr; default: LOG(FATAL) << static_cast<int>(src_class); shorty = nullptr; } ScopedObjectAccessUnchecked soa(Thread::Current()); - if (kIsDebugBuild) { - CHECK_EQ(soa.Self()->GetState(), kRunnable); - } + DCHECK_EQ(soa.Self()->GetState(), kRunnable); - ArgArray arg_array(nullptr, 0); + ArgArray arg_array(shorty, 2); JValue result; if (src_class == Primitive::kPrimDouble || src_class == Primitive::kPrimLong) { arg_array.AppendWide(value.GetJ()); |