Argument conversions for setter/getter MethodHandles.
Test: m test-art-host-run-test-959-invoke-polymorphic-accessors
Bug: 30550796
Change-Id: I9898605fc9f9f8a80f47f9559f3ccb99b02b07c8
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 57413dc..c6f479f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -120,6 +120,7 @@
"linear_alloc.cc",
"mem_map.cc",
"memory_region.cc",
+ "method_handles.cc",
"mirror/array.cc",
"mirror/class.cc",
"mirror/class_ext.cc",
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 981cc1b..4843c4d 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -48,7 +48,8 @@
const ShadowFrame& shadow_frame,
ObjPtr<mirror::Object>& obj,
ArtField* field,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
// Report this field access to instrumentation if needed.
@@ -299,6 +300,42 @@
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_) {
@@ -337,7 +374,8 @@
const ShadowFrame& shadow_frame,
ObjPtr<mirror::Object>& obj,
ArtField* f,
- size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
+ 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
@@ -347,36 +385,35 @@
StackHandleScope<1> hs(self);
// Wrap in handle wrapper in case the listener does thread suspension.
HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
- JValue field_value = GetFieldValue<field_type>(shadow_frame, vregA);
ObjPtr<mirror::Object> this_object = f->IsStatic() ? nullptr : obj;
instrumentation->FieldWriteEvent(self, this_object.Ptr(),
shadow_frame.GetMethod(),
shadow_frame.GetDexPC(),
f,
- field_value);
+ value);
}
switch (field_type) {
case Primitive::kPrimBoolean:
- f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+ f->SetBoolean<transaction_active>(obj, value.GetZ());
break;
case Primitive::kPrimByte:
- f->SetByte<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+ f->SetByte<transaction_active>(obj, value.GetB());
break;
case Primitive::kPrimChar:
- f->SetChar<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+ f->SetChar<transaction_active>(obj, value.GetC());
break;
case Primitive::kPrimShort:
- f->SetShort<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+ f->SetShort<transaction_active>(obj, value.GetS());
break;
case Primitive::kPrimInt:
- f->SetInt<transaction_active>(obj, shadow_frame.GetVReg(vregA));
+ f->SetInt<transaction_active>(obj, value.GetI());
break;
case Primitive::kPrimLong:
- f->SetLong<transaction_active>(obj, shadow_frame.GetVRegLong(vregA));
+ f->SetLong<transaction_active>(obj, value.GetJ());
break;
case Primitive::kPrimNot: {
- ObjPtr<mirror::Object> reg = shadow_frame.GetVRegReference(vregA);
+ 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.
@@ -434,11 +471,12 @@
}
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
shadow_frame,
obj,
f,
- vregA);
+ value);
}
// Explicitly instantiate all DoFieldPut functions.
@@ -479,37 +517,34 @@
ObjPtr<mirror::Object>& obj,
ArtField* field,
Primitive::Type field_type,
- size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
+ 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, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimByte:
return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimChar:
return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimShort:
return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimInt:
- return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
- case Primitive::kPrimLong:
- return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
case Primitive::kPrimFloat:
return DoFieldPutCommon<Primitive::kPrimInt, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
+ case Primitive::kPrimLong:
case Primitive::kPrimDouble:
return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimNot:
return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>(
- self, shadow_frame, obj, field, vregA);
+ self, shadow_frame, obj, field, value);
case Primitive::kPrimVoid:
LOG(FATAL) << "Unreachable: " << field_type;
UNREACHABLE();
@@ -797,7 +832,8 @@
ShadowFrame& shadow_frame,
const Instruction* inst,
uint16_t inst_data,
- JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ 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();
const int invoke_method_idx = (is_range) ? inst->VRegB_4rcc() : inst->VRegB_45cc();
@@ -960,20 +996,23 @@
Primitive::Type field_type = field->GetTypeAsPrimitiveType();;
if (!is_invoke_exact) {
- // TODO(oth): conversion plumbing for invoke().
- UNIMPLEMENTED(FATAL);
+ if (handle_type->GetPTypes()->GetLength() != callsite_type->GetPTypes()->GetLength()) {
+ // Too many arguments to setter or getter.
+ ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get());
+ return false;
+ }
}
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 kInstancePut: {
- ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
- return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[1]);
- }
case kStaticGet: {
ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
if (obj == nullptr) {
@@ -981,15 +1020,31 @@
return false;
}
DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
- return true;
- }
- case kStaticPut: {
- ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field);
- if (obj == nullptr) {
+ if (!ConvertReturnValue(callsite_type, handle_type, result)) {
DCHECK(self->IsExceptionPending());
return false;
}
- return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]);
+ 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);
+ result->SetL(0);
+ 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();
+ result->SetL(0);
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value);
}
default:
LOG(FATAL) << "Unreachable: " << handle_kind;
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index b279a37..1240792 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -31,127 +31,70 @@
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;
+inline bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ int index,
+ JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> from_class(callsite_type->GetPTypes()->GetWithoutChecks(index));
+ ObjPtr<mirror::Class> to_class(callee_type->GetPTypes()->GetWithoutChecks(index));
+ if (from_class == to_class) {
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;
+ }
+
+ // |value| may contain a bare heap pointer which is generally
+ // |unsafe. ConvertJValueCommon() saves |value|, |from_class|, and
+ // |to_class| to Handles where necessary to avoid issues if the heap
+ // changes.
+ if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) {
+ DCHECK(!Thread::Current()->IsExceptionPending());
return true;
} else {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ value->SetJ(0);
return false;
}
}
-inline bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) {
- const Primitive::Type from_type = from->GetPrimitiveType();
- const Primitive::Type to_type = to->GetPrimitiveType();
-
- // This method must be called only when the types don't match.
- DCHECK(from.Get() != to.Get());
-
- if ((from_type != Primitive::kPrimNot) && (to_type != Primitive::kPrimNot)) {
- // Throws a ClassCastException if we're unable to convert a primitive value.
- return ConvertPrimitiveValue(false, from_type, to_type, from_value, to_value);
- } 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|.
- mirror::Object* const ref = from_value.GetL();
- if (ref == nullptr || to->IsAssignableFrom(ref->GetClass())) {
- to_value->SetL(ref);
- return true;
- } else {
- ThrowClassCastException(to.Get(), ref->GetClass());
- 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));
- // 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)) {
- return false;
- }
- } else {
- *to_value = from_value;
- }
-
- // Then perform the actual boxing, and then set the reference. Note that
- // BoxPrimitive can return null if an OOM occurs.
- ObjPtr<mirror::Object> boxed = BoxPrimitive(type, from_value);
- if (boxed.Ptr() == nullptr) {
- DCHECK(Thread::Current()->IsExceptionPending());
- return false;
- }
-
- to_value->SetL(boxed.Ptr());
- return true;
- } else {
- // The target type is a primitive, we must unbox.
- ObjPtr<mirror::Object> ref(from_value.GetL());
-
- // Note that UnboxPrimitiveForResult already performs all of the type
- // conversions that we want, based on |to|.
- JValue unboxed_value;
- return UnboxPrimitiveForResult(ref, to.Get(), to_value);
- }
+inline bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ JValue* value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> from_class(callee_type->GetRType());
+ ObjPtr<mirror::Class> to_class(callsite_type->GetRType());
+ if (to_class->GetPrimitiveType() == Primitive::kPrimVoid || from_class == to_class) {
+ return true;
}
- return true;
+ // |value| may contain a bare heap pointer which is generally
+ // unsafe. ConvertJValueCommon() saves |value|, |from_class|, and
+ // |to_class| to Handles where necessary to avoid issues if the heap
+ // changes.
+ if (ConvertJValueCommon(callsite_type, callee_type, from_class, to_class, value)) {
+ DCHECK(!Thread::Current()->IsExceptionPending());
+ return true;
+ } else {
+ DCHECK(Thread::Current()->IsExceptionPending());
+ value->SetJ(0);
+ return false;
+ }
}
template <typename G, typename S>
bool PerformConversions(Thread* self,
- Handle<mirror::ObjectArray<mirror::Class>> from_types,
- Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
G* getter,
S* setter,
- int32_t num_conversions) {
+ int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<2> hs(self);
- MutableHandle<mirror::Class> from(hs.NewHandle<mirror::Class>(nullptr));
- MutableHandle<mirror::Class> to(hs.NewHandle<mirror::Class>(nullptr));
+ Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(callsite_type->GetPTypes()));
+ Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
for (int32_t i = 0; i < num_conversions; ++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();
-
- if (from.Get() == to.Get()) {
+ ObjPtr<mirror::Class> from(from_types->GetWithoutChecks(i));
+ ObjPtr<mirror::Class> to(to_types->GetWithoutChecks(i));
+ const Primitive::Type from_type = from_types->GetWithoutChecks(i)->GetPrimitiveType();
+ const Primitive::Type to_type = to_types->GetWithoutChecks(i)->GetPrimitiveType();
+ if (from == to) {
// Easy case - the types are identical. Nothing left to do except to pass
// the arguments along verbatim.
if (Primitive::Is64BitType(from_type)) {
@@ -162,28 +105,28 @@
setter->Set(getter->Get());
}
} else {
- JValue from_value;
- JValue to_value;
+ JValue value;
if (Primitive::Is64BitType(from_type)) {
- from_value.SetJ(getter->GetLong());
+ value.SetJ(getter->GetLong());
} else if (from_type == Primitive::kPrimNot) {
- from_value.SetL(getter->GetReference());
+ value.SetL(getter->GetReference());
} else {
- from_value.SetI(getter->Get());
+ value.SetI(getter->Get());
}
- if (!ConvertJValue(from, to, from_value, &to_value)) {
+ // Caveat emptor - ObjPtr's not guaranteed valid after this call.
+ if (!ConvertArgumentValue(callsite_type, callee_type, i, &value)) {
DCHECK(self->IsExceptionPending());
return false;
}
if (Primitive::Is64BitType(to_type)) {
- setter->SetLong(to_value.GetJ());
+ setter->SetLong(value.GetJ());
} else if (to_type == Primitive::kPrimNot) {
- setter->SetReference(to_value.GetL());
+ setter->SetReference(value.GetL());
} else {
- setter->Set(to_value.GetI());
+ setter->Set(value.GetI());
}
}
}
@@ -199,10 +142,10 @@
uint32_t first_src_reg,
uint32_t first_dest_reg,
const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- ShadowFrame* callee_frame) {
- 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()));
+ 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) {
@@ -214,8 +157,8 @@
ShadowFrameSetter setter(callee_frame, first_dest_reg);
return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self,
- from_types,
- to_types,
+ callsite_type,
+ callee_type,
&getter,
&setter,
num_method_params);
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
new file mode 100644
index 0000000..491d139
--- /dev/null
+++ b/runtime/method_handles.cc
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#include "method_handles.h"
+
+#include "method_handles-inl.h"
+#include "jvalue.h"
+#include "jvalue-inl.h"
+#include "reflection.h"
+#include "reflection-inl.h"
+
+namespace art {
+
+namespace {
+
+static const char* kBoxedBooleanClass = "Ljava/lang/Boolean;";
+static const char* kBoxedByteClass = "Ljava/lang/Byte;";
+static const char* kBoxedCharacterClass = "Ljava/lang/Character;";
+static const char* kBoxedDoubleClass = "Ljava/lang/Double;";
+static const char* kBoxedFloatClass = "Ljava/lang/Float;";
+static const char* kBoxedIntegerClass = "Ljava/lang/Integer;";
+static const char* kBoxedLongClass = "Ljava/lang/Long;";
+static const char* kBoxedShortClass = "Ljava/lang/Short;";
+
+// Assigns |type| to the primitive type associated with |klass|. Returns
+// true iff. |klass| was a boxed type (Integer, Long etc.), false otherwise.
+bool GetUnboxedPrimitiveType(ObjPtr<mirror::Class> klass, Primitive::Type* type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ if (klass->DescriptorEquals(kBoxedBooleanClass)) {
+ (*type) = Primitive::kPrimBoolean;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedByteClass)) {
+ (*type) = Primitive::kPrimByte;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
+ (*type) = Primitive::kPrimChar;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
+ (*type) = Primitive::kPrimFloat;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
+ (*type) = Primitive::kPrimDouble;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
+ (*type) = Primitive::kPrimInt;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedLongClass)) {
+ (*type) = Primitive::kPrimLong;
+ return true;
+ } else if (klass->DescriptorEquals(kBoxedShortClass)) {
+ (*type) = Primitive::kPrimShort;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Returns the class corresponding to the boxed type for the primitive |type|.
+ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ switch (type) {
+ case Primitive::kPrimBoolean:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedBooleanClass);
+ case Primitive::kPrimByte:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedByteClass);
+ case Primitive::kPrimChar:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedCharacterClass);
+ case Primitive::kPrimShort:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedShortClass);
+ case Primitive::kPrimInt:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedIntegerClass);
+ case Primitive::kPrimLong:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedLongClass);
+ case Primitive::kPrimFloat:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedFloatClass);
+ case Primitive::kPrimDouble:
+ return class_linker->FindSystemClass(Thread::Current(), kBoxedDoubleClass);
+ case Primitive::kPrimNot:
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable";
+ return nullptr;
+ }
+}
+
+// Returns true if |klass| is a boxed primitive type or a sub-class of a boxed primitive type.
+bool IsSubClassOfBoxedPrimitive(const Handle<mirror::Class>& klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(Thread::Current());
+ MutableHandle<mirror::Class> h_klass(hs.NewHandle(klass.Get()));
+ do {
+ Primitive::Type type;
+ if (GetUnboxedPrimitiveType(h_klass.Get(), &type)) {
+ return true;
+ }
+ h_klass.Assign(h_klass->GetSuperClass());
+ } while (h_klass.Get() != nullptr);
+ return false;
+}
+
+// Unboxed the value |o| to |unboxed_value| of type |dst_class|.
+// |unboxed_value| must be zero on entry to avoid dangling pointers.
+// Returns true on success, false if an exception is raised.
+bool UnboxPrimitiveForMethodHandles(ObjPtr<mirror::Object> o,
+ ObjPtr<mirror::Class> dst_class,
+ JValue* unboxed_value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Check unboxed_value does not contain a dangling pointer.
+ DCHECK_EQ(unboxed_value->GetJ(), 0);
+ DCHECK(dst_class->IsPrimitive());
+
+ // This is derived from UnboxPrimitive() in reflection.cc, but with
+ // exceptions appropriate to method handles.
+ if (UNLIKELY(dst_class->GetPrimitiveType() == Primitive::kPrimVoid)) {
+ ThrowClassCastException(o->GetClass(), dst_class);
+ return false;
+ }
+ if (UNLIKELY(o == nullptr)) {
+ ThrowNullPointerException(
+ StringPrintf("Expected to unbox a '%s' primitive type but was returned null",
+ dst_class->PrettyDescriptor().c_str()).c_str());
+ return false;
+ }
+
+ JValue boxed_value;
+ ObjPtr<mirror::Class> klass = o->GetClass();
+ ObjPtr<mirror::Class> src_class = nullptr;
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ ArtField* primitive_field = &klass->GetIFieldsPtr()->At(0);
+ if (klass->DescriptorEquals(kBoxedBooleanClass)) {
+ src_class = class_linker->FindPrimitiveClass('Z');
+ boxed_value.SetZ(primitive_field->GetBoolean(o));
+ } else if (klass->DescriptorEquals(kBoxedByteClass)) {
+ src_class = class_linker->FindPrimitiveClass('B');
+ boxed_value.SetB(primitive_field->GetByte(o));
+ } else if (klass->DescriptorEquals(kBoxedCharacterClass)) {
+ src_class = class_linker->FindPrimitiveClass('C');
+ boxed_value.SetC(primitive_field->GetChar(o));
+ } else if (klass->DescriptorEquals(kBoxedFloatClass)) {
+ src_class = class_linker->FindPrimitiveClass('F');
+ boxed_value.SetF(primitive_field->GetFloat(o));
+ } else if (klass->DescriptorEquals(kBoxedDoubleClass)) {
+ src_class = class_linker->FindPrimitiveClass('D');
+ boxed_value.SetD(primitive_field->GetDouble(o));
+ } else if (klass->DescriptorEquals(kBoxedIntegerClass)) {
+ src_class = class_linker->FindPrimitiveClass('I');
+ boxed_value.SetI(primitive_field->GetInt(o));
+ } else if (klass->DescriptorEquals(kBoxedLongClass)) {
+ src_class = class_linker->FindPrimitiveClass('J');
+ boxed_value.SetJ(primitive_field->GetLong(o));
+ } else if (klass->DescriptorEquals(kBoxedShortClass)) {
+ src_class = class_linker->FindPrimitiveClass('S');
+ boxed_value.SetS(primitive_field->GetShort(o));
+ } else {
+ std::string temp;
+ ThrowIllegalArgumentException(
+ StringPrintf("result has type %s, got %s",
+ dst_class->PrettyDescriptor().c_str(),
+ PrettyDescriptor(o->GetClass()->GetDescriptor(&temp)).c_str()).c_str());
+ return false;
+ }
+
+ if (!ConvertPrimitiveValueNoThrow(src_class->GetPrimitiveType(),
+ dst_class->GetPrimitiveType(),
+ boxed_value,
+ unboxed_value)) {
+ ThrowClassCastException(src_class, dst_class);
+ return false;
+ }
+ return true;
+}
+
+inline bool IsReferenceType(Primitive::Type type) {
+ return type == Primitive::kPrimNot;
+}
+
+inline bool IsPrimitiveType(Primitive::Type type) {
+ return !IsReferenceType(type);
+}
+
+} // namespace
+
+bool ConvertJValueCommon(
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ ObjPtr<mirror::Class> from,
+ ObjPtr<mirror::Class> to,
+ JValue* value) {
+ // The reader maybe concerned about the safety of the heap object
+ // that may be in |value|. There is only one case where allocation
+ // is obviously needed and that's for boxing. However, in the case
+ // of boxing |value| contains a non-reference type.
+
+ const Primitive::Type from_type = from->GetPrimitiveType();
+ const Primitive::Type to_type = to->GetPrimitiveType();
+
+ // This method must be called only when the types don't match.
+ DCHECK(from != to);
+
+ if (IsPrimitiveType(from_type) && IsPrimitiveType(to_type)) {
+ // The source and target types are both primitives.
+ if (UNLIKELY(!ConvertPrimitiveValueNoThrow(from_type, to_type, *value, value))) {
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ value->SetJ(0);
+ return false;
+ }
+ return true;
+ } else if (IsReferenceType(from_type) && IsReferenceType(to_type)) {
+ // 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|.
+ //
+ // Playing it safe with StackHandleScope here, not expecting any allocation
+ // in mirror::Class::IsAssignable().
+ StackHandleScope<2> hs(Thread::Current());
+ Handle<mirror::Class> h_to(hs.NewHandle(to));
+ Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
+
+ // |value| will now be the result value, invalidate its existing value
+ // as |h_obj| now owns it.
+ value->SetJ(0);
+
+ if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) {
+ ThrowClassCastException(h_to.Get(), h_obj->GetClass());
+ return false;
+ }
+ value->SetL(h_obj.Get());
+ return true;
+ } else if (IsReferenceType(to_type)) {
+ DCHECK(IsPrimitiveType(from_type));
+ // Playing it safe with StackHandleScope here with regards to
+ // GetUnboxedPrimitiveType() and GetBoxedPrimitiveClass().
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::Class> h_to(hs.NewHandle(to));
+ // The source type is a primitive and the target type is a reference, so we must box.
+ // The target type maybe a super class of the boxed source type, for example,
+ // if the source type is int, it's boxed type is java.lang.Integer, and the target
+ // type could be java.lang.Number.
+ Primitive::Type type;
+ if (!GetUnboxedPrimitiveType(to, &type)) {
+ ObjPtr<mirror::Class> boxed_from_class = GetBoxedPrimitiveClass(from_type);
+ if (boxed_from_class->IsSubClass(h_to.Get())) {
+ type = from_type;
+ } else {
+ value->SetJ(0);
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ return false;
+ }
+ }
+
+ if (UNLIKELY(from_type != type)) {
+ value->SetJ(0);
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ return false;
+ }
+
+ if (!ConvertPrimitiveValueNoThrow(from_type, type, *value, value)) {
+ value->SetJ(0);
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ return false;
+ }
+
+ // Then perform the actual boxing, and then set the reference.
+ ObjPtr<mirror::Object> boxed = BoxPrimitive(type, *value);
+ value->SetL(boxed.Ptr());
+ return true;
+ } else {
+ // The source type is a reference and the target type is a primitive, so we must unbox.
+ DCHECK(IsReferenceType(from_type));
+ DCHECK(IsPrimitiveType(to_type));
+
+ // Use StackHandleScope to protect |from|, |to|, and the reference
+ // in |value| from heap re-arrangements that could be triggered
+ // ahead of unboxing step.
+ StackHandleScope<3> hs(Thread::Current());
+ Handle<mirror::Class> h_to(hs.NewHandle(to));
+ Handle<mirror::Class> h_from(hs.NewHandle(from));
+ Handle<mirror::Object> h_obj(hs.NewHandle(value->GetL()));
+
+ // |value| will now be the result value, invalidate its existing value
+ // as |h_obj| now owns it.
+ value->SetJ(0);
+
+ // Check source type is a boxed primitive or has a boxed primitive super-class.
+ ObjPtr<mirror::Class> boxed_to_class = GetBoxedPrimitiveClass(to_type);
+ if (!IsSubClassOfBoxedPrimitive(h_from) && !boxed_to_class->IsSubClass(h_from.Get())) {
+ ThrowWrongMethodTypeException(callee_type.Get(), callsite_type.Get());
+ return false;
+ }
+
+ if (h_obj.Get() == nullptr) {
+ ThrowNullPointerException(
+ StringPrintf("Expected to unbox a '%s' but instance was null",
+ h_from->PrettyDescriptor().c_str()).c_str());
+ return false;
+ }
+
+ return UnboxPrimitiveForMethodHandles(h_obj.Get(), h_to.Get(), value);
+ }
+}
+
+} // namespace art
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 06509bf..0cc69f2 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -20,7 +20,10 @@
#include <ostream>
#include "dex_instruction.h"
+#include "handle.h"
#include "jvalue.h"
+#include "mirror/class.h"
+#include "mirror/method_type.h"
namespace art {
@@ -56,12 +59,36 @@
return handle_kind <= kLastInvokeKind;
}
-// Performs a single argument conversion from type |from| to a distinct
-// type |to|. Returns true on success, false otherwise.
-ALWAYS_INLINE bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) REQUIRES_SHARED(Locks::mutator_lock_);
+// Performs a conversion from type |from| to a distinct type |to| as
+// part of conversion of |caller_type| to |callee_type|. The value to
+// be converted is in |value|. Returns true on success and updates
+// |value| with the converted value, false otherwise.
+bool ConvertJValueCommon(Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ ObjPtr<mirror::Class> from,
+ ObjPtr<mirror::Class> to,
+ JValue* value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Converts the value of the argument at position |index| from type
+// expected by |callee_type| to type used by |callsite_type|. |value|
+// represents the value to be converted. Returns true on success and
+// updates |value|, false otherwise.
+ALWAYS_INLINE bool ConvertArgumentValue(Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ int index,
+ JValue* value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+// Converts the return value from return type yielded by
+// |callee_type| to the return type yielded by
+// |callsite_type|. |value| represents the value to be
+// converted. Returns true on success and updates |value|, false
+// otherwise.
+ALWAYS_INLINE bool ConvertReturnValue(Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
+ JValue* value)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Perform argument conversions between |callsite_type| (the type of the
// incoming arguments) and |callee_type| (the type of the method being
@@ -109,8 +136,8 @@
// overridden by concrete classes.
template <typename G, typename S>
bool PerformConversions(Thread* self,
- Handle<mirror::ObjectArray<mirror::Class>> from_types,
- Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
G* getter,
S* setter,
int32_t num_conversions) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
index 861d416..d607040 100644
--- a/runtime/mirror/emulated_stack_frame.cc
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -188,7 +188,7 @@
ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame);
EmulatedStackFrameAccessor setter(references, stack_frame, stack_frame->GetLength());
if (!PerformConversions<ShadowFrameGetter<is_range>, EmulatedStackFrameAccessor>(
- self, from_types, to_types, &getter, &setter, num_method_params)) {
+ self, caller_type, callee_type, &getter, &setter, num_method_params)) {
return nullptr;
}
@@ -206,9 +206,8 @@
Handle<mirror::MethodType> callee_type,
const uint32_t first_dest_reg,
ShadowFrame* callee_frame) {
- StackHandleScope<4> hs(self);
- Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(GetType()->GetPTypes()));
- Handle<mirror::ObjectArray<mirror::Class>> to_types(hs.NewHandle(callee_type->GetPTypes()));
+ ObjPtr<mirror::ObjectArray<mirror::Class>> from_types(GetType()->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) {
@@ -216,6 +215,8 @@
return false;
}
+ StackHandleScope<3> hs(self);
+ Handle<mirror::MethodType> frame_callsite_type(hs.NewHandle(GetType()));
Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
@@ -223,7 +224,7 @@
ShadowFrameSetter setter(callee_frame, first_dest_reg);
return PerformConversions<EmulatedStackFrameAccessor, ShadowFrameSetter>(
- self, from_types, to_types, &getter, &setter, num_method_params);
+ self, frame_callsite_type, callee_type, &getter, &setter, num_method_params);
}
void EmulatedStackFrame::GetReturnValue(Thread* self, JValue* value) {
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index c4d4fae..68e7a10 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -29,11 +29,10 @@
namespace art {
-inline bool ConvertPrimitiveValue(bool unbox_for_result,
- Primitive::Type srcType,
- Primitive::Type dstType,
- const JValue& src,
- JValue* dst) {
+inline bool ConvertPrimitiveValueNoThrow(Primitive::Type srcType,
+ Primitive::Type dstType,
+ const JValue& src,
+ JValue* dst) {
DCHECK(srcType != Primitive::kPrimNot && dstType != Primitive::kPrimNot);
if (LIKELY(srcType == dstType)) {
dst->SetJ(src.GetJ());
@@ -91,6 +90,18 @@
default:
break;
}
+ return false;
+}
+
+inline bool ConvertPrimitiveValue(bool unbox_for_result,
+ Primitive::Type srcType,
+ Primitive::Type dstType,
+ const JValue& src,
+ JValue* dst) {
+ if (ConvertPrimitiveValueNoThrow(srcType, dstType, src, dst)) {
+ return true;
+ }
+
if (!unbox_for_result) {
ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
PrettyDescriptor(srcType).c_str(),
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 6e5ef71..f2652fd 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -47,6 +47,12 @@
JValue* unboxed_value)
REQUIRES_SHARED(Locks::mutator_lock_);
+ALWAYS_INLINE bool ConvertPrimitiveValueNoThrow(Primitive::Type src_class,
+ Primitive::Type dst_class,
+ const JValue& src,
+ JValue* dst)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ALWAYS_INLINE bool ConvertPrimitiveValue(bool unbox_for_result,
Primitive::Type src_class,
Primitive::Type dst_class,