Merge "ART: Start RuntimeCallbacks"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5da59f3..faf8b41 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1060,13 +1060,13 @@
virtual bool operator()(ObjPtr<mirror::Class> c) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
const auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : c->GetMethods(pointer_size)) {
- ResolveExceptionsForMethod(&m, pointer_size);
+ ResolveExceptionsForMethod(&m);
}
return true;
}
private:
- void ResolveExceptionsForMethod(ArtMethod* method_handle, PointerSize pointer_size)
+ void ResolveExceptionsForMethod(ArtMethod* method_handle)
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile::CodeItem* code_item = method_handle->GetCodeItem();
if (code_item == nullptr) {
@@ -1088,8 +1088,7 @@
dex::TypeIndex encoded_catch_handler_handlers_type_idx =
dex::TypeIndex(DecodeUnsignedLeb128(&encoded_catch_handler_list));
// Add to set of types to resolve if not already in the dex cache resolved types
- if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx,
- pointer_size)) {
+ if (!method_handle->IsResolvedTypeIdx(encoded_catch_handler_handlers_type_idx)) {
exceptions_to_resolve_.emplace(encoded_catch_handler_handlers_type_idx,
method_handle->GetDexFile());
}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 86d92ff..c69ed31 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(164 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(159 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index 7dc094b..2ee4db9 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -153,21 +153,6 @@
return instruction_ == bound.instruction_ && constant_ == bound.constant_;
}
- /*
- * Hunt "under the hood" of array lengths (leading to array references),
- * null checks (also leading to array references), and new arrays
- * (leading to the actual length). This makes it more likely related
- * instructions become actually comparable.
- */
- static HInstruction* HuntForDeclaration(HInstruction* instruction) {
- while (instruction->IsArrayLength() ||
- instruction->IsNullCheck() ||
- instruction->IsNewArray()) {
- instruction = instruction->InputAt(0);
- }
- return instruction;
- }
-
static bool Equal(HInstruction* instruction1, HInstruction* instruction2) {
if (instruction1 == instruction2) {
return true;
@@ -1136,7 +1121,7 @@
}
void VisitNewArray(HNewArray* new_array) OVERRIDE {
- HInstruction* len = new_array->InputAt(0);
+ HInstruction* len = new_array->GetLength();
if (!len->IsIntConstant()) {
HInstruction *left;
int32_t right_const;
@@ -1324,7 +1309,7 @@
InductionVarRange::Value v2;
bool needs_finite_test = false;
HInstruction* index = context->InputAt(0);
- HInstruction* hint = ValueBound::HuntForDeclaration(context->InputAt(1));
+ HInstruction* hint = HuntForDeclaration(context->InputAt(1));
if (induction_range_.GetInductionRange(context, index, hint, &v1, &v2, &needs_finite_test)) {
if (v1.is_known && (v1.a_constant == 0 || v1.a_constant == 1) &&
v2.is_known && (v2.a_constant == 0 || v2.a_constant == 1)) {
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index dfa1504..5d58207 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -596,13 +596,11 @@
HBasicBlock* block = new (allocator) HBasicBlock(graph);
graph->AddBlock(block);
entry->AddSuccessor(block);
+ // We pass a bogus constant for the class to avoid mocking one.
HInstruction* new_array = new (allocator) HNewArray(
constant_10,
- graph->GetCurrentMethod(),
- 0,
- dex::TypeIndex(static_cast<uint16_t>(Primitive::kPrimInt)),
- graph->GetDexFile(),
- kQuickAllocArray);
+ constant_10,
+ 0);
block->AddInstruction(new_array);
block->AddInstruction(new (allocator) HGoto());
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 07b1746..9c9c604 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3984,19 +3984,16 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetOut(Location::RegisterLocation(R0));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorARM::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConvention calling_convention;
- __ LoadImmediate(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
}
void LocationsBuilderARM::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index b094e54..68d0b86 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -4747,22 +4747,16 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
locations->SetOut(LocationFrom(x0));
- locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- InvokeRuntimeCallingConvention calling_convention;
- Register type_index = RegisterFrom(locations->GetTemp(0), Primitive::kPrimInt);
- DCHECK(type_index.Is(w0));
- __ Mov(type_index, instruction->GetTypeIndex().index_);
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
}
void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 05a76e1..592ee5a 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -431,10 +431,12 @@
// TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to
// kSaveEverything and use a temporary for the .bss entry address in the fast path,
// so that we can avoid another calculation here.
+ UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
+ vixl32::Register temp = temps.Acquire();
CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index);
- arm_codegen->EmitMovwMovtPlaceholder(labels, ip);
- __ Str(OutputRegister(cls_), MemOperand(ip));
+ arm_codegen->EmitMovwMovtPlaceholder(labels, temp);
+ __ Str(OutputRegister(cls_), MemOperand(temp));
}
__ B(GetExitLabel());
}
@@ -3995,19 +3997,16 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConventionARMVIXL calling_convention;
- locations->AddTemp(LocationFrom(calling_convention.GetRegisterAt(0)));
locations->SetOut(LocationFrom(r0));
locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(1)));
locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(2)));
}
void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConventionARMVIXL calling_convention;
- __ Mov(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
}
void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 24234e1..a038382 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5897,21 +5897,14 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorMIPS::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConvention calling_convention;
- Register current_method_register = calling_convention.GetRegisterAt(2);
- __ Lw(current_method_register, SP, kCurrentMethodStackOffset);
- // Move an uint16_t value to a register.
- __ LoadConst32(calling_convention.GetRegisterAt(0), instruction->GetTypeIndex().index_);
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck,
- void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
}
void LocationsBuilderMIPS::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index fc8fb7a..446dea6 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -3841,19 +3841,14 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetOut(calling_convention.GetReturnLocation(Primitive::kPrimNot));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorMIPS64::VisitNewArray(HNewArray* instruction) {
- LocationSummary* locations = instruction->GetLocations();
- // Move an uint16_t value to a register.
- __ LoadConst32(locations->GetTemp(0).AsRegister<GpuRegister>(),
- instruction->GetTypeIndex().index_);
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
}
void LocationsBuilderMIPS64::VisitNewInstance(HNewInstance* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index cc727d2..853c91f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4192,18 +4192,15 @@
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
locations->SetOut(Location::RegisterLocation(EAX));
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorX86::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConvention calling_convention;
- __ movl(calling_convention.GetRegisterAt(0), Immediate(instruction->GetTypeIndex().index_));
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
DCHECK(!codegen_->IsLeafMethod());
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 9adedab..74c71cc 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -4088,21 +4088,16 @@
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnMainOnly);
InvokeRuntimeCallingConvention calling_convention;
- locations->AddTemp(Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
locations->SetOut(Location::RegisterLocation(RAX));
- locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
- locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(2)));
+ locations->SetInAt(0, Location::RegisterLocation(calling_convention.GetRegisterAt(0)));
+ locations->SetInAt(1, Location::RegisterLocation(calling_convention.GetRegisterAt(1)));
}
void InstructionCodeGeneratorX86_64::VisitNewArray(HNewArray* instruction) {
- InvokeRuntimeCallingConvention calling_convention;
- codegen_->Load64BitValue(CpuRegister(calling_convention.GetRegisterAt(0)),
- instruction->GetTypeIndex().index_);
// Note: if heap poisoning is enabled, the entry point takes cares
// of poisoning the reference.
- codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
- CheckEntrypointTypes<kQuickAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*>();
-
+ codegen_->InvokeRuntime(kQuickAllocArrayResolved, instruction, instruction->GetDexPc());
+ CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
DCHECK(!codegen_->IsLeafMethod());
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 6d8ae75..3973985 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -114,12 +114,7 @@
}
} else {
*suitable = instruction;
- while (instruction->IsArrayLength() ||
- instruction->IsNullCheck() ||
- instruction->IsNewArray()) {
- instruction = instruction->InputAt(0);
- }
- return instruction == hint;
+ return HuntForDeclaration(instruction) == hint;
}
return false;
}
@@ -629,7 +624,7 @@
if (chase_hint_ == nullptr) {
return is_min ? Value(0) : Value(std::numeric_limits<int32_t>::max());
} else if (instruction->InputAt(0)->IsNewArray()) {
- return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
+ return GetFetch(instruction->InputAt(0)->AsNewArray()->GetLength(), trip, in_body, is_min);
}
} else if (instruction->IsTypeConversion()) {
// Since analysis is 32-bit (or narrower), chase beyond widening along the path.
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index aa3e1aa..d81817f 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -697,13 +697,8 @@
}
TEST_F(InductionVarRangeTest, ArrayLengthAndHints) {
- HInstruction* new_array = new (&allocator_)
- HNewArray(x_,
- graph_->GetCurrentMethod(),
- 0,
- dex::TypeIndex(Primitive::kPrimInt),
- graph_->GetDexFile(),
- kQuickAllocArray);
+ // We pass a bogus constant for the class to avoid mocking one.
+ HInstruction* new_array = new (&allocator_) HNewArray(x_, x_, 0);
entry_block_->AddInstruction(new_array);
HInstruction* array_length = new (&allocator_) HArrayLength(new_array, 0);
entry_block_->AddInstruction(array_length);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 50aa442..5d40f75 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1429,15 +1429,6 @@
return false;
}
- if (current->IsNewArray() &&
- (current->AsNewArray()->GetEntrypoint() == kQuickAllocArrayWithAccessCheck)) {
- VLOG(compiler) << "Method " << callee_dex_file.PrettyMethod(method_index)
- << " could not be inlined because it is using an entrypoint"
- << " with access checks";
- // Allocation entrypoint does not handle inlined frames.
- return false;
- }
-
if (current->IsUnresolvedStaticFieldGet() ||
current->IsUnresolvedInstanceFieldGet() ||
current->IsUnresolvedStaticFieldSet() ||
@@ -1538,8 +1529,6 @@
}
}
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
-
// Iterate over the list of parameter types and test whether any of the
// actual inputs has a more specific reference type than the type declared in
// the signature.
@@ -1551,9 +1540,9 @@
++param_idx, ++input_idx) {
HInstruction* input = invoke_instruction->InputAt(input_idx);
if (input->GetType() == Primitive::kPrimNot) {
- mirror::Class* param_cls = resolved_method->GetDexCacheResolvedType(
+ mirror::Class* param_cls = resolved_method->GetClassFromTypeIndex(
param_list->GetTypeItem(param_idx).type_idx_,
- pointer_size);
+ /* resolve */ false);
if (IsReferenceTypeRefinement(GetClassRTI(param_cls),
/* declared_can_be_null */ true,
input)) {
@@ -1602,8 +1591,7 @@
// TODO: we could be more precise by merging the phi inputs but that requires
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
+ mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */);
return_replacement->SetReferenceTypeInfo(GetClassRTI(cls));
}
}
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 8ed0e7f..48653cf 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1498,16 +1498,8 @@
uint32_t* args,
uint32_t register_index) {
HInstruction* length = graph_->GetIntConstant(number_of_vreg_arguments, dex_pc);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- HInstruction* object = new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint);
+ HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
+ HInstruction* object = new (arena_) HNewArray(cls, length, dex_pc);
AppendInstruction(object);
const char* descriptor = dex_file_->StringByTypeIdx(type_index);
@@ -2503,16 +2495,8 @@
case Instruction::NEW_ARRAY: {
dex::TypeIndex type_index(instruction.VRegC_22c());
HInstruction* length = LoadLocal(instruction.VRegB_22c(), Primitive::kPrimInt);
- bool finalizable;
- QuickEntrypointEnum entrypoint = NeedsAccessCheck(type_index, &finalizable)
- ? kQuickAllocArrayWithAccessCheck
- : kQuickAllocArray;
- AppendInstruction(new (arena_) HNewArray(length,
- graph_->GetCurrentMethod(),
- dex_pc,
- type_index,
- *dex_compilation_unit_->GetDexFile(),
- entrypoint));
+ HLoadClass* cls = BuildLoadClass(type_index, dex_pc, /* check_access */ true);
+ AppendInstruction(new (arena_) HNewArray(cls, length, dex_pc));
UpdateLocal(instruction.VRegA_22c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 911bfb9..35f59cb 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -777,7 +777,7 @@
// If the array is a NewArray with constant size, replace the array length
// with the constant instruction. This helps the bounds check elimination phase.
if (input->IsNewArray()) {
- input = input->InputAt(0);
+ input = input->AsNewArray()->GetLength();
if (input->IsIntConstant()) {
instruction->ReplaceWith(input);
}
@@ -1774,7 +1774,7 @@
}
if (potential_array->IsNewArray()) {
- return potential_array->InputAt(0) == potential_length;
+ return potential_array->AsNewArray()->GetLength() == potential_length;
}
return false;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 53b0fdd..a2980dc 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -3801,6 +3801,15 @@
entrypoint_ = entrypoint;
}
+ HLoadClass* GetLoadClass() const {
+ HInstruction* input = InputAt(0);
+ if (input->IsClinitCheck()) {
+ input = input->InputAt(0);
+ }
+ DCHECK(input->IsLoadClass());
+ return input->AsLoadClass();
+ }
+
bool IsStringAlloc() const;
DECLARE_INSTRUCTION(NewInstance);
@@ -4355,23 +4364,12 @@
class HNewArray FINAL : public HExpression<2> {
public:
- HNewArray(HInstruction* length,
- HCurrentMethod* current_method,
- uint32_t dex_pc,
- dex::TypeIndex type_index,
- const DexFile& dex_file,
- QuickEntrypointEnum entrypoint)
- : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
- type_index_(type_index),
- dex_file_(dex_file),
- entrypoint_(entrypoint) {
- SetRawInputAt(0, length);
- SetRawInputAt(1, current_method);
+ HNewArray(HInstruction* cls, HInstruction* length, uint32_t dex_pc)
+ : HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc) {
+ SetRawInputAt(0, cls);
+ SetRawInputAt(1, length);
}
- dex::TypeIndex GetTypeIndex() const { return type_index_; }
- const DexFile& GetDexFile() const { return dex_file_; }
-
// Calls runtime so needs an environment.
bool NeedsEnvironment() const OVERRIDE { return true; }
@@ -4380,15 +4378,18 @@
bool CanBeNull() const OVERRIDE { return false; }
- QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; }
+ HLoadClass* GetLoadClass() const {
+ DCHECK(InputAt(0)->IsLoadClass());
+ return InputAt(0)->AsLoadClass();
+ }
+
+ HInstruction* GetLength() const {
+ return InputAt(1);
+ }
DECLARE_INSTRUCTION(NewArray);
private:
- const dex::TypeIndex type_index_;
- const DexFile& dex_file_;
- const QuickEntrypointEnum entrypoint_;
-
DISALLOW_COPY_AND_ASSIGN(HNewArray);
};
@@ -5891,7 +5892,10 @@
bool CanThrow() const OVERRIDE { return true; }
- HLoadClass* GetLoadClass() const { return InputAt(0)->AsLoadClass(); }
+ HLoadClass* GetLoadClass() const {
+ DCHECK(InputAt(0)->IsLoadClass());
+ return InputAt(0)->AsLoadClass();
+ }
DECLARE_INSTRUCTION(ClinitCheck);
@@ -6757,6 +6761,23 @@
std::copy_backward(blocks->begin() + after + 1u, blocks->begin() + old_size, blocks->end());
}
+/*
+ * Hunt "under the hood" of array lengths (leading to array references),
+ * null checks (also leading to array references), and new arrays
+ * (leading to the actual length). This makes it more likely related
+ * instructions become actually comparable.
+ */
+inline HInstruction* HuntForDeclaration(HInstruction* instruction) {
+ while (instruction->IsArrayLength() ||
+ instruction->IsNullCheck() ||
+ instruction->IsNewArray()) {
+ instruction = instruction->IsNewArray()
+ ? instruction->AsNewArray()->GetLength()
+ : instruction->InputAt(0);
+ }
+ return instruction;
+}
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_NODES_H_
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index a4d59ab..b02f250 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -548,11 +548,13 @@
}
void ReferenceTypePropagation::RTPVisitor::VisitNewInstance(HNewInstance* instr) {
- UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+ ScopedObjectAccess soa(Thread::Current());
+ SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
}
void ReferenceTypePropagation::RTPVisitor::VisitNewArray(HNewArray* instr) {
- UpdateReferenceTypeInfo(instr, instr->GetTypeIndex(), instr->GetDexFile(), /* is_exact */ true);
+ ScopedObjectAccess soa(Thread::Current());
+ SetClassAsTypeInfo(instr, instr->GetLoadClass()->GetClass().Get(), /* is_exact */ true);
}
static mirror::Class* GetClassFromDexCache(Thread* self,
@@ -840,10 +842,8 @@
}
ScopedObjectAccess soa(Thread::Current());
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = cl->GetImagePointerSize();
ArtMethod* method = instr->GetResolvedMethod();
- mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(false, pointer_size);
+ mirror::Class* klass = (method == nullptr) ? nullptr : method->GetReturnType(/* resolve */ false);
SetClassAsTypeInfo(instr, klass, /* is_exact */ false);
}
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 5e55210..a3fce02 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 c2c0 ldr.w ip, [r9, #704] ; 0x2c0\n",
+ " 21e: f8d9 c2ac ldr.w ip, [r9, #684] ; 0x2ac\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 102c313..db1cad6 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -856,27 +856,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * r0 = array, r1 = index, r2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- tst r0, r0
- bne art_quick_aput_obj_with_bound_check
- b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
- .hidden art_quick_aput_obj_with_bound_check
-ENTRY art_quick_aput_obj_with_bound_check
- ldr r3, [r0, #MIRROR_ARRAY_LENGTH_OFFSET]
- cmp r3, r1
- bhi art_quick_aput_obj
- mov r0, r1
- mov r1, r3
- b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 3b3783c..436d331 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1404,33 +1404,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * x0 = array, x1 = index, x2 = value
- *
- * Currently all values should fit into w0/w1/w2, and w1 always will as indices are 32b. We
- * assume, though, that the upper 32b are zeroed out. At least for x1/w1 we can do better by
- * using index-zero-extension in load/stores.
- *
- * Temporaries: x3, x4
- * TODO: x4 OK? ip seems wrong here.
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- tst x0, x0
- bne art_quick_aput_obj_with_bound_check
- b art_quick_throw_null_pointer_exception
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- ldr w3, [x0, #MIRROR_ARRAY_LENGTH_OFFSET]
- cmp w3, w1
- bhi art_quick_aput_obj
- mov x0, x1
- mov x1, x3
- b art_quick_throw_array_bounds
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
@@ -1672,11 +1645,9 @@
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB) implemented in asm
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -1764,13 +1735,6 @@
// The common fast path code for art_quick_alloc_array_region_tlab.
-.macro ALLOC_ARRAY_TLAB_FAST_PATH slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
- // Check null class
- cbz \wClass, \slowPathLabel
- ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED \slowPathLabel, \xClass, \wClass, \xCount, \wCount, \xTemp0, \wTemp0, \xTemp1, \wTemp1, \xTemp2, \wTemp2
-.endm
-
-// The common fast path code for art_quick_alloc_array_region_tlab.
.macro ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED slowPathLabel, xClass, wClass, xCount, wCount, xTemp0, wTemp0, xTemp1, wTemp1, xTemp2, wTemp2
// Array classes are never finalizable or uninitialized, no need to check.
ldr \wTemp0, [\xClass, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Load component type
@@ -1907,64 +1871,31 @@
// TODO: We could use this macro for the normal tlab allocator too.
// The common code for art_quick_alloc_array_*region_tlab
-.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path, is_resolved
+.macro GENERATE_ALLOC_ARRAY_REGION_TLAB name, entrypoint, fast_path
ENTRY \name
// Fast path array allocation for region tlab allocation.
- // x0: uint32_t type_idx
+ // x0: mirror::Class* type
// x1: int32_t component_count
- // x2: ArtMethod* method
- // x3-x7: free.
+ // x2-x7: free.
#if !defined(USE_READ_BARRIER)
mvn x0, xzr // Read barrier must be enabled here.
ret // Return -1.
#endif
-.if \is_resolved
mov x3, x0
- // If already resolved, class is stored in x0
-.else
- ldr x3, [x2, #ART_METHOD_DEX_CACHE_TYPES_OFFSET_64] // Load dex cache resolved types array
- // Load the class (x2)
- ldr w3, [x3, x0, lsl #COMPRESSED_REFERENCE_SIZE_SHIFT]
-.endif
- // Most common case: GC is not marking.
- ldr w4, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
- cbnz x4, .Lmarking\name
-.Ldo_allocation\name:
\fast_path .Lslow_path\name, x3, w3, x1, w1, x4, w4, x5, w5, x6, w6
-.Lmarking\name:
- // GC is marking, check the lock word of the class for the mark bit.
- // If the class is null, go slow path. The check is required to read the lock word.
- cbz w3, .Lslow_path\name
- // Class is not null, check mark bit in lock word.
- ldr w4, [x3, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
- // If the bit is not zero, do the allocation.
- tbnz w4, #LOCK_WORD_MARK_BIT_SHIFT, .Ldo_allocation\name
- // The read barrier slow path. Mark
- // the class.
- stp x0, x1, [sp, #-32]! // Save registers (x0, x1, x2, lr).
- stp x2, xLR, [sp, #16]
- mov x0, x3 // Pass the class as the first param.
- bl artReadBarrierMark
- mov x3, x0 // Get the (marked) class back.
- ldp x2, xLR, [sp, #16]
- ldp x0, x1, [sp], #32 // Restore registers.
- b .Ldo_allocation\name
.Lslow_path\name:
- // x0: uint32_t type_idx / mirror::Class* klass (if resolved)
+ // x0: mirror::Class* klass
// x1: int32_t component_count
- // x2: ArtMethod* method
- // x3: Thread* self
+ // x2: Thread* self
SETUP_SAVE_REFS_ONLY_FRAME // save callee saves in case of GC
- mov x3, xSELF // pass Thread::Current
+ mov x2, xSELF // pass Thread::Current
bl \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
END \name
.endm
-GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_region_tlab, artAllocArrayFromCodeRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH, 0
-// TODO: art_quick_alloc_array_resolved_region_tlab seems to not get called. Investigate compiler.
-GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, 1
+GENERATE_ALLOC_ARRAY_REGION_TLAB art_quick_alloc_array_resolved_region_tlab, artAllocArrayFromCodeResolvedRegionTLAB, ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED
/*
* Called by managed code when the thread has been asked to suspend.
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 5c56923..36f9ea7 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -142,16 +142,8 @@
static_assert(!IsDirectEntrypoint(kQuickGetObjStatic), "Non-direct C stub marked direct.");
// Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- static_assert(!IsDirectEntrypoint(kQuickAputObjectWithNullAndBoundCheck),
- "Non-direct C stub marked direct.");
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
- static_assert(!IsDirectEntrypoint(kQuickAputObjectWithBoundCheck),
- "Non-direct C stub marked direct.");
qpoints->pAputObject = art_quick_aput_obj;
static_assert(!IsDirectEntrypoint(kQuickAputObject), "Non-direct C stub marked direct.");
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
- static_assert(!IsDirectEntrypoint(kQuickHandleFillArrayData), "Non-direct C stub marked direct.");
// JNI
qpoints->pJniMethodStart = JniMethodStart;
@@ -262,6 +254,7 @@
art_quick_invoke_virtual_trampoline_with_access_check;
static_assert(!IsDirectEntrypoint(kQuickInvokeVirtualTrampolineWithAccessCheck),
"Non-direct C stub marked direct.");
+ qpoints->pInvokePolymorphic = art_quick_invoke_polymorphic;
// Thread
qpoints->pTestSuspend = art_quick_test_suspend;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 3acc0a9..76218fb 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -1389,28 +1389,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * a0 = array, a1 = index, a2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- bnez $a0, .Lart_quick_aput_obj_with_bound_check_gp_set
- nop
- b art_quick_throw_null_pointer_exception
- nop
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- lw $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
- sltu $t1, $a1, $t0
- bnez $t1, .Lart_quick_aput_obj_gp_set
- nop
- move $a0, $a1
- b art_quick_throw_array_bounds
- move $a1, $t0
-END art_quick_aput_obj_with_bound_check
-
#ifdef USE_READ_BARRIER
.extern artReadBarrierSlow
#endif
@@ -2285,15 +2263,12 @@
ENTRY art_quick_invoke_polymorphic
SETUP_SAVE_REFS_AND_ARGS_FRAME
move $a2, rSELF # Make $a2 an alias for the current Thread.
- move $a3, $sp # Make $a3 a pointer to the saved frame context.
- addiu $sp, $sp, -24 # Reserve space for JValue result and 4 words for callee.
- .cfi_adjust_cfa_offset 24
+ addiu $a3, $sp, ARG_SLOT_SIZE # Make $a3 a pointer to the saved frame context.
sw $zero, 20($sp) # Initialize JValue result.
sw $zero, 16($sp)
- addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
la $t9, artInvokePolymorphic
jalr $t9 # (result, receiver, Thread*, context)
- nop
+ addiu $a0, $sp, 16 # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
beq $v0, $t0, \handler
@@ -2307,18 +2282,17 @@
MATCH_RETURN_TYPE 'D', .Lstore_double_result
MATCH_RETURN_TYPE 'F', .Lstore_float_result
MATCH_RETURN_TYPE 'S', .Lstore_int_result
+ MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
.purgem MATCH_RETURN_TYPE
nop
b .Lcleanup_and_return
nop
.Lstore_boolean_result:
+ b .Lcleanup_and_return
lbu $v0, 16($sp) # Move byte from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_char_result:
- lhu $v0, 16($sp) # Move char from JValue result to return value register.
b .Lcleanup_and_return
- nop
+ lhu $v0, 16($sp) # Move char from JValue result to return value register.
.Lstore_double_result:
.Lstore_float_result:
LDu $f0, $f1, 16, $sp, $t0 # Move double/float from JValue result to return value register.
@@ -2331,8 +2305,6 @@
lw $v0, 16($sp) # Move lower bits from JValue result to return value register.
// Fall-through to clean up and return.
.Lcleanup_and_return:
- addiu $sp, $sp, 24 # Remove space for JValue result and the 4 words for the callee.
- .cfi_adjust_cfa_offset -24
lw $t7, THREAD_EXCEPTION_OFFSET(rSELF) # Load Thread::Current()->exception_
RESTORE_SAVE_REFS_AND_ARGS_FRAME
bnez $t7, 1f # Success if no exception is pending.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index ae786fe..b53fd10 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1360,29 +1360,6 @@
#endif // USE_READ_BARRIER
.endm
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * a0 = array, a1 = index, a2 = value
- */
-ENTRY art_quick_aput_obj_with_null_and_bound_check
- bne $a0, $zero, .Lart_quick_aput_obj_with_bound_check_gp_set
- nop
- b art_quick_throw_null_pointer_exception
- .cpreturn # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_null_and_bound_check
-
-ENTRY art_quick_aput_obj_with_bound_check
- lwu $t0, MIRROR_ARRAY_LENGTH_OFFSET($a0)
- sltu $t1, $a1, $t0
- bne $t1, $zero, .Lart_quick_aput_obj_gp_set
- nop
- move $a0, $a1
- move $a1, $t0
- b art_quick_throw_array_bounds
- .cpreturn # Restore gp from t8 in branch delay slot.
-END art_quick_aput_obj_with_bound_check
-
ENTRY art_quick_aput_obj
beq $a2, $zero, .Ldo_aput_null
nop
@@ -2132,9 +2109,8 @@
daddiu $sp, $sp, -8 # Reserve space for JValue result.
.cfi_adjust_cfa_offset 8
sd $zero, 0($sp) # Initialize JValue result.
- move $a0, $sp # Make $a0 a pointer to the JValue result
jal artInvokePolymorphic # (result, receiver, Thread*, context)
- nop
+ move $a0, $sp # Make $a0 a pointer to the JValue result
.macro MATCH_RETURN_TYPE c, handler
li $t0, \c
beq $v0, $t0, \handler
@@ -2148,27 +2124,24 @@
MATCH_RETURN_TYPE 'D', .Lstore_double_result
MATCH_RETURN_TYPE 'F', .Lstore_float_result
MATCH_RETURN_TYPE 'S', .Lstore_long_result
+ MATCH_RETURN_TYPE 'Z', .Lstore_boolean_result
.purgem MATCH_RETURN_TYPE
nop
b .Lcleanup_and_return
nop
.Lstore_boolean_result:
+ b .Lcleanup_and_return
lbu $v0, 0($sp) # Move byte from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_char_result:
- lhu $v0, 0($sp) # Move char from JValue result to return value register.
b .Lcleanup_and_return
- nop
+ lhu $v0, 0($sp) # Move char from JValue result to return value register.
.Lstore_double_result:
.Lstore_float_result:
+ b .Lcleanup_and_return
l.d $f0, 0($sp) # Move double/float from JValue result to return value register.
- b .Lcleanup_and_return
- nop
.Lstore_ref_result:
- lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register.
b .Lcleanup_and_return
- nop
+ lwu $v0, 0($sp) # Move zero extended lower 32-bits to return value register.
.Lstore_long_result:
ld $v0, 0($sp) # Move long from JValue result to return value register.
// Fall-through to clean up and return.
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index abd9046..cd6424a 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -25,14 +25,10 @@
// Called by managed code to allocate an array.
THREE_ARG_DOWNCALL art_quick_alloc_array\c_suffix, artAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array of a resolve class.
-THREE_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate an array when the caller doesn't know whether it has access
// to the created type.
THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check\c_suffix, artAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array\c_suffix, artCheckAndAllocArrayFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-// Called by managed code to allocate an array in a special case for FILLED_NEW_ARRAY.
-THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check\c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate a string from bytes
FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
// Called by managed code to allocate a string from chars
@@ -68,13 +64,9 @@
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(c_suffix, cxx_suffix) \
THREE_ARG_DOWNCALL art_quick_alloc_array ## c_suffix, artAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
THREE_ARG_DOWNCALL art_quick_alloc_array_with_access_check ## c_suffix, artAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_check_and_alloc_array ## c_suffix, artCheckAndAllocArrayFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-#define GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_check_and_alloc_array_with_access_check ## c_suffix, artCheckAndAllocArrayFromCodeWithAccessCheck ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
@@ -95,8 +87,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -110,8 +100,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -129,8 +117,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc, DlMalloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc, DlMalloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc, DlMalloc)
@@ -141,8 +127,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_dlmalloc_instrumented, DlMallocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_dlmalloc_instrumented, DlMallocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_dlmalloc_instrumented, DlMallocInstrumented)
@@ -154,8 +138,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc, RosAlloc)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc, RosAlloc)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc, RosAlloc)
@@ -166,8 +148,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_rosalloc_instrumented, RosAllocInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_rosalloc_instrumented, RosAllocInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_rosalloc_instrumented, RosAllocInstrumented)
@@ -178,8 +158,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer, BumpPointer)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer, BumpPointer)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer, BumpPointer)
@@ -190,8 +168,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_bump_pointer_instrumented, BumpPointerInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_bump_pointer_instrumented, BumpPointerInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_bump_pointer_instrumented, BumpPointerInstrumented)
@@ -202,8 +178,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab_instrumented, TLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab_instrumented, TLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab_instrumented, TLABInstrumented)
@@ -214,8 +188,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region, Region)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region, Region)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region, Region)
@@ -226,8 +198,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_instrumented, RegionInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_instrumented, RegionInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_instrumented, RegionInstrumented)
@@ -238,8 +208,6 @@
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab_instrumented, RegionTLABInstrumented)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab_instrumented, RegionTLABInstrumented)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab_instrumented, RegionTLABInstrumented)
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index ee65fa8..393dfe6 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -908,139 +908,6 @@
#endif
}
-
-TEST_F(StubTest, APutObj) {
-#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
- (defined(__x86_64__) && !defined(__APPLE__))
- Thread* self = Thread::Current();
-
- // Do not check non-checked ones, we'd need handlers and stuff...
- const uintptr_t art_quick_aput_obj_with_null_and_bound_check =
- StubTest::GetEntrypoint(self, kQuickAputObjectWithNullAndBoundCheck);
-
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
-
- StackHandleScope<5> hs(soa.Self());
- Handle<mirror::Class> c(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- Handle<mirror::Class> ca(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/String;")));
-
- // Build a string array of size 1
- Handle<mirror::ObjectArray<mirror::Object>> array(
- hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), 10)));
-
- // Build a string -> should be assignable
- Handle<mirror::String> str_obj(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
-
- // Build a generic object -> should fail assigning
- Handle<mirror::Object> obj_obj(hs.NewHandle(c->AllocObject(soa.Self())));
-
- // Play with it...
-
- // 1) Success cases
- // 1.1) Assign str_obj to array[0..3]
-
- EXPECT_FALSE(self->IsExceptionPending());
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(0));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(1));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(2));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(str_obj.Get(), array->Get(3));
-
- // 1.2) Assign null to array[0..3]
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(0));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 1U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(1));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 2U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(2));
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 3U, reinterpret_cast<size_t>(nullptr),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_EQ(nullptr, array->Get(3));
-
- // TODO: Check _which_ exception is thrown. Then make 3) check that it's the right check order.
-
- // 2) Failure cases (str into str[])
- // 2.1) Array = null
- // TODO: Throwing NPE needs actual DEX code
-
-// Invoke3(reinterpret_cast<size_t>(nullptr), 0U, reinterpret_cast<size_t>(str_obj.Get()),
-// reinterpret_cast<uintptr_t>(&art_quick_aput_obj_with_null_and_bound_check), self);
-//
-// EXPECT_TRUE(self->IsExceptionPending());
-// self->ClearException();
-
- // 2.2) Index < 0
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), static_cast<size_t>(-1),
- reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // 2.3) Index > 0
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 10U, reinterpret_cast<size_t>(str_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // 3) Failure cases (obj into str[])
-
- Invoke3(reinterpret_cast<size_t>(array.Get()), 0U, reinterpret_cast<size_t>(obj_obj.Get()),
- art_quick_aput_obj_with_null_and_bound_check, self);
-
- EXPECT_TRUE(self->IsExceptionPending());
- self->ClearException();
-
- // Tests done.
-#else
- LOG(INFO) << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA;
- // Force-print to std::cout so it's also outside the logcat.
- std::cout << "Skipping aput_obj as I don't know how to do that on " << kRuntimeISA << std::endl;
-#endif
-}
-
TEST_F(StubTest, AllocObject) {
#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) || defined(__mips__) || \
(defined(__x86_64__) && !defined(__APPLE__))
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 1d979d8..c420259 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -1352,26 +1352,6 @@
#endif // USE_READ_BARRIER
END_MACRO
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- * eax = array, ecx = index, edx = value
- */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
- testl %eax, %eax
- jnz SYMBOL(art_quick_aput_obj_with_bound_check)
- jmp SYMBOL(art_quick_throw_null_pointer_exception)
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
- movl MIRROR_ARRAY_LENGTH_OFFSET(%eax), %ebx
- cmpl %ebx, %ecx
- jb SYMBOL(art_quick_aput_obj)
- mov %ecx, %eax
- mov %ebx, %ecx
- jmp SYMBOL(art_quick_throw_array_bounds)
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
DEFINE_FUNCTION art_quick_aput_obj
test %edx, %edx // store of null
jz .Ldo_aput_null
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 28034c9..f5509bd 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -986,11 +986,9 @@
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_region_tlab, RegionTLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_region_tlab, RegionTLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_region_tlab, RegionTLAB)
@@ -999,9 +997,8 @@
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB)
// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB)
+GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY(_tlab, TLAB)
-GENERATE_ALLOC_ENTRYPOINTS_CHECK_AND_ALLOC_ARRAY_WITH_ACCESS_CHECK(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(_tlab, TLAB)
GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(_tlab, TLAB)
@@ -1119,12 +1116,11 @@
END_MACRO
// The fast path code for art_quick_alloc_array_region_tlab.
-// Inputs: RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod* method
-// Temps: RCX: the class, r8, r9
+// Inputs: RDI: the class, RSI: int32_t component_count
+// Free temps: RCX, RDX, R8, R9
// Output: RAX: return value.
MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel)
- movq %rcx, %r8 // Save class for later
- movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx // Load component type.
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rdi), %ecx // Load component type.
UNPOISON_HEAP_REF ecx
movl MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET(%rcx), %ecx // Load primitive type.
shrq LITERAL(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT), %rcx // Get component size shift.
@@ -1151,8 +1147,8 @@
// Store the class pointer in the
// header.
// No fence needed for x86.
- POISON_HEAP_REF r8d
- movl %r8d, MIRROR_OBJECT_CLASS_OFFSET(%rax)
+ POISON_HEAP_REF edi
+ movl %edi, MIRROR_OBJECT_CLASS_OFFSET(%rax)
movl %esi, MIRROR_ARRAY_LENGTH_OFFSET(%rax)
ret // Fast path succeeded.
END_MACRO
@@ -1173,8 +1169,8 @@
MACRO1(ALLOC_ARRAY_TLAB_SLOW_PATH, cxx_name)
SETUP_SAVE_REFS_ONLY_FRAME // save ref containing registers for GC
// Outgoing argument set up
- movq %gs:THREAD_SELF_OFFSET, %rcx // pass Thread::Current()
- call CALLVAR(cxx_name) // cxx_name(arg0, arg1, arg2, Thread*)
+ movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
+ call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
END_MACRO
@@ -1199,73 +1195,21 @@
ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedTLAB
END_FUNCTION art_quick_alloc_object_initialized_tlab
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_tlab, TLAB).
-DEFINE_FUNCTION art_quick_alloc_array_tlab
- // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod*
- // RCX: klass, R8, R9: free. RAX: return val.
- movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array
- movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class
- testl %ecx, %ecx
- jz .Lart_quick_alloc_array_tlab_slow_path
- ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_tlab_slow_path
-.Lart_quick_alloc_array_tlab_slow_path:
- ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeTLAB
-END_FUNCTION art_quick_alloc_array_tlab
-
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_tlab, TLAB).
DEFINE_FUNCTION art_quick_alloc_array_resolved_tlab
- // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod*
- // RCX: mirror::Class* klass, R8, R9: free. RAX: return val.
- movq %rdi, %rcx
- // Already resolved, no null check.
+ // RDI: mirror::Class* klass, RSI: int32_t component_count
+ // RDX, RCX, R8, R9: free. RAX: return val.
ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_tlab_slow_path
.Lart_quick_alloc_array_resolved_tlab_slow_path:
ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedTLAB
END_FUNCTION art_quick_alloc_array_resolved_tlab
-// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY(_region_tlab, RegionTLAB).
-DEFINE_FUNCTION art_quick_alloc_array_region_tlab
- // Fast path region tlab allocation.
- // RDI: uint32_t type_idx, RSI: int32_t component_count, RDX: ArtMethod*
- // RCX: klass, R8, R9: free. RAX: return val.
- ASSERT_USE_READ_BARRIER
- movq ART_METHOD_DEX_CACHE_TYPES_OFFSET_64(%rdx), %rcx // Load dex cache resolved types array
- movl 0(%rcx, %rdi, COMPRESSED_REFERENCE_SIZE), %ecx // Load the class
- // Null check so that we can load the lock word.
- testl %ecx, %ecx
- jz .Lart_quick_alloc_array_region_tlab_slow_path
- // Since we have allocation entrypoint switching, we know the GC is marking.
- // Check the mark bit, if it is 0, do the read barrier mark.
- testl LITERAL(LOCK_WORD_MARK_BIT_MASK_SHIFTED), MIRROR_OBJECT_LOCK_WORD_OFFSET(%ecx)
- jz .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path
-.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit:
- ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_region_tlab_slow_path
-.Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path:
- // The read barrier slow path. Mark the class.
- PUSH rdi
- PUSH rsi
- PUSH rdx
- // Outgoing argument set up
- movq %rcx, %rdi // Pass the class as the first param.
- call SYMBOL(artReadBarrierMark) // cxx_name(mirror::Object* obj)
- movq %rax, %rcx
- POP rdx
- POP rsi
- POP rdi
- jmp .Lart_quick_alloc_array_region_tlab_class_load_read_barrier_slow_path_exit
-.Lart_quick_alloc_array_region_tlab_slow_path:
- ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeRegionTLAB
-END_FUNCTION art_quick_alloc_array_region_tlab
-
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(_region_tlab, RegionTLAB).
DEFINE_FUNCTION art_quick_alloc_array_resolved_region_tlab
// Fast path region tlab allocation.
- // RDI: mirror::Class* klass, RSI: int32_t component_count, RDX: ArtMethod*
- // RCX: mirror::Class* klass, R8, R9: free. RAX: return val.
+ // RDI: mirror::Class* klass, RSI: int32_t component_count
+ // RCX, RDX, R8, R9: free. RAX: return val.
ASSERT_USE_READ_BARRIER
- movq %rdi, %rcx
- // Caller is responsible for read barrier.
- // Already resolved, no null check.
ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED .Lart_quick_alloc_array_resolved_region_tlab_slow_path
.Lart_quick_alloc_array_resolved_region_tlab_slow_path:
ALLOC_ARRAY_TLAB_SLOW_PATH artAllocArrayFromCodeResolvedRegionTLAB
@@ -1466,7 +1410,7 @@
* 64b PUSH/POP and 32b argument.
* TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
*
- * As with art_quick_aput_obj* functions, the 64b versions are in comments.
+ * As with art_quick_aput_obj function, the 64b versions are in comments.
*/
MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
#ifdef USE_READ_BARRIER
@@ -1503,46 +1447,6 @@
#endif // USE_READ_BARRIER
END_MACRO
- /*
- * Entry from managed code for array put operations of objects where the value being stored
- * needs to be checked for compatibility.
- *
- * Currently all the parameters should fit into the 32b portions of the registers. Index always
- * will. So we optimize for a tighter encoding. The 64b versions are in comments.
- *
- * rdi(edi) = array, rsi(esi) = index, rdx(edx) = value
- */
-DEFINE_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-#if defined(__APPLE__)
- int3
- int3
-#else
- testl %edi, %edi
-// testq %rdi, %rdi
- jnz art_quick_aput_obj_with_bound_check
- jmp art_quick_throw_null_pointer_exception
-#endif // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_null_and_bound_check
-
-
-DEFINE_FUNCTION art_quick_aput_obj_with_bound_check
-#if defined(__APPLE__)
- int3
- int3
-#else
- movl MIRROR_ARRAY_LENGTH_OFFSET(%edi), %ecx
-// movl MIRROR_ARRAY_LENGTH_OFFSET(%rdi), %ecx // This zero-extends, so value(%rcx)=value(%ecx)
- cmpl %ecx, %esi
- jb art_quick_aput_obj
- mov %esi, %edi
-// mov %rsi, %rdi
- mov %ecx, %esi
-// mov %rcx, %rsi
- jmp art_quick_throw_array_bounds
-#endif // __APPLE__
-END_FUNCTION art_quick_aput_obj_with_bound_check
-
-
DEFINE_FUNCTION art_quick_aput_obj
testl %edx, %edx // store of null
// test %rdx, %rdx
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 9c20740..0fd891c 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -180,20 +180,6 @@
pointer_size);
}
-template <bool kWithCheck>
-inline mirror::Class* ArtMethod::GetDexCacheResolvedType(dex::TypeIndex type_index,
- PointerSize pointer_size) {
- if (kWithCheck) {
- mirror::DexCache* dex_cache = GetInterfaceMethodIfProxy(pointer_size)->GetDexCache();
- if (UNLIKELY(type_index.index_ >= dex_cache->NumResolvedTypes())) {
- ThrowArrayIndexOutOfBoundsException(type_index.index_, dex_cache->NumResolvedTypes());
- return nullptr;
- }
- }
- mirror::Class* klass = GetDexCacheResolvedTypes(pointer_size)[type_index.index_].Read();
- return (klass != nullptr && !klass->IsErroneous()) ? klass : nullptr;
-}
-
inline bool ArtMethod::HasDexCacheResolvedTypes(PointerSize pointer_size) {
return GetDexCacheResolvedTypes(pointer_size) != nullptr;
}
@@ -207,15 +193,15 @@
return GetDexCacheResolvedTypes(pointer_size) == other->GetDexCacheResolvedTypes(pointer_size);
}
-inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx,
- bool resolve,
- PointerSize pointer_size) {
- mirror::Class* type = GetDexCacheResolvedType(type_idx, pointer_size);
- if (type == nullptr && resolve) {
- type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, this);
+inline mirror::Class* ArtMethod::GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve) {
+ ObjPtr<mirror::DexCache> dex_cache = GetDexCache();
+ ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(type_idx);
+ if (UNLIKELY(type == nullptr) && resolve) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ type = class_linker->ResolveType(type_idx, this);
CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
}
- return type;
+ return type.Ptr();
}
inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
@@ -333,9 +319,9 @@
return GetDexFile()->GetCodeItem(GetCodeItemOffset());
}
-inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size) {
+inline bool ArtMethod::IsResolvedTypeIdx(dex::TypeIndex type_idx) {
DCHECK(!IsProxyMethod());
- return GetDexCacheResolvedType(type_idx, pointer_size) != nullptr;
+ return GetClassFromTypeIndex(type_idx, /* resolve */ false) != nullptr;
}
inline int32_t ArtMethod::GetLineNumFromDexPC(uint32_t dex_pc) {
@@ -435,18 +421,13 @@
SetNativePointer(DexCacheResolvedTypesOffset(pointer_size), new_dex_cache_types, pointer_size);
}
-inline mirror::Class* ArtMethod::GetReturnType(bool resolve, PointerSize pointer_size) {
+inline mirror::Class* ArtMethod::GetReturnType(bool resolve) {
DCHECK(!IsProxyMethod());
const DexFile* dex_file = GetDexFile();
const DexFile::MethodId& method_id = dex_file->GetMethodId(GetDexMethodIndex());
const DexFile::ProtoId& proto_id = dex_file->GetMethodPrototype(method_id);
dex::TypeIndex return_type_idx = proto_id.return_type_idx_;
- mirror::Class* type = GetDexCacheResolvedType(return_type_idx, pointer_size);
- if (type == nullptr && resolve) {
- type = Runtime::Current()->GetClassLinker()->ResolveType(return_type_idx, this);
- CHECK(type != nullptr || Thread::Current()->IsExceptionPending());
- }
- return type;
+ return GetClassFromTypeIndex(return_type_idx, resolve);
}
inline bool ArtMethod::HasSingleImplementation() {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index dfc7837..d7d39af 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -236,7 +236,6 @@
// Default to handler not found.
uint32_t found_dex_pc = DexFile::kDexNoIndex;
// Iterate over the catch handlers associated with dex_pc.
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (CatchHandlerIterator it(*code_item, dex_pc); it.HasNext(); it.Next()) {
dex::TypeIndex iter_type_idx = it.GetHandlerTypeIndex();
// Catch all case
@@ -245,9 +244,7 @@
break;
}
// Does this catch exception type apply?
- mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx,
- true /* resolve */,
- pointer_size);
+ mirror::Class* iter_exception_type = GetClassFromTypeIndex(iter_type_idx, true /* resolve */);
if (UNLIKELY(iter_exception_type == nullptr)) {
// Now have a NoClassDefFoundError as exception. Ignore in case the exception class was
// removed by a pro-guard like tool.
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 2c31f6c..912df85 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -351,9 +351,6 @@
bool HasSameDexCacheResolvedMethods(ArtMethod** other_cache, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
- template <bool kWithCheck = true>
- mirror::Class* GetDexCacheResolvedType(dex::TypeIndex type_idx, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
void SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_cache_types,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -364,9 +361,7 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the Class* from the type index into this method's dex cache.
- mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx,
- bool resolve,
- PointerSize pointer_size)
+ mirror::Class* GetClassFromTypeIndex(dex::TypeIndex type_idx, bool resolve)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if this method has the same name and signature of the other method.
@@ -558,8 +553,7 @@
const DexFile::CodeItem* GetCodeItem() REQUIRES_SHARED(Locks::mutator_lock_);
- bool IsResolvedTypeIdx(dex::TypeIndex type_idx, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsResolvedTypeIdx(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
int32_t GetLineNumFromDexPC(uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -580,8 +574,7 @@
// May cause thread suspension due to GetClassFromTypeIdx calling ResolveType this caused a large
// number of bugs at call sites.
- mirror::Class* GetReturnType(bool resolve, PointerSize pointer_size)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ mirror::Class* GetReturnType(bool resolve) REQUIRES_SHARED(Locks::mutator_lock_);
mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 4b15a22..18a53c9 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -104,7 +104,7 @@
// Offset of field Thread::tlsPtr_.mterp_current_ibase.
#define THREAD_CURRENT_IBASE_OFFSET \
- (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 164) * __SIZEOF_POINTER__)
+ (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 159) * __SIZEOF_POINTER__)
ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
// Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index edb58c4..fcc92dd 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -46,6 +46,7 @@
ReaderWriterMutex* Locks::heap_bitmap_lock_ = nullptr;
Mutex* Locks::instrument_entrypoints_lock_ = nullptr;
Mutex* Locks::intern_table_lock_ = nullptr;
+Mutex* Locks::jni_function_table_lock_ = nullptr;
Mutex* Locks::jni_libraries_lock_ = nullptr;
Mutex* Locks::logging_lock_ = nullptr;
Mutex* Locks::mem_maps_lock_ = nullptr;
@@ -958,6 +959,7 @@
DCHECK(verifier_deps_lock_ != nullptr);
DCHECK(host_dlopen_handles_lock_ != nullptr);
DCHECK(intern_table_lock_ != nullptr);
+ DCHECK(jni_function_table_lock_ != nullptr);
DCHECK(jni_libraries_lock_ != nullptr);
DCHECK(logging_lock_ != nullptr);
DCHECK(mutator_lock_ != nullptr);
@@ -1104,6 +1106,10 @@
DCHECK(jni_weak_globals_lock_ == nullptr);
jni_weak_globals_lock_ = new Mutex("JNI weak global reference table lock", current_lock_level);
+ UPDATE_CURRENT_LOCK_LEVEL(kJniFunctionTableLock);
+ DCHECK(jni_function_table_lock_ == nullptr);
+ jni_function_table_lock_ = new Mutex("JNI function table lock", current_lock_level);
+
UPDATE_CURRENT_LOCK_LEVEL(kAbortLock);
DCHECK(abort_lock_ == nullptr);
abort_lock_ = new Mutex("abort lock", current_lock_level, true);
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index e44bdb8..3867b1b 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -68,6 +68,7 @@
kRosAllocBulkFreeLock,
kMarkSweepMarkStackLock,
kTransactionLogLock,
+ kJniFunctionTableLock,
kJniWeakGlobalsLock,
kJniGlobalsLock,
kReferenceQueueSoftReferencesLock,
@@ -702,8 +703,11 @@
// Guard accesses to the JNI Weak Global Reference table.
static Mutex* jni_weak_globals_lock_ ACQUIRED_AFTER(jni_globals_lock_);
+ // Guard accesses to the JNI function table override.
+ static Mutex* jni_function_table_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+
// Have an exclusive aborting thread.
- static Mutex* abort_lock_ ACQUIRED_AFTER(jni_weak_globals_lock_);
+ static Mutex* abort_lock_ ACQUIRED_AFTER(jni_function_table_lock_);
// Allow mutual exclusion when manipulating Thread::suspend_count_.
// TODO: Does the trade-off of a per-thread lock make sense?
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 5fc5f1a..2e17dd8 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,7 +25,6 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable.h"
-#include "mirror/throwable.h"
#include "mirror/object_array.h"
#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -90,25 +89,16 @@
if (kIsDebugBuild) {
Thread::Current()->AssertNoPendingException();
}
- ObjPtr<mirror::Class> resolved_type =
- referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
+ ObjPtr<mirror::Class> resolved_type = referrer->GetDexCache()->GetResolvedType(type_idx);
if (UNLIKELY(resolved_type == nullptr)) {
StackHandleScope<2> hs(Thread::Current());
- // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
- // ResolveType for this case.
- if (LIKELY(!hs.Self()->IsExceptionPending())) {
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- const DexFile& dex_file = *dex_cache->GetDexFile();
- resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
- } else {
- // Make sure its an array out of bounds exception.
- DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
- "Ljava/lang/ArrayIndexOutOfBoundsException;"));
- }
+ ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+ // Note: We cannot check here to see whether we added the type to the cache. The type
+ // might be an erroneous class, which results in it being hidden from us.
}
return resolved_type.Ptr();
}
@@ -256,8 +246,8 @@
// Locate the dex cache of the original interface/Object
for (const DexCacheData& data : dex_caches_) {
if (!self->IsJWeakCleared(data.weak_root) &&
- proxy_method->HasSameDexCacheResolvedTypes(data.resolved_types,
- image_pointer_size_)) {
+ proxy_method->HasSameDexCacheResolvedMethods(data.resolved_methods,
+ image_pointer_size_)) {
ObjPtr<mirror::DexCache> dex_cache =
ObjPtr<mirror::DexCache>::DownCast(self->DecodeJObject(data.weak_root));
if (dex_cache != nullptr) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 448b460..07c6eda 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3303,7 +3303,7 @@
DexCacheData data;
data.weak_root = dex_cache_jweak;
data.dex_file = dex_cache->GetDexFile();
- data.resolved_types = dex_cache->GetResolvedTypes();
+ data.resolved_methods = dex_cache->GetResolvedMethods();
dex_caches_.push_back(data);
}
@@ -4373,8 +4373,7 @@
CHECK_STREQ(np->GetName(), prototype->GetName());
CHECK_STREQ(np->GetShorty(), prototype->GetShorty());
// More complex sanity - via dex cache
- CHECK_EQ(np->GetReturnType(true /* resolve */, image_pointer_size_),
- prototype->GetReturnType(true /* resolve */, image_pointer_size_));
+ CHECK_EQ(np->GetReturnType(true /* resolve */), prototype->GetReturnType(true /* resolve */));
}
bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
@@ -4836,7 +4835,6 @@
}
static bool HasSameSignatureWithDifferentClassLoaders(Thread* self,
- PointerSize pointer_size,
Handle<mirror::Class> klass,
Handle<mirror::Class> super_klass,
ArtMethod* method1,
@@ -4844,14 +4842,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
{
StackHandleScope<1> hs(self);
- Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */,
- pointer_size)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */)));
if (UNLIKELY(return_type.Get() == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1);
return false;
}
- ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */,
- pointer_size);
+ ObjPtr<mirror::Class> other_return_type = method2->GetReturnType(true /* resolve */);
if (UNLIKELY(other_return_type == nullptr)) {
ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method2);
return false;
@@ -4896,7 +4892,7 @@
StackHandleScope<1> hs(self);
dex::TypeIndex param_type_idx = types1->GetTypeItem(i).type_idx_;
Handle<mirror::Class> param_type(hs.NewHandle(
- method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */, pointer_size)));
+ method1->GetClassFromTypeIndex(param_type_idx, true /* resolve */)));
if (UNLIKELY(param_type.Get() == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method1, i, param_type_idx);
@@ -4904,7 +4900,7 @@
}
dex::TypeIndex other_param_type_idx = types2->GetTypeItem(i).type_idx_;
ObjPtr<mirror::Class> other_param_type =
- method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */, pointer_size);
+ method2->GetClassFromTypeIndex(other_param_type_idx, true /* resolve */);
if (UNLIKELY(other_param_type == nullptr)) {
ThrowSignatureCheckResolveArgException(klass, super_klass, method1,
method2, i, other_param_type_idx);
@@ -4940,9 +4936,11 @@
auto* m = klass->GetVTableEntry(i, image_pointer_size_);
auto* super_m = klass->GetSuperClass()->GetVTableEntry(i, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
- klass, super_klass,
- m, super_m))) {
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+ klass,
+ super_klass,
+ m,
+ super_m))) {
self->AssertPendingException();
return false;
}
@@ -4958,9 +4956,11 @@
j, image_pointer_size_);
auto* super_m = super_klass->GetVirtualMethod(j, image_pointer_size_);
if (m != super_m) {
- if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self, image_pointer_size_,
- klass, super_klass,
- m, super_m))) {
+ if (UNLIKELY(!HasSameSignatureWithDifferentClassLoaders(self,
+ klass,
+ super_klass,
+ m,
+ super_m))) {
self->AssertPendingException();
return false;
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index cad674e..9b98671 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -649,6 +649,10 @@
ClassTable* ClassTableForClassLoader(ObjPtr<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_);
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -657,7 +661,7 @@
// jweak decode that triggers read barriers (and mark them alive unnecessarily and mess with
// class unloading.)
const DexFile* dex_file;
- GcRoot<mirror::Class>* resolved_types;
+ ArtMethod** resolved_methods;
};
private:
@@ -744,9 +748,6 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
- void AppendToBootClassPath(Thread* self, const DexFile& dex_file)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
void AppendToBootClassPath(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c97c4e4..6da7e3a 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -3987,9 +3987,7 @@
if (shorty[i + 1] == 'L') {
// Did we really get an argument of an appropriate reference type?
mirror::Class* parameter_type =
- m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_,
- true /* resolve */,
- kRuntimePointerSize);
+ m->GetClassFromTypeIndex(types->GetTypeItem(i).type_idx_, true /* resolve */);
mirror::Object* argument = gRegistry->Get<mirror::Object*>(arg_values[i], &error);
if (error != JDWP::ERR_NONE) {
return JDWP::ERR_INVALID_OBJECT;
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 9504e8b..16a447b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -608,7 +608,7 @@
return nullptr;
}
Handle<mirror::Class> method_return(hs.NewHandle(
- annotation_method->GetReturnType(true /* resolve */, pointer_size)));
+ annotation_method->GetReturnType(true /* resolve */)));
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue(klass, annotation, &annotation_value, method_return,
@@ -948,9 +948,7 @@
DexFile::AnnotationValue annotation_value;
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- Handle<mirror::Class> return_type(hs.NewHandle(
- method->GetReturnType(true /* resolve */, pointer_size)));
+ Handle<mirror::Class> return_type(hs.NewHandle(method->GetReturnType(true /* resolve */)));
if (!ProcessAnnotationValue(h_klass, &annotation, &annotation_value, return_type,
DexFile::kAllObjects)) {
return nullptr;
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7d6f866..ac0ce36 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -241,10 +241,9 @@
*slow_path = true;
return nullptr; // Failure
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- mirror::Class* klass = method->GetDexCacheResolvedType<false>(type_idx, pointer_size);
+ mirror::Class* klass = method->GetDexCache()->GetResolvedType(type_idx);
if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
klass = class_linker->ResolveType(type_idx, method);
*slow_path = true;
if (klass == nullptr) { // Error
@@ -294,11 +293,10 @@
klass->GetComponentSizeShift(), allocator_type);
}
-template <bool kAccessCheck, bool kInstrumented>
+template <bool kInstrumented>
ALWAYS_INLINE
inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
int32_t component_count,
- ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type) {
DCHECK(klass != nullptr);
@@ -306,13 +304,6 @@
ThrowNegativeArraySizeException(component_count);
return nullptr; // Failure
}
- if (kAccessCheck) {
- mirror::Class* referrer = method->GetDeclaringClass();
- if (UNLIKELY(!referrer->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer, klass);
- return nullptr; // Failure
- }
- }
// No need to retry a slow-path allocation as the above code won't cause a GC or thread
// suspension.
return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index b17e1a8..25fd727 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -38,98 +38,13 @@
namespace art {
-static inline mirror::Class* CheckFilledNewArrayAlloc(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) {
- if (UNLIKELY(component_count < 0)) {
- ThrowNegativeArraySizeException(component_count);
- return nullptr; // Failure
- }
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- mirror::Class* klass = referrer->GetDexCacheResolvedType<false>(type_idx, pointer_size);
- if (UNLIKELY(klass == nullptr)) { // Not in dex cache so try to resolve
- klass = class_linker->ResolveType(type_idx, referrer);
- if (klass == nullptr) { // Error
- DCHECK(self->IsExceptionPending());
- return nullptr; // Failure
- }
- }
- if (UNLIKELY(klass->IsPrimitive() && !klass->IsPrimitiveInt())) {
- if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) {
- ThrowRuntimeException("Bad filled array request for type %s",
- klass->PrettyDescriptor().c_str());
- } else {
- self->ThrowNewExceptionF(
- "Ljava/lang/InternalError;",
- "Found type %s; filled-new-array not implemented for anything but 'int'",
- klass->PrettyDescriptor().c_str());
- }
- return nullptr; // Failure
- }
- if (access_check) {
- mirror::Class* referrer_klass = referrer->GetDeclaringClass();
- if (UNLIKELY(!referrer_klass->CanAccess(klass))) {
- ThrowIllegalAccessErrorClass(referrer_klass, klass);
- return nullptr; // Failure
- }
- }
- DCHECK(klass->IsArrayClass()) << klass->PrettyClass();
- return klass;
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
- access_check);
- if (UNLIKELY(klass == nullptr)) {
- return nullptr;
- }
- // Always go slow path for now, filled new array is not common.
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
- // the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<false>(self, klass, component_count,
- klass->GetComponentSizeShift(),
- heap->GetCurrentAllocator());
-}
-
-// Helper function to allocate array for FILLED_NEW_ARRAY.
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(
- dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* referrer,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type ATTRIBUTE_UNUSED) {
- mirror::Class* klass = CheckFilledNewArrayAlloc(type_idx, component_count, referrer, self,
- access_check);
- if (UNLIKELY(klass == nullptr)) {
- return nullptr;
- }
- gc::Heap* heap = Runtime::Current()->GetHeap();
- // Use the current allocator type in case CheckFilledNewArrayAlloc caused us to suspend and then
- // the heap switched the allocator type while we were suspended.
- return mirror::Array::Alloc<true>(self, klass, component_count,
- klass->GetComponentSizeShift(),
- heap->GetCurrentAllocator());
-}
-
void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) {
if (o.Get() == nullptr) {
return;
}
// Make sure that the result is an instance of the type this method was expected to return.
ArtMethod* method = self->GetCurrentMethod(nullptr);
- mirror::Class* return_type = method->GetReturnType(true /* resolve */, kRuntimePointerSize);
+ mirror::Class* return_type = method->GetReturnType(true /* resolve */);
if (!o->InstanceOf(return_type)) {
Runtime::Current()->GetJavaVM()->JniAbortF(nullptr,
@@ -192,8 +107,7 @@
ArtMethod* interface_method =
soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
// This can cause thread suspension.
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */, pointer_size);
+ mirror::Class* result_type = interface_method->GetReturnType(true /* resolve */);
ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref.Ptr(), result_type, &result_unboxed)) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index d4cf83c..6a04f20 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -93,33 +93,14 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-template <bool kAccessCheck, bool kInstrumented>
+template <bool kInstrumented>
ALWAYS_INLINE inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass,
int32_t component_count,
- ArtMethod* method,
Thread* self,
gc::AllocatorType allocator_type)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-mirror::Array* CheckAndAllocArrayFromCode(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* method,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
-
-mirror::Array* CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex type_idx,
- int32_t component_count,
- ArtMethod* method,
- Thread* self,
- bool access_check,
- gc::AllocatorType allocator_type)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Roles::uninterruptible_);
-
// Type of find field operation for fast and slow case.
enum FindFieldType {
InstanceObjectRead,
diff --git a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
index 2d06508..3fa5fbf 100644
--- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc
@@ -93,11 +93,11 @@
allocator_type); \
} \
extern "C" mirror::Array* artAllocArrayFromCodeResolved##suffix##suffix2( \
- mirror::Class* klass, int32_t component_count, ArtMethod* method, Thread* self) \
+ mirror::Class* klass, int32_t component_count, Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
- return AllocArrayFromCodeResolved<false, instrumented_bool>(klass, component_count, method, self, \
- allocator_type); \
+ return AllocArrayFromCodeResolved<instrumented_bool>(klass, component_count, self, \
+ allocator_type); \
} \
extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
@@ -109,46 +109,6 @@
self, \
allocator_type); \
} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCode##suffix##suffix2( \
- uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- ScopedQuickEntrypointChecks sqec(self); \
- if (!(instrumented_bool)) { \
- return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- false, \
- allocator_type); \
- } else { \
- return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- false, \
- allocator_type); \
- } \
-} \
-extern "C" mirror::Array* artCheckAndAllocArrayFromCodeWithAccessCheck##suffix##suffix2( \
- uint32_t type_idx, int32_t component_count, ArtMethod* method, Thread* self) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- ScopedQuickEntrypointChecks sqec(self); \
- if (!(instrumented_bool)) { \
- return CheckAndAllocArrayFromCode(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- true, \
- allocator_type); \
- } else { \
- return CheckAndAllocArrayFromCodeInstrumented(dex::TypeIndex(type_idx), \
- component_count, \
- method, \
- self, \
- true, \
- allocator_type); \
- } \
-} \
extern "C" mirror::String* artAllocStringFromBytesFromCode##suffix##suffix2( \
mirror::ByteArray* byte_array, int32_t high, int32_t offset, int32_t byte_count, \
Thread* self) \
@@ -189,7 +149,7 @@
#define GENERATE_ENTRYPOINTS(suffix) \
extern "C" void* art_quick_alloc_array##suffix(uint32_t, int32_t, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix(mirror::Class* klass, int32_t); \
extern "C" void* art_quick_alloc_array_with_access_check##suffix(uint32_t, int32_t, ArtMethod* ref); \
extern "C" void* art_quick_alloc_object_resolved##suffix(mirror::Class* klass); \
extern "C" void* art_quick_alloc_object_initialized##suffix(mirror::Class* klass); \
@@ -200,7 +160,7 @@
extern "C" void* art_quick_alloc_string_from_chars##suffix(int32_t, int32_t, void*); \
extern "C" void* art_quick_alloc_string_from_string##suffix(void*); \
extern "C" void* art_quick_alloc_array##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
-extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t, ArtMethod* ref); \
+extern "C" void* art_quick_alloc_array_resolved##suffix##_instrumented(mirror::Class* klass, int32_t); \
extern "C" void* art_quick_alloc_array_with_access_check##suffix##_instrumented(uint32_t, int32_t, ArtMethod* ref); \
extern "C" void* art_quick_alloc_object##suffix##_instrumented(uint32_t type_idx, ArtMethod* ref); \
extern "C" void* art_quick_alloc_object_resolved##suffix##_instrumented(mirror::Class* klass); \
@@ -219,8 +179,6 @@
qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix##_instrumented; \
qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix##_instrumented; \
qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix##_instrumented; \
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix##_instrumented; \
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix##_instrumented; \
qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix##_instrumented; \
qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix##_instrumented; \
qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix##_instrumented; \
@@ -231,8 +189,6 @@
qpoints->pAllocObjectResolved = art_quick_alloc_object_resolved##suffix; \
qpoints->pAllocObjectInitialized = art_quick_alloc_object_initialized##suffix; \
qpoints->pAllocObjectWithChecks = art_quick_alloc_object_with_checks##suffix; \
- qpoints->pCheckAndAllocArray = art_quick_check_and_alloc_array##suffix; \
- qpoints->pCheckAndAllocArrayWithAccessCheck = art_quick_check_and_alloc_array_with_access_check##suffix; \
qpoints->pAllocStringFromBytes = art_quick_alloc_string_from_bytes##suffix; \
qpoints->pAllocStringFromChars = art_quick_alloc_string_from_chars##suffix; \
qpoints->pAllocStringFromString = art_quick_alloc_string_from_string##suffix; \
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8ce61c1..6481b97 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -66,10 +66,7 @@
qpoints->pGetObjStatic = art_quick_get_obj_static;
// Array
- qpoints->pAputObjectWithNullAndBoundCheck = art_quick_aput_obj_with_null_and_bound_check;
- qpoints->pAputObjectWithBoundCheck = art_quick_aput_obj_with_bound_check;
qpoints->pAputObject = art_quick_aput_obj;
- qpoints->pHandleFillArrayData = art_quick_handle_fill_data;
// JNI
qpoints->pJniMethodStart = JniMethodStart;
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 4d5d6de..b44f29b 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -21,13 +21,11 @@
#define QUICK_ENTRYPOINT_LIST(V) \
V(AllocArray, void*, uint32_t, int32_t, ArtMethod*) \
- V(AllocArrayResolved, void*, mirror::Class*, int32_t, ArtMethod*) \
+ V(AllocArrayResolved, void*, mirror::Class*, int32_t) \
V(AllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
V(AllocObjectResolved, void*, mirror::Class*) \
V(AllocObjectInitialized, void*, mirror::Class*) \
V(AllocObjectWithChecks, void*, mirror::Class*) \
- V(CheckAndAllocArray, void*, uint32_t, int32_t, ArtMethod*) \
- V(CheckAndAllocArrayWithAccessCheck, void*, uint32_t, int32_t, ArtMethod*) \
V(AllocStringFromBytes, void*, void*, int32_t, int32_t, int32_t) \
V(AllocStringFromChars, void*, int32_t, int32_t, void*) \
V(AllocStringFromString, void*, void*) \
@@ -65,10 +63,7 @@
V(GetObjInstance, void*, uint32_t, void*) \
V(GetObjStatic, void*, uint32_t) \
\
- V(AputObjectWithNullAndBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
- V(AputObjectWithBoundCheck, void, mirror::Array*, int32_t, mirror::Object*) \
V(AputObject, void, mirror::Array*, int32_t, mirror::Object*) \
- V(HandleFillArrayData, void, void*, void*) \
\
V(JniMethodStart, uint32_t, Thread*) \
V(JniMethodFastStart, uint32_t, Thread*) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 6301f93..b9caf0f 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -161,12 +161,8 @@
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectInitialized, pAllocObjectWithChecks,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pCheckAndAllocArray,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocObjectWithChecks, pAllocStringFromBytes,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArray, pCheckAndAllocArrayWithAccessCheck,
- sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCheckAndAllocArrayWithAccessCheck,
- pAllocStringFromBytes, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromBytes, pAllocStringFromChars,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAllocStringFromChars, pAllocStringFromString,
@@ -205,13 +201,8 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Instance, pGet64Static, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGet64Static, pGetObjInstance, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjInstance, pGetObjStatic, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObjectWithNullAndBoundCheck,
- sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithNullAndBoundCheck,
- pAputObjectWithBoundCheck, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObjectWithBoundCheck, pAputObject, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pHandleFillArrayData, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pHandleFillArrayData, pJniMethodStart, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pGetObjStatic, pAputObject, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pAputObject, pJniMethodStart, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodStart, pJniMethodFastStart,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodFastStart, pJniMethodStartSynchronized,
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 7b86339..6044053 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -846,7 +846,7 @@
// Some threads in 'runnable_thread_ids' are probably stuck. Try to dump their stacks.
// Avoid using ThreadList::Dump() initially because it is likely to get stuck as well.
{
- ReaderMutexLock mu0(self, *Locks::mutator_lock_);
+ ScopedObjectAccess soa(self);
MutexLock mu1(self, *Locks::thread_list_lock_);
for (Thread* thread : thread_list->GetList()) {
uint32_t tid = thread->GetThreadId();
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index bebcd71..9e7f52b 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -60,10 +60,6 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k32).Int32Value())))
#define ART_METHOD_DEX_CACHE_METHODS_OFFSET_64 24
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_METHODS_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedMethodsOffset(art::PointerSize::k64).Int32Value())))
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_32 24
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k32).Int32Value())))
-#define ART_METHOD_DEX_CACHE_TYPES_OFFSET_64 32
-DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DEX_CACHE_TYPES_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: DexCacheResolvedTypesOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_JNI_OFFSET_32 28
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_JNI_OFFSET_32), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())))
#define ART_METHOD_JNI_OFFSET_64 40
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index ca26207..76777d9 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -751,16 +751,14 @@
case 'L': {
ObjPtr<mirror::Object> o = shadow_frame.GetVRegReference(src_reg);
if (do_assignability_check && o != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
const dex::TypeIndex type_idx = params->GetTypeItem(shorty_pos).type_idx_;
- ObjPtr<mirror::Class> arg_type = method->GetDexCacheResolvedType(type_idx,
- pointer_size);
+ ObjPtr<mirror::Class> arg_type = method->GetDexCache()->GetResolvedType(type_idx);
if (arg_type == nullptr) {
StackHandleScope<1> hs(self);
// Preserve o since it is used below and GetClassFromTypeIndex may cause thread
// suspension.
HandleWrapperObjPtr<mirror::Object> h = hs.NewHandleWrapper(&o);
- arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */, pointer_size);
+ arg_type = method->GetClassFromTypeIndex(type_idx, true /* resolve */);
if (arg_type == nullptr) {
CHECK(self->IsExceptionPending());
return false;
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index d7dfcd4..a77a3fc 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -285,9 +285,7 @@
const size_t ref_idx = inst->VRegA_11x(inst_data);
ObjPtr<mirror::Object> obj_result = shadow_frame.GetVRegReference(ref_idx);
if (do_assignability_check && obj_result != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */,
- pointer_size);
+ ObjPtr<mirror::Class> return_type = method->GetReturnType(true /* resolve */);
// Re-load since it might have moved.
obj_result = shadow_frame.GetVRegReference(ref_idx);
if (return_type == nullptr) {
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 5a3fafa..0148a1c 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -29,6 +29,7 @@
#include "mirror/object-inl.h"
#include "nth_caller_visitor.h"
#include "thread-inl.h"
+#include "thread_list.h"
namespace art {
@@ -37,6 +38,8 @@
static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
+const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
+
// Checking "locals" requires the mutator lock, but at creation time we're really only interested
// in validity, which isn't changing. To avoid grabbing the mutator lock, factored out and tagged
// with NO_THREAD_SAFETY_ANALYSIS.
@@ -78,10 +81,10 @@
runtime_deleted(false),
critical(0),
monitors("monitors", kMonitorsInitial, kMonitorsMax) {
- functions = unchecked_functions = GetJniNativeInterface();
- if (vm->IsCheckJniEnabled()) {
- SetCheckJniEnabled(true);
- }
+ MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+ check_jni = vm->IsCheckJniEnabled();
+ functions = GetFunctionTable(check_jni);
+ unchecked_functions = GetJniNativeInterface();
}
void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
@@ -107,7 +110,12 @@
void JNIEnvExt::SetCheckJniEnabled(bool enabled) {
check_jni = enabled;
- functions = enabled ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+ MutexLock mu(Thread::Current(), *Locks::jni_function_table_lock_);
+ functions = GetFunctionTable(enabled);
+ // Check whether this is a no-op because of override.
+ if (enabled && JNIEnvExt::table_override_ != nullptr) {
+ LOG(WARNING) << "Enabling CheckJNI after a JNIEnv function table override is not functional.";
+ }
}
void JNIEnvExt::DumpReferenceTables(std::ostream& os) {
@@ -269,4 +277,33 @@
}
}
+static void ThreadResetFunctionTable(Thread* thread, void* arg ATTRIBUTE_UNUSED)
+ REQUIRES(Locks::jni_function_table_lock_) {
+ JNIEnvExt* env = thread->GetJniEnv();
+ bool check_jni = env->check_jni;
+ env->functions = JNIEnvExt::GetFunctionTable(check_jni);
+}
+
+void JNIEnvExt::SetTableOverride(const JNINativeInterface* table_override) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ MutexLock mu2(Thread::Current(), *Locks::jni_function_table_lock_);
+
+ JNIEnvExt::table_override_ = table_override;
+
+ // See if we have a runtime. Note: we cannot run other code (like JavaVMExt's CheckJNI install
+ // code), as we'd have to recursively lock the mutex.
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ runtime->GetThreadList()->ForEach(ThreadResetFunctionTable, nullptr);
+ }
+}
+
+const JNINativeInterface* JNIEnvExt::GetFunctionTable(bool check_jni) {
+ const JNINativeInterface* override = JNIEnvExt::table_override_;
+ if (override != nullptr) {
+ return override;
+ }
+ return check_jni ? GetCheckJniNativeInterface() : GetJniNativeInterface();
+}
+
} // namespace art
diff --git a/runtime/jni_env_ext.h b/runtime/jni_env_ext.h
index 5cca0ae..4004c45 100644
--- a/runtime/jni_env_ext.h
+++ b/runtime/jni_env_ext.h
@@ -43,7 +43,7 @@
void DumpReferenceTables(std::ostream& os)
REQUIRES_SHARED(Locks::mutator_lock_);
- void SetCheckJniEnabled(bool enabled);
+ void SetCheckJniEnabled(bool enabled) REQUIRES(!Locks::jni_function_table_lock_);
void PushFrame(int capacity) REQUIRES_SHARED(Locks::mutator_lock_);
void PopFrame() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -104,10 +104,27 @@
// Set the functions to the runtime shutdown functions.
void SetFunctionsToRuntimeShutdownFunctions();
+ // Set the function table override. This will install the override (or original table, if null)
+ // to all threads.
+ // Note: JNI function table overrides are sensitive to the order of operations wrt/ CheckJNI.
+ // After overriding the JNI function table, CheckJNI toggling is ignored.
+ static void SetTableOverride(const JNINativeInterface* table_override)
+ REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
+
+ // Return either the regular, or the CheckJNI function table. Will return table_override_ instead
+ // if it is not null.
+ static const JNINativeInterface* GetFunctionTable(bool check_jni)
+ REQUIRES(Locks::jni_function_table_lock_);
+
private:
+ // Override of function tables. This applies to both default as well as instrumented (CheckJNI)
+ // function tables.
+ static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
+
// The constructor should not be called directly. It may leave the object in an erroneous state,
// and the result needs to be checked.
- JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg);
+ JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+ REQUIRES(!Locks::jni_function_table_lock_);
// All locked objects, with the (Java caller) stack frame that locked them. Used in CheckJNI
// to ensure that only monitors locked in this native frame are being unlocked, and that at
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 4da5e23..08d1eeb 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2346,4 +2346,39 @@
EXPECT_EQ(segment_state_now, segment_state_computed);
}
+static size_t gGlobalRefCount = 0;
+static const JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+ ++gGlobalRefCount;
+ return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+// Test the table override.
+TEST_F(JniInternalTest, JNIEnvExtTableOverride) {
+ JNINativeInterface env_override;
+ memcpy(&env_override, env_->functions, sizeof(JNINativeInterface));
+
+ gOriginalEnv = env_->functions;
+ env_override.NewGlobalRef = CountNewGlobalRef;
+ gGlobalRefCount = 0;
+
+ jclass local = env_->FindClass("java/lang/Object");
+ ASSERT_TRUE(local != nullptr);
+
+ // Set the table, add a global ref, see whether the counter increases.
+ JNIEnvExt::SetTableOverride(&env_override);
+
+ jobject global = env_->NewGlobalRef(local);
+ EXPECT_EQ(1u, gGlobalRefCount);
+ env_->DeleteGlobalRef(global);
+
+ // Reset
+ JNIEnvExt::SetTableOverride(nullptr);
+
+ jobject global2 = env_->NewGlobalRef(local);
+ EXPECT_EQ(1u, gGlobalRefCount);
+ env_->DeleteGlobalRef(global2);
+}
+
} // namespace art
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index a6f56ae..6a4ec9d 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -306,23 +306,6 @@
}
-TEST_F(ObjectTest, CheckAndAllocArrayFromCode) {
- // pretend we are trying to call 'new char[3]' from String.toCharArray
- ScopedObjectAccess soa(Thread::Current());
- Class* java_util_Arrays = class_linker_->FindSystemClass(soa.Self(), "Ljava/util/Arrays;");
- ArtMethod* sort = java_util_Arrays->FindDirectMethod("sort", "([I)V", kRuntimePointerSize);
- const DexFile::TypeId* type_id = java_lang_dex_file_->FindTypeId("[I");
- ASSERT_TRUE(type_id != nullptr);
- dex::TypeIndex type_idx = java_lang_dex_file_->GetIndexForTypeId(*type_id);
- Object* array = CheckAndAllocArrayFromCodeInstrumented(
- type_idx, 3, sort, Thread::Current(), false,
- Runtime::Current()->GetHeap()->GetCurrentAllocator());
- EXPECT_TRUE(array->IsArrayInstance());
- EXPECT_EQ(3, array->AsArray()->GetLength());
- EXPECT_TRUE(array->GetClass()->IsArrayClass());
- EXPECT_TRUE(array->GetClass()->GetComponentType()->IsPrimitive());
-}
-
TEST_F(ObjectTest, CreateMultiArray) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/oat.h b/runtime/oat.h
index ab03252..5a4bc1c 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', '8', '\0' }; // art::Thread fields reorder
+ static constexpr uint8_t kOatVersion[] = { '1', '0', '0', '\0' }; // AllocArrayResolved change
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 42fed50..d5c6520 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -23,13 +23,16 @@
"ti_class.cc",
"ti_field.cc",
"ti_heap.cc",
+ "ti_jni.cc",
"ti_method.cc",
"ti_monitor.cc",
"ti_object.cc",
"ti_properties.cc",
+ "ti_search.cc",
"ti_stack.cc",
"ti_redefine.cc",
"ti_thread.cc",
+ "ti_threadgroup.cc",
"ti_timers.cc",
"transform.cc"],
include_dirs: ["art/runtime"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 4aedec9..195f179 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -51,13 +51,16 @@
#include "ti_class.h"
#include "ti_field.h"
#include "ti_heap.h"
+#include "ti_jni.h"
#include "ti_method.h"
#include "ti_monitor.h"
#include "ti_object.h"
#include "ti_properties.h"
#include "ti_redefine.h"
+#include "ti_search.h"
#include "ti_stack.h"
#include "ti_thread.h"
+#include "ti_threadgroup.h"
#include "ti_timers.h"
#include "transform.h"
@@ -205,13 +208,13 @@
static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
jint* group_count_ptr,
jthreadGroup** groups_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadGroupUtil::GetTopThreadGroups(env, group_count_ptr, groups_ptr);
}
static jvmtiError GetThreadGroupInfo(jvmtiEnv* env,
jthreadGroup group,
jvmtiThreadGroupInfo* info_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadGroupUtil::GetThreadGroupInfo(env, group, info_ptr);
}
static jvmtiError GetThreadGroupChildren(jvmtiEnv* env,
@@ -220,7 +223,12 @@
jthread** threads_ptr,
jint* group_count_ptr,
jthreadGroup** groups_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return ThreadGroupUtil::GetThreadGroupChildren(env,
+ group,
+ thread_count_ptr,
+ threads_ptr,
+ group_count_ptr,
+ groups_ptr);
}
static jvmtiError GetStackTrace(jvmtiEnv* env,
@@ -795,11 +803,11 @@
}
static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table) {
- return ERR(NOT_IMPLEMENTED);
+ return JNIUtil::SetJNIFunctionTable(env, function_table);
}
static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
- return ERR(NOT_IMPLEMENTED);
+ return JNIUtil::GetJNIFunctionTable(env, function_table);
}
// TODO: This will require locking, so that an agent can't remove callbacks when we're dispatching
@@ -850,7 +858,8 @@
}
}
- return gEventHandler.SetEvent(ArtJvmTiEnv::AsArtJvmTiEnv(env), art_thread, event_type, mode);
+ ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(env);
+ return gEventHandler.SetEvent(art_env, art_thread, GetArtJvmtiEvent(art_env, event_type), mode);
}
static jvmtiError GenerateEvents(jvmtiEnv* env, jvmtiEvent event_type) {
@@ -1046,11 +1055,11 @@
}
static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToBootstrapClassLoaderSearch(env, segment);
}
static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment) {
- return ERR(NOT_IMPLEMENTED);
+ return SearchUtil::AddToSystemClassLoaderSearch(env, segment);
}
static jvmtiError GetSystemProperties(jvmtiEnv* env, jint* count_ptr, char*** property_ptr) {
@@ -1225,7 +1234,12 @@
}
static jvmtiError GetJLocationFormat(jvmtiEnv* env, jvmtiJlocationFormat* format_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ // Report BCI as jlocation format. We report dex bytecode indices.
+ if (format_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ *format_ptr = jvmtiJlocationFormat::JVMTI_JLOCATION_JVMBCI;
+ return ERR(NONE);
}
// TODO Remove this once events are working.
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index d027201..fb39db5 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -23,8 +23,13 @@
namespace openjdkjvmti {
+static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env ATTRIBUTE_UNUSED,
+ jvmtiEvent e) {
+ return static_cast<ArtJvmtiEvent>(e);
+}
+
template <typename FnType>
-ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent event) {
+ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, ArtJvmtiEvent event) {
if (env->event_callbacks == nullptr) {
return nullptr;
}
@@ -33,84 +38,79 @@
// function.
switch (event) {
- case JVMTI_EVENT_VM_INIT:
+ case ArtJvmtiEvent::kVmInit:
return reinterpret_cast<FnType*>(env->event_callbacks->VMInit);
- case JVMTI_EVENT_VM_DEATH:
+ case ArtJvmtiEvent::kVmDeath:
return reinterpret_cast<FnType*>(env->event_callbacks->VMDeath);
- case JVMTI_EVENT_THREAD_START:
+ case ArtJvmtiEvent::kThreadStart:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadStart);
- case JVMTI_EVENT_THREAD_END:
+ case ArtJvmtiEvent::kThreadEnd:
return reinterpret_cast<FnType*>(env->event_callbacks->ThreadEnd);
- case JVMTI_EVENT_CLASS_FILE_LOAD_HOOK:
+ case ArtJvmtiEvent::kClassFileLoadHook:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassFileLoadHook);
- case JVMTI_EVENT_CLASS_LOAD:
+ case ArtJvmtiEvent::kClassLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassLoad);
- case JVMTI_EVENT_CLASS_PREPARE:
+ case ArtJvmtiEvent::kClassPrepare:
return reinterpret_cast<FnType*>(env->event_callbacks->ClassPrepare);
- case JVMTI_EVENT_VM_START:
+ case ArtJvmtiEvent::kVmStart:
return reinterpret_cast<FnType*>(env->event_callbacks->VMStart);
- case JVMTI_EVENT_EXCEPTION:
+ case ArtJvmtiEvent::kException:
return reinterpret_cast<FnType*>(env->event_callbacks->Exception);
- case JVMTI_EVENT_EXCEPTION_CATCH:
+ case ArtJvmtiEvent::kExceptionCatch:
return reinterpret_cast<FnType*>(env->event_callbacks->ExceptionCatch);
- case JVMTI_EVENT_SINGLE_STEP:
+ case ArtJvmtiEvent::kSingleStep:
return reinterpret_cast<FnType*>(env->event_callbacks->SingleStep);
- case JVMTI_EVENT_FRAME_POP:
+ case ArtJvmtiEvent::kFramePop:
return reinterpret_cast<FnType*>(env->event_callbacks->FramePop);
- case JVMTI_EVENT_BREAKPOINT:
+ case ArtJvmtiEvent::kBreakpoint:
return reinterpret_cast<FnType*>(env->event_callbacks->Breakpoint);
- case JVMTI_EVENT_FIELD_ACCESS:
+ case ArtJvmtiEvent::kFieldAccess:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldAccess);
- case JVMTI_EVENT_FIELD_MODIFICATION:
+ case ArtJvmtiEvent::kFieldModification:
return reinterpret_cast<FnType*>(env->event_callbacks->FieldModification);
- case JVMTI_EVENT_METHOD_ENTRY:
+ case ArtJvmtiEvent::kMethodEntry:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodEntry);
- case JVMTI_EVENT_METHOD_EXIT:
+ case ArtJvmtiEvent::kMethodExit:
return reinterpret_cast<FnType*>(env->event_callbacks->MethodExit);
- case JVMTI_EVENT_NATIVE_METHOD_BIND:
+ case ArtJvmtiEvent::kNativeMethodBind:
return reinterpret_cast<FnType*>(env->event_callbacks->NativeMethodBind);
- case JVMTI_EVENT_COMPILED_METHOD_LOAD:
+ case ArtJvmtiEvent::kCompiledMethodLoad:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodLoad);
- case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
return reinterpret_cast<FnType*>(env->event_callbacks->CompiledMethodUnload);
- case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
+ case ArtJvmtiEvent::kDynamicCodeGenerated:
return reinterpret_cast<FnType*>(env->event_callbacks->DynamicCodeGenerated);
- case JVMTI_EVENT_DATA_DUMP_REQUEST:
+ case ArtJvmtiEvent::kDataDumpRequest:
return reinterpret_cast<FnType*>(env->event_callbacks->DataDumpRequest);
- case JVMTI_EVENT_MONITOR_WAIT:
+ case ArtJvmtiEvent::kMonitorWait:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWait);
- case JVMTI_EVENT_MONITOR_WAITED:
+ case ArtJvmtiEvent::kMonitorWaited:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorWaited);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTER:
+ case ArtJvmtiEvent::kMonitorContendedEnter:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEnter);
- case JVMTI_EVENT_MONITOR_CONTENDED_ENTERED:
+ case ArtJvmtiEvent::kMonitorContendedEntered:
return reinterpret_cast<FnType*>(env->event_callbacks->MonitorContendedEntered);
- case JVMTI_EVENT_RESOURCE_EXHAUSTED:
+ case ArtJvmtiEvent::kResourceExhausted:
return reinterpret_cast<FnType*>(env->event_callbacks->ResourceExhausted);
- case JVMTI_EVENT_GARBAGE_COLLECTION_START:
+ case ArtJvmtiEvent::kGarbageCollectionStart:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionStart);
- case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
return reinterpret_cast<FnType*>(env->event_callbacks->GarbageCollectionFinish);
- case JVMTI_EVENT_OBJECT_FREE:
+ case ArtJvmtiEvent::kObjectFree:
return reinterpret_cast<FnType*>(env->event_callbacks->ObjectFree);
- case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ case ArtJvmtiEvent::kVmObjectAlloc:
return reinterpret_cast<FnType*>(env->event_callbacks->VMObjectAlloc);
}
return nullptr;
}
template <typename ...Args>
-inline void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args) {
+inline void EventHandler::DispatchEvent(art::Thread* thread,
+ ArtJvmtiEvent event,
+ Args... args) const {
using FnType = void(jvmtiEnv*, Args...);
for (ArtJvmTiEnv* env : envs) {
- bool dispatch = env->event_masks.global_event_mask.Test(event);
-
- if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
- EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
- dispatch = mask != nullptr && mask->Test(event);
- }
-
- if (dispatch) {
+ if (ShouldDispatch(event, env, thread)) {
FnType* callback = GetCallback<FnType>(env, event);
if (callback != nullptr) {
(*callback)(env, args...);
@@ -119,6 +119,18 @@
}
}
+inline bool EventHandler::ShouldDispatch(ArtJvmtiEvent event,
+ ArtJvmTiEnv* env,
+ art::Thread* thread) {
+ bool dispatch = env->event_masks.global_event_mask.Test(event);
+
+ if (!dispatch && thread != nullptr && env->event_masks.unioned_thread_event_mask.Test(event)) {
+ EventMask* mask = env->event_masks.GetEventMaskOrNull(thread);
+ dispatch = mask != nullptr && mask->Test(event);
+ }
+ return dispatch;
+}
+
} // namespace openjdkjvmti
#endif // ART_RUNTIME_OPENJDKJVMTI_EVENTS_INL_H_
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 12692a1..66929cf 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -83,7 +83,7 @@
}
-void EventMasks::EnableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::EnableEvent(art::Thread* thread, ArtJvmtiEvent event) {
DCHECK(EventMask::EventIsInRange(event));
GetEventMask(thread).Set(event);
if (thread != nullptr) {
@@ -91,7 +91,7 @@
}
}
-void EventMasks::DisableEvent(art::Thread* thread, jvmtiEvent event) {
+void EventMasks::DisableEvent(art::Thread* thread, ArtJvmtiEvent event) {
DCHECK(EventMask::EventIsInRange(event));
GetEventMask(thread).Set(event, false);
if (thread != nullptr) {
@@ -111,16 +111,16 @@
envs.push_back(env);
}
-static bool IsThreadControllable(jvmtiEvent event) {
+static bool IsThreadControllable(ArtJvmtiEvent event) {
switch (event) {
- case JVMTI_EVENT_VM_INIT:
- case JVMTI_EVENT_VM_START:
- case JVMTI_EVENT_VM_DEATH:
- case JVMTI_EVENT_THREAD_START:
- case JVMTI_EVENT_COMPILED_METHOD_LOAD:
- case JVMTI_EVENT_COMPILED_METHOD_UNLOAD:
- case JVMTI_EVENT_DYNAMIC_CODE_GENERATED:
- case JVMTI_EVENT_DATA_DUMP_REQUEST:
+ case ArtJvmtiEvent::kVmInit:
+ case ArtJvmtiEvent::kVmStart:
+ case ArtJvmtiEvent::kVmDeath:
+ case ArtJvmtiEvent::kThreadStart:
+ case ArtJvmtiEvent::kCompiledMethodLoad:
+ case ArtJvmtiEvent::kCompiledMethodUnload:
+ case ArtJvmtiEvent::kDynamicCodeGenerated:
+ case ArtJvmtiEvent::kDataDumpRequest:
return false;
default:
@@ -136,7 +136,7 @@
OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
- if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+ if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kVmObjectAlloc)) {
art::StackHandleScope<1> hs(self);
auto h = hs.NewHandleWrapper(obj);
// jvmtiEventVMObjectAlloc parameters:
@@ -162,7 +162,7 @@
jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
handler_->DispatchEvent(self,
- JVMTI_EVENT_VM_OBJECT_ALLOC,
+ ArtJvmtiEvent::kVmObjectAlloc,
jni_env,
thread.get(),
object.get(),
@@ -196,11 +196,11 @@
finish_enabled_(false) {}
void StartPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+ handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionStart);
}
void EndPause() OVERRIDE {
- handler_->DispatchEvent(nullptr, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+ handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kGarbageCollectionFinish);
}
bool IsEnabled() {
@@ -221,10 +221,10 @@
bool finish_enabled_;
};
-static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent event, bool enable) {
+static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, ArtJvmtiEvent event, bool enable) {
bool old_state = listener->IsEnabled();
- if (event == JVMTI_EVENT_GARBAGE_COLLECTION_START) {
+ if (event == ArtJvmtiEvent::kGarbageCollectionStart) {
listener->SetStartEnabled(enable);
} else {
listener->SetFinishEnabled(enable);
@@ -242,14 +242,14 @@
}
// Handle special work for the given event type, if necessary.
-void EventHandler::HandleEventType(jvmtiEvent event, bool enable) {
+void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
switch (event) {
- case JVMTI_EVENT_VM_OBJECT_ALLOC:
+ case ArtJvmtiEvent::kVmObjectAlloc:
SetupObjectAllocationTracking(alloc_listener_.get(), enable);
return;
- case JVMTI_EVENT_GARBAGE_COLLECTION_START:
- case JVMTI_EVENT_GARBAGE_COLLECTION_FINISH:
+ case ArtJvmtiEvent::kGarbageCollectionStart:
+ case ArtJvmtiEvent::kGarbageCollectionFinish:
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
@@ -260,7 +260,7 @@
jvmtiError EventHandler::SetEvent(ArtJvmTiEnv* env,
art::Thread* thread,
- jvmtiEvent event,
+ ArtJvmtiEvent event,
jvmtiEventMode mode) {
if (thread != nullptr) {
art::ThreadState state = thread->GetState();
diff --git a/runtime/openjdkjvmti/events.h b/runtime/openjdkjvmti/events.h
index 07d6bfd..8f56145 100644
--- a/runtime/openjdkjvmti/events.h
+++ b/runtime/openjdkjvmti/events.h
@@ -30,22 +30,70 @@
class JvmtiAllocationListener;
class JvmtiGcPauseListener;
+// an enum for ArtEvents.
+enum class ArtJvmtiEvent {
+ kMinEventTypeVal = JVMTI_MIN_EVENT_TYPE_VAL,
+ kVmInit = JVMTI_EVENT_VM_INIT,
+ kVmDeath = JVMTI_EVENT_VM_DEATH,
+ kThreadStart = JVMTI_EVENT_THREAD_START,
+ kThreadEnd = JVMTI_EVENT_THREAD_END,
+ kClassFileLoadHook = JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
+ kClassLoad = JVMTI_EVENT_CLASS_LOAD,
+ kClassPrepare = JVMTI_EVENT_CLASS_PREPARE,
+ kVmStart = JVMTI_EVENT_VM_START,
+ kException = JVMTI_EVENT_EXCEPTION,
+ kExceptionCatch = JVMTI_EVENT_EXCEPTION_CATCH,
+ kSingleStep = JVMTI_EVENT_SINGLE_STEP,
+ kFramePop = JVMTI_EVENT_FRAME_POP,
+ kBreakpoint = JVMTI_EVENT_BREAKPOINT,
+ kFieldAccess = JVMTI_EVENT_FIELD_ACCESS,
+ kFieldModification = JVMTI_EVENT_FIELD_MODIFICATION,
+ kMethodEntry = JVMTI_EVENT_METHOD_ENTRY,
+ kMethodExit = JVMTI_EVENT_METHOD_EXIT,
+ kNativeMethodBind = JVMTI_EVENT_NATIVE_METHOD_BIND,
+ kCompiledMethodLoad = JVMTI_EVENT_COMPILED_METHOD_LOAD,
+ kCompiledMethodUnload = JVMTI_EVENT_COMPILED_METHOD_UNLOAD,
+ kDynamicCodeGenerated = JVMTI_EVENT_DYNAMIC_CODE_GENERATED,
+ kDataDumpRequest = JVMTI_EVENT_DATA_DUMP_REQUEST,
+ kMonitorWait = JVMTI_EVENT_MONITOR_WAIT,
+ kMonitorWaited = JVMTI_EVENT_MONITOR_WAITED,
+ kMonitorContendedEnter = JVMTI_EVENT_MONITOR_CONTENDED_ENTER,
+ kMonitorContendedEntered = JVMTI_EVENT_MONITOR_CONTENDED_ENTERED,
+ kResourceExhausted = JVMTI_EVENT_RESOURCE_EXHAUSTED,
+ kGarbageCollectionStart = JVMTI_EVENT_GARBAGE_COLLECTION_START,
+ kGarbageCollectionFinish = JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
+ kObjectFree = JVMTI_EVENT_OBJECT_FREE,
+ kVmObjectAlloc = JVMTI_EVENT_VM_OBJECT_ALLOC,
+ kMaxEventTypeVal = JVMTI_MAX_EVENT_TYPE_VAL,
+};
+
+// Convert a jvmtiEvent into a ArtJvmtiEvent
+ALWAYS_INLINE static inline ArtJvmtiEvent GetArtJvmtiEvent(ArtJvmTiEnv* env, jvmtiEvent e);
+
+ALWAYS_INLINE static inline jvmtiEvent GetJvmtiEvent(ArtJvmtiEvent e) {
+ return static_cast<jvmtiEvent>(e);
+}
+
struct EventMask {
- static constexpr size_t kEventsSize = JVMTI_MAX_EVENT_TYPE_VAL - JVMTI_MIN_EVENT_TYPE_VAL + 1;
+ static constexpr size_t kEventsSize =
+ static_cast<size_t>(ArtJvmtiEvent::kMaxEventTypeVal) -
+ static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal) + 1;
std::bitset<kEventsSize> bit_set;
- static bool EventIsInRange(jvmtiEvent event) {
- return event >= JVMTI_MIN_EVENT_TYPE_VAL && event <= JVMTI_MAX_EVENT_TYPE_VAL;
+ static bool EventIsInRange(ArtJvmtiEvent event) {
+ return event >= ArtJvmtiEvent::kMinEventTypeVal && event <= ArtJvmtiEvent::kMaxEventTypeVal;
}
- void Set(jvmtiEvent event, bool value = true) {
+ void Set(ArtJvmtiEvent event, bool value = true) {
DCHECK(EventIsInRange(event));
- bit_set.set(event - JVMTI_MIN_EVENT_TYPE_VAL, value);
+ bit_set.set(static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal),
+ value);
}
- bool Test(jvmtiEvent event) const {
+ bool Test(ArtJvmtiEvent event) const {
DCHECK(EventIsInRange(event));
- return bit_set.test(event - JVMTI_MIN_EVENT_TYPE_VAL);
+ return bit_set.test(
+ static_cast<size_t>(event) - static_cast<size_t>(ArtJvmtiEvent::kMinEventTypeVal));
}
};
@@ -68,8 +116,8 @@
EventMask& GetEventMask(art::Thread* thread);
EventMask* GetEventMaskOrNull(art::Thread* thread);
- void EnableEvent(art::Thread* thread, jvmtiEvent event);
- void DisableEvent(art::Thread* thread, jvmtiEvent event);
+ void EnableEvent(art::Thread* thread, ArtJvmtiEvent event);
+ void DisableEvent(art::Thread* thread, ArtJvmtiEvent event);
};
// Helper class for event handling.
@@ -82,20 +130,27 @@
// enabled, yet.
void RegisterArtJvmTiEnv(ArtJvmTiEnv* env);
- bool IsEventEnabledAnywhere(jvmtiEvent event) {
+ bool IsEventEnabledAnywhere(ArtJvmtiEvent event) const {
if (!EventMask::EventIsInRange(event)) {
return false;
}
return global_mask.Test(event);
}
- jvmtiError SetEvent(ArtJvmTiEnv* env, art::Thread* thread, jvmtiEvent event, jvmtiEventMode mode);
+ jvmtiError SetEvent(ArtJvmTiEnv* env,
+ art::Thread* thread,
+ ArtJvmtiEvent event,
+ jvmtiEventMode mode);
template <typename ...Args>
- ALWAYS_INLINE inline void DispatchEvent(art::Thread* thread, jvmtiEvent event, Args... args);
+ ALWAYS_INLINE
+ inline void DispatchEvent(art::Thread* thread, ArtJvmtiEvent event, Args... args) const;
private:
- void HandleEventType(jvmtiEvent event, bool enable);
+ ALWAYS_INLINE
+ static inline bool ShouldDispatch(ArtJvmtiEvent event, ArtJvmTiEnv* env, art::Thread* thread);
+
+ void HandleEventType(ArtJvmtiEvent event, bool enable);
// List of all JvmTiEnv objects that have been created, in their creation order.
std::vector<ArtJvmTiEnv*> envs;
diff --git a/runtime/openjdkjvmti/jvmti.h b/runtime/openjdkjvmti/jvmti.h
index ee708cb..de07c16 100644
--- a/runtime/openjdkjvmti/jvmti.h
+++ b/runtime/openjdkjvmti/jvmti.h
@@ -74,7 +74,7 @@
typedef jlong jlocation;
struct _jrawMonitorID;
typedef struct _jrawMonitorID *jrawMonitorID;
-typedef struct JNINativeInterface_ jniNativeInterface;
+typedef struct JNINativeInterface jniNativeInterface;
/* Constants */
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index b983e79..94cb46a 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -177,7 +177,7 @@
}
void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
- if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kObjectFree)) {
SweepImpl<true>(visitor);
} else {
SweepImpl<false>(visitor);
@@ -207,7 +207,7 @@
}
void ObjectTagTable::HandleNullSweep(jlong tag) {
- event_handler_->DispatchEvent(nullptr, JVMTI_EVENT_OBJECT_FREE, tag);
+ event_handler_->DispatchEvent(nullptr, ArtJvmtiEvent::kObjectFree, tag);
}
template <typename T, ObjectTagTable::TableUpdateNullTarget kTargetNull>
diff --git a/runtime/openjdkjvmti/ti_jni.cc b/runtime/openjdkjvmti/ti_jni.cc
new file mode 100644
index 0000000..88f0395
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.cc
@@ -0,0 +1,91 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_jni.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/mutex.h"
+#include "java_vm_ext.h"
+#include "jni_env_ext.h"
+#include "runtime.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+jvmtiError JNIUtil::SetJNIFunctionTable(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const jniNativeInterface* function_table) {
+ // While we supporting setting null (which will reset the table), the spec says no.
+ if (function_table == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::JNIEnvExt::SetTableOverride(function_table);
+ return ERR(NONE);
+}
+
+jvmtiError JNIUtil::GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table) {
+ if (function_table == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ // We use the generic JNIEnvExt::GetFunctionTable instead of querying a specific JNIEnv, as
+ // this has to work in the start phase.
+
+ // Figure out which table is current. Conservatively assume check-jni is off.
+ bool check_jni = false;
+ art::Runtime* runtime = art::Runtime::Current();
+ if (runtime != nullptr && runtime->GetJavaVM() != nullptr) {
+ check_jni = runtime->GetJavaVM()->IsCheckJniEnabled();
+ }
+
+ // Get that table.
+ const JNINativeInterface* current_table;
+ {
+ art::MutexLock mu(art::Thread::Current(), *art::Locks::jni_function_table_lock_);
+ current_table = art::JNIEnvExt::GetFunctionTable(check_jni);
+ }
+
+ // Allocate memory and copy the table.
+ unsigned char* data;
+ jvmtiError data_result = env->Allocate(sizeof(JNINativeInterface), &data);
+ if (data_result != ERR(NONE)) {
+ return data_result;
+ }
+ memcpy(data, current_table, sizeof(JNINativeInterface));
+
+ *function_table = reinterpret_cast<JNINativeInterface*>(data);
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_jni.h b/runtime/openjdkjvmti/ti_jni.h
new file mode 100644
index 0000000..906aab0
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_jni.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+// Note: Currently, JNI function table changes are sensitive to the order of operations wrt/
+// CheckJNI. If an agent sets the function table, and a program than late-enables CheckJNI,
+// CheckJNI will not be working (as the agent will forward to the non-CheckJNI table).
+//
+// This behavior results from our usage of the function table to avoid a check of the
+// CheckJNI flag. A future implementation may install on loading of this plugin an
+// intermediate function table that explicitly checks the flag, so that switching CheckJNI
+// is transparently handled.
+
+class JNIUtil {
+ public:
+ static jvmtiError SetJNIFunctionTable(jvmtiEnv* env, const jniNativeInterface* function_table);
+
+ static jvmtiError GetJNIFunctionTable(jvmtiEnv* env, jniNativeInterface** function_table);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_JNI_H_
diff --git a/runtime/openjdkjvmti/ti_search.cc b/runtime/openjdkjvmti/ti_search.cc
new file mode 100644
index 0000000..913d2b6
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.cc
@@ -0,0 +1,122 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_search.h"
+
+#include "jni.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "class_linker.h"
+#include "dex_file.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+
+namespace openjdkjvmti {
+
+jvmtiError SearchUtil::AddToBootstrapClassLoaderSearch(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ if (current->GetClassLinker() == nullptr) {
+ // TODO: Support boot classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ std::string error_msg;
+ std::vector<std::unique_ptr<const art::DexFile>> dex_files;
+ if (!art::DexFile::Open(segment, segment, true, &error_msg, &dex_files)) {
+ LOG(WARNING) << "Could not open " << segment << " for boot classpath extension: " << error_msg;
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), *dex_file.release());
+ }
+
+ return ERR(NONE);
+}
+
+jvmtiError SearchUtil::AddToSystemClassLoaderSearch(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED,
+ const char* segment) {
+ if (segment == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Runtime* current = art::Runtime::Current();
+ if (current == nullptr) {
+ return ERR(WRONG_PHASE);
+ }
+ jobject sys_class_loader = current->GetSystemClassLoader();
+ if (sys_class_loader == nullptr) {
+ // TODO: Support classpath change in OnLoad.
+ return ERR(WRONG_PHASE);
+ }
+
+ // We'll use BaseDexClassLoader.addDexPath, as it takes care of array resizing etc. As a downside,
+ // exceptions are swallowed.
+
+ art::Thread* self = art::Thread::Current();
+ JNIEnv* env = self->GetJniEnv();
+ if (!env->IsInstanceOf(sys_class_loader,
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ return ERR(INTERNAL);
+ }
+
+ jmethodID add_dex_path_id = env->GetMethodID(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
+ "addDexPath",
+ "(Ljava/lang/String;)V");
+ if (add_dex_path_id == nullptr) {
+ return ERR(INTERNAL);
+ }
+
+ ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
+ if (dex_path.get() == nullptr) {
+ return ERR(INTERNAL);
+ }
+ env->CallVoidMethod(sys_class_loader, add_dex_path_id, dex_path.get());
+
+ if (env->ExceptionCheck()) {
+ env->ExceptionClear();
+ return ERR(ILLEGAL_ARGUMENT);
+ }
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_search.h b/runtime/openjdkjvmti/ti_search.h
new file mode 100644
index 0000000..6a52e80
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_search.h
@@ -0,0 +1,48 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class SearchUtil {
+ public:
+ static jvmtiError AddToBootstrapClassLoaderSearch(jvmtiEnv* env, const char* segment);
+
+ static jvmtiError AddToSystemClassLoaderSearch(jvmtiEnv* env, const char* segment);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_SEARCH_H_
diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc
new file mode 100644
index 0000000..35b1bfd
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_threadgroup.cc
@@ -0,0 +1,285 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "ti_threadgroup.h"
+
+#include "art_field.h"
+#include "art_jvmti.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "handle_scope-inl.h"
+#include "jni_internal.h"
+#include "mirror/class.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
+#include "obj_ptr.h"
+#include "object_lock.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+#include "thread_list.h"
+#include "well_known_classes.h"
+
+namespace openjdkjvmti {
+
+
+jvmtiError ThreadGroupUtil::GetTopThreadGroups(jvmtiEnv* env,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ // We only have a single top group. So we can take the current thread and move upwards.
+ if (group_count_ptr == nullptr || groups_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ art::Runtime* runtime = art::Runtime::Current();
+ if (runtime == nullptr) {
+ // Must be starting the runtime, or dying.
+ return ERR(WRONG_PHASE);
+ }
+
+ jobject sys_thread_group = runtime->GetSystemThreadGroup();
+ if (sys_thread_group == nullptr) {
+ // Seems we're still starting up.
+ return ERR(WRONG_PHASE);
+ }
+
+ unsigned char* data;
+ jvmtiError result = env->Allocate(sizeof(jthreadGroup), &data);
+ if (result != ERR(NONE)) {
+ return result;
+ }
+
+ jthreadGroup* groups = reinterpret_cast<jthreadGroup*>(data);
+ *groups =
+ reinterpret_cast<JNIEnv*>(art::Thread::Current()->GetJniEnv())->NewLocalRef(sys_thread_group);
+ *groups_ptr = groups;
+ *group_count_ptr = 1;
+
+ return ERR(NONE);
+}
+
+jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env,
+ jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr) {
+ if (group == nullptr) {
+ return ERR(INVALID_THREAD_GROUP);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) {
+ return ERR(INVALID_THREAD_GROUP);
+ }
+
+ art::ObjPtr<art::mirror::Object> obj = soa.Decode<art::mirror::Object>(group);
+
+ // Do the name first. It's the only thing that can fail.
+ {
+ art::ArtField* name_field =
+ art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name);
+ CHECK(name_field != nullptr);
+ art::ObjPtr<art::mirror::String> name_obj =
+ art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj));
+ std::string tmp_str;
+ const char* tmp_cstr;
+ if (name_obj == nullptr) {
+ tmp_cstr = "";
+ } else {
+ tmp_str = name_obj->ToModifiedUtf8();
+ tmp_cstr = tmp_str.c_str();
+ }
+ jvmtiError result =
+ CopyString(env, tmp_cstr, reinterpret_cast<unsigned char**>(&info_ptr->name));
+ if (result != ERR(NONE)) {
+ return result;
+ }
+ }
+
+ // Parent.
+ {
+ art::ArtField* parent_field =
+ art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent);
+ CHECK(parent_field != nullptr);
+ art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj);
+ info_ptr->parent = parent_group == nullptr
+ ? nullptr
+ : soa.AddLocalReference<jthreadGroup>(parent_group);
+ }
+
+ // Max priority.
+ {
+ art::ArtField* prio_field = obj->GetClass()->FindDeclaredInstanceField("maxPriority", "I");
+ CHECK(prio_field != nullptr);
+ info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj));
+ }
+
+ // Daemon.
+ {
+ art::ArtField* daemon_field = obj->GetClass()->FindDeclaredInstanceField("daemon", "Z");
+ CHECK(daemon_field != nullptr);
+ info_ptr->is_daemon = daemon_field->GetBoolean(obj) == 0 ? JNI_FALSE : JNI_TRUE;
+ }
+
+ return ERR(NONE);
+}
+
+
+static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thread_group,
+ art::ObjPtr<art::mirror::Object> peer)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ CHECK(desired_thread_group.Get() != nullptr);
+
+ art::ArtField* thread_group_field =
+ art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ DCHECK(thread_group_field != nullptr);
+ art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer);
+ return (group == desired_thread_group.Get());
+}
+
+static void GetThreads(art::Handle<art::mirror::Object> thread_group,
+ std::vector<art::ObjPtr<art::mirror::Object>>* thread_peers)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) REQUIRES(!art::Locks::thread_list_lock_) {
+ CHECK(thread_group.Get() != nullptr);
+
+ art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_);
+ for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) {
+ if (t->IsStillStarting()) {
+ continue;
+ }
+ art::ObjPtr<art::mirror::Object> peer = t->GetPeer();
+ if (peer == nullptr) {
+ continue;
+ }
+ if (IsInDesiredThreadGroup(thread_group, peer)) {
+ thread_peers->push_back(peer);
+ }
+ }
+}
+
+static void GetChildThreadGroups(art::Handle<art::mirror::Object> thread_group,
+ std::vector<art::ObjPtr<art::mirror::Object>>* thread_groups)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ CHECK(thread_group.Get() != nullptr);
+
+ // Get the ThreadGroup[] "groups" out of this thread group...
+ art::ArtField* groups_field =
+ art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups);
+ art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get());
+
+ if (groups_array == nullptr) {
+ return;
+ }
+ CHECK(groups_array->IsObjectArray());
+
+ art::ObjPtr<art::mirror::ObjectArray<art::mirror::Object>> groups_array_as_array =
+ groups_array->AsObjectArray<art::mirror::Object>();
+
+ // Copy all non-null elements.
+ for (int32_t i = 0; i < groups_array_as_array->GetLength(); ++i) {
+ art::ObjPtr<art::mirror::Object> entry = groups_array_as_array->Get(i);
+ if (entry != nullptr) {
+ thread_groups->push_back(entry);
+ }
+ }
+}
+
+jvmtiError ThreadGroupUtil::GetThreadGroupChildren(jvmtiEnv* env,
+ jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ if (group == nullptr) {
+ return ERR(INVALID_THREAD_GROUP);
+ }
+
+ art::ScopedObjectAccess soa(art::Thread::Current());
+
+ if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) {
+ return ERR(INVALID_THREAD_GROUP);
+ }
+
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Object> thread_group = hs.NewHandle(
+ soa.Decode<art::mirror::Object>(group));
+
+ art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group);
+
+ std::vector<art::ObjPtr<art::mirror::Object>> thread_peers;
+ GetThreads(thread_group, &thread_peers);
+
+ std::vector<art::ObjPtr<art::mirror::Object>> thread_groups;
+ GetChildThreadGroups(thread_group, &thread_groups);
+
+ jthread* thread_data = nullptr;
+ JvmtiUniquePtr peers_uptr;
+ if (!thread_peers.empty()) {
+ unsigned char* data;
+ jvmtiError res = env->Allocate(sizeof(jthread) * thread_peers.size(), &data);
+ if (res != ERR(NONE)) {
+ return res;
+ }
+ thread_data = reinterpret_cast<jthread*>(data);
+ peers_uptr = MakeJvmtiUniquePtr(env, data);
+ }
+
+ jthreadGroup* group_data = nullptr;
+ if (!thread_groups.empty()) {
+ unsigned char* data;
+ jvmtiError res = env->Allocate(sizeof(jthreadGroup) * thread_groups.size(), &data);
+ if (res != ERR(NONE)) {
+ return res;
+ }
+ group_data = reinterpret_cast<jthreadGroup*>(data);
+ }
+
+ // Can't fail anymore from here on.
+
+ // Copy data into out buffers.
+ for (size_t i = 0; i != thread_peers.size(); ++i) {
+ thread_data[i] = soa.AddLocalReference<jthread>(thread_peers[i]);
+ }
+ for (size_t i = 0; i != thread_groups.size(); ++i) {
+ group_data[i] = soa.AddLocalReference<jthreadGroup>(thread_groups[i]);
+ }
+
+ *thread_count_ptr = static_cast<jint>(thread_peers.size());
+ *threads_ptr = thread_data;
+ *group_count_ptr = static_cast<jint>(thread_groups.size());
+ *groups_ptr = group_data;
+
+ // Everything's fine.
+ peers_uptr.release();
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_threadgroup.h b/runtime/openjdkjvmti/ti_threadgroup.h
new file mode 100644
index 0000000..c3a0ff5
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_threadgroup.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ThreadGroupUtil {
+ public:
+ static jvmtiError GetTopThreadGroups(jvmtiEnv* env,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+
+ static jvmtiError GetThreadGroupInfo(jvmtiEnv* env,
+ jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr);
+
+ static jvmtiError GetThreadGroupChildren(jvmtiEnv* env,
+ jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_THREADGROUP_H_
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 4d24501..75176f9 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -227,11 +227,11 @@
for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) {
ObjPtr<mirror::Object> arg(args->Get(args_offset));
if (((shorty_[i] == 'L') && (arg != nullptr)) || ((arg == nullptr && shorty_[i] != 'L'))) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ // Note: The method's parameter's type must have been previously resolved.
ObjPtr<mirror::Class> dst_class(
m->GetClassFromTypeIndex(classes->GetTypeItem(args_offset).type_idx_,
- true /* resolve */,
- pointer_size));
+ false /* resolve */));
+ DCHECK(dst_class != nullptr) << m->PrettyMethod() << " arg #" << i;
if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) {
ThrowIllegalArgumentException(
StringPrintf("method %s argument %zd has type %s, got %s",
@@ -363,12 +363,9 @@
}
// TODO: If args contain object references, it may cause problems.
Thread* const self = Thread::Current();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (uint32_t i = 0; i < num_params; i++) {
dex::TypeIndex type_idx = params->GetTypeItem(i).type_idx_;
- ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx,
- true /* resolve*/,
- pointer_size));
+ ObjPtr<mirror::Class> param_type(m->GetClassFromTypeIndex(type_idx, true /* resolve */));
if (param_type == nullptr) {
CHECK(self->IsExceptionPending());
LOG(ERROR) << "Internal error: unresolvable type for argument type in JNI invoke: "
diff --git a/runtime/thread.cc b/runtime/thread.cc
index bf69e10..8c8afdc 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2641,8 +2641,6 @@
QUICK_ENTRY_POINT_INFO(pAllocObjectResolved)
QUICK_ENTRY_POINT_INFO(pAllocObjectInitialized)
QUICK_ENTRY_POINT_INFO(pAllocObjectWithChecks)
- QUICK_ENTRY_POINT_INFO(pCheckAndAllocArray)
- QUICK_ENTRY_POINT_INFO(pCheckAndAllocArrayWithAccessCheck)
QUICK_ENTRY_POINT_INFO(pAllocStringFromBytes)
QUICK_ENTRY_POINT_INFO(pAllocStringFromChars)
QUICK_ENTRY_POINT_INFO(pAllocStringFromString)
@@ -2676,10 +2674,7 @@
QUICK_ENTRY_POINT_INFO(pGet64Static)
QUICK_ENTRY_POINT_INFO(pGetObjInstance)
QUICK_ENTRY_POINT_INFO(pGetObjStatic)
- QUICK_ENTRY_POINT_INFO(pAputObjectWithNullAndBoundCheck)
- QUICK_ENTRY_POINT_INFO(pAputObjectWithBoundCheck)
QUICK_ENTRY_POINT_INFO(pAputObject)
- QUICK_ENTRY_POINT_INFO(pHandleFillArrayData)
QUICK_ENTRY_POINT_INFO(pJniMethodStart)
QUICK_ENTRY_POINT_INFO(pJniMethodStartSynchronized)
QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 25a179b..b915457 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2901,9 +2901,7 @@
ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range);
const RegType* return_type = nullptr;
if (called_method != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type = &FromClass(called_method->GetReturnTypeDescriptor(),
return_type_class,
@@ -2946,9 +2944,7 @@
} else {
is_constructor = called_method->IsConstructor();
return_type_descriptor = called_method->GetReturnTypeDescriptor();
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = called_method->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type = &FromClass(return_type_descriptor,
return_type_class,
@@ -5133,9 +5129,7 @@
const RegType& MethodVerifier::GetMethodReturnType() {
if (return_type_ == nullptr) {
if (mirror_method_ != nullptr) {
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_,
- pointer_size);
+ mirror::Class* return_type_class = mirror_method_->GetReturnType(can_load_classes_);
if (return_type_class != nullptr) {
return_type_ = &FromClass(mirror_method_->GetReturnTypeDescriptor(),
return_type_class,
diff --git a/test/129-ThreadGetId/src/Main.java b/test/129-ThreadGetId/src/Main.java
index 6ba01ff..4e48e0e 100644
--- a/test/129-ThreadGetId/src/Main.java
+++ b/test/129-ThreadGetId/src/Main.java
@@ -23,7 +23,6 @@
public static void main(String[] args) throws Exception {
final Thread[] threads = new Thread[numberOfThreads];
- test_getStackTraces();
for (int t = 0; t < threads.length; t++) {
threads[t] = new Thread(new Main());
threads[t].start();
@@ -31,6 +30,9 @@
for (Thread t : threads) {
t.join();
}
+ // Do this test after the other part to leave some time for the heap task daemon to start
+ // up.
+ test_getStackTraces();
System.out.println("Finishing");
}
diff --git a/test/559-checker-rtp-ifnotnull/src/Main.java b/test/559-checker-rtp-ifnotnull/src/Main.java
index 2dc5666..1e15654 100644
--- a/test/559-checker-rtp-ifnotnull/src/Main.java
+++ b/test/559-checker-rtp-ifnotnull/src/Main.java
@@ -18,7 +18,6 @@
public class Main {
/// CHECK-START: void Main.boundTypeForIfNotNull() builder (after)
- /// CHECK-DAG: <<Method:(i|j)\d+>> CurrentMethod
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Cst5:i\d+>> IntConstant 5
/// CHECK-DAG: <<Cst10:i\d+>> IntConstant 10
@@ -28,10 +27,12 @@
/// CHECK-DAG: <<LoopPhi>> Phi [<<Null>>,<<MergePhi:l\d+>>] klass:int[]
/// CHECK-DAG: <<BoundType:l\d+>> BoundType [<<LoopPhi>>] klass:int[] can_be_null:false
- /// CHECK-DAG: <<NewArray10:l\d+>> NewArray [<<Cst10>>,<<Method>>] klass:int[]
+ /// CHECK-DAG: <<LoadClass1:l\d+>> LoadClass
+ /// CHECK-DAG: <<LoadClass2:l\d+>> LoadClass
+ /// CHECK-DAG: <<NewArray10:l\d+>> NewArray [<<LoadClass2>>,<<Cst10>>] klass:int[]
/// CHECK-DAG: <<NotNullPhi:l\d+>> Phi [<<BoundType>>,<<NewArray10>>] klass:int[]
- /// CHECK-DAG: <<NewArray5:l\d+>> NewArray [<<Cst5>>,<<Method>>] klass:int[]
+ /// CHECK-DAG: <<NewArray5:l\d+>> NewArray [<<LoadClass1>>,<<Cst5>>] klass:int[]
/// CHECK-DAG: <<MergePhi>> Phi [<<NewArray5>>,<<NotNullPhi>>] klass:int[]
public static void boundTypeForIfNotNull() {
diff --git a/test/572-checker-array-get-regression/src/Main.java b/test/572-checker-array-get-regression/src/Main.java
index 89b97ed..03a8448 100644
--- a/test/572-checker-array-get-regression/src/Main.java
+++ b/test/572-checker-array-get-regression/src/Main.java
@@ -21,10 +21,10 @@
}
/// CHECK-START: java.lang.Integer Main.test() builder (after)
- /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
/// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288
/// CHECK-DAG: <<ConstM1:i\d+>> IntConstant -1
- /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>]
+ /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ /// CHECK-DAG: <<Array:l\d+>> NewArray [<<LoadClass>>,<<Const2P19>>]
/// CHECK-DAG: <<Length1:i\d+>> ArrayLength [<<Array>>]
/// CHECK-DAG: <<Index:i\d+>> Add [<<Length1>>,<<ConstM1>>]
/// CHECK-DAG: <<Length2:i\d+>> ArrayLength [<<Array>>]
@@ -34,10 +34,10 @@
/// CHECK-START: java.lang.Integer Main.test() register (before)
- /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
/// CHECK-DAG: <<Const2P19:i\d+>> IntConstant 524288
/// CHECK-DAG: <<Const2P19M1:i\d+>> IntConstant 524287
- /// CHECK-DAG: <<Array:l\d+>> NewArray [<<Const2P19>>,<<Method>>]
+ /// CHECK-DAG: <<LoadClass:l\d+>> LoadClass
+ /// CHECK-DAG: <<Array:l\d+>> NewArray [<<LoadClass>>,<<Const2P19>>]
/// CHECK-DAG: <<LastElement:l\d+>> ArrayGet [<<Array>>,<<Const2P19M1>>]
/// CHECK-DAG: Return [<<LastElement>>]
diff --git a/test/925-threadgroups/build b/test/925-threadgroups/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/925-threadgroups/build
@@ -0,0 +1,17 @@
+#!/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.
+
+./default-build "$@" --experimental agents
diff --git a/test/925-threadgroups/expected.txt b/test/925-threadgroups/expected.txt
new file mode 100644
index 0000000..7d1a259
--- /dev/null
+++ b/test/925-threadgroups/expected.txt
@@ -0,0 +1,16 @@
+java.lang.ThreadGroup[name=main,maxpri=10]
+ java.lang.ThreadGroup[name=system,maxpri=10]
+ main
+ 10
+ false
+java.lang.ThreadGroup[name=system,maxpri=10]
+ null
+ system
+ 10
+ false
+main:
+ [Thread[main,5,main]]
+ []
+system:
+ [Thread[FinalizerDaemon,5,system], Thread[FinalizerWatchdogDaemon,5,system], Thread[HeapTaskDaemon,5,system], Thread[ReferenceQueueDaemon,5,system], Thread[Signal Catcher,5,system]]
+ [java.lang.ThreadGroup[name=main,maxpri=10]]
diff --git a/test/925-threadgroups/info.txt b/test/925-threadgroups/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/925-threadgroups/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/925-threadgroups/run
@@ -0,0 +1,19 @@
+#!/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.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/925-threadgroups/src/Main.java b/test/925-threadgroups/src/Main.java
new file mode 100644
index 0000000..c59efe2
--- /dev/null
+++ b/test/925-threadgroups/src/Main.java
@@ -0,0 +1,113 @@
+/*
+ * 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.util.Arrays;
+import java.util.Comparator;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ private static void doTest() throws Exception {
+ Thread t1 = Thread.currentThread();
+ ThreadGroup curGroup = t1.getThreadGroup();
+
+ ThreadGroup rootGroup = curGroup;
+ while (rootGroup.getParent() != null) {
+ rootGroup = rootGroup.getParent();
+ }
+
+ ThreadGroup topGroups[] = getTopThreadGroups();
+ if (topGroups == null || topGroups.length != 1 || topGroups[0] != rootGroup) {
+ System.out.println(Arrays.toString(topGroups));
+ throw new RuntimeException("Unexpected topGroups");
+ }
+
+ printThreadGroupInfo(curGroup);
+ printThreadGroupInfo(rootGroup);
+
+ waitGroupChildren(rootGroup, 5 /* # daemons */, 30 /* timeout in seconds */);
+
+ checkChildren(curGroup);
+ }
+
+ private static void printThreadGroupInfo(ThreadGroup tg) {
+ Object[] threadGroupInfo = getThreadGroupInfo(tg);
+ if (threadGroupInfo == null || threadGroupInfo.length != 4) {
+ System.out.println(Arrays.toString(threadGroupInfo));
+ throw new RuntimeException("threadGroupInfo length wrong");
+ }
+
+ System.out.println(tg);
+ System.out.println(" " + threadGroupInfo[0]); // Parent
+ System.out.println(" " + threadGroupInfo[1]); // Name
+ System.out.println(" " + threadGroupInfo[2]); // Priority
+ System.out.println(" " + threadGroupInfo[3]); // Daemon
+ }
+
+ private static void checkChildren(ThreadGroup tg) {
+ Object[] data = getThreadGroupChildren(tg);
+ Thread[] threads = (Thread[])data[0];
+ ThreadGroup[] groups = (ThreadGroup[])data[1];
+
+ Arrays.sort(threads, THREAD_COMP);
+ Arrays.sort(groups, THREADGROUP_COMP);
+ System.out.println(tg.getName() + ":");
+ System.out.println(" " + Arrays.toString(threads));
+ System.out.println(" " + Arrays.toString(groups));
+
+ if (tg.getParent() != null) {
+ checkChildren(tg.getParent());
+ }
+ }
+
+ private static void waitGroupChildren(ThreadGroup tg, int expectedChildCount, int timeoutS)
+ throws Exception {
+ for (int i = 0; i < timeoutS; i++) {
+ Object[] data = getThreadGroupChildren(tg);
+ Thread[] threads = (Thread[])data[0];
+ if (threads.length == expectedChildCount) {
+ return;
+ }
+ Thread.sleep(1000);
+ }
+
+ Object[] data = getThreadGroupChildren(tg);
+ Thread[] threads = (Thread[])data[0];
+ System.out.println(Arrays.toString(threads));
+ throw new RuntimeException("Waited unsuccessfully for " + expectedChildCount + " children.");
+ }
+
+ private final static Comparator<Thread> THREAD_COMP = new Comparator<Thread>() {
+ public int compare(Thread o1, Thread o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ };
+
+ private final static Comparator<ThreadGroup> THREADGROUP_COMP = new Comparator<ThreadGroup>() {
+ public int compare(ThreadGroup o1, ThreadGroup o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ };
+
+ private static native ThreadGroup[] getTopThreadGroups();
+ private static native Object[] getThreadGroupInfo(ThreadGroup tg);
+ // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
+ private static native Object[] getThreadGroupChildren(ThreadGroup tg);
+}
diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc
new file mode 100644
index 0000000..6c6e835
--- /dev/null
+++ b/test/925-threadgroups/threadgroups.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 <stdio.h>
+
+#include "android-base/stringprintf.h"
+#include "base/macros.h"
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedLocalRef.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test925ThreadGroups {
+
+// private static native Object[] getThreadGroupInfo();
+// // Returns an array where element 0 is an array of threads and element 1 is an array of groups.
+// private static native Object[] getThreadGroupChildren();
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getTopThreadGroups(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
+ jthreadGroup* groups;
+ jint group_count;
+ jvmtiError result = jvmti_env->GetTopThreadGroups(&group_count, &groups);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ auto callback = [&](jint index) -> jobject {
+ return groups[index];
+ };
+ jobjectArray ret = CreateObjectArray(env, group_count, "java/lang/ThreadGroup", callback);
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups));
+
+ return ret;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupInfo(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
+ jvmtiThreadGroupInfo info;
+ jvmtiError result = jvmti_env->GetThreadGroupInfo(group, &info);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ auto callback = [&](jint index) -> jobject {
+ switch (index) {
+ // The parent.
+ case 0:
+ return info.parent;
+
+ // The name.
+ case 1:
+ return (info.name == nullptr) ? nullptr : env->NewStringUTF(info.name);
+
+ // The priority. Use a string for simplicity of construction.
+ case 2:
+ return env->NewStringUTF(android::base::StringPrintf("%d", info.max_priority).c_str());
+
+ // Whether it's a daemon. Use a string for simplicity of construction.
+ case 3:
+ return env->NewStringUTF(info.is_daemon == JNI_TRUE ? "true" : "false");
+ }
+ LOG(FATAL) << "Should not reach here";
+ UNREACHABLE();
+ };
+ return CreateObjectArray(env, 4, "java/lang/Object", callback);
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getThreadGroupChildren(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jthreadGroup group) {
+ jint thread_count;
+ jthread* threads;
+ jint threadgroup_count;
+ jthreadGroup* groups;
+
+ jvmtiError result = jvmti_env->GetThreadGroupChildren(group,
+ &thread_count,
+ &threads,
+ &threadgroup_count,
+ &groups);
+ if (JvmtiErrorToException(env, result)) {
+ return nullptr;
+ }
+
+ auto callback = [&](jint component_index) -> jobject {
+ if (component_index == 0) {
+ // Threads.
+ auto inner_callback = [&](jint index) {
+ return threads[index];
+ };
+ return CreateObjectArray(env, thread_count, "java/lang/Thread", inner_callback);
+ } else {
+ // Groups.
+ auto inner_callback = [&](jint index) {
+ return groups[index];
+ };
+ return CreateObjectArray(env, threadgroup_count, "java/lang/ThreadGroup", inner_callback);
+ }
+ };
+ jobjectArray ret = CreateObjectArray(env, 2, "java/lang/Object", callback);
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(threads));
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(groups));
+
+ return ret;
+}
+
+} // namespace Test925ThreadGroups
+} // namespace art
diff --git a/test/928-jni-table/build b/test/928-jni-table/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/928-jni-table/build
@@ -0,0 +1,17 @@
+#!/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.
+
+./default-build "$@" --experimental agents
diff --git a/test/928-jni-table/expected.txt b/test/928-jni-table/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/928-jni-table/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/928-jni-table/info.txt b/test/928-jni-table/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/928-jni-table/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
new file mode 100644
index 0000000..5123d3a
--- /dev/null
+++ b/test/928-jni-table/jni_table.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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 <stdio.h>
+
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test927JNITable {
+
+// This test is equivalent to the jni_internal_test JNIEnvExtTableOverride.
+
+static size_t gGlobalRefCount = 0;
+static JNINativeInterface* gOriginalEnv = nullptr;
+
+static jobject CountNewGlobalRef(JNIEnv* env, jobject o) {
+ ++gGlobalRefCount;
+ return gOriginalEnv->NewGlobalRef(env, o);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doJNITableTest(
+ JNIEnv* env, jclass klass) {
+ // Get the current table, as the delegate.
+ jvmtiError getorig_result = jvmti_env->GetJNIFunctionTable(&gOriginalEnv);
+ if (JvmtiErrorToException(env, getorig_result)) {
+ return;
+ }
+
+ // Get the current table, as the override we'll install.
+ JNINativeInterface* env_override;
+ jvmtiError getoverride_result = jvmti_env->GetJNIFunctionTable(&env_override);
+ if (JvmtiErrorToException(env, getoverride_result)) {
+ return;
+ }
+
+ env_override->NewGlobalRef = CountNewGlobalRef;
+ gGlobalRefCount = 0;
+
+ // Install the override.
+ jvmtiError setoverride_result = jvmti_env->SetJNIFunctionTable(env_override);
+ if (JvmtiErrorToException(env, setoverride_result)) {
+ return;
+ }
+
+ jobject global = env->NewGlobalRef(klass);
+ CHECK_EQ(1u, gGlobalRefCount);
+ env->DeleteGlobalRef(global);
+
+ // Install the "original." There is no real reset.
+ jvmtiError setoverride2_result = jvmti_env->SetJNIFunctionTable(gOriginalEnv);
+ if (JvmtiErrorToException(env, setoverride2_result)) {
+ return;
+ }
+
+ jobject global2 = env->NewGlobalRef(klass);
+ CHECK_EQ(1u, gGlobalRefCount);
+ env->DeleteGlobalRef(global2);
+
+ // Try to install null. Should return NULL_POINTER error.
+ jvmtiError setoverride3_result = jvmti_env->SetJNIFunctionTable(nullptr);
+ if (setoverride3_result != JVMTI_ERROR_NULL_POINTER) {
+ LOG(FATAL) << "Didn't receive NULL_POINTER";
+ }
+
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(env_override));
+}
+
+} // namespace Test927JNITable
+} // namespace art
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
new file mode 100755
index 0000000..4379349
--- /dev/null
+++ b/test/928-jni-table/run
@@ -0,0 +1,19 @@
+#!/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.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti
diff --git a/test/928-jni-table/src/Main.java b/test/928-jni-table/src/Main.java
new file mode 100644
index 0000000..b0baea1
--- /dev/null
+++ b/test/928-jni-table/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doJNITableTest();
+
+ System.out.println("Done");
+ }
+
+ public static native void doJNITableTest();
+}
diff --git a/test/929-search/build b/test/929-search/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/929-search/build
@@ -0,0 +1,17 @@
+#!/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.
+
+./default-build "$@" --experimental agents
diff --git a/test/929-search/expected.txt b/test/929-search/expected.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/929-search/expected.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/929-search/info.txt b/test/929-search/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/929-search/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/929-search/run b/test/929-search/run
new file mode 100755
index 0000000..0a8d067
--- /dev/null
+++ b/test/929-search/run
@@ -0,0 +1,23 @@
+#!/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.
+
+# This test checks whether dex files can be injected into parent classloaders. App images preload
+# classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --jvmti \
+ --no-app-image
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
new file mode 100644
index 0000000..d1c6984
--- /dev/null
+++ b/test/929-search/search.cc
@@ -0,0 +1,53 @@
+/*
+ * 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 <inttypes.h>
+
+#include "android-base/stringprintf.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedUtfChars.h"
+
+#include "ti-agent/common_helper.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test929Search {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToBootClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToBootstrapClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_addToSystemClassLoader(
+ JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jstring segment) {
+ ScopedUtfChars utf(env, segment);
+ if (utf.c_str() == nullptr) {
+ return;
+ }
+ jvmtiError result = jvmti_env->AddToSystemClassLoaderSearch(utf.c_str());
+ JvmtiErrorToException(env, result);
+}
+
+} // namespace Test929Search
+} // namespace art
diff --git a/test/929-search/src-ex/A.java b/test/929-search/src-ex/A.java
new file mode 100644
index 0000000..64acb2f
--- /dev/null
+++ b/test/929-search/src-ex/A.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class A {
+}
\ No newline at end of file
diff --git a/test/929-search/src/B.java b/test/929-search/src/B.java
new file mode 100644
index 0000000..f1458c3
--- /dev/null
+++ b/test/929-search/src/B.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class B {
+}
\ No newline at end of file
diff --git a/test/929-search/src/Main.java b/test/929-search/src/Main.java
new file mode 100644
index 0000000..d253e6f
--- /dev/null
+++ b/test/929-search/src/Main.java
@@ -0,0 +1,54 @@
+/*
+ * 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.util.Arrays;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ private static void doTest() throws Exception {
+ doTest(true, DEX1, "B");
+ doTest(false, DEX2, "A");
+ System.out.println("Done");
+ }
+
+ private static void doTest(boolean boot, String segment, String className) throws Exception {
+ ClassLoader expectedClassLoader;
+ if (boot) {
+ expectedClassLoader = Object.class.getClassLoader();
+ addToBootClassLoader(segment);
+ } else {
+ expectedClassLoader = ClassLoader.getSystemClassLoader();
+ addToSystemClassLoader(segment);
+ }
+
+ Class<?> c = Class.forName(className);
+ if (c.getClassLoader() != expectedClassLoader) {
+ throw new RuntimeException(className + "(" + boot + "/" + segment + "): " +
+ c.getClassLoader() + " vs " + expectedClassLoader);
+ }
+ }
+
+ private static native void addToBootClassLoader(String s);
+ private static native void addToSystemClassLoader(String s);
+
+ private static final String DEX1 = System.getenv("DEX_LOCATION") + "/929-search.jar";
+ private static final String DEX2 = System.getenv("DEX_LOCATION") + "/929-search-ex.jar";
+}
diff --git a/test/Android.bp b/test/Android.bp
index 1ea1252..965d07a 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -265,7 +265,10 @@
"922-properties/properties.cc",
"923-monitors/monitors.cc",
"924-threads/threads.cc",
+ "925-threadgroups/threadgroups.cc",
"927-timers/timers.cc",
+ "928-jni-table/jni_table.cc",
+ "929-search/search.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 55cef97..e604c93 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -280,6 +280,7 @@
# These 9** tests are not supported in current form due to linker
# restrictions. See b/31681198
TEST_ART_BROKEN_TARGET_TESTS += \
+ 901-hello-ti-agent \
902-hello-transformation \
903-hello-tagging \
904-object-allocation \
@@ -303,8 +304,11 @@
922-properties \
923-monitors \
924-threads \
+ 925-threadgroups \
926-multi-obsolescence \
927-timers \
+ 928-jni-table \
+ 929-search \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -465,10 +469,12 @@
629-vdex-speed
# This test fails without an image.
-# 964 often times out due to the large number of classes it tries to compile.
+# 018, 961, 964 often time out. b/34369284
TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \
137-cfi \
138-duplicate-classes-check \
+ 018-stack-overflow \
+ 961-default-iface-resolution-gen \
964-default-iface-init
ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES)))
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def
index abb5e1e..43f9434 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_dexcache.def
@@ -34,7 +34,6 @@
// New macro suffix Method Name (of the Offset method)
DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_METHODS, DexCacheResolvedMethods)
-DEFINE_ART_METHOD_OFFSET_SIZED(DEX_CACHE_TYPES, DexCacheResolvedTypes)
DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni)
DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode)
DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass)