diff options
author | 2017-01-13 11:53:47 +0000 | |
---|---|---|
committer | 2017-01-15 15:18:07 +0000 | |
commit | ac141397dc29189ad2b2df41f8d4312246beec60 (patch) | |
tree | a2f481463a14695bf9327fd2f549878ecf30c77b | |
parent | 5c9f90c5ecf2ff6f93ada0f7b18b46d866c59ea1 (diff) |
Revert "Revert "ART: Compiler support for invoke-polymorphic.""
This reverts commit 0fb5af1c8287b1ec85c55c306a1c43820c38a337.
This takes us back to the original change and attempts to fix the
issues encountered:
- Adds transition record push/pop around artInvokePolymorphic.
- Changes X86/X64 relocations for MacSDK.
- Implements MIPS entrypoint for art_quick_invoke_polymorphic.
- Corrects size of returned reference in art_quick_invoke_polymorphic
on ARM.
Bug: 30550796,33191393
Test: art/test/run-test 953
Test: m test-art-run-test
Change-Id: Ib6b93e00b37b9d4ab743a3470ab3d77fe857cda8
41 files changed, 1324 insertions, 74 deletions
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 4180e0e6c9..86d92ff0b5 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), + EXPECT_EQ(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)), sizeof(QuickEntryPoints)); } diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index f00648f570..9ca7b1997d 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -367,6 +367,12 @@ void CodeGenerator::GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invok InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); } +void CodeGenerator::GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke) { + MoveConstant(invoke->GetLocations()->GetTemp(0), static_cast<int32_t>(invoke->GetType())); + QuickEntrypointEnum entrypoint = kQuickInvokePolymorphic; + InvokeRuntime(entrypoint, invoke, invoke->GetDexPc(), nullptr); +} + void CodeGenerator::CreateUnresolvedFieldLocationSummary( HInstruction* field_access, Primitive::Type field_type, diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 6366b9838f..7e2dd48f5c 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -426,12 +426,12 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { } - // Perfoms checks pertaining to an InvokeRuntime call. + // Performs checks pertaining to an InvokeRuntime call. void ValidateInvokeRuntime(QuickEntrypointEnum entrypoint, HInstruction* instruction, SlowPathCode* slow_path); - // Perfoms checks pertaining to an InvokeRuntimeWithoutRecordingPcInfo call. + // Performs checks pertaining to an InvokeRuntimeWithoutRecordingPcInfo call. static void ValidateInvokeRuntimeWithoutRecordingPcInfo(HInstruction* instruction, SlowPathCode* slow_path); @@ -495,6 +495,8 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { void GenerateInvokeUnresolvedRuntimeCall(HInvokeUnresolved* invoke); + void GenerateInvokePolymorphicCall(HInvokePolymorphic* invoke); + void CreateUnresolvedFieldLocationSummary( HInstruction* field_access, Primitive::Type field_type, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 541a1c5b8f..3bb97b62ec 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -2370,6 +2370,14 @@ void InstructionCodeGeneratorARM::VisitInvokeInterface(HInvokeInterface* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderARM::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9aaeadb44a..227ad0f3e4 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4080,6 +4080,14 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te __ Blr(lr); } +void LocationsBuilderARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativeStringPatch( const DexFile& dex_file, uint32_t string_index, diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index c769decaa0..fd51aab760 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -2445,6 +2445,14 @@ void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* inv } } +void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index bc62854e5d..ed0d997f23 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5154,6 +5154,14 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo } } +void LocationsBuilderMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 1b9c6da460..0ea5bb0adf 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3256,6 +3256,14 @@ void LocationsBuilderMIPS64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* in HandleInvoke(invoke); } +void LocationsBuilderMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorMIPS64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS64* codegen) { if (invoke->GetLocations()->Intrinsified()) { IntrinsicCodeGeneratorMIPS64 intrinsic(codegen); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index a9b717db4f..624cf8135e 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -2244,6 +2244,14 @@ void InstructionCodeGeneratorX86::VisitInvokeInterface(HInvokeInterface* invoke) codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderX86::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 261473505f..152a9edc2a 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2423,6 +2423,14 @@ void InstructionCodeGeneratorX86_64::VisitInvokeInterface(HInvokeInterface* invo codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); } +void LocationsBuilderX86_64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + HandleInvoke(invoke); +} + +void InstructionCodeGeneratorX86_64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { + codegen_->GenerateInvokePolymorphicCall(invoke); +} + void LocationsBuilderX86_64::VisitNeg(HNeg* neg) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(neg, LocationSummary::kNoCall); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 09dcefa02c..f6fba883bd 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -464,6 +464,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("intrinsic") << invoke->GetIntrinsic(); } + void VisitInvokePolymorphic(HInvokePolymorphic* invoke) OVERRIDE { + VisitInvoke(invoke); + StartAttributeStream("invoke_type") << "InvokePolymorphic"; + } + void VisitInstanceFieldGet(HInstanceFieldGet* iget) OVERRIDE { StartAttributeStream("field_name") << iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(), diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index c970e5cbba..d7da46bbe7 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -308,8 +308,10 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { } bool HInliner::TryInline(HInvoke* invoke_instruction) { - if (invoke_instruction->IsInvokeUnresolved()) { - return false; // Don't bother to move further if we know the method is unresolved. + if (invoke_instruction->IsInvokeUnresolved() || + invoke_instruction->IsInvokePolymorphic()) { + return false; // Don't bother to move further if we know the method is unresolved or an + // invoke-polymorphic. } ScopedObjectAccess soa(Thread::Current()); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 009d549547..3cfabddf3a 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1,3 +1,4 @@ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -906,6 +907,33 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, false /* is_unresolved */); } +bool HInstructionBuilder::BuildInvokePolymorphic(const Instruction& instruction ATTRIBUTE_UNUSED, + uint32_t dex_pc, + uint32_t method_idx, + uint32_t proto_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index) { + const char* descriptor = dex_file_->GetShorty(proto_idx); + DCHECK_EQ(1 + ArtMethod::NumArgRegisters(descriptor), number_of_vreg_arguments); + Primitive::Type return_type = Primitive::GetType(descriptor[0]); + size_t number_of_arguments = strlen(descriptor); + HInvoke* invoke = new (arena_) HInvokePolymorphic(arena_, + number_of_arguments, + return_type, + dex_pc, + method_idx); + return HandleInvoke(invoke, + number_of_vreg_arguments, + args, + register_index, + is_range, + descriptor, + nullptr /* clinit_check */, + false /* is_unresolved */); +} + bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); @@ -1915,6 +1943,37 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, break; } + case Instruction::INVOKE_POLYMORPHIC: { + uint16_t method_idx = instruction.VRegB_45cc(); + uint16_t proto_idx = instruction.VRegH_45cc(); + uint32_t number_of_vreg_arguments = instruction.VRegA_45cc(); + uint32_t args[5]; + instruction.GetVarArgs(args); + return BuildInvokePolymorphic(instruction, + dex_pc, + method_idx, + proto_idx, + number_of_vreg_arguments, + false, + args, + -1); + } + + case Instruction::INVOKE_POLYMORPHIC_RANGE: { + uint16_t method_idx = instruction.VRegB_4rcc(); + uint16_t proto_idx = instruction.VRegH_4rcc(); + uint32_t number_of_vreg_arguments = instruction.VRegA_4rcc(); + uint32_t register_index = instruction.VRegC_4rcc(); + return BuildInvokePolymorphic(instruction, + dex_pc, + method_idx, + proto_idx, + number_of_vreg_arguments, + true, + nullptr, + register_index); + } + case Instruction::NEG_INT: { Unop_12x<HNeg>(instruction, Primitive::kPrimInt, dex_pc); break; diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f29e522040..aef0b94c1f 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -175,6 +175,17 @@ class HInstructionBuilder : public ValueObject { uint32_t* args, uint32_t register_index); + // Builds an invocation node for invoke-polymorphic and returns whether the + // instruction is supported. + bool BuildInvokePolymorphic(const Instruction& instruction, + uint32_t dex_pc, + uint32_t method_idx, + uint32_t proto_idx, + uint32_t number_of_vreg_arguments, + bool is_range, + uint32_t* args, + uint32_t register_index); + // Builds a new array node and the instructions that fill it. void BuildFilledNewArray(uint32_t dex_pc, dex::TypeIndex type_index, diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 2856c3ea11..4f30b11753 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -943,6 +943,10 @@ class LSEVisitor : public HGraphVisitor { HandleInvoke(invoke); } + void VisitInvokePolymorphic(HInvokePolymorphic* invoke) OVERRIDE { + HandleInvoke(invoke); + } + void VisitClinitCheck(HClinitCheck* clinit) OVERRIDE { HandleInvoke(clinit); } diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index ea9a94c420..064e119c38 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1291,6 +1291,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(InvokeInterface, Invoke) \ M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ + M(InvokePolymorphic, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ M(LoadClass, Instruction) \ @@ -3983,6 +3984,28 @@ class HInvokeUnresolved FINAL : public HInvoke { DISALLOW_COPY_AND_ASSIGN(HInvokeUnresolved); }; +class HInvokePolymorphic FINAL : public HInvoke { + public: + HInvokePolymorphic(ArenaAllocator* arena, + uint32_t number_of_arguments, + Primitive::Type return_type, + uint32_t dex_pc, + uint32_t dex_method_index) + : HInvoke(arena, + number_of_arguments, + 0u /* number_of_other_inputs */, + return_type, + dex_pc, + dex_method_index, + nullptr, + kVirtual) {} + + DECLARE_INSTRUCTION(InvokePolymorphic); + + private: + DISALLOW_COPY_AND_ASSIGN(HInvokePolymorphic); +}; + class HInvokeStaticOrDirect FINAL : public HInvoke { public: // Requirements of this method call regarding the class diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc index a3fce02970..ab4f9e944c 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\n", + " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n", " 222: 47e0 blx ip\n", nullptr }; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 61d1607112..102c313a6a 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -2010,3 +2010,83 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg08, r8 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg09, r9 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg10, r10 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg11, r11 + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME r2 + mov r2, r9 @ pass Thread::Current + mov r3, sp @ pass SP + mov r0, #0 @ initialize 64-bit JValue as zero. + str r0, [sp, #-4]! + .cfi_adjust_cfa_offset 4 + str r0, [sp, #-4]! + .cfi_adjust_cfa_offset 4 + mov r0, sp @ pass JValue for return result as first argument. + bl artInvokePolymorphic @ artInvokePolymorphic(JValue, receiver, Thread*, SP) + sub r0, 'A' @ return value is descriptor of handle's return type. + cmp r0, 'Z' - 'A' @ check if value is in bounds of handler table + bgt .Lcleanup_and_return @ and clean-up if not. + adr r1, .Lhandler_table + tbb [r0, r1] @ branch to handler for return value based on return type. + +.Lstart_of_handlers: +.Lstore_boolean_result: + ldrb r0, [sp] @ Copy boolean value to return value of this function. + b .Lcleanup_and_return +.Lstore_char_result: + ldrh r0, [sp] @ Copy char value to return value of this function. + b .Lcleanup_and_return +.Lstore_float_result: + vldr s0, [sp] @ Copy float value from JValue result to the context restored by + vstr s0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + b .Lcleanup_and_return +.Lstore_double_result: + vldr d0, [sp] @ Copy double value from JValue result to the context restored by + vstr d0, [sp, #16] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + b .Lcleanup_and_return +.Lstore_long_result: + ldr r1, [sp, #4] @ Copy the upper bits from JValue result to the context restored by + str r1, [sp, #80] @ RESTORE_SAVE_REFS_AND_ARGS_FRAME. + // Fall-through for lower bits. +.Lstore_int_result: + ldr r0, [sp] @ Copy int value to return value of this function. + // Fall-through to clean up and return. +.Lcleanup_and_return: + add sp, #8 + .cfi_adjust_cfa_offset -8 + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r2 + +.macro HANDLER_TABLE_OFFSET handler_label + .byte (\handler_label - .Lstart_of_handlers) / 2 +.endm + +.Lhandler_table: + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_int_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_int_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_int_result) // L (object) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_int_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) +.purgem HANDLER_TABLE_OFFSET +END art_quick_invoke_polymorphic diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 8b1e0388c6..3b3783cad4 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -2567,3 +2567,82 @@ READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg26, w26, x26 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg27, w27, x27 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg28, w28, x28 READ_BARRIER_MARK_REG art_quick_read_barrier_mark_reg29, w29, x29 + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME // Save callee saves in case allocation triggers GC. + mov x2, xSELF + mov x3, sp + INCREASE_FRAME 16 // Reserve space for JValue result. + str xzr, [sp, #0] // Initialize result to zero. + mov x0, sp // Set r0 to point to result. + bl artInvokePolymorphic // ArtInvokePolymorphic(result, receiver, thread, save_area) + uxtb w0, w0 // Result is the return type descriptor as a char. + sub w0, w0, 'A' // Convert to zero based index. + cmp w0, 'Z' - 'A' + bhi .Lcleanup_and_return // Clean-up if out-of-bounds. + adrp x1, .Lhandler_table // Compute address of handler table. + add x1, x1, :lo12:.Lhandler_table + ldrb w0, [x1, w0, uxtw] // Lookup handler offset in handler table. + adr x1, .Lstart_of_handlers + add x0, x1, w0, sxtb #2 // Convert relative offset to absolute address. + br x0 // Branch to handler. + +.Lstart_of_handlers: +.Lstore_boolean_result: + ldrb w0, [sp] + b .Lcleanup_and_return +.Lstore_char_result: + ldrh w0, [sp] + b .Lcleanup_and_return +.Lstore_float_result: + ldr s0, [sp] + str s0, [sp, #32] + b .Lcleanup_and_return +.Lstore_double_result: + ldr d0, [sp] + str d0, [sp, #32] + b .Lcleanup_and_return +.Lstore_long_result: + ldr x0, [sp] + // Fall-through +.Lcleanup_and_return: + DECREASE_FRAME 16 + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 + + .section .rodata // Place handler table in read-only section away from text. + .align 2 +.macro HANDLER_TABLE_OFFSET handler_label + .byte (\handler_label - .Lstart_of_handlers) / 4 +.endm +.Lhandler_table: + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) + .text + +END art_quick_invoke_polymorphic diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 964ea563b0..3acc0a9d5b 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -2230,7 +2230,7 @@ ENTRY_NO_GP art_quick_indexof li $v0, -1 # return -1; sll $v0, $a2, 1 # $a0 += $a2 * 2 - addu $a0, $a0, $v0 # " " " " " + addu $a0, $a0, $v0 # " ditto " move $v0, $a2 # Set i to fromIndex. 1: @@ -2280,3 +2280,65 @@ ENTRY_NO_GP art_quick_string_compareto j $ra nop END art_quick_string_compareto + +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a2, rSELF # Make $a2 an alias for the current Thread. + move $a3, $sp # Make $a3 a pointer to the saved frame context. + addiu $sp, $sp, -24 # Reserve space for JValue result and 4 words for callee. + .cfi_adjust_cfa_offset 24 + sw $zero, 20($sp) # Initialize JValue result. + sw $zero, 16($sp) + addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result + la $t9, artInvokePolymorphic + jalr $t9 # (result, receiver, Thread*, context) + nop +.macro MATCH_RETURN_TYPE c, handler + li $t0, \c + beq $v0, $t0, \handler +.endm + MATCH_RETURN_TYPE 'V', .Lcleanup_and_return + MATCH_RETURN_TYPE 'L', .Lstore_int_result + MATCH_RETURN_TYPE 'I', .Lstore_int_result + MATCH_RETURN_TYPE 'J', .Lstore_long_result + MATCH_RETURN_TYPE 'B', .Lstore_int_result + MATCH_RETURN_TYPE 'C', .Lstore_char_result + MATCH_RETURN_TYPE 'D', .Lstore_double_result + MATCH_RETURN_TYPE 'F', .Lstore_float_result + MATCH_RETURN_TYPE 'S', .Lstore_int_result +.purgem MATCH_RETURN_TYPE + nop + b .Lcleanup_and_return + nop +.Lstore_boolean_result: + lbu $v0, 16($sp) # Move byte from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_char_result: + lhu $v0, 16($sp) # Move char from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_double_result: +.Lstore_float_result: + LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_long_result: + lw $v1, 20($sp) # Move upper bits from JValue result to return value register. + // Fall-through for lower bits. +.Lstore_int_result: + lw $v0, 16($sp) # Move lower bits from JValue result to return value register. + // Fall-through to clean up and return. +.Lcleanup_and_return: + addiu $sp, $sp, 24 # Remove space for JValue result and the 4 words for the callee. + .cfi_adjust_cfa_offset -24 + lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t7, 1f # Success if no exception is pending. + nop + jalr $zero, $ra + nop +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 2a18d53aea..ae786fe626 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -2105,7 +2105,7 @@ ENTRY_NO_GP art_quick_indexof li $v0,-1 # return -1; sll $v0,$a2,1 # $a0 += $a2 * 2 - daddu $a0,$a0,$v0 # " " " " " + daddu $a0,$a0,$v0 # " ditto " move $v0,$a2 # Set i to fromIndex. 1: @@ -2124,4 +2124,65 @@ ENTRY_NO_GP art_quick_indexof nop END art_quick_indexof +.extern artInvokePolymorphic +ENTRY art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME + move $a2, rSELF # Make $a2 an alias for the current Thread. + move $a3, $sp # Make $a3 a pointer to the saved frame context. + daddiu $sp, $sp, -8 # Reserve space for JValue result. + .cfi_adjust_cfa_offset 8 + sd $zero, 0($sp) # Initialize JValue result. + move $a0, $sp # Make $a0 a pointer to the JValue result + jal artInvokePolymorphic # (result, receiver, Thread*, context) + nop +.macro MATCH_RETURN_TYPE c, handler + li $t0, \c + beq $v0, $t0, \handler +.endm + MATCH_RETURN_TYPE 'V', .Lcleanup_and_return + MATCH_RETURN_TYPE 'L', .Lstore_ref_result + MATCH_RETURN_TYPE 'I', .Lstore_long_result + MATCH_RETURN_TYPE 'J', .Lstore_long_result + MATCH_RETURN_TYPE 'B', .Lstore_long_result + MATCH_RETURN_TYPE 'C', .Lstore_char_result + MATCH_RETURN_TYPE 'D', .Lstore_double_result + MATCH_RETURN_TYPE 'F', .Lstore_float_result + MATCH_RETURN_TYPE 'S', .Lstore_long_result +.purgem MATCH_RETURN_TYPE + nop + b .Lcleanup_and_return + nop +.Lstore_boolean_result: + lbu $v0, 0($sp) # Move byte from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_char_result: + lhu $v0, 0($sp) # Move char from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_double_result: +.Lstore_float_result: + l.d $f0, 0($sp) # Move double/float from JValue result to return value register. + b .Lcleanup_and_return + nop +.Lstore_ref_result: + lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register. + b .Lcleanup_and_return + nop +.Lstore_long_result: + ld $v0, 0($sp) # Move long from JValue result to return value register. + // Fall-through to clean up and return. +.Lcleanup_and_return: + daddiu $sp, $sp, 8 # Remove space for JValue result. + .cfi_adjust_cfa_offset -8 + ld $t0, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_ + RESTORE_SAVE_REFS_AND_ARGS_FRAME + bnez $t0, 1f # Success if no exception is pending. + nop + jalr $zero, $ra + nop +1: + DELIVER_PENDING_EXCEPTION +END art_quick_invoke_polymorphic + .set pop diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 62c29cf268..1d979d852e 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -468,7 +468,7 @@ TWO_ARG_RUNTIME_EXCEPTION_SAVE_EVERYTHING art_quick_throw_string_bounds, artThro * The helper will attempt to locate the target and return a 64-bit result in r0/r1 consisting * of the target Method* in r0 and method->code_ in r1. * - * If unsuccessful, the helper will return null/null will bea pending exception in the + * If unsuccessful, the helper will return null/null and there will be a pending exception in the * thread and we branch to another stub to deliver it. * * On success this wrapper will restore arguments and *jump* to the target, leaving the lr @@ -2223,5 +2223,99 @@ DEFINE_FUNCTION art_quick_osr_stub jmp *%ebx END_FUNCTION art_quick_osr_stub +DEFINE_FUNCTION art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME ebx, ebx // Save frame. + mov %esp, %edx // Remember SP. + subl LITERAL(16), %esp // Make space for JValue result. + CFI_ADJUST_CFA_OFFSET(16) + movl LITERAL(0), (%esp) // Initialize result to zero. + movl LITERAL(0), 4(%esp) + mov %esp, %eax // Store pointer to JValue result in eax. + PUSH edx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + PUSH ecx // pass receiver (method handle) + PUSH eax // pass JResult + call SYMBOL(artInvokePolymorphic) // (result, receiver, Thread*, SP) + subl LITERAL('A'), %eax // Eliminate out of bounds options + cmpb LITERAL('Z' - 'A'), %al + ja .Lcleanup_and_return + movzbl %al, %eax + call .Lput_eip_in_ecx +.Lbranch_start: + movl %ecx, %edx + add $(.Lhandler_table - .Lbranch_start), %edx // Make EDX point to handler_table. + leal (%edx, %eax, 2), %eax // Calculate address of entry in table. + movzwl (%eax), %eax // Lookup relative branch in table. + addl %ecx, %eax // Add EIP relative offset. + jmp *%eax // Branch to handler. + + // Handlers for different return types. +.Lstore_boolean_result: + movzbl 16(%esp), %eax // Copy boolean result to the accumulator. + jmp .Lcleanup_and_return +.Lstore_char_result: + movzwl 16(%esp), %eax // Copy char result to the accumulator. + jmp .Lcleanup_and_return +.Lstore_float_result: + movd 16(%esp), %xmm0 // Copy float result to the context restored by + movd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + jmp .Lcleanup_and_return +.Lstore_double_result: + movsd 16(%esp), %xmm0 // Copy double result to the context restored by + movsd %xmm0, 36(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + jmp .Lcleanup_and_return +.Lstore_long_result: + movl 20(%esp), %edx // Copy upper-word of result to the context restored by + movl %edx, 72(%esp) // RESTORE_SAVE_REFS_ONLY_FRAME. + // Fall-through for lower bits. +.Lstore_int_result: + movl 16(%esp), %eax // Copy int result to the accumulator. + // Fall-through to clean up and return. +.Lcleanup_and_return: + addl LITERAL(32), %esp // Pop arguments and stack allocated JValue result. + CFI_ADJUST_CFA_OFFSET(-32) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION + +.Lput_eip_in_ecx: // Internal function that puts address of + movl 0(%esp), %ecx // next instruction into ECX when CALL + ret + + // Handler table to handlers for given type. +.Lhandler_table: +MACRO1(HANDLER_TABLE_ENTRY, handler_label) + // NB some tools require 16-bits for relocations. Shouldn't need adjusting. + .word RAW_VAR(handler_label) - .Lbranch_start +END_MACRO + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // A + HANDLER_TABLE_ENTRY(.Lstore_int_result) // B (byte) + HANDLER_TABLE_ENTRY(.Lstore_char_result) // C (char) + HANDLER_TABLE_ENTRY(.Lstore_double_result) // D (double) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // E + HANDLER_TABLE_ENTRY(.Lstore_float_result) // F (float) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // G + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // H + HANDLER_TABLE_ENTRY(.Lstore_int_result) // I (int) + HANDLER_TABLE_ENTRY(.Lstore_long_result) // J (long) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // K + HANDLER_TABLE_ENTRY(.Lstore_int_result) // L (object) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // M + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // N + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // O + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // P + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Q + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // R + HANDLER_TABLE_ENTRY(.Lstore_int_result) // S (short) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // T + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // U + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // W + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // X + HANDLER_TABLE_ENTRY(.Lcleanup_and_return) // Y + HANDLER_TABLE_ENTRY(.Lstore_boolean_result) // Z (boolean) + +END_FUNCTION art_quick_invoke_polymorphic + // TODO: implement these! UNIMPLEMENTED art_quick_memcmp16 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index facd563428..28034c9bae 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -2394,3 +2394,79 @@ DEFINE_FUNCTION art_quick_osr_stub rep movsb // while (rcx--) { *rdi++ = *rsi++ } jmp *%rdx END_FUNCTION art_quick_osr_stub + +DEFINE_FUNCTION art_quick_invoke_polymorphic + SETUP_SAVE_REFS_AND_ARGS_FRAME // save callee saves + movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread + movq %rsp, %rcx // pass SP + subq LITERAL(16), %rsp // make space for JValue result + CFI_ADJUST_CFA_OFFSET(16) + movq LITERAL(0), (%rsp) // initialize result + movq %rsp, %rdi // store pointer to JValue result + call SYMBOL(artInvokePolymorphic) // artInvokePolymorphic(result, receiver, Thread*, SP) + // save the code pointer + subq LITERAL('A'), %rax // Convert type descriptor character value to a zero based index. + cmpb LITERAL('Z' - 'A'), %al // Eliminate out of bounds options + ja .Lcleanup_and_return + movzbq %al, %rax + leaq .Lhandler_table(%rip), %rcx // Get the address of the handler table + movslq (%rcx, %rax, 4), %rax // Lookup handler offset relative to table + addq %rcx, %rax // Add table address to yield handler address. + jmpq *%rax // Jump to handler. + +.align 4 +.Lhandler_table: // Table of type descriptor to handlers. +MACRO1(HANDLER_TABLE_OFFSET, handle_label) + // NB some tools require 32-bits for relocations. Shouldn't need adjusting. + .long RAW_VAR(handle_label) - .Lhandler_table +END_MACRO + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // A + HANDLER_TABLE_OFFSET(.Lstore_long_result) // B (byte) + HANDLER_TABLE_OFFSET(.Lstore_char_result) // C (char) + HANDLER_TABLE_OFFSET(.Lstore_double_result) // D (double) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // E + HANDLER_TABLE_OFFSET(.Lstore_float_result) // F (float) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // G + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // H + HANDLER_TABLE_OFFSET(.Lstore_long_result) // I (int) + HANDLER_TABLE_OFFSET(.Lstore_long_result) // J (long) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // K + HANDLER_TABLE_OFFSET(.Lstore_long_result) // L (object - references are compressed and only 32-bits) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // M + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // N + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // O + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // P + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Q + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // R + HANDLER_TABLE_OFFSET(.Lstore_long_result) // S (short) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // T + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // U + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // V (void) + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // W + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // X + HANDLER_TABLE_OFFSET(.Lcleanup_and_return) // Y + HANDLER_TABLE_OFFSET(.Lstore_boolean_result) // Z (boolean) + +.Lstore_boolean_result: + movzbq (%rsp), %rax // Copy boolean result to the accumulator + jmp .Lcleanup_and_return +.Lstore_char_result: + movzwq (%rsp), %rax // Copy char result to the accumulator + jmp .Lcleanup_and_return +.Lstore_float_result: + movd (%rsp), %xmm0 // Copy float result to the context restored by + movd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. + jmp .Lcleanup_and_return +.Lstore_double_result: + movsd (%rsp), %xmm0 // Copy double result to the context restored by + movsd %xmm0, 32(%rsp) // RESTORE_SAVE_REFS_AND_ARGS_FRAME. + jmp .Lcleanup_and_return +.Lstore_long_result: + movq (%rsp), %rax // Copy long result to the accumulator. + // Fall-through +.Lcleanup_and_return: + addq LITERAL(16), %rsp // Pop space for JValue result. + CFI_ADJUST_CFA_OFFSET(16) + RESTORE_SAVE_REFS_AND_ARGS_FRAME + RETURN_OR_DELIVER_PENDING_EXCEPTION +END_FUNCTION art_quick_invoke_polymorphic diff --git a/runtime/asm_support.h b/runtime/asm_support.h index bfdddf7b03..e4972da13d 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -98,7 +98,7 @@ ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET, ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET, art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.thread_local_objects. -#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + __SIZEOF_POINTER__) +#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__) ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value()) // Offset of field Thread::tlsPtr_.mterp_current_ibase. diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index c30272e114..a44f79e193 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -428,6 +428,8 @@ static bool IsValidImplicitCheck(uintptr_t addr, ArtMethod* method, const Instru case Instruction::INVOKE_VIRTUAL_RANGE: case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: + case Instruction::INVOKE_POLYMORPHIC: + case Instruction::INVOKE_POLYMORPHIC_RANGE: case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { // Without inlining, we could just check that the offset is the class offset. @@ -551,6 +553,12 @@ void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) { case Instruction::INVOKE_INTERFACE_RANGE: ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface); break; + case Instruction::INVOKE_POLYMORPHIC: + ThrowNullPointerExceptionForMethodAccess(instr->VRegB_45cc(), kVirtual); + break; + case Instruction::INVOKE_POLYMORPHIC_RANGE: + ThrowNullPointerExceptionForMethodAccess(instr->VRegB_4rcc(), kVirtual); + break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { // Since we replaced the method index, we ask the verifier to tell us which diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 7b8974fa5a..37f3ac92e9 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -358,7 +358,7 @@ std::string Instruction::DumpString(const DexFile* file) const { } break; case k35c: { - uint32_t arg[5]; + uint32_t arg[kMaxVarArgRegs]; GetVarArgs(arg); switch (Opcode()) { case FILLED_NEW_ARRAY: @@ -443,8 +443,50 @@ std::string Instruction::DumpString(const DexFile* file) const { } break; } + case k45cc: { + uint32_t arg[kMaxVarArgRegs]; + GetVarArgs(arg); + uint32_t method_idx = VRegB_45cc(); + uint32_t proto_idx = VRegH_45cc(); + os << opcode << " {"; + for (int i = 0; i < VRegA_45cc(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}"; + if (file != nullptr) { + os << ", " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << " // "; + } else { + os << ", "; + } + os << "method@" << method_idx << ", proto@" << proto_idx; + break; + } + case k4rcc: + switch (Opcode()) { + case INVOKE_POLYMORPHIC_RANGE: { + if (file != nullptr) { + uint32_t method_idx = VRegB_4rcc(); + uint32_t proto_idx = VRegH_4rcc(); + os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) + << "}, " << file->PrettyMethod(method_idx) << ", " << file->GetShorty(proto_idx) + << " // method@" << method_idx << ", proto@" << proto_idx; + break; + } + } + FALLTHROUGH_INTENDED; + default: { + uint32_t method_idx = VRegB_4rcc(); + uint32_t proto_idx = VRegH_4rcc(); + os << opcode << ", {v" << VRegC_4rcc() << " .. v" << (VRegC_4rcc() + VRegA_4rcc()) + << "}, method@" << method_idx << ", proto@" << proto_idx; + } + } + break; case k51l: os << StringPrintf("%s v%d, #%+" PRId64, opcode, VRegA_51l(), VRegB_51l()); break; - default: os << " unknown format (" << DumpHex(5) << ")"; break; } return os.str(); } diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h index 64030f36bc..2d0932a0c4 100644 --- a/runtime/entrypoints/quick/quick_default_externs.h +++ b/runtime/entrypoints/quick/quick_default_externs.h @@ -109,8 +109,13 @@ extern "C" void art_quick_invoke_direct_trampoline_with_access_check(uint32_t, v extern "C" void art_quick_invoke_interface_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_static_trampoline_with_access_check(uint32_t, void*); extern "C" void art_quick_invoke_super_trampoline_with_access_check(uint32_t, void*); + extern "C" void art_quick_invoke_virtual_trampoline_with_access_check(uint32_t, void*); +// Invoke polymorphic entrypoint. Return type is dynamic and may be void, a primitive value, or +// reference return type. +extern "C" void art_quick_invoke_polymorphic(uint32_t, void*); + // Thread entrypoints. extern "C" void art_quick_test_suspend(); diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h index 78dad94dfe..8ce61c1021 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -106,6 +106,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) art_quick_invoke_super_trampoline_with_access_check; qpoints->pInvokeVirtualTrampolineWithAccessCheck = art_quick_invoke_virtual_trampoline_with_access_check; + qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic; // Thread qpoints->pTestSuspend = art_quick_test_suspend; diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h index 0911aeb0f4..4d5d6de41d 100644 --- a/runtime/entrypoints/quick/quick_entrypoints_list.h +++ b/runtime/entrypoints/quick/quick_entrypoints_list.h @@ -133,6 +133,7 @@ V(InvokeStaticTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeSuperTrampolineWithAccessCheck, void, uint32_t, void*) \ V(InvokeVirtualTrampolineWithAccessCheck, void, uint32_t, void*) \ + V(InvokePolymorphic, 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 a3e5b552b5..8f7f74680a 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -27,10 +27,12 @@ #include "imtable-inl.h" #include "interpreter/interpreter.h" #include "linear_alloc.h" +#include "method_handles.h" #include "method_reference.h" #include "mirror/class-inl.h" #include "mirror/dex_cache-inl.h" #include "mirror/method.h" +#include "mirror/method_handle_impl.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "oat_quick_method_header.h" @@ -39,6 +41,7 @@ #include "scoped_thread_state_change-inl.h" #include "stack.h" #include "debugger.h" +#include "well_known_classes.h" namespace art { @@ -2391,4 +2394,121 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(uint32_t deadbeef ATTRIBUT reinterpret_cast<uintptr_t>(method)); } +// Returns shorty type so the caller can determine how to put |result| +// into expected registers. The shorty type is static so the compiler +// could call different flavors of this code path depending on the +// shorty type though this would require different entry points for +// each type. +extern "C" uintptr_t artInvokePolymorphic( + JValue* result, + mirror::Object* raw_method_handle, + Thread* self, + ArtMethod** sp) + REQUIRES_SHARED(Locks::mutator_lock_) { + ScopedQuickEntrypointChecks sqec(self); + DCHECK_EQ(*sp, Runtime::Current()->GetCalleeSaveMethod(Runtime::kSaveRefsAndArgs)); + + // Start new JNI local reference state + 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. + ArtMethod* caller_method = QuickArgumentVisitor::GetCallingMethod(sp); + uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp); + const DexFile::CodeItem* code = caller_method->GetCodeItem(); + const Instruction* inst = Instruction::At(&code->insns_[dex_pc]); + DCHECK(inst->Opcode() == Instruction::INVOKE_POLYMORPHIC || + inst->Opcode() == Instruction::INVOKE_POLYMORPHIC_RANGE); + const DexFile* dex_file = caller_method->GetDexFile(); + const uint32_t proto_idx = inst->VRegH(); + const char* shorty = dex_file->GetShorty(proto_idx); + const size_t shorty_length = strlen(shorty); + static const bool kMethodIsStatic = false; // invoke() and invokeExact() are not static. + RememberForGcArgumentVisitor gc_visitor(sp, kMethodIsStatic, shorty, shorty_length, &soa); + gc_visitor.Visit(); + + // Wrap raw_method_handle in a Handle for safety. + StackHandleScope<5> hs(self); + Handle<mirror::MethodHandleImpl> method_handle( + hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle)))); + raw_method_handle = nullptr; + self->EndAssertNoThreadSuspension(old_cause); + + // Resolve method - it's either MethodHandle.invoke() or MethodHandle.invokeExact(). + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + ArtMethod* resolved_method = linker->ResolveMethod<ClassLinker::kForceICCECheck>(self, + inst->VRegB(), + caller_method, + kVirtual); + DCHECK((resolved_method == + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact)) || + (resolved_method == + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invoke))); + if (UNLIKELY(method_handle.IsNull())) { + ThrowNullPointerExceptionForMethodAccess(resolved_method, InvokeType::kVirtual); + return static_cast<uintptr_t>('V'); + } + + Handle<mirror::Class> caller_class(hs.NewHandle(caller_method->GetDeclaringClass())); + Handle<mirror::MethodType> method_type(hs.NewHandle(linker->ResolveMethodType( + *dex_file, proto_idx, + hs.NewHandle<mirror::DexCache>(caller_class->GetDexCache()), + hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader())))); + // This implies we couldn't resolve one or more types in this method handle. + if (UNLIKELY(method_type.IsNull())) { + CHECK(self->IsExceptionPending()); + return static_cast<uintptr_t>('V'); + } + + DCHECK_EQ(ArtMethod::NumArgRegisters(shorty) + 1u, (uint32_t)inst->VRegA()); + DCHECK_EQ(resolved_method->IsStatic(), kMethodIsStatic); + + // 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, /* link */ nullptr, resolved_method, dex_pc); + ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get(); + ScopedStackedShadowFramePusher + frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction); + BuildQuickShadowFrameVisitor shadow_frame_builder(sp, + kMethodIsStatic, + shorty, + strlen(shorty), + shadow_frame, + first_arg); + shadow_frame_builder.VisitArguments(); + + // Push a transition back into managed code onto the linked list in thread. + ManagedStack fragment; + self->PushManagedStackFragment(&fragment); + + // Call DoInvokePolymorphic with |is_range| = true, as shadow frame has argument registers in + // consecutive order. + uint32_t unused_args[Instruction::kMaxVarArgRegs] = {}; + uint32_t first_callee_arg = first_arg + 1; + const bool do_assignability_check = false; + if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self, + resolved_method, + *shadow_frame, + method_handle, + method_type, + unused_args, + first_callee_arg, + result)) { + DCHECK(self->IsExceptionPending()); + } + + // Pop transition record. + self->PopManagedStackFragment(fragment); + + return static_cast<uintptr_t>(shorty[0]); +} + } // namespace art diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index 6866abb6ae..96e17daa08 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -121,10 +121,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { sizeof(Thread::tls_ptr_sized_values::active_suspend_barriers)); // Skip across the entrypoints structures. - - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_pos, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_pos, thread_local_end, sizeof(void*)); - EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_end, thread_local_start, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_start, thread_local_objects, sizeof(void*)); + EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_objects, mterp_current_ibase, sizeof(size_t)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_current_ibase, mterp_default_ibase, sizeof(void*)); EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, mterp_default_ibase, mterp_alt_ibase, sizeof(void*)); @@ -286,6 +286,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeSuperTrampolineWithAccessCheck, pInvokeVirtualTrampolineWithAccessCheck, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokeVirtualTrampolineWithAccessCheck, + pInvokePolymorphic, sizeof(void*)); + EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInvokePolymorphic, pTestSuspend, sizeof(void*)); EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pTestSuspend, pDeliverException, sizeof(void*)); diff --git a/runtime/oat.h b/runtime/oat.h index dc103e2b52..685aa04313 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '5', '\0' }; // alloc entrypoints change + static constexpr uint8_t kOatVersion[] = { '0', '9', '6', '\0' }; // invoke-polymorphic entry static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/thread.cc b/runtime/thread.cc index bdd4ca6721..40b6d73d94 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2727,6 +2727,7 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { QUICK_ENTRY_POINT_INFO(pInvokeStaticTrampolineWithAccessCheck) QUICK_ENTRY_POINT_INFO(pInvokeSuperTrampolineWithAccessCheck) QUICK_ENTRY_POINT_INFO(pInvokeVirtualTrampolineWithAccessCheck) + QUICK_ENTRY_POINT_INFO(pInvokePolymorphic) QUICK_ENTRY_POINT_INFO(pTestSuspend) QUICK_ENTRY_POINT_INFO(pDeliverException) QUICK_ENTRY_POINT_INFO(pThrowArrayBounds) diff --git a/runtime/thread.h b/runtime/thread.h index a3ef9bc0a3..d54a80df99 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -1418,7 +1418,7 @@ class Thread { stacked_shadow_frame_record(nullptr), deoptimization_context_stack(nullptr), frame_id_to_shadow_frame(nullptr), name(nullptr), pthread_self(0), last_no_thread_suspension_cause(nullptr), checkpoint_function(nullptr), - thread_local_start(nullptr), thread_local_pos(nullptr), thread_local_end(nullptr), + thread_local_pos(nullptr), thread_local_end(nullptr), thread_local_start(nullptr), thread_local_objects(0), mterp_current_ibase(nullptr), mterp_default_ibase(nullptr), mterp_alt_ibase(nullptr), thread_local_alloc_stack_top(nullptr), thread_local_alloc_stack_end(nullptr), nested_signal_state(nullptr), @@ -1542,13 +1542,14 @@ class Thread { JniEntryPoints jni_entrypoints; QuickEntryPoints quick_entrypoints; - // Thread-local allocation pointer. Moved here to force alignment for thread_local_pos on ARM. - uint8_t* thread_local_start; // thread_local_pos and thread_local_end must be consecutive for ldrd and are 8 byte aligned for // potentially better performance. uint8_t* thread_local_pos; uint8_t* thread_local_end; + // Thread-local allocation pointer. + uint8_t* thread_local_start; + size_t thread_local_objects; // Mterp jump table bases. diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 715b237a6a..25a179bd32 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3106,19 +3106,16 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; } const uint32_t proto_idx = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); - const char* descriptor = + const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); const RegType& return_type = - reg_types_.FromDescriptor(GetClassLoader(), descriptor, false); + reg_types_.FromDescriptor(GetClassLoader(), return_descriptor, false); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); } - // TODO(oth): remove when compiler support is available. - Fail(VERIFY_ERROR_FORCE_INTERPRETER) - << "invoke-polymorphic is not supported by compiler"; - have_pending_experimental_failure_ = true; + just_set_result = true; break; } case Instruction::NEG_INT: diff --git a/test/953-invoke-polymorphic-compiler/build b/test/953-invoke-polymorphic-compiler/build new file mode 100755 index 0000000000..a423ca6b4e --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/953-invoke-polymorphic-compiler/expected.txt b/test/953-invoke-polymorphic-compiler/expected.txt new file mode 100644 index 0000000000..f47ee23fd8 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/expected.txt @@ -0,0 +1,25 @@ +Running Main.Min2Print2([33, -4]) +Running Main.Min2Print2([-4, 33]) +Running Main.Min2Print3([33, -4, 17]) +Running Main.Min2Print3([-4, 17, 33]) +Running Main.Min2Print3([17, 33, -4]) +Running Main.Min2Print6([33, -4, 77, 88, 99, 111]) +Running Main.Min2Print6([-4, 77, 88, 99, 111, 33]) +Running Main.Min2Print6([77, 88, 99, 111, 33, -4]) +Running Main.Min2Print6([88, 99, 111, 33, -4, 77]) +Running Main.Min2Print6([99, 111, 33, -4, 77, 88]) +Running Main.Min2Print6([111, 33, -4, 77, 88, 99]) +Running Main.Min2Print26([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]) +Running Main.Min2Print26([25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]) +Running Main.Min2Print26([24, 25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) +BasicTest done. +$opt$ReturnBooleanTest done. +$opt$ReturnCharTest done. +$opt$ReturnByteTest done. +$opt$ReturnShortTest done. +$opt$ReturnIntTest done. +$opt$ReturnLongTest done. +$opt$ReturnFloatTest done. +$opt$ReturnDoubleTest done. +$opt$ReturnStringTest done. +ReturnValuesTest done. diff --git a/test/953-invoke-polymorphic-compiler/info.txt b/test/953-invoke-polymorphic-compiler/info.txt new file mode 100644 index 0000000000..f1dbb61640 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/info.txt @@ -0,0 +1,3 @@ +Tests for method handle invocations. + +NOTE: needs to run under ART or a Java 8 Language runtime and compiler. diff --git a/test/953-invoke-polymorphic-compiler/run b/test/953-invoke-polymorphic-compiler/run new file mode 100755 index 0000000000..a9f182288c --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/run @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# make us exit on a failure +set -e + +./default-run "$@" --experimental method-handles diff --git a/test/953-invoke-polymorphic-compiler/src/Main.java b/test/953-invoke-polymorphic-compiler/src/Main.java new file mode 100644 index 0000000000..20a8fec112 --- /dev/null +++ b/test/953-invoke-polymorphic-compiler/src/Main.java @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.invoke.WrongMethodTypeException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Main { + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertFalse(boolean value) { + if (value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long i1, long i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals l1: " + i1 + ", l2: " + i2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + public static void fail() { + System.err.println("fail"); + Thread.dumpStack(); + } + + public static void fail(String message) { + System.err.println("fail: " + message); + Thread.dumpStack(); + } + + public static int Min2Print2(int a, int b) { + int[] values = new int[] { a, b }; + System.err.println("Running Main.Min2Print2(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print3(int a, int b, int c) { + int[] values = new int[] { a, b, c }; + System.err.println("Running Main.Min2Print3(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print6(int a, int b, int c, int d, int e, int f) { + int[] values = new int[] { a, b, c, d, e, f }; + System.err.println("Running Main.Min2Print6(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static int Min2Print26(int a, int b, int c, int d, + int e, int f, int g, int h, + int i, int j, int k, int l, + int m, int n, int o, int p, + int q, int r, int s, int t, + int u, int v, int w, int x, + int y, int z) { + int[] values = new int[] { a, b, c, d, e, f, g, h, i, j, k, l, m, + n, o, p, q, r, s, t, u, v, w, x, y, z }; + System.err.println("Running Main.Min2Print26(" + Arrays.toString(values) + ")"); + return a > b ? a : b; + } + + public static void $opt$BasicTest() throws Throwable { + MethodHandle mh; + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print2", MethodType.methodType(int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4), 33); + assertEquals((int) mh.invokeExact(-4, 33), 33); + + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print3", + MethodType.methodType(int.class, int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4, 17), 33); + assertEquals((int) mh.invokeExact(-4, 17, 33), 17); + assertEquals((int) mh.invokeExact(17, 33, -4), 33); + + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print6", + MethodType.methodType( + int.class, int.class, int.class, int.class, int.class, int.class, int.class)); + assertEquals((int) mh.invokeExact(33, -4, 77, 88, 99, 111), 33); + try { + // Too few arguments + assertEquals((int) mh.invokeExact(33, -4, 77, 88), 33); + fail("No WMTE for too few arguments"); + } catch (WrongMethodTypeException e) {} + try { + // Too many arguments + assertEquals((int) mh.invokeExact(33, -4, 77, 88, 89, 90, 91), 33); + fail("No WMTE for too many arguments"); + } catch (WrongMethodTypeException e) {} + assertEquals((int) mh.invokeExact(-4, 77, 88, 99, 111, 33), 77); + assertEquals((int) mh.invokeExact(77, 88, 99, 111, 33, -4), 88); + assertEquals((int) mh.invokeExact(88, 99, 111, 33, -4, 77), 99); + assertEquals((int) mh.invokeExact(99, 111, 33, -4, 77, 88), 111); + assertEquals((int) mh.invokeExact(111, 33, -4, 77, 88, 99), 111); + + // A preposterous number of arguments. + mh = MethodHandles.lookup().findStatic( + Main.class, "Min2Print26", + MethodType.methodType( + // Return-type + int.class, + // Arguments + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class, + int.class, int.class)); + assertEquals(1, (int) mh.invokeExact(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25)); + assertEquals(25, (int) mh.invokeExact(25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24)); + assertEquals(25, (int) mh.invokeExact(24, 25, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23)); + + try { + // Wrong argument type + mh.invokeExact("a"); + fail("No WMTE for wrong arguments"); + } catch (WrongMethodTypeException wmte) {} + + try { + // Invoke on null handle. + MethodHandle mh0 = null; + mh0.invokeExact("bad"); + fail("No NPE for you"); + } catch (NullPointerException npe) {} + + System.err.println("BasicTest done."); + } + + private static boolean And(boolean lhs, boolean rhs) { + return lhs & rhs; + } + + private static boolean Xor(boolean lhs, boolean rhs) { + return lhs ^ rhs; + } + + private static String Multiply(String value, int n) { + String result = ""; + for (int i = 0; i < n; ++i) { + result = value + result; + } + return result; + } + + private static byte Multiply(byte value, byte n) { + return (byte)(value * n); + } + + private static short Multiply(short value, short n) { + return (short)(value * n); + } + + private static int Multiply(int value, int n) { + return value * n; + } + + private static long Multiply(long value, long n) { + return value * n; + } + + private static float Multiply(float value, float n) { + return value * n; + } + + private static double Multiply(double value, double n) { + return value * n; + } + + private static char Next(char c) { + return (char)(c + 1); + } + + public static void $opt$ReturnBooleanTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = + lookup.findStatic(Main.class, "And", + MethodType.methodType(boolean.class, boolean.class, boolean.class)); + assertEquals(true, (boolean) mh.invokeExact(true, true)); + assertEquals(false, (boolean) mh.invokeExact(true, false)); + assertEquals(false, (boolean) mh.invokeExact(false, true)); + assertEquals(false, (boolean) mh.invokeExact(false, false)); + assertEquals(true, (boolean) mh.invoke(true, true)); + assertEquals(false, (boolean) mh.invoke(true, false)); + assertEquals(false, (boolean) mh.invoke(false, true)); + assertEquals(false, (boolean) mh.invoke(false, false)); + + mh = lookup.findStatic(Main.class, "Xor", + MethodType.methodType(boolean.class, boolean.class, boolean.class)); + assertEquals(false, (boolean) mh.invokeExact(true, true)); + assertEquals(true, (boolean) mh.invokeExact(true, false)); + assertEquals(true, (boolean) mh.invokeExact(false, true)); + assertEquals(false, (boolean) mh.invokeExact(false, false)); + assertEquals(false, (boolean) mh.invoke(true, true)); + assertEquals(true, (boolean) mh.invoke(true, false)); + assertEquals(true, (boolean) mh.invoke(false, true)); + assertEquals(false, (boolean) mh.invoke(false, false)); + + System.err.println("$opt$ReturnBooleanTest done."); + } + + public static void $opt$ReturnCharTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Next", + MethodType.methodType(char.class, char.class)); + assertEquals('B', (char) mh.invokeExact('A')); + assertEquals((char) -55, (char) mh.invokeExact((char) -56)); + System.err.println("$opt$ReturnCharTest done."); + } + + public static void $opt$ReturnByteTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(byte.class, byte.class, byte.class)); + assertEquals((byte) 30, (byte) mh.invokeExact((byte) 10, (byte) 3)); + assertEquals((byte) -90, (byte) mh.invoke((byte) -10, (byte) 9)); + System.err.println("$opt$ReturnByteTest done."); + } + + public static void $opt$ReturnShortTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(short.class, short.class, short.class)); + assertEquals((short) 3000, (short) mh.invokeExact((short) 1000, (short) 3)); + assertEquals((short) -3000, (short) mh.invoke((short) -1000, (short) 3)); + System.err.println("$opt$ReturnShortTest done."); + } + + public static void $opt$ReturnIntTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(int.class, int.class, int.class)); + assertEquals(3_000_000, (int) mh.invokeExact(1_000_000, 3)); + assertEquals(-3_000_000, (int) mh.invoke(-1_000, 3_000)); + System.err.println("$opt$ReturnIntTest done."); + } + + public static void $opt$ReturnLongTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(long.class, long.class, long.class)); + assertEquals(4_294_967_295_000L, (long) mh.invokeExact(1000L, 4_294_967_295L)); + assertEquals(-4_294_967_295_000L, (long) mh.invoke(-1000L, 4_294_967_295L)); + System.err.println("$opt$ReturnLongTest done."); + } + + public static void $opt$ReturnFloatTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(float.class, float.class, float.class)); + assertEquals(3.0F, (float) mh.invokeExact(1000.0F, 3e-3F)); + assertEquals(-3.0F, (float) mh.invoke(-1000.0F, 3e-3F)); + System.err.println("$opt$ReturnFloatTest done."); + } + + public static void $opt$ReturnDoubleTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(double.class, double.class, double.class)); + assertEquals(3033000.0, (double) mh.invokeExact(1000.0, 3.033e3)); + assertEquals(-3033000.0, (double) mh.invoke(-1000.0, 3.033e3)); + System.err.println("$opt$ReturnDoubleTest done."); + } + + public static void $opt$ReturnStringTest() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findStatic(Main.class, "Multiply", + MethodType.methodType(String.class, String.class, int.class)); + assertEquals("100010001000", (String) mh.invokeExact("1000", 3)); + assertEquals("100010001000", (String) mh.invoke("1000", 3)); + System.err.println("$opt$ReturnStringTest done."); + } + + public static void ReturnValuesTest() throws Throwable { + $opt$ReturnBooleanTest(); + $opt$ReturnCharTest(); + $opt$ReturnByteTest(); + $opt$ReturnShortTest(); + $opt$ReturnIntTest(); + $opt$ReturnLongTest(); + $opt$ReturnFloatTest(); + $opt$ReturnDoubleTest(); + $opt$ReturnStringTest(); + System.err.println("ReturnValuesTest done."); + } + + static class ValueHolder { + public boolean m_z; + public static boolean s_z; + } + + public static void $opt$AccessorsTest() throws Throwable { + ValueHolder valueHolder = new ValueHolder(); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + + MethodHandle setMember = lookup.findSetter(ValueHolder.class, "m_z", boolean.class); + MethodHandle getMember = lookup.findGetter(ValueHolder.class, "m_z", boolean.class); + MethodHandle setStatic = lookup.findStaticSetter(ValueHolder.class, "s_z", boolean.class); + MethodHandle getStatic = lookup.findStaticGetter(ValueHolder.class, "s_z", boolean.class); + + boolean [] values = { false, true, false, true, false }; + for (boolean value : values) { + assertEquals((boolean) getStatic.invoke(), ValueHolder.s_z); + setStatic.invoke(value); + ValueHolder.s_z = value; + assertEquals(ValueHolder.s_z, value); + assertEquals((boolean) getStatic.invoke(), value); + + assertEquals((boolean) getMember.invoke(valueHolder), valueHolder.m_z); + setMember.invoke(valueHolder, value); + valueHolder.m_z = value; + assertEquals(valueHolder.m_z, value); + assertEquals((boolean) getMember.invoke(valueHolder), value); + } + } + + public static void main(String[] args) throws Throwable { + $opt$BasicTest(); + ReturnValuesTest(); + $opt$AccessorsTest(); + } +} diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java index 5806509fc3..9e79ff4c10 100644 --- a/test/957-methodhandle-transforms/src/Main.java +++ b/test/957-methodhandle-transforms/src/Main.java @@ -40,17 +40,17 @@ public class Main { IllegalArgumentException.class); if (handle.type().returnType() != String.class) { - System.out.println("Unexpected return type for handle: " + handle + + fail("Unexpected return type for handle: " + handle + " [ " + handle.type() + "]"); } final IllegalArgumentException iae = new IllegalArgumentException("boo!"); try { handle.invoke(iae); - System.out.println("Expected an exception of type: java.lang.IllegalArgumentException"); + fail("Expected an exception of type: java.lang.IllegalArgumentException"); } catch (IllegalArgumentException expected) { if (expected != iae) { - System.out.println("Wrong exception: expected " + iae + " but was " + expected); + fail("Wrong exception: expected " + iae + " but was " + expected); } } } @@ -262,7 +262,7 @@ public class Main { array[0] = 42; int value = (int) getter.invoke(array, 0); if (value != 42) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } try { @@ -284,7 +284,7 @@ public class Main { array[0] = 42; long value = (long) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -294,7 +294,7 @@ public class Main { array[0] = 42; short value = (short) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -304,7 +304,7 @@ public class Main { array[0] = 42; char value = (char) getter.invoke(array, 0); if (value != 42l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -314,7 +314,7 @@ public class Main { array[0] = (byte) 0x8; byte value = (byte) getter.invoke(array, 0); if (value != (byte) 0x8) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -324,7 +324,7 @@ public class Main { array[0] = true; boolean value = (boolean) getter.invoke(array, 0); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -334,7 +334,7 @@ public class Main { array[0] = 42.0f; float value = (float) getter.invoke(array, 0); if (value != 42.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -344,7 +344,7 @@ public class Main { array[0] = 42.0; double value = (double) getter.invoke(array, 0); if (value != 42.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -372,10 +372,10 @@ public class Main { setter.invoke(array, 1, 43); if (array[0] != 42) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } if (array[1] != 43) { - System.out.println("Unexpected value: " + array[1]); + fail("Unexpected value: " + array[1]); } try { @@ -396,7 +396,7 @@ public class Main { long[] array = new long[1]; setter.invoke(array, 0, 42l); if (array[0] != 42l) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -405,7 +405,7 @@ public class Main { short[] array = new short[1]; setter.invoke(array, 0, (short) 42); if (array[0] != 42l) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -414,7 +414,7 @@ public class Main { char[] array = new char[1]; setter.invoke(array, 0, (char) 42); if (array[0] != 42) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -423,7 +423,7 @@ public class Main { byte[] array = new byte[1]; setter.invoke(array, 0, (byte) 0x8); if (array[0] != (byte) 0x8) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -432,7 +432,7 @@ public class Main { boolean[] array = new boolean[1]; setter.invoke(array, 0, true); if (!array[0]) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -441,7 +441,7 @@ public class Main { float[] array = new float[1]; setter.invoke(array, 0, 42.0f); if (array[0] != 42.0f) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -450,7 +450,7 @@ public class Main { double[] array = new double[1]; setter.invoke(array, 0, 42.0); if (array[0] != 42.0) { - System.out.println("Unexpected value: " + array[0]); + fail("Unexpected value: " + array[0]); } } @@ -471,7 +471,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(boolean.class); boolean value = (boolean) identity.invoke(false); if (value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -479,7 +479,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(byte.class); byte value = (byte) identity.invoke((byte) 0x8); if (value != (byte) 0x8) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -487,7 +487,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(char.class); char value = (char) identity.invoke((char) -56); if (value != (char) -56) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -495,7 +495,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(short.class); short value = (short) identity.invoke((short) -59); if (value != (short) -59) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + Short.toString(value)); } } @@ -503,7 +503,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(int.class); int value = (int) identity.invoke(52); if (value != 52) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -511,7 +511,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(long.class); long value = (long) identity.invoke(-76l); if (value != (long) -76) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -519,7 +519,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(float.class); float value = (float) identity.invoke(56.0f); if (value != (float) 56.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -527,7 +527,7 @@ public class Main { MethodHandle identity = MethodHandles.identity(double.class); double value = (double) identity.invoke((double) 72.0); if (value != (double) 72.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -544,28 +544,28 @@ public class Main { MethodHandle constant = MethodHandles.constant(int.class, 56); int value = (int) constant.invoke(); if (value != 56) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // short constant values are converted to int. constant = MethodHandles.constant(int.class, (short) 52); value = (int) constant.invoke(); if (value != 52) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // char constant values are converted to int. constant = MethodHandles.constant(int.class, (char) 'b'); value = (int) constant.invoke(); if (value != (int) 'b') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // int constant values are converted to int. constant = MethodHandles.constant(int.class, (byte) 0x1); value = (int) constant.invoke(); if (value != 1) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } // boolean, float, double and long primitive constants are not convertible @@ -600,13 +600,13 @@ public class Main { MethodHandle constant = MethodHandles.constant(long.class, 56l); long value = (long) constant.invoke(); if (value != 56l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } constant = MethodHandles.constant(long.class, (int) 56); value = (long) constant.invoke(); if (value != 56l) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -615,7 +615,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(byte.class, (byte) 0x12); byte value = (byte) constant.invoke(); if (value != (byte) 0x12) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -624,7 +624,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(boolean.class, true); boolean value = (boolean) constant.invoke(); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -633,7 +633,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(char.class, 'f'); char value = (char) constant.invoke(); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -642,7 +642,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(short.class, (short) 123); short value = (short) constant.invoke(); if (value != (short) 123) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -651,7 +651,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(float.class, 56.0f); float value = (float) constant.invoke(); if (value != 56.0f) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -660,7 +660,7 @@ public class Main { MethodHandle constant = MethodHandles.constant(double.class, 256.0); double value = (double) constant.invoke(); if (value != 256.0) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -678,13 +678,13 @@ public class Main { char value = (char) stringCharAt.invoke("foo", 0); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } MethodHandle bound = stringCharAt.bindTo("foo"); value = (char) bound.invoke(0); if (value != 'f') { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } try { @@ -706,7 +706,7 @@ public class Main { bound = integerParseInt.bindTo("78452"); int intValue = (int) bound.invoke(); if (intValue != 78452) { - System.out.println("Unexpected value: " + intValue); + fail("Unexpected value: " + intValue); } } @@ -745,11 +745,11 @@ public class Main { boolean value = (boolean) adapter.invoke((int) 42); if (!value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } value = (boolean) adapter.invoke((int) 43); if (value) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -764,7 +764,7 @@ public class Main { int value = (int) adapter.invoke("56"); if (value != 57) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } @@ -779,7 +779,7 @@ public class Main { int value = (int) adapter.invoke(); if (value != 42) { - System.out.println("Unexpected value: " + value); + fail("Unexpected value: " + value); } } } @@ -791,7 +791,7 @@ public class Main { return; } - System.out.println("Unexpected arguments: " + a + ", " + b + ", " + c + fail("Unexpected arguments: " + a + ", " + b + ", " + c + ", " + d + ", " + e + ", " + f + ", " + g + ", " + h); } @@ -800,7 +800,7 @@ public class Main { return; } - System.out.println("Unexpected arguments: " + a + ", " + b); + fail("Unexpected arguments: " + a + ", " + b); } public static void testPermuteArguments() throws Throwable { @@ -893,6 +893,11 @@ public class Main { Thread.dumpStack(); } + public static void fail(String message) { + System.out.println("fail: " + message); + Thread.dumpStack(); + } + public static void assertEquals(String s1, String s2) { if (s1 == s2) { return; |