Basic implementation of invoke / invoke-polymorphic.
Basic switch interpreter support for invoke-polymorphic. This change
allows for virtual/interface and static invokes on method handles.
Support for direct invokes (including constructors) and field
getters and setters will be added in follow up changes.
Bug: 30550796
Test: make test-art-host
Change-Id: Ieb3a991d974060d930d56467908d5c7c11d0e38e
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 7a6162c..2d90734 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -22,7 +22,13 @@
#include "debugger.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "jit/jit.h"
+#include "jvalue.h"
+#include "method_handles.h"
#include "mirror/array-inl.h"
+#include "mirror/class.h"
+#include "mirror/method_handle_impl.h"
+#include "reflection.h"
+#include "reflection-inl.h"
#include "stack.h"
#include "unstarted_runtime.h"
#include "verifier/method_verifier.h"
@@ -503,8 +509,7 @@
}
// Separate declaration is required solely for the attributes.
-template <bool is_range,
- bool do_assignability_check>
+template <bool is_range, bool do_assignability_check>
REQUIRES_SHARED(Locks::mutator_lock_)
static inline bool DoCallCommon(ArtMethod* called_method,
Thread* self,
@@ -576,6 +581,130 @@
}
}
+template<bool is_range, bool do_access_check>
+ REQUIRES_SHARED(Locks::mutator_lock_)
+inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data,
+ JValue* result) {
+ // 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();
+
+ // The 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.
+ //
+ // TODO(narayan) We'll have to check in the verifier that this is in fact a
+ // signature polymorphic method so that we disallow calls via invoke-polymorphic
+ // to non sig-poly methods. This would also have the side effect of verifying
+ // that vRegC really is a reference type.
+ mirror::MethodHandleImpl* const method_handle =
+ reinterpret_cast<mirror::MethodHandleImpl*>(shadow_frame.GetVRegReference(vRegC));
+ if (UNLIKELY(method_handle == nullptr)) {
+ 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);
+
+ result->SetJ(0);
+ return false;
+ }
+
+ // The vRegH value gives the index of the proto_id associated with this
+ // signature polymorphic callsite.
+ const uint32_t callsite_proto_id = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc();
+
+ // Call through to the classlinker and ask it to resolve the static type associated
+ // with the callsite. This information is stored in the dex cache so it's
+ // guaranteed to be fast after the first resolution.
+ StackHandleScope<2> hs(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ mirror::Class* caller_class = shadow_frame.GetMethod()->GetDeclaringClass();
+ mirror::MethodType* callsite_type = class_linker->ResolveMethodType(
+ caller_class->GetDexFile(), callsite_proto_id,
+ hs.NewHandle<mirror::DexCache>(caller_class->GetDexCache()),
+ hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader()));
+
+ // This implies we couldn't resolve one or more types in this method handle.
+ if (UNLIKELY(callsite_type == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ result->SetJ(0);
+ return false;
+ }
+
+ const char* old_cause = self->StartAssertNoThreadSuspension("DoInvokePolymorphic");
+
+ // Get the method we're actually invoking along with the kind of
+ // invoke that is desired. We don't need to perform access checks at this
+ // point because they would have been performed on our behalf at the point
+ // of creation of the method handle.
+ ArtMethod* called_method = method_handle->GetTargetMethod();
+ const MethodHandleKind handle_kind = method_handle->GetHandleKind();
+ mirror::MethodType* const handle_type = method_handle->GetMethodType();
+ CHECK(called_method != nullptr);
+ CHECK(handle_type != nullptr);
+
+ // We now have to massage the number of inputs to the target function.
+ // It's always one less than the number of inputs to the signature polymorphic
+ // invoke, the first input being a reference to the MethodHandle itself.
+ const uint16_t number_of_inputs =
+ ((is_range) ? inst->VRegA_4rcc(inst_data) : inst->VRegA_45cc(inst_data)) - 1;
+
+ uint32_t arg[Instruction::kMaxVarArgRegs] = {};
+ uint32_t receiver_vregC = 0;
+ if (is_range) {
+ receiver_vregC = (inst->VRegC_4rcc() + 1);
+ } else {
+ inst->GetVarArgs(arg, inst_data);
+ arg[0] = arg[1];
+ arg[1] = arg[2];
+ arg[2] = arg[3];
+ arg[3] = arg[4];
+ arg[4] = 0;
+ receiver_vregC = arg[0];
+ }
+
+ if (IsInvoke(handle_kind)) {
+ if (handle_kind == kInvokeVirtual || handle_kind == kInvokeInterface) {
+ mirror::Object* receiver = shadow_frame.GetVRegReference(receiver_vregC);
+ 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)) {
+ self->EndAssertNoThreadSuspension(old_cause);
+ return false;
+ }
+ } else if (handle_kind == kInvokeDirect) {
+ // TODO(narayan) : We need to handle the case where the target method is a
+ // constructor here. Also the case where we don't want to dynamically
+ // dispatch based on the type of the receiver.
+ self->EndAssertNoThreadSuspension(old_cause);
+ UNIMPLEMENTED(FATAL) << "Direct invokes are not implemented yet.";
+ return false;
+ }
+
+ // NOTE: handle_kind == kInvokeStatic needs no special treatment here. We
+ // can directly make the call. handle_kind == kInvokeSuper doesn't have any
+ // particular use and can probably be dropped.
+ if (callsite_type->IsExactMatch(handle_type)) {
+ self->EndAssertNoThreadSuspension(old_cause);
+ return DoCallCommon<is_range, do_access_check>(
+ called_method, self, shadow_frame, result, number_of_inputs,
+ arg, receiver_vregC);
+ }
+
+ self->EndAssertNoThreadSuspension(old_cause);
+ UNIMPLEMENTED(FATAL) << "Non exact invokes are not implemented yet.";
+ return false;
+ } else {
+ // TODO(narayan): Implement field getters and setters.
+ self->EndAssertNoThreadSuspension(old_cause);
+ UNIMPLEMENTED(FATAL) << "Field references in method handles are not implemented yet.";
+ return false;
+ }
+}
+
template <bool is_range,
bool do_assignability_check>
static inline bool DoCallCommon(ArtMethod* called_method,
@@ -926,6 +1055,19 @@
EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true);
#undef EXPLICIT_DO_CALL_TEMPLATE_DECL
+// Explicit DoInvokePolymorphic template function declarations.
+#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \
+ template REQUIRES_SHARED(Locks::mutator_lock_) \
+ bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \
+ Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \
+ uint16_t inst_data, JValue* result)
+
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false);
+EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true);
+#undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL
+
// Explicit DoFilledNewArray template function declarations.
#define EXPLICIT_DO_FILLED_NEW_ARRAY_TEMPLATE_DECL(_is_range_, _check, _transaction_active) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0feb013..6b28110 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -123,7 +123,7 @@
bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
const Instruction* inst, uint16_t inst_data, JValue* result);
-// Handles invoke-XXX/range instructions.
+// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
template<InvokeType type, bool is_range, bool do_access_check>
static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
@@ -164,6 +164,12 @@
}
}
+// Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range).
+template<bool is_range, bool do_access_check>
+bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame,
+ const Instruction* inst, uint16_t inst_data,
+ JValue* result);
+
// Handles invoke-virtual-quick and invoke-virtual-quick-range instructions.
// Returns true on success, otherwise throws an exception and returns false.
template<bool is_range>
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 227130e..6cff1da 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -1542,6 +1542,21 @@
POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
break;
}
+ case Instruction::INVOKE_POLYMORPHIC: {
+ PREAMBLE();
+ bool success = DoInvokePolymorphic<false, do_access_check>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
+ break;
+ }
+ case Instruction::INVOKE_POLYMORPHIC_RANGE: {
+ PREAMBLE();
+ bool success = DoInvokePolymorphic<true, do_access_check>(
+ self, shadow_frame, inst, inst_data, &result_register);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
+ break;
+ break;
+ }
case Instruction::NEG_INT:
PREAMBLE();
shadow_frame.SetVReg(
@@ -2323,8 +2338,6 @@
case Instruction::UNUSED_FC ... Instruction::UNUSED_FF:
case Instruction::UNUSED_79:
case Instruction::UNUSED_7A:
- case Instruction::INVOKE_POLYMORPHIC:
- case Instruction::INVOKE_POLYMORPHIC_RANGE:
UnexpectedOpcode(inst, shadow_frame);
}
} while (!interpret_one_instruction);