interpreter: support for non-exact invokes.
Performs primitive argument conversions as well as boxing and unboxing
operations. Support for return value conversions will be added in a
follow up change.
Test: make test-art-host
Change-Id: I2e3348ff64a5826e477f87c12a7d5c390eb3a653
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 7fa8cf9..6140e79 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -28,6 +28,7 @@
#include "dex_instruction-inl.h"
#include "invoke_type.h"
#include "mirror/class-inl.h"
+#include "mirror/method_type.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "thread.h"
@@ -791,4 +792,17 @@
va_end(args);
}
+// WrongMethodTypeException
+
+void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
+ mirror::MethodType* callsite_type) {
+ // TODO(narayan): Should we provide more detail here ? The RI doesn't bother.
+ UNUSED(callee_type);
+ UNUSED(callsite_type);
+
+ ThrowException("Ljava/lang/invoke/WrongMethodTypeException;",
+ nullptr,
+ "Invalid method type for signature polymorphic call");
+}
+
} // namespace art
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 945dc2d..ee36fb7 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -24,6 +24,7 @@
namespace mirror {
class Class;
class Object;
+ class MethodType;
} // namespace mirror
class ArtField;
class ArtMethod;
@@ -219,6 +220,11 @@
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+// WrontMethodTypeException
+void ThrowWrongMethodTypeException(mirror::MethodType* callee_type,
+ mirror::MethodType* callsite_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
} // namespace art
#endif // ART_RUNTIME_COMMON_THROWS_H_
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index db7ebb4..191ffcc 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -24,6 +24,7 @@
#include "jit/jit.h"
#include "jvalue.h"
#include "method_handles.h"
+#include "method_handles-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
#include "mirror/method_handle_impl.h"
@@ -474,24 +475,6 @@
UNREACHABLE();
}
-// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
-static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame,
- size_t dest_reg, size_t src_reg)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Uint required, so that sign extension does not make this wrong on 64b systems
- uint32_t src_value = shadow_frame.GetVReg(src_reg);
- mirror::Object* o = shadow_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)) {
- new_shadow_frame->SetVRegReference(dest_reg, o);
- } else {
- new_shadow_frame->SetVReg(dest_reg, src_value);
- }
-}
-
void AbortTransactionF(Thread* self, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -519,6 +502,17 @@
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
uint32_t vregC) ALWAYS_INLINE;
+// Separate declaration is required solely for the attributes.
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
+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 vregC) ALWAYS_INLINE;
+
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
const DexFile::CodeItem* code_item,
@@ -597,9 +591,10 @@
// signature polymorphic method so that we disallow calls via invoke-polymorphic
// to non sig-poly methods. This would also have the side effect of verifying
// that vRegC really is a reference type.
- mirror::MethodHandleImpl* const method_handle =
- reinterpret_cast<mirror::MethodHandleImpl*>(shadow_frame.GetVRegReference(vRegC));
- if (UNLIKELY(method_handle == nullptr)) {
+ StackHandleScope<6> hs(self);
+ Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle(
+ reinterpret_cast<mirror::MethodHandleImpl*>(shadow_frame.GetVRegReference(vRegC))));
+ if (UNLIKELY(method_handle.Get() == nullptr)) {
const int method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
// Note that the invoke type is kVirtual here because a call to a signature
// polymorphic method is shaped like a virtual call at the bytecode level.
@@ -616,32 +611,29 @@
// Call through to the classlinker and ask it to resolve the static type associated
// with the callsite. This information is stored in the dex cache so it's
// guaranteed to be fast after the first resolution.
- StackHandleScope<2> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- mirror::Class* caller_class = shadow_frame.GetMethod()->GetDeclaringClass();
- mirror::MethodType* callsite_type = class_linker->ResolveMethodType(
+ Handle<mirror::Class> caller_class(hs.NewHandle(shadow_frame.GetMethod()->GetDeclaringClass()));
+ Handle<mirror::MethodType> callsite_type(hs.NewHandle(class_linker->ResolveMethodType(
caller_class->GetDexFile(), callsite_proto_id,
hs.NewHandle<mirror::DexCache>(caller_class->GetDexCache()),
- hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()));
+ hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()))));
// This implies we couldn't resolve one or more types in this method handle.
- if (UNLIKELY(callsite_type == nullptr)) {
+ if (UNLIKELY(callsite_type.Get() == nullptr)) {
CHECK(self->IsExceptionPending());
result->SetJ(0);
return false;
}
- const char* old_cause = self->StartAssertNoThreadSuspension("DoInvokePolymorphic");
-
// 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();
const MethodHandleKind handle_kind = method_handle->GetHandleKind();
- mirror::MethodType* const handle_type = method_handle->GetMethodType();
+ Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
CHECK(called_method != nullptr);
- CHECK(handle_type != nullptr);
+ CHECK(handle_type.Get() != nullptr);
// We now have to massage the number of inputs to the target function.
// It's always one less than the number of inputs to the signature polymorphic
@@ -672,14 +664,12 @@
called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
called_method, kRuntimePointerSize);
if (!VerifyObjectIsClass(receiver, declaring_class)) {
- self->EndAssertNoThreadSuspension(old_cause);
return false;
}
} else if (handle_kind == kInvokeDirect) {
// TODO(narayan) : We need to handle the case where the target method is a
// constructor here. Also the case where we don't want to dynamically
// dispatch based on the type of the receiver.
- self->EndAssertNoThreadSuspension(old_cause);
UNIMPLEMENTED(FATAL) << "Direct invokes are not implemented yet.";
return false;
}
@@ -687,24 +677,123 @@
// NOTE: handle_kind == kInvokeStatic needs no special treatment here. We
// can directly make the call. handle_kind == kInvokeSuper doesn't have any
// particular use and can probably be dropped.
- if (callsite_type->IsExactMatch(handle_type)) {
- self->EndAssertNoThreadSuspension(old_cause);
+
+ if (callsite_type->IsExactMatch(handle_type.Get())) {
return DoCallCommon<is_range, do_access_check>(
called_method, self, shadow_frame, result, number_of_inputs,
arg, receiver_vregC);
+ } else {
+ return DoCallPolymorphic<is_range>(
+ called_method, callsite_type, handle_type, self, shadow_frame,
+ result, arg, receiver_vregC);
}
-
- self->EndAssertNoThreadSuspension(old_cause);
- UNIMPLEMENTED(FATAL) << "Non exact invokes are not implemented yet.";
- return false;
} else {
// TODO(narayan): Implement field getters and setters.
- self->EndAssertNoThreadSuspension(old_cause);
UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet.";
return false;
}
}
+// 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;
+}
+
+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 vregC) {
+ // TODO(narayan): Wire in the String.init hacks.
+
+ // 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 first_dest_reg;
+ if (LIKELY(code_item != nullptr)) {
+ num_regs = code_item->registers_size_;
+ first_dest_reg = num_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 = 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();
+
+ // Thread might be suspended during PerformArgumentConversions due to the
+ // allocations performed during boxing.
+ {
+ ScopedStackedShadowFramePusher pusher(
+ self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ if (!PerformArgumentConversions<is_range>(self, callsite_type, target_type,
+ shadow_frame, vregC, first_dest_reg,
+ arg, new_shadow_frame, result)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+ }
+
+ // Do the call now.
+ if (LIKELY(Runtime::Current()->IsStarted())) {
+ ArtMethod* target = new_shadow_frame->GetMethod();
+ if (ClassLinker::ShouldUseInterpreterEntrypoint(
+ target,
+ target->GetEntryPointFromQuickCompiledCode())) {
+ ArtInterpreterToInterpreterBridge(self, code_item, new_shadow_frame, result);
+ } else {
+ ArtInterpreterToCompiledCodeBridge(
+ self, shadow_frame.GetMethod(), code_item, new_shadow_frame, result);
+ }
+ } else {
+ UnstartedRuntime::Invoke(self, code_item, new_shadow_frame, result, first_dest_reg);
+ }
+
+ // TODO(narayan): Perform return value conversions.
+
+ return !self->IsExceptionPending();
+}
+
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 7d54d0a..bdb6bd3 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -463,6 +463,24 @@
return branch_offset <= 0;
}
+// Assign register 'src_reg' from shadow_frame to register 'dest_reg' into new_shadow_frame.
+static inline void AssignRegister(ShadowFrame* new_shadow_frame, const ShadowFrame& shadow_frame,
+ size_t dest_reg, size_t src_reg)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Uint required, so that sign extension does not make this wrong on 64b systems
+ uint32_t src_value = shadow_frame.GetVReg(src_reg);
+ mirror::Object* o = shadow_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)) {
+ new_shadow_frame->SetVRegReference(dest_reg, o);
+ } else {
+ new_shadow_frame->SetVReg(dest_reg, src_value);
+ }
+}
+
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
const DexFile::CodeItem* code_item,
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
new file mode 100644
index 0000000..5f9824c
--- /dev/null
+++ b/runtime/method_handles-inl.h
@@ -0,0 +1,260 @@
+/*
+ * 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_METHOD_HANDLES_INL_H_
+#define ART_RUNTIME_METHOD_HANDLES_INL_H_
+
+#include "method_handles.h"
+
+#include "common_throws.h"
+#include "dex_instruction.h"
+#include "interpreter/interpreter_common.h"
+#include "jvalue.h"
+#include "mirror/class.h"
+#include "mirror/method_type.h"
+#include "mirror/object.h"
+#include "reflection.h"
+#include "stack.h"
+
+namespace art {
+
+// Assigns |type| to the primitive type associated with |dst_class|. Returns
+// true iff. |dst_class| was a boxed type (Integer, Long etc.), false otherwise.
+REQUIRES_SHARED(Locks::mutator_lock_)
+static inline bool GetPrimitiveType(ObjPtr<mirror::Class> dst_class, Primitive::Type* type) {
+ if (dst_class->DescriptorEquals("Ljava/lang/Boolean;")) {
+ (*type) = Primitive::kPrimBoolean;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Byte;")) {
+ (*type) = Primitive::kPrimByte;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Character;")) {
+ (*type) = Primitive::kPrimChar;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Float;")) {
+ (*type) = Primitive::kPrimFloat;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Double;")) {
+ (*type) = Primitive::kPrimDouble;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Integer;")) {
+ (*type) = Primitive::kPrimInt;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Long;")) {
+ (*type) = Primitive::kPrimLong;
+ return true;
+ } else if (dst_class->DescriptorEquals("Ljava/lang/Short;")) {
+ (*type) = Primitive::kPrimShort;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// 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
+// invokes.
+template <bool is_range> class ArgIterator {
+ public:
+ ArgIterator(size_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) :
+ first_src_reg_(first_src_reg),
+ arg_(arg),
+ arg_index_(0) {
+ }
+
+ uint32_t Next() {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ ++arg_index_;
+
+ return next;
+ }
+
+ uint32_t NextPair() {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ arg_index_ += 2;
+
+ return next;
+ }
+
+ private:
+ const size_t first_src_reg_;
+ const uint32_t (&arg_)[Instruction::kMaxVarArgRegs];
+ size_t arg_index_;
+};
+
+template <bool is_range>
+bool PerformArgumentConversions(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,
+ JValue* result) {
+ StackHandleScope<4> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes()));
+ Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(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());
+ result->SetJ(0);
+ return false;
+ }
+
+ ArgIterator<is_range> input_args(first_src_reg, arg);
+ size_t to_arg_index = 0;
+ MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr));
+ MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr));
+ for (int32_t i = 0; i < num_method_params; ++i) {
+ from.Assign(from_types->GetWithoutChecks(i));
+ to.Assign(to_types->GetWithoutChecks(i));
+
+ const Primitive::Type from_type = from->GetPrimitiveType();
+ const Primitive::Type to_type = to->GetPrimitiveType();
+
+ // Easy case - the types are identical. Nothing left to do except to pass
+ // the arguments along verbatim.
+ if (from.Get() == to.Get()) {
+ interpreter::AssignRegister(callee_frame,
+ caller_frame,
+ first_dest_reg + to_arg_index,
+ input_args.Next());
+ ++to_arg_index;
+
+ // This is a wide argument, we must use the second half of the register
+ // pair as well.
+ if (Primitive::Is64BitType(from_type)) {
+ interpreter::AssignRegister(callee_frame,
+ caller_frame,
+ first_dest_reg + to_arg_index,
+ input_args.Next());
+ ++to_arg_index;
+ }
+
+ continue;
+ } else if ((from_type != Primitive::kPrimNot) && (to_type != Primitive::kPrimNot)) {
+ // They are both primitive types - we should perform any widening or
+ // narrowing conversions as applicable.
+ JValue from_value;
+ JValue to_value;
+
+ if (Primitive::Is64BitType(from_type)) {
+ from_value.SetJ(caller_frame.GetVRegLong(input_args.NextPair()));
+ } else {
+ from_value.SetI(caller_frame.GetVReg(input_args.Next()));
+ }
+
+ // Throws a ClassCastException if we're unable to convert a primitive value.
+ if (!ConvertPrimitiveValue(false, from_type, to_type, from_value, &to_value)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+
+ if (Primitive::Is64BitType(to_type)) {
+ callee_frame->SetVRegLong(first_dest_reg + to_arg_index, to_value.GetJ());
+ to_arg_index += 2;
+ } else {
+ callee_frame->SetVReg(first_dest_reg + to_arg_index, to_value.GetI());
+ ++to_arg_index;
+ }
+ } else if ((from_type == Primitive::kPrimNot) && (to_type == Primitive::kPrimNot)) {
+ // They're both reference types. If "from" is null, we can pass it
+ // through unchanged. If not, we must generate a cast exception if
+ // |to| is not assignable from the dynamic type of |ref|.
+ const size_t next_arg_reg = input_args.Next();
+ mirror::Object* const ref = caller_frame.GetVRegReference(next_arg_reg);
+ if (ref == nullptr || to->IsAssignableFrom(ref->GetClass())) {
+ interpreter::AssignRegister(callee_frame,
+ caller_frame,
+ first_dest_reg + to_arg_index,
+ next_arg_reg);
+ ++to_arg_index;
+ } else {
+ ThrowClassCastException(to.Get(), ref->GetClass());
+ result->SetL(0);
+ return false;
+ }
+ } else {
+ // Precisely one of the source or the destination are reference types.
+ // We must box or unbox.
+ if (to_type == Primitive::kPrimNot) {
+ // The target type is a reference, we must box.
+ Primitive::Type type;
+ // TODO(narayan): This is a CHECK for now. There might be a few corner cases
+ // here that we might not have handled yet. For exmple, if |to| is java/lang/Number;,
+ // we will need to box this "naturally".
+ CHECK(GetPrimitiveType(to.Get(), &type));
+
+ JValue from_value;
+ JValue to_value;
+
+ if (Primitive::Is64BitType(from_type)) {
+ from_value.SetJ(caller_frame.GetVRegLong(input_args.NextPair()));
+ } else {
+ from_value.SetI(caller_frame.GetVReg(input_args.Next()));
+ }
+
+ // First perform a primitive conversion to the unboxed equivalent of the target,
+ // if necessary. This should be for the rarer cases like (int->Long) etc.
+ if (UNLIKELY(from_type != type)) {
+ if (!ConvertPrimitiveValue(false, from_type, type, from_value, &to_value)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+ } else {
+ to_value = from_value;
+ }
+
+ // Then perform the actual boxing, and then set the reference.
+ ObjPtr<mirror::Object> boxed = BoxPrimitive(type, to_value);
+ callee_frame->SetVRegReference(first_dest_reg + to_arg_index, boxed.Ptr());
+ ++to_arg_index;
+ } else {
+ // The target type is a primitive, we must unbox.
+ ObjPtr<mirror::Object> ref(caller_frame.GetVRegReference(input_args.Next()));
+
+ // Note that UnboxPrimitiveForResult already performs all of the type
+ // conversions that we want, based on |to|.
+ JValue unboxed_value;
+ if (!UnboxPrimitiveForResult(ref, to.Get(), &unboxed_value)) {
+ DCHECK(self->IsExceptionPending());
+ result->SetL(0);
+ return false;
+ }
+
+ if (Primitive::Is64BitType(to_type)) {
+ callee_frame->SetVRegLong(first_dest_reg + to_arg_index, unboxed_value.GetJ());
+ to_arg_index += 2;
+ } else {
+ callee_frame->SetVReg(first_dest_reg + to_arg_index, unboxed_value.GetI());
+ ++to_arg_index;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 5c68a8f..a36b66d 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -19,8 +19,17 @@
#include <ostream>
+#include "dex_instruction.h"
+#include "jvalue.h"
+
namespace art {
+namespace mirror {
+ class MethodType;
+}
+
+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.
@@ -46,6 +55,22 @@
return handle_kind <= kLastInvokeKind;
}
+// Perform argument conversions between |callsite_type| (the type of the
+// incoming arguments) and |callee_type| (the type of the method being
+// invoked). These include widening and narrowing conversions as well as
+// boxing and unboxing. Returns true on success, on false on failure. A
+// pending exception will always be set on failure.
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
+bool PerformArgumentConversions(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,
+ JValue* result);
+
} // namespace art
#endif // ART_RUNTIME_METHOD_HANDLES_H_
diff --git a/test/955-methodhandles-smali/expected.txt b/test/955-methodhandles-smali/expected.txt
index 07d2422..047a287 100644
--- a/test/955-methodhandles-smali/expected.txt
+++ b/test/955-methodhandles-smali/expected.txt
@@ -1,2 +1,8 @@
[String1]+[String2]
[String1]
+[String1]+[String2]
+42
+40
+43
+44
+0-11
diff --git a/test/955-methodhandles-smali/smali/Main.smali b/test/955-methodhandles-smali/smali/Main.smali
index 2fc92f8..9681d56 100644
--- a/test/955-methodhandles-smali/smali/Main.smali
+++ b/test/955-methodhandles-smali/smali/Main.smali
@@ -66,8 +66,31 @@
return-object v0
.end method
-# Returns a method handle to static String java.lang.String.valueOf(String);
-.method public static getStringValueOfHandle()Ljava/lang/invoke/MethodHandle;
+# Returns a method handle to boolean java.lang.Long.compareTo(java.lang.Long other).
+.method public static getLongCompareToHandle()Ljava/lang/invoke/MethodHandle;
+.registers 4
+ new-instance v0, Ljava/lang/Long;
+ const-wide v1, 0
+ invoke-direct {v0, v1, v2}, Ljava/lang/Long;-><init>(J)V
+ invoke-virtual {v0}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+ move-result-object v0
+
+ # set v0 to Integer.TYPE aka. int.class
+ sget-object v1, Ljava/lang/Integer;->TYPE:Ljava/lang/Class;
+
+ # Call MethodType.methodType(rtype=int.class, ptype[0] = Long.class)
+ invoke-static {v1, v0}, Ljava/lang/invoke/MethodType;->methodType(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+ move-result-object v2
+
+ const-string v3, "compareTo"
+ # Call Main.getHandleForVirtual(Long.class, "compareTo", methodType);
+ invoke-static {v0, v3, v2}, LMain;->getHandleForVirtual(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
+ move-result-object v0
+ return-object v0
+.end method
+
+# Returns a method handle to static String java.lang.String.valueOf(Object);
+.method public static getStringValueOfObjectHandle()Ljava/lang/invoke/MethodHandle;
.registers 4
# set v0 to java.lang.Object.class
new-instance v0, Ljava/lang/Object;
@@ -90,6 +113,26 @@
return-object v0
.end method
+# Returns a method handle to static String java.lang.String.valueOf(String);
+.method public static getStringValueOfLongHandle()Ljava/lang/invoke/MethodHandle;
+.registers 4
+ # set v0 to Long.TYPE aka. long.class
+ sget-object v0, Ljava/lang/Long;->TYPE:Ljava/lang/Class;
+
+ # set v1 to the name of the method ("valueOf") and v2 to java.lang.String.class;
+ const-string v1, "valueOf"
+ invoke-virtual {v1}, Ljava/lang/Object;->getClass()Ljava/lang/Class;
+ move-result-object v2
+
+ # Call MethodType.methodType(rtype=String.class, ptype[0]=Long.class)
+ invoke-static {v2, v0}, Ljava/lang/invoke/MethodType;->methodType(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/MethodType;
+ move-result-object v3
+
+ # Call Main.getHandleForStatic(String.class, "valueOf", methodType);
+ invoke-static {v2, v1, v3}, LMain;->getHandleForStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;
+ move-result-object v0
+ return-object v0
+.end method
.method public static main([Ljava/lang/String;)V
.registers 5
@@ -105,7 +148,7 @@
invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
# Test case 2: Exercise String.valueOf(Object);
- invoke-static {}, LMain;->getStringValueOfHandle()Ljava/lang/invoke/MethodHandle;
+ invoke-static {}, LMain;->getStringValueOfObjectHandle()Ljava/lang/invoke/MethodHandle;
move-result-object v0
const-string v1, "[String1]"
invoke-polymorphic {v0, v1}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;)Ljava/lang/String;
@@ -113,5 +156,88 @@
sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ # Test case 3: Exercise String.concat(String, String) with an inexact invoke.
+ # Note that the callsite type here is String type(Object, Object); so the runtime
+ # will generate dynamic type checks for the input arguments.
+ invoke-static {}, LMain;->getStringConcatHandle()Ljava/lang/invoke/MethodHandle;
+ move-result-object v0
+ const-string v1, "[String1]"
+ const-string v2, "+[String2]"
+ invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/String;
+ move-result-object v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ # Test case 4: Exercise String.valueOf(long);
+ #
+ # We exercise it with various types of unboxing / widening conversions
+ invoke-static {}, LMain;->getStringValueOfLongHandle()Ljava/lang/invoke/MethodHandle;
+ move-result-object v0
+
+ # First use a long, this is an invokeExact because the callsite type matches
+ # the function type precisely.
+ const-wide v1, 42
+ invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invokeExact([Ljava/lang/Object;)Ljava/lang/Object;, (J)Ljava/lang/String;
+ move-result-object v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ # Then use an int, should perform a widening conversion.
+ const v1, 40
+ invoke-polymorphic {v0, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (I)Ljava/lang/String;
+ move-result-object v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ # Then use a java/lang/Long; - should perform an unboxing conversion.
+ new-instance v1, Ljava/lang/Long;
+ const-wide v2, 43
+ invoke-direct {v1, v2, v3}, Ljava/lang/Long;-><init>(J)V
+ invoke-polymorphic {v0, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;)Ljava/lang/String;
+ move-result-object v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ # Then use a java/lang/Integer; - should perform an unboxing in addition to a widening conversion.
+ new-instance v1, Ljava/lang/Integer;
+ const v2, 44
+ invoke-direct {v1, v2}, Ljava/lang/Integer;-><init>(I)V
+ invoke-polymorphic {v0, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Integer;)Ljava/lang/String;
+ move-result-object v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+
+ # Test case 5: Exercise int Long.compareTo(Long)
+ invoke-static {}, LMain;->getLongCompareToHandle()Ljava/lang/invoke/MethodHandle;
+ move-result-object v0
+ new-instance v1, Ljava/lang/Long;
+ const-wide v2, 43
+ invoke-direct {v1, v2, v3}, Ljava/lang/Long;-><init>(J)V
+
+ # At this point, v0 is our MethodHandle and v1 is the instance we're going to call compareTo on.
+
+ # Call compareTo(Long) - this is invokeExact semantics.
+ invoke-polymorphic {v0, v1, v1}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;Ljava/lang/Long;)I
+ move-result v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
+
+ # Call compareTo(long) - this is an implicit box.
+ const-wide v2, 44
+ invoke-polymorphic {v0, v1, v2, v3}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;J)I
+ move-result v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
+
+ # Call compareTo(int) - this is an implicit box.
+ const v2, 40
+ invoke-polymorphic {v0, v1, v2}, Ljava/lang/invoke/MethodHandle;->invoke([Ljava/lang/Object;)Ljava/lang/Object;, (Ljava/lang/Long;I)I
+ move-result v3
+ sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v4, v3}, Ljava/io/PrintStream;->print(I)V
+
+ # Add a newline at the end of file.
+ invoke-virtual {v4}, Ljava/io/PrintStream;->println()V
+
return-void
.end method