diff options
author | 2023-08-22 15:00:44 +0100 | |
---|---|---|
committer | 2024-08-28 13:31:56 +0000 | |
commit | 93163edd92664c79da56ffcf5de32b7b872136ce (patch) | |
tree | dad21273d417cf020aa527171dc00465076b6434 | |
parent | 46a77ad7d269ba087c4075029d92fe00b04fb6bb (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
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; + } + } +} |