diff options
-rw-r--r-- | dex2oat/verifier_deps_test.cc | 6 | ||||
-rw-r--r-- | openjdkjvmti/ti_method.cc | 10 | ||||
-rw-r--r-- | runtime/verifier/class_verifier.cc | 4 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.cc | 280 | ||||
-rw-r--r-- | runtime/verifier/method_verifier.h | 8 | ||||
-rw-r--r-- | runtime/verifier/reg_type.cc | 7 | ||||
-rw-r--r-- | runtime/verifier/reg_type_cache-inl.h | 29 | ||||
-rw-r--r-- | runtime/verifier/reg_type_cache.cc | 101 | ||||
-rw-r--r-- | runtime/verifier/reg_type_cache.h | 41 | ||||
-rw-r--r-- | runtime/verifier/reg_type_test.cc | 145 |
10 files changed, 325 insertions, 306 deletions
diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc index 46e40902de..686f18803c 100644 --- a/dex2oat/verifier_deps_test.cc +++ b/dex2oat/verifier_deps_test.cc @@ -162,14 +162,14 @@ class VerifierDepsTest : public CommonCompilerDriverTest { method.GetInvokeType(class_def->access_flags_)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { - RegTypeCache reg_types(soa.Self(), class_linker_, Runtime::Current()->GetArenaPool()); + ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); + RegTypeCache reg_types( + soa.Self(), class_linker_, arena_pool, class_loader_handle, primary_dex_file_); std::unique_ptr<MethodVerifier> verifier( MethodVerifier::CreateVerifier(soa.Self(), ®_types, callbacks_->GetVerifierDeps(), - primary_dex_file_, dex_cache_handle, - class_loader_handle, *class_def, method.GetCodeItem(), method.GetIndex(), diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc index 6150a4692b..bf89b6e476 100644 --- a/openjdkjvmti/ti_method.cc +++ b/openjdkjvmti/ti_method.cc @@ -642,15 +642,19 @@ class CommonLocalVariableClosure : public art::Closure { art::Runtime* runtime = art::Runtime::Current(); art::ClassLinker* class_linker = runtime->GetClassLinker(); art::ArenaPool* arena_pool = runtime->GetArenaPool(); - art::verifier::RegTypeCache reg_types( - self, class_linker, arena_pool, /* can_load_classes= */ false, /* can_suspend= */ false); + art::verifier::RegTypeCache reg_types(self, + class_linker, + arena_pool, + class_loader, + dex_cache->GetDexFile(), + /* can_load_classes= */ false, + /* can_suspend= */ false); std::unique_ptr<art::verifier::MethodVerifier> verifier( art::verifier::MethodVerifier::CalculateVerificationInfo( self, ®_types, method, dex_cache, - class_loader, dex_pc)); if (verifier == nullptr) { JVMTI_LOG(WARNING, jvmti_) << "Unable to extract verification information from " diff --git a/runtime/verifier/class_verifier.cc b/runtime/verifier/class_verifier.cc index d6fd64e376..07202901fc 100644 --- a/runtime/verifier/class_verifier.cc +++ b/runtime/verifier/class_verifier.cc @@ -113,7 +113,7 @@ FailureKind ClassVerifier::VerifyClass(Thread* self, if (accessor.NumMethods() != 0u) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); - RegTypeCache reg_types(self, linker, arena_pool); + RegTypeCache reg_types(self, linker, arena_pool, class_loader, dex_file); for (const ClassAccessor::Method& method : accessor.GetMethods()) { int64_t* previous_idx = &previous_method_idx[method.IsStaticOrDirect() ? 0u : 1u]; self->AllowThreadSuspension(); @@ -131,9 +131,7 @@ FailureKind ClassVerifier::VerifyClass(Thread* self, ®_types, verifier_deps, method_idx, - dex_file, dex_cache, - class_loader, class_def, method.GetCodeItem(), method.GetAccessFlags(), diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index f8f4c8d8f9..52907beb5d 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -133,12 +133,10 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { ArenaPool* arena_pool, RegTypeCache* reg_types, VerifierDeps* verifier_deps, - const DexFile* dex_file, const dex::CodeItem* code_item, uint32_t method_idx, bool aot_mode, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def, uint32_t access_flags, bool verify_to_dump, @@ -147,7 +145,6 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { arena_pool, reg_types, verifier_deps, - dex_file, class_def, code_item, method_idx, @@ -155,7 +152,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { method_access_flags_(access_flags), return_type_(nullptr), dex_cache_(dex_cache), - class_loader_(class_loader), + class_loader_(reg_types->GetClassLoader()), declaring_class_(nullptr), interesting_dex_pc_(-1), monitor_enter_dex_pcs_(nullptr), @@ -163,6 +160,9 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { allow_thread_suspension_(reg_types->CanSuspend()), is_constructor_(false), api_level_(api_level == 0 ? std::numeric_limits<uint32_t>::max() : api_level) { + DCHECK_EQ(dex_cache->GetDexFile(), reg_types->GetDexFile()) + << dex_cache->GetDexFile()->GetLocation() << " / " + << reg_types->GetDexFile()->GetLocation(); } void FinalAbstractClassError(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -671,9 +671,7 @@ class MethodVerifier final : public ::art::verifier::MethodVerifier { const RegType& GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_) { if (declaring_class_ == nullptr) { const dex::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); - const char* descriptor - = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - declaring_class_ = ®_types_.FromDescriptor(class_loader_, descriptor); + declaring_class_ = ®_types_.FromTypeIndex(method_id.class_idx_); } return *declaring_class_; } @@ -2521,7 +2519,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data for array of type " << array_type; } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type); DCHECK(!component_type.IsConflict()); if (component_type.IsNonZeroReferenceTypes()) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "invalid fill-array-data with component type " @@ -2846,12 +2844,11 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g ArtMethod* called_method = VerifyInvocationArgs(inst, type, is_range); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); - dex::TypeIndex return_type_idx = - dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - const char* return_type_descriptor = dex_file_->GetTypeDescriptor(return_type_idx); + dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; DCHECK_IMPLIES(called_method != nullptr, - called_method->GetReturnTypeDescriptorView() == return_type_descriptor); - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor); + called_method->GetReturnTypeDescriptorView() == + dex_file_->GetTypeDescriptorView(return_type_idx)); + const RegType& return_type = reg_types_.FromTypeIndex(return_type_idx); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -2866,11 +2863,10 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_DIRECT, is_range); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); - dex::TypeIndex return_type_idx = - dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - const char* return_type_descriptor = dex_file_->GetTypeDescriptor(return_type_idx); + dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; DCHECK_IMPLIES(called_method != nullptr, - called_method->GetReturnTypeDescriptorView() == return_type_descriptor); + called_method->GetReturnTypeDescriptorView() == + dex_file_->GetTypeDescriptorView(return_type_idx)); bool is_constructor = (called_method != nullptr) ? called_method->IsConstructor() : dex_file_->GetStringView(method_id.name_idx_) == "<init>"; @@ -2914,7 +2910,7 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g */ work_line_->MarkRefsAsInitialized(this, this_type); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor); + const RegType& return_type = reg_types_.FromTypeIndex(return_type_idx); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -2925,27 +2921,23 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g } case Instruction::INVOKE_STATIC: case Instruction::INVOKE_STATIC_RANGE: { - bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); - ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range); - const char* descriptor; - if (called_method == nullptr) { - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); - dex::TypeIndex return_type_idx = - dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->GetTypeDescriptor(return_type_idx); - } else { - descriptor = called_method->GetReturnTypeDescriptor(); - } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); - if (!return_type.IsLowHalf()) { - work_line_->SetResultRegisterType(this, return_type); - } else { - work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); - } - just_set_result = true; + bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); + ArtMethod* called_method = VerifyInvocationArgs(inst, METHOD_STATIC, is_range); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); + dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + DCHECK_IMPLIES(called_method != nullptr, + called_method->GetReturnTypeDescriptorView() == + dex_file_->GetTypeDescriptorView(return_type_idx)); + const RegType& return_type = reg_types_.FromTypeIndex(return_type_idx); + if (!return_type.IsLowHalf()) { + work_line_->SetResultRegisterType(this, return_type); + } else { + work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); } + just_set_result = true; break; + } case Instruction::INVOKE_INTERFACE: case Instruction::INVOKE_INTERFACE_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE); @@ -2982,17 +2974,13 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g * We don't have an object instance, so we can't find the concrete method. However, all of * the type information is in the abstract method, so we're good. */ - const char* descriptor; - if (abs_method == nullptr) { - uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); - dex::TypeIndex return_type_idx = - dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; - descriptor = dex_file_->GetTypeDescriptor(return_type_idx); - } else { - descriptor = abs_method->GetReturnTypeDescriptor(); - } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); + uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); + const dex::MethodId& method_id = dex_file_->GetMethodId(method_idx); + dex::TypeIndex return_type_idx = dex_file_->GetProtoId(method_id.proto_idx_).return_type_idx_; + DCHECK_IMPLIES(abs_method != nullptr, + abs_method->GetReturnTypeDescriptorView() == + dex_file_->GetTypeDescriptorView(return_type_idx)); + const RegType& return_type = reg_types_.FromTypeIndex(return_type_idx); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3020,12 +3008,9 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g DCHECK(HasFailures()); break; } - const uint16_t vRegH = (is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc(); - const dex::ProtoIndex proto_idx(vRegH); - const char* return_descriptor = - dex_file_->GetReturnTypeDescriptor(dex_file_->GetProtoId(proto_idx)); + const dex::ProtoIndex proto_idx((is_range) ? inst->VRegH_4rcc() : inst->VRegH_45cc()); const RegType& return_type = - reg_types_.FromDescriptor(class_loader_, return_descriptor); + reg_types_.FromTypeIndex(dex_file_->GetProtoId(proto_idx).return_type_idx_); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3054,11 +3039,9 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g DexFileParameterIterator param_it(*dex_file_, proto_id); // Treat method as static as it has yet to be determined. VerifyInvocationArgsFromIterator(¶m_it, inst, METHOD_STATIC, is_range, nullptr); - const char* return_descriptor = dex_file_->GetReturnTypeDescriptor(proto_id); // Step 3. Propagate return type information - const RegType& return_type = - reg_types_.FromDescriptor(class_loader_, return_descriptor); + const RegType& return_type = reg_types_.FromTypeIndex(proto_id.return_type_idx_); if (!return_type.IsLowHalf()) { work_line_->SetResultRegisterType(this, return_type); } else { @@ -3544,31 +3527,31 @@ bool MethodVerifier<kVerifierDebug>::CodeFlowVerifyInstruction(uint32_t* start_g template <bool kVerifierDebug> template <CheckAccess C> const RegType& MethodVerifier<kVerifierDebug>::ResolveClass(dex::TypeIndex class_idx) { - ClassLinker* linker = GetClassLinker(); - ObjPtr<mirror::Class> klass = CanLoadClasses() - ? linker->ResolveType(class_idx, dex_cache_, class_loader_) - : linker->LookupResolvedType(class_idx, dex_cache_.Get(), class_loader_.Get()); - if (CanLoadClasses() && klass == nullptr) { - DCHECK(self_->IsExceptionPending()); - self_->ClearException(); - } - const RegType* result = nullptr; - if (klass != nullptr) { - result = reg_types_.FindClass(klass); - if (result == nullptr) { - const char* descriptor = dex_file_->GetTypeDescriptor(class_idx); - result = reg_types_.InsertClass(descriptor, klass); + // FIXME: `RegTypeCache` can currently return a few fundamental classes such as j.l.Object + // or j.l.Class without resolving them using the current class loader and recording them + // in the corresponding `ClassTable`. The subsequent method and field lookup by callers of + // `ResolveClass<>()` can then put their methods and fields to the `DexCache` which should + // not be done for classes that are not in the `ClassTable`, potentially leading to crashes. + // For now, we force the class resolution here to avoid the inconsistency. + // Note that there's nothing we can do if we cannot load classes. (The only code path that + // does not allow loading classes is `FindLocksAtDexPc()` which should really need only to + // distinguish between reference and non-reference types and track locking. All the other + // work, including class lookup, is unnecessary as the class has already been verified.) + if (CanLoadClasses()) { + ClassLinker* linker = GetClassLinker(); + ObjPtr<mirror::Class> klass = linker->ResolveType(class_idx, dex_cache_, class_loader_); + if (klass == nullptr) { + DCHECK(self_->IsExceptionPending()); + self_->ClearException(); } - } else { - const char* descriptor = dex_file_->GetTypeDescriptor(class_idx); - result = ®_types_.FromDescriptor(class_loader_, descriptor); } - DCHECK(result != nullptr); - if (result->IsConflict()) { + + const RegType& result = reg_types_.FromTypeIndex(class_idx); + if (result.IsConflict()) { const char* descriptor = dex_file_->GetTypeDescriptor(class_idx); Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "accessing broken descriptor '" << descriptor << "' in " << GetDeclaringClass(); - return *result; + return result; } // If requested, check if access is allowed. Unresolved types are included in this check, as the @@ -3577,23 +3560,23 @@ const RegType& MethodVerifier<kVerifierDebug>::ResolveClass(dex::TypeIndex class // // Note: we do this for unresolved classes to trigger re-verification at runtime. if (C != CheckAccess::kNo && - result->IsNonZeroReferenceTypes() && + result.IsNonZeroReferenceTypes() && ((C == CheckAccess::kYes && IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP)) - || !result->IsUnresolvedTypes())) { + || !result.IsUnresolvedTypes())) { const RegType& referrer = GetDeclaringClass(); if ((IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kP) || !referrer.IsUnresolvedTypes()) && - !referrer.CanAccess(*result)) { + !referrer.CanAccess(result)) { if (IsAotMode()) { Fail(VERIFY_ERROR_ACCESS_CLASS); VLOG(verifier) - << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'"; + << "(possibly) illegal class access: '" << referrer << "' -> '" << result << "'"; } else { Fail(VERIFY_ERROR_ACCESS_CLASS) - << "(possibly) illegal class access: '" << referrer << "' -> '" << *result << "'"; + << "(possibly) illegal class access: '" << referrer << "' -> '" << result << "'"; } } } - return *result; + return result; } template <bool kVerifierDebug> @@ -3880,13 +3863,11 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgsFromIterator( // postponed to runtime). if (res_method != nullptr && !res_method->IsMiranda()) { ObjPtr<mirror::Class> klass = res_method->GetDeclaringClass(); - std::string temp; - res_method_class = ®_types_.FromClass(klass->GetDescriptor(&temp), klass); + res_method_class = ®_types_.FromClass(klass); } else { const uint32_t method_idx = GetMethodIdxOfInvoke(inst); const dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; - res_method_class = - ®_types_.FromDescriptor(class_loader_, dex_file_->GetTypeDescriptor(class_idx)); + res_method_class = ®_types_.FromTypeIndex(class_idx); } if (!res_method_class->IsAssignableFrom(adjusted_type, this)) { Fail(adjusted_type.IsUnresolvedTypes() @@ -3915,15 +3896,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgsFromIterator( return nullptr; } - const char* param_descriptor = it->GetDescriptor(); - - if (param_descriptor == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Rejecting invocation because of missing signature " - "component"; - return nullptr; - } - - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, param_descriptor); + const RegType& reg_type = reg_types_.FromTypeIndex(it->GetTypeIdx()); uint32_t get_reg = is_range ? inst->VRegC() + static_cast<uint32_t>(sig_registers) : arg[sig_registers]; if (reg_type.IsIntegralTypes()) { @@ -4034,32 +4007,6 @@ bool MethodVerifier<kVerifierDebug>::CheckCallSite(uint32_t call_site_idx) { return true; } -class MethodParamListDescriptorIterator { - public: - explicit MethodParamListDescriptorIterator(ArtMethod* res_method) : - res_method_(res_method), pos_(0), params_(res_method->GetParameterTypeList()), - params_size_(params_ == nullptr ? 0 : params_->Size()) { - } - - bool HasNext() { - return pos_ < params_size_; - } - - void Next() { - ++pos_; - } - - const char* GetDescriptor() REQUIRES_SHARED(Locks::mutator_lock_) { - return res_method_->GetTypeDescriptorFromTypeIdx(params_->GetTypeItem(pos_).type_idx_); - } - - private: - ArtMethod* res_method_; - size_t pos_; - const dex::TypeList* params_; - const size_t params_size_; -}; - template <bool kVerifierDebug> ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs( const Instruction* inst, MethodType method_type, bool is_range) { @@ -4079,8 +4026,7 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs( // has a vtable entry for the target method. Or the target is on a interface. if (method_type == METHOD_SUPER) { dex::TypeIndex class_idx = dex_file_->GetMethodId(method_idx).class_idx_; - const RegType& reference_type = - reg_types_.FromDescriptor(class_loader_, dex_file_->GetTypeDescriptor(class_idx)); + const RegType& reference_type = reg_types_.FromTypeIndex(class_idx); if (reference_type.IsUnresolvedTypes()) { // We cannot differentiate on whether this is a class change error or just // a missing method. This will be handled at runtime. @@ -4126,19 +4072,17 @@ ArtMethod* MethodVerifier<kVerifierDebug>::VerifyInvocationArgs( } } - ArtMethod* verified_method; + dex::ProtoIndex proto_idx; if (UNLIKELY(method_type == METHOD_POLYMORPHIC)) { // Process the signature of the calling site that is invoking the method handle. - dex::ProtoIndex proto_idx(inst->VRegH()); - DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx)); - verified_method = - VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); + proto_idx = dex::ProtoIndex(inst->VRegH()); } else { // Process the target method's signature. - MethodParamListDescriptorIterator it(res_method); - verified_method = - VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); + proto_idx = dex_file_->GetMethodId(method_idx).proto_idx_; } + DexFileParameterIterator it(*dex_file_, dex_file_->GetProtoId(proto_idx)); + ArtMethod* verified_method = + VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, res_method); if (verified_method != nullptr && !verified_method->GetDeclaringClass()->IsInterface()) { CheckForFinalAbstractClass(res_method->GetDeclaringClass()); @@ -4261,7 +4205,7 @@ void MethodVerifier<kVerifierDebug>::VerifyNewArray(const Instruction* inst, DCHECK(!res_type.IsUnresolvedMergedReference()); // Verify each register. If "arg_count" is bad, VerifyRegisterType() will run off the end of // the list and fail. It's legal, if silly, for arg_count to be zero. - const RegType& expected_type = reg_types_.GetComponentType(res_type, class_loader_); + const RegType& expected_type = reg_types_.GetComponentType(res_type); uint32_t arg_count = (is_range) ? inst->VRegA_3rc() : inst->VRegA_35c(); uint32_t arg[5]; if (!is_range) { @@ -4324,7 +4268,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAGet(const Instruction* inst, } } else { /* verify the class */ - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type); if (!component_type.IsReferenceTypes() && !is_primitive) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "primitive array type " << array_type << " source for aget-object"; @@ -4446,7 +4390,7 @@ void MethodVerifier<kVerifierDebug>::VerifyAPut(const Instruction* inst, << " because of missing class"; } } else { - const RegType& component_type = reg_types_.GetComponentType(array_type, class_loader_); + const RegType& component_type = reg_types_.GetComponentType(array_type); const uint32_t vregA = inst->VRegA_23x(); if (is_primitive) { VerifyPrimitivePut(component_type, insn_type, vregA); @@ -4546,8 +4490,7 @@ ArtField* MethodVerifier<kVerifierDebug>::GetInstanceField(const RegType& obj_ty // Fall through into a few last soft failure checks below. } else { ObjPtr<mirror::Class> klass = field->GetDeclaringClass(); - std::string temp; - const RegType& field_klass = reg_types_.FromClass(klass->GetDescriptor(&temp), klass); + const RegType& field_klass = reg_types_.FromClass(klass); if (obj_type.IsUninitializedTypes()) { // Field accesses through uninitialized references are only allowable for constructors where // the field is declared in this class. @@ -4667,12 +4610,10 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst // Note: see b/34966607. This and above may be changed in the future. if (kAccType == FieldAccessType::kAccPut) { const dex::FieldId& field_id = dex_file_->GetFieldId(field_idx); - const char* field_class_descriptor = dex_file_->GetFieldDeclaringClassDescriptor(field_id); - const RegType* field_class_type = - ®_types_.FromDescriptor(class_loader_, field_class_descriptor); - if (!field_class_type->Equals(GetDeclaringClass())) { + const RegType& field_class_type = reg_types_.FromTypeIndex(field_id.class_idx_); + if (!field_class_type.Equals(GetDeclaringClass())) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "could not check field put for final field modify of " - << field_class_descriptor + << dex_file_->GetFieldDeclaringClassDescriptor(field_id) << "." << dex_file_->GetFieldName(field_id) << " from other class " @@ -4681,8 +4622,7 @@ void MethodVerifier<kVerifierDebug>::VerifyISFieldAccess(const Instruction* inst } } const dex::FieldId& field_id = dex_file_->GetFieldId(field_idx); - const char* type_descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - const RegType& field_type = reg_types_.FromDescriptor(class_loader_, type_descriptor); + const RegType& field_type = reg_types_.FromTypeIndex(field_id.type_idx_); const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet, "Unexpected third access type"); @@ -4798,9 +4738,7 @@ const RegType& MethodVerifier<kVerifierDebug>::GetMethodReturnType() { if (return_type_ == nullptr) { const dex::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_); const dex::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); - dex::TypeIndex return_type_idx = proto_id.return_type_idx_; - const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return_type_ = ®_types_.FromDescriptor(class_loader_, descriptor); + return_type_ = ®_types_.FromTypeIndex(proto_id.return_type_idx_); } return *return_type_; } @@ -4877,7 +4815,6 @@ MethodVerifier::MethodVerifier(Thread* self, ArenaPool* arena_pool, RegTypeCache* reg_types, VerifierDeps* verifier_deps, - const DexFile* dex_file, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t dex_method_idx, @@ -4889,9 +4826,9 @@ MethodVerifier::MethodVerifier(Thread* self, reg_table_(allocator_), work_insn_idx_(dex::kDexNoIndex), dex_method_idx_(dex_method_idx), - dex_file_(dex_file), + dex_file_(reg_types->GetDexFile()), class_def_(class_def), - code_item_accessor_(*dex_file, code_item), + code_item_accessor_(*dex_file_, code_item), flags_{ .have_pending_hard_failure_ = false, .have_pending_runtime_throw_failure_ = false }, const_flags_{ .aot_mode_ = aot_mode, .can_load_classes_ = reg_types->CanLoadClasses() }, encountered_failure_types_(0), @@ -4908,9 +4845,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, RegTypeCache* reg_types, VerifierDeps* verifier_deps, uint32_t method_idx, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t method_access_flags, @@ -4924,9 +4859,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, reg_types, verifier_deps, method_idx, - dex_file, dex_cache, - class_loader, class_def, code_item, method_access_flags, @@ -4940,9 +4873,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, reg_types, verifier_deps, method_idx, - dex_file, dex_cache, - class_loader, class_def, code_item, method_access_flags, @@ -4976,9 +4907,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, RegTypeCache* reg_types, VerifierDeps* verifier_deps, uint32_t method_idx, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t method_access_flags, @@ -4993,12 +4922,10 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, arena_pool, reg_types, verifier_deps, - dex_file, code_item, method_idx, aot_mode, dex_cache, - class_loader, class_def, method_access_flags, /* verify_to_dump= */ false, @@ -5010,8 +4937,9 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, if (verifier.failures_.size() != 0) { if (VLOG_IS_ON(verifier)) { - verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in " - << dex_file->PrettyMethod(method_idx) << "\n"); + verifier.DumpFailures(VLOG_STREAM(verifier) + << "Soft verification failures in " + << reg_types->GetDexFile()->PrettyMethod(method_idx) << "\n"); } if (kVerifierDebug) { LOG(INFO) << verifier.info_messages_.str(); @@ -5050,9 +4978,9 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, LOG(FATAL) << "Unsupported log-level " << static_cast<uint32_t>(log_level); UNREACHABLE(); } - verifier.DumpFailures(LOG_STREAM(severity) << "Verification error in " - << dex_file->PrettyMethod(method_idx) - << "\n"); + verifier.DumpFailures(LOG_STREAM(severity) + << "Verification error in " + << reg_types->GetDexFile()->PrettyMethod(method_idx) << "\n"); } if (hard_failure_msg != nullptr) { CHECK(!verifier.failure_messages_.empty()); @@ -5080,7 +5008,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self, if (duration_ns > MsToNs(Runtime::Current()->GetVerifierLoggingThresholdMs())) { double bytecodes_per_second = verifier.code_item_accessor_.InsnsSizeInCodeUnits() / (duration_ns * 1e-9); - LOG(WARNING) << "Verification of " << dex_file->PrettyMethod(method_idx) + LOG(WARNING) << "Verification of " << reg_types->GetDexFile()->PrettyMethod(method_idx) << " took " << PrettyDuration(duration_ns) << (impl::IsLargeMethod(verifier.CodeItem()) ? " (large method)" : "") << " (" << StringPrintf("%.2f", bytecodes_per_second) << " bytecodes/s)" @@ -5097,7 +5025,6 @@ MethodVerifier* MethodVerifier::CalculateVerificationInfo( RegTypeCache* reg_types, ArtMethod* method, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, uint32_t dex_pc) { Runtime* runtime = Runtime::Current(); std::unique_ptr<impl::MethodVerifier<false>> verifier( @@ -5105,12 +5032,10 @@ MethodVerifier* MethodVerifier::CalculateVerificationInfo( runtime->GetArenaPool(), reg_types, /* verifier_deps= */ nullptr, - method->GetDexFile(), method->GetCodeItem(), method->GetDexMethodIndex(), runtime->IsAotCompiler(), dex_cache, - class_loader, *method->GetDeclaringClass()->GetClassDef(), method->GetAccessFlags(), /* verify_to_dump= */ false, @@ -5145,18 +5070,16 @@ void MethodVerifier::VerifyMethodAndDump(Thread* self, Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); ArenaPool* arena_pool = runtime->GetArenaPool(); - RegTypeCache reg_types(self, class_linker, arena_pool); + RegTypeCache reg_types(self, class_linker, arena_pool, class_loader, dex_file); impl::MethodVerifier<false> verifier( self, arena_pool, ®_types, /* verifier_deps= */ nullptr, - dex_file, code_item, dex_method_idx, runtime->IsAotCompiler(), dex_cache, - class_loader, class_def, method_access_flags, /* verify_to_dump= */ true, @@ -5183,18 +5106,21 @@ void MethodVerifier::FindLocksAtDexPc( Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); ArenaPool* arena_pool = runtime->GetArenaPool(); - RegTypeCache reg_types( - self, class_linker, arena_pool, /* can_load_classes= */ false, /* can_suspend= */ false); + RegTypeCache reg_types(self, + class_linker, + arena_pool, + class_loader, + dex_cache->GetDexFile(), + /* can_load_classes= */ false, + /* can_suspend= */ false); impl::MethodVerifier<false> verifier(self, arena_pool, ®_types, /* verifier_deps= */ nullptr, - m->GetDexFile(), m->GetCodeItem(), m->GetDexMethodIndex(), runtime->IsAotCompiler(), dex_cache, - class_loader, m->GetClassDef(), m->GetAccessFlags(), /* verify_to_dump= */ false, @@ -5207,9 +5133,7 @@ void MethodVerifier::FindLocksAtDexPc( MethodVerifier* MethodVerifier::CreateVerifier(Thread* self, RegTypeCache* reg_types, VerifierDeps* verifier_deps, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t method_idx, @@ -5220,12 +5144,10 @@ MethodVerifier* MethodVerifier::CreateVerifier(Thread* self, Runtime::Current()->GetArenaPool(), reg_types, verifier_deps, - dex_file, code_item, method_idx, Runtime::Current()->IsAotCompiler(), dex_cache, - class_loader, class_def, access_flags, verify_to_dump, diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index e12a2f8fb1..711b455c2d 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -116,7 +116,6 @@ class MethodVerifier { RegTypeCache* reg_types, ArtMethod* method, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_); @@ -196,7 +195,6 @@ class MethodVerifier { ArenaPool* arena_pool, RegTypeCache* reg_types, VerifierDeps* verifier_deps, - const DexFile* dex_file, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t dex_method_idx, @@ -229,9 +227,7 @@ class MethodVerifier { RegTypeCache* reg_types, VerifierDeps* verifier_deps, uint32_t method_idx, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def_idx, const dex::CodeItem* code_item, uint32_t method_access_flags, @@ -247,9 +243,7 @@ class MethodVerifier { RegTypeCache* reg_types, VerifierDeps* verifier_deps, uint32_t method_idx, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def_idx, const dex::CodeItem* code_item, uint32_t method_access_flags, @@ -267,9 +261,7 @@ class MethodVerifier { static MethodVerifier* CreateVerifier(Thread* self, RegTypeCache* reg_types, VerifierDeps* verifier_deps, - const DexFile* dex_file, Handle<mirror::DexCache> dex_cache, - Handle<mirror::ClassLoader> class_loader, const dex::ClassDef& class_def, const dex::CodeItem* code_item, uint32_t method_idx, diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc index 62ba55be2c..23f1ff0cab 100644 --- a/runtime/verifier/reg_type.cc +++ b/runtime/verifier/reg_type.cc @@ -312,8 +312,7 @@ const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedTypes()) { ObjPtr<mirror::Class> super_klass = GetClass()->GetSuperClass(); if (super_klass != nullptr) { - std::string temp; - return cache->FromClass(super_klass->GetDescriptor(&temp), super_klass); + return cache->FromClass(super_klass); } else { return cache->Zero(); } @@ -705,9 +704,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, } else if (incoming_type.GetClass() == join_class) { return incoming_type; } else { - std::string temp; - const char* descriptor = join_class->GetDescriptor(&temp); - return reg_types->FromClass(descriptor, join_class); + return reg_types->FromClass(join_class); } } } else { diff --git a/runtime/verifier/reg_type_cache-inl.h b/runtime/verifier/reg_type_cache-inl.h index c3b0850cde..6cde1ac048 100644 --- a/runtime/verifier/reg_type_cache-inl.h +++ b/runtime/verifier/reg_type_cache-inl.h @@ -19,6 +19,7 @@ #include "base/bit_vector-inl.h" #include "class_root-inl.h" +#include "dex/dex_file.h" #include "mirror/class-inl.h" #include "mirror/method_handle_impl.h" #include "mirror/method_type.h" @@ -46,6 +47,14 @@ inline const ConstantType& RegTypeCache::FromCat1Const(int32_t value, bool preci return FromCat1NonSmallConstant(value, precise); } +inline const RegType& RegTypeCache::FromTypeIndex(dex::TypeIndex type_index) { + DCHECK_LT(type_index.index_, dex_file_->NumTypeIds()); + if (entries_for_type_index_[type_index.index_] != nullptr) { + return *entries_for_type_index_[type_index.index_]; + } + return FromTypeIndexUncached(type_index); +} + inline const BooleanType& RegTypeCache::Boolean() { return *down_cast<const BooleanType*>(entries_[kBooleanCacheId]); } @@ -124,39 +133,43 @@ inline const ImpreciseConstType& RegTypeCache::PosShortConstant() { } inline const ReferenceType& RegTypeCache::JavaLangClass() { - const RegType* result = &FromClass("Ljava/lang/Class;", GetClassRoot<mirror::Class>()); + const RegType* result = &FromClass(GetClassRoot<mirror::Class>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/Class;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } inline const ReferenceType& RegTypeCache::JavaLangString() { - const RegType* result = &FromClass("Ljava/lang/String;", GetClassRoot<mirror::String>()); + const RegType* result = &FromClass(GetClassRoot<mirror::String>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/String;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } inline const ReferenceType& RegTypeCache::JavaLangInvokeMethodHandle() { - const RegType* result = &FromClass("Ljava/lang/invoke/MethodHandle;", - GetClassRoot<mirror::MethodHandle>()); + const RegType* result = &FromClass(GetClassRoot<mirror::MethodHandle>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/invoke/MethodHandle;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } inline const ReferenceType& RegTypeCache::JavaLangInvokeMethodType() { - const RegType* result = &FromClass("Ljava/lang/invoke/MethodType;", - GetClassRoot<mirror::MethodType>()); + const RegType* result = &FromClass(GetClassRoot<mirror::MethodType>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/invoke/MethodType;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } inline const ReferenceType& RegTypeCache::JavaLangThrowable() { - const RegType* result = &FromClass("Ljava/lang/Throwable;", GetClassRoot<mirror::Throwable>()); + const RegType* result = &FromClass(GetClassRoot<mirror::Throwable>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/Throwable;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } inline const ReferenceType& RegTypeCache::JavaLangObject() { - const RegType* result = &FromClass("Ljava/lang/Object;", GetClassRoot<mirror::Object>()); + const RegType* result = &FromClass(GetClassRoot<mirror::Object>()); + DCHECK(result->GetClass()->DescriptorEquals("Ljava/lang/Object;")); DCHECK(result->IsReference()); return *down_cast<const ReferenceType*>(result); } diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index c704209c60..64df3116eb 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -69,8 +69,7 @@ void RegTypeCache::FillPrimitiveAndSmallConstantTypes() { new (&allocator_) NullType(null_handle_, "", kNullCacheId); } -const RegType& RegTypeCache::FromDescriptor(Handle<mirror::ClassLoader> loader, - const char* descriptor) { +const RegType& RegTypeCache::FromDescriptor(const char* descriptor) { if (descriptor[1] == '\0') { switch (descriptor[0]) { case 'Z': @@ -94,12 +93,19 @@ const RegType& RegTypeCache::FromDescriptor(Handle<mirror::ClassLoader> loader, return Conflict(); } } else if (descriptor[0] == 'L' || descriptor[0] == '[') { - return From(loader, descriptor); + return From(descriptor); } else { return Conflict(); } } +const RegType& RegTypeCache::FromTypeIndexUncached(dex::TypeIndex type_index) { + DCHECK(entries_for_type_index_[type_index.index_] == nullptr); + const char* descriptor = dex_file_->GetTypeDescriptor(type_index); + const RegType& reg_type = FromDescriptor(descriptor); + entries_for_type_index_[type_index.index_] = ®_type; + return reg_type; +} const RegType& RegTypeCache::RegTypeFromPrimitiveType(Primitive::Type prim_type) const { switch (prim_type) { @@ -134,16 +140,15 @@ bool RegTypeCache::MatchDescriptor(size_t idx, const std::string_view& descripto return true; } -ObjPtr<mirror::Class> RegTypeCache::ResolveClass(const char* descriptor, - Handle<mirror::ClassLoader> loader) { +ObjPtr<mirror::Class> RegTypeCache::ResolveClass(const char* descriptor) { // Class was not found, must create new type. // Try resolving class Thread* self = Thread::Current(); ObjPtr<mirror::Class> klass = nullptr; if (can_load_classes_) { - klass = class_linker_->FindClass(self, descriptor, loader); + klass = class_linker_->FindClass(self, descriptor, class_loader_); } else { - klass = class_linker_->LookupClass(self, descriptor, loader.Get()); + klass = class_linker_->LookupClass(self, descriptor, class_loader_.Get()); if (klass != nullptr && !klass->IsResolved()) { // We found the class but without it being loaded its not safe for use. klass = nullptr; @@ -158,7 +163,7 @@ std::string_view RegTypeCache::AddString(const std::string_view& str) { return std::string_view(ptr, str.length()); } -const RegType& RegTypeCache::From(Handle<mirror::ClassLoader> loader, const char* descriptor) { +const RegType& RegTypeCache::From(const char* descriptor) { std::string_view sv_descriptor(descriptor); // Try looking up the class in the cache first. We use a std::string_view to avoid // repeated strlen operations on the descriptor. @@ -169,7 +174,9 @@ const RegType& RegTypeCache::From(Handle<mirror::ClassLoader> loader, const char } // Class not found in the cache, will create a new type for that. // Try resolving class. - ObjPtr<mirror::Class> klass = ResolveClass(descriptor, loader); + ObjPtr<mirror::Class> klass = ResolveClass(descriptor); + // TODO: Avoid copying the `descriptor` with `AddString()` below if the `descriptor` + // comes from the dex file, for example through `FromTypeIndex()`. if (klass != nullptr) { DCHECK(!klass->IsPrimitive()); RegType* entry = new (&allocator_) ReferenceType( @@ -202,42 +209,55 @@ const RegType& RegTypeCache::MakeUnresolvedReference() { null_handle_, AddString("a"), entries_.size())); } -const RegType* RegTypeCache::FindClass(ObjPtr<mirror::Class> klass) const { +const RegType& RegTypeCache::FromClass(ObjPtr<mirror::Class> klass) { DCHECK(klass != nullptr); + DCHECK(!klass->IsProxyClass()); + if (klass->IsPrimitive()) { - return &RegTypeFromPrimitiveType(klass->GetPrimitiveType()); + return RegTypeFromPrimitiveType(klass->GetPrimitiveType()); + } + if (!klass->IsArrayClass() && &klass->GetDexFile() == dex_file_) { + // Go through the `TypeIndex`-based cache. If the entry is not there yet, we shall + // fill it in now to make sure it's available for subsequent lookups. + std::optional<StackHandleScope<1u>> hs(std::nullopt); + if (kIsDebugBuild) { + hs.emplace(Thread::Current()); + } + Handle<mirror::Class> h_class = + kIsDebugBuild ? hs->NewHandle(klass) : Handle<mirror::Class>(); + const RegType& reg_type = FromTypeIndex(klass->GetDexTypeIndex()); + DCHECK(reg_type.HasClass()); + DCHECK(reg_type.GetClass() == h_class.Get()); + return reg_type; } for (auto& pair : klass_entries_) { - const Handle<mirror::Class> reg_klass = pair.first; - const RegType* reg_type = pair.second; - if (reg_klass.Get() == klass) { - return reg_type; + const Handle<mirror::Class> entry_klass = pair.first; + const RegType* entry_reg_type = pair.second; + if (entry_klass.Get() == klass) { + return *entry_reg_type; } } - return nullptr; -} -const RegType* RegTypeCache::InsertClass(const std::string_view& descriptor, - ObjPtr<mirror::Class> klass) { // No reference to the class was found, create new reference. - DCHECK(FindClass(klass) == nullptr); - RegType* const reg_type = - new (&allocator_) ReferenceType(handles_.NewHandle(klass), descriptor, entries_.size()); - return &AddEntry(reg_type); -} - -const RegType& RegTypeCache::FromClass(const char* descriptor, ObjPtr<mirror::Class> klass) { - DCHECK(klass != nullptr); - const RegType* reg_type = FindClass(klass); - if (reg_type == nullptr) { - reg_type = InsertClass(AddString(std::string_view(descriptor)), klass); + std::string_view descriptor; + if (klass->IsArrayClass()) { + std::string temp; + descriptor = AddString(std::string_view(klass->GetDescriptor(&temp))); + } else { + // Point `descriptor` to the string data in the dex file that defines the `klass`. + // That dex file cannot be unloaded while we hold a `Handle<>` to that `klass`. + descriptor = klass->GetDescriptorView(); } - return *reg_type; + Handle<mirror::Class> h_klass = handles_.NewHandle(klass); + const RegType* reg_type = new (&allocator_) ReferenceType(h_klass, descriptor, entries_.size()); + return AddEntry(reg_type); } RegTypeCache::RegTypeCache(Thread* self, ClassLinker* class_linker, ArenaPool* arena_pool, + Handle<mirror::ClassLoader> class_loader, + const DexFile* dex_file, bool can_load_classes, bool can_suspend) : arena_stack_(arena_pool), @@ -246,12 +266,18 @@ RegTypeCache::RegTypeCache(Thread* self, klass_entries_(allocator_.Adapter(kArenaAllocVerifier)), handles_(self), class_linker_(class_linker), + class_loader_(class_loader), + dex_file_(dex_file), + entries_for_type_index_(allocator_.AllocArray<const RegType*>(dex_file->NumTypeIds())), can_load_classes_(can_load_classes), can_suspend_(can_suspend) { DCHECK(can_suspend || !can_load_classes) << "Cannot load classes if suspension is disabled!"; if (kIsDebugBuild && can_suspend) { Thread::Current()->AssertThreadSuspensionIsAllowable(gAborting == 0); } + // TODO: Why are we using `ScopedArenaAllocator` here instead of the `ArenaAllocator` which + // guarantees zero-initialization? We could avoid this `fill_n()` with the `ArenaAllocator`. + std::fill_n(entries_for_type_index_, dex_file->NumTypeIds(), nullptr); // The klass_entries_ array does not have primitives or small constants. static constexpr size_t kNumReserveEntries = 32; klass_entries_.reserve(kNumReserveEntries); @@ -532,25 +558,24 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { return AddEntry(entry); } -const RegType& RegTypeCache::GetComponentType(const RegType& array, - Handle<mirror::ClassLoader> loader) { +const RegType& RegTypeCache::GetComponentType(const RegType& array) { if (!array.IsArrayTypes()) { return Conflict(); } else if (array.IsUnresolvedTypes()) { DCHECK(!array.IsUnresolvedMergedReference()); // Caller must make sure not to ask for this. const std::string descriptor(array.GetDescriptor()); - return FromDescriptor(loader, descriptor.c_str() + 1); + return FromDescriptor(descriptor.c_str() + 1); } else { ObjPtr<mirror::Class> klass = array.GetClass()->GetComponentType(); - std::string temp; - const char* descriptor = klass->GetDescriptor(&temp); if (klass->IsErroneous()) { // Arrays may have erroneous component types, use unresolved in that case. // We assume that the primitive classes are not erroneous, so we know it is a // reference type. - return FromDescriptor(loader, descriptor); + std::string temp; + const char* descriptor = klass->GetDescriptor(&temp); + return FromDescriptor(descriptor); } else { - return FromClass(descriptor, klass); + return FromClass(klass); } } } diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 54a6a6ae3c..737dc39c8b 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -36,7 +36,7 @@ class ClassLoader; } // namespace mirror class ClassLinker; -class ScopedArenaAllocator; +class DexFile; namespace verifier { @@ -69,9 +69,19 @@ class RegTypeCache { EXPORT RegTypeCache(Thread* self, ClassLinker* class_linker, ArenaPool* arena_pool, + Handle<mirror::ClassLoader> class_loader, + const DexFile* dex_file, bool can_load_classes = true, bool can_suspend = true); + Handle<mirror::ClassLoader> GetClassLoader() const { + return class_loader_; + } + + const DexFile* GetDexFile() const { + return dex_file_; + } + bool CanLoadClasses() const { return can_load_classes_; } @@ -81,14 +91,8 @@ class RegTypeCache { } const art::verifier::RegType& GetFromId(uint16_t id) const; - // Find a RegType, returns null if not found. - const RegType* FindClass(ObjPtr<mirror::Class> klass) const - REQUIRES_SHARED(Locks::mutator_lock_); - // Insert a new class with a specified descriptor, must not already be in the cache. - const RegType* InsertClass(const std::string_view& descriptor, ObjPtr<mirror::Class> klass) - REQUIRES_SHARED(Locks::mutator_lock_); - // Get or insert a reg type for a descriptor and klass. - const RegType& FromClass(const char* descriptor, ObjPtr<mirror::Class> klass) + // Get or insert a reg type for a klass. + const RegType& FromClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat1Const(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); @@ -96,7 +100,7 @@ class RegTypeCache { REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat2ConstHi(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& FromDescriptor(Handle<mirror::ClassLoader> loader, const char* descriptor) + const RegType& FromDescriptor(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& FromUnresolvedMerge(const RegType& left, const RegType& right, @@ -105,6 +109,8 @@ class RegTypeCache { const RegType& FromUnresolvedSuperClass(const RegType& child) REQUIRES_SHARED(Locks::mutator_lock_); + const RegType& FromTypeIndex(dex::TypeIndex type_index) REQUIRES_SHARED(Locks::mutator_lock_); + // Note: this should not be used outside of RegType::ClassJoin! const RegType& MakeUnresolvedReference() REQUIRES_SHARED(Locks::mutator_lock_); @@ -151,8 +157,7 @@ class RegTypeCache { const ImpreciseConstType& IntConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosByteConstant() REQUIRES_SHARED(Locks::mutator_lock_); const ImpreciseConstType& PosShortConstant() REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& GetComponentType(const RegType& array, Handle<mirror::ClassLoader> loader) - REQUIRES_SHARED(Locks::mutator_lock_); + const RegType& GetComponentType(const RegType& array) REQUIRES_SHARED(Locks::mutator_lock_); void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_); const RegType& RegTypeFromPrimitiveType(Primitive::Type) const; @@ -184,14 +189,17 @@ class RegTypeCache { private: void FillPrimitiveAndSmallConstantTypes() REQUIRES_SHARED(Locks::mutator_lock_); - ObjPtr<mirror::Class> ResolveClass(const char* descriptor, Handle<mirror::ClassLoader> loader) + ObjPtr<mirror::Class> ResolveClass(const char* descriptor) REQUIRES_SHARED(Locks::mutator_lock_); bool MatchDescriptor(size_t idx, const std::string_view& descriptor) REQUIRES_SHARED(Locks::mutator_lock_); const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise) REQUIRES_SHARED(Locks::mutator_lock_); - const RegType& From(Handle<mirror::ClassLoader> loader, const char* descriptor) + const RegType& From(const char* descriptor) + REQUIRES_SHARED(Locks::mutator_lock_); + + const RegType& FromTypeIndexUncached(dex::TypeIndex type_index) REQUIRES_SHARED(Locks::mutator_lock_); // Returns the pass in RegType. @@ -217,6 +225,11 @@ class RegTypeCache { ScopedNullHandle<mirror::Class> null_handle_; ClassLinker* class_linker_; + Handle<mirror::ClassLoader> class_loader_; + const DexFile* const dex_file_; + + // Fast lookup by type index. + const RegType** const entries_for_type_index_; // Whether or not we're allowed to load classes. const bool can_load_classes_; diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc index 2dc110cd70..693dff4602 100644 --- a/runtime/verifier/reg_type_test.cc +++ b/runtime/verifier/reg_type_test.cc @@ -23,6 +23,7 @@ #include "base/scoped_arena_allocator.h" #include "common_runtime_test.h" #include "compiler_callbacks.h" +#include "dex/test_dex_file_builder.h" #include "reg_type-inl.h" #include "reg_type_cache-inl.h" #include "scoped_thread_state_change-inl.h" @@ -36,13 +37,33 @@ class RegTypeTest : public CommonRuntimeTest { RegTypeTest() { use_boot_image_ = true; // Make the Runtime creation cheaper. } + + void SetUp() override { + CommonRuntimeTest::SetUp(); + + // Build a fake `DexFile` with some descriptors. + static const char* const descriptors[] = { + // References. + "Ljava/lang/Object;", "Ljava/lang/String;", "LNonExistent;", + // Primitives and `void`. + "Z", "B", "C", "S", "I", "J", "F", "D", "V" + }; + TestDexFileBuilder builder; + for (const char* descriptor : descriptors) { + builder.AddType(descriptor); + } + dex_file_ = builder.Build("arbitrary-location"); + } + + std::unique_ptr<const DexFile> dex_file_; }; TEST_F(RegTypeTest, ConstLoHi) { // Tests creating primitive types types. ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& ref_type_const_0 = cache.FromCat1Const(10, true); const RegType& ref_type_const_1 = cache.FromCat1Const(10, true); const RegType& ref_type_const_2 = cache.FromCat1Const(30, true); @@ -65,7 +86,8 @@ TEST_F(RegTypeTest, ConstLoHi) { TEST_F(RegTypeTest, Pairs) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); int64_t val = static_cast<int32_t>(1234); const RegType& precise_lo = cache.FromCat2ConstLo(static_cast<int32_t>(val), true); const RegType& precise_hi = cache.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true); @@ -90,7 +112,8 @@ TEST_F(RegTypeTest, Pairs) { TEST_F(RegTypeTest, Primitives) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& bool_reg_type = cache.Boolean(); EXPECT_FALSE(bool_reg_type.IsUndefined()); @@ -365,12 +388,12 @@ TEST_F(RegTypeReferenceTest, UnresolvedType) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); - const RegType& ref_type_0 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); + const RegType& ref_type_0 = cache.FromDescriptor("Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); EXPECT_TRUE(ref_type_0.IsNonZeroReferenceTypes()); - const RegType& ref_type_1 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + const RegType& ref_type_1 = cache.FromDescriptor("Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.Equals(ref_type_1)); const RegType& unresolved_super_class = cache.FromUnresolvedSuperClass(ref_type_0); @@ -383,10 +406,10 @@ TEST_F(RegTypeReferenceTest, UnresolvedUnintializedType) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); - const RegType& ref_type_0 = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); + const RegType& ref_type_0 = cache.FromDescriptor("Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); - const RegType& ref_type = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + const RegType& ref_type = cache.FromDescriptor("Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.Equals(ref_type)); // Create an uninitialized type of this unresolved type const RegType& unresolved_unintialised = cache.Uninitialized(ref_type, 1101ull); @@ -407,10 +430,9 @@ TEST_F(RegTypeReferenceTest, Dump) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); - const RegType& unresolved_ref = cache.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); - const RegType& unresolved_ref_another = - cache.FromDescriptor(loader, "Ljava/lang/DoesNotExistEither;"); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); + const RegType& unresolved_ref = cache.FromDescriptor("Ljava/lang/DoesNotExist;"); + const RegType& unresolved_ref_another = cache.FromDescriptor("Ljava/lang/DoesNotExistEither;"); const RegType& resolved_ref = cache.JavaLangString(); const RegType& resolved_unintialiesd = cache.Uninitialized(resolved_ref, 10); const RegType& unresolved_unintialized = cache.Uninitialized(unresolved_ref, 12); @@ -436,10 +458,10 @@ TEST_F(RegTypeReferenceTest, JavalangString) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& ref_type = cache.JavaLangString(); const RegType& ref_type_2 = cache.JavaLangString(); - const RegType& ref_type_3 = cache.FromDescriptor(loader, "Ljava/lang/String;"); + const RegType& ref_type_3 = cache.FromDescriptor("Ljava/lang/String;"); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_2.Equals(ref_type_3)); @@ -458,29 +480,30 @@ TEST_F(RegTypeReferenceTest, JavalangObject) { ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& ref_type = cache.JavaLangObject(); const RegType& ref_type_2 = cache.JavaLangObject(); - const RegType& ref_type_3 = cache.FromDescriptor(loader, "Ljava/lang/Object;"); + const RegType& ref_type_3 = cache.FromDescriptor("Ljava/lang/Object;"); EXPECT_TRUE(ref_type.Equals(ref_type_2)); EXPECT_TRUE(ref_type_3.Equals(ref_type_2)); EXPECT_EQ(ref_type.GetId(), ref_type_3.GetId()); } + TEST_F(RegTypeReferenceTest, Merging) { // Tests merging logic // String and object , LUB is object. ScopedObjectAccess soa(Thread::Current()); ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache_new(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + RegTypeCache cache_new(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& string = cache_new.JavaLangString(); const RegType& Object = cache_new.JavaLangObject(); EXPECT_TRUE(string.Merge(Object, &cache_new, /* verifier= */ nullptr).IsJavaLangObject()); // Merge two unresolved types. - const RegType& ref_type_0 = cache_new.FromDescriptor(loader, "Ljava/lang/DoesNotExist;"); + const RegType& ref_type_0 = cache_new.FromDescriptor("Ljava/lang/DoesNotExist;"); EXPECT_TRUE(ref_type_0.IsUnresolvedReference()); - const RegType& ref_type_1 = cache_new.FromDescriptor(loader, "Ljava/lang/DoesNotExistToo;"); + const RegType& ref_type_1 = cache_new.FromDescriptor("Ljava/lang/DoesNotExistToo;"); EXPECT_FALSE(ref_type_0.Equals(ref_type_1)); const RegType& merged = ref_type_1.Merge(ref_type_0, &cache_new, /* verifier= */ nullptr); @@ -497,7 +520,8 @@ TEST_F(RegTypeTest, MergingFloat) { // Testing merging logic with float and float constants. ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache_new(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); constexpr int32_t kTestConstantValue = 10; const RegType& float_type = cache_new.Float(); @@ -529,7 +553,8 @@ TEST_F(RegTypeTest, MergingLong) { // Testing merging logic with long and long constants. ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache_new(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); constexpr int32_t kTestConstantValue = 10; const RegType& long_lo_type = cache_new.LongLo(); @@ -588,7 +613,8 @@ TEST_F(RegTypeTest, MergingDouble) { // Testing merging logic with double and double constants. ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache_new(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); constexpr int32_t kTestConstantValue = 10; const RegType& double_lo_type = cache_new.DoubleLo(); @@ -702,7 +728,7 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { ScopedDisableMovingGC no_gc(soa.Self()); ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& conflict = cache.Conflict(); const RegType& zero = cache.Zero(); @@ -710,12 +736,12 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& int_type = cache.Integer(); const RegType& obj = cache.JavaLangObject(); - const RegType& obj_arr = cache.FromDescriptor(loader, "[Ljava/lang/Object;"); + const RegType& obj_arr = cache.FromDescriptor("[Ljava/lang/Object;"); ASSERT_FALSE(obj_arr.IsUnresolvedReference()); - const RegType& unresolved_a = cache.FromDescriptor(loader, "Ldoes/not/resolve/A;"); + const RegType& unresolved_a = cache.FromDescriptor("Ldoes/not/resolve/A;"); ASSERT_TRUE(unresolved_a.IsUnresolvedReference()); - const RegType& unresolved_b = cache.FromDescriptor(loader, "Ldoes/not/resolve/B;"); + const RegType& unresolved_b = cache.FromDescriptor("Ldoes/not/resolve/B;"); ASSERT_TRUE(unresolved_b.IsUnresolvedReference()); const RegType& unresolved_ab = cache.FromUnresolvedMerge(unresolved_a, unresolved_b, nullptr); ASSERT_TRUE(unresolved_ab.IsUnresolvedMergedReference()); @@ -728,25 +754,25 @@ TEST_F(RegTypeTest, MergeSemiLatticeRef) { const RegType& uninit_unres_a_0 = cache.Uninitialized(unresolved_a, 0); const RegType& uninit_unres_b_0 = cache.Uninitialized(unresolved_b, 0); - const RegType& number = cache.FromDescriptor(loader, "Ljava/lang/Number;"); + const RegType& number = cache.FromDescriptor("Ljava/lang/Number;"); ASSERT_FALSE(number.IsUnresolvedReference()); - const RegType& integer = cache.FromDescriptor(loader, "Ljava/lang/Integer;"); + const RegType& integer = cache.FromDescriptor("Ljava/lang/Integer;"); ASSERT_FALSE(integer.IsUnresolvedReference()); const RegType& uninit_number_0 = cache.Uninitialized(number, 0u); const RegType& uninit_integer_0 = cache.Uninitialized(integer, 0u); - const RegType& number_arr = cache.FromDescriptor(loader, "[Ljava/lang/Number;"); + const RegType& number_arr = cache.FromDescriptor("[Ljava/lang/Number;"); ASSERT_FALSE(number_arr.IsUnresolvedReference()); - const RegType& integer_arr = cache.FromDescriptor(loader, "[Ljava/lang/Integer;"); + const RegType& integer_arr = cache.FromDescriptor("[Ljava/lang/Integer;"); ASSERT_FALSE(integer_arr.IsUnresolvedReference()); - const RegType& number_arr_arr = cache.FromDescriptor(loader, "[[Ljava/lang/Number;"); + const RegType& number_arr_arr = cache.FromDescriptor("[[Ljava/lang/Number;"); ASSERT_FALSE(number_arr_arr.IsUnresolvedReference()); - const RegType& char_arr = cache.FromDescriptor(loader, "[C"); + const RegType& char_arr = cache.FromDescriptor("[C"); ASSERT_FALSE(char_arr.IsUnresolvedReference()); - const RegType& byte_arr = cache.FromDescriptor(loader, "[B"); + const RegType& byte_arr = cache.FromDescriptor("[B"); ASSERT_FALSE(byte_arr.IsUnresolvedReference()); const RegType& unresolved_a_num = cache.FromUnresolvedMerge(unresolved_a, number, nullptr); @@ -1025,7 +1051,8 @@ TEST_F(RegTypeTest, ConstPrecision) { // Tests creating primitive types types. ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); ScopedObjectAccess soa(Thread::Current()); - RegTypeCache cache_new(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache_new(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); const RegType& imprecise_const = cache_new.FromCat1Const(10, false); const RegType& precise_const = cache_new.FromCat1Const(10, true); @@ -1064,20 +1091,19 @@ TEST_F(RegTypeOOMTest, ClassJoinOOM) { constexpr const char* kNumberArrayFive = "[[[[[Ljava/lang/Number;"; ScopedNullHandle<mirror::ClassLoader> loader; - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); - const RegType& int_array_array = cache.FromDescriptor(loader, kIntArrayFive); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); + const RegType& int_array_array = cache.FromDescriptor(kIntArrayFive); ASSERT_TRUE(int_array_array.HasClass()); - const RegType& float_array_array = cache.FromDescriptor(loader, kFloatArrayFive); + const RegType& float_array_array = cache.FromDescriptor(kFloatArrayFive); ASSERT_TRUE(float_array_array.HasClass()); // Check assumptions: the joined classes don't exist, yet. - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFour, nullptr) == nullptr); - ASSERT_TRUE(class_linker->LookupClass(soa.Self(), kNumberArrayFive, nullptr) == nullptr); + ASSERT_TRUE(class_linker_->LookupClass(soa.Self(), kNumberArrayFour, nullptr) == nullptr); + ASSERT_TRUE(class_linker_->LookupClass(soa.Self(), kNumberArrayFive, nullptr) == nullptr); // Fill the heap. VariableSizedHandleScope hs(soa.Self()); - FillHeap(soa.Self(), class_linker, &hs); + FillHeap(soa.Self(), class_linker_, &hs); const RegType& join_type = int_array_array.Merge(float_array_array, &cache, nullptr); ASSERT_TRUE(join_type.IsUnresolvedReference()); @@ -1100,12 +1126,18 @@ class RegTypeClassJoinTest : public RegTypeTest { class_linker_->FindClass(soa.Self(), in2, class_loader))); ASSERT_TRUE(c1 != nullptr); ASSERT_TRUE(c2 != nullptr); + const DexFile* dex_file = &c1->GetDexFile(); + ASSERT_EQ(dex_file, &c2->GetDexFile()); ScopedDisableMovingGC no_gc(soa.Self()); - RegTypeCache cache(soa.Self(), Runtime::Current()->GetClassLinker(), arena_pool); - const RegType& c1_reg_type = *cache.InsertClass(in1, c1.Get()); - const RegType& c2_reg_type = *cache.InsertClass(in2, c2.Get()); + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, class_loader, dex_file); + const RegType& c1_reg_type = cache.FromClass(c1.Get()); + ASSERT_TRUE(c1_reg_type.HasClass()); + ASSERT_TRUE(c1_reg_type.GetClass() == c1.Get()); + const RegType& c2_reg_type = cache.FromClass(c2.Get()); + ASSERT_TRUE(c2_reg_type.HasClass()); + ASSERT_TRUE(c2_reg_type.GetClass() == c2.Get()); const RegType& join_type = c1_reg_type.Merge(c2_reg_type, &cache, nullptr); EXPECT_TRUE(join_type.HasClass()); @@ -1127,5 +1159,28 @@ TEST_F(RegTypeClassJoinTest, ClassJoinClassClass) { TestClassJoin("LInterfaces$A;", "LInterfaces$B;", "Ljava/lang/Object;"); } +TEST_F(RegTypeClassJoinTest, LookupByTypeIndex) { + ArenaPool* arena_pool = Runtime::Current()->GetArenaPool(); + ScopedObjectAccess soa(Thread::Current()); + ScopedNullHandle<mirror::ClassLoader> loader; + RegTypeCache cache(soa.Self(), class_linker_, arena_pool, loader, dex_file_.get()); + + auto get_type_index = [&](std::string_view descriptor) { + const dex::TypeId* type_id = dex_file_->FindTypeId(descriptor); + CHECK(type_id != nullptr); + return dex_file_->GetIndexForTypeId(*type_id); + }; + + ASSERT_EQ(&cache.Boolean(), &cache.FromTypeIndex(get_type_index("Z"))); + ASSERT_EQ(&cache.Byte(), &cache.FromTypeIndex(get_type_index("B"))); + ASSERT_EQ(&cache.Char(), &cache.FromTypeIndex(get_type_index("C"))); + ASSERT_EQ(&cache.Short(), &cache.FromTypeIndex(get_type_index("S"))); + ASSERT_EQ(&cache.Integer(), &cache.FromTypeIndex(get_type_index("I"))); + ASSERT_EQ(&cache.LongLo(), &cache.FromTypeIndex(get_type_index("J"))); + ASSERT_EQ(&cache.Float(), &cache.FromTypeIndex(get_type_index("F"))); + ASSERT_EQ(&cache.DoubleLo(), &cache.FromTypeIndex(get_type_index("D"))); + ASSERT_EQ(&cache.Conflict(), &cache.FromTypeIndex(get_type_index("V"))); +} + } // namespace verifier } // namespace art |