diff options
Diffstat (limited to 'runtime/method_handles.cc')
| -rw-r--r-- | runtime/method_handles.cc | 513 |
1 files changed, 288 insertions, 225 deletions
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 58c5d17d1c..bd7c4ad53c 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -49,10 +49,19 @@ namespace { bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedAssertNoThreadSuspension ants(__FUNCTION__); -#define LOOKUP_PRIMITIVE(primitive, _, __, ___) \ - if (klass->DescriptorEquals(Primitive::BoxedDescriptor(primitive))) { \ - *type = primitive; \ - return true; \ + std::string storage; + const char* descriptor = klass->GetDescriptor(&storage); + static const char kJavaLangPrefix[] = "Ljava/lang/"; + static const size_t kJavaLangPrefixSize = sizeof(kJavaLangPrefix) - 1; + if (strncmp(descriptor, kJavaLangPrefix, kJavaLangPrefixSize) != 0) { + return false; + } + + descriptor += kJavaLangPrefixSize; +#define LOOKUP_PRIMITIVE(primitive, _, java_name, ___) \ + if (strcmp(descriptor, #java_name ";") == 0) { \ + *type = primitive; \ + return true; \ } PRIMITIVES_LIST(LOOKUP_PRIMITIVE); @@ -141,21 +150,23 @@ bool IsParameterTypeConvertible(ObjPtr<mirror::Class> from, ObjPtr<mirror::Class if (from->DescriptorEquals("Ljava/lang/Object;")) { // Object might be converted into a primitive during unboxing. return true; - } else if (Primitive::IsNumericType(to_primitive) && - from->DescriptorEquals("Ljava/lang/Number;")) { + } + + if (Primitive::IsNumericType(to_primitive) && from->DescriptorEquals("Ljava/lang/Number;")) { // Number might be unboxed into any of the number primitive types. return true; } + Primitive::Type unboxed_type; if (GetUnboxedPrimitiveType(from, &unboxed_type)) { if (unboxed_type == to_primitive) { // Straightforward unboxing conversion such as Boolean => boolean. return true; - } else { - // Check if widening operations for numeric primitives would work, - // such as Byte => byte => long. - return Primitive::IsWidenable(unboxed_type, to_primitive); } + + // Check if widening operations for numeric primitives would work, + // such as Byte => byte => long. + return Primitive::IsWidenable(unboxed_type, to_primitive); } } @@ -372,25 +383,18 @@ inline bool IsFieldAccess(mirror::MethodHandle::Kind handle_kind) { 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') { + uint32_t shorty_length = 0; + const char* shorty = method->GetShorty(&shorty_length); + + // Static methods do not include the receiver. The receiver isn't included + // in the shorty_length though the return value is. + size_t num_ins = method->IsStatic() ? shorty_length - 1 : shorty_length; + for (const char* c = shorty + 1; *c != '\0'; ++c) { + if (*c == 'J' || *c == 'D') { ++num_ins; } } - return num_ins; } @@ -402,7 +406,10 @@ static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type) 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); + // NB Comparing descriptor here as it appears faster in cycle simulation than using: + // param == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_EmulatedStackFrame) + // Costs are 98 vs 173 cycles per invocation. + return param->DescriptorEquals("Ldalvik/system/EmulatedStackFrame;"); } return false; @@ -416,35 +423,8 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, ShadowFrame& shadow_frame, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, - JValue* result, - const mirror::MethodHandle::Kind handle_kind) + JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { - // For virtual and interface methods ensure called_method points to - // the actual method to invoke. - if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || - handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { - uint32_t receiver_reg = is_range ? first_arg : args[0]; - ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg)); - if (IsCallerTransformer(callsite_type)) { - // The current receiver is an emulated stack frame, the method's - // receiver needs to be fetched from there as the emulated frame - // will be unpacked into a new frame. - receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver(); - } - - ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass()); - if (receiver == nullptr || receiver->GetClass() != declaring_class) { - // 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); - } - } - // Compute method information. const DexFile::CodeItem* code_item = called_method->GetCodeItem(); @@ -513,17 +493,23 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, 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; + } else { + if (!callsite_type->IsConvertible(target_type.Get())) { + ThrowWrongMethodTypeException(target_type.Get(), callsite_type.Get()); + return false; + } + 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; + } } } } @@ -548,13 +534,13 @@ static inline bool DoCallPolymorphic(ArtMethod* called_method, 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); + + DCHECK(self->IsExceptionPending()); + return false; } + + return ConvertReturnValue(callsite_type, target_type, result); } template <bool is_range> @@ -650,98 +636,130 @@ inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self, return klass; } +ArtMethod* RefineTargetMethod(Thread* self, + ShadowFrame& shadow_frame, + const mirror::MethodHandle::Kind& handle_kind, + Handle<mirror::MethodType> handle_type, + Handle<mirror::MethodType> callsite_type, + const uint32_t receiver_reg, + ArtMethod* target_method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (handle_kind == mirror::MethodHandle::Kind::kInvokeVirtual || + handle_kind == mirror::MethodHandle::Kind::kInvokeInterface) { + // For virtual and interface methods ensure target_method points to + // the actual method to invoke. + ObjPtr<mirror::Object> receiver(shadow_frame.GetVRegReference(receiver_reg)); + if (IsCallerTransformer(callsite_type)) { + // The current receiver is an emulated stack frame, the method's + // receiver needs to be fetched from there as the emulated frame + // will be unpacked into a new frame. + receiver = ObjPtr<mirror::EmulatedStackFrame>::DownCast(receiver)->GetReceiver(); + } + + ObjPtr<mirror::Class> declaring_class(target_method->GetDeclaringClass()); + if (receiver == nullptr || receiver->GetClass() != declaring_class) { + // Verify that _vRegC is an object reference and of the type expected by + // the receiver. + if (!VerifyObjectIsClass(receiver, declaring_class)) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + return receiver->GetClass()->FindVirtualMethodForVirtualOrInterface( + target_method, kRuntimePointerSize); + } + } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) { + // String constructors are a special case, they are replaced with + // StringFactory methods. + if (target_method->IsConstructor() && target_method->GetDeclaringClass()->IsStringClass()) { + DCHECK(handle_type->GetRType()->IsStringClass()); + return WellKnownClasses::StringInitToStringFactory(target_method); + } + } else if (handle_kind == mirror::MethodHandle::Kind::kInvokeSuper) { + ObjPtr<mirror::Class> declaring_class = target_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 = target_method->GetMethodIndex(); + DCHECK(super_class != nullptr); + DCHECK(super_class->HasVTable()); + // Note that super_class is a super of referrer_class and target_method + // will always be declared by super_class (or one of its super classes). + DCHECK_LT(vtable_index, super_class->GetVTableLength()); + return super_class->GetVTableEntry(vtable_index, kRuntimePointerSize); + } else { + return referrer_class->FindVirtualMethodForInterfaceSuper(target_method, kRuntimePointerSize); + } + } + return target_method; +} + template <bool is_range> -bool DoInvokePolymorphicUnchecked(Thread* self, - ShadowFrame& shadow_frame, - Handle<mirror::MethodHandle> method_handle, - Handle<mirror::MethodType> callsite_type, - const uint32_t (&args)[Instruction::kMaxVarArgRegs], - uint32_t first_arg, - JValue* result) +bool DoInvokePolymorphicMethod(Thread* self, + ShadowFrame& shadow_frame, + Handle<mirror::MethodHandle> 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, + DCHECK(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* target_method = method_handle->GetTargetMethod(); + uint32_t receiver_reg = is_range ? first_arg: args[0]; + ArtMethod* called_method = RefineTargetMethod(self, + shadow_frame, + handle_kind, + handle_type, + callsite_type, + receiver_reg, + target_method); + if (called_method == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + + 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, - callee_type, + handle_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(); } } @@ -948,55 +966,30 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, 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()))) { + if (IsFieldAccess(handle_kind)) { + DCHECK(!callsite_type->IsExactMatch(handle_type.Ptr())); + if (!callsite_type->IsConvertible(handle_type.Ptr())) { ThrowWrongMethodTypeException(handle_type.Ptr(), callsite_type.Get()); return false; } + const bool do_convert = true; + return DoInvokePolymorphicFieldAccess<is_range, do_convert>( + self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); } - if (IsFieldAccess(handle_kind)) { - if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) { - const bool do_convert = false; - return DoInvokePolymorphicFieldAccess<is_range, do_convert>( - self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); - } else { - const bool do_convert = true; - return DoInvokePolymorphicFieldAccess<is_range, do_convert>( - 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); - } + return DoInvokePolymorphicMethod<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); } template <bool is_range> @@ -1008,32 +1001,9 @@ bool DoInvokePolymorphicExact(Thread* self, 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>(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; - } - + StackHandleScope<1> hs(self); const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); + Handle<mirror::MethodType> method_handle_type(hs.NewHandle(method_handle->GetMethodType())); if (IsFieldAccess(handle_kind)) { const bool do_convert = false; return DoInvokePolymorphicFieldAccess<is_range, do_convert>( @@ -1046,13 +1016,68 @@ bool DoInvokePolymorphicExact(Thread* self, result); } - return DoInvokePolymorphicUnchecked<is_range>(self, + // Slow-path check. + if (IsInvokeTransform(handle_kind) || IsCallerTransformer(callsite_type)) { + return DoInvokePolymorphicMethod<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } + + // On the fast-path. This is equivalent to DoCallPolymoprhic without the conversion paths. + ArtMethod* target_method = method_handle->GetTargetMethod(); + uint32_t receiver_reg = is_range ? first_arg : args[0]; + ArtMethod* called_method = RefineTargetMethod(self, shadow_frame, - method_handle, + handle_kind, + method_handle_type, callsite_type, - args, - first_arg, - result); + receiver_reg, + target_method); + if (called_method == nullptr) { + DCHECK(self->IsExceptionPending()); + return false; + } + + // Compute method information. + const DexFile::CodeItem* code_item = called_method->GetCodeItem(); + 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. + const char* old_cause = self->StartAssertNoThreadSuspension("DoCallCommon"); + 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(); + CopyArgumentsFromCallerFrame<is_range>(shadow_frame, + new_shadow_frame, + args, + first_arg, + first_dest_reg, + num_input_regs); + self->EndAssertNoThreadSuspension(old_cause); + + PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result); + if (self->IsExceptionPending()) { + return false; + } + return true; } } // namespace @@ -1067,7 +1092,35 @@ bool DoInvokePolymorphic(Thread* self, uint32_t first_arg, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { + ObjPtr<mirror::MethodType> method_handle_type = method_handle->GetMethodType(); if (IsMethodHandleInvokeExact(invoke_method)) { + // 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; + } + + if (LIKELY(!nominal_type->IsExactMatch(method_handle_type.Ptr()))) { + // Different nominal type means we have to treat as non-exact. + return DoInvokePolymorphicNonExact<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } + } + + if (!callsite_type->IsExactMatch(method_handle_type.Ptr())) { + ThrowWrongMethodTypeException(method_handle_type.Ptr(), callsite_type.Get()); + return false; + } return DoInvokePolymorphicExact<is_range>(self, shadow_frame, method_handle, @@ -1076,6 +1129,16 @@ bool DoInvokePolymorphic(Thread* self, first_arg, result); } else { + if (UNLIKELY(callsite_type->IsExactMatch(method_handle_type.Ptr()))) { + // A non-exact invoke that can be invoked exactly. + return DoInvokePolymorphicExact<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); + } return DoInvokePolymorphicNonExact<is_range>(self, shadow_frame, method_handle, |