Merge changes Ib44df099,I8508f96f
* changes:
ART: Add GetMethodModifiers
ART: Add GetMethodDeclaringClass
diff --git a/runtime/Android.bp b/runtime/Android.bp
index b498573..8f961af 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -123,6 +123,7 @@
"mirror/array.cc",
"mirror/class.cc",
"mirror/dex_cache.cc",
+ "mirror/emulated_stack_frame.cc",
"mirror/executable.cc",
"mirror/field.cc",
"mirror/method.cc",
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 7f15a16..4823caa 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -70,6 +70,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache.h"
#include "mirror/dex_cache-inl.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
#include "mirror/method.h"
@@ -650,6 +651,11 @@
SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root);
mirror::MethodHandleImpl::SetClass(class_root);
+ class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;");
+ CHECK(class_root != nullptr);
+ SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root);
+ mirror::EmulatedStackFrame::SetClass(class_root);
+
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
@@ -1059,6 +1065,7 @@
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
+ mirror::EmulatedStackFrame::SetClass(GetClassRoot(kDalvikSystemEmulatedStackFrame));
for (gc::space::ImageSpace* image_space : spaces) {
// Boot class loader, use a null handle.
@@ -2038,6 +2045,7 @@
mirror::ShortArray::ResetArrayClass();
mirror::MethodType::ResetClass();
mirror::MethodHandleImpl::ResetClass();
+ mirror::EmulatedStackFrame::ResetClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
DeleteClassLoader(self, data);
@@ -8050,6 +8058,7 @@
"Ljava/lang/Throwable;",
"Ljava/lang/ClassNotFoundException;",
"Ljava/lang/StackTraceElement;",
+ "Ldalvik/system/EmulatedStackFrame;",
"Z",
"B",
"C",
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index e99dfe3..3248d0e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -109,6 +109,7 @@
kJavaLangThrowable,
kJavaLangClassNotFoundException,
kJavaLangStackTraceElement,
+ kDalvikSystemEmulatedStackFrame,
kPrimitiveBoolean,
kPrimitiveByte,
kPrimitiveChar,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index ab18627..5878bf3 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -31,6 +31,7 @@
#include "mirror/accessible_object.h"
#include "mirror/class-inl.h"
#include "mirror/dex_cache.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/executable.h"
#include "mirror/field.h"
#include "mirror/method_type.h"
@@ -740,6 +741,15 @@
}
};
+struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> {
+ EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>(
+ false, "Ldalvik/system/EmulatedStackFrame;") {
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, references_), "references");
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, stack_frame_), "stackFrame");
+ addOffset(OFFSETOF_MEMBER(mirror::EmulatedStackFrame, type_), "type");
+ }
+};
+
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
// ClassLinker::LinkFields.
@@ -760,6 +770,7 @@
EXPECT_TRUE(ExecutableOffsets().Check());
EXPECT_TRUE(MethodTypeOffsets().Check());
EXPECT_TRUE(MethodHandleImplOffsets().Check());
+ EXPECT_TRUE(EmulatedStackFrameOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 1ed3d55..179e48b 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -27,6 +27,7 @@
#include "method_handles-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/method_handle_impl.h"
#include "reflection.h"
#include "reflection-inl.h"
@@ -42,6 +43,59 @@
ThrowNullPointerExceptionFromDexPC();
}
+template<Primitive::Type field_type>
+static ALWAYS_INLINE void DoFieldGetCommon(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
+ field->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
+
+ // Report this field access to instrumentation if needed.
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+ StackHandleScope<1> hs(self);
+ // Wrap in handle wrapper in case the listener does thread suspension.
+ HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
+ ObjPtr<mirror::Object> this_object;
+ if (!field->IsStatic()) {
+ this_object = obj;
+ }
+ instrumentation->FieldReadEvent(self,
+ this_object.Ptr(),
+ shadow_frame.GetMethod(),
+ shadow_frame.GetDexPC(),
+ field);
+ }
+
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ result->SetZ(field->GetBoolean(obj));
+ break;
+ case Primitive::kPrimByte:
+ result->SetB(field->GetByte(obj));
+ break;
+ case Primitive::kPrimChar:
+ result->SetC(field->GetChar(obj));
+ break;
+ case Primitive::kPrimShort:
+ result->SetS(field->GetShort(obj));
+ break;
+ case Primitive::kPrimInt:
+ result->SetI(field->GetInt(obj));
+ break;
+ case Primitive::kPrimLong:
+ result->SetJ(field->GetLong(obj));
+ break;
+ case Primitive::kPrimNot:
+ result->SetL(field->GetObject(obj));
+ break;
+ default:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check>
bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
uint16_t inst_data) {
@@ -64,45 +118,31 @@
return false;
}
}
- f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
- // Report this field access to instrumentation if needed.
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
- StackHandleScope<1> hs(self);
- // Wrap in handle wrapper in case the listener does thread suspension.
- HandleWrapperObjPtr<mirror::Object> h(hs.NewHandleWrapper(&obj));
- ObjPtr<mirror::Object> this_object;
- if (!f->IsStatic()) {
- this_object = obj;
- }
- instrumentation->FieldReadEvent(self,
- this_object.Ptr(),
- shadow_frame.GetMethod(),
- shadow_frame.GetDexPC(),
- f);
- }
+
+ JValue result;
+ DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result);
uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
switch (field_type) {
case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, f->GetBoolean(obj));
+ shadow_frame.SetVReg(vregA, result.GetZ());
break;
case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, f->GetByte(obj));
+ shadow_frame.SetVReg(vregA, result.GetB());
break;
case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, f->GetChar(obj));
+ shadow_frame.SetVReg(vregA, result.GetC());
break;
case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, f->GetShort(obj));
+ shadow_frame.SetVReg(vregA, result.GetS());
break;
case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, f->GetInt(obj));
+ shadow_frame.SetVReg(vregA, result.GetI());
break;
case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, f->GetLong(obj));
+ shadow_frame.SetVRegLong(vregA, result.GetJ());
break;
case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, f->GetObject(obj).Ptr());
+ shadow_frame.SetVRegReference(vregA, result.GetL());
break;
default:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -143,6 +183,48 @@
#undef EXPLICIT_DO_FIELD_GET_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_GET_TEMPLATE_DECL
+// Helper for getters in invoke-polymorphic.
+inline static void DoFieldGetForInvokePolymorphic(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ JValue* result)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (field_type) {
+ case Primitive::kPrimBoolean:
+ DoFieldGetCommon<Primitive::kPrimBoolean>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimByte:
+ DoFieldGetCommon<Primitive::kPrimByte>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimChar:
+ DoFieldGetCommon<Primitive::kPrimChar>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimShort:
+ DoFieldGetCommon<Primitive::kPrimShort>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimInt:
+ DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimLong:
+ DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimFloat:
+ DoFieldGetCommon<Primitive::kPrimInt>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimDouble:
+ DoFieldGetCommon<Primitive::kPrimLong>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimNot:
+ DoFieldGetCommon<Primitive::kPrimNot>(self, shadow_frame, obj, field, result);
+ break;
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
// Handles iget-quick, iget-wide-quick and iget-object-quick instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<Primitive::Type field_type>
@@ -250,32 +332,14 @@
return field_value;
}
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
- bool transaction_active>
-bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst,
- uint16_t inst_data) {
- bool do_assignability_check = do_access_check;
- bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtField* f =
- FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
- Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
- return false;
- }
- ObjPtr<mirror::Object> obj;
- if (is_static) {
- obj = f->GetDeclaringClass();
- } else {
- obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, false);
- return false;
- }
- }
+template<Primitive::Type field_type, bool do_assignability_check, bool transaction_active>
+static inline bool DoFieldPutCommon(Thread* self,
+ const ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* f,
+ size_t vregA) REQUIRES_SHARED(Locks::mutator_lock_) {
f->GetDeclaringClass()->AssertInitializedOrInitializingInThread(self);
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+
// Report this field access to instrumentation if needed. Since we only have the offset of
// the field from the base of the object, we need to look for it first.
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
@@ -291,6 +355,7 @@
f,
field_value);
}
+
switch (field_type) {
case Primitive::kPrimBoolean:
f->SetBoolean<transaction_active>(obj, shadow_frame.GetVReg(vregA));
@@ -343,6 +408,39 @@
return true;
}
+template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+ bool transaction_active>
+bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame, const Instruction* inst,
+ uint16_t inst_data) {
+ const bool do_assignability_check = do_access_check;
+ bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
+ uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
+ ArtField* f =
+ FindFieldFromCode<find_type, do_access_check>(field_idx, shadow_frame.GetMethod(), self,
+ Primitive::ComponentSize(field_type));
+ if (UNLIKELY(f == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return false;
+ }
+ ObjPtr<mirror::Object> obj;
+ if (is_static) {
+ obj = f->GetDeclaringClass();
+ } else {
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(f, false);
+ return false;
+ }
+ }
+
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
+ shadow_frame,
+ obj,
+ f,
+ vregA);
+}
+
// Explicitly instantiate all DoFieldPut functions.
#define EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL(_find_type, _field_type, _do_check, _transaction_active) \
template bool DoFieldPut<_find_type, _field_type, _do_check, _transaction_active>(Thread* self, \
@@ -375,6 +473,49 @@
#undef EXPLICIT_DO_FIELD_PUT_ALL_TEMPLATE_DECL
#undef EXPLICIT_DO_FIELD_PUT_TEMPLATE_DECL
+// Helper for setters in invoke-polymorphic.
+bool DoFieldPutForInvokePolymorphic(Thread* self,
+ ShadowFrame& shadow_frame,
+ ObjPtr<mirror::Object>& obj,
+ ArtField* field,
+ Primitive::Type field_type,
+ size_t vregA) 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);
+ case Primitive::kPrimByte:
+ return DoFieldPutCommon<Primitive::kPrimByte, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimChar:
+ return DoFieldPutCommon<Primitive::kPrimChar, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimShort:
+ return DoFieldPutCommon<Primitive::kPrimShort, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ 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);
+ case Primitive::kPrimDouble:
+ return DoFieldPutCommon<Primitive::kPrimLong, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimNot:
+ return DoFieldPutCommon<Primitive::kPrimNot, kDoCheckAssignability, kTransaction>(
+ self, shadow_frame, obj, field, vregA);
+ case Primitive::kPrimVoid:
+ LOG(FATAL) << "Unreachable: " << field_type;
+ UNREACHABLE();
+ }
+}
+
template<Primitive::Type field_type, bool transaction_active>
bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data) {
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
@@ -529,15 +670,19 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) ALWAYS_INLINE;
+ uint32_t vregC,
+ const MethodHandleKind handle_kind) ALWAYS_INLINE;
-REQUIRES_SHARED(Locks::mutator_lock_)
+template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallTransform(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
Handle<mirror::MethodHandleImpl> receiver,
- JValue* result) ALWAYS_INLINE;
+ JValue* result,
+ uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ uint32_t vregC) ALWAYS_INLINE;
REQUIRES_SHARED(Locks::mutator_lock_)
inline void PerformCall(Thread* self,
@@ -620,6 +765,17 @@
}
}
+inline static bool IsInvokeExact(const DexFile& dex_file, int invoke_method_idx) {
+ // This check uses string comparison as it needs less code and data
+ // to do than fetching the associated ArtMethod from the DexCache
+ // and checking against ArtMethods in the well known classes. The
+ // verifier needs to perform a more rigorous check.
+ const char* method_name = dex_file.GetMethodName(dex_file.GetMethodId(invoke_method_idx));
+ bool is_invoke_exact = (0 == strcmp(method_name, "invokeExact"));
+ DCHECK(is_invoke_exact || (0 == strcmp(method_name, "invoke")));
+ return is_invoke_exact;
+}
+
template<bool is_range, bool do_access_check>
inline bool DoInvokePolymorphic(Thread* self,
ShadowFrame& shadow_frame,
@@ -628,8 +784,14 @@
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();
- // The method_idx here is the name of the signature polymorphic method that
+ // Determine if this invocation is MethodHandle.invoke() or
+ // MethodHandle.invokeExact().
+ bool is_invoke_exact = IsInvokeExact(shadow_frame.GetMethod()->GetDeclaringClass()->GetDexFile(),
+ invoke_method_idx);
+
+ // The invoke_method_idx here is the name of the signature polymorphic method that
// was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact)
// and not the method that we'll dispatch to in the end.
//
@@ -642,11 +804,9 @@
ObjPtr<mirror::MethodHandleImpl>::DownCast(
MakeObjPtr(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.
- ThrowNullPointerExceptionForMethodAccess(method_idx, InvokeType::kVirtual);
-
+ ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual);
result->SetJ(0);
return false;
}
@@ -672,20 +832,18 @@
return false;
}
- // 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();
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
- CHECK(called_method != nullptr);
CHECK(handle_type.Get() != nullptr);
+ if (UNLIKELY(is_invoke_exact && !callsite_type->IsExactMatch(handle_type.Get()))) {
+ ThrowWrongMethodTypeException(callsite_type.Get(), handle_type.Get());
+ return false;
+ }
uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- uint32_t receiver_vregC = 0;
+ uint32_t first_src_reg = 0;
if (is_range) {
- receiver_vregC = (inst->VRegC_4rcc() + 1);
+ first_src_reg = (inst->VRegC_4rcc() + 1);
} else {
inst->GetVarArgs(arg, inst_data);
arg[0] = arg[1];
@@ -693,20 +851,22 @@
arg[2] = arg[3];
arg[3] = arg[4];
arg[4] = 0;
- receiver_vregC = arg[0];
+ first_src_reg = arg[0];
}
if (IsInvoke(handle_kind)) {
+ // Get the method we're actually invoking along with the kind of
+ // invoke that is desired. We don't need to perform access checks at this
+ // point because they would have been performed on our behalf at the point
+ // of creation of the method handle.
+ ArtMethod* called_method = method_handle->GetTargetMethod();
+ CHECK(called_method != nullptr);
+
if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) {
- ObjPtr<mirror::Object> receiver = shadow_frame.GetVRegReference(receiver_vregC);
- ObjPtr<mirror::Class> declaring_class = called_method->GetDeclaringClass();
- // Verify that _vRegC is an object reference and of the type expected by
- // the receiver.
- called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
- called_method, kRuntimePointerSize);
- if (!VerifyObjectIsClass(receiver, declaring_class)) {
- return false;
- }
+ // 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 actualy invoke.
} else if (handle_kind == kInvokeDirect) {
if (called_method->IsConstructor()) {
// TODO(narayan) : We need to handle the case where the target method is a
@@ -744,12 +904,15 @@
}
if (handle_kind == kInvokeTransform) {
- return DoCallTransform(called_method,
- callsite_type,
- self,
- shadow_frame,
- method_handle /* receiver */,
- result);
+ return DoCallTransform<is_range>(called_method,
+ callsite_type,
+ handle_type,
+ self,
+ shadow_frame,
+ method_handle /* receiver */,
+ result,
+ arg,
+ first_src_reg);
} else {
return DoCallPolymorphic<is_range>(called_method,
callsite_type,
@@ -758,12 +921,42 @@
shadow_frame,
result,
arg,
- receiver_vregC);
+ first_src_reg,
+ handle_kind);
}
} else {
- // TODO(narayan): Implement field getters and setters.
- UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet.";
- return false;
+ DCHECK(!is_range);
+ ArtField* field = method_handle->GetTargetField();
+ Primitive::Type field_type = field->GetTypeAsPrimitiveType();;
+
+ if (!is_invoke_exact) {
+ // TODO(oth): conversion plumbing for invoke().
+ UNIMPLEMENTED(FATAL);
+ }
+
+ switch (handle_kind) {
+ case kInstanceGet: {
+ ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(first_src_reg);
+ DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ 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 = field->GetDeclaringClass();
+ DoFieldGetForInvokePolymorphic(self, shadow_frame, obj, field, field_type, result);
+ return true;
+ }
+ case kStaticPut: {
+ ObjPtr<mirror::Object> obj = field->GetDeclaringClass();
+ return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, arg[0]);
+ }
+ default:
+ LOG(FATAL) << "Unreachable: " << handle_kind;
+ UNREACHABLE();
+ }
}
}
@@ -860,7 +1053,8 @@
ShadowFrame& shadow_frame,
JValue* result,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t first_src_reg) {
+ uint32_t first_src_reg,
+ const MethodHandleKind handle_kind) {
// TODO(narayan): Wire in the String.init hacks.
// Compute method information.
@@ -893,6 +1087,8 @@
CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
+ // Whether this polymorphic invoke was issued by a transformer method.
+ bool is_caller_transformer = false;
// Thread might be suspended during PerformArgumentConversions due to the
// allocations performed during boxing.
{
@@ -914,18 +1110,16 @@
// case, we'll have to unmarshal the EmulatedStackFrame into the
// new_shadow_frame and perform argument conversions on it.
if (IsCallerTransformer(callsite_type)) {
- // The emulated stack frame will be the first ahnd only argument
- // when we're coming through from a transformer.
- //
- // TODO(narayan): This should be a mirror::EmulatedStackFrame after that
- // type is introduced.
- ObjPtr<mirror::Object> emulated_stack_frame(
- shadow_frame.GetVRegReference(first_src_reg));
- if (!ConvertAndCopyArgumentsFromEmulatedStackFrame<is_range>(self,
- emulated_stack_frame,
- target_type,
- first_dest_reg,
- new_shadow_frame)) {
+ is_caller_transformer = true;
+ // The emulated stack frame is the first and only argument when we're coming
+ // through from a transformer.
+ ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
+ reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+ if (!emulated_stack_frame->WriteToShadowFrame(self,
+ target_type,
+ first_dest_reg,
+ new_shadow_frame)) {
DCHECK(self->IsExceptionPending());
result->SetL(0);
return false;
@@ -945,20 +1139,51 @@
}
}
+ // See TODO in DoInvokePolymorphic : We need to perform this dynamic, receiver
+ // based dispatch right before we perform the actual call, because the
+ // receiver isn't known very early.
+ if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) {
+ ObjPtr<mirror::Object> receiver(new_shadow_frame->GetVRegReference(first_dest_reg));
+ ObjPtr<mirror::Class> declaring_class(called_method->GetDeclaringClass());
+ // Verify that _vRegC is an object reference and of the type expected by
+ // the receiver.
+ if (!VerifyObjectIsClass(receiver, declaring_class)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+
+ called_method = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(
+ called_method, kRuntimePointerSize);
+ }
+
PerformCall(self, code_item, shadow_frame.GetMethod(), first_dest_reg, new_shadow_frame, result);
// TODO(narayan): Perform return value conversions.
+ // If the caller of this signature polymorphic method was a transformer,
+ // we need to copy the result back out to the emulated stack frame.
+ if (is_caller_transformer && !self->IsExceptionPending()) {
+ ObjPtr<mirror::EmulatedStackFrame> emulated_stack_frame(
+ reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+
+ emulated_stack_frame->SetReturnValue(self, *result);
+ }
+
return !self->IsExceptionPending();
}
+template <bool is_range>
static inline bool DoCallTransform(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
+ Handle<mirror::MethodType> callee_type,
Thread* self,
ShadowFrame& shadow_frame,
Handle<mirror::MethodHandleImpl> receiver,
- JValue* result) {
- // This can be fixed, because the method we're calling here
+ JValue* result,
+ uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ uint32_t first_src_reg) {
+ // This can be fixed to two, because the method we're calling here
// (MethodHandle.transformInternal) doesn't have any locals and the signature
// is known :
//
@@ -978,18 +1203,33 @@
CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
- // TODO(narayan): Perform argument conversions first (if this is an inexact invoke), and
- // then construct an argument list object that's passed through to the
- // method. Note that the ArgumentList reference is currently a nullptr.
- //
- // NOTE(narayan): If the caller is a transformer method (i.e, there is only
- // one argument and its type is EmulatedStackFrame), we can directly pass that
- // through without having to do any additional work.
- UNUSED(callsite_type);
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::EmulatedStackFrame> sf(hs.NewHandle<mirror::EmulatedStackFrame>(nullptr));
+ if (IsCallerTransformer(callsite_type)) {
+ // If we're entering this transformer from another transformer, we can pass
+ // through the handle directly to the callee, instead of having to
+ // instantiate a new stack frame based on the shadow frame.
+ sf.Assign(reinterpret_cast<mirror::EmulatedStackFrame*>(
+ shadow_frame.GetVRegReference(first_src_reg)));
+ } else {
+ sf.Assign(mirror::EmulatedStackFrame::CreateFromShadowFrameAndArgs<is_range>(
+ self,
+ callsite_type,
+ callee_type,
+ shadow_frame,
+ first_src_reg,
+ arg));
+
+ // Something went wrong while creating the emulated stack frame, we should
+ // throw the pending exception.
+ if (sf.Get() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ }
new_shadow_frame->SetVRegReference(0, receiver.Get());
- // TODO(narayan): This is the EmulatedStackFrame, currently nullptr.
- new_shadow_frame->SetVRegReference(1, nullptr);
+ new_shadow_frame->SetVRegReference(1, sf.Get());
PerformCall(self,
code_item,
@@ -998,6 +1238,12 @@
new_shadow_frame,
result);
+ // If the called transformer method we called has returned a value, then we
+ // need to copy it back to |result|.
+ if (!self->IsExceptionPending()) {
+ sf->GetReturnValue(self, result);
+ }
+
return !self->IsExceptionPending();
}
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 243ed57..435ac62 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1571,7 +1571,6 @@
self, shadow_frame, inst, inst_data, &result_register);
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
break;
- break;
}
case Instruction::NEG_INT:
PREAMBLE();
diff --git a/runtime/method_handles-inl.h b/runtime/method_handles-inl.h
index b488133..ff5d2a1 100644
--- a/runtime/method_handles-inl.h
+++ b/runtime/method_handles-inl.h
@@ -64,44 +64,11 @@
}
}
-// 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_;
-};
-
REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) {
+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();
@@ -161,6 +128,66 @@
return true;
}
+template <typename G, typename S>
+bool PerformConversions(Thread* self,
+ Handle<mirror::ObjectArray<mirror::Class>> from_types,
+ Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ G* getter,
+ S* setter,
+ int32_t num_conversions) {
+ StackHandleScope<2> hs(self);
+ 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_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()) {
+ // Easy case - the types are identical. Nothing left to do except to pass
+ // the arguments along verbatim.
+ if (Primitive::Is64BitType(from_type)) {
+ setter->SetLong(getter->GetLong());
+ } else if (from_type == Primitive::kPrimNot) {
+ setter->SetReference(getter->GetReference());
+ } else {
+ setter->Set(getter->Get());
+ }
+
+ continue;
+ } else {
+ JValue from_value;
+ JValue to_value;
+
+ if (Primitive::Is64BitType(from_type)) {
+ from_value.SetJ(getter->GetLong());
+ } else if (from_type == Primitive::kPrimNot) {
+ from_value.SetL(getter->GetReference());
+ } else {
+ from_value.SetI(getter->Get());
+ }
+
+ if (!ConvertJValue(from, to, from_value, &to_value)) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+
+ if (Primitive::Is64BitType(to_type)) {
+ setter->SetLong(to_value.GetJ());
+ } else if (to_type == Primitive::kPrimNot) {
+ setter->SetReference(to_value.GetL());
+ } else {
+ setter->Set(to_value.GetI());
+ }
+ }
+ }
+
+ return true;
+}
+
template <bool is_range>
bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
Handle<mirror::MethodType> callsite_type,
@@ -180,89 +207,17 @@
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));
+ ShadowFrameGetter<is_range> getter(first_src_reg, arg, caller_frame);
+ ShadowFrameSetter setter(callee_frame, first_dest_reg);
- 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 {
- JValue from_value;
- JValue to_value;
-
- if (Primitive::Is64BitType(from_type)) {
- from_value.SetJ(caller_frame.GetVRegLong(input_args.NextPair()));
- } else if (from_type == Primitive::kPrimNot) {
- from_value.SetL(caller_frame.GetVRegReference(input_args.Next()));
- } else {
- from_value.SetI(caller_frame.GetVReg(input_args.Next()));
- }
-
- if (!ConvertJValue(from, to, from_value, &to_value)) {
- DCHECK(self->IsExceptionPending());
- return false;
- }
-
- if (Primitive::Is64BitType(to_type)) {
- callee_frame->SetVRegLong(first_dest_reg + to_arg_index, to_value.GetJ());
- to_arg_index += 2;
- } else if (to_type == Primitive::kPrimNot) {
- callee_frame->SetVRegReference(first_dest_reg + to_arg_index, to_value.GetL());
- ++to_arg_index;
- } else {
- callee_frame->SetVReg(first_dest_reg + to_arg_index, to_value.GetI());
- ++to_arg_index;
- }
- }
- }
-
- return true;
+ return PerformConversions<ShadowFrameGetter<is_range>, ShadowFrameSetter>(self,
+ from_types,
+ to_types,
+ &getter,
+ &setter,
+ num_method_params);
}
-// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
-// arguments are copied from an |EmulatedStackFrame|.
-template <bool is_range>
-bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
- ObjPtr<mirror::Object> emulated_stack_frame,
- Handle<mirror::MethodType> callee_type,
- const uint32_t first_dest_reg,
- ShadowFrame* callee_frame) {
- UNUSED(self);
- UNUSED(emulated_stack_frame);
- UNUSED(callee_type);
- UNUSED(first_dest_reg);
- UNUSED(callee_frame);
-
- UNIMPLEMENTED(FATAL) << "ConvertAndCopyArgumentsFromEmulatedStackFrame is unimplemented";
- return false;
-}
-
-
} // namespace art
#endif // ART_RUNTIME_METHOD_HANDLES_INL_H_
diff --git a/runtime/method_handles.h b/runtime/method_handles.h
index 5175dce..0d3f9f1 100644
--- a/runtime/method_handles.h
+++ b/runtime/method_handles.h
@@ -59,16 +59,66 @@
// Performs a single argument conversion from type |from| to a distinct
// type |to|. Returns true on success, false otherwise.
REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertJValue(Handle<mirror::Class> from,
- Handle<mirror::Class> to,
- const JValue& from_value,
- JValue* to_value) ALWAYS_INLINE;
+inline bool ConvertJValue(Handle<mirror::Class> from,
+ Handle<mirror::Class> to,
+ const JValue& from_value,
+ JValue* to_value) ALWAYS_INLINE;
// 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.
+//
+// The values to be converted are read from an input source (of type G)
+// that provides three methods :
+//
+// class G {
+// // Used to read the next boolean/short/int or float value from the
+// // source.
+// uint32_t Get();
+//
+// // Used to the read the next reference value from the source.
+// ObjPtr<mirror::Object> GetReference();
+//
+// // Used to read the next double or long value from the source.
+// int64_t GetLong();
+// }
+//
+// After conversion, the values are written to an output sink (of type S)
+// that provides three methods :
+//
+// class S {
+// void Set(uint32_t);
+// void SetReference(ObjPtr<mirror::Object>)
+// void SetLong(int64_t);
+// }
+//
+// The semantics and usage of the Set methods are analagous to the getter
+// class.
+//
+// This method is instantiated in three different scenarions :
+// - <S = ShadowFrameSetter, G = ShadowFrameGetter> : copying from shadow
+// frame to shadow frame, used in a regular polymorphic non-exact invoke.
+// - <S = EmulatedShadowFrameAccessor, G = ShadowFrameGetter> : entering into
+// a transformer method from a polymorphic invoke.
+// - <S = ShadowFrameStter, G = EmulatedStackFrameAccessor> : entering into
+// a regular poly morphic invoke from a transformer method.
+//
+// TODO(narayan): If we find that the instantiations of this function take
+// up too much space, we can make G / S abstract base classes that are
+// overridden by concrete classes.
+template <typename G, typename S>
+REQUIRES_SHARED(Locks::mutator_lock_)
+bool PerformConversions(Thread* self,
+ Handle<mirror::ObjectArray<mirror::Class>> from_types,
+ Handle<mirror::ObjectArray<mirror::Class>> to_types,
+ G* getter,
+ S* setter,
+ int32_t num_conversions);
+
+// A convenience wrapper around |PerformConversions|, for the case where
+// the setter and getter are both ShadowFrame based.
template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
bool ConvertAndCopyArgumentsFromCallerFrame(Thread* self,
Handle<mirror::MethodType> callsite_type,
@@ -79,15 +129,80 @@
const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
ShadowFrame* callee_frame);
-// Similar to |ConvertAndCopyArgumentsFromCallerFrame|, except that the
-// arguments are copied from an |EmulatedStackFrame|.
-template <bool is_range> REQUIRES_SHARED(Locks::mutator_lock_)
-bool ConvertAndCopyArgumentsFromEmulatedStackFrame(Thread* self,
- ObjPtr<mirror::Object> emulated_stack_frame,
- Handle<mirror::MethodType> callee_type,
- const uint32_t first_dest_reg,
- ShadowFrame* callee_frame);
+// 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.
+//
+// This is used to iterate over input arguments while performing standard
+// argument conversions.
+template <bool is_range> class ShadowFrameGetter {
+ public:
+ ShadowFrameGetter(size_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs],
+ const ShadowFrame& shadow_frame) :
+ first_src_reg_(first_src_reg),
+ arg_(arg),
+ shadow_frame_(shadow_frame),
+ arg_index_(0) {
+ }
+ ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ ++arg_index_;
+
+ return shadow_frame_.GetVReg(next);
+ }
+
+ ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ arg_index_ += 2;
+
+ return shadow_frame_.GetVRegLong(next);
+ }
+
+ ALWAYS_INLINE ObjPtr<mirror::Object> GetReference() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const uint32_t next = (is_range ? first_src_reg_ + arg_index_ : arg_[arg_index_]);
+ ++arg_index_;
+
+ return shadow_frame_.GetVRegReference(next);
+ }
+
+ private:
+ const size_t first_src_reg_;
+ const uint32_t (&arg_)[Instruction::kMaxVarArgRegs];
+ const ShadowFrame& shadow_frame_;
+ size_t arg_index_;
+};
+
+// A convenience class that allows values to be written to a given shadow frame,
+// starting at location |first_dst_reg|.
+class ShadowFrameSetter {
+ public:
+ ShadowFrameSetter(ShadowFrame* shadow_frame,
+ size_t first_dst_reg) :
+ shadow_frame_(shadow_frame),
+ arg_index_(first_dst_reg) {
+ }
+
+ ALWAYS_INLINE void Set(uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVReg(arg_index_++, value);
+ }
+
+ ALWAYS_INLINE void SetReference(ObjPtr<mirror::Object> value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVRegReference(arg_index_++, value.Ptr());
+ }
+
+ ALWAYS_INLINE void SetLong(int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ shadow_frame_->SetVRegLong(arg_index_, value);
+ arg_index_ += 2;
+ }
+
+ private:
+ ShadowFrame* shadow_frame_;
+ size_t arg_index_;
+};
} // namespace art
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index f5d1b80..1ae694d 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -74,6 +74,7 @@
static GcRoot<T> Lookup(std::atomic<DexCachePair<T>>* dex_cache,
uint32_t idx,
uint32_t cache_size) {
+ DCHECK_NE(cache_size, 0u);
DexCachePair<T> element = dex_cache[idx % cache_size].load(std::memory_order_relaxed);
if (idx != element.index) {
return GcRoot<T>(nullptr);
diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc
new file mode 100644
index 0000000..4ba71ea
--- /dev/null
+++ b/runtime/mirror/emulated_stack_frame.cc
@@ -0,0 +1,298 @@
+/*
+ * 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 "emulated_stack_frame.h"
+
+#include "class-inl.h"
+#include "gc_root-inl.h"
+#include "jvalue-inl.h"
+#include "method_handles.h"
+#include "method_handles-inl.h"
+#include "reflection-inl.h"
+
+namespace art {
+namespace mirror {
+
+GcRoot<mirror::Class> EmulatedStackFrame::static_class_;
+
+// Calculates the size of a stack frame based on the size of its argument
+// types and return types.
+static void CalculateFrameAndReferencesSize(ObjPtr<mirror::ObjectArray<mirror::Class>> p_types,
+ ObjPtr<mirror::Class> r_type,
+ size_t* frame_size_out,
+ size_t* references_size_out)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const size_t length = p_types->GetLength();
+ size_t frame_size = 0;
+ size_t references_size = 0;
+ for (size_t i = 0; i < length; ++i) {
+ ObjPtr<mirror::Class> type = p_types->GetWithoutChecks(i);
+ const Primitive::Type primitive_type = type->GetPrimitiveType();
+ if (primitive_type == Primitive::kPrimNot) {
+ references_size++;
+ } else if (Primitive::Is64BitType(primitive_type)) {
+ frame_size += 8;
+ } else {
+ frame_size += 4;
+ }
+ }
+
+ const Primitive::Type return_type = r_type->GetPrimitiveType();
+ if (return_type == Primitive::kPrimNot) {
+ references_size++;
+ } else if (Primitive::Is64BitType(return_type)) {
+ frame_size += 8;
+ } else {
+ frame_size += 4;
+ }
+
+ (*frame_size_out) = frame_size;
+ (*references_size_out) = references_size;
+}
+
+// Allows for read or write access to an emulated stack frame. Each
+// accessor index has an associated index into the references / stack frame
+// arrays which is incremented on every read or write to the frame.
+//
+// This class is used in conjunction with PerformConversions, either as a setter
+// or as a getter.
+class EmulatedStackFrameAccessor {
+ public:
+ EmulatedStackFrameAccessor(Handle<mirror::ObjectArray<mirror::Object>> references,
+ Handle<mirror::ByteArray> stack_frame,
+ size_t stack_frame_size) :
+ references_(references),
+ stack_frame_(stack_frame),
+ stack_frame_size_(stack_frame_size),
+ reference_idx_(0u),
+ stack_frame_idx_(0u) {
+ }
+
+ ALWAYS_INLINE void SetReference(ObjPtr<mirror::Object> reference)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ references_->Set(reference_idx_++, reference);
+ }
+
+ ALWAYS_INLINE void Set(const uint32_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 4u), stack_frame_size_);
+ memcpy(array + stack_frame_idx_, &value, sizeof(uint32_t));
+ stack_frame_idx_ += 4u;
+ }
+
+ ALWAYS_INLINE void SetLong(const int64_t value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 8u), stack_frame_size_);
+ memcpy(array + stack_frame_idx_, &value, sizeof(int64_t));
+ stack_frame_idx_ += 8u;
+ }
+
+ ALWAYS_INLINE ObjPtr<mirror::Object> GetReference() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjPtr<mirror::Object>(references_->Get(reference_idx_++));
+ }
+
+ ALWAYS_INLINE uint32_t Get() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 4u), stack_frame_size_);
+ uint32_t val = 0;
+
+ memcpy(&val, array + stack_frame_idx_, sizeof(uint32_t));
+ stack_frame_idx_ += 4u;
+ return val;
+ }
+
+ ALWAYS_INLINE int64_t GetLong() REQUIRES_SHARED(Locks::mutator_lock_) {
+ const int8_t* array = stack_frame_->GetData();
+
+ CHECK_LE((stack_frame_idx_ + 8u), stack_frame_size_);
+ int64_t val = 0;
+
+ memcpy(&val, array + stack_frame_idx_, sizeof(int64_t));
+ stack_frame_idx_ += 8u;
+ return val;
+ }
+
+ private:
+ Handle<mirror::ObjectArray<mirror::Object>> references_;
+ Handle<mirror::ByteArray> stack_frame_;
+ const size_t stack_frame_size_;
+
+ size_t reference_idx_;
+ size_t stack_frame_idx_;
+
+ DISALLOW_COPY_AND_ASSIGN(EmulatedStackFrameAccessor);
+};
+
+template <bool is_range>
+mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs(
+ Thread* self,
+ Handle<mirror::MethodType> caller_type,
+ Handle<mirror::MethodType> callee_type,
+ const ShadowFrame& caller_frame,
+ const uint32_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) {
+ StackHandleScope<6> hs(self);
+
+ // Step 1: We must throw a WrongMethodTypeException if there's a mismatch in the
+ // number of arguments between the caller and the callsite.
+ Handle<mirror::ObjectArray<mirror::Class>> from_types(hs.NewHandle(caller_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(), caller_type.Get());
+ return nullptr;
+ }
+
+ // Step 2: Calculate the size of the reference / byte arrays in the emulated
+ // stack frame.
+ size_t frame_size = 0;
+ size_t refs_size = 0;
+ Handle<mirror::Class> r_type(hs.NewHandle(callee_type->GetRType()));
+ CalculateFrameAndReferencesSize(to_types.Get(), r_type.Get(), &frame_size, &refs_size);
+
+ // Step 3 : Allocate the arrays.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::Class> array_class(class_linker->GetClassRoot(ClassLinker::kObjectArrayClass));
+
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(self, array_class, refs_size)));
+ Handle<ByteArray> stack_frame(hs.NewHandle(ByteArray::Alloc(self, frame_size)));
+
+ // Step 4 : Perform argument conversions (if required).
+ 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)) {
+ return nullptr;
+ }
+
+ // Step 5: Construct the EmulatedStackFrame object.
+ Handle<EmulatedStackFrame> sf(hs.NewHandle(
+ ObjPtr<EmulatedStackFrame>::DownCast(StaticClass()->AllocObject(self))));
+ sf->SetFieldObject<false>(TypeOffset(), callee_type.Get());
+ sf->SetFieldObject<false>(ReferencesOffset(), references.Get());
+ sf->SetFieldObject<false>(StackFrameOffset(), stack_frame.Get());
+
+ return sf.Get();
+}
+
+bool EmulatedStackFrame::WriteToShadowFrame(Thread* self,
+ 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()));
+
+ const int32_t num_method_params = from_types->GetLength();
+ if (to_types->GetLength() != num_method_params) {
+ ThrowWrongMethodTypeException(callee_type.Get(), GetType());
+ return false;
+ }
+
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+
+ EmulatedStackFrameAccessor getter(references, stack_frame, stack_frame->GetLength());
+ ShadowFrameSetter setter(callee_frame, first_dest_reg);
+
+ return PerformConversions<EmulatedStackFrameAccessor, ShadowFrameSetter>(
+ self, from_types, to_types, &getter, &setter, num_method_params);
+}
+
+void EmulatedStackFrame::GetReturnValue(Thread* self, JValue* value) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> r_type(hs.NewHandle(GetType()->GetRType()));
+
+ const Primitive::Type type = r_type->GetPrimitiveType();
+ if (type == Primitive::kPrimNot) {
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ value->SetL(references->GetWithoutChecks(references->GetLength() - 1));
+ } else {
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+ const int8_t* array = stack_frame->GetData();
+ const size_t length = stack_frame->GetLength();
+ if (Primitive::Is64BitType(type)) {
+ int64_t primitive = 0;
+ memcpy(&primitive, array + length - sizeof(int64_t), sizeof(int64_t));
+ value->SetJ(primitive);
+ } else {
+ uint32_t primitive = 0;
+ memcpy(&primitive, array + length - sizeof(uint32_t), sizeof(uint32_t));
+ value->SetI(primitive);
+ }
+ }
+}
+
+void EmulatedStackFrame::SetReturnValue(Thread* self, const JValue& value) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> r_type(hs.NewHandle(GetType()->GetRType()));
+
+ const Primitive::Type type = r_type->GetPrimitiveType();
+ if (type == Primitive::kPrimNot) {
+ Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle(GetReferences()));
+ references->SetWithoutChecks<false>(references->GetLength() - 1, value.GetL());
+ } else {
+ Handle<ByteArray> stack_frame(hs.NewHandle(GetStackFrame()));
+ int8_t* array = stack_frame->GetData();
+ const size_t length = stack_frame->GetLength();
+ if (Primitive::Is64BitType(type)) {
+ const int64_t primitive = value.GetJ();
+ memcpy(array + length - sizeof(int64_t), &primitive, sizeof(int64_t));
+ } else {
+ const uint32_t primitive = value.GetI();
+ memcpy(array + length - sizeof(uint32_t), &primitive, sizeof(uint32_t));
+ }
+ }
+}
+
+void EmulatedStackFrame::SetClass(Class* klass) {
+ CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass;
+ CHECK(klass != nullptr);
+ static_class_ = GcRoot<Class>(klass);
+}
+
+void EmulatedStackFrame::ResetClass() {
+ CHECK(!static_class_.IsNull());
+ static_class_ = GcRoot<Class>(nullptr);
+}
+
+void EmulatedStackFrame::VisitRoots(RootVisitor* visitor) {
+ static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass));
+}
+
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(_is_range) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs<_is_range>( \
+ Thread* self, \
+ Handle<mirror::MethodType> caller_type, \
+ Handle<mirror::MethodType> callee_type, \
+ const ShadowFrame& caller_frame, \
+ const uint32_t first_src_reg, \
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) \
+
+EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(true);
+EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL(false);
+#undef EXPLICIT_CREATE_FROM_SHADOW_FRAME_AND_ARGS_DECL
+
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/emulated_stack_frame.h b/runtime/mirror/emulated_stack_frame.h
new file mode 100644
index 0000000..9fa06b7
--- /dev/null
+++ b/runtime/mirror/emulated_stack_frame.h
@@ -0,0 +1,109 @@
+/*
+ * 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_MIRROR_EMULATED_STACK_FRAME_H_
+#define ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
+
+#include "dex_instruction.h"
+#include "method_type.h"
+#include "object.h"
+#include "stack.h"
+#include "string.h"
+#include "utils.h"
+
+namespace art {
+
+struct EmulatedStackFrameOffsets;
+
+namespace mirror {
+
+// C++ mirror of dalvik.system.EmulatedStackFrame
+class MANAGED EmulatedStackFrame : public Object {
+ public:
+ // Creates an emulated stack frame whose type is |frame_type| from
+ // a shadow frame.
+ template <bool is_range>
+ static mirror::EmulatedStackFrame* CreateFromShadowFrameAndArgs(
+ Thread* self,
+ Handle<mirror::MethodType> args_type,
+ Handle<mirror::MethodType> frame_type,
+ const ShadowFrame& caller_frame,
+ const uint32_t first_src_reg,
+ const uint32_t (&arg)[Instruction::kMaxVarArgRegs]) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Writes the contents of this emulated stack frame to the |callee_frame|
+ // whose type is |callee_type|, starting at |first_dest_reg|.
+ bool WriteToShadowFrame(
+ Thread* self,
+ Handle<mirror::MethodType> callee_type,
+ const uint32_t first_dest_reg,
+ ShadowFrame* callee_frame) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Sets |value| to the return value written to this emulated stack frame (if any).
+ void GetReturnValue(Thread* self, JValue* value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Sets the return value slot of this emulated stack frame to |value|.
+ void SetReturnValue(Thread* self, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_);
+ static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return static_class_.Read();
+ }
+
+ mirror::MethodType* GetType() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<MethodType>(OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, type_));
+ }
+
+ mirror::ObjectArray<mirror::Object>* GetReferences() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<mirror::ObjectArray<mirror::Object>>(
+ OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, references_));
+ }
+
+ mirror::ByteArray* GetStackFrame() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetFieldObject<mirror::ByteArray>(
+ OFFSET_OF_OBJECT_MEMBER(EmulatedStackFrame, stack_frame_));
+ }
+
+ static MemberOffset TypeOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, type_));
+ }
+
+ static MemberOffset ReferencesOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, references_));
+ }
+
+ static MemberOffset StackFrameOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(EmulatedStackFrame, stack_frame_));
+ }
+
+ HeapReference<mirror::ObjectArray<mirror::Object>> references_;
+ HeapReference<mirror::ByteArray> stack_frame_;
+ HeapReference<mirror::MethodType> type_;
+
+ static GcRoot<mirror::Class> static_class_; // dalvik.system.EmulatedStackFrame.class
+
+ friend struct art::EmulatedStackFrameOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(EmulatedStackFrame);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_EMULATED_STACK_FRAME_H_
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 40716ad..7bf9c5b 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -36,6 +36,11 @@
return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, method_type_));
}
+ ArtField* GetTargetField() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return reinterpret_cast<ArtField*>(
+ GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
+ }
+
ArtMethod* GetTargetMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
return reinterpret_cast<ArtMethod*>(
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 329aae9..6206948 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -446,6 +446,12 @@
return soa.AddLocalReference<jobject>(annotations::GetAnnotationForField(field, klass));
}
+static jlong Field_getArtField(JNIEnv* env, jobject javaField) {
+ ScopedFastNativeObjectAccess soa(env);
+ ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
+ return reinterpret_cast<jlong>(field);
+}
+
static jobjectArray Field_getDeclaredAnnotations(JNIEnv* env, jobject javaField) {
ScopedFastNativeObjectAccess soa(env);
ArtField* field = soa.Decode<mirror::Field>(javaField)->GetArtField();
@@ -489,6 +495,7 @@
NATIVE_METHOD(Field, getChar, "!(Ljava/lang/Object;)C"),
NATIVE_METHOD(Field, getAnnotationNative,
"!(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
+ NATIVE_METHOD(Field, getArtField, "!()J"),
NATIVE_METHOD(Field, getDeclaredAnnotations, "!()[Ljava/lang/annotation/Annotation;"),
NATIVE_METHOD(Field, getSignatureAnnotation, "!()[Ljava/lang/String;"),
NATIVE_METHOD(Field, getDouble, "!(Ljava/lang/Object;)D"),
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 4e600ae..d645c5a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -86,6 +86,7 @@
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
+#include "mirror/emulated_stack_frame.h"
#include "mirror/field.h"
#include "mirror/method.h"
#include "mirror/method_handle_impl.h"
@@ -1593,6 +1594,7 @@
mirror::Field::VisitRoots(visitor);
mirror::MethodType::VisitRoots(visitor);
mirror::MethodHandleImpl::VisitRoots(visitor);
+ mirror::EmulatedStackFrame::VisitRoots(visitor);
// Visit all the primitive array types classes.
mirror::PrimitiveArray<uint8_t>::VisitRoots(visitor); // BooleanArray
mirror::PrimitiveArray<int8_t>::VisitRoots(visitor); // ByteArray
diff --git a/runtime/runtime.h b/runtime/runtime.h
index b25ec23..043ff5d 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -316,7 +316,8 @@
}
bool IsMethodHandlesEnabled() const {
- return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+ // return experimental_flags_ & ExperimentalFlags::kMethodHandles;
+ return true;
}
void DisallowNewSystemWeaks() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index badea53..42265a9 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -57,8 +57,6 @@
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
-
- testThrowException();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -87,10 +85,6 @@
mh1.invokeExact(aInstance);
System.out.println("mh1.invoke(aInstance) should not succeeed");
} catch (WrongMethodTypeException expected) {
- } catch (ClassCastException workaround) {
- // TODO(narayan): ART treats all invokes as if they were non-exact. We
- // should throw a WMTE if we execute an invoke-polymorphic instruction whose
- // target method is MethodHandle.invokeExact.
}
// This should *still* be as if an invoke-super was called from one of C's
@@ -133,21 +127,6 @@
} catch (IllegalAccessException expected) {
}
}
-
- public static void testThrowException() throws Throwable {
- MethodHandle handle = MethodHandles.throwException(String.class,
- IllegalArgumentException.class);
- if (handle.type().returnType() != String.class) {
- System.out.println("Unexpected return type for handle: " + handle
- + " [ " + handle.type() + "]");
- }
-
- try {
- handle.invoke();
- System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
- } catch (IllegalArgumentException expected) {
- }
- }
}
diff --git a/test/957-methodhandle-transforms/build b/test/957-methodhandle-transforms/build
new file mode 100755
index 0000000..a423ca6
--- /dev/null
+++ b/test/957-methodhandle-transforms/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/expected.txt b/test/957-methodhandle-transforms/expected.txt
new file mode 100644
index 0000000..73a34bc
--- /dev/null
+++ b/test/957-methodhandle-transforms/expected.txt
@@ -0,0 +1,35 @@
+---
+-- testDelegation
+---
+boolean: false
+char: h
+short: 56
+int: 72
+long: 2147483689
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+boolean: false
+char: h
+short: 56
+int: 72
+long: 73
+float: 0.56
+double: 100.0
+String: hello
+Object: goodbye
+true
+true
+a
+a
+42
+42
+43
+43
+43.0
+43.0
+43.0
+43.0
+plank
+plank
diff --git a/test/957-methodhandle-transforms/info.txt b/test/957-methodhandle-transforms/info.txt
new file mode 100644
index 0000000..bc50e85
--- /dev/null
+++ b/test/957-methodhandle-transforms/info.txt
@@ -0,0 +1,3 @@
+Tests for method handle transformations.
+
+NOTE: needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/957-methodhandle-transforms/run b/test/957-methodhandle-transforms/run
new file mode 100755
index 0000000..a9f1822
--- /dev/null
+++ b/test/957-methodhandle-transforms/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
new file mode 100644
index 0000000..e9d313b
--- /dev/null
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.invoke.Transformers.Transformer;
+
+import dalvik.system.EmulatedStackFrame;
+
+public class Main {
+
+ public static void testDelegate_allTypes(boolean z, char a, short b, int c, long d,
+ float e, double f, String g, Object h) {
+ System.out.println("boolean: " + z);
+ System.out.println("char: " + a);
+ System.out.println("short: " + b);
+ System.out.println("int: " + c);
+ System.out.println("long: " + d);
+ System.out.println("float: " + e);
+ System.out.println("double: " + f);
+ System.out.println("String: " + g);
+ System.out.println("Object: " + h);
+ }
+
+ public static boolean testDelegate_returnBoolean() {
+ return true;
+ }
+
+ public static char testDelegate_returnChar() {
+ return 'a';
+ }
+
+ public static int testDelegate_returnInt() {
+ return 42;
+ }
+
+ public static long testDelegate_returnLong() {
+ return 43;
+ }
+
+ public static float testDelegate_returnFloat() {
+ return 43.0f;
+ }
+
+ public static double testDelegate_returnDouble() {
+ return 43.0;
+ }
+
+ public static String testDelegate_returnString() {
+ return "plank";
+ }
+
+ public static class DelegatingTransformer extends Transformer {
+ private final MethodHandle delegate;
+
+ public DelegatingTransformer(MethodHandle delegate) {
+ super(delegate.type());
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void transform(EmulatedStackFrame stackFrame) throws Throwable {
+ delegate.invoke(stackFrame);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ testThrowException();
+
+ testDelegation();
+ }
+
+ public static void testDelegation() throws Throwable {
+ System.out.println("---");
+ System.out.println("-- testDelegation");
+ System.out.println("---");
+
+ MethodHandle specialFunctionHandle = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_allTypes", MethodType.methodType(void.class,
+ new Class<?>[] { boolean.class, char.class, short.class, int.class, long.class,
+ float.class, double.class, String.class, Object.class }));
+
+ DelegatingTransformer delegate = new DelegatingTransformer(specialFunctionHandle);
+
+ // Test an exact invoke.
+ //
+ // Note that the shorter form below doesn't work and must be
+ // investigated on the jack side : b/32536744
+ //
+ // delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ // 0.56f, 100.0d, "hello", (Object) "goodbye");
+
+ Object obj = "goodbye";
+ delegate.invokeExact(false, 'h', (short) 56, 72, Integer.MAX_VALUE + 42l,
+ 0.56f, 100.0d, "hello", obj);
+
+ // Test a non exact invoke with one int -> long conversion and a float -> double
+ // conversion.
+ delegate.invoke(false, 'h', (short) 56, 72, 73,
+ 0.56f, 100.0f, "hello", "goodbye");
+
+ // Should throw a WrongMethodTypeException if the types don't align.
+ try {
+ delegate.invoke(false);
+ throw new AssertionError("Call to invoke unexpectedly succeeded");
+ } catch (WrongMethodTypeException expected) {
+ }
+
+ // Test return values.
+
+ // boolean.
+ MethodHandle returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnBoolean", MethodType.methodType(boolean.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((boolean) delegate.invoke());
+ System.out.println((boolean) delegate.invokeExact());
+
+ // char.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnChar", MethodType.methodType(char.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((char) delegate.invoke());
+ System.out.println((char) delegate.invokeExact());
+
+ // int.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnInt", MethodType.methodType(int.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((int) delegate.invoke());
+ System.out.println((int) delegate.invokeExact());
+
+ // long.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnLong", MethodType.methodType(long.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((long) delegate.invoke());
+ System.out.println((long) delegate.invokeExact());
+
+ // float.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnFloat", MethodType.methodType(float.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((float) delegate.invoke());
+ System.out.println((float) delegate.invokeExact());
+
+ // double.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnDouble", MethodType.methodType(double.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((double) delegate.invoke());
+ System.out.println((double) delegate.invokeExact());
+
+ // references.
+ returner = MethodHandles.lookup().findStatic(
+ Main.class, "testDelegate_returnString", MethodType.methodType(String.class));
+ delegate = new DelegatingTransformer(returner);
+
+ System.out.println((String) delegate.invoke());
+ System.out.println((String) delegate.invokeExact());
+ }
+
+ public static void testThrowException() throws Throwable {
+ MethodHandle handle = MethodHandles.throwException(String.class,
+ IllegalArgumentException.class);
+
+ if (handle.type().returnType() != String.class) {
+ System.out.println("Unexpected return type for handle: " + handle +
+ " [ " + handle.type() + "]");
+ }
+
+ try {
+ handle.invoke();
+ System.out.println("Expected an exception of type: java.lang.IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+}
+
+
diff --git a/test/979-invoke-polymorphic-accessors/build b/test/979-invoke-polymorphic-accessors/build
new file mode 100644
index 0000000..a423ca6
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/build
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ # Don't do anything with jvm.
+ export USE_JACK=true
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/979-invoke-polymorphic-accessors/expected.txt b/test/979-invoke-polymorphic-accessors/expected.txt
new file mode 100644
index 0000000..2987b6c
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/expected.txt
@@ -0,0 +1 @@
+Passed InvokeExact tests for accessors.
diff --git a/test/979-invoke-polymorphic-accessors/info.txt b/test/979-invoke-polymorphic-accessors/info.txt
new file mode 100644
index 0000000..b2f55f0
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/info.txt
@@ -0,0 +1 @@
+This test requires Jack with invoke-polymorphic support.
diff --git a/test/979-invoke-polymorphic-accessors/run b/test/979-invoke-polymorphic-accessors/run
new file mode 100644
index 0000000..a9f1822
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/run
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/979-invoke-polymorphic-accessors/src/Main.java b/test/979-invoke-polymorphic-accessors/src/Main.java
new file mode 100644
index 0000000..6cdcd10
--- /dev/null
+++ b/test/979-invoke-polymorphic-accessors/src/Main.java
@@ -0,0 +1,727 @@
+/*
+ * 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.
+ */
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.WrongMethodTypeException;
+
+public class Main {
+
+ public static class ValueHolder {
+ public boolean m_z = false;
+ public byte m_b = 0;
+ public char m_c = 'a';
+ public short m_s = 0;
+ public int m_i = 0;
+ public float m_f = 0.0f;
+ public double m_d = 0.0;
+ public long m_j = 0;
+ public String m_l = "a";
+
+ public static boolean s_z;
+ public static byte s_b;
+ public static char s_c;
+ public static short s_s;
+ public static int s_i;
+ public static float s_f;
+ public static double s_d;
+ public static long s_j;
+ public static String s_l;
+
+ public final int m_fi = 0xa5a5a5a5;
+ public static final int s_fi = 0x5a5a5a5a;
+ }
+
+ public static class InvokeExactTester {
+ private enum PrimitiveType {
+ Boolean,
+ Byte,
+ Char,
+ Short,
+ Int,
+ Long,
+ Float,
+ Double,
+ String,
+ }
+
+ private enum AccessorType {
+ IPUT,
+ SPUT,
+ IGET,
+ SGET,
+ }
+
+ private static void assertActualAndExpectedMatch(boolean actual, boolean expected)
+ throws AssertionError {
+ if (actual != expected) {
+ throw new AssertionError("Actual != Expected (" + actual + " != " + expected + ")");
+ }
+ }
+
+ private static void assertTrue(boolean value) throws AssertionError {
+ if (!value) {
+ throw new AssertionError("Value is not true");
+ }
+ }
+
+ static void setByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable {
+ setByte(m, null, value, expectFailure);
+ }
+
+ static void getByte(MethodHandle m, ValueHolder v, byte value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final byte got;
+ if (v == null) {
+ got = (byte)m.invokeExact();
+ } else {
+ got = (byte)m.invokeExact(v);
+ }
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getByte(MethodHandle m, byte value, boolean expectFailure) throws Throwable {
+ getByte(m, null, value, expectFailure);
+ }
+
+ static void setChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setChar(MethodHandle m, char value, boolean expectFailure) throws Throwable {
+ setChar(m, null, value, expectFailure);
+ }
+
+ static void getChar(MethodHandle m, ValueHolder v, char value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final char got;
+ if (v == null) {
+ got = (char)m.invokeExact();
+ } else {
+ got = (char)m.invokeExact(v);
+ }
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getChar(MethodHandle m, char value, boolean expectFailure) throws Throwable {
+ getChar(m, null, value, expectFailure);
+ }
+
+ static void setShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setShort(MethodHandle m, short value, boolean expectFailure) throws Throwable {
+ setShort(m, null, value, expectFailure);
+ }
+
+ static void getShort(MethodHandle m, ValueHolder v, short value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final short got = (v == null) ? (short)m.invokeExact() : (short)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getShort(MethodHandle m, short value, boolean expectFailure) throws Throwable {
+ getShort(m, null, value, expectFailure);
+ }
+
+ static void setInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setInt(MethodHandle m, int value, boolean expectFailure) throws Throwable {
+ setInt(m, null, value, expectFailure);
+ }
+
+ static void getInt(MethodHandle m, ValueHolder v, int value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final int got = (v == null) ? (int)m.invokeExact() : (int)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getInt(MethodHandle m, int value, boolean expectFailure) throws Throwable {
+ getInt(m, null, value, expectFailure);
+ }
+
+ static void setLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setLong(MethodHandle m, long value, boolean expectFailure) throws Throwable {
+ setLong(m, null, value, expectFailure);
+ }
+
+ static void getLong(MethodHandle m, ValueHolder v, long value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final long got = (v == null) ? (long)m.invokeExact() : (long)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getLong(MethodHandle m, long value, boolean expectFailure) throws Throwable {
+ getLong(m, null, value, expectFailure);
+ }
+
+ static void setFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable {
+ setFloat(m, null, value, expectFailure);
+ }
+
+ static void getFloat(MethodHandle m, ValueHolder v, float value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final float got = (v == null) ? (float)m.invokeExact() : (float)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getFloat(MethodHandle m, float value, boolean expectFailure) throws Throwable {
+ getFloat(m, null, value, expectFailure);
+ }
+
+ static void setDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setDouble(MethodHandle m, double value, boolean expectFailure)
+ throws Throwable {
+ setDouble(m, null, value, expectFailure);
+ }
+
+ static void getDouble(MethodHandle m, ValueHolder v, double value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final double got = (v == null) ? (double)m.invokeExact() : (double)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getDouble(MethodHandle m, double value, boolean expectFailure)
+ throws Throwable {
+ getDouble(m, null, value, expectFailure);
+ }
+
+ static void setString(MethodHandle m, ValueHolder v, String value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setString(MethodHandle m, String value, boolean expectFailure)
+ throws Throwable {
+ setString(m, null, value, expectFailure);
+ }
+
+ static void getString(MethodHandle m, ValueHolder v, String value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final String got = (v == null) ? (String)m.invokeExact() : (String)m.invokeExact(v);
+ assertTrue(got.equals(value));
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getString(MethodHandle m, String value, boolean expectFailure)
+ throws Throwable {
+ getString(m, null, value, expectFailure);
+ }
+
+ static void setBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ if (v == null) {
+ m.invokeExact(value);
+ }
+ else {
+ m.invokeExact(v, value);
+ }
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void setBoolean(MethodHandle m, boolean value, boolean expectFailure)
+ throws Throwable {
+ setBoolean(m, null, value, expectFailure);
+ }
+
+ static void getBoolean(MethodHandle m, ValueHolder v, boolean value, boolean expectFailure)
+ throws Throwable {
+ boolean exceptionThrown = false;
+ try {
+ final boolean got =
+ (v == null) ? (boolean)m.invokeExact() : (boolean)m.invokeExact(v);
+ assertTrue(got == value);
+ }
+ catch (WrongMethodTypeException e) {
+ exceptionThrown = true;
+ }
+ assertActualAndExpectedMatch(exceptionThrown, expectFailure);
+ }
+
+ static void getBoolean(MethodHandle m, boolean value, boolean expectFailure)
+ throws Throwable {
+ getBoolean(m, null, value, expectFailure);
+ }
+
+ static boolean resultFor(PrimitiveType actualType, PrimitiveType expectedType,
+ AccessorType actualAccessor,
+ AccessorType expectedAccessor) {
+ return (actualType != expectedType) || (actualAccessor != expectedAccessor);
+ }
+
+ static void tryAccessor(MethodHandle methodHandle,
+ ValueHolder valueHolder,
+ PrimitiveType primitive,
+ Object value,
+ AccessorType accessor) throws Throwable {
+ boolean booleanValue =
+ value instanceof Boolean ? ((Boolean)value).booleanValue() : false;
+ setBoolean(methodHandle, valueHolder, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IPUT));
+ setBoolean(methodHandle, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SPUT));
+ getBoolean(methodHandle, valueHolder, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.IGET));
+ getBoolean(methodHandle, booleanValue,
+ resultFor(primitive, PrimitiveType.Boolean, accessor, AccessorType.SGET));
+
+ byte byteValue = value instanceof Byte ? ((Byte)value).byteValue() : (byte)0;
+ setByte(methodHandle, valueHolder, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IPUT));
+ setByte(methodHandle, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SPUT));
+ getByte(methodHandle, valueHolder, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.IGET));
+ getByte(methodHandle, byteValue,
+ resultFor(primitive, PrimitiveType.Byte, accessor, AccessorType.SGET));
+
+ char charValue = value instanceof Character ? ((Character)value).charValue() : 'z';
+ setChar(methodHandle, valueHolder, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IPUT));
+ setChar(methodHandle, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SPUT));
+ getChar(methodHandle, valueHolder, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.IGET));
+ getChar(methodHandle, charValue,
+ resultFor(primitive, PrimitiveType.Char, accessor, AccessorType.SGET));
+
+ short shortValue = value instanceof Short ? ((Short)value).shortValue() : (short)0;
+ setShort(methodHandle, valueHolder, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IPUT));
+ setShort(methodHandle, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SPUT));
+ getShort(methodHandle, valueHolder, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.IGET));
+ getShort(methodHandle, shortValue,
+ resultFor(primitive, PrimitiveType.Short, accessor, AccessorType.SGET));
+
+ int intValue = value instanceof Integer ? ((Integer)value).intValue() : -1;
+ setInt(methodHandle, valueHolder, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IPUT));
+ setInt(methodHandle, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SPUT));
+ getInt(methodHandle, valueHolder, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.IGET));
+ getInt(methodHandle, intValue,
+ resultFor(primitive, PrimitiveType.Int, accessor, AccessorType.SGET));
+
+ long longValue = value instanceof Long ? ((Long)value).longValue() : (long)-1;
+ setLong(methodHandle, valueHolder, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IPUT));
+ setLong(methodHandle, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SPUT));
+ getLong(methodHandle, valueHolder, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.IGET));
+ getLong(methodHandle, longValue,
+ resultFor(primitive, PrimitiveType.Long, accessor, AccessorType.SGET));
+
+ float floatValue = value instanceof Float ? ((Float)value).floatValue() : -1.0f;
+ setFloat(methodHandle, valueHolder, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IPUT));
+ setFloat(methodHandle, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SPUT));
+ getFloat(methodHandle, valueHolder, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.IGET));
+ getFloat(methodHandle, floatValue,
+ resultFor(primitive, PrimitiveType.Float, accessor, AccessorType.SGET));
+
+ double doubleValue = value instanceof Double ? ((Double)value).doubleValue() : -1.0;
+ setDouble(methodHandle, valueHolder, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IPUT));
+ setDouble(methodHandle, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SPUT));
+ getDouble(methodHandle, valueHolder, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.IGET));
+ getDouble(methodHandle, doubleValue,
+ resultFor(primitive, PrimitiveType.Double, accessor, AccessorType.SGET));
+
+ String stringValue = value instanceof String ? ((String) value) : "No Spock, no";
+ setString(methodHandle, valueHolder, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IPUT));
+ setString(methodHandle, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SPUT));
+ getString(methodHandle, valueHolder, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.IGET));
+ getString(methodHandle, stringValue,
+ resultFor(primitive, PrimitiveType.String, accessor, AccessorType.SGET));
+ }
+
+ public static void main() throws Throwable {
+ ValueHolder valueHolder = new ValueHolder();
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ boolean [] booleans = { false, true, false };
+ for (boolean b : booleans) {
+ Boolean boxed = new Boolean(b);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_z == b);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class),
+ valueHolder, PrimitiveType.Boolean, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_z == b);
+ }
+
+ byte [] bytes = { (byte)0x73, (byte)0xfe };
+ for (byte b : bytes) {
+ Byte boxed = new Byte(b);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_b == b);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_b", byte.class),
+ valueHolder, PrimitiveType.Byte, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_b == b);
+ }
+
+ char [] chars = { 'a', 'b', 'c' };
+ for (char c : chars) {
+ Character boxed = new Character(c);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_c == c);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_c", char.class),
+ valueHolder, PrimitiveType.Char, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_c == c);
+ }
+
+ short [] shorts = { (short)0x1234, (short)0x4321 };
+ for (short s : shorts) {
+ Short boxed = new Short(s);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_s == s);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_s", short.class),
+ valueHolder, PrimitiveType.Short, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_s == s);
+ }
+
+ int [] ints = { -100000000, 10000000 };
+ for (int i : ints) {
+ Integer boxed = new Integer(i);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_i == i);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_i", int.class),
+ valueHolder, PrimitiveType.Int, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_i == i);
+ }
+
+ float [] floats = { 0.99f, -1.23e-17f };
+ for (float f : floats) {
+ Float boxed = new Float(f);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_f == f);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_f", float.class),
+ valueHolder, PrimitiveType.Float, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_f == f);
+ }
+
+ double [] doubles = { 0.44444444444e37, -0.555555555e-37 };
+ for (double d : doubles) {
+ Double boxed = new Double(d);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_d == d);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_d", double.class),
+ valueHolder, PrimitiveType.Double, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_d == d);
+ }
+
+ long [] longs = { 0x0123456789abcdefl, 0xfedcba9876543210l };
+ for (long j : longs) {
+ Long boxed = new Long(j);
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.IGET);
+ assertTrue(valueHolder.m_j == j);
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_j", long.class),
+ valueHolder, PrimitiveType.Long, boxed, AccessorType.SGET);
+ assertTrue(ValueHolder.s_j == j);
+ }
+
+ String [] strings = { "octopus", "crab" };
+ for (String s : strings) {
+ tryAccessor(lookup.findSetter(ValueHolder.class, "m_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.IPUT);
+ tryAccessor(lookup.findGetter(ValueHolder.class, "m_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.IGET);
+ assertTrue(s.equals(valueHolder.m_l));
+ tryAccessor(lookup.findStaticSetter(ValueHolder.class, "s_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.SPUT);
+ tryAccessor(lookup.findStaticGetter(ValueHolder.class, "s_l", String.class),
+ valueHolder, PrimitiveType.String, s, AccessorType.SGET);
+ assertTrue(s.equals(ValueHolder.s_l));
+ }
+
+ System.out.println("Passed InvokeExact tests for accessors.");
+ }
+ }
+
+ public static class FindAccessorTester {
+ public static void main() throws Throwable {
+ ValueHolder valueHolder = new ValueHolder();
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+
+ lookup.findStaticGetter(ValueHolder.class, "s_fi", int.class);
+ try {
+ lookup.findStaticGetter(ValueHolder.class, "s_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findGetter(ValueHolder.class, "s_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findStaticSetter(ValueHolder.class, "s_fi", int.class);
+ unreachable();
+ } catch (IllegalAccessException e) {}
+
+ lookup.findGetter(ValueHolder.class, "m_fi", int.class);
+ try {
+ lookup.findGetter(ValueHolder.class, "m_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findStaticGetter(ValueHolder.class, "m_fi", byte.class);
+ unreachable();
+ } catch (NoSuchFieldException e) {}
+ try {
+ lookup.findSetter(ValueHolder.class, "m_fi", int.class);
+ unreachable();
+ } catch (IllegalAccessException e) {}
+ }
+
+ public static void unreachable() throws Throwable{
+ throw new Error("unreachable");
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ FindAccessorTester.main();
+ InvokeExactTester.main();
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index fcb6f4d..cfb05f9 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -277,6 +277,7 @@
908-gc-start-finish \
909-attach-agent \
910-methods \
+ 911-get-stack-trace \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \