diff options
97 files changed, 1809 insertions, 1073 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 5da59f3b3d..faf8b41be1 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1060,13 +1060,13 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { 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 @@ class ResolveCatchBlockExceptionsClassVisitor : public ClassVisitor { 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 86d92ff0b5..c69ed3198b 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -487,7 +487,7 @@ TEST_F(OatTest, OatHeaderSizeCheck) { EXPECT_EQ(72U, sizeof(OatHeader)); EXPECT_EQ(4U, sizeof(OatMethodOffsets)); EXPECT_EQ(20U, sizeof(OatQuickMethodHeader)); - EXPECT_EQ(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 7dc094b25f..2ee4db923a 100644 --- a/compiler/optimizing/bounds_check_elimination.cc +++ b/compiler/optimizing/bounds_check_elimination.cc @@ -153,21 +153,6 @@ class ValueBound : public ValueObject { 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 @@ class BCEVisitor : public HGraphVisitor { } 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 @@ class BCEVisitor : public HGraphVisitor { 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 dfa150406d..5d58207511 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -596,13 +596,11 @@ static HInstruction* BuildSSAGraph3(HGraph* graph, 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 07b174698a..9c9c604dca 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -3984,19 +3984,16 @@ void LocationsBuilderARM::VisitNewArray(HNewArray* instruction) { 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 b094e54f8a..68d0b869e7 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -4747,22 +4747,16 @@ void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) { 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 05a76e1105..592ee5aba6 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -431,10 +431,12 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { // 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 @@ void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) { 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 24234e18c1..a0383826d3 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -5897,21 +5897,14 @@ void LocationsBuilderMIPS::VisitNewArray(HNewArray* instruction) { 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 fc8fb7acb2..446dea659e 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -3841,19 +3841,14 @@ void LocationsBuilderMIPS64::VisitNewArray(HNewArray* instruction) { 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 cc727d2068..853c91fac8 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -4192,18 +4192,15 @@ void LocationsBuilderX86::VisitNewArray(HNewArray* instruction) { 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 9adedab130..74c71cce1f 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4088,21 +4088,16 @@ void LocationsBuilderX86_64::VisitNewArray(HNewArray* instruction) { 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 6d8ae75460..3973985338 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -114,12 +114,7 @@ static bool IsMaxAtHint( } } 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 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, 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 aa3e1aab4f..d81817fb09 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -697,13 +697,8 @@ TEST_F(InductionVarRangeTest, MaxValue) { } 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 50aa4425d9..5d40f75618 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1429,15 +1429,6 @@ bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction, 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 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* } } - 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 @@ bool HInliner::ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* ++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 @@ void HInliner::FixUpReturnReferenceType(ArtMethod* resolved_method, // 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 8ed0e7fa06..48653cfeb9 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1498,16 +1498,8 @@ void HInstructionBuilder::BuildFilledNewArray(uint32_t dex_pc, 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 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, 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 911bfb9cc6..35f59cb4a4 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -777,7 +777,7 @@ void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { // 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 @@ static bool IsArrayLengthOf(HInstruction* potential_length, HInstruction* potent } 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 53b0fdde75..a2980dca20 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3801,6 +3801,15 @@ class HNewInstance FINAL : public HExpression<1> { 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 HNeg FINAL : public HUnaryOperation { 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 @@ class HNewArray FINAL : public HExpression<2> { 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 @@ class HClinitCheck FINAL : public HExpression<1> { 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 @@ inline void MakeRoomFor(ArenaVector<HBasicBlock*>* blocks, 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 a4d59ab587..b02f2509ab 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -548,11 +548,13 @@ void ReferenceTypePropagation::RTPVisitor::UpdateReferenceTypeInfo(HInstruction* } 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 @@ void ReferenceTypePropagation::RTPVisitor::VisitInvoke(HInvoke* instr) { } 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 5e552106ab..a3fce02970 100644 --- a/compiler/utils/assembler_thumb_test_expected.cc.inc +++ b/compiler/utils/assembler_thumb_test_expected.cc.inc @@ -5610,7 +5610,7 @@ const char* const VixlJniHelpersResults[] = { " 214: ecbd 8a10 vpop {s16-s31}\n", " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n", " 21c: 4660 mov r0, ip\n", - " 21e: f8d9 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 102c313a6a..db1cad670d 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -856,27 +856,6 @@ END art_quick_check_instance_of #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 3b3783cad4..436d331119 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1404,33 +1404,6 @@ END art_quick_check_instance_of #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_FOR_NON_REGION_TLAB_ALLOCATORS // 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 @@ END art_quick_alloc_object_resolved_rosalloc // 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 @@ GENERATE_ALLOC_OBJECT_RESOLVED_REGION_TLAB art_quick_alloc_object_initialized_re // 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 5c569232ac..36f9ea78e1 100644 --- a/runtime/arch/mips/entrypoints_init_mips.cc +++ b/runtime/arch/mips/entrypoints_init_mips.cc @@ -142,16 +142,8 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { 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 @@ void InitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) { 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 3acc0a9d5b..76218fb542 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1389,28 +1389,6 @@ END art_quick_check_instance_of #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 @@ END art_quick_string_compareto 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 @@ ENTRY art_quick_invoke_polymorphic 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: - lbu $v0, 16($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 16($sp) # Move byte from JValue result to return value register. .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 @@ ENTRY art_quick_invoke_polymorphic 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 ae786fe626..b53fd100fa 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1360,29 +1360,6 @@ END art_quick_check_instance_of #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 @@ ENTRY art_quick_invoke_polymorphic 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 @@ ENTRY art_quick_invoke_polymorphic 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: - lbu $v0, 0($sp) # Move byte from JValue result to return value register. b .Lcleanup_and_return - nop + lbu $v0, 0($sp) # Move byte from JValue result to return value register. .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: - l.d $f0, 0($sp) # Move double/float from JValue result to return value register. b .Lcleanup_and_return - nop + l.d $f0, 0($sp) # Move double/float from JValue result to return value register. .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 abd9046174..cd6424a489 100644 --- a/runtime/arch/quick_alloc_entrypoints.S +++ b/runtime/arch/quick_alloc_entrypoints.S @@ -25,14 +25,10 @@ ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFrom // 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 @@ GENERATE_ALLOC_ENTRYPOINTS _region_tlab_instrumented, RegionTLABInstrumented #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_OBJECT_WITH_ACCESS_CHECK(_region_tlab, RegionTL 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_OBJECT_WITH_ACCESS_CHECK(_tlab, TLAB) 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_OBJECT_WITH_ACCESS_CHECK(_dlmalloc, DlMalloc) 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_OBJECT_WITH_ACCESS_CHECK(_dlmalloc_instrumented 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_OBJECT_WITH_ACCESS_CHECK(_rosalloc, RosAlloc) 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_OBJECT_WITH_ACCESS_CHECK(_rosalloc_instrumented 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_OBJECT_WITH_ACCESS_CHECK(_bump_pointer, BumpPoi 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_OBJECT_WITH_ACCESS_CHECK(_bump_pointer_instrume 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_OBJECT_WITH_ACCESS_CHECK(_tlab_instrumented, TL 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_OBJECT_WITH_ACCESS_CHECK(_region, Region) 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_OBJECT_WITH_ACCESS_CHECK(_region_instrumented, 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_OBJECT_WITH_ACCESS_CHECK(_region_tlab_instrumen 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 ee65fa8ab0..393dfe6d19 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -908,139 +908,6 @@ TEST_F(StubTest, CheckCast) { #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 1d979d852e..c4202596f2 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -1352,26 +1352,6 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax) #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 28034c9bae..f5509bddcb 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_FOR_NON_TLAB_ALLOCATORS // 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_STRING_FROM_STRING(_region_tlab, RegionTLAB) // 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 @@ MACRO1(ALLOC_OBJECT_INITIALIZED_TLAB_FAST_PATH, slowPathLabel) 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 @@ MACRO1(ALLOC_ARRAY_TLAB_FAST_PATH_RESOLVED, slowPathLabel) // 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 @@ END_MACRO 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 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_tlab 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 @@ END_MACRO * 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 @@ MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64) #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 9c207400a2..0fd891c9ec 100644 --- a/runtime/art_method-inl.h +++ b/runtime/art_method-inl.h @@ -180,20 +180,6 @@ inline GcRoot<mirror::Class>* ArtMethod::GetDexCacheResolvedTypes(PointerSize po 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 @@ inline bool ArtMethod::HasSameDexCacheResolvedTypes(ArtMethod* other, PointerSiz 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 @@ inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { 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 @@ inline void ArtMethod::SetDexCacheResolvedTypes(GcRoot<mirror::Class>* new_dex_c 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 dfc7837aea..d7d39afa8f 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -236,7 +236,6 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, // 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 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, 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 2c31f6c95e..912df85815 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -351,9 +351,6 @@ class ArtMethod FINAL { 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 @@ class ArtMethod FINAL { 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 @@ class ArtMethod FINAL { 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 @@ class ArtMethod FINAL { // 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 4b15a22411..18a53c916e 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -104,7 +104,7 @@ ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET, // 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 edb58c4eb7..fcc92dd96d 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -46,6 +46,7 @@ Mutex* Locks::deoptimization_lock_ = nullptr; 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 @@ void Locks::Init() { 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 @@ void Locks::Init() { 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 e44bdb89cc..3867b1b810 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -68,6 +68,7 @@ enum LockLevel { kRosAllocBulkFreeLock, kMarkSweepMarkStackLock, kTransactionLogLock, + kJniFunctionTableLock, kJniWeakGlobalsLock, kJniGlobalsLock, kReferenceQueueSoftReferencesLock, @@ -702,8 +703,11 @@ class Locks { // 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 5fc5f1a2f5..2e17dd85e6 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 @@ inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMetho 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 @@ ArtMethod* ClassLinker::FindMethodForProxy(ObjPtr<mirror::Class> proxy_class, // 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 448b4600cc..07c6eda03f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3303,7 +3303,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file, 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 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons 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 void ThrowSignatureMismatch(Handle<mirror::Class> klass, } static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, - PointerSize pointer_size, Handle<mirror::Class> klass, Handle<mirror::Class> super_klass, ArtMethod* method1, @@ -4844,14 +4842,12 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, 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 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, 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 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, } 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 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { 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 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle<mirror::Class> klass) { 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 cad674e472..9b98671cb4 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -649,6 +649,10 @@ class ClassLinker { 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 @@ class ClassLinker { // 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 @@ class ClassLinker { 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 c97c4e4f5b..6da7e3a94e 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -3987,9 +3987,7 @@ JDWP::JdwpError Dbg::PrepareInvokeMethod(uint32_t request_id, JDWP::ObjectId thr 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 9504e8bd29..16a447b0a6 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -608,7 +608,7 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, 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 @@ mirror::Object* GetAnnotationDefaultValue(ArtMethod* method) { 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 7d6f866617..ac0ce36016 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -241,10 +241,9 @@ inline mirror::Class* CheckArrayAlloc(dex::TypeIndex type_idx, *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 @@ inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, 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 @@ inline mirror::Array* AllocArrayFromCodeResolved(mirror::Class* klass, 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 b17e1a8ab1..25fd727968 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 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons 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 d4cf83c8de..6a04f20f3a 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -93,33 +93,14 @@ ALWAYS_INLINE inline mirror::Array* AllocArrayFromCode(dex::TypeIndex type_idx, 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 2d06508069..3fa5fbf44f 100644 --- a/runtime/entrypoints/quick/quick_alloc_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_alloc_entrypoints.cc @@ -93,11 +93,11 @@ extern "C" mirror::Array* artAllocArrayFromCode##suffix##suffix2( \ 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 @@ extern "C" mirror::Array* artAllocArrayFromCodeWithAccessCheck##suffix##suffix2( 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 @@ GENERATE_ENTRYPOINTS_FOR_ALLOCATOR(RegionTLAB, gc::kAllocatorTypeRegionTLAB) #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_bytes##suffix(void*, int32_t, int32 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 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument 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 @@ void SetQuickAllocEntryPoints##suffix(QuickEntryPoints* qpoints, bool instrument 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 8ce61c1021..6481b97ae1 100644 --- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h +++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h @@ -66,10 +66,7 @@ void DefaultInitEntryPoints(JniEntryPoints* jpoints, QuickEntryPoints* qpoints) 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 4d5d6de41d..b44f29b410 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 6301f93b2f..b9caf0f555 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -161,12 +161,8 @@ class EntrypointsOrderTest : public CommonRuntimeTest { 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 @@ class EntrypointsOrderTest : public CommonRuntimeTest { 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 7b86339663..6044053b4f 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -846,7 +846,7 @@ void ConcurrentCopying::IssueEmptyCheckpoint() { // 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 bebcd71951..9e7f52bfd3 100644 --- a/runtime/generated/asm_support_gen.h +++ b/runtime/generated/asm_support_gen.h @@ -60,10 +60,6 @@ DEFINE_CHECK_EQ(static_cast<uint32_t>(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT), (s 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 ca26207093..76777d938b 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -751,16 +751,14 @@ static inline bool DoCallCommon(ArtMethod* called_method, 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 d7dfcd4408..a77a3fc2b3 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -285,9 +285,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, 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 5a3fafa726..0148a1c3b0 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 @@ using android::base::StringPrintf; 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 @@ JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) 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::DeleteLocalRef(jobject obj) { 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 @@ void JNIEnvExt::CheckNoHeldMonitors() { } } +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 5cca0aef9b..4004c457b5 100644 --- a/runtime/jni_env_ext.h +++ b/runtime/jni_env_ext.h @@ -43,7 +43,7 @@ struct JNIEnvExt : public JNIEnv { 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 @@ struct JNIEnvExt : public JNIEnv { // 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 4da5e23502..08d1eeb95d 100644 --- a/runtime/jni_internal_test.cc +++ b/runtime/jni_internal_test.cc @@ -2346,4 +2346,39 @@ TEST_F(JniInternalTest, JNIEnvExtOffsets) { 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 a6f56aee42..6a4ec9dca7 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -306,23 +306,6 @@ TEST_F(ObjectTest, PrimitiveArray_Float_Alloc) { } -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 ab03252b9b..5a4bc1cd1f 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -32,7 +32,7 @@ class InstructionSetFeatures; class PACKED(4) OatHeader { public: static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' }; - static constexpr uint8_t kOatVersion[] = { '0', '9', '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 42fed50baf..d5c652035a 100644 --- a/runtime/openjdkjvmti/Android.bp +++ b/runtime/openjdkjvmti/Android.bp @@ -23,13 +23,16 @@ cc_defaults { "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 4aedec9be0..195f179d3c 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 @@ class JvmtiFunctions { 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 @@ class JvmtiFunctions { 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 @@ class JvmtiFunctions { } 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 @@ class JvmtiFunctions { } } - 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 @@ class JvmtiFunctions { } 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 @@ class JvmtiFunctions { } 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 d0272010d4..fb39db5da9 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 @@ ALWAYS_INLINE static inline FnType* GetCallback(ArtJvmTiEnv* env, jvmtiEvent eve // 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 void EventHandler::DispatchEvent(art::Thread* thread, jvmtiEvent event, A } } +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 12692a168d..66929cf8ab 100644 --- a/runtime/openjdkjvmti/events.cc +++ b/runtime/openjdkjvmti/events.cc @@ -83,7 +83,7 @@ EventMask* EventMasks::GetEventMaskOrNull(art::Thread* thread) { } -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::EnableEvent(art::Thread* thread, jvmtiEvent event) { } } -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 @@ void EventHandler::RegisterArtJvmTiEnv(ArtJvmTiEnv* env) { 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 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { 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 @@ class JvmtiAllocationListener : public art::gc::AllocationListener { 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 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { 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 @@ class JvmtiGcPauseListener : public art::gc::GcPauseListener { 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 @@ static void SetupGcPauseTracking(JvmtiGcPauseListener* listener, jvmtiEvent even } // 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 @@ void EventHandler::HandleEventType(jvmtiEvent event, bool enable) { 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 07d6bfd4c2..8f56145217 100644 --- a/runtime/openjdkjvmti/events.h +++ b/runtime/openjdkjvmti/events.h @@ -30,22 +30,70 @@ struct ArtJvmTiEnv; 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 @@ struct EventMasks { 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 @@ class EventHandler { // 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 ee708cb193..de07c163fc 100644 --- a/runtime/openjdkjvmti/jvmti.h +++ b/runtime/openjdkjvmti/jvmti.h @@ -74,7 +74,7 @@ typedef jobject jthreadGroup; 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 b983e79658..94cb46a428 100644 --- a/runtime/openjdkjvmti/object_tagging.cc +++ b/runtime/openjdkjvmti/object_tagging.cc @@ -177,7 +177,7 @@ bool ObjectTagTable::SetLocked(art::Thread* self, art::mirror::Object* obj, jlon } 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::SweepImpl(art::IsMarkedVisitor* visitor) { } 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 0000000000..88f0395ba5 --- /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 0000000000..906aab0667 --- /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 0000000000..913d2b6a74 --- /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 0000000000..6a52e80405 --- /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 0000000000..35b1bfd920 --- /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 0000000000..c3a0ff5e15 --- /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 4d2450135e..75176f938e 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -227,11 +227,11 @@ class ArgArray { 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 @@ static void CheckMethodArguments(JavaVMExt* vm, ArtMethod* m, uint32_t* args) } // 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 bf69e1010d..8c8afdc913 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -2641,8 +2641,6 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { 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 @@ void Thread::DumpThreadOffset(std::ostream& os, uint32_t offset) { 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 25a179bd32..b915457557 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2901,9 +2901,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { 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 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } 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 @@ InstructionFlags* MethodVerifier::CurrentInsnFlags() { 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 6ba01ff0b5..4e48e0e1a6 100644 --- a/test/129-ThreadGetId/src/Main.java +++ b/test/129-ThreadGetId/src/Main.java @@ -23,7 +23,6 @@ public class Main implements Runnable { 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 @@ public class Main implements Runnable { 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 2dc5666e18..1e15654950 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 @@ public class Main { /// 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 89b97ed316..03a84486de 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 @@ public class Main { } /// 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 @@ public class Main { /// 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 0000000000..898e2e54a2 --- /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 0000000000..7d1a259c89 --- /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 0000000000..875a5f6ec1 --- /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 0000000000..4379349cb2 --- /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 0000000000..c59efe2f7b --- /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 0000000000..6c6e835dd3 --- /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 0000000000..898e2e54a2 --- /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 0000000000..a965a70ed4 --- /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 0000000000..875a5f6ec1 --- /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 0000000000..5123d3a43f --- /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 0000000000..4379349cb2 --- /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 0000000000..b0baea1f9d --- /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 0000000000..898e2e54a2 --- /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 0000000000..a965a70ed4 --- /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 0000000000..875a5f6ec1 --- /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 0000000000..0a8d0672f6 --- /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 0000000000..d1c698491e --- /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 0000000000..64acb2fcfe --- /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 0000000000..f1458c3bca --- /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 0000000000..d253e6fdf6 --- /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 1ea1252893..965d07aa43 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -265,7 +265,10 @@ art_cc_defaults { "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 55cef974ab..e604c93c72 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -280,6 +280,7 @@ TEST_ART_BROKEN_TARGET_TESTS := \ # 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 @@ TEST_ART_BROKEN_TARGET_TESTS += \ 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 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ 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 abb5e1ecac..43f94344cc 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) |