Merge "liblog: use log/log.h to access ALOG macros"
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 4180e0e..86d92ff 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -487,7 +487,7 @@
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 f00648f..9ca7b19 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -367,6 +367,12 @@
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 6366b98..7e2dd48 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -426,12 +426,12 @@
}
- // 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 @@
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 541a1c5..3bb97b6 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -2370,6 +2370,14 @@
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 9aaeadb..227ad0f 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4080,6 +4080,14 @@
__ 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 c769dec..fd51aab 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -2445,6 +2445,14 @@
}
}
+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 bc62854..ed0d997 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5154,6 +5154,14 @@
}
}
+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 1b9c6da..0ea5bb0 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3256,6 +3256,14 @@
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 a9b717d..624cf81 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -2244,6 +2244,14 @@
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 2614735..152a9ed 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -2423,6 +2423,14 @@
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 09dcefa..f6fba88 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -464,6 +464,11 @@
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 c970e5c..d7da46b 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -308,8 +308,10 @@
}
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 009d549..3cfabdd 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 @@
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 @@
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 f29e522..aef0b94 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -175,6 +175,17 @@
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 2856c3e..4f30b11 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -943,6 +943,10 @@
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 ea9a94c..064e119 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1291,6 +1291,7 @@
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 @@
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 a3fce02..ab4f9e9 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5610,7 +5610,7 @@
" 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 4d4ebdc..9e82f01 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_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_long_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 8b1e038..eba0c87 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_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)
+ 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/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 62c29cf..ea95bbc 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -468,7 +468,7 @@
* 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,97 @@
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:
+ add $(.Lhandler_table - .Lbranch_start), %eax // Add offset of handler table to index.
+ addl %ecx, %eax // Add EIP relative address of table.
+ movzbl (%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)
+ .byte 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 facd563..5b3a314 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -2394,3 +2394,78 @@
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
+ movsbq (%rcx, %rax, 1), %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)
+ .byte 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)
+ 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/art_method.h b/runtime/art_method.h
index b38508b..11dcc35 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -95,18 +95,20 @@
// This setter guarantees atomicity.
void AddAccessFlags(uint32_t flag) {
- uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed);
+ uint32_t old_access_flags;
uint32_t new_access_flags;
do {
+ old_access_flags = access_flags_.load(std::memory_order_relaxed);
new_access_flags = old_access_flags | flag;
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
// This setter guarantees atomicity.
void ClearAccessFlags(uint32_t flag) {
- uint32_t old_access_flags = access_flags_.load(std::memory_order_relaxed);
+ uint32_t old_access_flags;
uint32_t new_access_flags;
do {
+ old_access_flags = access_flags_.load(std::memory_order_relaxed);
new_access_flags = old_access_flags & ~flag;
} while (!access_flags_.compare_exchange_weak(old_access_flags, new_access_flags));
}
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index bfdddf7..e4972da 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -98,7 +98,7 @@
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 c30272e..a44f79e 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -428,6 +428,8 @@
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 @@
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 7b8974f..37f3ac9 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -358,7 +358,7 @@
}
break;
case k35c: {
- uint32_t arg[5];
+ uint32_t arg[kMaxVarArgRegs];
GetVarArgs(arg);
switch (Opcode()) {
case FILLED_NEW_ARRAY:
@@ -443,8 +443,50 @@
}
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 64030f3..2d0932a 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_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 78dad94..8ce61c1 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -106,6 +106,7 @@
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 0911aeb..4d5d6de 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 bf1d4ea..b22afc3 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 {
@@ -705,7 +708,7 @@
QuickExceptionHandler::DumpFramesWithType(self, true);
}
- mirror::Throwable* pending_exception = nullptr;
+ ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
self->PopDeoptimizationContext(&result, &pending_exception, /* out */ &from_code);
@@ -2391,4 +2394,114 @@
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();
+
+ // 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());
+ }
+
+ return static_cast<uintptr_t>(shorty[0]);
+}
+
} // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 6866abb..96e17da 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -121,10 +121,10 @@
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 @@
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 dc103e2..685aa04 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,7 +32,7 @@
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/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a81458f..b809c3e 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -140,7 +140,7 @@
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
-void QuickExceptionHandler::FindCatch(mirror::Throwable* exception) {
+void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
DCHECK(!is_deoptimization_);
if (kDebugExceptionDelivery) {
mirror::String* msg = exception->GetDetailMessage();
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 5592126..3ead7db 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -46,7 +46,7 @@
}
// Find the catch handler for the given exception.
- void FindCatch(mirror::Throwable* exception) REQUIRES_SHARED(Locks::mutator_lock_);
+ void FindCatch(ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize the stack to the upcall/some code that's not deoptimizeable. For
// every compiled frame, we create a "copy" shadow frame that will be executed
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 33c6a40..40b6d73 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -154,18 +154,18 @@
DeoptimizationContextRecord(const JValue& ret_val,
bool is_reference,
bool from_code,
- mirror::Throwable* pending_exception,
+ ObjPtr<mirror::Throwable> pending_exception,
DeoptimizationContextRecord* link)
: ret_val_(ret_val),
is_reference_(is_reference),
from_code_(from_code),
- pending_exception_(pending_exception),
+ pending_exception_(pending_exception.Ptr()),
link_(link) {}
JValue GetReturnValue() const { return ret_val_; }
bool IsReference() const { return is_reference_; }
bool GetFromCode() const { return from_code_; }
- mirror::Throwable* GetPendingException() const { return pending_exception_; }
+ ObjPtr<mirror::Throwable> GetPendingException() const { return pending_exception_; }
DeoptimizationContextRecord* GetLink() const { return link_; }
mirror::Object** GetReturnValueAsGCRoot() {
DCHECK(is_reference_);
@@ -219,7 +219,7 @@
void Thread::PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
bool from_code,
- mirror::Throwable* exception) {
+ ObjPtr<mirror::Throwable> exception) {
DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
return_value,
is_reference,
@@ -230,7 +230,7 @@
}
void Thread::PopDeoptimizationContext(JValue* result,
- mirror::Throwable** exception,
+ ObjPtr<mirror::Throwable>* exception,
bool* from_code) {
AssertHasDeoptimizationContext();
DeoptimizationContextRecord* record = tlsPtr_.deoptimization_context_stack;
@@ -434,7 +434,7 @@
Dbg::PostThreadStart(self);
// Invoke the 'run' method of our java.lang.Thread.
- mirror::Object* receiver = self->tlsPtr_.opeer;
+ ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
jmethodID mid = WellKnownClasses::java_lang_Thread_run;
ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
@@ -446,7 +446,7 @@
}
Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
- mirror::Object* thread_peer) {
+ ObjPtr<mirror::Object> thread_peer) {
ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer);
Thread* result = reinterpret_cast<Thread*>(static_cast<uintptr_t>(f->GetLong(thread_peer)));
// Sanity check that if we have a result it is either suspended or we hold the thread_list_lock_
@@ -1573,8 +1573,8 @@
}
m = m->GetInterfaceMethodIfProxy(kRuntimePointerSize);
const int kMaxRepetition = 3;
- mirror::Class* c = m->GetDeclaringClass();
- mirror::DexCache* dex_cache = c->GetDexCache();
+ ObjPtr<mirror::Class> c = m->GetDeclaringClass();
+ ObjPtr<mirror::DexCache> dex_cache = c->GetDexCache();
int line_number = -1;
if (dex_cache != nullptr) { // be tolerant of bad input
const DexFile* dex_file = dex_cache->GetDexFile();
@@ -1860,17 +1860,15 @@
void Thread::AssertNoPendingException() const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Throwable* exception = GetException();
- LOG(FATAL) << "No pending exception expected: " << exception->Dump();
+ LOG(FATAL) << "No pending exception expected: " << GetException()->Dump();
}
}
void Thread::AssertNoPendingExceptionForNewException(const char* msg) const {
if (UNLIKELY(IsExceptionPending())) {
ScopedObjectAccess soa(Thread::Current());
- mirror::Throwable* exception = GetException();
LOG(FATAL) << "Throwing new exception '" << msg << "' with unexpected pending exception: "
- << exception->Dump();
+ << GetException()->Dump();
}
}
@@ -2213,7 +2211,7 @@
// class of the ArtMethod pointers.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
StackHandleScope<1> hs(self_);
- mirror::Class* array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass);
+ ObjPtr<mirror::Class> array_class = class_linker->GetClassRoot(ClassLinker::kObjectArrayClass);
// The first element is the methods and dex pc array, the other elements are declaring classes
// for the methods to ensure classes in the stack trace don't get unloaded.
Handle<mirror::ObjectArray<mirror::Object>> trace(
@@ -2225,7 +2223,8 @@
self_->AssertPendingOOMException();
return false;
}
- mirror::PointerArray* methods_and_pcs = class_linker->AllocPointerArray(self_, depth * 2);
+ ObjPtr<mirror::PointerArray> methods_and_pcs =
+ class_linker->AllocPointerArray(self_, depth * 2);
const char* last_no_suspend_cause =
self_->StartAssertNoThreadSuspension("Building internal stack trace");
if (methods_and_pcs == nullptr) {
@@ -2255,7 +2254,7 @@
if (m->IsRuntimeMethod()) {
return true; // Ignore runtime frames (in particular callee save).
}
- mirror::PointerArray* trace_methods_and_pcs = GetTraceMethodsAndPCs();
+ ObjPtr<mirror::PointerArray> trace_methods_and_pcs = GetTraceMethodsAndPCs();
trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(count_, m, pointer_size_);
trace_methods_and_pcs->SetElementPtrSize<kTransactionActive>(
trace_methods_and_pcs->GetLength() / 2 + count_,
@@ -2268,8 +2267,8 @@
return true;
}
- mirror::PointerArray* GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) {
- return down_cast<mirror::PointerArray*>(trace_->Get(0));
+ ObjPtr<mirror::PointerArray> GetTraceMethodsAndPCs() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ return ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(trace_->Get(0)));
}
mirror::ObjectArray<mirror::Object>* GetInternalStackTrace() const {
@@ -2311,7 +2310,7 @@
build_trace_visitor.WalkStack();
mirror::ObjectArray<mirror::Object>* trace = build_trace_visitor.GetInternalStackTrace();
if (kIsDebugBuild) {
- mirror::PointerArray* trace_methods = build_trace_visitor.GetTraceMethodsAndPCs();
+ ObjPtr<mirror::PointerArray> trace_methods = build_trace_visitor.GetTraceMethodsAndPCs();
// Second half of trace_methods is dex PCs.
for (uint32_t i = 0; i < static_cast<uint32_t>(trace_methods->GetLength() / 2); ++i) {
auto* method = trace_methods->GetElementPtrSize<ArtMethod*>(
@@ -2326,7 +2325,7 @@
template jobject Thread::CreateInternalStackTrace<true>(
const ScopedObjectAccessAlreadyRunnable& soa) const;
-bool Thread::IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const {
+bool Thread::IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const {
CountStackDepthVisitor count_visitor(const_cast<Thread*>(this));
count_visitor.WalkStack();
return count_visitor.GetDepth() == exception->GetStackDepth();
@@ -2368,12 +2367,12 @@
}
for (int32_t i = 0; i < depth; ++i) {
- mirror::ObjectArray<mirror::Object>* decoded_traces =
+ ObjPtr<mirror::ObjectArray<mirror::Object>> decoded_traces =
soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>();
// Methods and dex PC trace is element 0.
DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
- mirror::PointerArray* const method_trace =
- down_cast<mirror::PointerArray*>(decoded_traces->Get(0));
+ ObjPtr<mirror::PointerArray> const method_trace =
+ ObjPtr<mirror::PointerArray>::DownCast(MakeObjPtr(decoded_traces->Get(0)));
// Prepare parameters for StackTraceElement(String cls, String method, String file, int line)
ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
@@ -2415,8 +2414,11 @@
if (method_name_object.Get() == nullptr) {
return nullptr;
}
- mirror::StackTraceElement* obj = mirror::StackTraceElement::Alloc(
- soa.Self(), class_name_object, method_name_object, source_name_object, line_number);
+ ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(),
+ class_name_object,
+ method_name_object,
+ source_name_object,
+ line_number);
if (obj == nullptr) {
return nullptr;
}
@@ -2447,7 +2449,7 @@
ThrowNewWrappedException(exception_class_descriptor, msg);
}
-static mirror::ClassLoader* GetCurrentClassLoader(Thread* self)
+static ObjPtr<mirror::ClassLoader> GetCurrentClassLoader(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = self->GetCurrentMethod(nullptr);
return method != nullptr
@@ -2725,6 +2727,7 @@
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)
@@ -2793,7 +2796,7 @@
void Thread::QuickDeliverException() {
// Get exception from thread.
- mirror::Throwable* exception = GetException();
+ ObjPtr<mirror::Throwable> exception = GetException();
CHECK(exception != nullptr);
if (exception == GetDeoptimizationException()) {
artDeoptimize(this);
@@ -2806,8 +2809,8 @@
IsExceptionThrownByCurrentMethod(exception)) {
// Instrumentation may cause GC so keep the exception object safe.
StackHandleScope<1> hs(this);
- HandleWrapper<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
- instrumentation->ExceptionCaughtEvent(this, exception);
+ HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
+ instrumentation->ExceptionCaughtEvent(this, exception.Ptr());
}
// Does instrumentation need to deoptimize the stack?
// Note: we do this *after* reporting the exception to instrumentation in case it
@@ -2869,7 +2872,7 @@
dex_pc_ = GetDexPc(abort_on_error_);
return false;
}
- mirror::Object* this_object_;
+ ObjPtr<mirror::Object> this_object_;
ArtMethod* method_;
uint32_t dex_pc_;
const bool abort_on_error_;
@@ -2884,11 +2887,8 @@
return visitor.method_;
}
-bool Thread::HoldsLock(mirror::Object* object) const {
- if (object == nullptr) {
- return false;
- }
- return object->GetLockOwnerThreadId() == GetThreadId();
+bool Thread::HoldsLock(ObjPtr<mirror::Object> object) const {
+ return object != nullptr && object->GetLockOwnerThreadId() == GetThreadId();
}
// RootVisitor parameters are: (const Object* obj, size_t vreg, const StackVisitor* visitor).
@@ -2944,7 +2944,7 @@
void VisitDeclaringClass(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS {
- mirror::Class* klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
+ ObjPtr<mirror::Class> klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
// klass can be null for runtime methods.
if (klass != nullptr) {
if (kVerifyImageObjectsMarked) {
@@ -2953,10 +2953,10 @@
/*fail_ok*/true);
if (space != nullptr && space->IsImageSpace()) {
bool failed = false;
- if (!space->GetLiveBitmap()->Test(klass)) {
+ if (!space->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image " << *space;
- } else if (!heap->GetLiveBitmap()->Test(klass)) {
+ } else if (!heap->GetLiveBitmap()->Test(klass.Ptr())) {
failed = true;
LOG(FATAL_WITHOUT_ABORT) << "Unmarked object in image through live bitmap " << *space;
}
@@ -2964,17 +2964,17 @@
GetThread()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT));
space->AsImageSpace()->DumpSections(LOG_STREAM(FATAL_WITHOUT_ABORT));
LOG(FATAL_WITHOUT_ABORT) << "Method@" << method->GetDexMethodIndex() << ":" << method
- << " klass@" << klass;
+ << " klass@" << klass.Ptr();
// Pretty info last in case it crashes.
LOG(FATAL) << "Method " << method->PrettyMethod() << " klass "
<< klass->PrettyClass();
}
}
}
- mirror::Object* new_ref = klass;
+ mirror::Object* new_ref = klass.Ptr();
visitor_(&new_ref, -1, this);
if (new_ref != klass) {
- method->CASDeclaringClass(klass, new_ref->AsClass());
+ method->CASDeclaringClass(klass.Ptr(), new_ref->AsClass());
}
}
}
@@ -3366,7 +3366,7 @@
ClearException();
ShadowFrame* shadow_frame =
PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
- mirror::Throwable* pending_exception = nullptr;
+ ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
PopDeoptimizationContext(result, &pending_exception, &from_code);
SetTopOfStack(nullptr);
diff --git a/runtime/thread.h b/runtime/thread.h
index 6308851..d54a80d 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -177,7 +177,7 @@
void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_);
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts,
- mirror::Object* thread_peer)
+ ObjPtr<mirror::Object> thread_peer)
REQUIRES(Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, jobject thread)
@@ -312,7 +312,7 @@
size_t NumberOfHeldMutexes() const;
- bool HoldsLock(mirror::Object*) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool HoldsLock(ObjPtr<mirror::Object> object) const REQUIRES_SHARED(Locks::mutator_lock_);
/*
* Changes the priority of this thread to match that of the java.lang.Thread object.
@@ -413,7 +413,7 @@
// Returns whether the given exception was thrown by the current Java method being executed
// (Note that this includes native Java methods).
- bool IsExceptionThrownByCurrentMethod(mirror::Throwable* exception) const
+ bool IsExceptionThrownByCurrentMethod(ObjPtr<mirror::Throwable> exception) const
REQUIRES_SHARED(Locks::mutator_lock_);
void SetTopOfStack(ArtMethod** top_method) {
@@ -925,9 +925,11 @@
void PushDeoptimizationContext(const JValue& return_value,
bool is_reference,
bool from_code,
- mirror::Throwable* exception)
+ ObjPtr<mirror::Throwable> exception)
REQUIRES_SHARED(Locks::mutator_lock_);
- void PopDeoptimizationContext(JValue* result, mirror::Throwable** exception, bool* from_code)
+ void PopDeoptimizationContext(JValue* result,
+ ObjPtr<mirror::Throwable>* exception,
+ bool* from_code)
REQUIRES_SHARED(Locks::mutator_lock_);
void AssertHasDeoptimizationContext()
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1416,7 +1418,7 @@
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),
@@ -1540,13 +1542,14 @@
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 715b237..25a179b 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -3106,19 +3106,16 @@
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 0000000..a423ca6
--- /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 0000000..f47ee23
--- /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 0000000..f1dbb61
--- /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 0000000..a9f1822
--- /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 0000000..20a8fec
--- /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 5806509..9e79ff4 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -40,17 +40,17 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
int value = (int) adapter.invoke("56");
if (value != 57) {
- System.out.println("Unexpected value: " + value);
+ fail("Unexpected value: " + value);
}
}
@@ -779,7 +779,7 @@
int value = (int) adapter.invoke();
if (value != 42) {
- System.out.println("Unexpected value: " + value);
+ fail("Unexpected value: " + value);
}
}
}
@@ -791,7 +791,7 @@
return;
}
- System.out.println("Unexpected arguments: " + a + ", " + b + ", " + c
+ fail("Unexpected arguments: " + a + ", " + b + ", " + c
+ ", " + d + ", " + e + ", " + f + ", " + g + ", " + h);
}
@@ -800,7 +800,7 @@
return;
}
- System.out.println("Unexpected arguments: " + a + ", " + b);
+ fail("Unexpected arguments: " + a + ", " + b);
}
public static void testPermuteArguments() throws Throwable {
@@ -893,6 +893,11 @@
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;