diff options
-rw-r--r-- | runtime/common_dex_operations.h | 199 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.cc | 801 | ||||
-rw-r--r-- | runtime/interpreter/interpreter_common.h | 8 | ||||
-rw-r--r-- | runtime/method_handles-inl.h | 30 | ||||
-rw-r--r-- | runtime/method_handles.cc | 822 | ||||
-rw-r--r-- | runtime/method_handles.h | 63 | ||||
-rw-r--r-- | runtime/mirror/method_handle_impl.h | 38 | ||||
-rw-r--r-- | runtime/well_known_classes.cc | 11 | ||||
-rw-r--r-- | runtime/well_known_classes.h | 3 |
9 files changed, 1127 insertions, 848 deletions
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h new file mode 100644 index 0000000000..6693eefa5a --- /dev/null +++ b/runtime/common_dex_operations.h @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_COMMON_DEX_OPERATIONS_H_ +#define ART_RUNTIME_COMMON_DEX_OPERATIONS_H_ + +#include "art_field.h" +#include "art_method.h" +#include "class_linker.h" +#include "interpreter/unstarted_runtime.h" +#include "runtime.h" +#include "stack.h" +#include "thread.h" + +namespace art { + +namespace interpreter { + void ArtInterpreterToInterpreterBridge(Thread* self, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + + void ArtInterpreterToCompiledCodeBridge(Thread* self, + ArtMethod* caller, + const DexFile::CodeItem* code_item, + ShadowFrame* shadow_frame, + JValue* result); +} // namespace interpreter + +inline void PerformCall(Thread* self, + const DexFile::CodeItem* code_item, + ArtMethod* caller_method, + const size_t first_dest_reg, + ShadowFrame* callee_frame, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (LIKELY(Runtime::Current()->IsStarted())) { + ArtMethod* target = callee_frame->GetMethod(); + if (ClassLinker::ShouldUseInterpreterEntrypoint( + target, + target->GetEntryPointFromQuickCompiledCode())) { + interpreter::ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result); + } else { + interpreter::ArtInterpreterToCompiledCodeBridge( + self, caller_method, code_item, callee_frame, result); + } + } else { + interpreter::UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg); + } +} + +template<Primitive::Type field_type> +static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr<mirror::Object> obj, + ArtField* field, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); + + // Report this field access to instrumentation if needed. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldReadListeners())) { + StackHandleScope<1> hs(self); + // Wrap in handle wrapper in case the listener does thread suspension. + HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); + ObjPtr<mirror::Object> this_object; + if (!field->IsStatic()) { + this_object = obj; + } + instrumentation->FieldReadEvent(self, + this_object.Ptr(), + shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), + field); + } + + switch (field_type) { + case Primitive::kPrimBoolean: + result->SetZ(field->GetBoolean(obj)); + break; + case Primitive::kPrimByte: + result->SetB(field->GetByte(obj)); + break; + case Primitive::kPrimChar: + result->SetC(field->GetChar(obj)); + break; + case Primitive::kPrimShort: + result->SetS(field->GetShort(obj)); + break; + case Primitive::kPrimInt: + result->SetI(field->GetInt(obj)); + break; + case Primitive::kPrimLong: + result->SetJ(field->GetLong(obj)); + break; + case Primitive::kPrimNot: + result->SetL(field->GetObject(obj)); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable " << field_type; + break; + } +} + +template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active> +ALWAYS_INLINE bool DoFieldPutCommon(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr<mirror::Object> obj, + ArtField* field, + const JValue& value) + REQUIRES_SHARED(Locks::mutator_lock_) { + field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); + + // Report this field access to instrumentation if needed. Since we only have the offset of + // the field from the base of the object, we need to look for it first. + instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { + StackHandleScope<1> hs(self); + // Wrap in handle wrapper in case the listener does thread suspension. + HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); + ObjPtr<mirror::Object> this_object = field->IsStatic() ? nullptr : obj; + instrumentation->FieldWriteEvent(self, this_object.Ptr(), + shadow_frame.GetMethod(), + shadow_frame.GetDexPC(), + field, + value); + } + + switch (field_type) { + case Primitive::kPrimBoolean: + field->SetBoolean<transaction_active>(obj, value.GetZ()); + break; + case Primitive::kPrimByte: + field->SetByte<transaction_active>(obj, value.GetB()); + break; + case Primitive::kPrimChar: + field->SetChar<transaction_active>(obj, value.GetC()); + break; + case Primitive::kPrimShort: + field->SetShort<transaction_active>(obj, value.GetS()); + break; + case Primitive::kPrimInt: + field->SetInt<transaction_active>(obj, value.GetI()); + break; + case Primitive::kPrimLong: + field->SetLong<transaction_active>(obj, value.GetJ()); + break; + case Primitive::kPrimNot: { + ObjPtr<mirror::Object> reg = value.GetL(); + if (do_assignability_check && reg != nullptr) { + // FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the + // object in the destructor. + ObjPtr<mirror::Class> field_class; + { + StackHandleScope<2> hs(self); + HandleWrapperObjPtr<mirror::Object> h_reg(hs.NewHandleWrapper(®)); + HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); + field_class = field->GetType<true>(); + } + if (!reg->VerifierInstanceOf(field_class.Ptr())) { + // This should never happen. + std::string temp1, temp2, temp3; + self->ThrowNewExceptionF("Ljava/lang/InternalError;", + "Put '%s' that is not instance of field '%s' in '%s'", + reg->GetClass()->GetDescriptor(&temp1), + field_class->GetDescriptor(&temp2), + field->GetDeclaringClass()->GetDescriptor(&temp3)); + return false; + } + } + field->SetObj<transaction_active>(obj, reg); + break; + } + case Primitive::kPrimVoid: { + LOG(FATAL) << "Unreachable " << field_type; + break; + } + } + return true; +} + +} // namespace art + +#endif // ART_RUNTIME_COMMON_DEX_OPERATIONS_H_ diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index a09e71b6c0..ca26207093 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -32,7 +32,6 @@ #include "reflection.h" #include "reflection-inl.h" #include "stack.h" -#include "unstarted_runtime.h" #include "verifier/method_verifier.h" #include "well_known_classes.h" @@ -43,60 +42,6 @@ void ThrowNullPointerExceptionFromInterpreter() { ThrowNullPointerExceptionFromDexPC(); } -template<Primitive::Type field_type> -static ALWAYS_INLINE void DoFieldGetCommon(Thread* self, - const ShadowFrame& shadow_frame, - ObjPtr<mirror::Object>& obj, - ArtField* field, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); - - // Report this field access to instrumentation if needed. - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - if (UNLIKELY(instrumentation->HasFieldReadListeners())) { - StackHandleScope<1> hs(self); - // Wrap in handle wrapper in case the listener does thread suspension. - HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); - ObjPtr<mirror::Object> this_object; - if (!field->IsStatic()) { - this_object = obj; - } - instrumentation->FieldReadEvent(self, - this_object.Ptr(), - shadow_frame.GetMethod(), - shadow_frame.GetDexPC(), - field); - } - - switch (field_type) { - case Primitive::kPrimBoolean: - result->SetZ(field->GetBoolean(obj)); - break; - case Primitive::kPrimByte: - result->SetB(field->GetByte(obj)); - break; - case Primitive::kPrimChar: - result->SetC(field->GetChar(obj)); - break; - case Primitive::kPrimShort: - result->SetS(field->GetShort(obj)); - break; - case Primitive::kPrimInt: - result->SetI(field->GetInt(obj)); - break; - case Primitive::kPrimLong: - result->SetJ(field->GetLong(obj)); - break; - case Primitive::kPrimNot: - result->SetL(field->GetObject(obj)); - break; - default: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } -} - template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check> bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { @@ -184,48 +129,6 @@ EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL(StaticObjectRead, Primitive::kPrimNot) #undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL -// Helper for getters in invoke-polymorphic. -inline static void DoFieldGetForInvokePolymorphic(Thread* self, - const ShadowFrame& shadow_frame, - ObjPtr<mirror::Object>& obj, - ArtField* field, - Primitive::Type field_type, - JValue* result) - REQUIRES_SHARED(Locks::mutator_lock_) { - switch (field_type) { - case Primitive::kPrimBoolean: - DoFieldGetCommon<Primitive::kPrimBoolean>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimByte: - DoFieldGetCommon<Primitive::kPrimByte>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimChar: - DoFieldGetCommon<Primitive::kPrimChar>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimShort: - DoFieldGetCommon<Primitive::kPrimShort>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimInt: - DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimLong: - DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimFloat: - DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimDouble: - DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimNot: - DoFieldGetCommon<Primitive::kPrimNot>(self, shadow_frame, obj, field, result); - break; - case Primitive::kPrimVoid: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } -} - // Handles iget-quick, iget-wide-quick and iget-object-quick instructions. // Returns true on success, otherwise throws an exception and returns false. template<Primitive::Type field_type> @@ -300,42 +203,6 @@ EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimLong); // iget-wide-qui EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL(Primitive::kPrimNot); // iget-object-quick. #undef EXPLICIT_DO_IGET_QUICK_TEMPLATE_DECL -static JValue GetFieldValue(const ShadowFrame& shadow_frame, - Primitive::Type field_type, - uint32_t vreg) - REQUIRES_SHARED(Locks::mutator_lock_) { - JValue field_value; - switch (field_type) { - case Primitive::kPrimBoolean: - field_value.SetZ(static_cast<uint8_t>(shadow_frame.GetVReg(vreg))); - break; - case Primitive::kPrimByte: - field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg))); - break; - case Primitive::kPrimChar: - field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg))); - break; - case Primitive::kPrimShort: - field_value.SetS(static_cast<int16_t>(shadow_frame.GetVReg(vreg))); - break; - case Primitive::kPrimInt: - case Primitive::kPrimFloat: - field_value.SetI(shadow_frame.GetVReg(vreg)); - break; - case Primitive::kPrimLong: - case Primitive::kPrimDouble: - field_value.SetJ(shadow_frame.GetVRegLong(vreg)); - break; - case Primitive::kPrimNot: - field_value.SetL(shadow_frame.GetVRegReference(vreg)); - break; - case Primitive::kPrimVoid: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } - return field_value; -} - template<Primitive::Type field_type> static JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -369,82 +236,6 @@ static JValue GetFieldValue(const ShadowFrame& shadow_frame, uint32_t vreg) return field_value; } -template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active> -static inline bool DoFieldPutCommon(Thread* self, - const ShadowFrame& shadow_frame, - ObjPtr<mirror::Object>& obj, - ArtField* f, - const JValue& value) - REQUIRES_SHARED(Locks::mutator_lock_) { - f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self); - - // Report this field access to instrumentation if needed. Since we only have the offset of - // the field from the base of the object, we need to look for it first. - instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); - if (UNLIKELY(instrumentation->HasFieldWriteListeners())) { - StackHandleScope<1> hs(self); - // Wrap in handle wrapper in case the listener does thread suspension. - HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj)); - ObjPtr<mirror::Object> this_object = f->IsStatic() ? nullptr : obj; - instrumentation->FieldWriteEvent(self, this_object.Ptr(), - shadow_frame.GetMethod(), - shadow_frame.GetDexPC(), - f, - value); - } - - switch (field_type) { - case Primitive::kPrimBoolean: - f->SetBoolean<transaction_active>(obj, value.GetZ()); - break; - case Primitive::kPrimByte: - f->SetByte<transaction_active>(obj, value.GetB()); - break; - case Primitive::kPrimChar: - f->SetChar<transaction_active>(obj, value.GetC()); - break; - case Primitive::kPrimShort: - f->SetShort<transaction_active>(obj, value.GetS()); - break; - case Primitive::kPrimInt: - f->SetInt<transaction_active>(obj, value.GetI()); - break; - case Primitive::kPrimLong: - f->SetLong<transaction_active>(obj, value.GetJ()); - break; - case Primitive::kPrimNot: { - ObjPtr<mirror::Object> reg = value.GetL(); - if (do_assignability_check && reg != nullptr) { - // FieldHelper::GetType can resolve classes, use a handle wrapper which will restore the - // object in the destructor. - ObjPtr<mirror::Class> field_class; - { - StackHandleScope<2> hs(self); - HandleWrapperObjPtr<mirror::Object> h_reg(hs.NewHandleWrapper(®)); - HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&obj)); - field_class = f->GetType<true>(); - } - if (!reg->VerifierInstanceOf(field_class.Ptr())) { - // This should never happen. - std::string temp1, temp2, temp3; - self->ThrowNewExceptionF("Ljava/lang/InternalError;", - "Put '%s' that is not instance of field '%s' in '%s'", - reg->GetClass()->GetDescriptor(&temp1), - field_class->GetDescriptor(&temp2), - f->GetDeclaringClass()->GetDescriptor(&temp3)); - return false; - } - } - f->SetObj<transaction_active>(obj, reg); - break; - } - default: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } - return true; -} - template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check, bool transaction_active> bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst, @@ -511,46 +302,6 @@ EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL(StaticObjectWrite, Primitive::kPrimNot) #undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL #undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL -// Helper for setters in invoke-polymorphic. -bool DoFieldPutForInvokePolymorphic(Thread* self, - ShadowFrame& shadow_frame, - ObjPtr<mirror::Object>& obj, - ArtField* field, - Primitive::Type field_type, - const JValue& value) - REQUIRES_SHARED(Locks::mutator_lock_) { - static const bool kDoCheckAssignability = false; - static const bool kTransaction = false; - switch (field_type) { - case Primitive::kPrimBoolean: - return DoFieldPutCommon<Primitive::kPrimBoolean, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimByte: - return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimChar: - return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimShort: - return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimInt: - case Primitive::kPrimFloat: - return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimLong: - case Primitive::kPrimDouble: - return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimNot: - return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>( - self, shadow_frame, obj, field, value); - case Primitive::kPrimVoid: - LOG(FATAL) << "Unreachable: " << field_type; - UNREACHABLE(); - } -} - template<Primitive::Type field_type, bool transaction_active> bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) { ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data)); @@ -697,36 +448,6 @@ static ALWAYS_INLINE bool DoCallCommon(ArtMethod* called_method, uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_); template <bool is_range> -static ALWAYS_INLINE bool DoCallPolymorphic(ArtMethod* called_method, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> target_type, - Thread* self, - ShadowFrame& shadow_frame, - JValue* result, - uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t vregC, - const MethodHandleKind handle_kind) - REQUIRES_SHARED(Locks::mutator_lock_); - -template <bool is_range> -static ALWAYS_INLINE bool DoCallTransform(ArtMethod* called_method, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - Thread* self, - ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> receiver, - JValue* result, - uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_); - -ALWAYS_INLINE void PerformCall(Thread* self, - const DexFile::CodeItem* code_item, - ArtMethod* caller_method, - const size_t first_dest_reg, - ShadowFrame* callee_frame, - JValue* result) REQUIRES_SHARED(Locks::mutator_lock_); - -template <bool is_range> ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame, ShadowFrame* callee_frame, const uint32_t (&arg)[Instruction::kMaxVarArgRegs], @@ -798,55 +519,12 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } } -inline static bool IsInvokeExact(const DexFile& dex_file, int invoke_method_idx) { - // This check uses string comparison as it needs less code and data - // to do than fetching the associated ArtMethod from the DexCache - // and checking against ArtMethods in the well known classes. The - // verifier needs to perform a more rigorous check. - const char* method_name = dex_file.GetMethodName(dex_file.GetMethodId(invoke_method_idx)); - bool is_invoke_exact = (0 == strcmp(method_name, "invokeExact")); - DCHECK(is_invoke_exact || (0 == strcmp(method_name, "invoke"))); - return is_invoke_exact; -} - -inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self, ArtField* field) - REQUIRES_SHARED(Locks::mutator_lock_) { - // Method handle invocations on static fields should ensure class is - // initialized. This usually happens when an instance is constructed - // or class members referenced, but this is not guaranteed when - // looking up method handles. - ObjPtr<mirror::Class> klass = field->GetDeclaringClass(); - if (UNLIKELY(!klass->IsInitialized())) { - StackHandleScope<1> hs(self); - HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(&klass)); - if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h, true, true)) { - DCHECK(self->IsExceptionPending()); - return nullptr; - } - } - return klass; -} - -// Returns true iff. the callsite type for a polymorphic invoke is transformer -// like, i.e that it has a single input argument whose type is -// dalvik.system.EmulatedStackFrame. -static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::ObjectArray<mirror::Class>> param_types(callsite_type->GetPTypes()); - if (param_types->GetLength() == 1) { - ObjPtr<mirror::Class> param(param_types->GetWithoutChecks(0)); - return param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame); - } - - return false; -} - template<bool is_range, bool do_access_check> -inline bool DoInvokePolymorphic(Thread* self, - ShadowFrame& shadow_frame, - const Instruction* inst, - uint16_t inst_data, - JValue* result) +bool DoInvokePolymorphic(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { // Invoke-polymorphic instructions always take a receiver. i.e, they are never static. const uint32_t vRegC = (is_range) ? inst->VRegC_4rcc() : inst->VRegC_45cc(); @@ -857,15 +535,10 @@ inline bool DoInvokePolymorphic(Thread* self, // and provides sane return result in error cases. result->SetJ(0); - // Determine if this invocation is MethodHandle.invoke() or - // MethodHandle.invokeExact(). - bool is_invoke_exact = IsInvokeExact(shadow_frame.GetMethod()->GetDeclaringClass()->GetDexFile(), - invoke_method_idx); - // The invoke_method_idx here is the name of the signature polymorphic method that // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact) // and not the method that we'll dispatch to in the end. - StackHandleScope<6> hs(self); + StackHandleScope<5> hs(self); Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle( ObjPtr<mirror::MethodHandleImpl>::DownCast( MakeObjPtr(shadow_frame.GetVRegReference(vRegC))))); @@ -877,7 +550,7 @@ inline bool DoInvokePolymorphic(Thread* self, } // The vRegH value gives the index of the proto_id associated with this - // signature polymorphic callsite. + // signature polymorphic call site. const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); // Call through to the classlinker and ask it to resolve the static type associated @@ -896,224 +569,43 @@ inline bool DoInvokePolymorphic(Thread* self, return false; } - const MethodHandleKind handle_kind = method_handle->GetHandleKind(); - Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType())); - CHECK(handle_type.Get() != nullptr); - { - // We need to check the nominal type of the handle in addition to the - // real type. The "nominal" type is present when MethodHandle.asType is - // called any handle, and results in the declared type of the handle - // changing. - ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType()); - ObjPtr<mirror::MethodType> check_type(nullptr); - if (LIKELY(nominal_type.Ptr() == nullptr)) { - check_type.Assign(handle_type.Get()); - } else { - check_type.Assign(nominal_type.Ptr()); - } - - if (is_invoke_exact) { - if (UNLIKELY(!callsite_type->IsExactMatch(check_type.Ptr()))) { - ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get()); - return false; - } - } else if (!IsInvokeTransform(handle_kind)) { - if (UNLIKELY(!IsCallerTransformer(callsite_type) && - !callsite_type->IsConvertible(check_type.Ptr()))) { - ThrowWrongMethodTypeException(check_type.Ptr(), callsite_type.Get()); - return false; - } - } - } + ArtMethod* invoke_method = + class_linker->ResolveMethod<ClassLinker::kForceICCECheck>(self, + invoke_method_idx, + shadow_frame.GetMethod(), + kVirtual); - uint32_t arg[Instruction::kMaxVarArgRegs] = {}; - uint32_t first_src_reg = 0; + // There is a common dispatch method for method handles that takes + // arguments either from a range or an array of arguments depending + // on whether the DEX instruction is invoke-polymorphic/range or + // invoke-polymorphic. The array here is for the latter. + uint32_t args[Instruction::kMaxVarArgRegs] = {}; if (is_range) { - first_src_reg = (inst->VRegC_4rcc() + 1); + // VRegC is the register holding the method handle. Arguments passed + // to the method handle's target do not include the method handle. + uint32_t first_arg = inst->VRegC_4rcc() + 1; + return DoInvokePolymorphic<is_range, do_access_check>(self, + invoke_method, + shadow_frame, + method_handle, + callsite_type, + args /* unused */, + first_arg, + result); } else { - inst->GetVarArgs(arg, inst_data); - arg[0] = arg[1]; - arg[1] = arg[2]; - arg[2] = arg[3]; - arg[3] = arg[4]; - arg[4] = 0; - first_src_reg = arg[0]; - } - - if (IsInvoke(handle_kind)) { - // Get the method we're actually invoking along with the kind of - // invoke that is desired. We don't need to perform access checks at this - // point because they would have been performed on our behalf at the point - // of creation of the method handle. - ArtMethod* called_method = method_handle->GetTargetMethod(); - CHECK(called_method != nullptr); - - if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) { - // TODO: Unfortunately, we have to postpone dynamic receiver based checks - // because the receiver might be cast or might come from an emulated stack - // frame, which means that it is unknown at this point. We perform these - // checks inside DoCallPolymorphic right before we do the actual invoke. - } else if (handle_kind == kInvokeDirect) { - // String constructors are a special case, they are replaced with StringFactory - // methods. - if (called_method->IsConstructor() && called_method->GetDeclaringClass()->IsStringClass()) { - DCHECK(handle_type->GetRType()->IsStringClass()); - called_method = WellKnownClasses::StringInitToStringFactory(called_method); - } - } else if (handle_kind == kInvokeSuper) { - ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass(); - - // Note that we're not dynamically dispatching on the type of the receiver - // here. We use the static type of the "receiver" object that we've - // recorded in the method handle's type, which will be the same as the - // special caller that was specified at the point of lookup. - ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0); - if (!declaring_class->IsInterface()) { - ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass(); - uint16_t vtable_index = called_method->GetMethodIndex(); - DCHECK(super_class != nullptr); - DCHECK(super_class->HasVTable()); - // Note that super_class is a super of referrer_class and called_method - // will always be declared by super_class (or one of its super classes). - DCHECK_LT(vtable_index, super_class->GetVTableLength()); - called_method = super_class->GetVTableEntry(vtable_index, kRuntimePointerSize); - } else { - called_method = referrer_class->FindVirtualMethodForInterfaceSuper( - called_method, kRuntimePointerSize); - } - - CHECK(called_method != nullptr); - } - - if (IsInvokeTransform(handle_kind)) { - // There are two cases here - method handles representing regular - // transforms and those representing call site transforms. Method - // handles for call site transforms adapt their MethodType to match - // the call site. For these, the |callee_type| is the same as the - // |callsite_type|. The VarargsCollector is such a tranform, its - // method type depends on the call site, ie. x(a) or x(a, b), or - // x(a, b, c). The VarargsCollector invokes a variable arity method - // with the arity arguments in an array. - Handle<mirror::MethodType> callee_type = - (handle_kind == kInvokeCallSiteTransform) ? callsite_type : handle_type; - return DoCallTransform<is_range>(called_method, - callsite_type, - callee_type, - self, - shadow_frame, - method_handle /* receiver */, - result, - arg, - first_src_reg); - } else { - return DoCallPolymorphic<is_range>(called_method, - callsite_type, - handle_type, - self, - shadow_frame, - result, - arg, - first_src_reg, - handle_kind); - } - } else { - DCHECK(!is_range); - ArtField* field = method_handle->GetTargetField(); - Primitive::Type field_type = field->GetTypeAsPrimitiveType(); - - switch (handle_kind) { - case kInstanceGet: { - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg); - DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); - if (!ConvertReturnValue(callsite_type, handle_type, result)) { - DCHECK(self->IsExceptionPending()); - return false; - } - return true; - } - case kStaticGet: { - ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); - if (obj == nullptr) { - DCHECK(self->IsExceptionPending()); - return false; - } - DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); - if (!ConvertReturnValue(callsite_type, handle_type, result)) { - DCHECK(self->IsExceptionPending()); - return false; - } - return true; - } - case kInstancePut: { - JValue value = GetFieldValue(shadow_frame, field_type, arg[1]); - if (!ConvertArgumentValue(callsite_type, handle_type, 1, &value)) { - DCHECK(self->IsExceptionPending()); - return false; - } - ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg); - return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); - } - case kStaticPut: { - JValue value = GetFieldValue(shadow_frame, field_type, arg[0]); - if (!ConvertArgumentValue(callsite_type, handle_type, 0, &value)) { - DCHECK(self->IsExceptionPending()); - return false; - } - ObjPtr<mirror::Object> obj = field->GetDeclaringClass(); - return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); - } - default: - LOG(FATAL) << "Unreachable: " << handle_kind; - UNREACHABLE(); - } - } -} - -// Calculate the number of ins for a proxy or native method, where we -// can't just look at the code item. -static inline size_t GetInsForProxyOrNativeMethod(ArtMethod* method) - REQUIRES_SHARED(Locks::mutator_lock_) { - DCHECK(method->IsNative() || method->IsProxyMethod()); - - method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); - size_t num_ins = 0; - // Separate accounting for the receiver, which isn't a part of the - // shorty. - if (!method->IsStatic()) { - ++num_ins; - } - - uint32_t shorty_len = 0; - const char* shorty = method->GetShorty(&shorty_len); - for (size_t i = 1; i < shorty_len; ++i) { - const char c = shorty[i]; - ++num_ins; - if (c == 'J' || c == 'D') { - ++num_ins; - } - } - - return num_ins; -} - -inline void PerformCall(Thread* self, - const DexFile::CodeItem* code_item, - ArtMethod* caller_method, - const size_t first_dest_reg, - ShadowFrame* callee_frame, - JValue* result) { - if (LIKELY(Runtime::Current()->IsStarted())) { - ArtMethod* target = callee_frame->GetMethod(); - if (ClassLinker::ShouldUseInterpreterEntrypoint( - target, - target->GetEntryPointFromQuickCompiledCode())) { - ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result); - } else { - ArtInterpreterToCompiledCodeBridge( - self, caller_method, code_item, callee_frame, result); - } - } else { - UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg); + // Get the register arguments for the invoke. + inst->GetVarArgs(args, inst_data); + // Drop the first register which is the method handle performing the invoke. + memcpy(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); + args[Instruction::kMaxVarArgRegs - 1] = 0; + return DoInvokePolymorphic<is_range, do_access_check>(self, + invoke_method, + shadow_frame, + method_handle, + callsite_type, + args, + args[0], + result); } } @@ -1139,217 +631,6 @@ inline void CopyRegisters(ShadowFrame& caller_frame, } } -template <bool is_range> -static inline bool DoCallPolymorphic(ArtMethod* called_method, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> target_type, - Thread* self, - ShadowFrame& shadow_frame, - JValue* result, - uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t first_src_reg, - const MethodHandleKind handle_kind) { - // Compute method information. - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); - - // Number of registers for the callee's call frame. Note that for non-exact - // invokes, we always derive this information from the callee method. We - // cannot guarantee during verification that the number of registers encoded - // in the invoke is equal to the number of ins for the callee. This is because - // some transformations (such as boxing a long -> Long or wideining an - // int -> long will change that number. - uint16_t num_regs; - size_t num_input_regs; - size_t first_dest_reg; - if (LIKELY(code_item != nullptr)) { - num_regs = code_item->registers_size_; - first_dest_reg = num_regs - code_item->ins_size_; - num_input_regs = code_item->ins_size_; - // Parameter registers go at the end of the shadow frame. - DCHECK_NE(first_dest_reg, (size_t)-1); - } else { - // No local regs for proxy and native methods. - DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); - num_regs = num_input_regs = GetInsForProxyOrNativeMethod(called_method); - first_dest_reg = 0; - } - - // Allocate shadow frame on the stack. - ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = - CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0); - ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); - - // Whether this polymorphic invoke was issued by a transformer method. - bool is_caller_transformer = false; - // Thread might be suspended during PerformArgumentConversions due to the - // allocations performed during boxing. - { - ScopedStackedShadowFramePusher pusher( - self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); - if (callsite_type->IsExactMatch(target_type.Get())) { - // This is an exact invoke, we can take the fast path of just copying all - // registers without performing any argument conversions. - CopyRegisters<is_range>(shadow_frame, - new_shadow_frame, - arg, - first_src_reg, - first_dest_reg, - num_input_regs); - } else { - // This includes the case where we're entering this invoke-polymorphic - // from a transformer method. In that case, the callsite_type will contain - // a single argument of type dalvik.system.EmulatedStackFrame. In that - // case, we'll have to unmarshal the EmulatedStackFrame into the - // new_shadow_frame and perform argument conversions on it. - if (IsCallerTransformer(callsite_type)) { - is_caller_transformer = true; - // The emulated stack frame is the first and only argument when we're coming - // through from a transformer. - ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame( - reinterpret_cast<mirror::EmulatedStackFrame*>( - shadow_frame.GetVRegReference(first_src_reg))); - if (!emulated_stack_frame->WriteToShadowFrame(self, - target_type, - first_dest_reg, - new_shadow_frame)) { - DCHECK(self->IsExceptionPending()); - result->SetL(0); - return false; - } - } else if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self, - callsite_type, - target_type, - shadow_frame, - first_src_reg, - first_dest_reg, - arg, - new_shadow_frame)) { - DCHECK(self->IsExceptionPending()); - result->SetL(0); - return false; - } - } - } - - // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver - // based dispatch right before we perform the actual call, because the - // receiver isn't known very early. - if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) { - ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg)); - ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass()); - // Verify that _vRegC is an object reference and of the type expected by - // the receiver. - if (!VerifyObjectIsClass(receiver, declaring_class)) { - DCHECK(self->IsExceptionPending()); - return false; - } - - called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( - called_method, kRuntimePointerSize); - } - - PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result); - if (self->IsExceptionPending()) { - return false; - } - - // If the caller of this signature polymorphic method was a transformer, - // we need to copy the result back out to the emulated stack frame. - if (is_caller_transformer) { - StackHandleScope<2> hs(self); - Handle<mirror::EmulatedStackFrame> emulated_stack_frame( - hs.NewHandle(reinterpret_cast<mirror::EmulatedStackFrame*>( - shadow_frame.GetVRegReference(first_src_reg)))); - Handle<mirror::MethodType> emulated_stack_type(hs.NewHandle(emulated_stack_frame->GetType())); - JValue local_result; - local_result.SetJ(result->GetJ()); - - if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) { - emulated_stack_frame->SetReturnValue(self, local_result); - return true; - } else { - DCHECK(self->IsExceptionPending()); - return false; - } - } else { - return ConvertReturnValue(callsite_type, target_type, result); - } -} - -template <bool is_range> -static inline bool DoCallTransform(ArtMethod* called_method, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - Thread* self, - ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> receiver, - JValue* result, - uint32_t (&arg)[Instruction::kMaxVarArgRegs], - uint32_t first_src_reg) { - // This can be fixed to two, because the method we're calling here - // (MethodHandle.transformInternal) doesn't have any locals and the signature - // is known : - // - // private MethodHandle.transformInternal(EmulatedStackFrame sf); - // - // This means we need only two vregs : - // - One for the receiver object. - // - One for the only method argument (an EmulatedStackFrame). - static constexpr size_t kNumRegsForTransform = 2; - - const DexFile::CodeItem* code_item = called_method->GetCodeItem(); - DCHECK(code_item != nullptr); - DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_); - DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_); - - ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = - CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0); - ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); - - StackHandleScope<1> hs(self); - MutableHandle<mirror::EmulatedStackFrame> sf(hs.NewHandle<mirror::EmulatedStackFrame>(nullptr)); - if (IsCallerTransformer(callsite_type)) { - // If we're entering this transformer from another transformer, we can pass - // through the handle directly to the callee, instead of having to - // instantiate a new stack frame based on the shadow frame. - sf.Assign(reinterpret_cast<mirror::EmulatedStackFrame*>( - shadow_frame.GetVRegReference(first_src_reg))); - } else { - sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs<is_range>( - self, - callsite_type, - callee_type, - shadow_frame, - first_src_reg, - arg)); - - // Something went wrong while creating the emulated stack frame, we should - // throw the pending exception. - if (sf.Get() == nullptr) { - DCHECK(self->IsExceptionPending()); - return false; - } - } - - new_shadow_frame->SetVRegReference(0, receiver.Get()); - new_shadow_frame->SetVRegReference(1, sf.Get()); - - PerformCall(self, - code_item, - shadow_frame.GetMethod(), - 0 /* first dest reg */, - new_shadow_frame, - result); - if (self->IsExceptionPending()) { - return false; - } - - // If the called transformer method we called has returned a value, then we - // need to copy it back to |result|. - sf->GetReturnValue(self, result); - return ConvertReturnValue(callsite_type, callee_type, result); -} - template <bool is_range, bool do_assignability_check> static inline bool DoCallCommon(ArtMethod* called_method, diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index b599949af4..aeb438f05f 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -33,6 +33,7 @@ #include "base/logging.h" #include "base/macros.h" #include "class_linker-inl.h" +#include "common_dex_operations.h" #include "common_throws.h" #include "dex_file-inl.h" #include "dex_instruction-inl.h" @@ -48,6 +49,7 @@ #include "obj_ptr.h" #include "stack.h" #include "thread.h" +#include "unstarted_runtime.h" #include "well_known_classes.h" namespace art { @@ -153,8 +155,10 @@ static inline bool DoInvoke(Thread* self, // Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range). template<bool is_range, bool do_access_check> -bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, - const Instruction* inst, uint16_t inst_data, +bool DoInvokePolymorphic(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, JValue* result); // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h index 1240792643..08b8ad937a 100644 --- a/runtime/method_handles-inl.h +++ b/runtime/method_handles-inl.h @@ -134,36 +134,6 @@ bool PerformConversions(Thread* self, return true; } -template <bool is_range> -bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - const ShadowFrame& caller_frame, - uint32_t first_src_reg, - uint32_t first_dest_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs], - ShadowFrame* callee_frame) - REQUIRES_SHARED(Locks::mutator_lock_) { - ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(callsite_type->GetPTypes()); - ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes()); - - const int32_t num_method_params = from_types->GetLength(); - if (to_types->GetLength() != num_method_params) { - ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); - return false; - } - - ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame); - ShadowFrameSetter setter(callee_frame, first_dest_reg); - - return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self, - callsite_type, - callee_type, - &getter, - &setter, - num_method_params); -} - } // namespace art #endif // ART_RUNTIME_METHOD_HANDLES_INL_H_ diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index da510ceb5c..99886e5c2f 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -18,8 +18,12 @@ #include "android-base/stringprintf.h" +#include "common_dex_operations.h" #include "jvalue.h" #include "jvalue-inl.h" +#include "mirror/emulated_stack_frame.h" +#include "mirror/method_handle_impl.h" +#include "mirror/method_type.h" #include "reflection.h" #include "reflection-inl.h" #include "well_known_classes.h" @@ -282,4 +286,822 @@ bool ConvertJValueCommon( } } +namespace { + +template <bool is_range> +inline void CopyArgumentsFromCallerFrame(const ShadowFrame& caller_frame, + ShadowFrame* callee_frame, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + const size_t first_dst_reg, + const size_t num_regs) + REQUIRES_SHARED(Locks::mutator_lock_) { + for (size_t i = 0; i < num_regs; ++i) { + size_t dst_reg = first_dst_reg + i; + size_t src_reg = is_range ? (first_arg + i) : args[i]; + // Uint required, so that sign extension does not make this wrong on 64-bit systems + uint32_t src_value = caller_frame.GetVReg(src_reg); + ObjPtr<mirror::Object> o = caller_frame.GetVRegReference<kVerifyNone>(src_reg); + // If both register locations contains the same value, the register probably holds a reference. + // Note: As an optimization, non-moving collectors leave a stale reference value + // in the references array even after the original vreg was overwritten to a non-reference. + if (src_value == reinterpret_cast<uintptr_t>(o.Ptr())) { + callee_frame->SetVRegReference(dst_reg, o.Ptr()); + } else { + callee_frame->SetVReg(dst_reg, src_value); + } + } +} + +template <bool is_range> +inline bool ConvertAndCopyArgumentsFromCallerFrame( + Thread* self, + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, + const ShadowFrame& caller_frame, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + uint32_t first_dst_reg, + ShadowFrame* callee_frame) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(callsite_type->GetPTypes()); + ObjPtr<mirror::ObjectArray<mirror::Class>> to_types(callee_type->GetPTypes()); + + const int32_t num_method_params = from_types->GetLength(); + if (to_types->GetLength() != num_method_params) { + ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get()); + return false; + } + + ShadowFrameGetter<is_range> getter(first_arg, args, caller_frame); + ShadowFrameSetter setter(callee_frame, first_dst_reg); + + return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self, + callsite_type, + callee_type, + &getter, + &setter, + num_method_params); +} + +inline bool IsMethodHandleInvokeExact(const ArtMethod* const method) { + if (method == jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) { + return true; + } else { + DCHECK_EQ(method, jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke)); + return false; + } +} + +inline bool IsInvoke(const mirror::MethodHandle::Kind handle_kind) { + return handle_kind <= mirror::MethodHandle::Kind::kLastInvokeKind; +} + +inline bool IsInvokeTransform(const mirror::MethodHandle::Kind handle_kind) { + return (handle_kind == mirror::MethodHandle::Kind::kInvokeTransform + || handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform); +} + +inline bool IsFieldAccess(mirror::MethodHandle::Kind handle_kind) { + return (handle_kind >= mirror::MethodHandle::Kind::kFirstAccessorKind + && handle_kind <= mirror::MethodHandle::Kind::kLastAccessorKind); +} + +// Calculate the number of ins for a proxy or native method, where we +// can't just look at the code item. +static inline size_t GetInsForProxyOrNativeMethod(ArtMethod* method) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(method->IsNative() || method->IsProxyMethod()); + + method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize); + size_t num_ins = 0; + // Separate accounting for the receiver, which isn't a part of the + // shorty. + if (!method->IsStatic()) { + ++num_ins; + } + + uint32_t shorty_len = 0; + const char* shorty = method->GetShorty(&shorty_len); + for (size_t i = 1; i < shorty_len; ++i) { + const char c = shorty[i]; + ++num_ins; + if (c == 'J' || c == 'D') { + ++num_ins; + } + } + + return num_ins; +} + +// Returns true iff. the callsite type for a polymorphic invoke is transformer +// like, i.e that it has a single input argument whose type is +// dalvik.system.EmulatedStackFrame. +static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::ObjectArray<mirror::Class>> param_types(callsite_type->GetPTypes()); + if (param_types->GetLength() == 1) { + ObjPtr<mirror::Class> param(param_types->GetWithoutChecks(0)); + return param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame); + } + + return false; +} + +template <bool is_range> +static inline bool DoCallPolymorphic(ArtMethod* called_method, + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> target_type, + Thread* self, + ShadowFrame& shadow_frame, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result, + const mirror::MethodHandle::Kind handle_kind) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Compute method information. + const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + + // Number of registers for the callee's call frame. Note that for non-exact + // invokes, we always derive this information from the callee method. We + // cannot guarantee during verification that the number of registers encoded + // in the invoke is equal to the number of ins for the callee. This is because + // some transformations (such as boxing a long -> Long or wideining an + // int -> long will change that number. + uint16_t num_regs; + size_t num_input_regs; + size_t first_dest_reg; + if (LIKELY(code_item != nullptr)) { + num_regs = code_item->registers_size_; + first_dest_reg = num_regs - code_item->ins_size_; + num_input_regs = code_item->ins_size_; + // Parameter registers go at the end of the shadow frame. + DCHECK_NE(first_dest_reg, (size_t)-1); + } else { + // No local regs for proxy and native methods. + DCHECK(called_method->IsNative() || called_method->IsProxyMethod()); + num_regs = num_input_regs = GetInsForProxyOrNativeMethod(called_method); + first_dest_reg = 0; + } + + // Allocate shadow frame on the stack. + ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = + CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0); + ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); + + // Whether this polymorphic invoke was issued by a transformer method. + bool is_caller_transformer = false; + // Thread might be suspended during PerformArgumentConversions due to the + // allocations performed during boxing. + { + ScopedStackedShadowFramePusher pusher( + self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); + if (callsite_type->IsExactMatch(target_type.Get())) { + // This is an exact invoke, we can take the fast path of just copying all + // registers without performing any argument conversions. + CopyArgumentsFromCallerFrame<is_range>(shadow_frame, + new_shadow_frame, + args, + first_arg, + first_dest_reg, + num_input_regs); + } else { + // This includes the case where we're entering this invoke-polymorphic + // from a transformer method. In that case, the callsite_type will contain + // a single argument of type dalvik.system.EmulatedStackFrame. In that + // case, we'll have to unmarshal the EmulatedStackFrame into the + // new_shadow_frame and perform argument conversions on it. + if (IsCallerTransformer(callsite_type)) { + is_caller_transformer = true; + // The emulated stack frame is the first and only argument when we're coming + // through from a transformer. + size_t first_arg_register = (is_range) ? first_arg : args[0]; + ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame( + reinterpret_cast<mirror::EmulatedStackFrame*>( + shadow_frame.GetVRegReference(first_arg_register))); + if (!emulated_stack_frame->WriteToShadowFrame(self, + target_type, + first_dest_reg, + new_shadow_frame)) { + DCHECK(self->IsExceptionPending()); + result->SetL(0); + return false; + } + } else if (!ConvertAndCopyArgumentsFromCallerFrame<is_range>(self, + callsite_type, + target_type, + shadow_frame, + args, + first_arg, + first_dest_reg, + new_shadow_frame)) { + DCHECK(self->IsExceptionPending()); + result->SetL(0); + return false; + } + } + } + + // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver + // based dispatch right before we perform the actual call, because the + // receiver isn't known very early. + if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || + handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { + ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg)); + ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass()); + // Verify that _vRegC is an object reference and of the type expected by + // the receiver. + if (!VerifyObjectIsClass(receiver, declaring_class)) { + DCHECK(self->IsExceptionPending()); + return false; + } + + called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( + called_method, kRuntimePointerSize); + } + + PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result); + if (self->IsExceptionPending()) { + return false; + } + + // If the caller of this signature polymorphic method was a transformer, + // we need to copy the result back out to the emulated stack frame. + if (is_caller_transformer) { + StackHandleScope<2> hs(self); + size_t first_callee_register = is_range ? (first_arg) : args[0]; + Handle<mirror::EmulatedStackFrame> emulated_stack_frame( + hs.NewHandle(reinterpret_cast<mirror::EmulatedStackFrame*>( + shadow_frame.GetVRegReference(first_callee_register)))); + Handle<mirror::MethodType> emulated_stack_type(hs.NewHandle(emulated_stack_frame->GetType())); + JValue local_result; + local_result.SetJ(result->GetJ()); + + if (ConvertReturnValue(emulated_stack_type, target_type, &local_result)) { + emulated_stack_frame->SetReturnValue(self, local_result); + return true; + } else { + DCHECK(self->IsExceptionPending()); + return false; + } + } else { + return ConvertReturnValue(callsite_type, target_type, result); + } +} + +template <bool is_range> +static inline bool DoCallTransform(ArtMethod* called_method, + Handle<mirror::MethodType> callsite_type, + Handle<mirror::MethodType> callee_type, + Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> receiver, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + // This can be fixed to two, because the method we're calling here + // (MethodHandle.transformInternal) doesn't have any locals and the signature + // is known : + // + // private MethodHandle.transformInternal(EmulatedStackFrame sf); + // + // This means we need only two vregs : + // - One for the receiver object. + // - One for the only method argument (an EmulatedStackFrame). + static constexpr size_t kNumRegsForTransform = 2; + + const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + DCHECK(code_item != nullptr); + DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_); + DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_); + + ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr = + CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0); + ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get(); + + StackHandleScope<1> hs(self); + MutableHandle<mirror::EmulatedStackFrame> sf(hs.NewHandle<mirror::EmulatedStackFrame>(nullptr)); + if (IsCallerTransformer(callsite_type)) { + // If we're entering this transformer from another transformer, we can pass + // through the handle directly to the callee, instead of having to + // instantiate a new stack frame based on the shadow frame. + size_t first_callee_register = is_range ? first_arg : args[0]; + sf.Assign(reinterpret_cast<mirror::EmulatedStackFrame*>( + shadow_frame.GetVRegReference(first_callee_register))); + } else { + sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs<is_range>(self, + callsite_type, + callee_type, + shadow_frame, + first_arg, + args)); + + // Something went wrong while creating the emulated stack frame, we should + // throw the pending exception. + if (sf.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + } + + new_shadow_frame->SetVRegReference(0, receiver.Get()); + new_shadow_frame->SetVRegReference(1, sf.Get()); + + PerformCall(self, + code_item, + shadow_frame.GetMethod(), + 0 /* first destination register */, + new_shadow_frame, + result); + if (self->IsExceptionPending()) { + return false; + } + + // If the called transformer method we called has returned a value, then we + // need to copy it back to |result|. + sf->GetReturnValue(self, result); + return ConvertReturnValue(callsite_type, callee_type, result); +} + +inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self, ArtField* field) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Method handle invocations on static fields should ensure class is + // initialized. This usually happens when an instance is constructed + // or class members referenced, but this is not guaranteed when + // looking up method handles. + ObjPtr<mirror::Class> klass = field->GetDeclaringClass(); + if (UNLIKELY(!klass->IsInitialized())) { + StackHandleScope<1> hs(self); + HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(&klass)); + if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h, true, true)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + } + return klass; +} + +template <bool is_range> +bool DoInvokePolymorphicUnchecked(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType())); + const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + if (IsInvoke(handle_kind)) { + // Get the method we're actually invoking along with the kind of + // invoke that is desired. We don't need to perform access checks at this + // point because they would have been performed on our behalf at the point + // of creation of the method handle. + ArtMethod* called_method = method_handle->GetTargetMethod(); + CHECK(called_method != nullptr); + + if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || + handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { + // TODO: Unfortunately, we have to postpone dynamic receiver based checks + // because the receiver might be cast or might come from an emulated stack + // frame, which means that it is unknown at this point. We perform these + // checks inside DoCallPolymorphic right before we do the actual invoke. + } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) { + // String constructors are a special case, they are replaced with StringFactory + // methods. + if (called_method->IsConstructor() && called_method->GetDeclaringClass()->IsStringClass()) { + DCHECK(handle_type->GetRType()->IsStringClass()); + called_method = WellKnownClasses::StringInitToStringFactory(called_method); + } + } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) { + ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass(); + + // Note that we're not dynamically dispatching on the type of the receiver + // here. We use the static type of the "receiver" object that we've + // recorded in the method handle's type, which will be the same as the + // special caller that was specified at the point of lookup. + ObjPtr<mirror::Class> referrer_class = handle_type->GetPTypes()->Get(0); + if (!declaring_class->IsInterface()) { + ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass(); + uint16_t vtable_index = called_method->GetMethodIndex(); + DCHECK(super_class != nullptr); + DCHECK(super_class->HasVTable()); + // Note that super_class is a super of referrer_class and called_method + // will always be declared by super_class (or one of its super classes). + DCHECK_LT(vtable_index, super_class->GetVTableLength()); + called_method = super_class->GetVTableEntry(vtable_index, kRuntimePointerSize); + } else { + called_method = referrer_class->FindVirtualMethodForInterfaceSuper( + called_method, kRuntimePointerSize); + } + CHECK(called_method != nullptr); + } + if (IsInvokeTransform(handle_kind)) { + // There are two cases here - method handles representing regular + // transforms and those representing call site transforms. Method + // handles for call site transforms adapt their MethodType to match + // the call site. For these, the |callee_type| is the same as the + // |callsite_type|. The VarargsCollector is such a tranform, its + // method type depends on the call site, ie. x(a) or x(a, b), or + // x(a, b, c). The VarargsCollector invokes a variable arity method + // with the arity arguments in an array. + Handle<mirror::MethodType> callee_type = + (handle_kind == mirror::MethodHandle::Kind::kInvokeCallSiteTransform) ? callsite_type + : handle_type; + return DoCallTransform<is_range>(called_method, + callsite_type, + callee_type, + self, + shadow_frame, + method_handle /* receiver */, + args, + first_arg, + result); + + } else { + return DoCallPolymorphic<is_range>(called_method, + callsite_type, + handle_type, + self, + shadow_frame, + args, + first_arg, + result, + handle_kind); + } + } else { + LOG(FATAL) << "Unreachable: " << handle_kind; + UNREACHABLE(); + } +} + +// Helper for getters in invoke-polymorphic. +inline static void DoFieldGetForInvokePolymorphic(Thread* self, + const ShadowFrame& shadow_frame, + ObjPtr<mirror::Object>& obj, + ArtField* field, + Primitive::Type field_type, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + switch (field_type) { + case Primitive::kPrimBoolean: + DoFieldGetCommon<Primitive::kPrimBoolean>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimByte: + DoFieldGetCommon<Primitive::kPrimByte>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimChar: + DoFieldGetCommon<Primitive::kPrimChar>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimShort: + DoFieldGetCommon<Primitive::kPrimShort>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimInt: + DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimLong: + DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimFloat: + DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimDouble: + DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimNot: + DoFieldGetCommon<Primitive::kPrimNot>(self, shadow_frame, obj, field, result); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } +} + +// Helper for setters in invoke-polymorphic. +template <bool do_assignability_check> +inline bool DoFieldPutForInvokePolymorphic(Thread* self, + ShadowFrame& shadow_frame, + ObjPtr<mirror::Object>& obj, + ArtField* field, + Primitive::Type field_type, + const JValue& value) + REQUIRES_SHARED(Locks::mutator_lock_) { + static const bool kTransaction = false; + switch (field_type) { + case Primitive::kPrimBoolean: + return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimByte: + return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimChar: + return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimShort: + return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimNot: + return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>( + self, shadow_frame, obj, field, value); + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } +} + +static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, + Primitive::Type field_type, + uint32_t vreg) + REQUIRES_SHARED(Locks::mutator_lock_) { + JValue field_value; + switch (field_type) { + case Primitive::kPrimBoolean: + field_value.SetZ(static_cast<uint8_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimByte: + field_value.SetB(static_cast<int8_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimChar: + field_value.SetC(static_cast<uint16_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimShort: + field_value.SetS(static_cast<int16_t>(shadow_frame.GetVReg(vreg))); + break; + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + field_value.SetI(shadow_frame.GetVReg(vreg)); + break; + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + field_value.SetJ(shadow_frame.GetVRegLong(vreg)); + break; + case Primitive::kPrimNot: + field_value.SetL(shadow_frame.GetVRegReference(vreg)); + break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable: " << field_type; + UNREACHABLE(); + } + return field_value; +} + +template <bool is_range, bool do_conversions, bool do_assignability_check> +bool DoInvokePolymorphicFieldAccess(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + StackHandleScope<1> hs(self); + Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType())); + const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + ArtField* field = method_handle->GetTargetField(); + Primitive::Type field_type = field->GetTypeAsPrimitiveType(); + + switch (handle_kind) { + case mirror::MethodHandle::kInstanceGet: { + size_t obj_reg = is_range ? first_arg : args[0]; + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg); + DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { + DCHECK(self->IsExceptionPending()); + return false; + } + return true; + } + case mirror::MethodHandle::kStaticGet: { + ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); + if (obj == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result); + if (do_conversions && !ConvertReturnValue(callsite_type, handle_type, result)) { + DCHECK(self->IsExceptionPending()); + return false; + } + return true; + } + case mirror::MethodHandle::kInstancePut: { + size_t obj_reg = is_range ? first_arg : args[0]; + size_t value_reg = is_range ? (first_arg + 1) : args[1]; + JValue value = GetValueFromShadowFrame(shadow_frame, field_type, value_reg); + if (do_conversions && !ConvertArgumentValue(callsite_type, handle_type, 1, &value)) { + DCHECK(self->IsExceptionPending()); + return false; + } + ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg); + return DoFieldPutForInvokePolymorphic<do_assignability_check>(self, + shadow_frame, + obj, + field, + field_type, + value); + } + case mirror::MethodHandle::kStaticPut: { + ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); + if (obj == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + size_t value_reg = is_range ? first_arg : args[0]; + JValue value = GetValueFromShadowFrame(shadow_frame, field_type, value_reg); + if (do_conversions && !ConvertArgumentValue(callsite_type, handle_type, 0, &value)) { + DCHECK(self->IsExceptionPending()); + return false; + } + return DoFieldPutForInvokePolymorphic<do_assignability_check>(self, + shadow_frame, + obj, + field, + field_type, + value); + } + default: + LOG(FATAL) << "Unreachable: " << handle_kind; + UNREACHABLE(); + } +} + +template <bool is_range, bool do_assignability_check> +static inline bool DoInvokePolymorphicNonExact(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType()); + CHECK(handle_type != nullptr); + + if (!IsInvokeTransform(handle_kind)) { + if (UNLIKELY(!IsCallerTransformer(callsite_type) && + !callsite_type->IsConvertible(handle_type.Ptr()))) { + ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get()); + return false; + } + } + + if (IsFieldAccess(handle_kind)) { + if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) { + const bool do_convert = false; + return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } else { + const bool do_convert = true; + return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } + } + + if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) { + return DoInvokePolymorphicUnchecked<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } else { + return DoInvokePolymorphicUnchecked<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } +} + +template <bool is_range, bool do_assignability_check> +bool DoInvokePolymorphicExact(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + // We need to check the nominal type of the handle in addition to the + // real type. The "nominal" type is present when MethodHandle.asType is + // called any handle, and results in the declared type of the handle + // changing. + ObjPtr<mirror::MethodType> nominal_type(method_handle->GetNominalType()); + if (UNLIKELY(nominal_type != nullptr)) { + if (UNLIKELY(!callsite_type->IsExactMatch(nominal_type.Ptr()))) { + ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get()); + return false; + } + return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } + + ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType()); + if (UNLIKELY(!callsite_type->IsExactMatch(handle_type.Ptr()))) { + ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get()); + return false; + } + + const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + if (IsFieldAccess(handle_kind)) { + const bool do_convert = false; + return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } + + return DoInvokePolymorphicUnchecked<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); +} + +} // namespace + +template <bool is_range, bool do_assignability_check> +bool DoInvokePolymorphic(Thread* self, + ArtMethod* invoke_method, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (IsMethodHandleInvokeExact(invoke_method)) { + return DoInvokePolymorphicExact<is_range, do_assignability_check>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } else { + return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } +} + +#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \ +template REQUIRES_SHARED(Locks::mutator_lock_) \ +bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \ + Thread* self, \ + ArtMethod* invoke_method, \ + ShadowFrame& shadow_frame, \ + Handle<mirror::MethodHandleImpl> method_handle, \ + Handle<mirror::MethodType> callsite_type, \ + const uint32_t (&args)[Instruction::kMaxVarArgRegs], \ + uint32_t first_arg, \ + JValue* result) + +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false); +#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL + } // namespace art diff --git a/runtime/method_handles.h b/runtime/method_handles.h index d0a4902cbc..734d7c7bf4 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -23,48 +23,16 @@ #include "handle.h" #include "jvalue.h" #include "mirror/class.h" -#include "mirror/method_type.h" namespace art { namespace mirror { + class MethodHandleImpl; class MethodType; -} +} // mirror class ShadowFrame; -// Defines the behaviour of a given method handle. The behaviour -// of a handle of a given kind is identical to the dex bytecode behaviour -// of the equivalent instruction. -// -// NOTE: These must be kept in sync with the constants defined in -// java.lang.invoke.MethodHandle. -enum MethodHandleKind { - kInvokeVirtual = 0, - kInvokeSuper, - kInvokeDirect, - kInvokeStatic, - kInvokeInterface, - kInvokeTransform, - kInvokeCallSiteTransform, - kInstanceGet, - kInstancePut, - kStaticGet, - kStaticPut, - kLastValidKind = kStaticPut, - kLastInvokeKind = kInvokeCallSiteTransform -}; - -// Whether the given method handle kind is some variant of an invoke. -inline bool IsInvoke(const MethodHandleKind handle_kind) { - return handle_kind <= kLastInvokeKind; -} - -// Whether the given method handle kind is some variant of a tranform. -inline bool IsInvokeTransform(const MethodHandleKind handle_kind) { - return handle_kind == kInvokeTransform || handle_kind == kInvokeCallSiteTransform; -} - // Returns true if there is a possible conversion from |from| to |to| // for a MethodHandle parameter. bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from, @@ -158,19 +126,6 @@ bool PerformConversions(Thread* self, S* setter, int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_); -// A convenience wrapper around |PerformConversions|, for the case where -// the setter and getter are both ShadowFrame based. -template <bool is_range> -bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self, - Handle<mirror::MethodType> callsite_type, - Handle<mirror::MethodType> callee_type, - const ShadowFrame& caller_frame, - uint32_t first_src_reg, - uint32_t first_dest_reg, - const uint32_t (&arg)[Instruction::kMaxVarArgRegs], - ShadowFrame* callee_frame) - REQUIRES_SHARED(Locks::mutator_lock_); - // A convenience class that allows for iteration through a list of // input argument registers |arg| for non-range invokes or a list of // consecutive registers starting with a given based for range @@ -178,7 +133,8 @@ bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self, // // This is used to iterate over input arguments while performing standard // argument conversions. -template <bool is_range> class ShadowFrameGetter { +template <bool is_range> +class ShadowFrameGetter { public: ShadowFrameGetter(size_t first_src_reg, const uint32_t (&arg)[Instruction::kMaxVarArgRegs], @@ -246,6 +202,17 @@ class ShadowFrameSetter { size_t arg_index_; }; +template <bool is_range, bool do_assignability_check> +bool DoInvokePolymorphic(Thread* self, + ArtMethod* invoke_method, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodType> callsite_type, + const uint32_t (&args)[Instruction::kMaxVarArgRegs], + uint32_t first_arg, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_); + } // namespace art #endif // ART_RUNTIME_METHOD_HANDLES_H_ diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 5ea82b5357..abe999a5ac 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -32,6 +32,37 @@ namespace mirror { // C++ mirror of java.lang.invoke.MethodHandle class MANAGED MethodHandle : public Object { public: + // Defines the behaviour of a given method handle. The behaviour + // of a handle of a given kind is identical to the dex bytecode behaviour + // of the equivalent instruction. + // + // NOTE: These must be kept in sync with the constants defined in + // java.lang.invoke.MethodHandle. + enum Kind { + kInvokeVirtual = 0, + kInvokeSuper, + kInvokeDirect, + kInvokeStatic, + kInvokeInterface, + kInvokeTransform, + kInvokeCallSiteTransform, + kInstanceGet, + kInstancePut, + kStaticGet, + kStaticPut, + kLastValidKind = kStaticPut, + kFirstAccessorKind = kInstanceGet, + kLastAccessorKind = kStaticPut, + kLastInvokeKind = kInvokeCallSiteTransform + }; + + Kind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) { + const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_)); + DCHECK(handle_kind >= 0 && + handle_kind <= static_cast<int32_t>(Kind::kLastValidKind)); + return static_cast<Kind>(handle_kind); + } + mirror::MethodType* GetMethodType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_)); } @@ -50,13 +81,6 @@ class MANAGED MethodHandle : public Object { GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); } - MethodHandleKind GetHandleKind() REQUIRES_SHARED(Locks::mutator_lock_) { - const int32_t handle_kind = GetField32(OFFSET_OF_OBJECT_MEMBER(MethodHandle, handle_kind_)); - - DCHECK(handle_kind >= 0 && handle_kind <= MethodHandleKind::kLastValidKind); - return static_cast<MethodHandleKind>(handle_kind); - } - static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); private: diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 009170cb1c..7b5ced19dc 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -52,6 +52,7 @@ jclass WellKnownClasses::java_lang_ClassNotFoundException; jclass WellKnownClasses::java_lang_Daemons; jclass WellKnownClasses::java_lang_Error; jclass WellKnownClasses::java_lang_ExceptionInInitializerError; +jclass WellKnownClasses::java_lang_invoke_MethodHandle; jclass WellKnownClasses::java_lang_IllegalAccessError; jclass WellKnownClasses::java_lang_NoClassDefFoundError; jclass WellKnownClasses::java_lang_Object; @@ -93,6 +94,8 @@ jmethodID WellKnownClasses::java_lang_Daemons_stop; jmethodID WellKnownClasses::java_lang_Double_valueOf; jmethodID WellKnownClasses::java_lang_Float_valueOf; jmethodID WellKnownClasses::java_lang_Integer_valueOf; +jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invoke; +jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact; jmethodID WellKnownClasses::java_lang_Long_valueOf; jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add; jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add; @@ -288,6 +291,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Error = CacheClass(env, "java/lang/Error"); java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError"); java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError"); + java_lang_invoke_MethodHandle = CacheClass(env, "java/lang/invoke/MethodHandle"); java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError"); java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor"); java_lang_reflect_Executable = CacheClass(env, "java/lang/reflect/Executable"); @@ -321,7 +325,12 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_Daemons_requestHeapTrim = CacheMethod(env, java_lang_Daemons, true, "requestHeapTrim", "()V"); java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V"); java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V"); - + java_lang_invoke_MethodHandle_invoke = + CacheMethod(env, java_lang_invoke_MethodHandle, false, + "invoke", "([Ljava/lang/Object;)Ljava/lang/Object;"); + java_lang_invoke_MethodHandle_invokeExact = + CacheMethod(env, java_lang_invoke_MethodHandle, false, + "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;"); ScopedLocalRef<jclass> java_lang_ref_FinalizerReference(env, env->FindClass("java/lang/ref/FinalizerReference")); java_lang_ref_FinalizerReference_add = CacheMethod(env, java_lang_ref_FinalizerReference.get(), true, "add", "(Ljava/lang/Object;)V"); ScopedLocalRef<jclass> java_lang_ref_ReferenceQueue(env, env->FindClass("java/lang/ref/ReferenceQueue")); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 227996ad99..371be61f43 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -63,6 +63,7 @@ struct WellKnownClasses { static jclass java_lang_Error; static jclass java_lang_ExceptionInInitializerError; static jclass java_lang_IllegalAccessError; + static jclass java_lang_invoke_MethodHandle; static jclass java_lang_NoClassDefFoundError; static jclass java_lang_Object; static jclass java_lang_OutOfMemoryError; @@ -103,6 +104,8 @@ struct WellKnownClasses { static jmethodID java_lang_Double_valueOf; static jmethodID java_lang_Float_valueOf; static jmethodID java_lang_Integer_valueOf; + static jmethodID java_lang_invoke_MethodHandle_invoke; + static jmethodID java_lang_invoke_MethodHandle_invokeExact; static jmethodID java_lang_Long_valueOf; static jmethodID java_lang_ref_FinalizerReference_add; static jmethodID java_lang_ref_ReferenceQueue_add; |