Merge "Refactor compact dex writing"
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 60c7a8f..d2cfa4f 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -461,6 +461,10 @@
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS"; }
+ HBasicBlock* GetSuccessor() const {
+ return successor_;
+ }
+
private:
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -1996,8 +2000,19 @@
void InstructionCodeGeneratorMIPS::GenerateSuspendCheck(HSuspendCheck* instruction,
HBasicBlock* successor) {
SuspendCheckSlowPathMIPS* slow_path =
- new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS(instruction, successor);
- codegen_->AddSlowPath(slow_path);
+ down_cast<SuspendCheckSlowPathMIPS*>(instruction->GetSlowPath());
+
+ if (slow_path == nullptr) {
+ slow_path =
+ new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS(instruction, successor);
+ instruction->SetSlowPath(slow_path);
+ codegen_->AddSlowPath(slow_path);
+ if (successor != nullptr) {
+ DCHECK(successor->IsLoopHeader());
+ }
+ } else {
+ DCHECK_EQ(slow_path->GetSuccessor(), successor);
+ }
__ LoadFromOffset(kLoadUnsignedHalfword,
TMP,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5292638..28ca7cb 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -415,6 +415,10 @@
const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathMIPS64"; }
+ HBasicBlock* GetSuccessor() const {
+ return successor_;
+ }
+
private:
// If not null, the block to branch to after the suspend check.
HBasicBlock* const successor_;
@@ -1834,8 +1838,19 @@
void InstructionCodeGeneratorMIPS64::GenerateSuspendCheck(HSuspendCheck* instruction,
HBasicBlock* successor) {
SuspendCheckSlowPathMIPS64* slow_path =
- new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS64(instruction, successor);
- codegen_->AddSlowPath(slow_path);
+ down_cast<SuspendCheckSlowPathMIPS64*>(instruction->GetSlowPath());
+
+ if (slow_path == nullptr) {
+ slow_path =
+ new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathMIPS64(instruction, successor);
+ instruction->SetSlowPath(slow_path);
+ codegen_->AddSlowPath(slow_path);
+ if (successor != nullptr) {
+ DCHECK(successor->IsLoopHeader());
+ }
+ } else {
+ DCHECK_EQ(slow_path->GetSuccessor(), successor);
+ }
__ LoadFromOffset(kLoadUnsignedHalfword,
TMP,
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 4a8fbf2..98ccce7 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -3203,6 +3203,26 @@
}
}
+// static boolean java.lang.Thread.interrupted()
+void IntrinsicLocationsBuilderMIPS::VisitThreadInterrupted(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorMIPS::VisitThreadInterrupted(HInvoke* invoke) {
+ MipsAssembler* assembler = GetAssembler();
+ Register out = invoke->GetLocations()->Out().AsRegister<Register>();
+ int32_t offset = Thread::InterruptedOffset<kMipsPointerSize>().Int32Value();
+ __ LoadFromOffset(kLoadWord, out, TR, offset);
+ MipsLabel done;
+ __ Beqz(out, &done);
+ __ Sync(0);
+ __ StoreToOffset(kStoreWord, ZERO, TR, offset);
+ __ Sync(0);
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
UNIMPLEMENTED_INTRINSIC(MIPS, MathCeil)
@@ -3232,8 +3252,6 @@
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(MIPS, ThreadInterrupted)
-
UNREACHABLE_INTRINSICS(MIPS)
#undef __
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 512fb68..f629134 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2586,6 +2586,26 @@
}
}
+// static boolean java.lang.Thread.interrupted()
+void IntrinsicLocationsBuilderMIPS64::VisitThreadInterrupted(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorMIPS64::VisitThreadInterrupted(HInvoke* invoke) {
+ Mips64Assembler* assembler = GetAssembler();
+ GpuRegister out = invoke->GetLocations()->Out().AsRegister<GpuRegister>();
+ int32_t offset = Thread::InterruptedOffset<kMips64PointerSize>().Int32Value();
+ __ LoadFromOffset(kLoadWord, out, TR, offset);
+ Mips64Label done;
+ __ Beqzc(out, &done);
+ __ Sync(0);
+ __ StoreToOffset(kStoreWord, ZERO, TR, offset);
+ __ Sync(0);
+ __ Bind(&done);
+}
+
UNIMPLEMENTED_INTRINSIC(MIPS64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(MIPS64, SystemArrayCopy)
@@ -2605,8 +2625,6 @@
UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetLong)
UNIMPLEMENTED_INTRINSIC(MIPS64, UnsafeGetAndSetObject)
-UNIMPLEMENTED_INTRINSIC(MIPS64, ThreadInterrupted)
-
UNREACHABLE_INTRINSICS(MIPS64)
#undef __
diff --git a/runtime/Android.bp b/runtime/Android.bp
index d9bb04e..52ec782 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -616,6 +616,7 @@
"prebuilt_tools_test.cc",
"reference_table_test.cc",
"runtime_callbacks_test.cc",
+ "subtype_check_info_test.cc",
"thread_pool_test.cc",
"transaction_test.cc",
"type_lookup_table_test.cc",
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index e18f110..3cf2b93 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -110,9 +110,9 @@
#define SHADOWFRAME_DEX_PC_PTR_OFFSET (SHADOWFRAME_LINK_OFFSET + 3 * __SIZEOF_POINTER__)
ADD_TEST_EQ(SHADOWFRAME_DEX_PC_PTR_OFFSET,
static_cast<int32_t>(art::ShadowFrame::DexPCPtrOffset()))
-#define SHADOWFRAME_CODE_ITEM_OFFSET (SHADOWFRAME_LINK_OFFSET + 4 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_CODE_ITEM_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::CodeItemOffset()))
+#define SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET (SHADOWFRAME_LINK_OFFSET + 4 * __SIZEOF_POINTER__)
+ADD_TEST_EQ(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET,
+ static_cast<int32_t>(art::ShadowFrame::DexInstructionsOffset()))
#define SHADOWFRAME_LOCK_COUNT_DATA_OFFSET (SHADOWFRAME_LINK_OFFSET + 5 * __SIZEOF_POINTER__)
ADD_TEST_EQ(SHADOWFRAME_LOCK_COUNT_DATA_OFFSET,
static_cast<int32_t>(art::ShadowFrame::LockCountDataOffset()))
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index 74265bb..e158212 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -46,8 +46,6 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_IS_GC_MARKING_OFFSET), (static_cast<int32_t>(art::Thread::IsGcMarkingOffset<art::kRuntimePointerSize>().Int32Value())))
#define THREAD_CARD_TABLE_OFFSET 136
DEFINE_CHECK_EQ(static_cast<int32_t>(THREAD_CARD_TABLE_OFFSET), (static_cast<int32_t>(art::Thread::CardTableOffset<art::kRuntimePointerSize>().Int32Value())))
-#define CODEITEM_INSNS_OFFSET 16
-DEFINE_CHECK_EQ(static_cast<int32_t>(CODEITEM_INSNS_OFFSET), (static_cast<int32_t>(__builtin_offsetof(art::DexFile::CodeItem, insns_))))
#define MIRROR_CLASS_DEX_CACHE_OFFSET 16
DEFINE_CHECK_EQ(static_cast<int32_t>(MIRROR_CLASS_DEX_CACHE_OFFSET), (static_cast<int32_t>(art::mirror::Class::DexCacheOffset().Int32Value())))
#define MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET 48
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 038405e..01b7d4e 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -314,7 +314,10 @@
return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
false);
}
- bool returned = ExecuteMterpImpl(self, code_item, &shadow_frame, &result_register);
+ bool returned = ExecuteMterpImpl(self,
+ code_item->insns_,
+ &shadow_frame,
+ &result_register);
if (returned) {
return result_register;
} else {
diff --git a/runtime/interpreter/interpreter_mterp_impl.h b/runtime/interpreter/interpreter_mterp_impl.h
index 1be20fa..7aa5a34 100644
--- a/runtime/interpreter/interpreter_mterp_impl.h
+++ b/runtime/interpreter/interpreter_mterp_impl.h
@@ -32,7 +32,7 @@
// Mterp does not support transactions or access check, thus no templated versions.
extern "C" bool ExecuteMterpImpl(Thread* self,
- const DexFile::CodeItem* code_item,
+ const uint16_t* dex_instructions,
ShadowFrame* shadow_frame,
JValue* result_register) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S
index 5781414..de617a9 100644
--- a/runtime/interpreter/mterp/arm/entry.S
+++ b/runtime/interpreter/mterp/arm/entry.S
@@ -46,8 +46,8 @@
/* Remember the return register */
str r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
- /* Remember the code_item */
- str r1, [r2, #SHADOWFRAME_CODE_ITEM_OFFSET]
+ /* Remember the dex instruction pointer */
+ str r1, [r2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
/* set up "named" registers */
mov rSELF, r0
@@ -55,8 +55,7 @@
add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs.
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
- add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[]
- add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode
+ add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm/footer.S b/runtime/interpreter/mterp/arm/footer.S
index c6801e5..f3a3ad2 100644
--- a/runtime/interpreter/mterp/arm/footer.S
+++ b/runtime/interpreter/mterp/arm/footer.S
@@ -97,11 +97,10 @@
bl MterpHandleException @ (self, shadow_frame)
cmp r0, #0
beq MterpExceptionReturn @ no local catch, back to caller.
- ldr r0, [rFP, #OFF_FP_CODE_ITEM]
+ ldr r0, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
ldr r1, [rFP, #OFF_FP_DEX_PC]
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
- add rPC, r0, #CODEITEM_INSNS_OFFSET
- add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr
+ add rPC, r0, r1, lsl #1 @ generate new dex_pc_ptr
/* Do we need to switch interpreters? */
bl MterpShouldSwitchInterpreters
cmp r0, #0
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 597d9d4..51c2ba4 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -110,7 +110,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
/*
@@ -130,9 +130,8 @@
.endm
.macro EXPORT_DEX_PC tmp
- ldr \tmp, [rFP, #OFF_FP_CODE_ITEM]
+ ldr \tmp, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
str rPC, [rFP, #OFF_FP_DEX_PC_PTR]
- add \tmp, #CODEITEM_INSNS_OFFSET
sub \tmp, rPC, \tmp
asr \tmp, #1
str \tmp, [rFP, #OFF_FP_DEX_PC]
diff --git a/runtime/interpreter/mterp/arm/op_aget_wide.S b/runtime/interpreter/mterp/arm/op_aget_wide.S
index 853a7a4..66ec950 100644
--- a/runtime/interpreter/mterp/arm/op_aget_wide.S
+++ b/runtime/interpreter/mterp/arm/op_aget_wide.S
@@ -10,7 +10,6 @@
mov r3, r0, lsr #8 @ r3<- CC
GET_VREG r0, r2 @ r0<- vBB (array object)
GET_VREG r1, r3 @ r1<- vCC (requested index)
- CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
cmp r0, #0 @ null array object?
beq common_errNullObject @ yes, bail
ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length
@@ -18,6 +17,7 @@
cmp r1, r3 @ compare unsigned index, length
bcs common_errArrayIndex @ index >= length, bail
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC]
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S
index 7306e4e..f3d40ff 100644
--- a/runtime/interpreter/mterp/arm64/entry.S
+++ b/runtime/interpreter/mterp/arm64/entry.S
@@ -36,8 +36,8 @@
/* Remember the return register */
str x3, [x2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
- /* Remember the code_item */
- str x1, [x2, #SHADOWFRAME_CODE_ITEM_OFFSET]
+ /* Remember the dex instruction pointer */
+ str x1, [x2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
/* set up "named" registers */
mov xSELF, x0
@@ -45,8 +45,7 @@
add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs.
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
- add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[]
- add xPC, xPC, w0, lsl #1 // Create direct pointer to 1st dex opcode
+ add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm64/footer.S b/runtime/interpreter/mterp/arm64/footer.S
index fafa606..0ce3543 100644
--- a/runtime/interpreter/mterp/arm64/footer.S
+++ b/runtime/interpreter/mterp/arm64/footer.S
@@ -93,11 +93,10 @@
add x1, xFP, #OFF_FP_SHADOWFRAME
bl MterpHandleException // (self, shadow_frame)
cbz w0, MterpExceptionReturn // no local catch, back to caller.
- ldr x0, [xFP, #OFF_FP_CODE_ITEM]
+ ldr x0, [xFP, #OFF_FP_DEX_INSTRUCTIONS]
ldr w1, [xFP, #OFF_FP_DEX_PC]
ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
- add xPC, x0, #CODEITEM_INSNS_OFFSET
- add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr
+ add xPC, x0, x1, lsl #1 // generate new dex_pc_ptr
/* Do we need to switch interpreters? */
bl MterpShouldSwitchInterpreters
cbnz w0, MterpFallback
diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S
index cedfa49..47f12d2 100644
--- a/runtime/interpreter/mterp/arm64/header.S
+++ b/runtime/interpreter/mterp/arm64/header.S
@@ -116,7 +116,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
/*
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index 404c260..92dd19e 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -577,7 +577,7 @@
self->AssertNoPendingException();
}
if (kTraceExecutionEnabled) {
- uint32_t dex_pc = dex_pc_ptr - shadow_frame->GetCodeItem()->insns_;
+ uint32_t dex_pc = dex_pc_ptr - shadow_frame->GetDexInstructions();
TraceExecution(*shadow_frame, inst, dex_pc);
}
if (kTestExportPC) {
diff --git a/runtime/interpreter/mterp/mterp_stub.cc b/runtime/interpreter/mterp/mterp_stub.cc
index 35f8f1c..e515ec4 100644
--- a/runtime/interpreter/mterp/mterp_stub.cc
+++ b/runtime/interpreter/mterp/mterp_stub.cc
@@ -38,8 +38,10 @@
/*
* The platform-specific implementation must provide this.
*/
-extern "C" bool ExecuteMterpImpl(Thread* self, const DexFile::CodeItem* code_item,
- ShadowFrame* shadow_frame, JValue* result_register)
+extern "C" bool ExecuteMterpImpl(Thread* self,
+ const uint16_t* dex_instructions,
+ ShadowFrame* shadow_frame,
+ JValue* result_register)
REQUIRES_SHARED(Locks::mutator_lock_) {
UNUSED(self); UNUSED(shadow_frame); UNUSED(code_item); UNUSED(result_register);
UNIMPLEMENTED(art::FATAL);
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 8ca5bd4..69d7edb 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -117,7 +117,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
/*
@@ -137,9 +137,8 @@
.endm
.macro EXPORT_DEX_PC tmp
- ldr \tmp, [rFP, #OFF_FP_CODE_ITEM]
+ ldr \tmp, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
str rPC, [rFP, #OFF_FP_DEX_PC_PTR]
- add \tmp, #CODEITEM_INSNS_OFFSET
sub \tmp, rPC, \tmp
asr \tmp, #1
str \tmp, [rFP, #OFF_FP_DEX_PC]
@@ -365,8 +364,8 @@
/* Remember the return register */
str r3, [r2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
- /* Remember the code_item */
- str r1, [r2, #SHADOWFRAME_CODE_ITEM_OFFSET]
+ /* Remember the dex instruction pointer */
+ str r1, [r2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
/* set up "named" registers */
mov rSELF, r0
@@ -374,8 +373,7 @@
add rFP, r2, #SHADOWFRAME_VREGS_OFFSET @ point to vregs.
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
- add rPC, r1, #CODEITEM_INSNS_OFFSET @ Point to base of insns[]
- add rPC, rPC, r0, lsl #1 @ Create direct pointer to 1st dex opcode
+ add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
EXPORT_PC
/* Starting ibase */
@@ -1836,7 +1834,6 @@
mov r3, r0, lsr #8 @ r3<- CC
GET_VREG r0, r2 @ r0<- vBB (array object)
GET_VREG r1, r3 @ r1<- vCC (requested index)
- CLEAR_SHADOW_PAIR r9, r2, r3 @ Zero out the shadow regs
cmp r0, #0 @ null array object?
beq common_errNullObject @ yes, bail
ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET] @ r3<- arrayObj->length
@@ -1844,6 +1841,7 @@
cmp r1, r3 @ compare unsigned index, length
bcs common_errArrayIndex @ index >= length, bail
FETCH_ADVANCE_INST 2 @ advance rPC, load rINST
+ CLEAR_SHADOW_PAIR r9, lr, ip @ Zero out the shadow regs
ldrd r2, [r0, #MIRROR_WIDE_ARRAY_DATA_OFFSET] @ r2/r3<- vBB[vCC]
VREG_INDEX_TO_ADDR r9, r9 @ r9<- &fp[AA]
GET_INST_OPCODE ip @ extract opcode from rINST
@@ -12044,11 +12042,10 @@
bl MterpHandleException @ (self, shadow_frame)
cmp r0, #0
beq MterpExceptionReturn @ no local catch, back to caller.
- ldr r0, [rFP, #OFF_FP_CODE_ITEM]
+ ldr r0, [rFP, #OFF_FP_DEX_INSTRUCTIONS]
ldr r1, [rFP, #OFF_FP_DEX_PC]
ldr rIBASE, [rSELF, #THREAD_CURRENT_IBASE_OFFSET]
- add rPC, r0, #CODEITEM_INSNS_OFFSET
- add rPC, rPC, r1, lsl #1 @ generate new dex_pc_ptr
+ add rPC, r0, r1, lsl #1 @ generate new dex_pc_ptr
/* Do we need to switch interpreters? */
bl MterpShouldSwitchInterpreters
cmp r0, #0
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index d4423ab..82edab4 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -123,7 +123,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
/*
@@ -394,8 +394,8 @@
/* Remember the return register */
str x3, [x2, #SHADOWFRAME_RESULT_REGISTER_OFFSET]
- /* Remember the code_item */
- str x1, [x2, #SHADOWFRAME_CODE_ITEM_OFFSET]
+ /* Remember the dex instruction pointer */
+ str x1, [x2, #SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET]
/* set up "named" registers */
mov xSELF, x0
@@ -403,8 +403,7 @@
add xFP, x2, #SHADOWFRAME_VREGS_OFFSET // point to vregs.
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
- add xPC, x1, #CODEITEM_INSNS_OFFSET // Point to base of insns[]
- add xPC, xPC, w0, lsl #1 // Create direct pointer to 1st dex opcode
+ add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
EXPORT_PC
/* Starting ibase */
@@ -7182,11 +7181,10 @@
add x1, xFP, #OFF_FP_SHADOWFRAME
bl MterpHandleException // (self, shadow_frame)
cbz w0, MterpExceptionReturn // no local catch, back to caller.
- ldr x0, [xFP, #OFF_FP_CODE_ITEM]
+ ldr x0, [xFP, #OFF_FP_DEX_INSTRUCTIONS]
ldr w1, [xFP, #OFF_FP_DEX_PC]
ldr xIBASE, [xSELF, #THREAD_CURRENT_IBASE_OFFSET]
- add xPC, x0, #CODEITEM_INSNS_OFFSET
- add xPC, xPC, x1, lsl #1 // generate new dex_pc_ptr
+ add xPC, x0, x1, lsl #1 // generate new dex_pc_ptr
/* Do we need to switch interpreters? */
bl MterpShouldSwitchInterpreters
cbnz w0, MterpFallback
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index 514ecac..cbab61e 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -135,7 +135,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
@@ -371,15 +371,14 @@
/* Remember the code_item */
movl IN_ARG1(%esp), %ecx
- movl %ecx, SHADOWFRAME_CODE_ITEM_OFFSET(%edx)
+ movl %ecx, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(%edx)
/* set up "named" registers */
movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(%edx), %eax
leal SHADOWFRAME_VREGS_OFFSET(%edx), rFP
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
- lea CODEITEM_INSNS_OFFSET(%ecx), rPC
- lea (rPC, %eax, 2), rPC
+ lea (%ecx, %eax, 2), rPC
EXPORT_PC
/* Set up for backwards branches & osr profiling */
@@ -12749,10 +12748,9 @@
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- movl OFF_FP_CODE_ITEM(rFP), %eax
+ movl OFF_FP_DEX_INSTRUCTIONS(rFP), %eax
movl OFF_FP_DEX_PC(rFP), %ecx
- lea CODEITEM_INSNS_OFFSET(%eax), rPC
- lea (rPC, %ecx, 2), rPC
+ lea (%eax, %ecx, 2), rPC
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
call SYMBOL(MterpShouldSwitchInterpreters)
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index cfee2b8..83c3e4f 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -131,7 +131,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
@@ -354,15 +354,14 @@
movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2)
/* Remember the code_item */
- movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2)
+ movq IN_ARG1, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(IN_ARG2)
/* set up "named" registers */
movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax
leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
- leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC
- leaq (rPC, %rax, 2), rPC
+ leaq (IN_ARG1, %rax, 2), rPC
EXPORT_PC
/* Starting ibase */
@@ -11967,10 +11966,9 @@
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- movq OFF_FP_CODE_ITEM(rFP), %rax
+ movq OFF_FP_DEX_INSTRUCTIONS(rFP), %rax
mov OFF_FP_DEX_PC(rFP), %ecx
- leaq CODEITEM_INSNS_OFFSET(%rax), rPC
- leaq (rPC, %rcx, 2), rPC
+ leaq (%rax, %rcx, 2), rPC
movq rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
call SYMBOL(MterpShouldSwitchInterpreters)
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index 34adf53..055e834 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -53,15 +53,14 @@
/* Remember the code_item */
movl IN_ARG1(%esp), %ecx
- movl %ecx, SHADOWFRAME_CODE_ITEM_OFFSET(%edx)
+ movl %ecx, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(%edx)
/* set up "named" registers */
movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(%edx), %eax
leal SHADOWFRAME_VREGS_OFFSET(%edx), rFP
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
- lea CODEITEM_INSNS_OFFSET(%ecx), rPC
- lea (rPC, %eax, 2), rPC
+ lea (%ecx, %eax, 2), rPC
EXPORT_PC
/* Set up for backwards branches & osr profiling */
diff --git a/runtime/interpreter/mterp/x86/footer.S b/runtime/interpreter/mterp/x86/footer.S
index 088cb12..0b08cf9 100644
--- a/runtime/interpreter/mterp/x86/footer.S
+++ b/runtime/interpreter/mterp/x86/footer.S
@@ -115,10 +115,9 @@
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- movl OFF_FP_CODE_ITEM(rFP), %eax
+ movl OFF_FP_DEX_INSTRUCTIONS(rFP), %eax
movl OFF_FP_DEX_PC(rFP), %ecx
- lea CODEITEM_INSNS_OFFSET(%eax), rPC
- lea (rPC, %ecx, 2), rPC
+ lea (%eax, %ecx, 2), rPC
movl rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
call SYMBOL(MterpShouldSwitchInterpreters)
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 3a2dcb7..370012f 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -128,7 +128,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
#define OFF_FP_SHADOWFRAME OFF_FP(0)
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index 0f969eb..83b845b 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -50,15 +50,14 @@
movq IN_ARG3, SHADOWFRAME_RESULT_REGISTER_OFFSET(IN_ARG2)
/* Remember the code_item */
- movq IN_ARG1, SHADOWFRAME_CODE_ITEM_OFFSET(IN_ARG2)
+ movq IN_ARG1, SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET(IN_ARG2)
/* set up "named" registers */
movl SHADOWFRAME_NUMBER_OF_VREGS_OFFSET(IN_ARG2), %eax
leaq SHADOWFRAME_VREGS_OFFSET(IN_ARG2), rFP
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
- leaq CODEITEM_INSNS_OFFSET(IN_ARG1), rPC
- leaq (rPC, %rax, 2), rPC
+ leaq (IN_ARG1, %rax, 2), rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/x86_64/footer.S b/runtime/interpreter/mterp/x86_64/footer.S
index ac6cd19..3cc7532 100644
--- a/runtime/interpreter/mterp/x86_64/footer.S
+++ b/runtime/interpreter/mterp/x86_64/footer.S
@@ -98,10 +98,9 @@
call SYMBOL(MterpHandleException)
testb %al, %al
jz MterpExceptionReturn
- movq OFF_FP_CODE_ITEM(rFP), %rax
+ movq OFF_FP_DEX_INSTRUCTIONS(rFP), %rax
mov OFF_FP_DEX_PC(rFP), %ecx
- leaq CODEITEM_INSNS_OFFSET(%rax), rPC
- leaq (rPC, %rcx, 2), rPC
+ leaq (%rax, %rcx, 2), rPC
movq rPC, OFF_FP_DEX_PC_PTR(rFP)
/* Do we need to switch interpreters? */
call SYMBOL(MterpShouldSwitchInterpreters)
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index f229e84..9d21f3f 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -124,7 +124,7 @@
#define OFF_FP_METHOD OFF_FP(SHADOWFRAME_METHOD_OFFSET)
#define OFF_FP_RESULT_REGISTER OFF_FP(SHADOWFRAME_RESULT_REGISTER_OFFSET)
#define OFF_FP_DEX_PC_PTR OFF_FP(SHADOWFRAME_DEX_PC_PTR_OFFSET)
-#define OFF_FP_CODE_ITEM OFF_FP(SHADOWFRAME_CODE_ITEM_OFFSET)
+#define OFF_FP_DEX_INSTRUCTIONS OFF_FP(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET)
#define OFF_FP_COUNTDOWN_OFFSET OFF_FP(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET)
#define OFF_FP_SHADOWFRAME (-SHADOWFRAME_VREGS_OFFSET)
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 80fdadb..88275cc 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -92,7 +92,7 @@
}
uint32_t GetDexPC() const {
- return (dex_pc_ptr_ == nullptr) ? dex_pc_ : dex_pc_ptr_ - code_item_->insns_;
+ return (dex_pc_ptr_ == nullptr) ? dex_pc_ : dex_pc_ptr_ - dex_instructions_;
}
int16_t GetCachedHotnessCountdown() const {
@@ -146,12 +146,8 @@
return &vregs_[i + NumberOfVRegs()];
}
- void SetCodeItem(const DexFile::CodeItem* code_item) {
- code_item_ = code_item;
- }
-
- const DexFile::CodeItem* GetCodeItem() const {
- return code_item_;
+ const uint16_t* GetDexInstructions() const {
+ return dex_instructions_;
}
float GetVRegFloat(size_t i) const {
@@ -324,8 +320,8 @@
return OFFSETOF_MEMBER(ShadowFrame, dex_pc_ptr_);
}
- static size_t CodeItemOffset() {
- return OFFSETOF_MEMBER(ShadowFrame, code_item_);
+ static size_t DexInstructionsOffset() {
+ return OFFSETOF_MEMBER(ShadowFrame, dex_instructions_);
}
static size_t CachedHotnessCountdownOffset() {
@@ -372,7 +368,7 @@
method_(method),
result_register_(nullptr),
dex_pc_ptr_(nullptr),
- code_item_(nullptr),
+ dex_instructions_(nullptr),
number_of_vregs_(num_vregs),
dex_pc_(dex_pc),
cached_hotness_countdown_(0),
@@ -403,7 +399,8 @@
ArtMethod* method_;
JValue* result_register_;
const uint16_t* dex_pc_ptr_;
- const DexFile::CodeItem* code_item_;
+ // Dex instruction base of the code item.
+ const uint16_t* dex_instructions_;
LockCountData lock_count_data_; // This may contain GC roots when lock counting is active.
const uint32_t number_of_vregs_;
uint32_t dex_pc_;
diff --git a/runtime/subtype_check_bits.h b/runtime/subtype_check_bits.h
new file mode 100644
index 0000000..4305ff8
--- /dev/null
+++ b/runtime/subtype_check_bits.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_SUBTYPE_CHECK_BITS_H_
+#define ART_RUNTIME_SUBTYPE_CHECK_BITS_H_
+
+#include "base/bit_string.h"
+#include "base/bit_struct.h"
+#include "base/bit_utils.h"
+
+namespace art {
+
+/**
+ * The SubtypeCheckBits memory layout (in bits):
+ *
+ * Variable
+ * |
+ * <---- up to 23 bits ----> v +---> 1 bit
+ * |
+ * +-------------------------+--------+-----------+---++
+ * | Bitstring | |
+ * +-------------------------+--------+-----------+ |
+ * | Path To Root | Next | (unused) | OF |
+ * +---+---------------------+--------+ | |
+ * | | | | | ... | | (0....0) | |
+ * +---+---------------------+--------+-----------+----+
+ * MSB LSB
+ *
+ * The bitstring takes up to 23 bits; anything exceeding that is truncated:
+ * - Path To Root is a list of chars, encoded as a BitString:
+ * starting at the root (in MSB), each character is a sibling index unique to the parent,
+ * Paths longer than BitString::kCapacity are truncated to fit within the BitString.
+ * - Next is a single BitStringChar (immediatelly following Path To Root)
+ * When new children are assigned paths, they get allocated the parent's Next value.
+ * The next value is subsequently incremented.
+ *
+ * The exact bit position of (unused) is variable-length:
+ * In the cases that the "Path To Root" + "Next" does not fill up the entire
+ * BitString capacity, the remaining bits are (unused) and left as 0s.
+ *
+ * There is also an additional "OF" (overflow) field to indicate that the
+ * PathToRoot has been truncated.
+ *
+ * See subtype_check.h and subtype_check_info.h for more details.
+ */
+BITSTRUCT_DEFINE_START(SubtypeCheckBits, /*size*/ BitString::BitStructSizeOf() + 1u)
+ BitStructUint</*lsb*/0, /*width*/1> overflow_;
+ BitStructField<BitString, /*lsb*/1> bitstring_;
+BITSTRUCT_DEFINE_END(SubtypeCheckBits);
+
+} // namespace art
+
+#endif // ART_RUNTIME_SUBTYPE_CHECK_BITS_H_
diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h
new file mode 100644
index 0000000..f60e0ac
--- /dev/null
+++ b/runtime/subtype_check_info.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_SUBTYPE_CHECK_INFO_H_
+#define ART_RUNTIME_SUBTYPE_CHECK_INFO_H_
+
+#include "base/bit_string.h"
+#include "subtype_check_bits.h"
+
+// Forward-declare for testing purposes.
+struct SubtypeCheckInfoTest;
+
+namespace art {
+
+/**
+ * SubtypeCheckInfo is a logical label for the class SubtypeCheck data, which is necessary to
+ * perform efficient O(1) subtype comparison checks. See also subtype_check.h
+ * for the more general explanation of how the labels are used overall.
+ *
+ * For convenience, we also store the class depth within an SubtypeCheckInfo, since nearly all
+ * calculations are dependent on knowing the depth of the class.
+ *
+ * A SubtypeCheckInfo logically has:
+ * * Depth - How many levels up to the root (j.l.Object)?
+ * * PathToRoot - Possibly truncated BitString that encodes path to root
+ * * Next - The value a newly inserted Child would get appended to its path.
+ * * Overflow - If this path can never become a full path.
+ *
+ * Depending on the values of the above, it can be in one of these logical states,
+ * which are introduced in subtype_check.h:
+ *
+ * Transient States Terminal States
+ *
+ * +-----------------+ +--------------------+ +-------------------+
+ * | | | | | |
+ * | Uninitialized | +---> Initialized | +---> Assigned |
+ * | | | | | |
+ * +--------+--------+ +---------+----------+ +-------------------+
+ * | |
+ * | |
+ * | | +-------------------+
+ * | +----------------> |
+ * | | Overflowed |
+ * +-----------------------------------------> |
+ * +-------------------+
+ *
+ * Invariants:
+ *
+ * Initialized => Parent >= Initialized
+ *
+ * Assigned => Parent == Assigned
+ *
+ * Overflowed => Parent == Overflowed || Parent.Next == Overflowed
+ *
+ * Thread-safety invariants:
+ *
+ * Initialized => Parent == Assigned
+ * // For a class that has an Initialized bitstring, its superclass needs to have an
+ * // Assigned bitstring since if its super class's bitstring is not Assigned yet,
+ * // once it becomes Assigned, we cannot update its children's bitstrings to maintain
+ * // all the tree invariants (below) atomically.
+ *
+ * --------------------------------------------------------------------------------------------
+ * Knowing these transitions above, we can more closely define the various terms and operations:
+ *
+ * Definitions:
+ * (see also base/bit_string.h definitions).
+ *
+ * Depth := Distance(Root, Class)
+ * Safe(Depth) := Min(Depth, MaxBitstringLen)
+ * PathToRoot := Bitstring[0..Safe(Depth))
+ * Next := Bitstring[Depth]
+ * OF ∈ {False, True}
+ * TruncPath(D) := PathToRoot[0..D)
+ *
+ * Local Invariants:
+ *
+ * Uninitialized <=> StrLen(PathToRoot) == 0
+ * Next == 0
+ * Initialized <=> StrLen(PathToRoot) < Depth
+ * Next == 0
+ * Assigned <=> StrLen(PathToRoot) == Depth
+ * Next > 1
+ * Overflowed <=> OF == True
+ *
+ * Tree Invariants:
+ *
+ * Uninitialized =>
+ * forall child ∈ Children(Class):
+ * child.State == Uninitialized
+ *
+ * Assigned =>
+ * forall child ∈ Children(Class):
+ * Next > Child.PathToRoot[Child.Depth-1]
+ *
+ * ! Uninitialized =>
+ * forall ancestor ∈ Ancestors(Class):
+ * TruncPath(ancestor.Depth) == ancestor.PathToRoot
+ * forall unrelated ∈ (Classes - Ancestors(Class))
+ * s.t. unrelated.State == Assigned:
+ * TruncPath(unrelated.Depth) != unrelated.PathToRoot
+ *
+ * Thread-safety invariants:
+ *
+ * Initialized <=> StrLen(PathToRoot) == Safe(Depth - 1)
+ * // Initialized State corresponds to exactly 1 bitstring.
+ * // Cannot transition from Initialized to Initialized.
+ */
+struct SubtypeCheckInfo {
+ // See above documentation for possible state transitions.
+ enum State {
+ kUninitialized,
+ kInitialized,
+ kAssigned,
+ kOverflowed
+ };
+
+ // The result of a "src IsSubType target" check:
+ enum Result {
+ kUnknownSubtypeOf, // Not enough data. Operand states weren't high enough.
+ kNotSubtypeOf, // Enough data. src is not a subchild of the target.
+ kSubtypeOf // Enough data. src is a subchild of the target.
+ };
+
+ // Chop off the depth, returning only the bitstring+of state.
+ // (Used to store into memory, since storing the depth would be redundant.)
+ SubtypeCheckBits GetSubtypeCheckBits() const {
+ return bitstring_and_of_;
+ }
+
+ // Create from the depth and the bitstring+of state.
+ // This is done for convenience to avoid passing in "depth" everywhere,
+ // since our current state is almost always a function of depth.
+ static SubtypeCheckInfo Create(SubtypeCheckBits compressed_value, size_t depth) {
+ SubtypeCheckInfo io;
+ io.depth_ = depth;
+ io.bitstring_and_of_ = compressed_value;
+ io.DcheckInvariants();
+ return io;
+ }
+
+ // Is this a subtype of the target?
+ //
+ // The current state must be at least Initialized, and the target state
+ // must be Assigned, otherwise the result will return kUnknownSubtypeOf.
+ //
+ // Normally, return kSubtypeOf or kNotSubtypeOf.
+ Result IsSubtypeOf(const SubtypeCheckInfo& target) {
+ if (target.GetState() != SubtypeCheckInfo::kAssigned) {
+ return Result::kUnknownSubtypeOf;
+ } else if (GetState() == SubtypeCheckInfo::kUninitialized) {
+ return Result::kUnknownSubtypeOf;
+ }
+
+ BitString::StorageType source_value = GetEncodedPathToRoot();
+ BitString::StorageType target_value = target.GetEncodedPathToRoot();
+ BitString::StorageType target_mask = target.GetEncodedPathToRootMask();
+
+ bool result = (source_value & target_mask) == (target_value);
+ if (result) {
+ DCHECK_EQ(GetPathToRoot().Truncate(target.GetSafeDepth()), target.GetPathToRoot())
+ << "Source: " << *this << ", Target: " << target;
+ } else {
+ DCHECK_NE(GetPathToRoot().Truncate(target.GetSafeDepth()), target.GetPathToRoot())
+ << "Source: " << *this << ", Target: " << target;
+ }
+
+ // Note: We could've also used shifts here, as described in subtype_check_bits.h,
+ // but it doesn't make much of a difference in the Runtime since we aren't trying to optimize
+ // for code size.
+
+ return result ? Result::kSubtypeOf : Result::kNotSubtypeOf;
+ }
+
+ // Returns a new root SubtypeCheckInfo with a blank PathToRoot.
+ // Post-condition: The return valued has an Assigned state.
+ static SubtypeCheckInfo CreateRoot() {
+ SubtypeCheckInfo io{}; // NOLINT
+ io.depth_ = 0u;
+ io.SetNext(io.GetNext() + 1u);
+
+ // The root is always considered assigned once it is no longer Initialized.
+ DCHECK_EQ(SubtypeCheckInfo::kAssigned, io.GetState());
+ return io;
+ }
+
+ // Copies the current PathToRoot into the child.
+ //
+ // If assign_next is true, then also assign a new SubtypeCheckInfo for a child by
+ // assigning the current Next value to its PathToRoot[Depth] component.
+ // Updates the current Next value as a side effect.
+ //
+ // Preconditions: State is either Assigned or Overflowed.
+ // Returns: A new child >= Initialized state.
+ SubtypeCheckInfo CreateChild(bool assign_next) {
+ SubtypeCheckInfo child = *this; // Copy everything (path, next, of).
+ child.depth_ = depth_ + 1u;
+
+ // Must be Assigned or Overflowed in order to create a subchild.
+ DCHECK(GetState() == kAssigned || GetState() == kOverflowed)
+ << "Unexpected bitstring state: " << GetState();
+
+ // Begin transition to >= Initialized.
+
+ // Always attempt to re-initialize Child's Next value.
+ // Next must be non-0 to disambiguate it from Uninitialized.
+ child.MaybeInitNext();
+
+ // Always clear the inherited Parent's next Value on the child.
+ OverwriteNextValueFromParent(/*inout*/&child, BitStringChar{}); // NOLINT
+
+ // The state is now Initialized | Overflowed.
+ DCHECK_NE(kAssigned, child.GetState()) << child.GetBitString();
+ DCHECK_NE(kUninitialized, child.GetState()) << child.GetBitString();
+
+ if (assign_next == false) {
+ child.DcheckInvariants();
+ return child;
+ }
+
+ // Begin transition to >= Assigned.
+
+ // Assign attempt.
+ if (HasNext() && !bitstring_and_of_.overflow_) {
+ // Do not bother assigning if parent had overflowed.
+ BitStringChar next = GetNext();
+ if (next != next.MaximumValue()) {
+ // The parent's "next" value is now the child's latest path element.
+ OverwriteNextValueFromParent(/*inout*/&child, next);
+ // Update self next value, so that future CreateChild calls
+ // do not get the same path value.
+ SetNext(next + 1u);
+ } else {
+ child.MarkOverflowed(); // Too wide.
+ }
+ } else {
+ child.MarkOverflowed(); // Too deep, or parent was already overflowed.
+ }
+
+ // The state is now Assigned | Overflowed.
+ DCHECK(child.GetState() == kAssigned || child.GetState() == kOverflowed);
+
+ child.DcheckInvariants();
+ return child;
+ }
+
+ // Get the current state (Uninitialized, Initialized, Assigned, or Overflowed).
+ // See the "SubtypeCheckInfo" documentation above which explains how a state is determined.
+ State GetState() const {
+ if (GetBitString() == BitString{}) { // NOLINT
+ // Empty bitstring (all 0s) -> uninitialized.
+ DCHECK(!bitstring_and_of_.overflow_);
+ return kUninitialized;
+ }
+
+ if (bitstring_and_of_.overflow_) {
+ // Overflowed if and only if the OF bit was set.
+ return kOverflowed;
+ }
+
+ // Either Assigned or Initialized.
+ BitString path_to_root = GetPathToRoot();
+
+ DCHECK(!HasNext() || GetNext() != BitStringChar{}) // NOLINT
+ << "Expected (Assigned|Initialized) state to have >0 Next value: "
+ << GetNext() << " path: " << path_to_root;
+
+ if (path_to_root.Length() == depth_) {
+ return kAssigned;
+ }
+
+ return kInitialized;
+ }
+
+ // Retrieve the path to root bitstring as a plain uintN_t value that is amenable to
+ // be used by a fast check "encoded_src & mask_target == encoded_target".
+ BitString::StorageType GetEncodedPathToRoot() const {
+ BitString::StorageType data = static_cast<BitString::StorageType>(GetPathToRoot());
+ // Bit strings are logically in the least-significant memory.
+ // Shift it so the bits are all most-significant.
+ return data << (BitSizeOf(data) - BitStructSizeOf<BitString>());
+ }
+
+ // Retrieve the path to root bitstring mask as a plain uintN_t that is amenable to
+ // be used by a fast check "encoded_src & mask_target == encoded_target".
+ BitString::StorageType GetEncodedPathToRootMask() const {
+ size_t num_bitchars = GetSafeDepth();
+ size_t bitlength = BitString::GetBitLengthTotalAtPosition(num_bitchars);
+
+ BitString::StorageType mask_all =
+ std::numeric_limits<BitString::StorageType>::max();
+ BitString::StorageType mask_lsb =
+ MaskLeastSignificant<BitString::StorageType>(
+ BitSizeOf<BitString::StorageType>() - bitlength);
+
+ BitString::StorageType result = mask_all & ~mask_lsb;
+
+ // TODO: refactor above code into MaskMostSignificant?
+ return result;
+ }
+
+ // Get the "Next" bitchar, assuming that there is one to get.
+ BitStringChar GetNext() const {
+ DCHECK(HasNext());
+ return GetBitString()[depth_];
+ }
+
+ // Try to get the Next value, if there is one.
+ // Returns: Whether or not there was a Next value.
+ bool MaybeGetNext(/*out*/BitStringChar* next) const {
+ DCHECK(next != nullptr);
+
+ if (HasNext()) {
+ *next = GetBitString()[depth_];
+ return true;
+ }
+ return false;
+ }
+
+ private:
+ // Constructor intended for testing. Runs all invariant checks.
+ SubtypeCheckInfo(BitString path_to_root, BitStringChar next, bool overflow, size_t depth) {
+ SubtypeCheckBits iod;
+ iod.bitstring_ = path_to_root;
+ iod.overflow_ = overflow;
+
+ bitstring_and_of_ = iod;
+ depth_ = depth;
+
+ // Len(Path-to-root) <= Depth.
+ DCHECK_GE(depth_, path_to_root.Length())
+ << "Path was too long for the depth, path: " << path_to_root;
+
+ bool did_overlap = false;
+ if (HasNext()) {
+ if (kIsDebugBuild) {
+ did_overlap = (GetNext() != BitStringChar{}); // NOLINT
+ }
+
+ SetNext(next);
+
+ DCHECK_EQ(next, GetNext());
+ }
+ // "Next" must be set before we can check the invariants.
+ DcheckInvariants();
+ DCHECK(!did_overlap)
+ << "Path to root overlapped with Next value, path: " << path_to_root;
+ DCHECK_EQ(path_to_root, GetPathToRoot());
+ }
+
+ // Factory intended for testing. Skips DcheckInvariants.
+ static SubtypeCheckInfo MakeUnchecked(BitString bitstring, bool overflow, size_t depth) {
+ SubtypeCheckBits iod;
+ iod.bitstring_ = bitstring;
+ iod.overflow_ = overflow;
+
+ SubtypeCheckInfo io;
+ io.depth_ = depth;
+ io.bitstring_and_of_ = iod;
+
+ return io;
+ }
+
+ void SetNext(BitStringChar next) {
+ DCHECK(HasNext());
+ BitString bs = GetBitString();
+ bs.SetAt(depth_, next);
+ SetBitString(bs);
+ }
+
+ void SetNextUnchecked(BitStringChar next) {
+ BitString bs = GetBitString();
+ bs.SetAt(depth_, next);
+ SetBitStringUnchecked(bs);
+ }
+
+ void MaybeInitNext() {
+ if (HasNext()) {
+ // Clearing out the "Next" value like this
+ // is often an intermediate operation which temporarily
+ // violates the invariants. Do not do the extra dchecks.
+ SetNextUnchecked(BitStringChar{}); // NOLINT
+ SetNextUnchecked(GetNext()+1u);
+ }
+ }
+
+ BitString GetPathToRoot() const {
+ size_t end = GetSafeDepth();
+ return GetBitString().Truncate(end);
+ }
+
+ bool HasNext() const {
+ return depth_ < BitString::kCapacity;
+ }
+
+ void MarkOverflowed() {
+ bitstring_and_of_.overflow_ = true;
+ }
+
+ static constexpr bool HasBitStringCharStorage(size_t idx) {
+ return idx < BitString::kCapacity;
+ }
+
+ size_t GetSafeDepth() const {
+ return GetSafeDepth(depth_);
+ }
+
+ // Get a "safe" depth, one that is truncated to the bitstring max capacity.
+ // Using a value larger than this will cause undefined behavior.
+ static size_t GetSafeDepth(size_t depth) {
+ return std::min(depth, BitString::kCapacity);
+ }
+
+ BitString GetBitString() const {
+ return bitstring_and_of_.bitstring_;
+ }
+
+ void SetBitString(const BitString& val) {
+ SetBitStringUnchecked(val);
+ DcheckInvariants();
+ }
+
+ void SetBitStringUnchecked(const BitString& val) {
+ bitstring_and_of_.bitstring_ = val;
+ }
+
+ void OverwriteNextValueFromParent(/*inout*/SubtypeCheckInfo* child, BitStringChar value) const {
+ // Helper function for CreateChild.
+ if (HasNext()) {
+ // When we copied the "Next" value, it is now our
+ // last path component in the child.
+ // Always overwrite it with either a cleared value or the parent's Next value.
+ BitString bs = child->GetBitString();
+
+ // Safe write. This.Next always occupies same slot as Child[Depth_].
+ DCHECK(child->HasBitStringCharStorage(depth_));
+
+ bs.SetAt(depth_, value);
+
+ // The child is temporarily in a bad state until it is fixed up further.
+ // Do not do the normal dchecks which do not allow transient badness.
+ child->SetBitStringUnchecked(bs);
+ }
+ }
+
+ void DcheckInvariants() const {
+ if (kIsDebugBuild) {
+ CHECK_GE(GetSafeDepth(depth_ + 1u), GetBitString().Length())
+ << "Bitstring too long for depth, bitstring: " << GetBitString() << ", depth: " << depth_;
+
+ BitString path_to_root = GetPathToRoot();
+
+ // A 'null' (\0) character in path-to-root must be followed only
+ // by other null characters.
+ size_t i;
+ for (i = 0; i < BitString::kCapacity; ++i) {
+ BitStringChar bc = path_to_root[i];
+ if (bc == 0u) {
+ break;
+ }
+ }
+
+ // All characters following a 0 must also be 0.
+ for (; i < BitString::kCapacity; ++i) {
+ BitStringChar bc = path_to_root[i];
+ if (bc != 0u) {
+ LOG(FATAL) << "Path to root had non-0s following 0s: " << path_to_root;
+ }
+ }
+
+ // Trigger any dchecks in GetState.
+ (void)GetState();
+ }
+ }
+
+ SubtypeCheckInfo() = default;
+ size_t depth_;
+ SubtypeCheckBits bitstring_and_of_;
+
+ friend struct ::SubtypeCheckInfoTest;
+ friend std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo& io);
+};
+
+// Prints the SubtypeCheckInfo::State, e.g. "kUnitialized".
+inline std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo::State& state) {
+ switch (state) {
+ case SubtypeCheckInfo::kUninitialized:
+ os << "kUninitialized";
+ break;
+ case SubtypeCheckInfo::kInitialized:
+ os << "kInitialized";
+ break;
+ case SubtypeCheckInfo::kAssigned:
+ os << "kAssigned";
+ break;
+ case SubtypeCheckInfo::kOverflowed:
+ os << "kOverflowed";
+ break;
+ default:
+ os << "(Invalid SubtypeCheckInfo::State " << static_cast<int>(state) << ")";
+ }
+ return os;
+}
+
+// Prints e.g. "SubtypeCheckInfo{BitString[1,2,3], depth: 3, of:1}"
+inline std::ostream& operator<<(std::ostream& os, const SubtypeCheckInfo& io) {
+ os << "SubtypeCheckInfo{" << io.GetBitString() << ", "
+ << "depth: " << io.depth_ << ", of:" << io.bitstring_and_of_.overflow_ << "}";
+ return os;
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_SUBTYPE_CHECK_INFO_H_
diff --git a/runtime/subtype_check_info_test.cc b/runtime/subtype_check_info_test.cc
new file mode 100644
index 0000000..bc2e84e
--- /dev/null
+++ b/runtime/subtype_check_info_test.cc
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "subtype_check_info.h"
+
+#include "gtest/gtest.h"
+#include "android-base/logging.h"
+
+namespace art {
+
+constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity];
+constexpr size_t BitString::kCapacity;
+
+}; // namespace art
+
+using namespace art; // NOLINT
+
+// These helper functions are only used by the test,
+// so they are not in the main BitString class.
+std::string Stringify(BitString bit_string) {
+ std::stringstream ss;
+ ss << bit_string;
+ return ss.str();
+}
+
+BitStringChar MakeBitStringChar(size_t idx, size_t val) {
+ return BitStringChar(val, BitString::MaybeGetBitLengthAtPosition(idx));
+}
+
+BitStringChar MakeBitStringChar(size_t val) {
+ return BitStringChar(val, MinimumBitsToStore(val));
+}
+
+BitString MakeBitString(std::initializer_list<size_t> values = {}) {
+ CHECK_GE(BitString::kCapacity, values.size());
+
+ BitString bs{}; // NOLINT
+
+ size_t i = 0;
+ for (size_t val : values) {
+ bs.SetAt(i, MakeBitStringChar(i, val));
+ ++i;
+ }
+
+ return bs;
+}
+
+template <typename T>
+size_t AsUint(const T& value) {
+ size_t uint_value = 0;
+ memcpy(&uint_value, &value, sizeof(value));
+ return uint_value;
+}
+
+// Make max bistring, e.g. BitString[4095,7,255] for {12,3,8}
+template <size_t kCount = BitString::kCapacity>
+BitString MakeBitStringMax() {
+ BitString bs{}; // NOLINT
+
+ for (size_t i = 0; i < kCount; ++i) {
+ bs.SetAt(i,
+ MakeBitStringChar(i, MaxInt<BitStringChar::StorageType>(BitString::kBitSizeAtPosition[i])));
+ }
+
+ return bs;
+}
+
+BitString SetBitStringCharAt(BitString bit_string, size_t i, size_t val) {
+ BitString bs = bit_string;
+ bs.SetAt(i, MakeBitStringChar(i, val));
+ return bs;
+}
+
+struct SubtypeCheckInfoTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ android::base::InitLogging(/*argv*/nullptr);
+ }
+
+ virtual void TearDown() {
+ }
+
+ static SubtypeCheckInfo MakeSubtypeCheckInfo(BitString path_to_root = {},
+ BitStringChar next = {},
+ bool overflow = false,
+ size_t depth = 1u) {
+ // Depth=1 is good default because it will go through all state transitions,
+ // and its children will also go through all state transitions.
+ return SubtypeCheckInfo(path_to_root, next, overflow, depth);
+ }
+
+ static SubtypeCheckInfo MakeSubtypeCheckInfoInfused(BitString bs = {},
+ bool overflow = false,
+ size_t depth = 1u) {
+ // Depth=1 is good default because it will go through all state transitions,
+ // and its children will also go through all state transitions.
+ SubtypeCheckBits iod;
+ iod.bitstring_ = bs;
+ iod.overflow_ = overflow;
+ return SubtypeCheckInfo::Create(iod, depth);
+ }
+
+ static SubtypeCheckInfo MakeSubtypeCheckInfoUnchecked(BitString bs = {},
+ bool overflow = false,
+ size_t depth = 1u) {
+ // Depth=1 is good default because it will go through all state transitions,
+ // and its children will also go through all state transitions.
+ return SubtypeCheckInfo::MakeUnchecked(bs, overflow, depth);
+ }
+
+ static bool HasNext(SubtypeCheckInfo io) {
+ return io.HasNext();
+ }
+
+ static BitString GetPathToRoot(SubtypeCheckInfo io) {
+ return io.GetPathToRoot();
+ }
+
+ // Create an SubtypeCheckInfo with the same depth, but with everything else reset.
+ // Returns: SubtypeCheckInfo in the Uninitialized state.
+ static SubtypeCheckInfo CopyCleared(SubtypeCheckInfo sc) {
+ SubtypeCheckInfo cleared_copy{}; // NOLINT
+ cleared_copy.depth_ = sc.depth_;
+ DCHECK_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState());
+ return cleared_copy;
+ }
+};
+
+const char* GetExpectedMessageForDeathTest(const char* msg) {
+#ifdef ART_TARGET_ANDROID
+ // On Android, dcheck failure messages go to logcat,
+ // which gtest death tests does not check, and thus the tests would fail with
+ // "unexpected message ''"
+ UNUSED(msg);
+ return ""; // Still ensures there was a bad return code, but match anything.
+#else
+ return msg;
+#endif
+}
+
+TEST_F(SubtypeCheckInfoTest, IllegalValues) {
+ // This test relies on BitString being at least 3 large.
+ // It will need to be updated otherwise.
+ ASSERT_LE(3u, BitString::kCapacity);
+
+ // Illegal values during construction would cause a Dcheck failure and crash.
+ ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({1u}),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/false,
+ /*depth*/0u),
+ GetExpectedMessageForDeathTest("Path was too long for the depth"));
+ ASSERT_DEATH(MakeSubtypeCheckInfoInfused(MakeBitString({1u, 1u}),
+ /*overflow*/false,
+ /*depth*/0u),
+ GetExpectedMessageForDeathTest("Bitstring too long for depth"));
+ ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({1u}),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/false,
+ /*depth*/1u),
+ GetExpectedMessageForDeathTest("Expected \\(Assigned\\|Initialized\\) "
+ "state to have >0 Next value"));
+ ASSERT_DEATH(MakeSubtypeCheckInfoInfused(MakeBitString({0u, 2u, 1u}),
+ /*overflow*/false,
+ /*depth*/2u),
+ GetExpectedMessageForDeathTest("Path to root had non-0s following 0s"));
+ ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({0u, 2u}),
+ /*next*/MakeBitStringChar(1u),
+ /*overflow*/false,
+ /*depth*/2u),
+ GetExpectedMessageForDeathTest("Path to root had non-0s following 0s"));
+ ASSERT_DEATH(MakeSubtypeCheckInfo(MakeBitString({0u, 1u, 1u}),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/false,
+ /*depth*/3u),
+ GetExpectedMessageForDeathTest("Path to root had non-0s following 0s"));
+
+ // These are really slow (~1sec per death test on host),
+ // keep them down to a minimum.
+}
+
+TEST_F(SubtypeCheckInfoTest, States) {
+ EXPECT_EQ(SubtypeCheckInfo::kUninitialized, MakeSubtypeCheckInfo().GetState());
+ EXPECT_EQ(SubtypeCheckInfo::kInitialized,
+ MakeSubtypeCheckInfo(/*path*/{}, /*next*/MakeBitStringChar(1)).GetState());
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed,
+ MakeSubtypeCheckInfo(/*path*/{},
+ /*next*/MakeBitStringChar(1),
+ /*overflow*/true,
+ /*depth*/1u).GetState());
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned,
+ MakeSubtypeCheckInfo(/*path*/MakeBitString({1u}),
+ /*next*/MakeBitStringChar(1),
+ /*overflow*/false,
+ /*depth*/1u).GetState());
+
+ // Test edge conditions: depth == BitString::kCapacity (No Next value).
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned,
+ MakeSubtypeCheckInfo(/*path*/MakeBitStringMax(),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity).GetState());
+ EXPECT_EQ(SubtypeCheckInfo::kInitialized,
+ MakeSubtypeCheckInfo(/*path*/MakeBitStringMax<BitString::kCapacity - 1u>(),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity).GetState());
+ // Test edge conditions: depth > BitString::kCapacity (Must overflow).
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed,
+ MakeSubtypeCheckInfo(/*path*/MakeBitStringMax(),
+ /*next*/MakeBitStringChar(0),
+ /*overflow*/true,
+ /*depth*/BitString::kCapacity + 1u).GetState());
+}
+
+TEST_F(SubtypeCheckInfoTest, NextValue) {
+ // Validate "Next" is correctly aliased as the Bitstring[Depth] character.
+ EXPECT_EQ(MakeBitStringChar(1u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}),
+ /*overflow*/false,
+ /*depth*/0u).GetNext());
+ EXPECT_EQ(MakeBitStringChar(2u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}),
+ /*overflow*/false,
+ /*depth*/1u).GetNext());
+ EXPECT_EQ(MakeBitStringChar(3u), MakeSubtypeCheckInfoUnchecked(MakeBitString({1u, 2u, 3u}),
+ /*overflow*/false,
+ /*depth*/2u).GetNext());
+ EXPECT_EQ(MakeBitStringChar(1u), MakeSubtypeCheckInfoUnchecked(MakeBitString({0u, 2u, 1u}),
+ /*overflow*/false,
+ /*depth*/2u).GetNext());
+ // Test edge conditions: depth == BitString::kCapacity (No Next value).
+ EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<BitString::kCapacity>(),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity)));
+ // Anything with depth >= BitString::kCapacity has no next value.
+ EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<BitString::kCapacity>(),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity + 1u)));
+ EXPECT_FALSE(HasNext(MakeSubtypeCheckInfoUnchecked(MakeBitStringMax(),
+ /*overflow*/false,
+ /*depth*/std::numeric_limits<size_t>::max())));
+}
+
+template <size_t kPos = BitString::kCapacity>
+size_t LenForPos() { return BitString::GetBitLengthTotalAtPosition(kPos); }
+
+TEST_F(SubtypeCheckInfoTest, EncodedPathToRoot) {
+ using StorageType = BitString::StorageType;
+
+ SubtypeCheckInfo io =
+ MakeSubtypeCheckInfo(/*path_to_root*/MakeBitStringMax(),
+ /*next*/BitStringChar{}, // NOLINT
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity);
+ // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation.
+ EXPECT_EQ(MaxInt<StorageType>(LenForPos()) << (BitSizeOf<StorageType>() - LenForPos()),
+ io.GetEncodedPathToRoot());
+
+ EXPECT_EQ(MaxInt<StorageType>(LenForPos()) << (BitSizeOf<StorageType>() - LenForPos()),
+ io.GetEncodedPathToRootMask());
+
+ // 0b11111...000 where MSB == 1, and leading 1s = the maximum bitstring representation.
+
+ // The rest of this test is written assuming kCapacity == 3 for convenience.
+ // Please update the test if this changes.
+ ASSERT_EQ(3u, BitString::kCapacity);
+ ASSERT_EQ(12u, BitString::kBitSizeAtPosition[0]);
+ ASSERT_EQ(3u, BitString::kBitSizeAtPosition[1]);
+ ASSERT_EQ(8u, BitString::kBitSizeAtPosition[2]);
+
+ SubtypeCheckInfo io2 =
+ MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity);
+
+#define MAKE_ENCODED_PATH(pos0, pos1, pos2) (((pos0) << 3u << 8u << 9u) | ((pos1) << 8u << 9u) | ((pos2) << 9u))
+
+ EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io2.GetEncodedPathToRoot());
+ EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b11111111), io2.GetEncodedPathToRootMask());
+
+ SubtypeCheckInfo io3 =
+ MakeSubtypeCheckInfoUnchecked(MakeBitStringMax<2u>(),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity - 1u);
+
+ EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io3.GetEncodedPathToRoot());
+ EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b111, 0b0), io3.GetEncodedPathToRootMask());
+
+ SubtypeCheckInfo io4 =
+ MakeSubtypeCheckInfoUnchecked(MakeBitString({0b1010101u}),
+ /*overflow*/false,
+ /*depth*/BitString::kCapacity - 2u);
+
+ EXPECT_EQ(MAKE_ENCODED_PATH(0b1010101u, 0b000, 0b0), io4.GetEncodedPathToRoot());
+ EXPECT_EQ(MAKE_ENCODED_PATH(MaxInt<BitString::StorageType>(12), 0b000, 0b0), io4.GetEncodedPathToRootMask());
+}
+
+TEST_F(SubtypeCheckInfoTest, NewForRoot) {
+ SubtypeCheckInfo io = SubtypeCheckInfo::CreateRoot();
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned, io.GetState()); // Root is always assigned.
+ EXPECT_EQ(0u, GetPathToRoot(io).Length()); // Root's path length is 0.
+ EXPECT_TRUE(HasNext(io)); // Root always has a "Next".
+ EXPECT_EQ(MakeBitStringChar(1u), io.GetNext()); // Next>=1 to disambiguate from Uninitialized.
+}
+
+TEST_F(SubtypeCheckInfoTest, CopyCleared) {
+ SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot();
+ EXPECT_EQ(MakeBitStringChar(1u), root.GetNext());
+
+ SubtypeCheckInfo childC = root.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState());
+ EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign.
+ EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC));
+
+ SubtypeCheckInfo cleared_copy = CopyCleared(childC);
+ EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy.GetState());
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy));
+
+ // CopyCleared is just a thin wrapper around value-init and providing the depth.
+ SubtypeCheckInfo cleared_copy_value =
+ SubtypeCheckInfo::Create(SubtypeCheckBits{}, /*depth*/1u); // NOLINT
+ EXPECT_EQ(SubtypeCheckInfo::kUninitialized, cleared_copy_value.GetState());
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(cleared_copy_value));
+}
+
+TEST_F(SubtypeCheckInfoTest, NewForChild2) {
+ SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot();
+ EXPECT_EQ(MakeBitStringChar(1u), root.GetNext());
+
+ SubtypeCheckInfo childC = root.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState());
+ EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign.
+ EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC));
+}
+
+TEST_F(SubtypeCheckInfoTest, NewForChild) {
+ SubtypeCheckInfo root = SubtypeCheckInfo::CreateRoot();
+ EXPECT_EQ(MakeBitStringChar(1u), root.GetNext());
+
+ SubtypeCheckInfo childA = root.CreateChild(/*assign*/false);
+ EXPECT_EQ(SubtypeCheckInfo::kInitialized, childA.GetState());
+ EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); // Next unchanged for Initialize.
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(childA));
+
+ SubtypeCheckInfo childB = root.CreateChild(/*assign*/false);
+ EXPECT_EQ(SubtypeCheckInfo::kInitialized, childB.GetState());
+ EXPECT_EQ(MakeBitStringChar(1u), root.GetNext()); // Next unchanged for Initialize.
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(childB));
+
+ SubtypeCheckInfo childC = root.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kAssigned, childC.GetState());
+ EXPECT_EQ(MakeBitStringChar(2u), root.GetNext()); // Next incremented for Assign.
+ EXPECT_EQ(MakeBitString({1u}), GetPathToRoot(childC));
+
+ {
+ size_t cur_depth = 1u;
+ SubtypeCheckInfo latest_child = childC;
+ while (cur_depth != BitString::kCapacity) {
+ latest_child = latest_child.CreateChild(/*assign*/true);
+ ASSERT_EQ(SubtypeCheckInfo::kAssigned, latest_child.GetState());
+ ASSERT_EQ(cur_depth + 1u, GetPathToRoot(latest_child).Length());
+ cur_depth++;
+ }
+
+ // Future assignments will result in a too-deep overflow.
+ SubtypeCheckInfo child_of_deep = latest_child.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of_deep.GetState());
+ EXPECT_EQ(GetPathToRoot(latest_child), GetPathToRoot(child_of_deep));
+
+ // Assignment of too-deep overflow also causes overflow.
+ SubtypeCheckInfo child_of_deep_2 = child_of_deep.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of_deep_2.GetState());
+ EXPECT_EQ(GetPathToRoot(child_of_deep), GetPathToRoot(child_of_deep_2));
+ }
+
+ {
+ size_t cur_next = 2u;
+ while (true) {
+ if (cur_next == MaxInt<BitString::StorageType>(BitString::kBitSizeAtPosition[0u])) {
+ break;
+ }
+
+ SubtypeCheckInfo child = root.CreateChild(/*assign*/true);
+ ASSERT_EQ(SubtypeCheckInfo::kAssigned, child.GetState());
+ ASSERT_EQ(MakeBitStringChar(cur_next+1u), root.GetNext());
+ ASSERT_EQ(MakeBitString({cur_next}), GetPathToRoot(child));
+
+ cur_next++;
+ }
+ // Now the root will be in a state that further assigns will be too-wide overflow.
+
+ // Initialization still succeeds.
+ SubtypeCheckInfo child = root.CreateChild(/*assign*/false);
+ EXPECT_EQ(SubtypeCheckInfo::kInitialized, child.GetState());
+ EXPECT_EQ(MakeBitStringChar(cur_next), root.GetNext());
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(child));
+
+ // Assignment goes to too-wide Overflow.
+ SubtypeCheckInfo child_of = root.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of.GetState());
+ EXPECT_EQ(MakeBitStringChar(cur_next), root.GetNext());
+ EXPECT_EQ(MakeBitString({}), GetPathToRoot(child_of));
+
+ // Assignment of overflowed child still succeeds.
+ // The path to root is the same.
+ SubtypeCheckInfo child_of2 = child_of.CreateChild(/*assign*/true);
+ EXPECT_EQ(SubtypeCheckInfo::kOverflowed, child_of2.GetState());
+ EXPECT_EQ(GetPathToRoot(child_of), GetPathToRoot(child_of2));
+ }
+}
diff --git a/test/668-aiobe/expected.txt b/test/668-aiobe/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/668-aiobe/expected.txt
diff --git a/test/668-aiobe/info.txt b/test/668-aiobe/info.txt
new file mode 100644
index 0000000..e422601
--- /dev/null
+++ b/test/668-aiobe/info.txt
@@ -0,0 +1,2 @@
+Regression test for the mterp arm interpreter which used to throw
+the wrong exception when accessing out of bounds a long/double array.
diff --git a/test/668-aiobe/smali/TestCase.smali b/test/668-aiobe/smali/TestCase.smali
new file mode 100644
index 0000000..5fa62e9
--- /dev/null
+++ b/test/668-aiobe/smali/TestCase.smali
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2017 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.
+
+
+.class public LTestCase;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {v0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public static run([DI)D
+.registers 2
+ aget-wide p0, p0, p1
+ return-wide p0
+.end method
diff --git a/test/668-aiobe/src/Main.java b/test/668-aiobe/src/Main.java
new file mode 100644
index 0000000..2bd30c4
--- /dev/null
+++ b/test/668-aiobe/src/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ double[] array = new double[5];
+ try {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("run", double[].class, int.class);
+ m.invoke(null, array, 42);
+ } catch (InvocationTargetException e) {
+ // expected
+ if (!(e.getCause() instanceof ArrayIndexOutOfBoundsException)) {
+ throw new Error("Expected ArrayIndexOutOfBoundsException, got " + e.getCause());
+ }
+ return;
+ }
+ throw new Error("Expected InvocationTargetException");
+ }
+}
diff --git a/test/911-get-stack-trace/check b/test/911-get-stack-trace/check
index 8358500..a46ea9e 100644
--- a/test/911-get-stack-trace/check
+++ b/test/911-get-stack-trace/check
@@ -19,4 +19,18 @@
patch -p0 expected.txt < expected_jack.diff
fi
+if [[ "$DX" == 'd8' ]]; then
+ patch -p0 expected.txt < expected_d8.diff
+fi
+
+./default-check "$@"
+if [[ "$?" == "0" ]]; then
+ exit 0;
+fi
+
+# We cannot always correctly determine if D8 was used because of (b/68406220).
+# So we are just going to try to see it matches the expect output of D8 no
+# matter what.
+patch -p0 expected.txt < expected_d8.diff
+
./default-check "$@"
diff --git a/test/911-get-stack-trace/expected_d8.diff b/test/911-get-stack-trace/expected_d8.diff
new file mode 100644
index 0000000..3ce9bed
--- /dev/null
+++ b/test/911-get-stack-trace/expected_d8.diff
@@ -0,0 +1,456 @@
+12c12
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+15c15
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+18c18
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+21c21
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+24c24
+< doTest ()V 34 25
+---
+> doTest ()V 33 25
+32c32
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+35c35
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+38c38
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+41c41
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+44c44
+< doTest ()V 38 26
+---
+> doTest ()V 37 26
+57c57
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+62c62
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+70c70
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+84c84
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+87c87
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+90c90
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+93c93
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+102c102
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+105c105
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+108c108
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+111c111
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+125c125
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+132c132
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+137c137
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+140c140
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+148c148
+< printOrWait (IILart/ControlData;)V 44 54
+---
+> printOrWait (IILart/ControlData;)V 45 54
+152c152
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+155c155
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+158c158
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+161c161
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+169c169
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+172c172
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+175c175
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+178c178
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+183c183
+< printOrWait (IILart/ControlData;)V 44 54
+---
+> printOrWait (IILart/ControlData;)V 45 54
+187c187
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+191c191
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+199c199
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+204c204
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+207c207
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+363c363
+< doTest ()V 122 59
+---
+> doTest ()V 119 59
+376c376
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+379c379
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+382c382
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+385c385
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+397c397
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+400c400
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+403c403
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+406c406
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+418c418
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+421c421
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+424c424
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+427c427
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+439c439
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+442c442
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+445c445
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+448c448
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+460c460
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+463c463
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+466c466
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+469c469
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+481c481
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+484c484
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+487c487
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+490c490
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+502c502
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+505c505
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+508c508
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+511c511
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+523c523
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+526c526
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+529c529
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+532c532
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+544c544
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+547c547
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+550c550
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+553c553
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+565c565
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+568c568
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+571c571
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+574c574
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+598c598
+< doTest ()V 127 61
+---
+> doTest ()V 124 61
+630c630
+< doTest ()V 112 54
+---
+> doTest ()V 109 54
+677c677
+< doTest ()V 117 56
+---
+> doTest ()V 114 56
+687c687
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+690c690
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+693c693
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+696c696
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+708c708
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+711c711
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+714c714
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+717c717
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+729c729
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+732c732
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+735c735
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+738c738
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+750c750
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+753c753
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+756c756
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+759c759
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+771c771
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+774c774
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+777c777
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+780c780
+< baz (IIILart/ControlData;)Ljava/lang/Object; 9 34
+---
+> baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
+792c792
+< [public static void art.Frames.doTestSameThread(), 35]
+---
+> [public static void art.Frames.doTestSameThread(), 40]
+807c807
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+810c810
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+813c813
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+816c816
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+827c827
+< [private static void art.Recurse.printOrWait(int,int,art.ControlData), 2c]
+---
+> [private static void art.Recurse.printOrWait(int,int,art.ControlData), 2d]
+831c831
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+834c834
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+837c837
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
+840c840
+< [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 9]
+---
+> [private static java.lang.Object art.Recurse.baz(int,int,int,art.ControlData), 8]
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index d1ea15e..53b5093 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -35,7 +35,7 @@
using_jack=$(get_build_var ANDROID_COMPILE_WITH_JACK)
java_libraries_dir=${out_dir}/target/common/obj/JAVA_LIBRARIES
-common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target"
+common_targets="vogar core-tests apache-harmony-jdwp-tests-hostdex jsr166-tests mockito-target libjdwp"
mode="target"
j_arg="-j$(nproc)"
showcommands=
diff --git a/tools/cpp-define-generator/offset_codeitem.def b/tools/cpp-define-generator/offset_codeitem.def
deleted file mode 100644
index e5acd1d..0000000
--- a/tools/cpp-define-generator/offset_codeitem.def
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within CodeItem.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include <cstddef> // offsetof
-#include "dex_file.h" // art::DexFile
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_CODEITEM_OFFSET(field_name) \
- DEFINE_OFFSET_EXPR(CodeItem, field_name, int32_t, offsetof(art::DexFile::CodeItem, field_name ## _))
-
-// Field Name
-DEFINE_CODEITEM_OFFSET(insns)
-
-#undef DEFINE_CODEITEM_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offsets_all.def b/tools/cpp-define-generator/offsets_all.def
index c2e8c97..31587d8 100644
--- a/tools/cpp-define-generator/offsets_all.def
+++ b/tools/cpp-define-generator/offsets_all.def
@@ -40,7 +40,6 @@
#include "offset_thread.def"
// TODO: SHADOW_FRAME depends on __SIZEOF__POINTER__
// #include "offset_shadow_frame.def"
-#include "offset_codeitem.def"
// TODO: MIRROR_OBJECT_HEADER_SIZE (depends on #ifdef read barrier)
#include "offset_mirror_class.def"
#include "offset_mirror_dex_cache.def"
diff --git a/tools/libjdwp_oj_art_failures.txt b/tools/libjdwp_oj_art_failures.txt
new file mode 100644
index 0000000..e0f243c
--- /dev/null
+++ b/tools/libjdwp_oj_art_failures.txt
@@ -0,0 +1,65 @@
+/*
+ * This file contains expectations for ART's buildbot. The purpose of this file is
+ * to temporarily list failing tests and not break the bots.
+ */
+[
+{
+ description: "Test fails due to unexpectedly getting the thread-groups of zombie threads",
+ result: EXEC_FAILED,
+ bug: 66906414,
+ name: "org.apache.harmony.jpda.tests.jdwp.ThreadReference.ThreadGroup002Test#testThreadGroup002"
+},
+{
+ description: "Test fails due to modifiers not including ACC_SUPER",
+ result: EXEC_FAILED,
+ bug: 66906055,
+ name: "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ModifiersTest#testModifiers001"
+},
+{
+ description: "Test fails due to static values not being set correctly.",
+ result: EXEC_FAILED,
+ bug: 66905894,
+ name: "org.apache.harmony.jpda.tests.jdwp.ReferenceType.GetValues006Test#testGetValues006"
+},
+{
+ description: "Tests fail with assertion error on slot number",
+ result: EXEC_FAILED,
+ bug: 66905468,
+ names: [ "org.apache.harmony.jpda.tests.jdwp.Method.VariableTableTest#testVariableTableTest001",
+ "org.apache.harmony.jpda.tests.jdwp.Method.VariableTableWithGenericTest#testVariableTableWithGenericTest001" ]
+},
+{
+ description: "Test fails with Error VM_DEAD when trying to resume during VM_DEATH event",
+ result: EXEC_FAILED,
+ bug: 66904725,
+ name: "org.apache.harmony.jpda.tests.jdwp.Events.VMDeath002Test#testVMDeathRequest"
+},
+/* TODO Categorize these failures more. */
+{
+ description: "Tests that fail on both ART and RI. These tests are likely incorrect",
+ result: EXEC_FAILED,
+ bug: 66906734,
+ names: [ "org.apache.harmony.jpda.tests.jdwp.ArrayReference.SetValues003Test#testSetValues003_InvalidIndex",
+ "org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethod002Test#testInvokeMethod_wrong_argument_types",
+ "org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethodTest#testInvokeMethod002",
+ "org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethodTest#testInvokeMethod003",
+ "org.apache.harmony.jpda.tests.jdwp.ClassType.NewInstanceTest#testNewInstance002",
+ "org.apache.harmony.jpda.tests.jdwp.ClassType.SetValues002Test#testSetValues002",
+ "org.apache.harmony.jpda.tests.jdwp.Events.ClassPrepare002Test#testClassPrepareCausedByDebugger",
+ "org.apache.harmony.jpda.tests.jdwp.Events.ExceptionCaughtTest#testExceptionEvent_ThrowLocation_FromNative",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.DisableCollectionTest#testDisableCollection_null",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.EnableCollectionTest#testEnableCollection_invalid",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.EnableCollectionTest#testEnableCollection_null",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.GetValues002Test#testGetValues002",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.SetValues003Test#testSetValues003",
+ "org.apache.harmony.jpda.tests.jdwp.ObjectReference.SetValuesTest#testSetValues001",
+ "org.apache.harmony.jpda.tests.jdwp.ReferenceType.FieldsWithGenericTest#testFieldsWithGeneric001",
+ "org.apache.harmony.jpda.tests.jdwp.ReferenceType.GetValues002Test#testGetValues002",
+ "org.apache.harmony.jpda.tests.jdwp.ReferenceType.GetValues004Test#testGetValues004",
+ "org.apache.harmony.jpda.tests.jdwp.StringReference.ValueTest#testStringReferenceValueTest001_NullString",
+ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ChildrenTest#testChildren_NullObject",
+ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.NameTest#testName001_NullObject",
+ "org.apache.harmony.jpda.tests.jdwp.ThreadGroupReference.ParentTest#testParent_NullObject",
+ "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CapabilitiesNewTest#testCapabilitiesNew001" ]
+}
+]
diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh
new file mode 100755
index 0000000..964bb38
--- /dev/null
+++ b/tools/run-libjdwp-tests.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+if [[ ! -d libcore ]]; then
+ echo "Script needs to be run at the root of the android tree"
+ exit 1
+fi
+
+if [[ `uname` != 'Linux' ]]; then
+ echo "Script cannot be run on $(uname). It is Linux only."
+ exit 2
+fi
+
+args=("$@")
+debug="no"
+has_variant="no"
+has_mode="no"
+mode="target"
+
+while true; do
+ if [[ $1 == "--debug" ]]; then
+ debug="yes"
+ shift
+ elif [[ "$1" == "--mode=jvm" ]]; then
+ has_mode="yes"
+ mode="ri"
+ shift
+ elif [[ "$1" == --mode=host ]]; then
+ has_mode="yes"
+ mode="host"
+ shift
+ elif [[ $1 == --variant=* ]]; then
+ has_variant="yes"
+ shift
+ elif [[ "$1" == "" ]]; then
+ break
+ else
+ shift
+ fi
+done
+
+if [[ "$has_mode" = "no" ]]; then
+ args+=(--mode=device)
+fi
+
+if [[ "$has_variant" = "no" ]]; then
+ args+=(--variant=X32)
+fi
+
+# We don't use full paths since it is difficult to determine them for device
+# tests and not needed due to resolution rules of dlopen.
+if [[ "$debug" = "yes" ]]; then
+ args+=(-Xplugin:libopenjdkjvmtid.so)
+else
+ args+=(-Xplugin:libopenjdkjvmti.so)
+fi
+
+expect_path=$PWD/art/tools/libjdwp_oj_art_failures.txt
+function verbose_run() {
+ echo "$@"
+ env "$@"
+}
+
+verbose_run ./art/tools/run-jdwp-tests.sh \
+ "${args[@]}" \
+ --jdwp-path "libjdwp.so" \
+ --expectations "$expect_path"