summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Almaz Mingaleev <mingaleev@google.com> 2023-08-22 15:00:44 +0100
committer Almaz Mingaleev <mingaleev@google.com> 2024-08-28 13:31:56 +0000
commit93163edd92664c79da56ffcf5de32b7b872136ce (patch)
treedad21273d417cf020aa527171dc00465076b6434
parent46a77ad7d269ba087c4075029d92fe00b04fb6bb (diff)
x86_64: Add instrinsic for MethodHandle::invokeExact...
... which targets invoke-virtual methods. New entrypoint changes deliverException's offset, hence arm test change. Bug: 297147201 Test: ./art/test/testrunner/testrunner.py --host --64 -b --optimizing Test: ./art/test.py --host -g Change-Id: I636fc60c088bfdf9b695c92de47f1c539e3956f1
-rw-r--r--compiler/optimizing/code_generator_x86_64.h1
-rw-r--r--compiler/optimizing/instruction_builder.cc32
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc134
-rw-r--r--compiler/optimizing/nodes.h9
-rw-r--r--compiler/utils/assembler_thumb_test_expected.cc.inc2
-rw-r--r--dex2oat/linker/oat_writer_test.cc2
-rw-r--r--runtime/arch/x86_64/entrypoints_init_x86_64.cc4
-rw-r--r--runtime/arch/x86_64/quick_entrypoints_x86_64.S19
-rw-r--r--runtime/entrypoints/quick/quick_default_externs.h1
-rw-r--r--runtime/entrypoints/quick/quick_default_init_entrypoints.h2
-rw-r--r--runtime/entrypoints/quick/quick_entrypoints_list.h1
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc110
-rw-r--r--runtime/entrypoints_order_test.cc5
-rw-r--r--runtime/mirror/method_handle_impl.h21
-rw-r--r--runtime/oat/image.cc4
-rw-r--r--runtime/oat/oat.h4
-rw-r--r--runtime/thread.cc1
-rw-r--r--runtime/well_known_classes.h2
-rw-r--r--test/2277-methodhandle-invokeexact/build.py4
-rw-r--r--test/2277-methodhandle-invokeexact/expected-stderr.txt0
-rw-r--r--test/2277-methodhandle-invokeexact/expected-stdout.txt28
-rw-r--r--test/2277-methodhandle-invokeexact/info.txt2
-rw-r--r--test/2277-methodhandle-invokeexact/src-multidex/Multi.java54
-rw-r--r--test/2277-methodhandle-invokeexact/src/Main.java371
24 files changed, 794 insertions, 19 deletions
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index cbb4b17fe5..ad4a60e091 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -93,7 +93,6 @@ static constexpr FloatRegister non_volatile_xmm_regs[] = { XMM12, XMM13, XMM14,
V(StringBuilderLength) \
V(StringBuilderToString) \
/* 1.8 */ \
- V(MethodHandleInvokeExact) \
V(MethodHandleInvoke)
class InvokeRuntimeCallingConvention : public CallingConvention<Register, FloatRegister> {
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index c97c78ca17..d7553dd14f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1390,8 +1390,20 @@ bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc,
&is_string_constructor);
MethodReference method_reference(&graph_->GetDexFile(), method_idx);
+
+ bool is_invoke_exact =
+ static_cast<Intrinsics>(resolved_method->GetIntrinsic()) ==
+ Intrinsics::kMethodHandleInvokeExact;
+ bool can_be_virtual = number_of_arguments >= 2 &&
+ DataType::FromShorty(shorty[1]) == DataType::Type::kReference;
+
+ bool can_be_intrinsified = is_invoke_exact && can_be_virtual;
+
+ uint32_t number_of_other_inputs = can_be_intrinsified ? 1u : 0u;
+
HInvoke* invoke = new (allocator_) HInvokePolymorphic(allocator_,
number_of_arguments,
+ number_of_other_inputs,
return_type,
dex_pc,
method_reference,
@@ -1402,6 +1414,8 @@ bool HInstructionBuilder::BuildInvokePolymorphic(uint32_t dex_pc,
return false;
}
+ DCHECK_EQ(invoke->AsInvokePolymorphic()->CanTargetInvokeVirtual(), can_be_intrinsified);
+
if (invoke->GetIntrinsic() != Intrinsics::kNone &&
invoke->GetIntrinsic() != Intrinsics::kMethodHandleInvoke &&
invoke->GetIntrinsic() != Intrinsics::kMethodHandleInvokeExact &&
@@ -1879,6 +1893,24 @@ bool HInstructionBuilder::SetupInvokeArguments(HInstruction* invoke,
graph_->GetCurrentMethod());
}
+ if (invoke->IsInvokePolymorphic()) {
+ HInvokePolymorphic* invoke_polymorphic = invoke->AsInvokePolymorphic();
+
+ if (invoke_polymorphic->CanTargetInvokeVirtual()) {
+ HLoadMethodType* load_method_type =
+ new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(),
+ invoke_polymorphic->GetProtoIndex(),
+ graph_->GetDexFile(),
+ invoke_polymorphic->GetDexPc());
+ HSharpening::ProcessLoadMethodType(load_method_type,
+ code_generator_,
+ *dex_compilation_unit_,
+ graph_->GetHandleCache()->GetHandles());
+ invoke->SetRawInputAt(invoke_polymorphic->GetNumberOfArguments(), load_method_type);
+ AppendInstruction(load_method_type);
+ }
+ }
+
return true;
}
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 0a60e9c642..c32595c486 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -19,10 +19,13 @@
#include <limits>
#include "arch/x86_64/instruction_set_features_x86_64.h"
+#include "arch/x86_64/registers_x86_64.h"
#include "art_method.h"
#include "base/bit_utils.h"
#include "code_generator_x86_64.h"
+#include "dex/modifiers.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "heap_poisoning.h"
#include "intrinsics.h"
#include "intrinsic_objects.h"
@@ -32,6 +35,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/reference.h"
#include "mirror/string.h"
+#include "optimizing/code_generator.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "utils/x86_64/assembler_x86_64.h"
@@ -141,6 +145,36 @@ class ReadBarrierSystemArrayCopySlowPathX86_64 : public SlowPathCode {
DISALLOW_COPY_AND_ASSIGN(ReadBarrierSystemArrayCopySlowPathX86_64);
};
+// invoke-polymorphic's slow-path which does not move arguments.
+class InvokePolymorphicSlowPathX86_64 : public SlowPathCode {
+ public:
+ explicit InvokePolymorphicSlowPathX86_64(HInstruction* instruction, CpuRegister method_handle)
+ : SlowPathCode(instruction), method_handle_(method_handle) {
+ DCHECK(instruction->IsInvokePolymorphic());
+ }
+
+ void EmitNativeCode(CodeGenerator* codegen) override {
+ CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen);
+ X86_64Assembler* assembler = x86_64_codegen->GetAssembler();
+ __ Bind(GetEntryLabel());
+ SaveLiveRegisters(codegen, instruction_->GetLocations());
+
+ __ movq(CpuRegister(RDI), method_handle_);
+ x86_64_codegen->InvokeRuntime(QuickEntrypointEnum::kQuickInvokePolymorphicWithHiddenReceiver,
+ instruction_,
+ instruction_->GetDexPc());
+
+ RestoreLiveRegisters(codegen, instruction_->GetLocations());
+ __ jmp(GetExitLabel());
+ }
+
+ const char* GetDescription() const override { return "InvokePolymorphicSlowPathX86_64"; }
+
+ private:
+ const CpuRegister method_handle_;
+ DISALLOW_COPY_AND_ASSIGN(InvokePolymorphicSlowPathX86_64);
+};
+
static void CreateFPToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
LocationSummary* locations =
new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
@@ -3606,7 +3640,7 @@ void IntrinsicLocationsBuilderX86_64::VisitMathFmaFloat(HInvoke* invoke) {
// Generate subtype check without read barriers.
static void GenerateSubTypeObjectCheckNoReadBarrier(CodeGeneratorX86_64* codegen,
- VarHandleSlowPathX86_64* slow_path,
+ SlowPathCode* slow_path,
CpuRegister object,
CpuRegister temp,
Address type_address,
@@ -4062,6 +4096,104 @@ static void GenerateVarHandleGet(HInvoke* invoke,
}
}
+void IntrinsicLocationsBuilderX86_64::VisitMethodHandleInvokeExact(HInvoke* invoke) {
+ // Don't emit intrinsic code for MethodHandle.invokeExact when it certainly does not target
+ // invoke-virtual: if invokeExact is called w/o arguments or if the first argument in that
+ // call is not a reference.
+ if (!invoke->AsInvokePolymorphic()->CanTargetInvokeVirtual()) {
+ return;
+ }
+ ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
+ LocationSummary* locations = new (allocator)
+ LocationSummary(invoke, LocationSummary::kCallOnMainAndSlowPath, kIntrinsified);
+
+ InvokeDexCallingConventionVisitorX86_64 calling_convention;
+ locations->SetOut(calling_convention.GetReturnLocation(invoke->GetType()));
+
+ locations->SetInAt(0, Location::RequiresRegister());
+
+ // Accomodating LocationSummary for underlying invoke-* call.
+ uint32_t number_of_args = invoke->GetNumberOfArguments();
+ for (uint32_t i = 1; i < number_of_args; ++i) {
+ locations->SetInAt(i, calling_convention.GetNextLocation(invoke->InputAt(i)->GetType()));
+ }
+
+ // The last input is MethodType object corresponding to the call-site.
+ locations->SetInAt(number_of_args, Location::RequiresRegister());
+
+ // We use a fixed-register temporary to pass the target method.
+ locations->AddTemp(calling_convention.GetMethodLocation());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorX86_64::VisitMethodHandleInvokeExact(HInvoke* invoke) {
+ DCHECK(invoke->AsInvokePolymorphic()->CanTargetInvokeVirtual());
+ LocationSummary* locations = invoke->GetLocations();
+
+ CpuRegister method_handle = locations->InAt(0).AsRegister<CpuRegister>();
+
+ SlowPathCode* slow_path =
+ new (codegen_->GetScopedAllocator()) InvokePolymorphicSlowPathX86_64(invoke, method_handle);
+ codegen_->AddSlowPath(slow_path);
+ X86_64Assembler* assembler = codegen_->GetAssembler();
+
+ Address method_handle_kind = Address(method_handle, mirror::MethodHandle::HandleKindOffset());
+
+ // If it is not InvokeVirtual then go to slow path.
+ // Even if MethodHandle's kind is kInvokeVirtual underlying method still can be an interface or
+ // direct method (that's what current `MethodHandles$Lookup.findVirtual` is doing). We don't check
+ // whether `method` is an interface method explicitly: in that case the subtype check will fail.
+ // TODO(b/297147201): check whether it can be more precise and what d8/r8 can produce.
+ __ cmpl(method_handle_kind, Immediate(mirror::MethodHandle::Kind::kInvokeVirtual));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+
+ CpuRegister call_site_type =
+ locations->InAt(invoke->GetNumberOfArguments()).AsRegister<CpuRegister>();
+
+ // Call site should match with MethodHandle's type.
+ __ cmpl(call_site_type, Address(method_handle, mirror::MethodHandle::MethodTypeOffset()));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+
+ CpuRegister method = locations->GetTemp(0).AsRegister<CpuRegister>();
+
+ // Find method to call.
+ __ movq(method, Address(method_handle, mirror::MethodHandle::ArtFieldOrMethodOffset()));
+
+ CpuRegister receiver = locations->InAt(1).AsRegister<CpuRegister>();
+
+ // Using vtable_index register as temporary in subtype check. It will be overridden later.
+ // If `method` is an interface method this check will fail.
+ CpuRegister vtable_index = locations->GetTemp(1).AsRegister<CpuRegister>();
+ GenerateSubTypeObjectCheckNoReadBarrier(codegen_,
+ slow_path,
+ receiver,
+ vtable_index,
+ Address(method, ArtMethod::DeclaringClassOffset()));
+
+ NearLabel execute_target_method;
+ // Skip virtual dispatch if `method` is private.
+ __ testl(Address(method, ArtMethod::AccessFlagsOffset()), Immediate(kAccPrivate));
+ __ j(kNotZero, &execute_target_method);
+
+ // MethodIndex is uint16_t.
+ __ movzxw(vtable_index, Address(method, ArtMethod::MethodIndexOffset()));
+
+ constexpr uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
+ // Re-using method register for receiver class.
+ __ movl(method, Address(receiver, class_offset));
+
+ constexpr uint32_t vtable_offset =
+ mirror::Class::EmbeddedVTableOffset(art::PointerSize::k64).Int32Value();
+ __ movq(method, Address(method, vtable_index, TIMES_8, vtable_offset));
+
+ __ Bind(&execute_target_method);
+ __ call(Address(
+ method,
+ ArtMethod::EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).SizeValue()));
+ codegen_->RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
+ __ Bind(slow_path->GetExitLabel());
+}
+
void IntrinsicLocationsBuilderX86_64::VisitVarHandleGet(HInvoke* invoke) {
CreateVarHandleGetLocations(invoke, codegen_);
}
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 23cc3704e9..ffddd25843 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4912,6 +4912,7 @@ class HInvokePolymorphic final : public HInvoke {
public:
HInvokePolymorphic(ArenaAllocator* allocator,
uint32_t number_of_arguments,
+ uint32_t number_of_other_inputs,
DataType::Type return_type,
uint32_t dex_pc,
MethodReference method_reference,
@@ -4924,7 +4925,7 @@ class HInvokePolymorphic final : public HInvoke {
: HInvoke(kInvokePolymorphic,
allocator,
number_of_arguments,
- /* number_of_other_inputs= */ 0u,
+ number_of_other_inputs,
return_type,
dex_pc,
method_reference,
@@ -4938,6 +4939,12 @@ class HInvokePolymorphic final : public HInvoke {
dex::ProtoIndex GetProtoIndex() { return proto_idx_; }
+ bool CanTargetInvokeVirtual() const {
+ return GetIntrinsic() == Intrinsics::kMethodHandleInvokeExact &&
+ GetNumberOfArguments() >= 2 &&
+ InputAt(1)->GetType() == DataType::Type::kReference;
+ }
+
DECLARE_INSTRUCTION(InvokePolymorphic);
protected:
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index f96a9bc154..5184b2c897 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -154,7 +154,7 @@ const char* const VixlJniHelpersResults = {
" 210: f8d9 8020 ldr.w r8, [r9, #32]\n"
" 214: 4770 bx lr\n"
" 216: f8d9 0094 ldr.w r0, [r9, #148]\n"
- " 21a: f8d9 e2bc ldr.w lr, [r9, #700]\n"
+ " 21a: f8d9 e2c0 ldr.w lr, [r9, #704]\n"
" 21e: 47f0 blx lr\n"
};
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 5683e8b2c4..8486bff0d3 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -502,7 +502,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) {
EXPECT_EQ(68U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(4U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(172 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(173 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/runtime/arch/x86_64/entrypoints_init_x86_64.cc b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
index f48ff9b541..9652f43c7f 100644
--- a/runtime/arch/x86_64/entrypoints_init_x86_64.cc
+++ b/runtime/arch/x86_64/entrypoints_init_x86_64.cc
@@ -120,6 +120,10 @@ void InitEntryPoints(JniEntryPoints* jpoints,
qpoints->SetStringCompareTo(art_quick_string_compareto);
qpoints->SetMemcpy(art_quick_memcpy);
+ // Invoke.
+ qpoints->SetInvokePolymorphicWithHiddenReceiver(
+ art_quick_invoke_polymorphic_with_hidden_receiver);
+
// Read barrier.
UpdateReadBarrierEntrypoints(qpoints, /*is_active=*/ false);
qpoints->SetReadBarrierMarkReg04(nullptr); // Cannot use register 4 (RSP) to pass arguments.
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 52024ca040..b72890d444 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -1970,6 +1970,25 @@ DEFINE_FUNCTION art_quick_invoke_polymorphic
RETURN_OR_DELIVER_PENDING_EXCEPTION
END_FUNCTION art_quick_invoke_polymorphic
+ /*
+ * Slow path for MethodHandle.invokeExact intrinsic.
+ * That intrinsic has a custom calling convention: the argument allocation doesn't start from
+ * the receiver (MethodHandle) object, but from the argument following it. That's done to match
+ * expectation of the underlying method when MethodHandle targets a method. That also affects
+ * the way arguments are spilled onto the stack.
+ */
+DEFINE_FUNCTION art_quick_invoke_polymorphic_with_hidden_receiver
+ // On entry: RDI := receiver
+ SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves
+ movq %gs:THREAD_SELF_OFFSET, %rsi // RSI := Thread (self)
+ movq %rsp, %rdx // RDX := pass SP
+ call SYMBOL(artInvokePolymorphicWithHiddenReceiver) // invoke with (receiver, self, SP)
+ // save the code pointer
+ RESTORE_SAVE_REFS_AND_ARGS_FRAME
+ movq %rax, %xmm0 // Result is in RAX. Copy to FP result register.
+ RETURN_OR_DELIVER_PENDING_EXCEPTION
+END_FUNCTION art_quick_invoke_polymorphic_with_hidden_receiver
+
DEFINE_FUNCTION art_quick_invoke_custom
SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves
// RDI := call_site_index
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 5119b27076..7736964067 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -132,6 +132,7 @@ extern "C" void art_jni_unlock_object_no_inline(art::mirror::Object*);
// Polymorphic invoke entrypoints.
extern "C" void art_quick_invoke_polymorphic(uint32_t, void*);
+extern "C" void art_quick_invoke_polymorphic_with_hidden_receiver(uint32_t, void*);
extern "C" void art_quick_invoke_custom(uint32_t, void*);
// Thread entrypoints.
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 05b4bd7c2f..8249285aae 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -109,6 +109,8 @@ static void DefaultInitEntryPoints(JniEntryPoints* jpoints,
qpoints->SetInvokeVirtualTrampolineWithAccessCheck(
art_quick_invoke_virtual_trampoline_with_access_check);
qpoints->SetInvokePolymorphic(art_quick_invoke_polymorphic);
+ // Adding support for x86_64 first.
+ qpoints->SetInvokePolymorphicWithHiddenReceiver(nullptr);
qpoints->SetInvokeCustom(art_quick_invoke_custom);
// Thread
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index be417964a8..e6f89e337c 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -134,6 +134,7 @@
V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \
V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \
V(InvokePolymorphic, void, uint32_t, void*) \
+ V(InvokePolymorphicWithHiddenReceiver, void, uint32_t, void*) \
V(InvokeCustom, void, uint32_t, void*) \
\
V(TestSuspend, void, void) \
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 9ddb61e503..692b0c40ed 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#include "android-base/logging.h"
#include "arch/context.h"
#include "art_method-inl.h"
+#include "art_method.h"
#include "base/callee_save_type.h"
+#include "base/globals.h"
#include "base/pointer_size.h"
#include "callee_save_frame.h"
#include "common_throws.h"
@@ -581,6 +584,7 @@ class BuildQuickShadowFrameVisitor final : public QuickArgumentVisitor {
: QuickArgumentVisitor(sp, is_static, shorty), sf_(sf), cur_reg_(first_arg_reg) {}
void Visit() REQUIRES_SHARED(Locks::mutator_lock_) override;
+ void SetReceiver(ObjPtr<mirror::Object> receiver) REQUIRES_SHARED(Locks::mutator_lock_);
private:
ShadowFrame* const sf_;
@@ -589,6 +593,12 @@ class BuildQuickShadowFrameVisitor final : public QuickArgumentVisitor {
DISALLOW_COPY_AND_ASSIGN(BuildQuickShadowFrameVisitor);
};
+void BuildQuickShadowFrameVisitor::SetReceiver(ObjPtr<mirror::Object> receiver) {
+ DCHECK_EQ(cur_reg_, 0u);
+ sf_->SetVRegReference(cur_reg_, receiver);
+ ++cur_reg_;
+}
+
void BuildQuickShadowFrameVisitor::Visit() {
Primitive::Type type = GetParamPrimitiveType();
switch (type) {
@@ -2456,6 +2466,106 @@ extern "C" uint64_t artInvokePolymorphic(mirror::Object* raw_receiver, Thread* s
return NanBoxResultIfNeeded(result.GetJ(), shorty[0]);
}
+extern "C" uint64_t artInvokePolymorphicWithHiddenReceiver(mirror::Object* raw_receiver,
+ Thread* self,
+ ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedQuickEntrypointChecks sqec(self);
+ DCHECK(raw_receiver != nullptr);
+ DCHECK(raw_receiver->InstanceOf(WellKnownClasses::java_lang_invoke_MethodHandle.Get()));
+ DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
+
+ JNIEnvExt* env = self->GetJniEnv();
+ ScopedObjectAccessUnchecked soa(env);
+ ScopedJniEnvLocalRefState env_state(env);
+ const char* old_cause = self->StartAssertNoThreadSuspension("Making stack arguments safe.");
+
+ // From the instruction, get the |callsite_shorty| and expose arguments on the stack to the GC.
+ uint32_t dex_pc;
+ ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethodAndDexPc(sp, &dex_pc);
+ const Instruction& inst = caller_method->DexInstructions().InstructionAt(dex_pc);
+ DCHECK(inst.Opcode() == Instruction::INVOKE_POLYMORPHIC ||
+ inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE);
+ const dex::ProtoIndex proto_idx(inst.VRegH());
+ std::string_view shorty = caller_method->GetDexFile()->GetShortyView(proto_idx);
+
+ // invokeExact is not a static method, but here we use custom calling convention and the receiver
+ // (MethodHandle) object is not passed as a first argument, but through different means and hence
+ // shorty and arguments allocation looks as-if invokeExact was static.
+ RememberForGcArgumentVisitor gc_visitor(sp, /* is_static= */ true, shorty, &soa);
+ gc_visitor.VisitArguments();
+
+ // Wrap raw_receiver in a Handle for safety.
+ StackHandleScope<2> hs(self);
+ Handle<mirror::MethodHandle> method_handle(
+ hs.NewHandle(down_cast<mirror::MethodHandle*>(raw_receiver)));
+
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ ClassLinker* linker = Runtime::Current()->GetClassLinker();
+ ArtMethod* invoke_exact = WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
+ if (kIsDebugBuild) {
+ ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::ResolveMode::kCheckICCEAndIAE>(
+ self, inst.VRegB(), caller_method, kVirtual);
+ CHECK_EQ(resolved_method, invoke_exact);
+ }
+
+ Handle<mirror::MethodType> method_type(
+ hs.NewHandle(linker->ResolveMethodType(self, proto_idx, caller_method)));
+ if (UNLIKELY(method_type.IsNull())) {
+ // This implies we couldn't resolve one or more types in this method handle.
+ CHECK(self->IsExceptionPending());
+ return 0UL;
+ }
+
+ DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst.VRegA());
+
+ // Fix references before constructing the shadow frame.
+ gc_visitor.FixupReferences();
+
+ // Construct shadow frame placing arguments consecutively from |first_arg|.
+ const bool is_range = inst.Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE;
+ const size_t num_vregs = is_range ? inst.VRegA_4rcc() : inst.VRegA_45cc();
+ const size_t first_arg = 0;
+ ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
+ CREATE_SHADOW_FRAME(num_vregs, invoke_exact, dex_pc);
+ ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
+ ScopedStackedShadowFramePusher frame_pusher(self, shadow_frame);
+ // Pretend the method is static, see the gc_visitor comment above.
+ BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
+ /* is_static= */ true,
+ shorty,
+ shadow_frame,
+ first_arg);
+ // Receiver is not passed as a regular argument, adding it to ShadowFrame manually.
+ shadow_frame_builder.SetReceiver(method_handle.Get());
+ shadow_frame_builder.VisitArguments();
+
+ // Push a transition back into managed code onto the linked list in thread.
+ ManagedStack fragment;
+ self->PushManagedStackFragment(&fragment);
+
+ RangeInstructionOperands operands(first_arg + 1, num_vregs - 1);
+ JValue result;
+ bool success = MethodHandleInvokeExact(self,
+ *shadow_frame,
+ method_handle,
+ method_type,
+ &operands,
+ &result);
+
+ DCHECK(success || self->IsExceptionPending());
+
+ // Pop transition record.
+ self->PopManagedStackFragment(fragment);
+
+ bool is_ref = shorty[0] == 'L';
+ Runtime::Current()->GetInstrumentation()->PushDeoptContextIfNeeded(
+ self, DeoptimizationMethodType::kDefault, is_ref, result);
+
+ return NanBoxResultIfNeeded(result.GetJ(), shorty[0]);
+}
+
// Returns uint64_t representing raw bits from JValue.
extern "C" uint64_t artInvokeCustom(uint32_t call_site_idx, Thread* self, ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 9556672d05..3f55d37e8d 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -291,7 +291,10 @@ class EntrypointsOrderTest : public CommonArtTest {
pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck,
pInvokePolymorphic, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pInvokeCustom, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic,
+ pInvokePolymorphicWithHiddenReceiver, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphicWithHiddenReceiver,
+ pInvokeCustom, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeCustom, pTestSuspend, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*));
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index ba1763afe3..f80ef912f6 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -81,6 +81,18 @@ class MANAGED MethodHandle : public Object {
// method or field.
void VisitTarget(ReflectiveValueVisitor* v) REQUIRES(Locks::mutator_lock_);
+ static MemberOffset ArtFieldOrMethodOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, art_field_or_method_));
+ }
+
+ static MemberOffset HandleKindOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_));
+ }
+
+ static MemberOffset MethodTypeOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_));
+ }
+
protected:
void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -99,15 +111,6 @@ class MANAGED MethodHandle : public Object {
static MemberOffset AsTypeCacheOffset() {
return MemberOffset(OFFSETOF_MEMBER(MethodHandle, as_type_cache_));
}
- static MemberOffset MethodTypeOffset() {
- return MemberOffset(OFFSETOF_MEMBER(MethodHandle, method_type_));
- }
- static MemberOffset ArtFieldOrMethodOffset() {
- return MemberOffset(OFFSETOF_MEMBER(MethodHandle, art_field_or_method_));
- }
- static MemberOffset HandleKindOffset() {
- return MemberOffset(OFFSETOF_MEMBER(MethodHandle, handle_kind_));
- }
friend struct art::MethodHandleOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandle);
diff --git a/runtime/oat/image.cc b/runtime/oat/image.cc
index 7e94c82c85..f765822585 100644
--- a/runtime/oat/image.cc
+++ b/runtime/oat/image.cc
@@ -34,8 +34,8 @@
namespace art HIDDEN {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Add signum, copySign intrinsics.
-const uint8_t ImageHeader::kImageVersion[] = { '1', '1', '2', '\0' };
+// Last change: Add invoke-virtual targeting MethodHandle.invokeExact intrinsic.
+const uint8_t ImageHeader::kImageVersion[] = { '1', '1', '3', '\0' };
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
diff --git a/runtime/oat/oat.h b/runtime/oat/oat.h
index 10e47f5b31..fa50571b27 100644
--- a/runtime/oat/oat.h
+++ b/runtime/oat/oat.h
@@ -44,8 +44,8 @@ std::ostream& operator<<(std::ostream& stream, StubType stub_type);
class EXPORT PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Implement variable sized ref-offset bitmap in mirror::Class.
- static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '6', '\0'}};
+ // Last oat version changed reason: Adding new entrypoints for InvokeExact intrisic.
+ static constexpr std::array<uint8_t, 4> kOatVersion{{'2', '4', '7', '\0'}};
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/thread.cc b/runtime/thread.cc
index df75f34e59..32fb4aa098 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3867,6 +3867,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) {
QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck)
QUICK_ENTRY_POINT_INFO(pInvokeVirtualTrampolineWithAccessCheck)
QUICK_ENTRY_POINT_INFO(pInvokePolymorphic)
+ QUICK_ENTRY_POINT_INFO(pInvokePolymorphicWithHiddenReceiver)
QUICK_ENTRY_POINT_INFO(pTestSuspend)
QUICK_ENTRY_POINT_INFO(pDeliverException)
QUICK_ENTRY_POINT_INFO(pThrowArrayBounds)
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index f29daad204..cb634a1694 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -251,6 +251,8 @@ struct EXPORT WellKnownClasses {
java_lang_StackOverflowError;
static constexpr ClassFromField<&java_lang_Thread_daemon> java_lang_Thread;
static constexpr ClassFromField<&java_lang_ThreadGroup_groups> java_lang_ThreadGroup;
+ static constexpr ClassFromMethod<&java_lang_invoke_MethodHandle_invokeExact>
+ java_lang_invoke_MethodHandle;
static constexpr ClassFromMethod<&java_lang_invoke_MethodType_makeImpl>
java_lang_invoke_MethodType;
static constexpr ClassFromMethod<&java_lang_reflect_InvocationTargetException_init>
diff --git a/test/2277-methodhandle-invokeexact/build.py b/test/2277-methodhandle-invokeexact/build.py
new file mode 100644
index 0000000000..d71324961b
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/build.py
@@ -0,0 +1,4 @@
+def build(ctx):
+ # To allow private interface methods.
+ ctx.default_build(javac_source_arg="17",
+ javac_target_arg="17")
diff --git a/test/2277-methodhandle-invokeexact/expected-stderr.txt b/test/2277-methodhandle-invokeexact/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/expected-stderr.txt
diff --git a/test/2277-methodhandle-invokeexact/expected-stdout.txt b/test/2277-methodhandle-invokeexact/expected-stdout.txt
new file mode 100644
index 0000000000..d924f9903d
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/expected-stdout.txt
@@ -0,0 +1,28 @@
+in voidMethod
+A.returnInt()=42
+A.returnDouble()=42.0
+in I.defaultMethod
+in A.overrideMe
+I'm private interface method
+A.privateReturnInt()=1042
+B.privateReturnInt()=9999
+((B) new A()).privateReturnInt()=9999
+I am from throwException
+A.staticMethod()=staticMethod
+Multi.$noinline$getMethodHandle().invokeExact(nonEmpty)=hello
+Multi: mh.invokeExact(nonEmpty)=1001
+Sums.sum(1)=1
+Sums.sum(1, 2)=3
+Sums.sum(1, 2, 3)=6
+Sums.sum(1, 2, 3, 4)=10
+Sums.sum(1, 2, 3, 4, 5)=15
+Sums.sum(1, 2, 3, 4, 5, 6)=21
+Sums.sum(1, 2, 3, 4, 5, 6, 7)=28
+Sums.sum(1, 2, 3, 4, 5, 6, 7, 8)=36
+Sums.sum(1, 2, 3, 4, 5, 6, 7, 8, 9)=45
+Sums.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)=55
+Sums.sum(1, 2L)=3
+Sums.sum(1, 2L, 3, 4L)=10
+Sums.sum(1, 2L, 3, 4L, 5, 6L)=21
+Sums.sum(1, 2L, 3, 4L, 5, 6L, 7, 8L)=36
+Sums.sum(1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L)=55
diff --git a/test/2277-methodhandle-invokeexact/info.txt b/test/2277-methodhandle-invokeexact/info.txt
new file mode 100644
index 0000000000..a1aebb4d77
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/info.txt
@@ -0,0 +1,2 @@
+More MethodHandle.invokeExact tests.
+Here we also add multidex calls to verify implementation details.
diff --git a/test/2277-methodhandle-invokeexact/src-multidex/Multi.java b/test/2277-methodhandle-invokeexact/src-multidex/Multi.java
new file mode 100644
index 0000000000..43601a947e
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/src-multidex/Multi.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+import java.util.Optional;
+
+public class Multi {
+ public static MethodHandle $noinline$getMethodHandle() {
+ return OPTIONAL_GET;
+ }
+
+ // MethodHandle comes from a different dex file.
+ public static void $noinline$testMHFromMain(MethodHandle mh) throws Throwable {
+ Optional<Integer> nonEmpty = Optional.<Integer>of(1001);
+ Object result = (Object) mh.invokeExact(nonEmpty);
+ System.out.println("Multi: mh.invokeExact(nonEmpty)=" + result);
+
+ try {
+ mh.invokeExact(nonEmpty);
+ fail("mh.type() is (Optional)Object, but callsite is (Optional)V");
+ } catch (WrongMethodTypeException expected) {}
+ }
+
+ private static void fail(String msg) {
+ throw new AssertionError(msg);
+ }
+
+ private static final MethodHandle OPTIONAL_GET;
+
+ static {
+ try {
+ OPTIONAL_GET = MethodHandles.lookup()
+ .findVirtual(Optional.class, "get", MethodType.methodType(Object.class));
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+}
diff --git a/test/2277-methodhandle-invokeexact/src/Main.java b/test/2277-methodhandle-invokeexact/src/Main.java
new file mode 100644
index 0000000000..729857e47a
--- /dev/null
+++ b/test/2277-methodhandle-invokeexact/src/Main.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2024 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 static java.lang.invoke.MethodType.methodType;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.WrongMethodTypeException;
+import java.util.Arrays;
+import java.util.Optional;
+
+public class Main {
+
+ public static void main(String[] args) throws Throwable {
+ $noinline$testNoArgsCalls();
+ $noinline$testMethodHandleFromOtherDex();
+ Multi.$noinline$testMHFromMain(OPTIONAL_GET);
+ $noinline$testWithArgs();
+ }
+
+ private static void $noinline$testMethodHandleFromOtherDex() throws Throwable {
+ MethodHandle mh = Multi.$noinline$getMethodHandle();
+ Optional<String> nonEmpty = Optional.<String>of("hello");
+ Object returnedObject = mh.invokeExact(nonEmpty);
+ System.out.println(
+ "Multi.$noinline$getMethodHandle().invokeExact(nonEmpty)=" + returnedObject);
+
+ try {
+ mh.invokeExact(nonEmpty);
+ unreachable("mh.type() is (Optional)Object, but callsite is (Optional)V");
+ } catch (WrongMethodTypeException expected) {}
+ }
+
+ private static void $noinline$testNoArgsCalls() throws Throwable {
+ VOID_METHOD.invokeExact(new A());
+ int returnedInt = (int) RETURN_INT.invokeExact(new A());
+ System.out.println("A.returnInt()=" + returnedInt);
+ double returnedDouble = (double) RETURN_DOUBLE.invokeExact(new A());
+ System.out.println("A.returnDouble()=" + returnedDouble);
+
+ try {
+ INTERFACE_DEFAULT_METHOD.invokeExact(new A());
+ unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V");
+ } catch (WrongMethodTypeException ignored) {}
+
+ INTERFACE_DEFAULT_METHOD.invokeExact((I) new A());
+ OVERWRITTEN_INTERFACE_DEFAULT_METHOD.invokeExact((I) new A());
+
+ System.out.println((String) PRIVATE_INTERFACE_METHOD.invokeExact((I) new A()));
+
+ int privateIntA = (int) A_PRIVATE_RETURN_INT.invokeExact(new A());
+ System.out.println("A.privateReturnInt()=" + privateIntA);
+
+ int privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact(new B());
+ System.out.println("B.privateReturnInt()=" + privateIntB);
+ privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact((B) new A());
+ System.out.println("((B) new A()).privateReturnInt()=" + privateIntB);
+
+ try {
+ EXCEPTION_THROWING_METHOD.invokeExact(new A());
+ unreachable("Target method always throws");
+ } catch (RuntimeException e) {
+ System.out.println(e.getMessage());
+ }
+
+ try {
+ RETURN_INT.invokeExact(new A());
+ unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V");
+ } catch (WrongMethodTypeException ignored) {}
+
+ String returnedString = (String) STATIC_METHOD.invokeExact(new A());
+ System.out.println("A.staticMethod()=" + returnedString);
+ }
+
+ private static void $noinline$testWithArgs() throws Throwable {
+ int sum = (int) SUM_I.invokeExact(new Sums(), 1);
+ System.out.println("Sums.sum(1)=" + sum);
+
+ sum = (int) SUM_2I.invokeExact(new Sums(), 1, 2);
+ System.out.println("Sums.sum(1, 2)=" + sum);
+
+ sum = (int) SUM_3I.invokeExact(new Sums(), 1, 2, 3);
+ System.out.println("Sums.sum(1, 2, 3)=" + sum);
+
+ sum = (int) SUM_4I.invokeExact(new Sums(), 1, 2, 3, 4);
+ System.out.println("Sums.sum(1, 2, 3, 4)=" + sum);
+
+ sum = (int) SUM_5I.invokeExact(new Sums(), 1, 2, 3, 4, 5);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5)=" + sum);
+
+ sum = (int) SUM_6I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5, 6)=" + sum);
+
+ sum = (int) SUM_7I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5, 6, 7)=" + sum);
+
+ sum = (int) SUM_8I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5, 6, 7, 8)=" + sum);
+
+ sum = (int) SUM_9I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5, 6, 7, 8, 9)=" + sum);
+
+ sum = (int) SUM_10I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ System.out.println("Sums.sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)=" + sum);
+
+ long lsum = (long) SUM_IJ.invokeExact(new Sums(), 1, 2L);
+ System.out.println("Sums.sum(1, 2L)=" + lsum);
+
+ lsum = (long) SUM_2IJ.invokeExact(new Sums(), 1, 2L, 3, 4L);
+ System.out.println("Sums.sum(1, 2L, 3, 4L)=" + lsum);
+
+ lsum = (long) SUM_3IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L);
+ System.out.println("Sums.sum(1, 2L, 3, 4L, 5, 6L)=" + lsum);
+
+ lsum = (long) SUM_4IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L);
+ System.out.println("Sums.sum(1, 2L, 3, 4L, 5, 6L, 7, 8L)=" + lsum);
+
+ lsum = (long) SUM_5IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L);
+ System.out.println("Sums.sum(1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L)=" + lsum);
+ }
+
+ private static void unreachable(String msg) {
+ throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg);
+ }
+
+ private static final MethodHandle VOID_METHOD;
+ private static final MethodHandle RETURN_DOUBLE;
+ private static final MethodHandle RETURN_INT;
+ private static final MethodHandle PRIVATE_INTERFACE_METHOD;
+ private static final MethodHandle B_PRIVATE_RETURN_INT;
+ private static final MethodHandle A_PRIVATE_RETURN_INT;
+ private static final MethodHandle STATIC_METHOD;
+ private static final MethodHandle EXCEPTION_THROWING_METHOD;
+ private static final MethodHandle INTERFACE_DEFAULT_METHOD;
+ private static final MethodHandle OVERWRITTEN_INTERFACE_DEFAULT_METHOD;
+ private static final MethodHandle OPTIONAL_GET;
+
+ private static final MethodHandle SUM_I;
+ private static final MethodHandle SUM_2I;
+ private static final MethodHandle SUM_3I;
+ private static final MethodHandle SUM_4I;
+ private static final MethodHandle SUM_5I;
+ private static final MethodHandle SUM_6I;
+ private static final MethodHandle SUM_7I;
+ private static final MethodHandle SUM_8I;
+ private static final MethodHandle SUM_9I;
+ private static final MethodHandle SUM_10I;
+
+ private static final MethodHandle SUM_IJ;
+ private static final MethodHandle SUM_2IJ;
+ private static final MethodHandle SUM_3IJ;
+ private static final MethodHandle SUM_4IJ;
+ private static final MethodHandle SUM_5IJ;
+
+ static {
+ try {
+ VOID_METHOD = MethodHandles.lookup()
+ .findVirtual(A.class, "voidMethod", methodType(void.class));
+ RETURN_DOUBLE = MethodHandles.lookup()
+ .findVirtual(A.class, "returnDouble", methodType(double.class));
+ RETURN_INT = MethodHandles.lookup()
+ .findVirtual(A.class, "returnInt", methodType(int.class));
+ PRIVATE_INTERFACE_METHOD = MethodHandles.privateLookupIn(I.class, MethodHandles.lookup())
+ .findVirtual(I.class, "innerPrivateMethod", methodType(String.class));
+ A_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(A.class, MethodHandles.lookup())
+ .findVirtual(A.class, "privateReturnInt", methodType(int.class));
+ B_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(B.class, MethodHandles.lookup())
+ .findVirtual(B.class, "privateReturnInt", methodType(int.class));
+ STATIC_METHOD = MethodHandles.lookup()
+ .findStatic(A.class, "staticMethod", methodType(String.class, A.class));
+ EXCEPTION_THROWING_METHOD = MethodHandles.lookup()
+ .findVirtual(A.class, "throwException", methodType(void.class));
+ INTERFACE_DEFAULT_METHOD = MethodHandles.lookup()
+ .findVirtual(I.class, "defaultMethod", methodType(void.class));
+ OVERWRITTEN_INTERFACE_DEFAULT_METHOD = MethodHandles.lookup()
+ .findVirtual(I.class, "overrideMe", methodType(void.class));
+ OPTIONAL_GET = MethodHandles.lookup()
+ .findVirtual(Optional.class, "get", methodType(Object.class));
+
+ SUM_I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(1, int.class)));
+ SUM_2I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(2, int.class)));
+ SUM_3I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(3, int.class)));
+ SUM_4I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(4, int.class)));
+ SUM_5I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(5, int.class)));
+ SUM_6I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(6, int.class)));
+ SUM_7I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(7, int.class)));
+ SUM_8I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(8, int.class)));
+ SUM_9I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(9, int.class)));
+ SUM_10I = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class)));
+
+ SUM_IJ = MethodHandles.lookup()
+ .findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class));
+ SUM_2IJ = MethodHandles.lookup()
+ .findVirtual(Sums.class,
+ "sum",
+ methodType(long.class, repeat(2, int.class, long.class)));
+ SUM_3IJ = MethodHandles.lookup()
+ .findVirtual(Sums.class,
+ "sum",
+ methodType(long.class, repeat(3, int.class, long.class)));
+ SUM_4IJ = MethodHandles.lookup()
+ .findVirtual(Sums.class,
+ "sum",
+ methodType(long.class, repeat(4, int.class, long.class)));
+ SUM_5IJ = MethodHandles.lookup()
+ .findVirtual(Sums.class,
+ "sum",
+ methodType(long.class, repeat(5, int.class, long.class)));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static Class<?>[] repeat(int times, Class<?> clazz) {
+ Class<?>[] classes = new Class<?>[times];
+ Arrays.fill(classes, clazz);
+ return classes;
+ }
+
+ private static Class<?>[] repeat(int times, Class<?> first, Class<?> second) {
+ Class<?>[] classes = new Class<?>[times * 2];
+ for (int i = 0; i < 2 * times;) {
+ classes[i++] = first;
+ classes[i++] = second;
+ }
+ return classes;
+ }
+
+ static interface I {
+ public default void defaultMethod() {
+ System.out.println("in I.defaultMethod");
+ }
+
+ public default void overrideMe() {
+ throw new RuntimeException("should be overwritten");
+ }
+
+ private String innerPrivateMethod() {
+ return "I'm private interface method";
+ }
+ }
+
+ static class A extends B implements I {
+ public int field;
+ public void voidMethod() {
+ System.out.println("in voidMethod");
+ }
+
+ public void throwException() {
+ throw new RuntimeException("I am from throwException");
+ }
+
+ @Override
+ public void overrideMe() {
+ System.out.println("in A.overrideMe");
+ }
+
+ public double returnDouble() {
+ return 42.0d;
+ }
+
+ public int returnInt() {
+ return 42;
+ }
+
+ private int privateReturnInt() {
+ return 1042;
+ }
+
+ public static String staticMethod(A a) {
+ return "staticMethod";
+ }
+
+ public static double staticMethod() {
+ return 41.0d;
+ }
+ }
+
+ static class B {
+ private int privateReturnInt() {
+ return 9999;
+ }
+ }
+
+ static class Sums {
+ public int sum(int a) {
+ return a;
+ }
+
+ public int sum(int a1, int a2) {
+ return a1 + a2;
+ }
+
+ public int sum(int a1, int a2, int a3) {
+ return a1 + a2 + a3;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4) {
+ return a1 + a2 + a3 + a4;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5) {
+ return a1 + a2 + a3 + a4 + a5;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5, int a6) {
+ return a1 + a2 + a3 + a4 + a5 + a6;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
+ }
+
+ public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9,
+ int a10) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10;
+ }
+
+ public long sum(int a1, long a2) {
+ return a1 + a2;
+ }
+
+ public long sum(int a1, long a2, int a3, long a4) {
+ return a1 + a2 + a3 + a4;
+ }
+
+ public long sum(int a1, long a2, int a3, long a4, int a5, long a6) {
+ return a1 + a2 + a3 + a4 + a5 + a6;
+ }
+
+ public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8;
+ }
+
+ public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8, int a9,
+ long a10) {
+ return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10;
+ }
+ }
+}