diff options
238 files changed, 13609 insertions, 2026 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index e5258087db..d376f291cd 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -92,8 +92,8 @@ ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB Erroneou ART_GTEST_class_table_test_DEX_DEPS := XandY ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes -ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested -ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics +ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex +ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB @@ -102,6 +102,7 @@ ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods ART_GTEST_oat_file_assistant_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_dexoptanalyzer_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) +ART_GTEST_image_space_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex ART_GTEST_oat_test_DEX_DEPS := Main ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY @@ -146,6 +147,11 @@ ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) \ dexoptanalyzerd +ART_GTEST_image_space_test_HOST_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) +ART_GTEST_image_space_test_TARGET_DEPS := \ + $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS) + ART_GTEST_dex2oat_test_HOST_DEPS := \ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS) ART_GTEST_dex2oat_test_TARGET_DEPS := \ @@ -627,6 +633,9 @@ ART_GTEST_oat_file_assistant_test_TARGET_DEPS := ART_GTEST_dexoptanalyzer_test_DEX_DEPS := ART_GTEST_dexoptanalyzer_test_HOST_DEPS := ART_GTEST_dexoptanalyzer_test_TARGET_DEPS := +ART_GTEST_image_space_test_DEX_DEPS := +ART_GTEST_image_space_test_HOST_DEPS := +ART_GTEST_image_space_test_TARGET_DEPS := ART_GTEST_dex2oat_test_DEX_DEPS := ART_GTEST_dex2oat_test_HOST_DEPS := ART_GTEST_dex2oat_test_TARGET_DEPS := diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7af850a263..f7bea32d4c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -960,7 +960,7 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader)); - if (cls.Get() == nullptr) { + if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { DCHECK(cls->IsErroneous() || cls->IsVerified() || cls->IsCompileTimeVerified()) @@ -1155,7 +1155,7 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { StackHandleScope<1> hs(self); Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindSystemClass(self, descriptor.c_str()))); - if (klass.Get() == nullptr) { + if (klass == nullptr) { VLOG(compiler) << "Failed to find class " << descriptor; image_classes_->erase(it++); self->ClearException(); @@ -1182,13 +1182,13 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) { Handle<mirror::DexCache> dex_cache(hs2.NewHandle(class_linker->RegisterDexFile(*dex_file, nullptr))); Handle<mirror::Class> klass(hs2.NewHandle( - (dex_cache.Get() != nullptr) + (dex_cache != nullptr) ? class_linker->ResolveType(*dex_file, exception_type_idx, dex_cache, ScopedNullHandle<mirror::ClassLoader>()) : nullptr)); - if (klass.Get() == nullptr) { + if (klass == nullptr) { const DexFile::TypeId& type_id = dex_file->GetTypeId(exception_type_idx); const char* descriptor = dex_file->GetTypeDescriptor(type_id); LOG(FATAL) << "Failed to resolve class " << descriptor; @@ -1877,7 +1877,7 @@ class ResolveTypeVisitor : public CompilationVisitor { Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->RegisterDexFile( dex_file, class_loader.Get()))); - ObjPtr<mirror::Class> klass = (dex_cache.Get() != nullptr) + ObjPtr<mirror::Class> klass = (dex_cache != nullptr) ? class_linker->ResolveType(dex_file, dex::TypeIndex(type_idx), dex_cache, class_loader) : nullptr; @@ -1978,7 +1978,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>( class_linker->FindClass(self, descriptor, class_loader))); - if (cls.Get() != nullptr) { + if (cls != nullptr) { // Check that the class is resolved with the current dex file. We might get // a boot image class, or a class in a different dex file for multidex, and // we should not update the status in that case. @@ -2126,7 +2126,7 @@ class VerifyClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); verifier::MethodVerifier::FailureKind failure_kind; - if (klass.Get() == nullptr) { + if (klass == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); @@ -2228,7 +2228,7 @@ class SetVerifiedClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); // Class might have failed resolution. Then don't set it to verified. - if (klass.Get() != nullptr) { + if (klass != nullptr) { // Only do this if the class is resolved. If even resolution fails, quickening will go very, // very wrong. if (klass->IsResolved() && !klass->IsErroneousResolved()) { @@ -2290,7 +2290,7 @@ class InitializeClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(manager_->GetClassLinker()->FindClass(soa.Self(), descriptor, class_loader))); - if (klass.Get() != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) { + if (klass != nullptr && !SkipClass(jclass_loader, dex_file, klass.Get())) { // Only try to initialize classes that were successfully verified. if (klass->IsVerified()) { // Attempt to initialize the class but bail if we either need to initialize the super-class @@ -2540,7 +2540,7 @@ class CompileClassVisitor : public CompilationVisitor { Handle<mirror::Class> klass( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); Handle<mirror::DexCache> dex_cache; - if (klass.Get() == nullptr) { + if (klass == nullptr) { soa.Self()->AssertPendingException(); soa.Self()->ClearException(); dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index c72edb18a3..d2dd30d8e6 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1078,7 +1078,7 @@ ObjectArray<Object>* ImageWriter::CreateImageRoots(size_t oat_index) const { } Handle<ObjectArray<Object>> dex_caches( hs.NewHandle(ObjectArray<Object>::Alloc(self, object_array_class.Get(), dex_cache_count))); - CHECK(dex_caches.Get() != nullptr) << "Failed to allocate a dex cache array."; + CHECK(dex_caches != nullptr) << "Failed to allocate a dex cache array."; { ReaderMutexLock mu(self, *Locks::dex_lock_); size_t non_image_dex_caches = 0; @@ -2254,6 +2254,14 @@ void ImageWriter::FixupDexCache(mirror::DexCache* orig_dex_cache, orig_dex_cache->FixupResolvedMethodTypes(NativeCopyLocation(orig_method_types, orig_dex_cache), ImageAddressVisitor(this)); } + GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites(); + if (orig_call_sites != nullptr) { + copy_dex_cache->SetFieldPtrWithSize<false>(mirror::DexCache::ResolvedCallSitesOffset(), + NativeLocationInImage(orig_call_sites), + PointerSize::k64); + orig_dex_cache->FixupResolvedCallSites(NativeCopyLocation(orig_call_sites, orig_dex_cache), + ImageAddressVisitor(this)); + } // Remove the DexFile pointers. They will be fixed up when the runtime loads the oat file. Leaving // compiler pointers in here will make the output non-deterministic. diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 79e1785e91..9ddf200237 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -31,9 +31,7 @@ namespace linker { namespace { inline bool IsAdrpPatch(const LinkerPatch& patch) { - LinkerPatch::Type type = patch.GetType(); - return - (type == LinkerPatch::Type::kStringRelative || type == LinkerPatch::Type::kDexCacheArray) && + return (patch.IsPcRelative() && patch.GetType() != LinkerPatch::Type::kCallRelative) && patch.LiteralOffset() == patch.PcInsnOffset(); } @@ -214,11 +212,11 @@ void Arm64RelativePatcher::PatchPcRelativeReference(std::vector<uint8_t>* code, DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative || patch.GetType() == LinkerPatch::Type::kTypeRelative) << patch.GetType(); } else { - // With the read barrier (non-Baker) enabled, it could be kDexCacheArray in the - // HLoadString::LoadKind::kDexCachePcRelative case of VisitLoadString(). + // With the read barrier (non-Baker) enabled, it could be kStringBssEntry or kTypeBssEntry. DCHECK(patch.GetType() == LinkerPatch::Type::kStringRelative || patch.GetType() == LinkerPatch::Type::kTypeRelative || - patch.GetType() == LinkerPatch::Type::kDexCacheArray) << patch.GetType(); + patch.GetType() == LinkerPatch::Type::kStringBssEntry || + patch.GetType() == LinkerPatch::Type::kTypeBssEntry) << patch.GetType(); } shift = 0u; // No shift for ADD. } else { diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index d68aa51b1b..bac16cd5df 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1419,7 +1419,7 @@ void CodeGenerator::EmitJitRoots(uint8_t* code, QuickEntrypointEnum CodeGenerator::GetArrayAllocationEntrypoint(Handle<mirror::Class> array_klass) { ScopedObjectAccess soa(Thread::Current()); - if (array_klass.Get() == nullptr) { + if (array_klass == nullptr) { // This can only happen for non-primitive arrays, as primitive arrays can always // be resolved. return kQuickAllocArrayResolved32; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 20cdae3619..759a951d6b 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -367,22 +367,37 @@ class BoundsCheckSlowPathARM : public SlowPathCodeARM { class LoadClassSlowPathARM : public SlowPathCodeARM { public: - LoadClassSlowPathARM(HLoadClass* cls, - HInstruction* at, - uint32_t dex_pc, - bool do_clinit) + LoadClassSlowPathARM(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, bool do_clinit) : SlowPathCodeARM(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); + Location out = locations->Out(); + constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConvention calling_convention; + // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + bool is_load_class_bss_entry = + (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); + Register entry_address = kNoRegister; + if (is_load_class_bss_entry && call_saves_everything_except_r0) { + Register temp = locations->GetTemp(0).AsRegister<Register>(); + // In the unlucky case that the `temp` is R0, we preserve the address in `out` across + // the kSaveEverything call. + bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0)); + entry_address = temp_is_r0 ? out.AsRegister<Register>() : temp; + DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); + if (temp_is_r0) { + __ mov(entry_address, ShifterOperand(temp)); + } + } dex::TypeIndex type_index = cls_->GetTypeIndex(); __ LoadImmediate(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -394,30 +409,31 @@ class LoadClassSlowPathARM : public SlowPathCodeARM { CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); } + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + if (is_load_class_bss_entry) { + if (call_saves_everything_except_r0) { + // The class entry address was preserved in `entry_address` thanks to kSaveEverything. + __ str(R0, Address(entry_address)); + } else { + // For non-Baker read barrier, we need to re-calculate the address of the string entry. + Register temp = IP; + CodeGeneratorARM::PcRelativePatchInfo* labels = + arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); + __ BindTrackedLabel(&labels->movw_label); + __ movw(temp, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->movt_label); + __ movt(temp, /* placeholder */ 0u); + __ BindTrackedLabel(&labels->add_pc_label); + __ add(temp, temp, ShifterOperand(PC)); + __ str(R0, Address(temp)); + } + } // Move the class to the desired location. - Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - // 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. - CodeGeneratorARM::PcRelativePatchInfo* labels = - arm_codegen->NewTypeBssEntryPatch(cls_->GetDexFile(), type_index); - __ BindTrackedLabel(&labels->movw_label); - __ movw(IP, /* placeholder */ 0u); - __ BindTrackedLabel(&labels->movt_label); - __ movt(IP, /* placeholder */ 0u); - __ BindTrackedLabel(&labels->add_pc_label); - __ add(IP, IP, ShifterOperand(PC)); - __ str(locations->Out().AsRegister<Register>(), Address(IP)); - } __ b(GetExitLabel()); } @@ -441,12 +457,13 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { explicit LoadStringSlowPathARM(HLoadString* instruction) : SlowPathCodeARM(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + DCHECK(instruction_->IsLoadString()); + DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); HLoadString* load = instruction_->AsLoadString(); const dex::StringIndex string_index = load->GetStringIndex(); Register out = locations->Out().AsRegister<Register>(); - Register temp = locations->GetTemp(0).AsRegister<Register>(); constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARM* arm_codegen = down_cast<CodeGeneratorARM*>(codegen); @@ -455,12 +472,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { InvokeRuntimeCallingConvention calling_convention; // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call). - bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0)); - Register entry_address = temp_is_r0 ? out : temp; - DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); - if (call_saves_everything_except_r0 && temp_is_r0) { - __ mov(entry_address, ShifterOperand(temp)); + // the kSaveEverything call. + Register entry_address = kNoRegister; + if (call_saves_everything_except_r0) { + Register temp = locations->GetTemp(0).AsRegister<Register>(); + bool temp_is_r0 = (temp == calling_convention.GetRegisterAt(0)); + entry_address = temp_is_r0 ? out : temp; + DCHECK_NE(entry_address, calling_convention.GetRegisterAt(0)); + if (temp_is_r0) { + __ mov(entry_address, ShifterOperand(temp)); + } } __ LoadImmediate(calling_convention.GetRegisterAt(0), string_index.index_); @@ -473,15 +494,16 @@ class LoadStringSlowPathARM : public SlowPathCodeARM { __ str(R0, Address(entry_address)); } else { // For non-Baker read barrier, we need to re-calculate the address of the string entry. + Register temp = IP; CodeGeneratorARM::PcRelativePatchInfo* labels = arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index); __ BindTrackedLabel(&labels->movw_label); - __ movw(entry_address, /* placeholder */ 0u); + __ movw(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); - __ movt(entry_address, /* placeholder */ 0u); + __ movt(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->add_pc_label); - __ add(entry_address, entry_address, ShifterOperand(PC)); - __ str(R0, Address(entry_address)); + __ add(temp, temp, ShifterOperand(PC)); + __ str(R0, Address(temp)); } arm_codegen->Move32(locations->Out(), Location::RegisterLocation(R0)); @@ -624,6 +646,10 @@ class ArraySetSlowPathARM : public SlowPathCodeARM { // probably still be a from-space reference (unless it gets updated by // another thread, or if another thread installed another object // reference (different from `ref`) in `obj.field`). +// +// If `entrypoint` is a valid location it is assumed to already be +// holding the entrypoint. The case where the entrypoint is passed in +// is for the GcRoot read barrier. class ReadBarrierMarkSlowPathARM : public SlowPathCodeARM { public: ReadBarrierMarkSlowPathARM(HInstruction* instruction, @@ -5755,6 +5781,7 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), Location::RegisterLocation(R0)); + DCHECK_EQ(calling_convention.GetRegisterAt(0), R0); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -5772,6 +5799,22 @@ void LocationsBuilderARM::VisitLoadClass(HLoadClass* cls) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadClass::LoadKind::kBssEntry) { + if (!kUseReadBarrier || kUseBakerReadBarrier) { + // Rely on the type resolution or initialization and marking to save everything we need. + // Note that IP may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we request a different temp. + locations->AddTemp(Location::RequiresRegister()); + RegisterSet caller_saves = RegisterSet::Empty(); + InvokeRuntimeCallingConvention calling_convention; + caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK() + // that the the kPrimNot result register is the same as the first argument register. + locations->SetCustomSlowPathCallerSaves(caller_saves); + } else { + // For non-Baker read barrier we have a temp-clobbering call. + } + } } // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not @@ -5834,15 +5877,18 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE break; } case HLoadClass::LoadKind::kBssEntry: { + Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) + ? locations->GetTemp(0).AsRegister<Register>() + : out; CodeGeneratorARM::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); __ BindTrackedLabel(&labels->movw_label); - __ movw(out, /* placeholder */ 0u); + __ movw(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->movt_label); - __ movt(out, /* placeholder */ 0u); + __ movt(temp, /* placeholder */ 0u); __ BindTrackedLabel(&labels->add_pc_label); - __ add(out, out, ShifterOperand(PC)); - GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption); + __ add(temp, temp, ShifterOperand(PC)); + GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -5851,7 +5897,7 @@ void InstructionCodeGeneratorARM::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -5938,9 +5984,9 @@ void LocationsBuilderARM::VisitLoadString(HLoadString* load) { locations->SetOut(Location::RequiresRegister()); if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { - // Rely on the pResolveString and/or marking to save everything, including temps. - // Note that IP may theoretically be clobbered by saving/restoring the live register - // (only one thanks to the custom calling convention), so we request a different temp. + // Rely on the pResolveString and marking to save everything we need, including temps. + // Note that IP may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we request a different temp. locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; @@ -5991,7 +6037,9 @@ void InstructionCodeGeneratorARM::VisitLoadString(HLoadString* load) NO_THREAD_S } case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - Register temp = locations->GetTemp(0).AsRegister<Register>(); + Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) + ? locations->GetTemp(0).AsRegister<Register>() + : out; CodeGeneratorARM::PcRelativePatchInfo* labels = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); __ BindTrackedLabel(&labels->movw_label); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 598be4715b..e6032d2381 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -34,6 +34,9 @@ #include "utils/stack_checks.h" using namespace vixl::aarch64; // NOLINT(build/namespaces) +using vixl::ExactAssemblyScope; +using vixl::CodeBufferCheckScope; +using vixl::EmissionCheckScope; #ifdef __ #error "ARM64 Codegen VIXL macro-assembler macro already defined." @@ -275,15 +278,37 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { LoadClassSlowPathARM64(HLoadClass* cls, HInstruction* at, uint32_t dex_pc, - bool do_clinit) - : SlowPathCodeARM64(at), cls_(cls), dex_pc_(dex_pc), do_clinit_(do_clinit) { + bool do_clinit, + vixl::aarch64::Register bss_entry_temp = vixl::aarch64::Register(), + vixl::aarch64::Label* bss_entry_adrp_label = nullptr) + : SlowPathCodeARM64(at), + cls_(cls), + dex_pc_(dex_pc), + do_clinit_(do_clinit), + bss_entry_temp_(bss_entry_temp), + bss_entry_adrp_label_(bss_entry_adrp_label) { DCHECK(at->IsLoadClass() || at->IsClinitCheck()); } void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); + Location out = locations->Out(); + constexpr bool call_saves_everything_except_r0_ip0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen); + // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the page address of + // the entry which is in a scratch register. Make sure it's not used for saving/restoring + // registers. Exclude the scratch register also for non-Baker read barrier for simplicity. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + bool is_load_class_bss_entry = + (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); + UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler()); + if (is_load_class_bss_entry) { + // This temp is a scratch register. + DCHECK(bss_entry_temp_.IsValid()); + temps.Exclude(bss_entry_temp_); + } + __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); @@ -300,7 +325,6 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { } // Move the class to the desired location. - Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); Primitive::Type type = instruction_->GetType(); @@ -308,25 +332,23 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { } RestoreLiveRegisters(codegen, locations); // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + if (is_load_class_bss_entry) { DCHECK(out.IsValid()); - UseScratchRegisterScope temps(arm64_codegen->GetVIXLAssembler()); - Register temp = temps.AcquireX(); const DexFile& dex_file = cls_->GetDexFile(); - // TODO: Change art_quick_initialize_type/art_quick_initialize_static_storage to - // kSaveEverything and use a temporary for the ADRP in the fast path, so that we - // can avoid the ADRP here. - vixl::aarch64::Label* adrp_label = - arm64_codegen->NewBssEntryTypePatch(dex_file, type_index); - arm64_codegen->EmitAdrpPlaceholder(adrp_label, temp); + if (call_saves_everything_except_r0_ip0) { + // The class entry page address was preserved in bss_entry_temp_ thanks to kSaveEverything. + } else { + // For non-Baker read barrier, we need to re-calculate the address of the class entry page. + bss_entry_adrp_label_ = arm64_codegen->NewBssEntryTypePatch(dex_file, type_index); + arm64_codegen->EmitAdrpPlaceholder(bss_entry_adrp_label_, bss_entry_temp_); + } vixl::aarch64::Label* strp_label = - arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, adrp_label); + arm64_codegen->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label_); { SingleEmissionCheckScope guard(arm64_codegen->GetVIXLAssembler()); __ Bind(strp_label); __ str(RegisterFrom(locations->Out(), Primitive::kPrimNot), - MemOperand(temp, /* offset placeholder */ 0)); + MemOperand(bss_entry_temp_, /* offset placeholder */ 0)); } } __ B(GetExitLabel()); @@ -344,6 +366,10 @@ class LoadClassSlowPathARM64 : public SlowPathCodeARM64 { // Whether to initialize the class. const bool do_clinit_; + // For HLoadClass/kBssEntry, the temp register and the label of the ADRP where it was loaded. + vixl::aarch64::Register bss_entry_temp_; + vixl::aarch64::Label* bss_entry_adrp_label_; + DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64); }; @@ -590,10 +616,9 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { // We are about to use the assembler to place literals directly. Make sure we have enough // underlying code buffer and we have generated the jump table with right size. - vixl::CodeBufferCheckScope scope(codegen->GetVIXLAssembler(), - num_entries * sizeof(int32_t), - vixl::CodeBufferCheckScope::kReserveBufferSpace, - vixl::CodeBufferCheckScope::kExactSize); + EmissionCheckScope scope(codegen->GetVIXLAssembler(), + num_entries * sizeof(int32_t), + CodeBufferCheckScope::kExactSize); __ Bind(&table_start_); const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors(); @@ -619,8 +644,10 @@ void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) { // probably still be a from-space reference (unless it gets updated by // another thread, or if another thread installed another object // reference (different from `ref`) in `obj.field`). -// If entrypoint is a valid location it is assumed to already be holding the entrypoint. The case -// where the entrypoint is passed in is for the GcRoot read barrier. +// +// If `entrypoint` is a valid location it is assumed to already be +// holding the entrypoint. The case where the entrypoint is passed in +// is for the GcRoot read barrier. class ReadBarrierMarkSlowPathARM64 : public SlowPathCodeARM64 { public: ReadBarrierMarkSlowPathARM64(HInstruction* instruction, @@ -1254,7 +1281,6 @@ void ParallelMoveResolverARM64::EmitMove(size_t index) { void CodeGeneratorARM64::GenerateFrameEntry() { MacroAssembler* masm = GetVIXLAssembler(); - BlockPoolsScope block_pools(masm); __ Bind(&frame_entry_label_); bool do_overflow_check = FrameNeedsStackCheck(GetFrameSize(), kArm64) || !IsLeafMethod(); @@ -1263,8 +1289,14 @@ void CodeGeneratorARM64::GenerateFrameEntry() { Register temp = temps.AcquireX(); DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks()); __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm64))); - __ Ldr(wzr, MemOperand(temp, 0)); - RecordPcInfo(nullptr, 0); + { + // Ensure that between load and RecordPcInfo there are no pools emitted. + ExactAssemblyScope eas(GetVIXLAssembler(), + kInstructionSize, + CodeBufferCheckScope::kExactSize); + __ ldr(wzr, MemOperand(temp, 0)); + RecordPcInfo(nullptr, 0); + } } if (!HasEmptyFrame()) { @@ -1299,7 +1331,6 @@ void CodeGeneratorARM64::GenerateFrameEntry() { } void CodeGeneratorARM64::GenerateFrameExit() { - BlockPoolsScope block_pools(GetVIXLAssembler()); GetAssembler()->cfi().RememberState(); if (!HasEmptyFrame()) { int frame_size = GetFrameSize(); @@ -1626,7 +1657,6 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, const MemOperand& src, bool needs_null_check) { MacroAssembler* masm = GetVIXLAssembler(); - BlockPoolsScope block_pools(masm); UseScratchRegisterScope temps(masm); Register temp_base = temps.AcquireX(); Primitive::Type type = instruction->GetType(); @@ -1636,58 +1666,79 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction, // TODO(vixl): Let the MacroAssembler handle MemOperand. __ Add(temp_base, src.GetBaseRegister(), OperandFromMemOperand(src)); - MemOperand base = MemOperand(temp_base); - switch (type) { - case Primitive::kPrimBoolean: - __ Ldarb(Register(dst), base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); - } - break; - case Primitive::kPrimByte: - __ Ldarb(Register(dst), base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); - } - __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); - break; - case Primitive::kPrimChar: - __ Ldarh(Register(dst), base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); - } - break; - case Primitive::kPrimShort: - __ Ldarh(Register(dst), base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); - } - __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); - break; - case Primitive::kPrimInt: - case Primitive::kPrimNot: - case Primitive::kPrimLong: - DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); - __ Ldar(Register(dst), base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); - } - break; - case Primitive::kPrimFloat: - case Primitive::kPrimDouble: { - DCHECK(dst.IsFPRegister()); - DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); - - Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); - __ Ldar(temp, base); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + MemOperand base = MemOperand(temp_base); + switch (type) { + case Primitive::kPrimBoolean: + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldarb(Register(dst), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + break; + case Primitive::kPrimByte: + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldarb(Register(dst), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); + break; + case Primitive::kPrimChar: + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldarh(Register(dst), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + break; + case Primitive::kPrimShort: + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldarh(Register(dst), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + __ Sbfx(Register(dst), Register(dst), 0, Primitive::ComponentSize(type) * kBitsPerByte); + break; + case Primitive::kPrimInt: + case Primitive::kPrimNot: + case Primitive::kPrimLong: + DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldar(Register(dst), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + break; + case Primitive::kPrimFloat: + case Primitive::kPrimDouble: { + DCHECK(dst.IsFPRegister()); + DCHECK_EQ(dst.Is64Bits(), Primitive::Is64BitType(type)); + + Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ ldar(temp, base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } + __ Fmov(FPRegister(dst), temp); + break; } - __ Fmov(FPRegister(dst), temp); - break; + case Primitive::kPrimVoid: + LOG(FATAL) << "Unreachable type " << type; } - case Primitive::kPrimVoid: - LOG(FATAL) << "Unreachable type " << type; } } @@ -1716,9 +1767,12 @@ void CodeGeneratorARM64::Store(Primitive::Type type, } } -void CodeGeneratorARM64::StoreRelease(Primitive::Type type, +void CodeGeneratorARM64::StoreRelease(HInstruction* instruction, + Primitive::Type type, CPURegister src, - const MemOperand& dst) { + const MemOperand& dst, + bool needs_null_check) { + MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(GetVIXLAssembler()); Register temp_base = temps.AcquireX(); @@ -1729,20 +1783,39 @@ void CodeGeneratorARM64::StoreRelease(Primitive::Type type, Operand op = OperandFromMemOperand(dst); __ Add(temp_base, dst.GetBaseRegister(), op); MemOperand base = MemOperand(temp_base); + // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted. switch (type) { case Primitive::kPrimBoolean: case Primitive::kPrimByte: - __ Stlrb(Register(src), base); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ stlrb(Register(src), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } break; case Primitive::kPrimChar: case Primitive::kPrimShort: - __ Stlrh(Register(src), base); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ stlrh(Register(src), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } break; case Primitive::kPrimInt: case Primitive::kPrimNot: case Primitive::kPrimLong: DCHECK_EQ(src.Is64Bits(), Primitive::Is64BitType(type)); - __ Stlr(Register(src), base); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ stlr(Register(src), base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } break; case Primitive::kPrimFloat: case Primitive::kPrimDouble: { @@ -1756,8 +1829,13 @@ void CodeGeneratorARM64::StoreRelease(Primitive::Type type, temp_src = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW(); __ Fmov(temp_src, FPRegister(src)); } - - __ Stlr(temp_src, base); + { + ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); + __ stlr(temp_src, base); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } + } break; } case Primitive::kPrimVoid: @@ -1770,9 +1848,15 @@ void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint, uint32_t dex_pc, SlowPathCode* slow_path) { ValidateInvokeRuntime(entrypoint, instruction, slow_path); - GenerateInvokeRuntime(GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value()); - if (EntrypointRequiresStackMap(entrypoint)) { - RecordPcInfo(instruction, dex_pc, slow_path); + + __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value())); + { + // Ensure the pc position is recorded immediately after the `blr` instruction. + ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); + __ blr(lr); + if (EntrypointRequiresStackMap(entrypoint)) { + RecordPcInfo(instruction, dex_pc, slow_path); + } } } @@ -1780,11 +1864,6 @@ void CodeGeneratorARM64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point HInstruction* instruction, SlowPathCode* slow_path) { ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path); - GenerateInvokeRuntime(entry_point_offset); -} - -void CodeGeneratorARM64::GenerateInvokeRuntime(int32_t entry_point_offset) { - BlockPoolsScope block_pools(GetVIXLAssembler()); __ Ldr(lr, MemOperand(tr, entry_point_offset)); __ Blr(lr); } @@ -1951,7 +2030,6 @@ void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, Location out = locations->Out(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); Primitive::Type field_type = field_info.GetFieldType(); - BlockPoolsScope block_pools(GetVIXLAssembler()); MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset()); if (field_type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -1978,6 +2056,8 @@ void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, codegen_->LoadAcquire( instruction, OutputCPURegister(instruction), field, /* needs_null_check */ true); } else { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); codegen_->Load(field_type, OutputCPURegister(instruction), field); codegen_->MaybeRecordImplicitNullCheck(instruction); } @@ -2007,7 +2087,6 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, bool value_can_be_null) { DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); - BlockPoolsScope block_pools(GetVIXLAssembler()); Register obj = InputRegisterAt(instruction, 0); CPURegister value = InputCPURegisterOrZeroRegAt(instruction, 1); @@ -2029,9 +2108,11 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, } if (field_info.IsVolatile()) { - codegen_->StoreRelease(field_type, source, HeapOperand(obj, offset)); - codegen_->MaybeRecordImplicitNullCheck(instruction); + codegen_->StoreRelease( + instruction, field_type, source, HeapOperand(obj, offset), /* needs_null_check */ true); } else { + // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); codegen_->Store(field_type, source, HeapOperand(obj, offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); } @@ -2317,10 +2398,7 @@ void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize; if (prev->IsLoadOrStore()) { // Make sure we emit only exactly one nop. - vixl::CodeBufferCheckScope scope(masm, - kInstructionSize, - vixl::CodeBufferCheckScope::kReserveBufferSpace, - vixl::CodeBufferCheckScope::kExactSize); + ExactAssemblyScope scope(masm, kInstructionSize, CodeBufferCheckScope::kExactSize); __ nop(); } } @@ -2376,8 +2454,6 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { instruction->IsStringCharAt(); MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope temps(masm); - // Block pools between `Load` and `MaybeRecordImplicitNullCheck`. - BlockPoolsScope block_pools(masm); // The read barrier instrumentation of object ArrayGet instructions // does not support the HIntermediateAddress instruction. @@ -2399,15 +2475,21 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { if (maybe_compressed_char_at) { uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); length = temps.AcquireW(); - if (instruction->GetArray()->IsIntermediateAddress()) { - DCHECK_LT(count_offset, offset); - int64_t adjusted_offset = static_cast<int64_t>(count_offset) - static_cast<int64_t>(offset); - // Note that `adjusted_offset` is negative, so this will be a LDUR. - __ Ldr(length, MemOperand(obj.X(), adjusted_offset)); - } else { - __ Ldr(length, HeapOperand(obj, count_offset)); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + + if (instruction->GetArray()->IsIntermediateAddress()) { + DCHECK_LT(count_offset, offset); + int64_t adjusted_offset = + static_cast<int64_t>(count_offset) - static_cast<int64_t>(offset); + // Note that `adjusted_offset` is negative, so this will be a LDUR. + __ Ldr(length, MemOperand(obj.X(), adjusted_offset)); + } else { + __ Ldr(length, HeapOperand(obj, count_offset)); + } + codegen_->MaybeRecordImplicitNullCheck(instruction); } - codegen_->MaybeRecordImplicitNullCheck(instruction); } if (index.IsConstant()) { if (maybe_compressed_char_at) { @@ -2457,6 +2539,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { } } if (!maybe_compressed_char_at) { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); codegen_->Load(type, OutputCPURegister(instruction), source); codegen_->MaybeRecordImplicitNullCheck(instruction); } @@ -2484,9 +2568,12 @@ void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); vixl::aarch64::Register out = OutputRegister(instruction); - BlockPoolsScope block_pools(GetVIXLAssembler()); - __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset)); - codegen_->MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); + } // Mask out compression flag from String's array length. if (mirror::kUseStringCompression && instruction->IsStringLength()) { __ Lsr(out.W(), out.W(), 1u); @@ -2527,7 +2614,6 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); MemOperand destination = HeapOperand(array); MacroAssembler* masm = GetVIXLAssembler(); - BlockPoolsScope block_pools(masm); if (!needs_write_barrier) { DCHECK(!may_need_runtime_call_for_type_check); @@ -2554,8 +2640,12 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { LSL, Primitive::ComponentSizeShift(value_type)); } - codegen_->Store(value_type, value, destination); - codegen_->MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + codegen_->Store(value_type, value, destination); + codegen_->MaybeRecordImplicitNullCheck(instruction); + } } else { DCHECK(!instruction->GetArray()->IsIntermediateAddress()); vixl::aarch64::Label done; @@ -2588,8 +2678,13 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { if (!index.IsConstant()) { __ Add(temp, array, offset); } - __ Str(wzr, destination); - codegen_->MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools + // emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + __ Str(wzr, destination); + codegen_->MaybeRecordImplicitNullCheck(instruction); + } __ B(&done); __ Bind(&non_zero); } @@ -2604,8 +2699,12 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { Register temp2 = temps.AcquireSameSizeAs(array); // /* HeapReference<Class> */ temp = array->klass_ - __ Ldr(temp, HeapOperand(array, class_offset)); - codegen_->MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + __ Ldr(temp, HeapOperand(array, class_offset)); + codegen_->MaybeRecordImplicitNullCheck(instruction); + } GetAssembler()->MaybeUnpoisonHeapReference(temp); // /* HeapReference<Class> */ temp = temp->component_type_ @@ -2646,10 +2745,14 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { if (!index.IsConstant()) { __ Add(temp, array, offset); } - __ Str(source, destination); + { + // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + __ Str(source, destination); - if (!may_need_runtime_call_for_type_check) { - codegen_->MaybeRecordImplicitNullCheck(instruction); + if (!may_need_runtime_call_for_type_check) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } } } @@ -3944,19 +4047,25 @@ void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invok // art_quick_imt_conflict_trampoline, so prevent VIXL from using it. MacroAssembler* masm = GetVIXLAssembler(); UseScratchRegisterScope scratch_scope(masm); - BlockPoolsScope block_pools(masm); scratch_scope.Exclude(ip1); __ Mov(ip1, invoke->GetDexMethodIndex()); + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. if (receiver.IsStackSlot()) { __ Ldr(temp.W(), StackOperandFrom(receiver)); - // /* HeapReference<Class> */ temp = temp->klass_ - __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset)); + { + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + // /* HeapReference<Class> */ temp = temp->klass_ + __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset)); + codegen_->MaybeRecordImplicitNullCheck(invoke); + } } else { + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); // /* HeapReference<Class> */ temp = receiver->klass_ __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset)); + codegen_->MaybeRecordImplicitNullCheck(invoke); } - codegen_->MaybeRecordImplicitNullCheck(invoke); + // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. // However this is not required in practice, as this is an @@ -3973,10 +4082,16 @@ void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invok __ Ldr(temp, MemOperand(temp, method_offset)); // lr = temp->GetEntryPoint(); __ Ldr(lr, MemOperand(temp, entry_point.Int32Value())); - // lr(); - __ Blr(lr); - DCHECK(!codegen_->IsLeafMethod()); - codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + + { + // Ensure the pc position is recorded immediately after the `blr` instruction. + ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); + + // lr(); + __ blr(lr); + DCHECK(!codegen_->IsLeafMethod()); + codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); + } } void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { @@ -4088,8 +4203,16 @@ void CodeGeneratorARM64::GenerateStaticOrDirectCall(HInvokeStaticOrDirect* invok __ Ldr(lr, MemOperand( XRegisterFrom(callee_method), ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize).Int32Value())); - // lr() - __ Blr(lr); + { + // To ensure that the pc position is recorded immediately after the `blr` instruction + // BLR must be the last instruction emitted in this function. + // Recording the pc will occur right after returning from this function. + ExactAssemblyScope eas(GetVIXLAssembler(), + kInstructionSize, + CodeBufferCheckScope::kExactSize); + // lr() + __ blr(lr); + } break; } @@ -4109,12 +4232,15 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te Offset class_offset = mirror::Object::ClassOffset(); Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize); - BlockPoolsScope block_pools(GetVIXLAssembler()); - DCHECK(receiver.IsRegister()); - // /* HeapReference<Class> */ temp = receiver->klass_ - __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset)); - MaybeRecordImplicitNullCheck(invoke); + + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + // /* HeapReference<Class> */ temp = receiver->klass_ + __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset)); + MaybeRecordImplicitNullCheck(invoke); + } // Instead of simply (possibly) unpoisoning `temp` here, we should // emit a read barrier for the previous class reference load. // intermediate/temporary reference and because the current @@ -4126,8 +4252,14 @@ void CodeGeneratorARM64::GenerateVirtualCall(HInvokeVirtual* invoke, Location te __ Ldr(temp, MemOperand(temp, method_offset)); // lr = temp->GetEntryPoint(); __ Ldr(lr, MemOperand(temp, entry_point.SizeValue())); - // lr(); - __ Blr(lr); + { + // To ensure that the pc position is recorded immediately after the `blr` instruction + // BLR should be the last instruction emitted in this function. + // Recording the pc will occur right after returning from this function. + ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize); + // lr(); + __ blr(lr); + } } void LocationsBuilderARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) { @@ -4340,7 +4472,9 @@ void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDir return; } - BlockPoolsScope block_pools(GetVIXLAssembler()); + // Ensure that between the BLR (emitted by GenerateStaticOrDirectCall) and RecordPcInfo there + // are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes); LocationSummary* locations = invoke->GetLocations(); codegen_->GenerateStaticOrDirectCall( invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation()); @@ -4352,6 +4486,9 @@ void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { return; } + // Ensure that between the BLR (emitted by GenerateVirtualCall) and RecordPcInfo there + // are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes); codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0)); DCHECK(!codegen_->IsLeafMethod()); codegen_->RecordPcInfo(invoke, invoke->GetDexPc()); @@ -4393,6 +4530,7 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { cls, LocationFrom(calling_convention.GetRegisterAt(0)), LocationFrom(vixl::aarch64::x0)); + DCHECK(calling_convention.GetRegisterAt(0).Is(vixl::aarch64::x0)); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -4410,6 +4548,22 @@ void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); + if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { + if (!kUseReadBarrier || kUseBakerReadBarrier) { + // Rely on the type resolution or initialization and marking to save everything we need. + // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we shall use IP1. + RegisterSet caller_saves = RegisterSet::Empty(); + InvokeRuntimeCallingConvention calling_convention; + caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); + DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(), + RegisterFrom(calling_convention.GetReturnLocation(Primitive::kPrimNot), + Primitive::kPrimNot).GetCode()); + locations->SetCustomSlowPathCallerSaves(caller_saves); + } else { + // For non-Baker read barrier we have a temp-clobbering call. + } + } } // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not @@ -4424,6 +4578,8 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA Location out_loc = cls->GetLocations()->Out(); Register out = OutputRegister(cls); + Register bss_entry_temp; + vixl::aarch64::Label* bss_entry_adrp_label = nullptr; const ReadBarrierOption read_barrier_option = cls->IsInBootImage() ? kWithoutReadBarrier @@ -4473,18 +4629,23 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA // Add ADRP with its PC-relative Class .bss entry patch. const DexFile& dex_file = cls->GetDexFile(); dex::TypeIndex type_index = cls->GetTypeIndex(); - vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); - codegen_->EmitAdrpPlaceholder(adrp_label, out.X()); + // We can go to slow path even with non-zero reference and in that case marking + // can clobber IP0, so we need to use IP1 which shall be preserved. + bss_entry_temp = ip1; + UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); + temps.Exclude(bss_entry_temp); + bss_entry_adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index); + codegen_->EmitAdrpPlaceholder(bss_entry_adrp_label, bss_entry_temp); // Add LDR with its PC-relative Class patch. vixl::aarch64::Label* ldr_label = - codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label); + codegen_->NewBssEntryTypePatch(dex_file, type_index, bss_entry_adrp_label); // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */ GenerateGcRootFieldLoad(cls, - cls->GetLocations()->Out(), - out.X(), - /* placeholder */ 0u, + out_loc, + bss_entry_temp, + /* offset placeholder */ 0u, ldr_label, - kCompilerReadBarrierOption); + read_barrier_option); generate_null_check = true; break; } @@ -4497,7 +4658,7 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA out.X(), /* offset */ 0, /* fixup_label */ nullptr, - kCompilerReadBarrierOption); + read_barrier_option); break; } case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -4506,10 +4667,11 @@ void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SA UNREACHABLE(); } - if (generate_null_check || cls->MustGenerateClinitCheck()) { + bool do_clinit = cls->MustGenerateClinitCheck(); + if (generate_null_check || do_clinit) { DCHECK(cls->CanCallRuntime()); SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathARM64( - cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck()); + cls, cls, cls->GetDexPc(), do_clinit, bss_entry_temp, bss_entry_adrp_label); codegen_->AddSlowPath(slow_path); if (generate_null_check) { __ Cbz(out, slow_path->GetEntryLabel()); @@ -4577,7 +4739,9 @@ void LocationsBuilderARM64::VisitLoadString(HLoadString* load) { locations->SetOut(Location::RequiresRegister()); if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { - // Rely on the pResolveString and/or marking to save everything, including temps. + // Rely on the pResolveString and marking to save everything we need. + // Note that IP0 may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we shall use IP1. RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode())); @@ -4628,8 +4792,11 @@ void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD const DexFile& dex_file = load->GetDexFile(); const dex::StringIndex string_index = load->GetStringIndex(); DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); + // We could use IP0 as the marking shall not clobber IP0 if the reference is null and + // that's when we need the slow path. But let's not rely on such details and use IP1. + Register temp = ip1; UseScratchRegisterScope temps(codegen_->GetVIXLAssembler()); - Register temp = temps.AcquireX(); + temps.Exclude(temp); vixl::aarch64::Label* adrp_label = codegen_->NewPcRelativeStringPatch(dex_file, string_index); codegen_->EmitAdrpPlaceholder(adrp_label, temp); // Add LDR with its PC-relative String patch. @@ -4817,8 +4984,15 @@ void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize); __ Ldr(XRegisterFrom(temp), MemOperand(tr, QUICK_ENTRY_POINT(pNewEmptyString))); __ Ldr(lr, MemOperand(XRegisterFrom(temp), code_offset.Int32Value())); - __ Blr(lr); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + + { + // Ensure the pc position is recorded immediately after the `blr` instruction. + ExactAssemblyScope eas(GetVIXLAssembler(), + kInstructionSize, + CodeBufferCheckScope::kExactSize); + __ blr(lr); + codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + } } else { codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc()); CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>(); @@ -4862,11 +5036,13 @@ void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) { if (CanMoveNullCheckToUser(instruction)) { return; } - - BlockPoolsScope block_pools(GetVIXLAssembler()); - Location obj = instruction->GetLocations()->InAt(0); - __ Ldr(wzr, HeapOperandFrom(obj, Offset(0))); - RecordPcInfo(instruction, instruction->GetDexPc()); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + Location obj = instruction->GetLocations()->InAt(0); + __ Ldr(wzr, HeapOperandFrom(obj, Offset(0))); + RecordPcInfo(instruction, instruction->GetDexPc()); + } } void CodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) { @@ -5603,10 +5779,14 @@ void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* DCHECK(obj.IsW()); uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value(); - // /* int32_t */ monitor = obj->monitor_ - __ Ldr(temp, HeapOperand(obj, monitor_offset)); - if (needs_null_check) { - MaybeRecordImplicitNullCheck(instruction); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + // /* int32_t */ monitor = obj->monitor_ + __ Ldr(temp, HeapOperand(obj, monitor_offset)); + if (needs_null_check) { + MaybeRecordImplicitNullCheck(instruction); + } } // /* LockWord */ lock_word = LockWord(monitor) static_assert(sizeof(LockWord) == sizeof(int32_t), diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index f6cb90a63a..5faf29a90f 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -43,6 +43,11 @@ class CodeGeneratorARM64; // Use a local definition to prevent copying mistakes. static constexpr size_t kArm64WordSize = static_cast<size_t>(kArm64PointerSize); +// These constants are used as an approximate margin when emission of veneer and literal pools +// must be blocked. +static constexpr int kMaxMacroInstructionSizeInBytes = 15 * vixl::aarch64::kInstructionSize; +static constexpr int kInvokeCodeMarginSizeInBytes = 6 * kMaxMacroInstructionSizeInBytes; + static const vixl::aarch64::Register kParameterCoreRegisters[] = { vixl::aarch64::x1, vixl::aarch64::x2, @@ -486,9 +491,11 @@ class CodeGeneratorARM64 : public CodeGenerator { vixl::aarch64::CPURegister dst, const vixl::aarch64::MemOperand& src, bool needs_null_check); - void StoreRelease(Primitive::Type type, + void StoreRelease(HInstruction* instruction, + Primitive::Type type, vixl::aarch64::CPURegister src, - const vixl::aarch64::MemOperand& dst); + const vixl::aarch64::MemOperand& dst, + bool needs_null_check); // Generate code to invoke a runtime entry point. void InvokeRuntime(QuickEntrypointEnum entrypoint, @@ -502,8 +509,6 @@ class CodeGeneratorARM64 : public CodeGenerator { HInstruction* instruction, SlowPathCode* slow_path); - void GenerateInvokeRuntime(int32_t entry_point_offset); - ParallelMoveResolverARM64* GetMoveResolver() OVERRIDE { return &move_resolver_; } bool NeedsTwoRegisters(Primitive::Type type ATTRIBUTE_UNUSED) const OVERRIDE { diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index e18960872e..5c4ca5bc17 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -400,12 +400,30 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { LocationSummary* locations = instruction_->GetLocations(); + Location out = locations->Out(); + constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); __ Bind(GetEntryLabel()); SaveLiveRegisters(codegen, locations); InvokeRuntimeCallingConventionARMVIXL calling_convention; + // For HLoadClass/kBssEntry/kSaveEverything, make sure we preserve the address of the entry. + DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); + bool is_load_class_bss_entry = + (cls_ == instruction_) && (cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry); + vixl32::Register entry_address; + if (is_load_class_bss_entry && call_saves_everything_except_r0) { + vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); + // In the unlucky case that the `temp` is R0, we preserve the address in `out` across + // the kSaveEverything call. + bool temp_is_r0 = temp.Is(calling_convention.GetRegisterAt(0)); + entry_address = temp_is_r0 ? RegisterFrom(out) : temp; + DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); + if (temp_is_r0) { + __ Mov(entry_address, temp); + } + } dex::TypeIndex type_index = cls_->GetTypeIndex(); __ Mov(calling_convention.GetRegisterAt(0), type_index.index_); QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage @@ -417,27 +435,28 @@ class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL { CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>(); } + // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. + if (is_load_class_bss_entry) { + if (call_saves_everything_except_r0) { + // The class entry address was preserved in `entry_address` thanks to kSaveEverything. + __ Str(r0, MemOperand(entry_address)); + } else { + // For non-Baker read barrier, we need to re-calculate the address of the string entry. + 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, temp); + __ Str(r0, MemOperand(temp)); + } + } // Move the class to the desired location. - Location out = locations->Out(); if (out.IsValid()) { DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg())); arm_codegen->Move32(locations->Out(), LocationFrom(r0)); } RestoreLiveRegisters(codegen, locations); - // For HLoadClass/kBssEntry, store the resolved Class to the BSS entry. - DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_); - if (cls_ == instruction_ && cls_->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) { - DCHECK(out.IsValid()); - // 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, temp); - __ Str(OutputRegister(cls_), MemOperand(temp)); - } __ B(GetExitLabel()); } @@ -462,12 +481,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { : SlowPathCodeARMVIXL(instruction) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + DCHECK(instruction_->IsLoadString()); + DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry); LocationSummary* locations = instruction_->GetLocations(); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg())); HLoadString* load = instruction_->AsLoadString(); const dex::StringIndex string_index = load->GetStringIndex(); vixl32::Register out = OutputRegister(load); - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); constexpr bool call_saves_everything_except_r0 = (!kUseReadBarrier || kUseBakerReadBarrier); CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen); @@ -476,12 +496,16 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { InvokeRuntimeCallingConventionARMVIXL calling_convention; // In the unlucky case that the `temp` is R0, we preserve the address in `out` across - // the kSaveEverything call (or use `out` for the address after non-kSaveEverything call). - bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0))); - vixl32::Register entry_address = temp_is_r0 ? out : temp; - DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); - if (call_saves_everything_except_r0 && temp_is_r0) { - __ Mov(entry_address, temp); + // the kSaveEverything call. + vixl32::Register entry_address; + if (call_saves_everything_except_r0) { + vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); + bool temp_is_r0 = (temp.Is(calling_convention.GetRegisterAt(0))); + entry_address = temp_is_r0 ? out : temp; + DCHECK(!entry_address.Is(calling_convention.GetRegisterAt(0))); + if (temp_is_r0) { + __ Mov(entry_address, temp); + } } __ Mov(calling_convention.GetRegisterAt(0), string_index.index_); @@ -494,10 +518,13 @@ class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL { __ Str(r0, MemOperand(entry_address)); } else { // For non-Baker read barrier, we need to re-calculate the address of the string entry. + UseScratchRegisterScope temps( + down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()); + vixl32::Register temp = temps.Acquire(); CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = arm_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index); - arm_codegen->EmitMovwMovtPlaceholder(labels, out); - __ Str(r0, MemOperand(entry_address)); + arm_codegen->EmitMovwMovtPlaceholder(labels, temp); + __ Str(r0, MemOperand(temp)); } arm_codegen->Move32(locations->Out(), LocationFrom(r0)); @@ -5832,6 +5859,7 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { cls, LocationFrom(calling_convention.GetRegisterAt(0)), LocationFrom(r0)); + DCHECK(calling_convention.GetRegisterAt(0).Is(r0)); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -5849,6 +5877,22 @@ void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadClass::LoadKind::kBssEntry) { + if (!kUseReadBarrier || kUseBakerReadBarrier) { + // Rely on the type resolution or initialization and marking to save everything we need. + // Note that IP may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we request a different temp. + locations->AddTemp(Location::RequiresRegister()); + RegisterSet caller_saves = RegisterSet::Empty(); + InvokeRuntimeCallingConventionARMVIXL calling_convention; + caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0))); + // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK() + // that the the kPrimNot result register is the same as the first argument register. + locations->SetCustomSlowPathCallerSaves(caller_saves); + } else { + // For non-Baker read barrier we have a temp-clobbering call. + } + } } // NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not @@ -5906,10 +5950,13 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ break; } case HLoadClass::LoadKind::kBssEntry: { + vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) + ? RegisterFrom(locations->GetTemp(0)) + : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex()); - codegen_->EmitMovwMovtPlaceholder(labels, out); - GenerateGcRootFieldLoad(cls, out_loc, out, 0, kCompilerReadBarrierOption); + codegen_->EmitMovwMovtPlaceholder(labels, temp); + GenerateGcRootFieldLoad(cls, out_loc, temp, /* offset */ 0, read_barrier_option); generate_null_check = true; break; } @@ -5918,7 +5965,7 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_ cls->GetTypeIndex(), cls->GetClass())); // /* GcRoot<mirror::Class> */ out = *out - GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(cls, out_loc, out, /* offset */ 0, read_barrier_option); break; } case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -6012,9 +6059,9 @@ void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) { locations->SetOut(Location::RequiresRegister()); if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { - // Rely on the pResolveString and/or marking to save everything, including temps. - // Note that IP may theoretically be clobbered by saving/restoring the live register - // (only one thanks to the custom calling convention), so we request a different temp. + // Rely on the pResolveString and marking to save everything we need, including temps. + // Note that IP may be clobbered by saving/restoring the live register (only one thanks + // to the custom calling convention) or by marking, so we request a different temp. locations->AddTemp(Location::RequiresRegister()); RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConventionARMVIXL calling_convention; @@ -6059,7 +6106,9 @@ void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THRE } case HLoadString::LoadKind::kBssEntry: { DCHECK(!codegen_->GetCompilerOptions().IsBootImage()); - vixl32::Register temp = RegisterFrom(locations->GetTemp(0)); + vixl32::Register temp = (!kUseReadBarrier || kUseBakerReadBarrier) + ? RegisterFrom(locations->GetTemp(0)) + : out; CodeGeneratorARMVIXL::PcRelativePatchInfo* labels = codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex()); codegen_->EmitMovwMovtPlaceholder(labels, temp); diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 0677dad078..c9dde7cc55 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1914,6 +1914,8 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { auto null_checker = GetImplicitNullChecker(instruction); Primitive::Type type = instruction->GetType(); + const bool maybe_compressed_char_at = mirror::kUseStringCompression && + instruction->IsStringCharAt(); switch (type) { case Primitive::kPrimBoolean: { Register out = locations->Out().AsRegister<Register>(); @@ -1957,14 +1959,54 @@ void InstructionCodeGeneratorMIPS::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimChar: { Register out = locations->Out().AsRegister<Register>(); + if (maybe_compressed_char_at) { + uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + __ LoadFromOffset(kLoadWord, TMP, obj, count_offset, null_checker); + __ Sll(TMP, TMP, 31); // Extract compression flag into the most significant bit of TMP. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + } if (index.IsConstant()) { - size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; - __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset, null_checker); + int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + if (maybe_compressed_char_at) { + MipsLabel uncompressed_load, done; + __ Bnez(TMP, &uncompressed_load); + __ LoadFromOffset(kLoadUnsignedByte, + out, + obj, + data_offset + (const_index << TIMES_1)); + __ B(&done); + __ Bind(&uncompressed_load); + __ LoadFromOffset(kLoadUnsignedHalfword, + out, + obj, + data_offset + (const_index << TIMES_2)); + __ Bind(&done); + } else { + __ LoadFromOffset(kLoadUnsignedHalfword, + out, + obj, + data_offset + (const_index << TIMES_2), + null_checker); + } } else { - __ Sll(TMP, index.AsRegister<Register>(), TIMES_2); - __ Addu(TMP, obj, TMP); - __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker); + Register index_reg = index.AsRegister<Register>(); + if (maybe_compressed_char_at) { + MipsLabel uncompressed_load, done; + __ Bnez(TMP, &uncompressed_load); + __ Addu(TMP, obj, index_reg); + __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset); + __ B(&done); + __ Bind(&uncompressed_load); + __ Sll(TMP, index_reg, TIMES_2); + __ Addu(TMP, obj, TMP); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + __ Bind(&done); + } else { + __ Sll(TMP, index_reg, TIMES_2); + __ Addu(TMP, obj, TMP); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset, null_checker); + } } break; } @@ -2046,6 +2088,10 @@ void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) { Register out = locations->Out().AsRegister<Register>(); __ LoadFromOffset(kLoadWord, out, obj, offset); codegen_->MaybeRecordImplicitNullCheck(instruction); + // Mask out compression flag from String's array length. + if (mirror::kUseStringCompression && instruction->IsStringLength()) { + __ Srl(out, out, 1u); + } } Location LocationsBuilderMIPS::RegisterOrZeroConstant(HInstruction* instruction) { diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 4c8dabfede..5be0da4011 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1490,6 +1490,8 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); + const bool maybe_compressed_char_at = mirror::kUseStringCompression && + instruction->IsStringCharAt(); switch (type) { case Primitive::kPrimBoolean: { GpuRegister out = locations->Out().AsRegister<GpuRegister>(); @@ -1533,14 +1535,54 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { case Primitive::kPrimChar: { GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + if (maybe_compressed_char_at) { + uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + __ LoadFromOffset(kLoadWord, TMP, obj, count_offset); + codegen_->MaybeRecordImplicitNullCheck(instruction); + __ Dext(TMP, TMP, 0, 1); + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + } if (index.IsConstant()) { - size_t offset = - (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + data_offset; - __ LoadFromOffset(kLoadUnsignedHalfword, out, obj, offset); + int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); + if (maybe_compressed_char_at) { + Mips64Label uncompressed_load, done; + __ Bnezc(TMP, &uncompressed_load); + __ LoadFromOffset(kLoadUnsignedByte, + out, + obj, + data_offset + (const_index << TIMES_1)); + __ Bc(&done); + __ Bind(&uncompressed_load); + __ LoadFromOffset(kLoadUnsignedHalfword, + out, + obj, + data_offset + (const_index << TIMES_2)); + __ Bind(&done); + } else { + __ LoadFromOffset(kLoadUnsignedHalfword, + out, + obj, + data_offset + (const_index << TIMES_2)); + } } else { - __ Dsll(TMP, index.AsRegister<GpuRegister>(), TIMES_2); - __ Daddu(TMP, obj, TMP); - __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + GpuRegister index_reg = index.AsRegister<GpuRegister>(); + if (maybe_compressed_char_at) { + Mips64Label uncompressed_load, done; + __ Bnezc(TMP, &uncompressed_load); + __ Daddu(TMP, obj, index_reg); + __ LoadFromOffset(kLoadUnsignedByte, out, TMP, data_offset); + __ Bc(&done); + __ Bind(&uncompressed_load); + __ Dsll(TMP, index_reg, TIMES_2); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + __ Bind(&done); + } else { + __ Dsll(TMP, index_reg, TIMES_2); + __ Daddu(TMP, obj, TMP); + __ LoadFromOffset(kLoadUnsignedHalfword, out, TMP, data_offset); + } } break; } @@ -1608,7 +1650,9 @@ void InstructionCodeGeneratorMIPS64::VisitArrayGet(HArrayGet* instruction) { LOG(FATAL) << "Unreachable type " << instruction->GetType(); UNREACHABLE(); } - codegen_->MaybeRecordImplicitNullCheck(instruction); + if (!maybe_compressed_char_at) { + codegen_->MaybeRecordImplicitNullCheck(instruction); + } } void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) { @@ -1624,6 +1668,10 @@ void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction) GpuRegister out = locations->Out().AsRegister<GpuRegister>(); __ LoadFromOffset(kLoadWord, out, obj, offset); codegen_->MaybeRecordImplicitNullCheck(instruction); + // Mask out compression flag from String's array length. + if (mirror::kUseStringCompression && instruction->IsStringLength()) { + __ Srl(out, out, 1u); + } } void LocationsBuilderMIPS64::VisitArraySet(HArraySet* instruction) { diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 137b55423b..09612c8dbf 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -6057,6 +6057,7 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { cls, Location::RegisterLocation(calling_convention.GetRegisterAt(0)), Location::RegisterLocation(EAX)); + DCHECK_EQ(calling_convention.GetRegisterAt(0), EAX); return; } DCHECK(!cls->NeedsAccessCheck()); @@ -6076,6 +6077,17 @@ void LocationsBuilderX86::VisitLoadClass(HLoadClass* cls) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadClass::LoadKind::kBssEntry) { + if (!kUseReadBarrier || kUseBakerReadBarrier) { + // Rely on the type resolution and/or initialization to save everything. + RegisterSet caller_saves = RegisterSet::Empty(); + InvokeRuntimeCallingConvention calling_convention; + caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); + locations->SetCustomSlowPathCallerSaves(caller_saves); + } else { + // For non-Baker read barrier we have a temp-clobbering call. + } + } } Label* CodeGeneratorX86::NewJitRootClassPatch(const DexFile& dex_file, @@ -6158,7 +6170,7 @@ void InstructionCodeGeneratorX86::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFE Label* fixup_label = codegen_->NewJitRootClassPatch( cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); // /* GcRoot<mirror::Class> */ out = *address - GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option); break; } case HLoadClass::LoadKind::kDexCacheViaMethod: @@ -6250,7 +6262,7 @@ void LocationsBuilderX86::VisitLoadString(HLoadString* load) { locations->SetOut(Location::RequiresRegister()); if (load_kind == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { - // Rely on the pResolveString and/or marking to save everything. + // Rely on the pResolveString to save everything. RegisterSet caller_saves = RegisterSet::Empty(); InvokeRuntimeCallingConvention calling_convention; caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0))); @@ -7136,9 +7148,10 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad( // Fast path implementation of art::ReadBarrier::BarrierForRoot when // Baker's read barrier are used: // - // root = *address; - // if (Thread::Current()->GetIsGcMarking()) { - // root = ReadBarrier::Mark(root) + // root = obj.field; + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + // if (temp != null) { + // root = temp(root) // } // /* GcRoot<mirror::Object> */ root = *address @@ -7159,8 +7172,11 @@ void InstructionCodeGeneratorX86::GenerateGcRootFieldLoad( instruction, root, /* unpoison_ref_before_marking */ false); codegen_->AddSlowPath(slow_path); - __ fs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86PointerSize>().Int32Value()), - Immediate(0)); + // Test the entrypoint (`Thread::Current()->pReadBarrierMarkReg ## root.reg()`). + const int32_t entry_point_offset = + CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86PointerSize>(root.reg()); + __ fs()->cmpl(Address::Absolute(entry_point_offset), Immediate(0)); + // The entrypoint is null when the GC is not marking. __ j(kNotEqual, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } else { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index c5367ce86e..0879992e32 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -245,9 +245,8 @@ class LoadClassSlowPathX86_64 : public SlowPathCode { SaveLiveRegisters(codegen, locations); - InvokeRuntimeCallingConvention calling_convention; - __ movl(CpuRegister(calling_convention.GetRegisterAt(0)), - Immediate(cls_->GetTypeIndex().index_)); + // Custom calling convention: RAX serves as both input and output. + __ movl(CpuRegister(RAX), Immediate(cls_->GetTypeIndex().index_)); x86_64_codegen->InvokeRuntime(do_clinit_ ? kQuickInitializeStaticStorage : kQuickInitializeType, instruction_, dex_pc_, @@ -5456,10 +5455,10 @@ HLoadClass::LoadKind CodeGeneratorX86_64::GetSupportedLoadClassKind( void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { HLoadClass::LoadKind load_kind = cls->GetLoadKind(); if (load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) { - InvokeRuntimeCallingConvention calling_convention; + // Custom calling convention: RAX serves as both input and output. CodeGenerator::CreateLoadClassRuntimeCallLocationSummary( cls, - Location::RegisterLocation(calling_convention.GetRegisterAt(0)), + Location::RegisterLocation(RAX), Location::RegisterLocation(RAX)); return; } @@ -5478,6 +5477,17 @@ void LocationsBuilderX86_64::VisitLoadClass(HLoadClass* cls) { locations->SetInAt(0, Location::RequiresRegister()); } locations->SetOut(Location::RequiresRegister()); + if (load_kind == HLoadClass::LoadKind::kBssEntry) { + if (!kUseReadBarrier || kUseBakerReadBarrier) { + // Rely on the type resolution and/or initialization to save everything. + // Custom calling convention: RAX serves as both input and output. + RegisterSet caller_saves = RegisterSet::Empty(); + caller_saves.Add(Location::RegisterLocation(RAX)); + locations->SetCustomSlowPathCallerSaves(caller_saves); + } else { + // For non-Baker read barrier we have a temp-clobbering call. + } + } } Label* CodeGeneratorX86_64::NewJitRootClassPatch(const DexFile& dex_file, @@ -5553,7 +5563,7 @@ void InstructionCodeGeneratorX86_64::VisitLoadClass(HLoadClass* cls) NO_THREAD_S Label* fixup_label = codegen_->NewJitRootClassPatch(cls->GetDexFile(), cls->GetTypeIndex(), cls->GetClass()); // /* GcRoot<mirror::Class> */ out = *address - GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, kCompilerReadBarrierOption); + GenerateGcRootFieldLoad(cls, out_loc, address, fixup_label, read_barrier_option); break; } default: @@ -5629,7 +5639,7 @@ void LocationsBuilderX86_64::VisitLoadString(HLoadString* load) { locations->SetOut(Location::RequiresRegister()); if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) { if (!kUseReadBarrier || kUseBakerReadBarrier) { - // Rely on the pResolveString and/or marking to save everything. + // Rely on the pResolveString to save everything. // Custom calling convention: RAX serves as both input and output. RegisterSet caller_saves = RegisterSet::Empty(); caller_saves.Add(Location::RegisterLocation(RAX)); @@ -6501,9 +6511,10 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad( // Fast path implementation of art::ReadBarrier::BarrierForRoot when // Baker's read barrier are used: // - // root = *address; - // if (Thread::Current()->GetIsGcMarking()) { - // root = ReadBarrier::Mark(root) + // root = obj.field; + // temp = Thread::Current()->pReadBarrierMarkReg ## root.reg() + // if (temp != null) { + // root = temp(root) // } // /* GcRoot<mirror::Object> */ root = *address @@ -6524,9 +6535,11 @@ void InstructionCodeGeneratorX86_64::GenerateGcRootFieldLoad( instruction, root, /* unpoison_ref_before_marking */ false); codegen_->AddSlowPath(slow_path); - __ gs()->cmpl(Address::Absolute(Thread::IsGcMarkingOffset<kX86_64PointerSize>().Int32Value(), - /* no_rip */ true), - Immediate(0)); + // Test the `Thread::Current()->pReadBarrierMarkReg ## root.reg()` entrypoint. + const int32_t entry_point_offset = + CodeGenerator::GetReadBarrierMarkEntryPointsOffset<kX86_64PointerSize>(root.reg()); + __ gs()->cmpl(Address::Absolute(entry_point_offset, /* no_rip */ true), Immediate(0)); + // The entrypoint is null when the GC is not marking. __ j(kNotEqual, slow_path->GetEntryLabel()); __ Bind(slow_path->GetExitLabel()); } else { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index f0afccb782..2dd5fefc80 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -85,22 +85,20 @@ void HInliner::Run() { HInvoke* call = instruction->AsInvoke(); // As long as the call is not intrinsified, it is worth trying to inline. if (call != nullptr && call->GetIntrinsic() == Intrinsics::kNone) { - // We use the original invoke type to ensure the resolution of the called method - // works properly. - if (!TryInline(call)) { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool should_inline = callee_name.find("$inline$") != std::string::npos; - CHECK(!should_inline) << "Could not inline " << callee_name; + if (kIsDebugBuild && IsCompilingWithCoreImage()) { + // Debugging case: directives in method names control or assert on inlining. + std::string callee_name = outer_compilation_unit_.GetDexFile()->PrettyMethod( + call->GetDexMethodIndex(), /* with_signature */ false); + // Tests prevent inlining by having $noinline$ in their method names. + if (callee_name.find("$noinline$") == std::string::npos) { + if (!TryInline(call)) { + bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos); + CHECK(!should_have_inlined) << "Could not inline " << callee_name; + } } } else { - if (kIsDebugBuild && IsCompilingWithCoreImage()) { - std::string callee_name = - outer_compilation_unit_.GetDexFile()->PrettyMethod(call->GetDexMethodIndex()); - bool must_not_inline = callee_name.find("$noinline$") != std::string::npos; - CHECK(!must_not_inline) << "Should not have inlined " << callee_name; - } + // Normal case: try to inline. + TryInline(call); } } instruction = next; @@ -380,7 +378,7 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { soa.Self(), class_linker->GetClassRoot(ClassLinker::kClassArrayClass), InlineCache::kIndividualCacheSize)); - if (inline_cache.Get() == nullptr) { + if (inline_cache == nullptr) { // We got an OOME. Just clear the exception, and don't inline. DCHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index a1c391f455..3374e42955 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -680,7 +680,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in Handle<mirror::Class> methods_class(hs.NewHandle(class_linker->ResolveReferencedClassOfMethod( method_idx, dex_compilation_unit_->GetDexCache(), class_loader))); - if (UNLIKELY(methods_class.Get() == nullptr)) { + if (UNLIKELY(methods_class == nullptr)) { // Clean up any exception left by type resolution. soa.Self()->ClearException(); return nullptr; @@ -702,7 +702,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // Check access. The class linker has a fast path for looking into the dex cache // and does not check the access if it hits it. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_method->IsPublic()) { return nullptr; } @@ -718,7 +718,7 @@ ArtMethod* HInstructionBuilder::ResolveMethod(uint16_t method_idx, InvokeType in // make this an invoke-unresolved to handle cross-dex invokes or abstract super methods, both of // which require runtime handling. if (invoke_type == kSuper) { - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { // We could not determine the method's class we need to wait until runtime. DCHECK(Runtime::Current()->IsAotCompiler()); return nullptr; @@ -954,7 +954,7 @@ bool HInstructionBuilder::BuildNewInstance(dex::TypeIndex type_index, uint32_t d } // Consider classes we haven't resolved as potentially finalizable. - bool finalizable = (klass.Get() == nullptr) || klass->IsFinalizable(); + bool finalizable = (klass == nullptr) || klass->IsFinalizable(); AppendInstruction(new (arena_) HNewInstance( cls, @@ -972,7 +972,7 @@ static bool IsSubClass(mirror::Class* to_test, mirror::Class* super_class) } bool HInstructionBuilder::IsInitialized(Handle<mirror::Class> cls) const { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return false; } @@ -1292,7 +1292,7 @@ bool HInstructionBuilder::IsOutermostCompilingClass(dex::TypeIndex type_index) c // When this happens we cannot establish a direct relation between the current // class and the outer class, so we return false. // (Note that this is only used for optimizing invokes and field accesses) - return (cls.Get() != nullptr) && (outer_class.Get() == cls.Get()); + return (cls != nullptr) && (outer_class.Get() == cls.Get()); } void HInstructionBuilder::BuildUnresolvedStaticFieldAccess(const Instruction& instruction, @@ -1340,7 +1340,7 @@ ArtField* HInstructionBuilder::ResolveField(uint16_t field_idx, bool is_static, } // Check access. - if (compiling_class.Get() == nullptr) { + if (compiling_class == nullptr) { if (!resolved_field->IsPublic()) { return nullptr; } @@ -1612,7 +1612,7 @@ void HInstructionBuilder::BuildFillWideArrayData(HInstruction* object, static TypeCheckKind ComputeTypeCheckKind(Handle<mirror::Class> cls) REQUIRES_SHARED(Locks::mutator_lock_) { - if (cls.Get() == nullptr) { + if (cls == nullptr) { return TypeCheckKind::kUnresolvedCheck; } else if (cls->IsInterface()) { return TypeCheckKind::kInterfaceCheck; @@ -1643,7 +1643,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, uint3 soa, dex_compilation_unit_->GetDexCache(), class_loader, type_index, dex_compilation_unit_)); bool needs_access_check = true; - if (klass.Get() != nullptr) { + if (klass != nullptr) { if (klass->IsPublic()) { needs_access_check = false; } else { @@ -1679,7 +1679,7 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, type_index, *actual_dex_file, klass, - klass.Get() != nullptr && (klass.Get() == GetOutermostCompilingClass()), + klass != nullptr && (klass.Get() == GetOutermostCompilingClass()), dex_pc, needs_access_check); diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc index bbf826ce7e..86e54294ae 100644 --- a/compiler/optimizing/intrinsics_arm64.cc +++ b/compiler/optimizing/intrinsics_arm64.cc @@ -23,7 +23,7 @@ #include "entrypoints/quick/quick_entrypoints.h" #include "intrinsics.h" #include "mirror/array-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" @@ -115,13 +115,18 @@ class IntrinsicSlowPathARM64 : public SlowPathCodeARM64 { MoveArguments(invoke_, codegen); - if (invoke_->IsInvokeStaticOrDirect()) { - codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), - LocationFrom(kArtMethodRegister)); - } else { - codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister)); + { + // Ensure that between the BLR (emitted by Generate*Call) and RecordPcInfo there + // are no pools emitted. + vixl::EmissionCheckScope guard(codegen->GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes); + if (invoke_->IsInvokeStaticOrDirect()) { + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), + LocationFrom(kArtMethodRegister)); + } else { + codegen->GenerateVirtualCall(invoke_->AsInvokeVirtual(), LocationFrom(kArtMethodRegister)); + } + codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); } - codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); // Copy the result back to the expected output. Location out = invoke_->GetLocations()->Out(); @@ -980,11 +985,12 @@ void IntrinsicLocationsBuilderARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) CreateIntIntIntIntToVoid(arena_, invoke); } -static void GenUnsafePut(LocationSummary* locations, +static void GenUnsafePut(HInvoke* invoke, Primitive::Type type, bool is_volatile, bool is_ordered, CodeGeneratorARM64* codegen) { + LocationSummary* locations = invoke->GetLocations(); MacroAssembler* masm = codegen->GetVIXLAssembler(); Register base = WRegisterFrom(locations->InAt(1)); // Object pointer. @@ -1007,7 +1013,7 @@ static void GenUnsafePut(LocationSummary* locations, } if (is_volatile || is_ordered) { - codegen->StoreRelease(type, source, mem_op); + codegen->StoreRelease(invoke, type, source, mem_op, /* needs_null_check */ false); } else { codegen->Store(type, source, mem_op); } @@ -1020,63 +1026,63 @@ static void GenUnsafePut(LocationSummary* locations, } void IntrinsicCodeGeneratorARM64::VisitUnsafePut(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimInt, /* is_volatile */ false, /* is_ordered */ false, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutOrdered(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimInt, /* is_volatile */ false, /* is_ordered */ true, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutVolatile(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimInt, /* is_volatile */ true, /* is_ordered */ false, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutObject(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimNot, /* is_volatile */ false, /* is_ordered */ false, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectOrdered(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimNot, /* is_volatile */ false, /* is_ordered */ true, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutObjectVolatile(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimNot, /* is_volatile */ true, /* is_ordered */ false, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutLong(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimLong, /* is_volatile */ false, /* is_ordered */ false, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongOrdered(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimLong, /* is_volatile */ false, /* is_ordered */ true, codegen_); } void IntrinsicCodeGeneratorARM64::VisitUnsafePutLongVolatile(HInvoke* invoke) { - GenUnsafePut(invoke->GetLocations(), + GenUnsafePut(invoke, Primitive::kPrimLong, /* is_volatile */ true, /* is_ordered */ false, @@ -1444,16 +1450,47 @@ void IntrinsicCodeGeneratorARM64::VisitStringCompareTo(HInvoke* invoke) { } } +// The cut off for unrolling the loop in String.equals() intrinsic for const strings. +// The normal loop plus the pre-header is 9 instructions without string compression and 12 +// instructions with string compression. We can compare up to 8 bytes in 4 instructions +// (LDR+LDR+CMP+BNE) and up to 16 bytes in 5 instructions (LDP+LDP+CMP+CCMP+BNE). Allow up +// to 10 instructions for the unrolled loop. +constexpr size_t kShortConstStringEqualsCutoffInBytes = 32; + +static const char* GetConstString(HInstruction* candidate, uint32_t* utf16_length) { + if (candidate->IsLoadString()) { + HLoadString* load_string = candidate->AsLoadString(); + const DexFile& dex_file = load_string->GetDexFile(); + return dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), utf16_length); + } + return nullptr; +} + void IntrinsicLocationsBuilderARM64::VisitStringEquals(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // Temporary registers to store lengths of strings and for calculations. - locations->AddTemp(Location::RequiresRegister()); - locations->AddTemp(Location::RequiresRegister()); + // For the generic implementation and for long const strings we need a temporary. + // We do not need it for short const strings, up to 8 bytes, see code generation below. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + if (const_string == nullptr || const_string_length > (is_compressed ? 8u : 4u)) { + locations->AddTemp(Location::RequiresRegister()); + } + + // TODO: If the String.equals() is used only for an immediately following HIf, we can + // mark it as emitted-at-use-site and emit branches directly to the appropriate blocks. + // Then we shall need an extra temporary register instead of the output register. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -1467,8 +1504,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { UseScratchRegisterScope scratch_scope(masm); Register temp = scratch_scope.AcquireW(); - Register temp1 = WRegisterFrom(locations->GetTemp(0)); - Register temp2 = WRegisterFrom(locations->GetTemp(1)); + Register temp1 = scratch_scope.AcquireW(); vixl::aarch64::Label loop; vixl::aarch64::Label end; @@ -1504,46 +1540,98 @@ void IntrinsicCodeGeneratorARM64::VisitStringEquals(HInvoke* invoke) { __ B(&return_false, ne); } - // Load `count` fields of this and argument strings. - __ Ldr(temp, MemOperand(str.X(), count_offset)); - __ Ldr(temp1, MemOperand(arg.X(), count_offset)); - // Check if `count` fields are equal, return false if they're not. - // Also compares the compression style, if differs return false. - __ Cmp(temp, temp1); - __ B(&return_false, ne); - // Return true if both strings are empty. Even with string compression `count == 0` means empty. - static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, - "Expecting 0=compressed, 1=uncompressed"); - __ Cbz(temp, &return_true); + // Check if one of the inputs is a const string. Do not special-case both strings + // being const, such cases should be handled by constant folding if needed. + uint32_t const_string_length = 0u; + const char* const_string = GetConstString(invoke->InputAt(0), &const_string_length); + if (const_string == nullptr) { + const_string = GetConstString(invoke->InputAt(1), &const_string_length); + if (const_string != nullptr) { + std::swap(str, arg); // Make sure the const string is in `str`. + } + } + bool is_compressed = + mirror::kUseStringCompression && + const_string != nullptr && + mirror::String::DexFileStringAllASCII(const_string, const_string_length); + + if (const_string != nullptr) { + // Load `count` field of the argument string and check if it matches the const string. + // Also compares the compression style, if differs return false. + __ Ldr(temp, MemOperand(arg.X(), count_offset)); + __ Cmp(temp, Operand(mirror::String::GetFlaggedCount(const_string_length, is_compressed))); + __ B(&return_false, ne); + } else { + // Load `count` fields of this and argument strings. + __ Ldr(temp, MemOperand(str.X(), count_offset)); + __ Ldr(temp1, MemOperand(arg.X(), count_offset)); + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } // Assertions that must hold in order to compare strings 8 bytes at a time. DCHECK_ALIGNED(value_offset, 8); static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); - if (mirror::kUseStringCompression) { - // For string compression, calculate the number of bytes to compare (not chars). - // This could in theory exceed INT32_MAX, so treat temp as unsigned. - __ Lsr(temp, temp, 1u); // Extract length. - __ And(temp1, temp1, Operand(1)); // Extract compression flag. - __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. - } - - // Store offset of string value in preparation for comparison loop - __ Mov(temp1, value_offset); + if (const_string != nullptr && + const_string_length < (is_compressed ? kShortConstStringEqualsCutoffInBytes + : kShortConstStringEqualsCutoffInBytes / 2u)) { + // Load and compare the contents. Though we know the contents of the short const string + // at compile time, materializing constants may be more code than loading from memory. + int32_t offset = value_offset; + size_t remaining_bytes = + RoundUp(is_compressed ? const_string_length : const_string_length * 2u, 8u); + temp = temp.X(); + temp1 = temp1.X(); + while (remaining_bytes > 8u) { + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + __ Ldp(temp, temp1, MemOperand(str.X(), offset)); + __ Ldp(temp2, out, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp2); + __ Ccmp(temp1, out, NoFlag, eq); + __ B(&return_false, ne); + offset += 2u * sizeof(uint64_t); + remaining_bytes -= 2u * sizeof(uint64_t); + } + if (remaining_bytes != 0u) { + __ Ldr(temp, MemOperand(str.X(), offset)); + __ Ldr(temp1, MemOperand(arg.X(), offset)); + __ Cmp(temp, temp1); + __ B(&return_false, ne); + } + } else { + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); + __ Cbz(temp, &return_true); + + if (mirror::kUseStringCompression) { + // For string compression, calculate the number of bytes to compare (not chars). + // This could in theory exceed INT32_MAX, so treat temp as unsigned. + __ And(temp1, temp, Operand(1)); // Extract compression flag. + __ Lsr(temp, temp, 1u); // Extract length. + __ Lsl(temp, temp, temp1); // Calculate number of bytes to compare. + } - temp1 = temp1.X(); - temp2 = temp2.X(); - // Loop to compare strings 8 bytes at a time starting at the front of the string. - // Ok to do this because strings are zero-padded to kObjectAlignment. - __ Bind(&loop); - __ Ldr(out, MemOperand(str.X(), temp1)); - __ Ldr(temp2, MemOperand(arg.X(), temp1)); - __ Add(temp1, temp1, Operand(sizeof(uint64_t))); - __ Cmp(out, temp2); - __ B(&return_false, ne); - // With string compression, we have compared 8 bytes, otherwise 4 chars. - __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); - __ B(&loop, hi); + // Store offset of string value in preparation for comparison loop + __ Mov(temp1, value_offset); + + temp1 = temp1.X(); + Register temp2 = XRegisterFrom(locations->GetTemp(0)); + // Loop to compare strings 8 bytes at a time starting at the front of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. + __ Bind(&loop); + __ Ldr(out, MemOperand(str.X(), temp1)); + __ Ldr(temp2, MemOperand(arg.X(), temp1)); + __ Add(temp1, temp1, Operand(sizeof(uint64_t))); + __ Cmp(out, temp2); + __ B(&return_false, ne); + // With string compression, we have compared 8 bytes, otherwise 4 chars. + __ Sub(temp, temp, Operand(mirror::kUseStringCompression ? 8 : 4), SetFlags); + __ B(&loop, hi); + } // Return true and exit the function. // If loop does not result in returning false, we return true. @@ -2825,9 +2913,13 @@ void IntrinsicCodeGeneratorARM64::VisitReferenceGetReferent(HInvoke* invoke) { } __ Cbnz(temp0, slow_path->GetEntryLabel()); - // Fast path. - __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value())); - codegen_->MaybeRecordImplicitNullCheck(invoke); + { + // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted. + vixl::EmissionCheckScope guard(codegen_->GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes); + // Fast path. + __ Ldr(out, HeapOperand(obj, mirror::Reference::ReferentOffset().Int32Value())); + codegen_->MaybeRecordImplicitNullCheck(invoke); + } codegen_->GetAssembler()->MaybeUnpoisonHeapReference(out); __ Bind(slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index 6cf9b83d44..64a68403e9 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -2004,31 +2004,48 @@ void IntrinsicCodeGeneratorMIPS::VisitStringEquals(HInvoke* invoke) { __ Lw(temp2, arg, class_offset); __ Bne(temp1, temp2, &return_false); - // Load lengths of this and argument strings. + // Load `count` fields of this and argument strings. __ Lw(temp1, str, count_offset); __ Lw(temp2, arg, count_offset); - // Check if lengths are equal, return false if they're not. + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. __ Bne(temp1, temp2, &return_false); - // Return true if both strings are empty. + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); __ Beqz(temp1, &return_true); // Don't overwrite input registers __ Move(TMP, str); __ Move(temp3, arg); - // Assertions that must hold in order to compare strings 2 characters at a time. + // Assertions that must hold in order to compare strings 4 bytes at a time. DCHECK_ALIGNED(value_offset, 4); static_assert(IsAligned<4>(kObjectAlignment), "String of odd length is not zero padded"); - // Loop to compare strings 2 characters at a time starting at the beginning of the string. - // Ok to do this because strings are zero-padded. + // For string compression, calculate the number of bytes to compare (not chars). + if (mirror::kUseStringCompression) { + // Extract compression flag. + if (IsR2OrNewer()) { + __ Ext(temp2, temp1, 0, 1); + } else { + __ Sll(temp2, temp1, 31); + __ Srl(temp2, temp2, 31); + } + __ Srl(temp1, temp1, 1); // Extract length. + __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed. + } + + // Loop to compare strings 4 bytes at a time starting at the beginning of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. __ Bind(&loop); __ Lw(out, TMP, value_offset); __ Lw(temp2, temp3, value_offset); __ Bne(out, temp2, &return_false); __ Addiu(TMP, TMP, 4); __ Addiu(temp3, temp3, 4); - __ Addiu(temp1, temp1, -2); + // With string compression, we have compared 4 bytes, otherwise 2 chars. + __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -4 : -2); __ Bgtz(temp1, &loop); // Return true and exit the function. @@ -2578,6 +2595,30 @@ void IntrinsicCodeGeneratorMIPS::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Addu(dstPtr, dstPtr, AT); } + if (mirror::kUseStringCompression) { + MipsLabel uncompressed_copy, compressed_loop; + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + // Load count field and extract compression flag. + __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset); + __ Sll(TMP, TMP, 31); + + // If string is uncompressed, use memcpy() path. + __ Bnez(TMP, &uncompressed_copy); + + // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time. + __ Addu(srcPtr, srcObj, srcBegin); + __ Bind(&compressed_loop); + __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset); + __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0); + __ Addiu(numChrs, numChrs, -1); + __ Addiu(srcPtr, srcPtr, 1); + __ Addiu(dstPtr, dstPtr, 2); + __ Bnez(numChrs, &compressed_loop); + + __ B(&done); + __ Bind(&uncompressed_copy); + } + // Calculate source address. __ Addiu(srcPtr, srcObj, value_offset); if (IsR6()) { diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 00a1fa11bb..3888828722 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1607,31 +1607,42 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringEquals(HInvoke* invoke) { __ Lw(temp2, arg, class_offset); __ Bnec(temp1, temp2, &return_false); - // Load lengths of this and argument strings. + // Load `count` fields of this and argument strings. __ Lw(temp1, str, count_offset); __ Lw(temp2, arg, count_offset); - // Check if lengths are equal, return false if they're not. + // Check if `count` fields are equal, return false if they're not. + // Also compares the compression style, if differs return false. __ Bnec(temp1, temp2, &return_false); - // Return true if both strings are empty. + // Return true if both strings are empty. Even with string compression `count == 0` means empty. + static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, + "Expecting 0=compressed, 1=uncompressed"); __ Beqzc(temp1, &return_true); // Don't overwrite input registers __ Move(TMP, str); __ Move(temp3, arg); - // Assertions that must hold in order to compare strings 4 characters at a time. + // Assertions that must hold in order to compare strings 8 bytes at a time. DCHECK_ALIGNED(value_offset, 8); static_assert(IsAligned<8>(kObjectAlignment), "String of odd length is not zero padded"); - // Loop to compare strings 4 characters at a time starting at the beginning of the string. - // Ok to do this because strings are zero-padded to be 8-byte aligned. + if (mirror::kUseStringCompression) { + // For string compression, calculate the number of bytes to compare (not chars). + __ Dext(temp2, temp1, 0, 1); // Extract compression flag. + __ Srl(temp1, temp1, 1); // Extract length. + __ Sllv(temp1, temp1, temp2); // Double the byte count if uncompressed. + } + + // Loop to compare strings 8 bytes at a time starting at the beginning of the string. + // Ok to do this because strings are zero-padded to kObjectAlignment. __ Bind(&loop); __ Ld(out, TMP, value_offset); __ Ld(temp2, temp3, value_offset); __ Bnec(out, temp2, &return_false); __ Daddiu(TMP, TMP, 8); __ Daddiu(temp3, temp3, 8); - __ Addiu(temp1, temp1, -4); + // With string compression, we have compared 8 bytes, otherwise 4 chars. + __ Addiu(temp1, temp1, mirror::kUseStringCompression ? -8 : -4); __ Bgtzc(temp1, &loop); // Return true and exit the function. @@ -1912,6 +1923,30 @@ void IntrinsicCodeGeneratorMIPS64::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ Daddiu(dstPtr, dstObj, data_offset); __ Dlsa(dstPtr, dstBegin, dstPtr, char_shift); + if (mirror::kUseStringCompression) { + Mips64Label uncompressed_copy, compressed_loop; + const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); + // Load count field and extract compression flag. + __ LoadFromOffset(kLoadWord, TMP, srcObj, count_offset); + __ Dext(TMP, TMP, 0, 1); + + // If string is uncompressed, use memcpy() path. + __ Bnezc(TMP, &uncompressed_copy); + + // Copy loop for compressed src, copying 1 character (8-bit) to (16-bit) at a time. + __ Daddu(srcPtr, srcObj, srcBegin); + __ Bind(&compressed_loop); + __ LoadFromOffset(kLoadUnsignedByte, TMP, srcPtr, value_offset); + __ StoreToOffset(kStoreHalfword, TMP, dstPtr, 0); + __ Daddiu(numChrs, numChrs, -1); + __ Daddiu(srcPtr, srcPtr, 1); + __ Daddiu(dstPtr, dstPtr, 2); + __ Bnezc(numChrs, &compressed_loop); + + __ Bc(&done); + __ Bind(&uncompressed_copy); + } + // Calculate source address. __ Daddiu(srcPtr, srcObj, value_offset); __ Dlsa(srcPtr, srcBegin, srcPtr, char_shift); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 727ca7d893..8638e346fb 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -1214,7 +1214,7 @@ bool OptimizingCompiler::JitCompile(Thread* self, Handle<mirror::ObjectArray<mirror::Object>> roots( hs.NewHandle(mirror::ObjectArray<mirror::Object>::Alloc( self, class_linker->GetClassRoot(ClassLinker::kObjectArrayClass), number_of_roots))); - if (roots.Get() == nullptr) { + if (roots == nullptr) { // Out of memory, just clear the exception to avoid any Java exception uncaught problems. DCHECK(self->IsExceptionPending()); self->ClearException(); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index f07f02a719..be400925d5 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -163,7 +163,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, if (!compiler_driver->GetSupportBootImageFixup()) { // compiler_driver_test. Do not sharpen. desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; - } else if ((klass.Get() != nullptr) && compiler_driver->IsImageClass( + } else if ((klass != nullptr) && compiler_driver->IsImageClass( dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { is_in_boot_image = true; desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() @@ -175,7 +175,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, desired_load_kind = HLoadClass::LoadKind::kBssEntry; } } else { - is_in_boot_image = (klass.Get() != nullptr) && + is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. @@ -183,7 +183,7 @@ HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, if (is_in_boot_image) { // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; - } else if (klass.Get() != nullptr) { + } else if (klass != nullptr) { desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; } else { // Class not loaded yet. This happens when the dex code requesting diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index f8e01b7537..1bcc8e1ace 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -38,19 +38,14 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_entry_.native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); current_entry_.register_mask = register_mask; current_entry_.sp_mask = sp_mask; - current_entry_.num_dex_registers = num_dex_registers; current_entry_.inlining_depth = inlining_depth; - current_entry_.dex_register_locations_start_index = dex_register_locations_.size(); current_entry_.inline_infos_start_index = inline_infos_.size(); - current_entry_.dex_register_map_hash = 0; - current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; current_entry_.stack_mask_index = 0; - if (num_dex_registers != 0) { - current_entry_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_entry_.live_dex_registers_mask = nullptr; - } + current_entry_.dex_register_entry.num_dex_registers = num_dex_registers; + current_entry_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_entry_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; if (sp_mask != nullptr) { stack_mask_max_ = std::max(stack_mask_max_, sp_mask->GetHighestBitSet()); @@ -65,7 +60,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, } void StackMapStream::EndStackMapEntry() { - current_entry_.same_dex_register_map_as_ = FindEntryWithTheSameDexMap(); + current_entry_.dex_register_map_index = AddDexRegisterMapEntry(current_entry_.dex_register_entry); stack_maps_.push_back(current_entry_); current_entry_ = StackMapEntry(); } @@ -91,19 +86,15 @@ void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t dex_register_locations_.push_back(index); location_catalog_entries_indices_.Insert(std::make_pair(location, index)); } - - if (in_inline_frame_) { - // TODO: Support sharing DexRegisterMap across InlineInfo. - DCHECK_LT(current_dex_register_, current_inline_info_.num_dex_registers); - current_inline_info_.live_dex_registers_mask->SetBit(current_dex_register_); - } else { - DCHECK_LT(current_dex_register_, current_entry_.num_dex_registers); - current_entry_.live_dex_registers_mask->SetBit(current_dex_register_); - current_entry_.dex_register_map_hash += (1 << - (current_dex_register_ % (sizeof(current_entry_.dex_register_map_hash) * kBitsPerByte))); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(value); - current_entry_.dex_register_map_hash += static_cast<uint32_t>(kind); - } + DexRegisterMapEntry* const entry = in_inline_frame_ + ? ¤t_inline_info_.dex_register_entry + : ¤t_entry_.dex_register_entry; + DCHECK_LT(current_dex_register_, entry->num_dex_registers); + entry->live_dex_registers_mask->SetBit(current_dex_register_); + entry->hash += (1 << + (current_dex_register_ % (sizeof(DexRegisterMapEntry::hash) * kBitsPerByte))); + entry->hash += static_cast<uint32_t>(value); + entry->hash += static_cast<uint32_t>(kind); } current_dex_register_++; } @@ -124,20 +115,19 @@ void StackMapStream::BeginInlineInfoEntry(ArtMethod* method, current_inline_info_.method_index = method->GetDexMethodIndexUnchecked(); } current_inline_info_.dex_pc = dex_pc; - current_inline_info_.num_dex_registers = num_dex_registers; - current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); - if (num_dex_registers != 0) { - current_inline_info_.live_dex_registers_mask = - ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); - } else { - current_inline_info_.live_dex_registers_mask = nullptr; - } + current_inline_info_.dex_register_entry.num_dex_registers = num_dex_registers; + current_inline_info_.dex_register_entry.locations_start_index = dex_register_locations_.size(); + current_inline_info_.dex_register_entry.live_dex_registers_mask = (num_dex_registers != 0) + ? ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream) + : nullptr; current_dex_register_ = 0; } void StackMapStream::EndInlineInfoEntry() { + current_inline_info_.dex_register_map_index = + AddDexRegisterMapEntry(current_inline_info_.dex_register_entry); DCHECK(in_inline_frame_); - DCHECK_EQ(current_dex_register_, current_inline_info_.num_dex_registers) + DCHECK_EQ(current_dex_register_, current_inline_info_.dex_register_entry.num_dex_registers) << "Inline information contains less registers than expected"; in_inline_frame_ = false; inline_infos_.push_back(current_inline_info_); @@ -193,8 +183,7 @@ size_t StackMapStream::ComputeDexRegisterLocationCatalogSize() const { return size; } -size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const { +size_t StackMapStream::DexRegisterMapEntry::ComputeSize(size_t catalog_size) const { // For num_dex_registers == 0u live_dex_registers_mask may be null. if (num_dex_registers == 0u) { return 0u; // No register map will be emitted. @@ -208,8 +197,7 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, // Compute the size of the set of live Dex register entries. size_t number_of_live_dex_registers = live_dex_registers_mask->NumSetBits(); size_t map_entries_size_in_bits = - DexRegisterMap::SingleEntrySizeInBits(location_catalog_entries_.size()) - * number_of_live_dex_registers; + DexRegisterMap::SingleEntrySizeInBits(catalog_size) * number_of_live_dex_registers; size_t map_entries_size_in_bytes = RoundUp(map_entries_size_in_bits, kBitsPerByte) / kBitsPerByte; size += map_entries_size_in_bytes; @@ -218,18 +206,8 @@ size_t StackMapStream::ComputeDexRegisterMapSize(uint32_t num_dex_registers, size_t StackMapStream::ComputeDexRegisterMapsSize() const { size_t size = 0; - size_t inline_info_index = 0; - for (const StackMapEntry& entry : stack_maps_) { - if (entry.same_dex_register_map_as_ == kNoSameDexMapFound) { - size += ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask); - } else { - // Entries with the same dex map will have the same offset. - } - for (size_t j = 0; j < entry.inlining_depth; ++j) { - InlineInfoEntry inline_entry = inline_infos_[inline_info_index++]; - size += ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask); - } + for (const DexRegisterMapEntry& entry : dex_register_entries_) { + size += entry.ComputeSize(location_catalog_entries_.size()); } return size; } @@ -264,6 +242,30 @@ void StackMapStream::ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, encoding->SetFromSizes(method_index_max, dex_pc_max, extra_data_max, dex_register_maps_bytes); } +size_t StackMapStream::MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region) { + DCHECK(current_offset != nullptr); + if ((entry.num_dex_registers == 0) || (entry.live_dex_registers_mask->NumSetBits() == 0)) { + // No dex register map needed. + return StackMap::kNoDexRegisterMap; + } + if (entry.offset == DexRegisterMapEntry::kOffsetUnassigned) { + // Not already copied, need to copy and and assign an offset. + entry.offset = *current_offset; + const size_t entry_size = entry.ComputeSize(location_catalog_entries_.size()); + DexRegisterMap dex_register_map( + dex_register_locations_region.Subregion(entry.offset, entry_size)); + *current_offset += entry_size; + // Fill in the map since it was just added. + FillInDexRegisterMap(dex_register_map, + entry.num_dex_registers, + *entry.live_dex_registers_mask, + entry.locations_start_index); + } + return entry.offset; +} + void StackMapStream::FillIn(MemoryRegion region) { DCHECK_EQ(0u, current_entry_.dex_pc) << "EndStackMapEntry not called after BeginStackMapEntry"; DCHECK_NE(0u, needed_size_) << "PrepareForFillIn not called before FillIn"; @@ -311,35 +313,10 @@ void StackMapStream::FillIn(MemoryRegion region) { stack_map.SetRegisterMaskIndex(encoding.stack_map.encoding, entry.register_mask_index); stack_map.SetStackMaskIndex(encoding.stack_map.encoding, entry.stack_mask_index); - if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) { - // No dex map available. - stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, StackMap::kNoDexRegisterMap); - } else { - // Search for an entry with the same dex map. - if (entry.same_dex_register_map_as_ != kNoSameDexMapFound) { - // If we have a hit reuse the offset. - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - code_info.GetStackMapAt(entry.same_dex_register_map_as_, encoding) - .GetDexRegisterMapOffset(encoding.stack_map.encoding)); - } else { - // New dex registers maps should be added to the stack map. - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(entry.num_dex_registers, entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - stack_map.SetDexRegisterMapOffset( - encoding.stack_map.encoding, - register_region.begin() - dex_register_locations_region.begin()); - - // Set the dex register location. - FillInDexRegisterMap(dex_register_map, - entry.num_dex_registers, - *entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); - } - } + size_t offset = MaybeCopyDexRegisterMap(dex_register_entries_[entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + stack_map.SetDexRegisterMapOffset(encoding.stack_map.encoding, offset); // Set the inlining info. if (entry.inlining_depth != 0) { @@ -371,29 +348,13 @@ void StackMapStream::FillIn(MemoryRegion region) { inline_info.SetExtraDataAtDepth(encoding.inline_info.encoding, depth, 1); } inline_info.SetDexPcAtDepth(encoding.inline_info.encoding, depth, inline_entry.dex_pc); - if (inline_entry.num_dex_registers == 0) { - // No dex map available. - inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, - depth, - StackMap::kNoDexRegisterMap); - DCHECK(inline_entry.live_dex_registers_mask == nullptr); - } else { - MemoryRegion register_region = dex_register_locations_region.Subregion( - next_dex_register_map_offset, - ComputeDexRegisterMapSize(inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask)); - next_dex_register_map_offset += register_region.size(); - DexRegisterMap dex_register_map(register_region); - inline_info.SetDexRegisterMapOffsetAtDepth( - encoding.inline_info.encoding, - depth, - register_region.begin() - dex_register_locations_region.begin()); - - FillInDexRegisterMap(dex_register_map, - inline_entry.num_dex_registers, - *inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); - } + size_t dex_register_map_offset = MaybeCopyDexRegisterMap( + dex_register_entries_[inline_entry.dex_register_map_index], + &next_dex_register_map_offset, + dex_register_locations_region); + inline_info.SetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, + depth, + dex_register_map_offset); } } else if (encoding.stack_map.encoding.GetInlineInfoEncoding().BitSize() > 0) { stack_map.SetInlineInfoIndex(encoding.stack_map.encoding, StackMap::kNoInlineInfo); @@ -448,34 +409,31 @@ void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map, } } -size_t StackMapStream::FindEntryWithTheSameDexMap() { - size_t current_entry_index = stack_maps_.size(); - auto entries_it = dex_map_hash_to_stack_map_indices_.find(current_entry_.dex_register_map_hash); +size_t StackMapStream::AddDexRegisterMapEntry(const DexRegisterMapEntry& entry) { + const size_t current_entry_index = dex_register_entries_.size(); + auto entries_it = dex_map_hash_to_stack_map_indices_.find(entry.hash); if (entries_it == dex_map_hash_to_stack_map_indices_.end()) { // We don't have a perfect hash functions so we need a list to collect all stack maps // which might have the same dex register map. ArenaVector<uint32_t> stack_map_indices(allocator_->Adapter(kArenaAllocStackMapStream)); stack_map_indices.push_back(current_entry_index); - dex_map_hash_to_stack_map_indices_.Put(current_entry_.dex_register_map_hash, - std::move(stack_map_indices)); - return kNoSameDexMapFound; - } - - // We might have collisions, so we need to check whether or not we really have a match. - for (uint32_t test_entry_index : entries_it->second) { - if (HaveTheSameDexMaps(GetStackMap(test_entry_index), current_entry_)) { - return test_entry_index; + dex_map_hash_to_stack_map_indices_.Put(entry.hash, std::move(stack_map_indices)); + } else { + // We might have collisions, so we need to check whether or not we really have a match. + for (uint32_t test_entry_index : entries_it->second) { + if (DexRegisterMapEntryEquals(dex_register_entries_[test_entry_index], entry)) { + return test_entry_index; + } } + entries_it->second.push_back(current_entry_index); } - entries_it->second.push_back(current_entry_index); - return kNoSameDexMapFound; + dex_register_entries_.push_back(entry); + return current_entry_index; } -bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const { - if (a.live_dex_registers_mask == nullptr && b.live_dex_registers_mask == nullptr) { - return true; - } - if (a.live_dex_registers_mask == nullptr || b.live_dex_registers_mask == nullptr) { +bool StackMapStream::DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, + const DexRegisterMapEntry& b) const { + if ((a.live_dex_registers_mask == nullptr) != (b.live_dex_registers_mask == nullptr)) { return false; } if (a.num_dex_registers != b.num_dex_registers) { @@ -489,12 +447,12 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn } size_t number_of_live_dex_registers = a.live_dex_registers_mask->NumSetBits(); DCHECK_LE(number_of_live_dex_registers, dex_register_locations_.size()); - DCHECK_LE(a.dex_register_locations_start_index, + DCHECK_LE(a.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - DCHECK_LE(b.dex_register_locations_start_index, + DCHECK_LE(b.locations_start_index, dex_register_locations_.size() - number_of_live_dex_registers); - auto a_begin = dex_register_locations_.begin() + a.dex_register_locations_start_index; - auto b_begin = dex_register_locations_.begin() + b.dex_register_locations_start_index; + auto a_begin = dex_register_locations_.begin() + a.locations_start_index; + auto b_begin = dex_register_locations_.begin() + b.locations_start_index; if (!std::equal(a_begin, a_begin + number_of_live_dex_registers, b_begin)) { return false; } @@ -597,10 +555,10 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapOf( - stack_map, encoding, entry.num_dex_registers), - entry.num_dex_registers, - entry.live_dex_registers_mask, - entry.dex_register_locations_start_index); + stack_map, encoding, entry.dex_register_entry.num_dex_registers), + entry.dex_register_entry.num_dex_registers, + entry.dex_register_entry.live_dex_registers_mask, + entry.dex_register_entry.locations_start_index); // Check inline info. DCHECK_EQ(stack_map.HasInlineInfo(stack_map_encoding), (entry.inlining_depth != 0)); @@ -623,10 +581,13 @@ void StackMapStream::CheckCodeInfo(MemoryRegion region) const { CheckDexRegisterMap(code_info, code_info.GetDexRegisterMapAtDepth( - d, inline_info, encoding, inline_entry.num_dex_registers), - inline_entry.num_dex_registers, - inline_entry.live_dex_registers_mask, - inline_entry.dex_register_locations_start_index); + d, + inline_info, + encoding, + inline_entry.dex_register_entry.num_dex_registers), + inline_entry.dex_register_entry.num_dex_registers, + inline_entry.dex_register_entry.live_dex_registers_mask, + inline_entry.dex_register_entry.locations_start_index); } } } diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h index 08c1d3e3c0..bba3d51e62 100644 --- a/compiler/optimizing/stack_map_stream.h +++ b/compiler/optimizing/stack_map_stream.h @@ -70,6 +70,7 @@ class StackMapStream : public ValueObject { inline_infos_(allocator->Adapter(kArenaAllocStackMapStream)), stack_masks_(allocator->Adapter(kArenaAllocStackMapStream)), register_masks_(allocator->Adapter(kArenaAllocStackMapStream)), + dex_register_entries_(allocator->Adapter(kArenaAllocStackMapStream)), stack_mask_max_(-1), dex_pc_max_(0), register_mask_max_(0), @@ -89,30 +90,42 @@ class StackMapStream : public ValueObject { code_info_encoding_.reserve(16); } + // A dex register map entry for a single stack map entry, contains what registers are live as + // well as indices into the location catalog. + class DexRegisterMapEntry { + public: + static const size_t kOffsetUnassigned = -1; + + BitVector* live_dex_registers_mask; + uint32_t num_dex_registers; + size_t locations_start_index; + // Computed fields + size_t hash = 0; + size_t offset = kOffsetUnassigned; + + size_t ComputeSize(size_t catalog_size) const; + }; + // See runtime/stack_map.h to know what these fields contain. struct StackMapEntry { uint32_t dex_pc; CodeOffset native_pc_code_offset; uint32_t register_mask; BitVector* sp_mask; - uint32_t num_dex_registers; uint8_t inlining_depth; - size_t dex_register_locations_start_index; size_t inline_infos_start_index; - BitVector* live_dex_registers_mask; - uint32_t dex_register_map_hash; - size_t same_dex_register_map_as_; uint32_t stack_mask_index; uint32_t register_mask_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; struct InlineInfoEntry { uint32_t dex_pc; // DexFile::kDexNoIndex for intrinsified native methods. ArtMethod* method; uint32_t method_index; - uint32_t num_dex_registers; - BitVector* live_dex_registers_mask; - size_t dex_register_locations_start_index; + DexRegisterMapEntry dex_register_entry; + size_t dex_register_map_index; }; void BeginStackMapEntry(uint32_t dex_pc, @@ -140,7 +153,8 @@ class StackMapStream : public ValueObject { } void SetStackMapNativePcOffset(size_t i, uint32_t native_pc_offset) { - stack_maps_[i].native_pc_code_offset = CodeOffset::FromOffset(native_pc_offset, instruction_set_); + stack_maps_[i].native_pc_code_offset = + CodeOffset::FromOffset(native_pc_offset, instruction_set_); } // Prepares the stream to fill in a memory region. Must be called before FillIn. @@ -150,8 +164,6 @@ class StackMapStream : public ValueObject { private: size_t ComputeDexRegisterLocationCatalogSize() const; - size_t ComputeDexRegisterMapSize(uint32_t num_dex_registers, - const BitVector* live_dex_registers_mask) const; size_t ComputeDexRegisterMapsSize() const; void ComputeInlineInfoEncoding(InlineInfoEncoding* encoding, size_t dex_register_maps_bytes); @@ -164,15 +176,24 @@ class StackMapStream : public ValueObject { // Returns the number of unique register masks. size_t PrepareRegisterMasks(); - // Returns the index of an entry with the same dex register map as the current_entry, - // or kNoSameDexMapFound if no such entry exists. - size_t FindEntryWithTheSameDexMap(); - bool HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEntry& b) const; + // Deduplicate entry if possible and return the corresponding index into dex_register_entries_ + // array. If entry is not a duplicate, a new entry is added to dex_register_entries_. + size_t AddDexRegisterMapEntry(const DexRegisterMapEntry& entry); + + // Return true if the two dex register map entries are equal. + bool DexRegisterMapEntryEquals(const DexRegisterMapEntry& a, const DexRegisterMapEntry& b) const; + + // Fill in the corresponding entries of a register map. void FillInDexRegisterMap(DexRegisterMap dex_register_map, uint32_t num_dex_registers, const BitVector& live_dex_registers_mask, uint32_t start_index_in_dex_register_locations) const; + // Returns the offset for the dex register inside of the dex register location region. See FillIn. + // Only copies the dex register map if the offset for the entry is not already assigned. + size_t MaybeCopyDexRegisterMap(DexRegisterMapEntry& entry, + size_t* current_offset, + MemoryRegion dex_register_locations_region); void CheckDexRegisterMap(const CodeInfo& code_info, const DexRegisterMap& dex_register_map, size_t num_dex_registers, @@ -199,6 +220,7 @@ class StackMapStream : public ValueObject { ArenaVector<InlineInfoEntry> inline_infos_; ArenaVector<uint8_t> stack_masks_; ArenaVector<uint32_t> register_masks_; + ArenaVector<DexRegisterMapEntry> dex_register_entries_; int stack_mask_max_; uint32_t dex_pc_max_; uint32_t register_mask_max_; diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc index bd0aa6dea7..041695187b 100644 --- a/compiler/optimizing/stack_map_test.cc +++ b/compiler/optimizing/stack_map_test.cc @@ -410,6 +410,100 @@ TEST(StackMapTest, Test2) { } } +TEST(StackMapTest, TestDeduplicateInlineInfoDexRegisterMap) { + ArenaPool pool; + ArenaAllocator arena(&pool); + StackMapStream stream(&arena, kRuntimeISA); + ArtMethod art_method; + + ArenaBitVector sp_mask1(&arena, 0, true); + sp_mask1.SetBit(2); + sp_mask1.SetBit(4); + const size_t number_of_dex_registers = 2; + const size_t number_of_dex_registers_in_inline_info = 2; + stream.BeginStackMapEntry(0, 64, 0x3, &sp_mask1, number_of_dex_registers, 1); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.BeginInlineInfoEntry(&art_method, 3, number_of_dex_registers_in_inline_info); + stream.AddDexRegisterEntry(Kind::kInStack, 0); // Short location. + stream.AddDexRegisterEntry(Kind::kConstant, -2); // Large location. + stream.EndInlineInfoEntry(); + stream.EndStackMapEntry(); + + size_t size = stream.PrepareForFillIn(); + void* memory = arena.Alloc(size, kArenaAllocMisc); + MemoryRegion region(memory, size); + stream.FillIn(region); + + CodeInfo code_info(region); + CodeInfoEncoding encoding = code_info.ExtractEncoding(); + ASSERT_EQ(1u, code_info.GetNumberOfStackMaps(encoding)); + + uint32_t number_of_catalog_entries = code_info.GetNumberOfLocationCatalogEntries(encoding); + ASSERT_EQ(2u, number_of_catalog_entries); + DexRegisterLocationCatalog location_catalog = code_info.GetDexRegisterLocationCatalog(encoding); + // The Dex register location catalog contains: + // - one 1-byte short Dex register locations, and + // - one 5-byte large Dex register location. + const size_t expected_location_catalog_size = 1u + 5u; + ASSERT_EQ(expected_location_catalog_size, location_catalog.Size()); + + // First stack map. + { + StackMap stack_map = code_info.GetStackMapAt(0, encoding); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForDexPc(0, encoding))); + ASSERT_TRUE(stack_map.Equals(code_info.GetStackMapForNativePcOffset(64, encoding))); + ASSERT_EQ(0u, stack_map.GetDexPc(encoding.stack_map.encoding)); + ASSERT_EQ(64u, stack_map.GetNativePcOffset(encoding.stack_map.encoding, kRuntimeISA)); + ASSERT_EQ(0x3u, code_info.GetRegisterMaskOf(encoding, stack_map)); + + ASSERT_TRUE(CheckStackMask(code_info, encoding, stack_map, sp_mask1)); + + ASSERT_TRUE(stack_map.HasDexRegisterMap(encoding.stack_map.encoding)); + DexRegisterMap map(code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers)); + ASSERT_TRUE(map.IsDexRegisterLive(0)); + ASSERT_TRUE(map.IsDexRegisterLive(1)); + ASSERT_EQ(2u, map.GetNumberOfLiveDexRegisters(number_of_dex_registers)); + // The Dex register map contains: + // - one 1-byte live bit mask, and + // - one 1-byte set of location catalog entry indices composed of two 2-bit values. + size_t expected_map_size = 1u + 1u; + ASSERT_EQ(expected_map_size, map.Size()); + + ASSERT_EQ(Kind::kInStack, map.GetLocationKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstant, + map.GetLocationKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kInStack, + map.GetLocationInternalKind(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(Kind::kConstantLargeValue, + map.GetLocationInternalKind(1, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(0, map.GetStackOffsetInBytes(0, number_of_dex_registers, code_info, encoding)); + ASSERT_EQ(-2, map.GetConstant(1, number_of_dex_registers, code_info, encoding)); + + const size_t index0 = + map.GetLocationCatalogEntryIndex(0, number_of_dex_registers, number_of_catalog_entries); + const size_t index1 = + map.GetLocationCatalogEntryIndex(1, number_of_dex_registers, number_of_catalog_entries); + ASSERT_EQ(0u, index0); + ASSERT_EQ(1u, index1); + DexRegisterLocation location0 = location_catalog.GetDexRegisterLocation(index0); + DexRegisterLocation location1 = location_catalog.GetDexRegisterLocation(index1); + ASSERT_EQ(Kind::kInStack, location0.GetKind()); + ASSERT_EQ(Kind::kConstant, location1.GetKind()); + ASSERT_EQ(Kind::kInStack, location0.GetInternalKind()); + ASSERT_EQ(Kind::kConstantLargeValue, location1.GetInternalKind()); + ASSERT_EQ(0, location0.GetValue()); + ASSERT_EQ(-2, location1.GetValue()); + + // Test that the inline info dex register map deduplicated to the same offset as the stack map + // one. + ASSERT_TRUE(stack_map.HasInlineInfo(encoding.stack_map.encoding)); + InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding); + EXPECT_EQ(inline_info.GetDexRegisterMapOffsetAtDepth(encoding.inline_info.encoding, 0), + stack_map.GetDexRegisterMapOffset(encoding.stack_map.encoding)); + } +} + TEST(StackMapTest, TestNonLiveDexRegisters) { ArenaPool pool; ArenaAllocator arena(&pool); diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc index a24d49e08d..5a466e1d5d 100644 --- a/compiler/utils/x86/assembler_x86.cc +++ b/compiler/utils/x86/assembler_x86.cc @@ -783,6 +783,79 @@ void X86Assembler::divpd(XmmRegister dst, XmmRegister src) { } +void X86Assembler::movdqa(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::movdqa(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst, src); +} + + +void X86Assembler::movdqu(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst, src); +} + + +void X86Assembler::movdqa(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src, dst); +} + + +void X86Assembler::movdqu(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src, dst); +} + + +void X86Assembler::paddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFE); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::psubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xFA); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pmulld(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x40); + EmitXmmRegisterOperand(dst, src); +} + + void X86Assembler::cvtsi2ss(XmmRegister dst, Register src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xF3); @@ -990,10 +1063,27 @@ void X86Assembler::xorpd(XmmRegister dst, XmmRegister src) { } -void X86Assembler::andps(XmmRegister dst, XmmRegister src) { +void X86Assembler::xorps(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x54); + EmitUint8(0x57); + EmitOperand(dst, src); +} + + +void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x57); + EmitXmmRegisterOperand(dst, src); +} + + +void X86Assembler::pxor(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0xEF); EmitXmmRegisterOperand(dst, src); } @@ -1007,53 +1097,63 @@ void X86Assembler::andpd(XmmRegister dst, XmmRegister src) { } -void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { +void X86Assembler::andpd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x56); + EmitUint8(0x54); + EmitOperand(dst, src); +} + + +void X86Assembler::andps(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x0F); + EmitUint8(0x54); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::xorps(XmmRegister dst, const Address& src) { +void X86Assembler::andps(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x57); + EmitUint8(0x54); EmitOperand(dst, src); } -void X86Assembler::orps(XmmRegister dst, XmmRegister src) { +void X86Assembler::pand(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x56); + EmitUint8(0xDB); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::xorps(XmmRegister dst, XmmRegister src) { +void X86Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x57); + EmitUint8(0x56); EmitXmmRegisterOperand(dst, src); } -void X86Assembler::andps(XmmRegister dst, const Address& src) { +void X86Assembler::orps(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x0F); - EmitUint8(0x54); - EmitOperand(dst, src); + EmitUint8(0x56); + EmitXmmRegisterOperand(dst, src); } -void X86Assembler::andpd(XmmRegister dst, const Address& src) { +void X86Assembler::por(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); EmitUint8(0x0F); - EmitUint8(0x54); - EmitOperand(dst, src); + EmitUint8(0xEB); + EmitXmmRegisterOperand(dst, src); } @@ -1076,6 +1176,16 @@ void X86Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& imm } +void X86Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitUint8(0x0F); + EmitUint8(0x70); + EmitXmmRegisterOperand(dst, src); + EmitUint8(imm.value()); +} + + void X86Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h index 4056ca67fb..4343e2e734 100644 --- a/compiler/utils/x86/assembler_x86.h +++ b/compiler/utils/x86/assembler_x86.h @@ -430,6 +430,16 @@ class X86Assembler FINAL : public Assembler { void mulpd(XmmRegister dst, XmmRegister src); void divpd(XmmRegister dst, XmmRegister src); + void movdqa(XmmRegister dst, XmmRegister src); // move + void movdqa(XmmRegister dst, const Address& src); // load aligned + void movdqu(XmmRegister dst, const Address& src); // load unaligned + void movdqa(const Address& dst, XmmRegister src); // store aligned + void movdqu(const Address& dst, XmmRegister src); // store unaligned + + void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubd(XmmRegister dst, XmmRegister src); + void pmulld(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, Register src); void cvtsi2sd(XmmRegister dst, Register src); @@ -463,17 +473,21 @@ class X86Assembler FINAL : public Assembler { void xorpd(XmmRegister dst, XmmRegister src); void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); + void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now) void andpd(XmmRegister dst, XmmRegister src); void andpd(XmmRegister dst, const Address& src); void andps(XmmRegister dst, XmmRegister src); void andps(XmmRegister dst, const Address& src); + void pand(XmmRegister dst, XmmRegister src); // no addr variant (for now) - void orpd(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); + void por(XmmRegister dst, XmmRegister src); void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); + void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); void flds(const Address& src); void fstps(const Address& dst); diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc index 1768d8b715..c6ab893aea 100644 --- a/compiler/utils/x86/assembler_x86_test.cc +++ b/compiler/utils/x86/assembler_x86_test.cc @@ -467,6 +467,28 @@ TEST_F(AssemblerX86Test, MovupdAddr) { DriverStr(expected, "movupd_address"); } +TEST_F(AssemblerX86Test, Movdqa) { + DriverStr(RepeatFF(&x86::X86Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movdqa"); +} + +TEST_F(AssemblerX86Test, MovdqaAddr) { + GetAssembler()->movdqa(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movdqa(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movdqa 0x4(%ESP), %xmm0\n" + "movdqa %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movdqa_address"); +} + +TEST_F(AssemblerX86Test, MovdquAddr) { + GetAssembler()->movdqu(x86::XmmRegister(x86::XMM0), x86::Address(x86::Register(x86::ESP), 4)); + GetAssembler()->movdqu(x86::Address(x86::Register(x86::ESP), 2), x86::XmmRegister(x86::XMM1)); + const char* expected = + "movdqu 0x4(%ESP), %xmm0\n" + "movdqu %xmm1, 0x2(%ESP)\n"; + DriverStr(expected, "movdqu_address"); +} + TEST_F(AssemblerX86Test, AddPS) { DriverStr(RepeatFF(&x86::X86Assembler::addps, "addps %{reg2}, %{reg1}"), "addps"); } @@ -499,6 +521,54 @@ TEST_F(AssemblerX86Test, DivPD) { DriverStr(RepeatFF(&x86::X86Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86Test, PAddD) { + DriverStr(RepeatFF(&x86::X86Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); +} + +TEST_F(AssemblerX86Test, PSubD) { + DriverStr(RepeatFF(&x86::X86Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd"); +} + +TEST_F(AssemblerX86Test, PMullD) { + DriverStr(RepeatFF(&x86::X86Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); +} + +TEST_F(AssemblerX86Test, XorPD) { + DriverStr(RepeatFF(&x86::X86Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); +} + +TEST_F(AssemblerX86Test, XorPS) { + DriverStr(RepeatFF(&x86::X86Assembler::xorps, "xorps %{reg2}, %{reg1}"), "xorps"); +} + +TEST_F(AssemblerX86Test, PXor) { + DriverStr(RepeatFF(&x86::X86Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor"); +} + +TEST_F(AssemblerX86Test, AndPD) { + DriverStr(RepeatFF(&x86::X86Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd"); +} + +TEST_F(AssemblerX86Test, AndPS) { + DriverStr(RepeatFF(&x86::X86Assembler::andps, "andps %{reg2}, %{reg1}"), "andps"); +} + +TEST_F(AssemblerX86Test, PAnd) { + DriverStr(RepeatFF(&x86::X86Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); +} + +TEST_F(AssemblerX86Test, OrPD) { + DriverStr(RepeatFF(&x86::X86Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd"); +} + +TEST_F(AssemblerX86Test, OrPS) { + DriverStr(RepeatFF(&x86::X86Assembler::orps, "orps %{reg2}, %{reg1}"), "orps"); +} + +TEST_F(AssemblerX86Test, POr) { + DriverStr(RepeatFF(&x86::X86Assembler::por, "por %{reg2}, %{reg1}"), "por"); +} + TEST_F(AssemblerX86Test, ShufPS) { DriverStr(RepeatFFI(&x86::X86Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } @@ -507,6 +577,10 @@ TEST_F(AssemblerX86Test, ShufPD) { DriverStr(RepeatFFI(&x86::X86Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); } +TEST_F(AssemblerX86Test, PShufD) { + DriverStr(RepeatFFI(&x86::X86Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); +} + ///////////////// // Near labels // ///////////////// diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc index c2c44ab58c..b41be80ae4 100644 --- a/compiler/utils/x86_64/assembler_x86_64.cc +++ b/compiler/utils/x86_64/assembler_x86_64.cc @@ -832,6 +832,87 @@ void X86_64Assembler::divpd(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::movdqa(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqa(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqu(XmmRegister dst, const Address& src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x6F); + EmitOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::movdqa(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::movdqu(const Address& dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0xF3); + EmitOptionalRex32(src, dst); + EmitUint8(0x0F); + EmitUint8(0x7F); + EmitOperand(src.LowBits(), dst); +} + + +void X86_64Assembler::paddd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFE); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::psubd(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xFA); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + +void X86_64Assembler::pmulld(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x38); + EmitUint8(0x40); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::cvtsi2ss(XmmRegister dst, CpuRegister src) { cvtsi2ss(dst, src, false); } @@ -1170,6 +1251,16 @@ void X86_64Assembler::xorps(XmmRegister dst, XmmRegister src) { } +void X86_64Assembler::pxor(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEF); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + + void X86_64Assembler::andpd(XmmRegister dst, const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1196,6 +1287,15 @@ void X86_64Assembler::andps(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::pand(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xDB); + EmitXmmRegisterOperand(dst.LowBits(), src); +} + void X86_64Assembler::orpd(XmmRegister dst, XmmRegister src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0x66); @@ -1213,6 +1313,14 @@ void X86_64Assembler::orps(XmmRegister dst, XmmRegister src) { EmitXmmRegisterOperand(dst.LowBits(), src); } +void X86_64Assembler::por(XmmRegister dst, XmmRegister src) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0xEB); + EmitXmmRegisterOperand(dst.LowBits(), src); +} void X86_64Assembler::shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); @@ -1235,6 +1343,17 @@ void X86_64Assembler::shufps(XmmRegister dst, XmmRegister src, const Immediate& } +void X86_64Assembler::pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm) { + AssemblerBuffer::EnsureCapacity ensured(&buffer_); + EmitUint8(0x66); + EmitOptionalRex32(dst, src); + EmitUint8(0x0F); + EmitUint8(0x70); + EmitXmmRegisterOperand(dst.LowBits(), src); + EmitUint8(imm.value()); +} + + void X86_64Assembler::fldl(const Address& src) { AssemblerBuffer::EnsureCapacity ensured(&buffer_); EmitUint8(0xDD); diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h index e140b45a00..43ea12a4cb 100644 --- a/compiler/utils/x86_64/assembler_x86_64.h +++ b/compiler/utils/x86_64/assembler_x86_64.h @@ -446,6 +446,16 @@ class X86_64Assembler FINAL : public Assembler { void mulpd(XmmRegister dst, XmmRegister src); void divpd(XmmRegister dst, XmmRegister src); + void movdqa(XmmRegister dst, XmmRegister src); // move + void movdqa(XmmRegister dst, const Address& src); // load aligned + void movdqu(XmmRegister dst, const Address& src); // load unaligned + void movdqa(const Address& dst, XmmRegister src); // store aligned + void movdqu(const Address& dst, XmmRegister src); // store unaligned + + void paddd(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void psubd(XmmRegister dst, XmmRegister src); + void pmulld(XmmRegister dst, XmmRegister src); + void cvtsi2ss(XmmRegister dst, CpuRegister src); // Note: this is the r/m32 version. void cvtsi2ss(XmmRegister dst, CpuRegister src, bool is64bit); void cvtsi2ss(XmmRegister dst, const Address& src, bool is64bit); @@ -487,16 +497,20 @@ class X86_64Assembler FINAL : public Assembler { void xorpd(XmmRegister dst, XmmRegister src); void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); + void pxor(XmmRegister dst, XmmRegister src); // no addr variant (for now) void andpd(XmmRegister dst, const Address& src); void andpd(XmmRegister dst, XmmRegister src); - void andps(XmmRegister dst, XmmRegister src); + void andps(XmmRegister dst, XmmRegister src); // no addr variant (for now) + void pand(XmmRegister dst, XmmRegister src); - void orpd(XmmRegister dst, XmmRegister src); + void orpd(XmmRegister dst, XmmRegister src); // no addr variant (for now) void orps(XmmRegister dst, XmmRegister src); + void por(XmmRegister dst, XmmRegister src); void shufpd(XmmRegister dst, XmmRegister src, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& imm); + void pshufd(XmmRegister dst, XmmRegister src, const Immediate& imm); void flds(const Address& src); void fstps(const Address& dst); diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc index efa5cc97ea..aeb1911835 100644 --- a/compiler/utils/x86_64/assembler_x86_64_test.cc +++ b/compiler/utils/x86_64/assembler_x86_64_test.cc @@ -1034,6 +1034,28 @@ TEST_F(AssemblerX86_64Test, Movsd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::movsd, "movsd %{reg2}, %{reg1}"), "movsd"); } +TEST_F(AssemblerX86_64Test, Movdqa) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::movdqa, "movdqa %{reg2}, %{reg1}"), "movapd"); +} + +TEST_F(AssemblerX86_64Test, MovdqaAddr) { + GetAssembler()->movdqa(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movdqa(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movdqa 0x4(%RSP), %xmm0\n" + "movdqa %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movdqa_address"); +} + +TEST_F(AssemblerX86_64Test, MovdquAddr) { + GetAssembler()->movdqu(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 4)); + GetAssembler()->movdqu(x86_64::Address(x86_64::CpuRegister(x86_64::RSP), 2), x86_64::XmmRegister(x86_64::XMM1)); + const char* expected = + "movdqu 0x4(%RSP), %xmm0\n" + "movdqu %xmm1, 0x2(%RSP)\n"; + DriverStr(expected, "movdqu_address"); +} + TEST_F(AssemblerX86_64Test, Movd1) { DriverStr(RepeatFR(&x86_64::X86_64Assembler::movd, "movd %{reg2}, %{reg1}"), "movd.1"); } @@ -1106,6 +1128,18 @@ TEST_F(AssemblerX86_64Test, Divpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::divpd, "divpd %{reg2}, %{reg1}"), "divpd"); } +TEST_F(AssemblerX86_64Test, Paddd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::paddd, "paddd %{reg2}, %{reg1}"), "paddd"); +} + +TEST_F(AssemblerX86_64Test, Psubd) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::psubd, "psubd %{reg2}, %{reg1}"), "psubd"); +} + +TEST_F(AssemblerX86_64Test, Pmulld) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmulld, "pmulld %{reg2}, %{reg1}"), "pmulld"); +} + TEST_F(AssemblerX86_64Test, Cvtsi2ss) { DriverStr(RepeatFr(&x86_64::X86_64Assembler::cvtsi2ss, "cvtsi2ss %{reg2}, %{reg1}"), "cvtsi2ss"); } @@ -1187,6 +1221,10 @@ TEST_F(AssemblerX86_64Test, Xorpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::xorpd, "xorpd %{reg2}, %{reg1}"), "xorpd"); } +TEST_F(AssemblerX86_64Test, Pxor) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pxor, "pxor %{reg2}, %{reg1}"), "pxor"); +} + TEST_F(AssemblerX86_64Test, Andps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::andps, "andps %{reg2}, %{reg1}"), "andps"); } @@ -1195,6 +1233,10 @@ TEST_F(AssemblerX86_64Test, Andpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::andpd, "andpd %{reg2}, %{reg1}"), "andpd"); } +TEST_F(AssemblerX86_64Test, Pand) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::pand, "pand %{reg2}, %{reg1}"), "pand"); +} + TEST_F(AssemblerX86_64Test, Orps) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::orps, "orps %{reg2}, %{reg1}"), "orps"); } @@ -1203,6 +1245,10 @@ TEST_F(AssemblerX86_64Test, Orpd) { DriverStr(RepeatFF(&x86_64::X86_64Assembler::orpd, "orpd %{reg2}, %{reg1}"), "orpd"); } +TEST_F(AssemblerX86_64Test, Por) { + DriverStr(RepeatFF(&x86_64::X86_64Assembler::por, "por %{reg2}, %{reg1}"), "por"); +} + TEST_F(AssemblerX86_64Test, Shufps) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufps, 1, "shufps ${imm}, %{reg2}, %{reg1}"), "shufps"); } @@ -1211,6 +1257,10 @@ TEST_F(AssemblerX86_64Test, Shufpd) { DriverStr(RepeatFFI(&x86_64::X86_64Assembler::shufpd, 1, "shufpd ${imm}, %{reg2}, %{reg1}"), "shufpd"); } +TEST_F(AssemblerX86_64Test, PShufd) { + DriverStr(RepeatFFI(&x86_64::X86_64Assembler::pshufd, 1, "pshufd ${imm}, %{reg2}, %{reg1}"), "pshufd"); +} + TEST_F(AssemblerX86_64Test, UcomissAddress) { GetAssembler()->ucomiss(x86_64::XmmRegister(x86_64::XMM0), x86_64::Address( x86_64::CpuRegister(x86_64::RDI), x86_64::CpuRegister(x86_64::RBX), x86_64::TIMES_4, 12)); diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 5fc9972d09..c892b25ed3 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -233,7 +233,7 @@ class VerifierDepsTest : public CommonCompilerTest { const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); const char* descriptor = dex_file->GetClassDescriptor(class_def); cls.Assign(class_linker_->FindClass(soa.Self(), descriptor, class_loader_handle)); - if (cls.Get() == nullptr) { + if (cls == nullptr) { CHECK(soa.Self()->IsExceptionPending()); soa.Self()->ClearException(); } else if (set.find(class_def.class_idx_) == set.end()) { diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 192fc270f9..026a567fe0 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -419,12 +419,9 @@ class WatchDog { } while (false) public: - explicit WatchDog(bool is_watch_dog_enabled) { - is_watch_dog_enabled_ = is_watch_dog_enabled; - if (!is_watch_dog_enabled_) { - return; - } - shutting_down_ = false; + explicit WatchDog(int64_t timeout_in_milliseconds) + : timeout_in_milliseconds_(timeout_in_milliseconds), + shutting_down_(false) { const char* reason = "dex2oat watch dog thread startup"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_init, (&mutex_, nullptr), reason); CHECK_WATCH_DOG_PTHREAD_CALL(pthread_cond_init, (&cond_, nullptr), reason); @@ -433,9 +430,6 @@ class WatchDog { CHECK_WATCH_DOG_PTHREAD_CALL(pthread_attr_destroy, (&attr_), reason); } ~WatchDog() { - if (!is_watch_dog_enabled_) { - return; - } const char* reason = "dex2oat watch dog thread shutdown"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason); shutting_down_ = true; @@ -448,6 +442,23 @@ class WatchDog { CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_destroy, (&mutex_), reason); } + // TODO: tune the multiplier for GC verification, the following is just to make the timeout + // large. + static constexpr int64_t kWatchdogVerifyMultiplier = + kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1; + + // When setting timeouts, keep in mind that the build server may not be as fast as your + // desktop. Debug builds are slower so they have larger timeouts. + static constexpr int64_t kWatchdogSlowdownFactor = kIsDebugBuild ? 5U : 1U; + + // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager + // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort + // itself before that watchdog would take down the system server. + static constexpr int64_t kWatchDogTimeoutSeconds = kWatchdogSlowdownFactor * (9 * 60 + 30); + + static constexpr int64_t kDefaultWatchdogTimeoutInMS = + kWatchdogVerifyMultiplier * kWatchDogTimeoutSeconds * 1000; + private: static void* CallBack(void* arg) { WatchDog* self = reinterpret_cast<WatchDog*>(arg); @@ -470,18 +481,15 @@ class WatchDog { } void Wait() { - // TODO: tune the multiplier for GC verification, the following is just to make the timeout - // large. - constexpr int64_t multiplier = kVerifyObjectSupport > kVerifyObjectModeFast ? 100 : 1; timespec timeout_ts; - InitTimeSpec(true, CLOCK_REALTIME, multiplier * kWatchDogTimeoutSeconds * 1000, 0, &timeout_ts); + InitTimeSpec(true, CLOCK_REALTIME, timeout_in_milliseconds_, 0, &timeout_ts); const char* reason = "dex2oat watch dog thread waiting"; CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_lock, (&mutex_), reason); while (!shutting_down_) { int rc = TEMP_FAILURE_RETRY(pthread_cond_timedwait(&cond_, &mutex_, &timeout_ts)); if (rc == ETIMEDOUT) { Fatal(StringPrintf("dex2oat did not finish after %" PRId64 " seconds", - kWatchDogTimeoutSeconds)); + timeout_in_milliseconds_/1000)); } else if (rc != 0) { std::string message(StringPrintf("pthread_cond_timedwait failed: %s", strerror(errno))); @@ -491,16 +499,7 @@ class WatchDog { CHECK_WATCH_DOG_PTHREAD_CALL(pthread_mutex_unlock, (&mutex_), reason); } - // When setting timeouts, keep in mind that the build server may not be as fast as your desktop. - // Debug builds are slower so they have larger timeouts. - static constexpr int64_t kSlowdownFactor = kIsDebugBuild ? 5U : 1U; - - // 9.5 minutes scaled by kSlowdownFactor. This is slightly smaller than the Package Manager - // watchdog (PackageManagerService.WATCHDOG_TIMEOUT, 10 minutes), so that dex2oat will abort - // itself before that watchdog would take down the system server. - static constexpr int64_t kWatchDogTimeoutSeconds = kSlowdownFactor * (9 * 60 + 30); - - bool is_watch_dog_enabled_; + const int64_t timeout_in_milliseconds_; bool shutting_down_; // TODO: Switch to Mutex when we can guarantee it won't prevent shutdown in error cases. pthread_mutex_t mutex_; @@ -591,6 +590,7 @@ class Dex2Oat FINAL { struct ParserOptions { std::vector<const char*> oat_symbols; std::string boot_image_filename; + int64_t watch_dog_timeout_in_ms = -1; bool watch_dog_enabled = true; bool requested_specific_compiler = false; std::string error_msg; @@ -919,7 +919,10 @@ class Dex2Oat FINAL { // Done with usage checks, enable watchdog if requested if (parser_options->watch_dog_enabled) { - watchdog_.reset(new WatchDog(true)); + int64_t timeout = parser_options->watch_dog_timeout_in_ms > 0 + ? parser_options->watch_dog_timeout_in_ms + : WatchDog::kDefaultWatchdogTimeoutInMS; + watchdog_.reset(new WatchDog(timeout)); } // Fill some values into the key-value store for the oat header. @@ -1150,6 +1153,11 @@ class Dex2Oat FINAL { parser_options->watch_dog_enabled = true; } else if (option == "--no-watch-dog") { parser_options->watch_dog_enabled = false; + } else if (option.starts_with("--watchdog-timeout=")) { + ParseIntOption(option, + "--watchdog-timeout", + &parser_options->watch_dog_timeout_in_ms, + Usage); } else if (option.starts_with("-j")) { ParseJ(option); } else if (option.starts_with("--image=")) { diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index c2275aca95..5dcdd9e69a 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -66,7 +66,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { bool success = Dex2Oat(args, &error_msg); if (expect_success) { - ASSERT_TRUE(success) << error_msg; + ASSERT_TRUE(success) << error_msg << std::endl << output_; // Verify the odex file was generated as expected. std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(), @@ -231,7 +231,7 @@ class Dex2oatSwapTest : public Dex2oatTest { } virtual std::string GetTestDexFileName() { - return GetDexSrc1(); + return Dex2oatEnvironmentTest::GetTestDexFileName("VerifierDeps"); } virtual void CheckResult(bool expect_use) { @@ -658,4 +658,41 @@ TEST_F(Dex2oatLayoutTest, TestLayout) { RunTest(); } +class Dex2oatWatchdogTest : public Dex2oatTest { + protected: + void RunTest(bool expect_success, const std::vector<std::string>& extra_args = {}) { + std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar"; + std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex"; + + Copy(GetTestDexFileName(), dex_location); + + std::vector<std::string> copy(extra_args); + + std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap"; + copy.push_back("--swap-file=" + swap_location); + GenerateOdexForTest(dex_location, + odex_location, + CompilerFilter::kSpeed, + copy, + expect_success); + } + + std::string GetTestDexFileName() { + return GetDexSrc1(); + } +}; + +TEST_F(Dex2oatWatchdogTest, TestWatchdogOK) { + // Check with default. + RunTest(true); + + // Check with ten minutes. + RunTest(true, { "--watchdog-timeout=600000" }); +} + +TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { + // Check with ten milliseconds. + RunTest(false, { "--watchdog-timeout=10" }); +} + } // namespace art diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index d5776fa61e..5656ddd59c 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -881,26 +881,30 @@ static std::unique_ptr<char[]> indexString(const DexFile* pDexFile, outSize = snprintf(buf.get(), bufSize, "[obj+%0*x]", width, index); break; case Instruction::kIndexMethodAndProtoRef: { - std::string method("<method?>"); - std::string proto("<proto?>"); - if (index < pDexFile->GetHeader().method_ids_size_) { - const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index); - const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); - const Signature signature = pDexFile->GetMethodSignature(pMethodId); - const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); - method = android::base::StringPrintf("%s.%s:%s", - backDescriptor, - name, - signature.ToString().c_str()); - } - if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { - const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); - const Signature signature = pDexFile->GetProtoSignature(protoId); - proto = signature.ToString(); - } - outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x", - method.c_str(), proto.c_str(), width, index, width, secondary_index); + std::string method("<method?>"); + std::string proto("<proto?>"); + if (index < pDexFile->GetHeader().method_ids_size_) { + const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(index); + const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_); + const Signature signature = pDexFile->GetMethodSignature(pMethodId); + const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_); + method = android::base::StringPrintf("%s.%s:%s", + backDescriptor, + name, + signature.ToString().c_str()); } + if (secondary_index < pDexFile->GetHeader().proto_ids_size_) { + const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index); + const Signature signature = pDexFile->GetProtoSignature(protoId); + proto = signature.ToString(); + } + outSize = snprintf(buf.get(), bufSize, "%s, %s // method@%0*x, proto@%0*x", + method.c_str(), proto.c_str(), width, index, width, secondary_index); + break; + } + case Instruction::kIndexCallSiteRef: + // Call site information is too large to detail in disassembly so just output the index. + outSize = snprintf(buf.get(), bufSize, "call_site@%0*x", width, index); break; // SOME NOT SUPPORTED: // case Instruction::kIndexVaries: @@ -1581,6 +1585,198 @@ static void dumpClass(const DexFile* pDexFile, int idx, char** pLastPackage) { free(accessStr); } +static void dumpMethodHandle(const DexFile* pDexFile, u4 idx) { + const DexFile::MethodHandleItem& mh = pDexFile->GetMethodHandle(idx); + bool is_invoke = false; + const char* type; + switch (static_cast<DexFile::MethodHandleType>(mh.method_handle_type_)) { + case DexFile::MethodHandleType::kStaticPut: + type = "put-static"; + break; + case DexFile::MethodHandleType::kStaticGet: + type = "get-static"; + break; + case DexFile::MethodHandleType::kInstancePut: + type = "put-instance"; + break; + case DexFile::MethodHandleType::kInstanceGet: + type = "get-instance"; + break; + case DexFile::MethodHandleType::kInvokeStatic: + type = "invoke-static"; + is_invoke = true; + break; + case DexFile::MethodHandleType::kInvokeInstance: + type = "invoke-instance"; + is_invoke = true; + break; + case DexFile::MethodHandleType::kInvokeConstructor: + type = "invoke-constructor"; + is_invoke = true; + break; + } + + const char* declaring_class; + const char* member; + std::string member_type; + if (is_invoke) { + const DexFile::MethodId& method_id = pDexFile->GetMethodId(mh.field_or_method_idx_); + declaring_class = pDexFile->GetMethodDeclaringClassDescriptor(method_id); + member = pDexFile->GetMethodName(method_id); + member_type = pDexFile->GetMethodSignature(method_id).ToString(); + } else { + const DexFile::FieldId& field_id = pDexFile->GetFieldId(mh.field_or_method_idx_); + declaring_class = pDexFile->GetFieldDeclaringClassDescriptor(field_id); + member = pDexFile->GetFieldName(field_id); + member_type = pDexFile->GetFieldTypeDescriptor(field_id); + } + + if (gOptions.outputFormat == OUTPUT_PLAIN) { + fprintf(gOutFile, "Method handle #%u:\n", idx); + fprintf(gOutFile, " type : %s\n", type); + fprintf(gOutFile, " target : %s %s\n", declaring_class, member); + fprintf(gOutFile, " target_type : %s\n", member_type.c_str()); + } else { + fprintf(gOutFile, "<method_handle index=\"%u\"\n", idx); + fprintf(gOutFile, " type=\"%s\"\n", type); + fprintf(gOutFile, " target_class=\"%s\"\n", declaring_class); + fprintf(gOutFile, " target_member=\"%s\"\n", member); + fprintf(gOutFile, " target_member_type="); + dumpEscapedString(member_type.c_str()); + fprintf(gOutFile, "\n>\n</method_handle>\n"); + } +} + +static void dumpCallSite(const DexFile* pDexFile, u4 idx) { + const DexFile::CallSiteIdItem& call_site_id = pDexFile->GetCallSiteId(idx); + CallSiteArrayValueIterator it(*pDexFile, call_site_id); + if (it.Size() < 3) { + fprintf(stderr, "ERROR: Call site %u has too few values.\n", idx); + return; + } + + uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i); + it.Next(); + dex::StringIndex method_name_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i); + const char* method_name = pDexFile->StringDataByIdx(method_name_idx); + it.Next(); + uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i); + const DexFile::ProtoId& method_type_id = pDexFile->GetProtoId(method_type_idx); + std::string method_type = pDexFile->GetProtoSignature(method_type_id).ToString(); + it.Next(); + + if (gOptions.outputFormat == OUTPUT_PLAIN) { + fprintf(gOutFile, "Call site #%u:\n", idx); + fprintf(gOutFile, " link_argument[0] : %u (MethodHandle)\n", method_handle_idx); + fprintf(gOutFile, " link_argument[1] : %s (String)\n", method_name); + fprintf(gOutFile, " link_argument[2] : %s (MethodType)\n", method_type.c_str()); + } else { + fprintf(gOutFile, "<call_site index=\"%u\">\n", idx); + fprintf(gOutFile, + "<link_argument index=\"0\" type=\"MethodHandle\" value=\"%u\"/>\n", + method_handle_idx); + fprintf(gOutFile, + "<link_argument index=\"1\" type=\"String\" values=\"%s\"/>\n", + method_name); + fprintf(gOutFile, + "<link_argument index=\"2\" type=\"MethodType\" value=\"%s\"/>\n", + method_type.c_str()); + } + + size_t argument = 3; + while (it.HasNext()) { + const char* type; + std::string value; + switch (it.GetValueType()) { + case EncodedArrayValueIterator::ValueType::kByte: + type = "byte"; + value = android::base::StringPrintf("%u", it.GetJavaValue().b); + break; + case EncodedArrayValueIterator::ValueType::kShort: + type = "short"; + value = android::base::StringPrintf("%d", it.GetJavaValue().s); + break; + case EncodedArrayValueIterator::ValueType::kChar: + type = "char"; + value = android::base::StringPrintf("%u", it.GetJavaValue().c); + break; + case EncodedArrayValueIterator::ValueType::kInt: + type = "int"; + value = android::base::StringPrintf("%d", it.GetJavaValue().i); + break; + case EncodedArrayValueIterator::ValueType::kLong: + type = "long"; + value = android::base::StringPrintf("%" PRId64, it.GetJavaValue().j); + break; + case EncodedArrayValueIterator::ValueType::kFloat: + type = "float"; + value = android::base::StringPrintf("%g", it.GetJavaValue().f); + break; + case EncodedArrayValueIterator::ValueType::kDouble: + type = "double"; + value = android::base::StringPrintf("%g", it.GetJavaValue().d); + break; + case EncodedArrayValueIterator::ValueType::kMethodType: { + type = "MethodType"; + uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i); + const DexFile::ProtoId& proto_id = pDexFile->GetProtoId(proto_idx); + value = pDexFile->GetProtoSignature(proto_id).ToString(); + break; + } + case EncodedArrayValueIterator::ValueType::kMethodHandle: + type = "MethodHandle"; + value = android::base::StringPrintf("%d", it.GetJavaValue().i); + break; + case EncodedArrayValueIterator::ValueType::kString: { + type = "String"; + dex::StringIndex string_idx = static_cast<dex::StringIndex>(it.GetJavaValue().i); + value = pDexFile->StringDataByIdx(string_idx); + break; + } + case EncodedArrayValueIterator::ValueType::kType: { + type = "Class"; + dex::TypeIndex type_idx = static_cast<dex::TypeIndex>(it.GetJavaValue().i); + const DexFile::ClassDef* class_def = pDexFile->FindClassDef(type_idx); + value = pDexFile->GetClassDescriptor(*class_def); + value = descriptorClassToDot(value.c_str()).get(); + break; + } + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + // Unreachable based on current EncodedArrayValueIterator::Next(). + UNIMPLEMENTED(FATAL) << " type " << type; + UNREACHABLE(); + break; + case EncodedArrayValueIterator::ValueType::kNull: + type = "Null"; + value = "null"; + break; + case EncodedArrayValueIterator::ValueType::kBoolean: + type = "boolean"; + value = it.GetJavaValue().z ? "true" : "false"; + break; + } + + if (gOptions.outputFormat == OUTPUT_PLAIN) { + fprintf(gOutFile, " link_argument[%zu] : %s (%s)\n", argument, value.c_str(), type); + } else { + fprintf(gOutFile, "<link_argument index=\"%zu\" type=\"%s\" value=", argument, type); + dumpEscapedString(value.c_str()); + fprintf(gOutFile, "/>\n"); + } + + it.Next(); + argument++; + } + + if (gOptions.outputFormat == OUTPUT_XML) { + fprintf(gOutFile, "</call_site>\n"); + } +} + /* * Dumps the requested sections of the file. */ @@ -1612,6 +1808,16 @@ static void processDexFile(const char* fileName, dumpClass(pDexFile, i, &package); } // for + // Iterate over all method handles. + for (u4 i = 0; i < pDexFile->NumMethodHandles(); ++i) { + dumpMethodHandle(pDexFile, i); + } // for + + // Iterate over all call site ids. + for (u4 i = 0; i < pDexFile->NumCallSiteIds(); ++i) { + dumpCallSite(pDexFile, i); + } // for + // Free the last package allocated. if (package != nullptr) { fprintf(gOutFile, "</package>\n"); diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index 9f49ec6f60..ff05733345 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -859,6 +859,22 @@ DISASSEMBLER_ENTRY(cmp, has_modrm = true; store = true; break; + case 0x7F: + if (prefix[2] == 0x66) { + src_reg_file = dst_reg_file = SSE; + opcode1 = "movdqa"; + prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode + } else if (prefix[0] == 0xF3) { + src_reg_file = dst_reg_file = SSE; + opcode1 = "movdqu"; + prefix[0] = 0; // clear prefix now it's served its purpose as part of the opcode + } else { + dst_reg_file = MMX; + opcode1 = "movq"; + } + store = true; + has_modrm = true; + break; case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: opcode1 = "j"; diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index ce63e18921..9a3b28b16e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1473,7 +1473,7 @@ class OatDumper { Runtime* const runtime = Runtime::Current(); Handle<mirror::DexCache> dex_cache( hs->NewHandle(runtime->GetClassLinker()->RegisterDexFile(*dex_file, nullptr))); - CHECK(dex_cache.Get() != nullptr); + CHECK(dex_cache != nullptr); DCHECK(options_.class_loader_ != nullptr); return verifier::MethodVerifier::VerifyMethodAndDump( soa.Self(), vios, dex_method_idx, dex_file, dex_cache, *options_.class_loader_, @@ -2950,7 +2950,7 @@ class IMTDumper { const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); const char* descriptor = dex_file->GetClassDescriptor(class_def); h_klass.Assign(class_linker->FindClass(self, descriptor, h_class_loader)); - if (h_klass.Get() == nullptr) { + if (h_klass == nullptr) { std::cerr << "Warning: could not load " << descriptor << std::endl; continue; } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 9a73830f99..b9be5f2605 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -688,6 +688,16 @@ void PatchOat::PatchDexFileArrays(mirror::ObjectArray<mirror::Object>* img_roots orig_dex_cache->FixupResolvedMethodTypes(RelocatedCopyOf(orig_method_types), RelocatedPointerVisitor(this)); } + + GcRoot<mirror::CallSite>* orig_call_sites = orig_dex_cache->GetResolvedCallSites(); + GcRoot<mirror::CallSite>* relocated_call_sites = RelocatedAddressOfPointer(orig_call_sites); + copy_dex_cache->SetField64<false>( + mirror::DexCache::ResolvedCallSitesOffset(), + static_cast<int64_t>(reinterpret_cast<uintptr_t>(relocated_call_sites))); + if (orig_call_sites != nullptr) { + orig_dex_cache->FixupResolvedCallSites(RelocatedCopyOf(orig_call_sites), + RelocatedPointerVisitor(this)); + } } } diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index a6c3cf067b..d395c170bf 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -94,6 +94,54 @@ class ProfileAssistantTest : public CommonRuntimeTest { std::string error; return ExecAndReturnCode(argv_str, &error); } + + bool CreateProfile(std::string class_file_contents, const std::string& filename) { + ScratchFile class_names_file; + File* file = class_names_file.GetFile(); + EXPECT_TRUE(file->WriteFully(class_file_contents.c_str(), class_file_contents.length())); + EXPECT_EQ(0, file->Flush()); + EXPECT_TRUE(file->ResetOffset()); + std::string profman_cmd = GetProfmanCmd(); + std::vector<std::string> argv_str; + argv_str.push_back(profman_cmd); + argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename()); + argv_str.push_back("--reference-profile-file=" + filename); + argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); + argv_str.push_back("--dex-location=classes.dex"); + std::string error; + EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); + return true; + } + + bool DumpClasses(const std::string& filename, std::string* file_contents) { + ScratchFile class_names_file; + std::string profman_cmd = GetProfmanCmd(); + std::vector<std::string> argv_str; + argv_str.push_back(profman_cmd); + argv_str.push_back("--dump-classes"); + argv_str.push_back("--profile-file=" + filename); + argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]); + argv_str.push_back("--dex-location=classes.dex"); + argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(class_names_file))); + std::string error; + EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0); + File* file = class_names_file.GetFile(); + EXPECT_EQ(0, file->Flush()); + EXPECT_TRUE(file->ResetOffset()); + int64_t length = file->GetLength(); + std::unique_ptr<char[]> buf(new char[length]); + EXPECT_EQ(file->Read(buf.get(), length, 0), length); + *file_contents = std::string(buf.get(), length); + return true; + } + + bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) { + ScratchFile profile_file; + EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename())); + profile_file.GetFile()->ResetOffset(); + EXPECT_TRUE(DumpClasses(profile_file.GetFilename(), output_file_contents)); + return true; + } }; TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) { @@ -307,4 +355,55 @@ TEST_F(ProfileAssistantTest, TestProfileGeneration) { ASSERT_TRUE(info.Load(GetFd(profile))); } +TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { + // Class names put here need to be in sorted order. + std::vector<std::string> class_names = { + "java.lang.Comparable", + "java.lang.Math", + "java.lang.Object" + }; + std::string input_file_contents; + for (std::string& class_name : class_names) { + input_file_contents += class_name + std::string("\n"); + } + std::string output_file_contents; + ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); + ASSERT_EQ(output_file_contents, input_file_contents); +} + +TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { + // Class names put here need to be in sorted order. + std::vector<std::string> class_names = { + "doesnt.match.this.one", + "java.lang.Comparable", + "java.lang.Object" + }; + std::string input_file_contents; + for (std::string& class_name : class_names) { + input_file_contents += class_name + std::string("\n"); + } + std::string output_file_contents; + ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); + std::string expected_contents = + class_names[1] + std::string("\n") + class_names[2] + std::string("\n"); + ASSERT_EQ(output_file_contents, expected_contents); +} + +TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) { + // Class names put here need to be in sorted order. + std::vector<std::string> class_names = { + "doesnt.match.this.one", + "doesnt.match.this.one.either", + "nor.this.one" + }; + std::string input_file_contents; + for (std::string& class_name : class_names) { + input_file_contents += class_name + std::string("\n"); + } + std::string output_file_contents; + ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents)); + std::string expected_contents(""); + ASSERT_EQ(output_file_contents, expected_contents); +} + } // namespace art diff --git a/profman/profman.cc b/profman/profman.cc index b0cbed1ef9..f6b145aa76 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -21,8 +21,11 @@ #include <sys/stat.h> #include <unistd.h> +#include <fstream> #include <iostream> +#include <set> #include <string> +#include <unordered_set> #include <vector> #include "android-base/stringprintf.h" @@ -84,8 +87,10 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --dump-only: dumps the content of the specified profile files"); UsageError(" to standard output (default) in a human readable form."); UsageError(""); - UsageError(" --dump-output-to-fd=<number>: redirects --dump-info-for output to a file"); - UsageError(" descriptor."); + UsageError(" --dump-output-to-fd=<number>: redirects --dump-only output to a file descriptor."); + UsageError(""); + UsageError(" --dump-classes: dumps a sorted list of classes that are in the specified profile"); + UsageError(" file to standard output (default) in a human readable form."); UsageError(""); UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation."); UsageError(" Can be specified multiple time, in which case the data from the different"); @@ -103,6 +108,7 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but"); UsageError(" accepts a file descriptor. Cannot be used together with"); UsageError(" --reference-profile-file."); + UsageError(""); UsageError(" --generate-test-profile=<filename>: generates a random profile file for testing."); UsageError(" --generate-test-profile-num-dex=<number>: number of dex files that should be"); UsageError(" included in the generated profile. Defaults to 20."); @@ -111,12 +117,15 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(" --generate-test-profile-class-ratio=<number>: the percentage from the maximum"); UsageError(" number of classes that should be generated. Defaults to 5."); UsageError(""); + UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes."); + UsageError(""); UsageError(""); UsageError(" --dex-location=<string>: location string to use with corresponding"); UsageError(" apk-fd to find dex files"); UsageError(""); UsageError(" --apk-fd=<number>: file descriptor containing an open APK to"); UsageError(" search for dex files"); + UsageError(" --apk-=<filename>: an APK to search for dex files"); UsageError(""); exit(EXIT_FAILURE); @@ -132,6 +141,7 @@ class ProfMan FINAL { ProfMan() : reference_profile_file_fd_(kInvalidFd), dump_only_(false), + dump_classes_(false), dump_output_to_fd_(kInvalidFd), test_profile_num_dex_(kDefaultTestProfileNumDex), test_profile_method_ratio_(kDefaultTestProfileMethodRatio), @@ -164,6 +174,10 @@ class ProfMan FINAL { } if (option == "--dump-only") { dump_only_ = true; + } else if (option == "--dump-classes") { + dump_classes_ = true; + } else if (option.starts_with("--create-profile-from=")) { + create_profile_from_file_ = option.substr(strlen("--create-profile-from=")).ToString(); } else if (option.starts_with("--dump-output-to-fd=")) { ParseUintOption(option, "--dump-output-to-fd", &dump_output_to_fd_, Usage); } else if (option.starts_with("--profile-file=")) { @@ -178,6 +192,8 @@ class ProfMan FINAL { dex_locations_.push_back(option.substr(strlen("--dex-location=")).ToString()); } else if (option.starts_with("--apk-fd=")) { ParseFdForCollection(option, "--apk-fd", &apks_fd_); + } else if (option.starts_with("--apk=")) { + apk_files_.push_back(option.substr(strlen("--apk=")).ToString()); } else if (option.starts_with("--generate-test-profile=")) { test_profile_ = option.substr(strlen("--generate-test-profile=")).ToString(); } else if (option.starts_with("--generate-test-profile-num-dex=")) { @@ -213,14 +229,34 @@ class ProfMan FINAL { } return; } - // --dump-only may be specified with only --reference-profiles present. - if (!dump_only_ && !has_profiles) { + if (!apk_files_.empty() && !apks_fd_.empty()) { + Usage("APK files should not be specified with both --apk-fd and --apk"); + } + if (!create_profile_from_file_.empty()) { + if (apk_files_.empty() && apks_fd_.empty()) { + Usage("APK files must be specified"); + } + if (dex_locations_.empty()) { + Usage("DEX locations must be specified"); + } + if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) { + Usage("Reference profile must be specified with --reference-profile-file or " + "--reference-profile-file-fd"); + } + if (has_profiles) { + Usage("Profile must be specified with --reference-profile-file or " + "--reference-profile-file-fd"); + } + return; + } + // --dump-only and --dump-classes may be specified with only --reference-profiles present. + if (!dump_only_ && !dump_classes_ && !has_profiles) { Usage("No profile files specified."); } if (!profile_files_.empty() && !profile_files_fd_.empty()) { Usage("Profile files should not be specified with both --profile-file-fd and --profile-file"); } - if (!dump_only_ && !has_reference_profile) { + if (!dump_only_ && !dump_classes_ && !has_reference_profile) { Usage("No reference profile file specified."); } if (!reference_profile_file_.empty() && FdIsValid(reference_profile_file_fd_)) { @@ -248,6 +284,46 @@ class ProfMan FINAL { return result; } + void OpenApkFilesFromLocations(std::vector<std::unique_ptr<const DexFile>>* dex_files) { + bool use_apk_fd_list = !apks_fd_.empty(); + if (use_apk_fd_list) { + CHECK(apk_files_.empty()); + CHECK_EQ(dex_locations_.size(), apks_fd_.size()); + } else { + CHECK_EQ(dex_locations_.size(), apk_files_.size()); + CHECK(!apk_files_.empty()); + } + static constexpr bool kVerifyChecksum = true; + for (size_t i = 0; i < dex_locations_.size(); ++i) { + std::string error_msg; + std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; + if (use_apk_fd_list) { + if (DexFile::OpenZip(apks_fd_[i], + dex_locations_[i], + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(WARNING) << "OpenZip failed for '" << dex_locations_[i] << "' " << error_msg; + continue; + } + } else { + if (DexFile::Open(apk_files_[i].c_str(), + dex_locations_[i], + kVerifyChecksum, + &error_msg, + &dex_files_for_location)) { + } else { + LOG(WARNING) << "Open failed for '" << dex_locations_[i] << "' " << error_msg; + continue; + } + } + for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) { + dex_files->emplace_back(std::move(dex_file)); + } + } + } + int DumpOneProfile(const std::string& banner, const std::string& filename, int fd, @@ -256,13 +332,13 @@ class ProfMan FINAL { if (!filename.empty()) { fd = open(filename.c_str(), O_RDWR); if (fd < 0) { - std::cerr << "Cannot open " << filename << strerror(errno); + LOG(ERROR) << "Cannot open " << filename << strerror(errno); return -1; } } ProfileCompilationInfo info; if (!info.Load(fd)) { - std::cerr << "Cannot load profile info from fd=" << fd << "\n"; + LOG(ERROR) << "Cannot load profile info from fd=" << fd << "\n"; return -1; } std::string this_dump = banner + "\n" + info.DumpInfo(dex_files) + "\n"; @@ -281,25 +357,7 @@ class ProfMan FINAL { // Open apk/zip files and and read dex files. MemMap::Init(); // for ZipArchive::OpenFromFd std::vector<std::unique_ptr<const DexFile>> dex_files; - assert(dex_locations_.size() == apks_fd_.size()); - static constexpr bool kVerifyChecksum = true; - for (size_t i = 0; i < dex_locations_.size(); ++i) { - std::string error_msg; - std::vector<std::unique_ptr<const DexFile>> dex_files_for_location; - if (DexFile::OpenZip(apks_fd_[i], - dex_locations_[i], - kVerifyChecksum, - &error_msg, - &dex_files_for_location)) { - } else { - LOG(WARNING) << "OpenFromZip failed for '" << dex_locations_[i] << "' " << error_msg; - continue; - } - for (std::unique_ptr<const DexFile>& dex_file : dex_files_for_location) { - dex_files.emplace_back(std::move(dex_file)); - } - } - + OpenApkFilesFromLocations(&dex_files); std::string dump; // Dump individual profile files. if (!profile_files_fd_.empty()) { @@ -358,17 +416,207 @@ class ProfMan FINAL { return dump_only_; } + bool GetClassNames(int fd, + std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* class_names) { + ProfileCompilationInfo profile_info; + if (!profile_info.Load(fd)) { + LOG(ERROR) << "Cannot load profile info"; + return false; + } + profile_info.GetClassNames(dex_files, class_names); + return true; + } + + bool GetClassNames(std::string profile_file, + std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* class_names) { + int fd = open(profile_file.c_str(), O_RDONLY); + if (!FdIsValid(fd)) { + LOG(ERROR) << "Cannot open " << profile_file << strerror(errno); + return false; + } + if (!GetClassNames(fd, dex_files, class_names)) { + return false; + } + if (close(fd) < 0) { + PLOG(WARNING) << "Failed to close descriptor"; + } + return true; + } + + int DumpClasses() { + // Open apk/zip files and and read dex files. + MemMap::Init(); // for ZipArchive::OpenFromFd + // Open the dex files to get the names for classes. + std::vector<std::unique_ptr<const DexFile>> dex_files; + OpenApkFilesFromLocations(&dex_files); + // Build a vector of class names from individual profile files. + std::set<std::string> class_names; + if (!profile_files_fd_.empty()) { + for (int profile_file_fd : profile_files_fd_) { + if (!GetClassNames(profile_file_fd, &dex_files, &class_names)) { + return -1; + } + } + } + if (!profile_files_.empty()) { + for (const std::string& profile_file : profile_files_) { + if (!GetClassNames(profile_file, &dex_files, &class_names)) { + return -1; + } + } + } + // Concatenate class names from reference profile file. + if (FdIsValid(reference_profile_file_fd_)) { + if (!GetClassNames(reference_profile_file_fd_, &dex_files, &class_names)) { + return -1; + } + } + if (!reference_profile_file_.empty()) { + if (!GetClassNames(reference_profile_file_, &dex_files, &class_names)) { + return -1; + } + } + // Dump the class names. + std::string dump; + for (const std::string& class_name : class_names) { + dump += class_name + std::string("\n"); + } + if (!FdIsValid(dump_output_to_fd_)) { + std::cout << dump; + } else { + unix_file::FdFile out_fd(dump_output_to_fd_, false /*check_usage*/); + if (!out_fd.WriteFully(dump.c_str(), dump.length())) { + return -1; + } + } + return 0; + } + + bool ShouldOnlyDumpClasses() { + return dump_classes_; + } + + // Read lines from the given file, dropping comments and empty lines. Post-process each line with + // the given function. + template <typename T> + static T* ReadCommentedInputFromFile( + const char* input_filename, std::function<std::string(const char*)>* process) { + std::unique_ptr<std::ifstream> input_file(new std::ifstream(input_filename, std::ifstream::in)); + if (input_file.get() == nullptr) { + LOG(ERROR) << "Failed to open input file " << input_filename; + return nullptr; + } + std::unique_ptr<T> result( + ReadCommentedInputStream<T>(*input_file, process)); + input_file->close(); + return result.release(); + } + + // Read lines from the given stream, dropping comments and empty lines. Post-process each line + // with the given function. + template <typename T> + static T* ReadCommentedInputStream( + std::istream& in_stream, + std::function<std::string(const char*)>* process) { + std::unique_ptr<T> output(new T()); + while (in_stream.good()) { + std::string dot; + std::getline(in_stream, dot); + if (android::base::StartsWith(dot, "#") || dot.empty()) { + continue; + } + if (process != nullptr) { + std::string descriptor((*process)(dot.c_str())); + output->insert(output->end(), descriptor); + } else { + output->insert(output->end(), dot); + } + } + return output.release(); + } + + int CreateProfile() { + MemMap::Init(); // for ZipArchive::OpenFromFd + // Open the profile output file if needed. + int fd = reference_profile_file_fd_; + if (!FdIsValid(fd)) { + CHECK(!reference_profile_file_.empty()); + fd = open(reference_profile_file_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (fd < 0) { + LOG(ERROR) << "Cannot open " << reference_profile_file_ << strerror(errno); + return -1; + } + } + // Read the user-specified list of classes (dot notation rather than descriptors). + std::unique_ptr<std::unordered_set<std::string>> + user_class_list(ReadCommentedInputFromFile<std::unordered_set<std::string>>( + create_profile_from_file_.c_str(), nullptr)); // No post-processing. + std::unordered_set<std::string> matched_user_classes; + // Open the dex files to look up class names. + std::vector<std::unique_ptr<const DexFile>> dex_files; + OpenApkFilesFromLocations(&dex_files); + // Iterate over the dex files looking for class names in the input stream. + std::set<DexCacheResolvedClasses> resolved_class_set; + for (auto& dex_file : dex_files) { + // Compute the set of classes to be added for this dex file first. This + // avoids creating an entry in the profile information for dex files that + // contribute no classes. + std::unordered_set<dex::TypeIndex> classes_to_be_added; + for (const auto& klass : *user_class_list) { + std::string descriptor = DotToDescriptor(klass.c_str()); + const DexFile::TypeId* type_id = dex_file->FindTypeId(descriptor.c_str()); + if (type_id == nullptr) { + continue; + } + classes_to_be_added.insert(dex_file->GetIndexForTypeId(*type_id)); + matched_user_classes.insert(klass); + } + if (classes_to_be_added.empty()) { + continue; + } + // Insert the DexCacheResolved Classes into the set expected for + // AddMethodsAndClasses. + std::set<DexCacheResolvedClasses>::iterator dex_resolved_classes = + resolved_class_set.emplace(dex_file->GetLocation(), + dex_file->GetBaseLocation(), + dex_file->GetLocationChecksum()).first; + dex_resolved_classes->AddClasses(classes_to_be_added.begin(), classes_to_be_added.end()); + } + // Warn the user if we didn't find matches for every class. + for (const auto& klass : *user_class_list) { + if (matched_user_classes.find(klass) == matched_user_classes.end()) { + LOG(WARNING) << "requested class '" << klass << "' was not matched in any dex file"; + } + } + // Generate the profile data structure. + ProfileCompilationInfo info; + std::vector<MethodReference> methods; // No methods for now. + info.AddMethodsAndClasses(methods, resolved_class_set); + // Write the profile file. + CHECK(info.Save(fd)); + if (close(fd) < 0) { + PLOG(WARNING) << "Failed to close descriptor"; + } + return 0; + } + + bool ShouldCreateProfile() { + return !create_profile_from_file_.empty(); + } + int GenerateTestProfile() { int profile_test_fd = open(test_profile_.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); if (profile_test_fd < 0) { - std::cerr << "Cannot open " << test_profile_ << strerror(errno); + LOG(ERROR) << "Cannot open " << test_profile_ << strerror(errno); return -1; } bool result = ProfileCompilationInfo::GenerateTestProfile(profile_test_fd, - test_profile_num_dex_, - test_profile_method_ratio_, - test_profile_class_ratio_); + test_profile_num_dex_, + test_profile_method_ratio_, + test_profile_class_ratio_); close(profile_test_fd); // ignore close result. return result ? 0 : -1; } @@ -405,12 +653,15 @@ class ProfMan FINAL { std::vector<std::string> profile_files_; std::vector<int> profile_files_fd_; std::vector<std::string> dex_locations_; + std::vector<std::string> apk_files_; std::vector<int> apks_fd_; std::string reference_profile_file_; int reference_profile_file_fd_; bool dump_only_; + bool dump_classes_; int dump_output_to_fd_; std::string test_profile_; + std::string create_profile_from_file_; uint16_t test_profile_num_dex_; uint16_t test_profile_method_ratio_; uint16_t test_profile_class_ratio_; @@ -430,6 +681,12 @@ static int profman(int argc, char** argv) { if (profman.ShouldOnlyDumpProfile()) { return profman.DumpProfileInfo(); } + if (profman.ShouldOnlyDumpClasses()) { + return profman.DumpClasses(); + } + if (profman.ShouldCreateProfile()) { + return profman.CreateProfile(); + } // Process profile information and assess if we need to do a profile guided compilation. // This operation involves I/O. return profman.ProcessProfiles(); diff --git a/runtime/Android.bp b/runtime/Android.bp index 9585ba2d8e..d3a81a9add 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -123,6 +123,7 @@ cc_defaults { "memory_region.cc", "method_handles.cc", "mirror/array.cc", + "mirror/call_site.cc", "mirror/class.cc", "mirror/class_ext.cc", "mirror/dex_cache.cc", @@ -131,6 +132,7 @@ cc_defaults { "mirror/field.cc", "mirror/method.cc", "mirror/method_handle_impl.cc", + "mirror/method_handles_lookup.cc", "mirror/method_type.cc", "mirror/object.cc", "mirror/reference.cc", @@ -546,6 +548,7 @@ art_cc_test { "gc/reference_queue_test.cc", "gc/space/dlmalloc_space_static_test.cc", "gc/space/dlmalloc_space_random_test.cc", + "gc/space/image_space_test.cc", "gc/space/large_object_space_test.cc", "gc/space/rosalloc_space_static_test.cc", "gc/space/rosalloc_space_random_test.cc", diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index a443a4060d..cfe8406fbf 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -965,9 +965,27 @@ ENTRY \name END \name .endm -ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +// Macro for string and type resolution and initialization. +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint + .extern \entrypoint +ENTRY \name + SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC + mov r1, r9 @ pass Thread::Current + bl \entrypoint @ (uint32_t index, Thread*) + cbz r0, 1f @ If result is null, deliver the OOME. + .cfi_remember_state + RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0 + bx lr + .cfi_restore_state +1: + DELIVER_PENDING_EXCEPTION_FRAME_READY +END \name +.endm + +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode /* * Called by managed code to resolve a static field and load a non-wide value. @@ -1066,27 +1084,6 @@ ENTRY art_quick_set64_static DELIVER_PENDING_EXCEPTION END art_quick_set64_static - /* - * Entry from managed code to resolve a string, this stub will - * check the dex cache for a matching string (the fast path), and if not found, - * it will allocate a String and deliver an exception on error. - * On success the String is returned. R0 holds the string index. - */ - -ENTRY art_quick_resolve_string - SETUP_SAVE_EVERYTHING_FRAME r1 @ save everything in case of GC - mov r1, r9 @ pass Thread::Current - bl artResolveStringFromCode @ (uint32_t type_idx, Thread*) - cbz r0, 1f @ If result is null, deliver the OOME. - .cfi_remember_state - RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0 - bx lr - .cfi_restore_state -1: - DELIVER_PENDING_EXCEPTION_FRAME_READY -END art_quick_resolve_string - - // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // Comment out allocators that have arm specific asm. @@ -2057,7 +2054,9 @@ ENTRY \name beq .Lret_forwarding_address\name .Lslow_rb_\name: - // Save IP: the kSaveEverything entrypoint art_quick_resolve_string makes a tail call here. + // Save IP: The kSaveEverything entrypoint art_quick_resolve_string used to + // make a tail call here. Currently, it serves only for stack alignment but + // we may reintroduce kSaveEverything calls here in the future. push {r0-r4, r9, ip, lr} @ save return address, core caller-save registers and ip .cfi_adjust_cfa_offset 32 .cfi_rel_offset r0, 0 diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 219d8b447a..bfbe4816ba 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1553,6 +1553,24 @@ ENTRY \name END \name .endm +// Macro for string and type resolution and initialization. +.macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint + .extern \entrypoint +ENTRY \name + SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl + mov x1, xSELF // pass Thread::Current + bl \entrypoint // (int32_t index, Thread* self) + cbz w0, 1f // If result is null, deliver the OOME. + .cfi_remember_state + RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0 + ret // return + .cfi_restore_state + .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598 +1: + DELIVER_PENDING_EXCEPTION_FRAME_READY +END \name +.endm + .macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER cbz w0, 1f // result zero branch over ret // return @@ -1571,10 +1589,11 @@ TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, * initializer and deliver the exception on error. On success the static storage base is * returned. */ -ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode -ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1 @@ -1604,27 +1623,6 @@ THREE_ARG_REF_DOWNCALL art_quick_set32_instance, artSet32InstanceFromCompiledCod THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER - /* - * Entry from managed code to resolve a string, this stub will - * check the dex cache for a matching string (the fast path), and if not found, - * it will allocate a String and deliver an exception on error. - * On success the String is returned. R0 holds the string index. - */ - -ENTRY art_quick_resolve_string - SETUP_SAVE_EVERYTHING_FRAME // save everything for stack crawl - mov x1, xSELF // pass Thread::Current - bl artResolveStringFromCode // (int32_t string_idx, Thread* self) - cbz w0, 1f // If result is null, deliver the OOME. - .cfi_remember_state - RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0 - ret // return - .cfi_restore_state - .cfi_def_cfa_offset FRAME_SIZE_SAVE_EVERYTHING // workaround for clang bug: 31975598 -1: - DELIVER_PENDING_EXCEPTION_FRAME_READY -END art_quick_resolve_string - // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_NON_TLAB_ALLOCATORS // Comment out allocators that have arm64 specific asm. @@ -2380,13 +2378,6 @@ END art_quick_indexof ENTRY \name // Reference is null, no work to do at all. cbz \wreg, .Lret_rb_\name - /* - * Allocate 46 stack slots * 8 = 368 bytes: - * - 20 slots for core registers X0-X19 - * - 24 slots for floating-point registers D0-D7 and D16-D31 - * - 1 slot for return address register XLR - * - 1 padding slot for 16-byte stack alignment - */ // Use wIP0 as temp and check the mark bit of the reference. wIP0 is not used by the compiler. ldr wIP0, [\xreg, #MIRROR_OBJECT_LOCK_WORD_OFFSET] tbz wIP0, #LOCK_WORD_MARK_BIT_SHIFT, .Lnot_marked_rb_\name @@ -2398,10 +2389,15 @@ ENTRY \name cmp wzr, wIP0, lsr #30 beq .Lret_forwarding_address\name .Lslow_rb_\name: - // We must not clobber IP0 since art_quick_resolve_string makes a tail call here and relies on - // IP0 being restored. + /* + * Allocate 44 stack slots * 8 = 352 bytes: + * - 20 slots for core registers X0-15, X17-X19, LR + * - 24 slots for floating-point registers D0-D7 and D16-D31 + */ + // We must not clobber IP1 since code emitted for HLoadClass and HLoadString + // relies on IP1 being preserved. // Save all potentially live caller-save core registers. - SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 368 + SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 352 SAVE_TWO_REGS x2, x3, 16 SAVE_TWO_REGS x4, x5, 32 SAVE_TWO_REGS x6, x7, 48 @@ -2409,8 +2405,8 @@ ENTRY \name SAVE_TWO_REGS x10, x11, 80 SAVE_TWO_REGS x12, x13, 96 SAVE_TWO_REGS x14, x15, 112 - SAVE_TWO_REGS x16, x17, 128 - SAVE_TWO_REGS x18, x19, 144 + SAVE_TWO_REGS x17, x18, 128 // Skip x16, i.e. IP0. + SAVE_TWO_REGS x19, xLR, 144 // Save also return address. // Save all potentially live caller-save floating-point registers. stp d0, d1, [sp, #160] stp d2, d3, [sp, #176] @@ -2424,9 +2420,6 @@ ENTRY \name stp d26, d27, [sp, #304] stp d28, d29, [sp, #320] stp d30, d31, [sp, #336] - // Save return address. - // (sp + #352 is a padding slot) - SAVE_REG xLR, 360 .ifnc \wreg, w0 mov w0, \wreg // Pass arg1 - obj from `wreg` @@ -2446,8 +2439,8 @@ ENTRY \name POP_REGS_NE x10, x11, 80, \xreg POP_REGS_NE x12, x13, 96, \xreg POP_REGS_NE x14, x15, 112, \xreg - POP_REGS_NE x16, x17, 128, \xreg - POP_REGS_NE x18, x19, 144, \xreg + POP_REGS_NE x17, x18, 128, \xreg + POP_REGS_NE x19, xLR, 144, \xreg // Restore also return address. // Restore floating-point registers. ldp d0, d1, [sp, #160] ldp d2, d3, [sp, #176] @@ -2461,9 +2454,8 @@ ENTRY \name ldp d26, d27, [sp, #304] ldp d28, d29, [sp, #320] ldp d30, d31, [sp, #336] - // Restore return address and remove padding. - RESTORE_REG xLR, 360 - DECREASE_FRAME 368 + // Remove frame and return. + DECREASE_FRAME 352 ret .Lret_forwarding_address\name: mvn wIP0, wIP0 diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S index 663cb6c62f..ec8ae85722 100644 --- a/runtime/arch/mips/quick_entrypoints_mips.S +++ b/runtime/arch/mips/quick_entrypoints_mips.S @@ -1576,9 +1576,87 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR +// A hand-written override for: +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) +.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name +ENTRY \c_name + # Fast path rosalloc allocation + # a0: type + # s1: Thread::Current + # ----------------------------- + # t1: object size + # t2: rosalloc run + # t3: thread stack top offset + # t4: thread stack bottom offset + # v0: free list head + # + # t5, t6 : temps + lw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation + lw $t4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # stack has any room left. + bgeu $t3, $t4, .Lslow_path_\c_name + + lw $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1). + li $t5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local + # allocation. Also does the + # initialized and finalizable checks. + bgtu $t1, $t5, .Lslow_path_\c_name + + # Compute the rosalloc bracket index from the size. Since the size is already aligned we can + # combine the two shifts together. + srl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) + + addu $t2, $t1, $s1 + lw $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2). + + # Load the free list head (v0). + # NOTE: this will be the return val. + lw $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + beqz $v0, .Lslow_path_\c_name + nop + + # Load the next pointer of the head and update the list head with the next pointer. + lw $t5, ROSALLOC_SLOT_NEXT_OFFSET($v0) + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + + # Store the class pointer in the header. This also overwrites the first pointer. The offsets are + # asserted to match. + +#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET +#error "Class pointer needs to overwrite next pointer." +#endif + + POISON_HEAP_REF $a0 + sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0) + + # Push the new object onto the thread local allocation stack and increment the thread local + # allocation stack top. + sw $v0, 0($t3) + addiu $t3, $t3, COMPRESSED_REFERENCE_SIZE + sw $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) + + # Decrement the size of the free list. + lw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + addiu $t5, $t5, -1 + sw $t5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + + sync # Fence. + + jalr $zero, $ra + nop + + .Lslow_path_\c_name: + SETUP_SAVE_REFS_ONLY_FRAME + la $t9, \cxx_name + jalr $t9 + move $a1, $s1 # Pass self as argument. + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END \c_name +.endm + +ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc +ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) @@ -1964,67 +2042,158 @@ ENTRY_NO_GP art_quick_indexof /* $a0 holds address of "this" */ /* $a1 holds "ch" */ /* $a2 holds "fromIndex" */ - lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - slt $t1, $a2, $zero # if fromIndex < 0 +#if (STRING_COMPRESSION_FEATURE) + lw $a3, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this +#else + lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() +#endif + slt $t1, $a2, $zero # if fromIndex < 0 #if defined(_MIPS_ARCH_MIPS32R6) || defined(_MIPS_ARCH_MIPS64R6) - seleqz $a2, $a2, $t1 # fromIndex = 0; + seleqz $a2, $a2, $t1 # fromIndex = 0; #else - movn $a2, $zero, $t1 # fromIndex = 0; + movn $a2, $zero, $t1 # fromIndex = 0; #endif - subu $t0, $t0, $a2 # this.length() - fromIndex - blez $t0, 6f # if this.length()-fromIndex <= 0 - li $v0, -1 # return -1; - - sll $v0, $a2, 1 # $a0 += $a2 * 2 - addu $a0, $a0, $v0 # " ditto " - move $v0, $a2 # Set i to fromIndex. +#if (STRING_COMPRESSION_FEATURE) + srl $t0, $a3, 1 # $a3 holds count (with flag) and $t0 holds actual length +#endif + subu $t0, $t0, $a2 # this.length() - fromIndex + blez $t0, 6f # if this.length()-fromIndex <= 0 + li $v0, -1 # return -1; + +#if (STRING_COMPRESSION_FEATURE) + sll $a3, $a3, 31 # Extract compression flag. + beqz $a3, .Lstring_indexof_compressed + move $t2, $a0 # Save a copy in $t2 to later compute result (in branch delay slot). +#endif + sll $v0, $a2, 1 # $a0 += $a2 * 2 + addu $a0, $a0, $v0 # " ditto " + move $v0, $a2 # Set i to fromIndex. 1: - lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch - beq $t3, $a1, 6f # return i; - addu $a0, $a0, 2 # i++ - subu $t0, $t0, 1 # this.length() - i - bnez $t0, 1b # while this.length() - i > 0 - addu $v0, $v0, 1 # i++ + lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch + beq $t3, $a1, 6f # return i; + addu $a0, $a0, 2 # i++ + subu $t0, $t0, 1 # this.length() - i + bnez $t0, 1b # while this.length() - i > 0 + addu $v0, $v0, 1 # i++ - li $v0, -1 # if this.length() - i <= 0 - # return -1; + li $v0, -1 # if this.length() - i <= 0 + # return -1; 6: - j $ra - nop + j $ra + nop + +#if (STRING_COMPRESSION_FEATURE) +.Lstring_indexof_compressed: + addu $a0, $a0, $a2 # $a0 += $a2 + +.Lstring_indexof_compressed_loop: + lbu $t3, MIRROR_STRING_VALUE_OFFSET($a0) + beq $t3, $a1, .Lstring_indexof_compressed_matched + subu $t0, $t0, 1 + bgtz $t0, .Lstring_indexof_compressed_loop + addu $a0, $a0, 1 + +.Lstring_indexof_nomatch: + jalr $zero, $ra + li $v0, -1 # return -1; + +.Lstring_indexof_compressed_matched: + jalr $zero, $ra + subu $v0, $a0, $t2 # return (current - start); +#endif END art_quick_indexof /* java.lang.String.compareTo(String anotherString) */ ENTRY_NO_GP art_quick_string_compareto /* $a0 holds address of "this" */ /* $a1 holds address of "anotherString" */ - beq $a0, $a1, 9f # this and anotherString are the same object - move $v0, $zero + beq $a0, $a1, .Lstring_compareto_length_diff # this and anotherString are the same object + move $a3, $a2 # trick to return 0 (it returns a2 - a3) + +#if (STRING_COMPRESSION_FEATURE) + lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this + lw $t1, MIRROR_STRING_COUNT_OFFSET($a1) # 'count' field of anotherString + sra $a2, $t0, 1 # this.length() + sra $a3, $t1, 1 # anotherString.length() +#else + lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() +#endif - lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() - MINu $t2, $a2, $a3 -# $t2 now holds min(this.length(),anotherString.length()) + MINu $t2, $a2, $a3 + # $t2 now holds min(this.length(),anotherString.length()) - beqz $t2, 9f # while min(this.length(),anotherString.length())-i != 0 - subu $v0, $a2, $a3 # if $t2==0 return - # (this.length() - anotherString.length()) -1: - lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) - lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) - bne $t0, $t1, 9f # if this.charAt(i) != anotherString.charAt(i) - subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i)) - addiu $a0, $a0, 2 # point at this.charAt(i++) - subu $t2, $t2, 1 # new value of - # min(this.length(),anotherString.length())-i - bnez $t2, 1b - addiu $a1, $a1, 2 # point at anotherString.charAt(i++) - subu $v0, $a2, $a3 - -9: - j $ra - nop + # while min(this.length(),anotherString.length())-i != 0 + beqz $t2, .Lstring_compareto_length_diff # if $t2==0 + nop # return (this.length() - anotherString.length()) + +#if (STRING_COMPRESSION_FEATURE) + # Differ cases: + sll $t3, $t0, 31 + beqz $t3, .Lstring_compareto_this_is_compressed + sll $t3, $t1, 31 # In branch delay slot. + beqz $t3, .Lstring_compareto_that_is_compressed + nop + b .Lstring_compareto_both_not_compressed + nop + +.Lstring_compareto_this_is_compressed: + beqz $t3, .Lstring_compareto_both_compressed + nop + /* If (this->IsCompressed() && that->IsCompressed() == false) */ +.Lstring_compareto_loop_comparison_this_compressed: + lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, .Lstring_compareto_char_diff + addiu $a0, $a0, 1 # point at this.charAt(i++) - compressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_loop_comparison_this_compressed + addiu $a1, $a1, 2 # point at anotherString.charAt(i++) - uncompressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_that_is_compressed: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, .Lstring_compareto_char_diff + addiu $a0, $a0, 2 # point at this.charAt(i++) - uncompressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_that_is_compressed + addiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_both_compressed: + lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, .Lstring_compareto_char_diff + addiu $a0, $a0, 1 # point at this.charAt(i++) - compressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_both_compressed + addiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) +#endif + +.Lstring_compareto_both_not_compressed: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bne $t0, $t1, .Lstring_compareto_char_diff # if this.charAt(i) != anotherString.charAt(i) + # return (this.charAt(i) - anotherString.charAt(i)) + addiu $a0, $a0, 2 # point at this.charAt(i++) + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_both_not_compressed + addiu $a1, $a1, 2 # point at anotherString.charAt(i++) + +.Lstring_compareto_length_diff: + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_char_diff: + jalr $zero, $ra + subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i)) END art_quick_string_compareto .extern artInvokePolymorphic diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S index 5fee575331..28d7c77938 100644 --- a/runtime/arch/mips64/quick_entrypoints_mips64.S +++ b/runtime/arch/mips64/quick_entrypoints_mips64.S @@ -1533,8 +1533,85 @@ END \name // Generate the allocation entrypoints for each allocator. GENERATE_ALLOC_ENTRYPOINTS_FOR_EACH_ALLOCATOR -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) -GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) +// A hand-written override for: +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_rosalloc, RosAlloc) +// GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(_rosalloc, RosAlloc) +.macro ART_QUICK_ALLOC_OBJECT_ROSALLOC c_name, cxx_name +ENTRY \c_name + # Fast path rosalloc allocation + # a0: type + # s1: Thread::Current + # ----------------------------- + # t1: object size + # t2: rosalloc run + # t3: thread stack top offset + # a4: thread stack bottom offset + # v0: free list head + # + # a5, a6 : temps + ld $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) # Check if thread local allocation stack + ld $a4, THREAD_LOCAL_ALLOC_STACK_END_OFFSET($s1) # has any room left. + bgeuc $t3, $a4, .Lslow_path_\c_name + + lwu $t1, MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET($a0) # Load object size (t1). + li $a5, ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE # Check if size is for a thread local + # allocation. Also does the initialized + # and finalizable checks. + bltuc $a5, $t1, .Lslow_path_\c_name + + # Compute the rosalloc bracket index from the size. Since the size is already aligned we can + # combine the two shifts together. + dsrl $t1, $t1, (ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT - POINTER_SIZE_SHIFT) + + daddu $t2, $t1, $s1 + ld $t2, (THREAD_ROSALLOC_RUNS_OFFSET - __SIZEOF_POINTER__)($t2) # Load rosalloc run (t2). + + # Load the free list head (v0). + # NOTE: this will be the return val. + ld $v0, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + beqzc $v0, .Lslow_path_\c_name + + # Load the next pointer of the head and update the list head with the next pointer. + ld $a5, ROSALLOC_SLOT_NEXT_OFFSET($v0) + sd $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET)($t2) + + # Store the class pointer in the header. This also overwrites the first pointer. The offsets are + # asserted to match. + +#if ROSALLOC_SLOT_NEXT_OFFSET != MIRROR_OBJECT_CLASS_OFFSET +#error "Class pointer needs to overwrite next pointer." +#endif + + POISON_HEAP_REF $a0 + sw $a0, MIRROR_OBJECT_CLASS_OFFSET($v0) + + # Push the new object onto the thread local allocation stack and increment the thread local + # allocation stack top. + sd $v0, 0($t3) + daddiu $t3, $t3, COMPRESSED_REFERENCE_SIZE + sd $t3, THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET($s1) + + # Decrement the size of the free list. + lw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + addiu $a5, $a5, -1 + sw $a5, (ROSALLOC_RUN_FREE_LIST_OFFSET + ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET)($t2) + + sync # Fence. + + jalr $zero, $ra + .cpreturn # Restore gp from t8 in branch delay slot. + +.Lslow_path_\c_name: + SETUP_SAVE_REFS_ONLY_FRAME + jal \cxx_name + move $a1 ,$s1 # Pass self as argument. + RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +END \c_name +.endm + +ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_resolved_rosalloc, artAllocObjectFromCodeResolvedRosAlloc +ART_QUICK_ALLOC_OBJECT_ROSALLOC art_quick_alloc_object_initialized_rosalloc, artAllocObjectFromCodeInitializedRosAlloc + GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB) GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_region_tlab, RegionTLAB) @@ -1823,32 +1900,91 @@ END art_quick_deoptimize_from_compiled_code ENTRY_NO_GP art_quick_string_compareto /* $a0 holds address of "this" */ /* $a1 holds address of "anotherString" */ - beq $a0,$a1,9f # this and anotherString are the same object - move $v0,$zero + move $a2, $zero + beq $a0, $a1, .Lstring_compareto_length_diff # this and anotherString are the same object + move $a3, $zero # return 0 (it returns a2 - a3) + +#if (STRING_COMPRESSION_FEATURE) + lw $a4, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this + lw $a5, MIRROR_STRING_COUNT_OFFSET($a1) # 'count' field of anotherString + sra $a2, $a4, 1 # this.length() + sra $a3, $a5, 1 # anotherString.length() +#else + lw $a2, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() + lw $a3, MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() +#endif - lw $a2,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - lw $a3,MIRROR_STRING_COUNT_OFFSET($a1) # anotherString.length() - MINu $t2, $a2, $a3 -# $t2 now holds min(this.length(),anotherString.length()) + MINu $t2, $a2, $a3 + # $t2 now holds min(this.length(),anotherString.length()) - beqz $t2,9f # while min(this.length(),anotherString.length())-i != 0 - subu $v0,$a2,$a3 # if $t2==0 return - # (this.length() - anotherString.length()) -1: - lhu $t0,MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) - lhu $t1,MIRROR_STRING_VALUE_OFFSET($a1) - bne $t0,$t1,9f # if this.charAt(i) != anotherString.charAt(i) - subu $v0,$t0,$t1 # return (this.charAt(i) - anotherString.charAt(i)) - daddiu $a0,$a0,2 # point at this.charAt(i++) - subu $t2,$t2,1 # new value of - # min(this.length(),anotherString.length())-i - bnez $t2,1b - daddiu $a1,$a1,2 # point at anotherString.charAt(i++) - subu $v0,$a2,$a3 - -9: - j $ra - nop + # while min(this.length(),anotherString.length())-i != 0 + beqzc $t2, .Lstring_compareto_length_diff # if $t2==0 + # return (this.length() - anotherString.length()) + +#if (STRING_COMPRESSION_FEATURE) + # Differ cases: + dext $a6, $a4, 0, 1 + beqz $a6, .Lstring_compareto_this_is_compressed + dext $a6, $a5, 0, 1 # In branch delay slot. + beqz $a6, .Lstring_compareto_that_is_compressed + nop + b .Lstring_compareto_both_not_compressed + nop + +.Lstring_compareto_this_is_compressed: + beqzc $a6, .Lstring_compareto_both_compressed + /* If (this->IsCompressed() && that->IsCompressed() == false) */ +.Lstring_compareto_loop_comparison_this_compressed: + lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bnec $t0, $t1, .Lstring_compareto_char_diff + daddiu $a0, $a0, 1 # point at this.charAt(i++) - compressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_loop_comparison_this_compressed + daddiu $a1, $a1, 2 # point at anotherString.charAt(i++) - uncompressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_that_is_compressed: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bnec $t0, $t1, .Lstring_compareto_char_diff + daddiu $a0, $a0, 2 # point at this.charAt(i++) - uncompressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_that_is_compressed + daddiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_both_compressed: + lbu $t0, MIRROR_STRING_VALUE_OFFSET($a0) + lbu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bnec $t0, $t1, .Lstring_compareto_char_diff + daddiu $a0, $a0, 1 # point at this.charAt(i++) - compressed + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_both_compressed + daddiu $a1, $a1, 1 # point at anotherString.charAt(i++) - compressed + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) +#endif + +.Lstring_compareto_both_not_compressed: + lhu $t0, MIRROR_STRING_VALUE_OFFSET($a0) # while this.charAt(i) == anotherString.charAt(i) + lhu $t1, MIRROR_STRING_VALUE_OFFSET($a1) + bnec $t0, $t1, .Lstring_compareto_char_diff # if this.charAt(i) != anotherString.charAt(i) + # return (this.charAt(i) - anotherString.charAt(i)) + daddiu $a0, $a0, 2 # point at this.charAt(i++) + subu $t2, $t2, 1 # new value of min(this.length(),anotherString.length())-i + bnez $t2, .Lstring_compareto_both_not_compressed + daddiu $a1, $a1, 2 # point at anotherString.charAt(i++) + +.Lstring_compareto_length_diff: + jalr $zero, $ra + subu $v0, $a2, $a3 # return (this.length() - anotherString.length()) + +.Lstring_compareto_char_diff: + jalr $zero, $ra + subu $v0, $t0, $t1 # return (this.charAt(i) - anotherString.charAt(i)) END art_quick_string_compareto /* java.lang.String.indexOf(int ch, int fromIndex=0) */ @@ -1856,31 +1992,64 @@ ENTRY_NO_GP art_quick_indexof /* $a0 holds address of "this" */ /* $a1 holds "ch" */ /* $a2 holds "fromIndex" */ - lw $t0,MIRROR_STRING_COUNT_OFFSET($a0) # this.length() - slt $at, $a2, $zero # if fromIndex < 0 - seleqz $a2, $a2, $at # fromIndex = 0; - subu $t0,$t0,$a2 # this.length() - fromIndex - blez $t0,6f # if this.length()-fromIndex <= 0 - li $v0,-1 # return -1; +#if (STRING_COMPRESSION_FEATURE) + lw $a3, MIRROR_STRING_COUNT_OFFSET($a0) # 'count' field of this +#else + lw $t0, MIRROR_STRING_COUNT_OFFSET($a0) # this.length() +#endif + slt $at, $a2, $zero # if fromIndex < 0 + seleqz $a2, $a2, $at # fromIndex = 0; +#if (STRING_COMPRESSION_FEATURE) + srl $t0, $a3, 1 # $a3 holds count (with flag) and $t0 holds actual length +#endif + subu $t0, $t0, $a2 # this.length() - fromIndex + blez $t0, 6f # if this.length()-fromIndex <= 0 + li $v0, -1 # return -1; - sll $v0,$a2,1 # $a0 += $a2 * 2 - daddu $a0,$a0,$v0 # " ditto " - move $v0,$a2 # Set i to fromIndex. +#if (STRING_COMPRESSION_FEATURE) + dext $a3, $a3, 0, 1 # Extract compression flag. + beqzc $a3, .Lstring_indexof_compressed +#endif + + sll $v0, $a2, 1 # $a0 += $a2 * 2 + daddu $a0, $a0, $v0 # " ditto " + move $v0, $a2 # Set i to fromIndex. 1: - lhu $t3,MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch - beq $t3,$a1,6f # return i; - daddu $a0,$a0,2 # i++ - subu $t0,$t0,1 # this.length() - i - bnez $t0,1b # while this.length() - i > 0 - addu $v0,$v0,1 # i++ + lhu $t3, MIRROR_STRING_VALUE_OFFSET($a0) # if this.charAt(i) == ch + beq $t3, $a1, 6f # return i; + daddu $a0, $a0, 2 # i++ + subu $t0, $t0, 1 # this.length() - i + bnez $t0, 1b # while this.length() - i > 0 + addu $v0, $v0, 1 # i++ - li $v0,-1 # if this.length() - i <= 0 - # return -1; + li $v0, -1 # if this.length() - i <= 0 + # return -1; 6: - j $ra - nop + j $ra + nop + +#if (STRING_COMPRESSION_FEATURE) +.Lstring_indexof_compressed: + move $a4, $a0 # Save a copy in $a4 to later compute result. + daddu $a0, $a0, $a2 # $a0 += $a2 + +.Lstring_indexof_compressed_loop: + lbu $t3, MIRROR_STRING_VALUE_OFFSET($a0) + beq $t3, $a1, .Lstring_indexof_compressed_matched + subu $t0, $t0, 1 + bgtz $t0, .Lstring_indexof_compressed_loop + daddu $a0, $a0, 1 + +.Lstring_indexof_nomatch: + jalr $zero, $ra + li $v0, -1 # return -1; + +.Lstring_indexof_compressed_matched: + jalr $zero, $ra + dsubu $v0, $a0, $a4 # return (current - start); +#endif END art_quick_indexof .extern artInvokePolymorphic diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc index 0bf08a6d97..207bf9d365 100644 --- a/runtime/arch/stub_test.cc +++ b/runtime/arch/stub_test.cc @@ -984,7 +984,7 @@ TEST_F(StubTest, AllocObject) { while (length > 10) { Handle<mirror::Object> h(hsp->NewHandle<mirror::Object>( mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), ca.Get(), length / 4))); - if (self->IsExceptionPending() || h.Get() == nullptr) { + if (self->IsExceptionPending() || h == nullptr) { self->ClearException(); // Try a smaller length @@ -1003,7 +1003,7 @@ TEST_F(StubTest, AllocObject) { // Allocate simple objects till it fails. while (!self->IsExceptionPending()) { Handle<mirror::Object> h = hsp->NewHandle(c->AllocObject(soa.Self())); - if (!self->IsExceptionPending() && h.Get() != nullptr) { + if (!self->IsExceptionPending() && h != nullptr) { handles.push_back(h); } } diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 76615e843b..8c907e0790 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -922,6 +922,31 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO +// Macro for string and type resolution and initialization. +MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) + DEFINE_FUNCTION VAR(c_name) + SETUP_SAVE_EVERYTHING_FRAME ebx, ebx // save ref containing registers for GC + // Outgoing argument set up + subl MACRO_LITERAL(8), %esp // push padding + CFI_ADJUST_CFA_OFFSET(8) + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + PUSH eax // pass arg1 + call CALLVAR(cxx_name) // cxx_name(arg1, Thread*) + addl MACRO_LITERAL(16), %esp // pop arguments + CFI_ADJUST_CFA_OFFSET(-16) + testl %eax, %eax // If result is null, deliver the OOME. + jz 1f + CFI_REMEMBER_STATE + RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX // restore frame up to return address + ret // return + CFI_RESTORE_STATE + CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598 +1: + DELIVER_PENDING_EXCEPTION_FRAME_READY + END_FUNCTION VAR(c_name) +END_MACRO + MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER) testl %eax, %eax // eax == 0 ? jz 1f // if eax == 0 goto 1 @@ -1245,31 +1270,10 @@ GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved16_tlab, artAllocArrayFr GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64 -DEFINE_FUNCTION art_quick_resolve_string - SETUP_SAVE_EVERYTHING_FRAME ebx, ebx - // Outgoing argument set up - subl LITERAL(8), %esp // push padding - CFI_ADJUST_CFA_OFFSET(8) - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - PUSH eax // pass arg1 - call SYMBOL(artResolveStringFromCode) - addl LITERAL(16), %esp // pop arguments - CFI_ADJUST_CFA_OFFSET(-16) - testl %eax, %eax // If result is null, deliver the OOME. - jz 1f - CFI_REMEMBER_STATE - RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX - ret - CFI_RESTORE_STATE - CFI_DEF_CFA(esp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598 -1: - DELIVER_PENDING_EXCEPTION_FRAME_READY -END_FUNCTION art_quick_resolve_string - -ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index a1ae858735..f1be52eeb6 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -950,6 +950,26 @@ MACRO3(THREE_ARG_REF_DOWNCALL, c_name, cxx_name, return_macro) END_FUNCTION VAR(c_name) END_MACRO +// Macro for string and type resolution and initialization. +MACRO2(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name) + DEFINE_FUNCTION VAR(c_name) + SETUP_SAVE_EVERYTHING_FRAME // save everything for GC + // Outgoing argument set up + movl %eax, %edi // pass string index + movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() + call CALLVAR(cxx_name) // cxx_name(arg0, Thread*) + testl %eax, %eax // If result is null, deliver the OOME. + jz 1f + CFI_REMEMBER_STATE + RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address + ret + CFI_RESTORE_STATE + CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598 +1: + DELIVER_PENDING_EXCEPTION_FRAME_READY + END_FUNCTION VAR(c_name) +END_MACRO + MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER) testq %rax, %rax // rax == 0 ? jz 1f // if rax == 0 goto 1 @@ -1270,27 +1290,10 @@ DEFINE_FUNCTION art_quick_alloc_object_initialized_region_tlab ALLOC_OBJECT_TLAB_SLOW_PATH artAllocObjectFromCodeInitializedRegionTLAB END_FUNCTION art_quick_alloc_object_initialized_region_tlab -DEFINE_FUNCTION art_quick_resolve_string - SETUP_SAVE_EVERYTHING_FRAME - // Outgoing argument set up - movl %eax, %edi // pass string index - movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current() - call SYMBOL(artResolveStringFromCode) // artResolveStringFromCode(arg0, Thread*) - - testl %eax, %eax // If result is null, deliver the OOME. - jz 1f - CFI_REMEMBER_STATE - RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address - ret - CFI_RESTORE_STATE - CFI_DEF_CFA(rsp, FRAME_SIZE_SAVE_EVERYTHING) // workaround for clang bug: 31975598 -1: - DELIVER_PENDING_EXCEPTION_FRAME_READY -END_FUNCTION art_quick_resolve_string - -ONE_ARG_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER -ONE_ARG_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_static_storage, artInitializeStaticStorageFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type, artInitializeTypeFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode +ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 6cb8544617..9d74e7c92b 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -67,7 +67,6 @@ ArtMethod* ArtMethod::GetNonObsoleteMethod() { } ArtMethod* ArtMethod::GetSingleImplementation(PointerSize pointer_size) { - DCHECK(!IsNative()); if (!IsAbstract()) { // A non-abstract's single implementation is itself. return this; @@ -275,7 +274,7 @@ uint32_t ArtMethod::FindCatchBlock(Handle<mirror::Class> exception_type, *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION); } // Put the exception back. - if (exception.Get() != nullptr) { + if (exception != nullptr) { self->SetException(exception.Get()); } return found_dex_pc; @@ -442,12 +441,56 @@ static uint32_t GetOatMethodIndexFromMethodIndex(const DexFile& dex_file, UNREACHABLE(); } +// We use the method's DexFile and declaring class name to find the OatMethod for an obsolete +// method. This is extremely slow but we need it if we want to be able to have obsolete native +// methods since we need this to find the size of its stack frames. +// +// NB We could (potentially) do this differently and rely on the way the transformation is applied +// in order to use the entrypoint to find this information. However, for debugging reasons (most +// notably making sure that new invokes of obsolete methods fail) we choose to instead get the data +// directly from the dex file. +static const OatFile::OatMethod FindOatMethodFromDexFileFor(ArtMethod* method, bool* found) + REQUIRES_SHARED(Locks::mutator_lock_) { + DCHECK(method->IsObsolete() && method->IsNative()); + const DexFile* dex_file = method->GetDexFile(); + + // recreate the class_def_index from the descriptor. + std::string descriptor_storage; + const DexFile::TypeId* declaring_class_type_id = + dex_file->FindTypeId(method->GetDeclaringClass()->GetDescriptor(&descriptor_storage)); + CHECK(declaring_class_type_id != nullptr); + dex::TypeIndex declaring_class_type_index = dex_file->GetIndexForTypeId(*declaring_class_type_id); + const DexFile::ClassDef* declaring_class_type_def = + dex_file->FindClassDef(declaring_class_type_index); + CHECK(declaring_class_type_def != nullptr); + uint16_t declaring_class_def_index = dex_file->GetIndexForClassDef(*declaring_class_type_def); + + size_t oat_method_index = GetOatMethodIndexFromMethodIndex(*dex_file, + declaring_class_def_index, + method->GetDexMethodIndex()); + + OatFile::OatClass oat_class = OatFile::FindOatClass(*dex_file, + declaring_class_def_index, + found); + if (!(*found)) { + return OatFile::OatMethod::Invalid(); + } + return oat_class.GetOatMethod(oat_method_index); +} + static const OatFile::OatMethod FindOatMethodFor(ArtMethod* method, PointerSize pointer_size, bool* found) REQUIRES_SHARED(Locks::mutator_lock_) { - // We shouldn't be calling this with obsolete methods. - DCHECK(!method->IsObsolete()); + if (UNLIKELY(method->IsObsolete())) { + // We shouldn't be calling this with obsolete methods except for native obsolete methods for + // which we need to use the oat method to figure out how large the quick frame is. + DCHECK(method->IsNative()) << "We should only be finding the OatMethod of obsolete methods in " + << "order to allow stack walking. Other obsolete methods should " + << "never need to access this information."; + DCHECK_EQ(pointer_size, kRuntimePointerSize) << "Obsolete method in compiler!"; + return FindOatMethodFromDexFileFor(method, found); + } // Although we overwrite the trampoline of non-static methods, we may get here via the resolution // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); @@ -490,7 +533,7 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param const auto& proto_id = dex_file->GetMethodPrototype(method_id); const DexFile::TypeList* proto_params = dex_file->GetProtoParameters(proto_id); auto count = proto_params != nullptr ? proto_params->Size() : 0u; - auto param_len = params.Get() != nullptr ? params->GetLength() : 0u; + auto param_len = params != nullptr ? params->GetLength() : 0u; if (param_len != count) { return false; } diff --git a/runtime/art_method.h b/runtime/art_method.h index 383630363e..3d51fdde94 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -432,6 +432,7 @@ class ArtMethod FINAL { } ProfilingInfo* GetProfilingInfo(PointerSize pointer_size) { + DCHECK(!IsNative()); return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size)); } diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 6e102be1a1..7bba944ca8 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -72,6 +72,7 @@ Uninterruptible Roles::uninterruptible_; ReaderWriterMutex* Locks::jni_globals_lock_ = nullptr; Mutex* Locks::jni_weak_globals_lock_ = nullptr; ReaderWriterMutex* Locks::dex_lock_ = nullptr; +std::vector<BaseMutex*> Locks::expected_mutexes_on_weak_ref_access_; struct AllMutexData { // A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait). @@ -146,7 +147,10 @@ class ScopedContentionRecorder FINAL : public ValueObject { const uint64_t start_nano_time_; }; -BaseMutex::BaseMutex(const char* name, LockLevel level) : level_(level), name_(name) { +BaseMutex::BaseMutex(const char* name, LockLevel level) + : level_(level), + name_(name), + should_respond_to_empty_checkpoint_request_(false) { if (kLogLockContentions) { ScopedAllMutexesLock mu(this); std::set<BaseMutex*>** all_mutexes_ptr = &gAllMutexData->all_mutexes; @@ -377,6 +381,9 @@ void Mutex::ExclusiveLock(Thread* self) { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); num_contenders_++; + if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) { + self->CheckEmptyCheckpointFromMutex(); + } if (futex(state_.Address(), FUTEX_WAIT, 1, nullptr, nullptr, 0) != 0) { // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. @@ -519,6 +526,18 @@ std::ostream& operator<<(std::ostream& os, const Mutex& mu) { return os; } +void Mutex::WakeupToRespondToEmptyCheckpoint() { +#if ART_USE_FUTEXES + // Wake up all the waiters so they will respond to the emtpy checkpoint. + DCHECK(should_respond_to_empty_checkpoint_request_); + if (UNLIKELY(num_contenders_.LoadRelaxed() > 0)) { + futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); + } +#else + LOG(FATAL) << "Non futex case isn't supported."; +#endif +} + ReaderWriterMutex::ReaderWriterMutex(const char* name, LockLevel level) : BaseMutex(name, level) #if ART_USE_FUTEXES @@ -563,6 +582,9 @@ void ReaderWriterMutex::ExclusiveLock(Thread* self) { // Failed to acquire, hang up. ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); ++num_pending_writers_; + if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) { + self->CheckEmptyCheckpointFromMutex(); + } if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) { // EAGAIN and EINTR both indicate a spurious failure, try again from the beginning. // We don't use TEMP_FAILURE_RETRY so we can intentionally retry to acquire the lock. @@ -639,6 +661,9 @@ bool ReaderWriterMutex::ExclusiveLockWithTimeout(Thread* self, int64_t ms, int32 } ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid()); ++num_pending_writers_; + if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) { + self->CheckEmptyCheckpointFromMutex(); + } if (futex(state_.Address(), FUTEX_WAIT, cur_state, &rel_ts, nullptr, 0) != 0) { if (errno == ETIMEDOUT) { --num_pending_writers_; @@ -677,6 +702,9 @@ void ReaderWriterMutex::HandleSharedLockContention(Thread* self, int32_t cur_sta // Owner holds it exclusively, hang up. ScopedContentionRecorder scr(this, GetExclusiveOwnerTid(), SafeGetTid(self)); ++num_pending_readers_; + if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) { + self->CheckEmptyCheckpointFromMutex(); + } if (futex(state_.Address(), FUTEX_WAIT, cur_state, nullptr, nullptr, 0) != 0) { if (errno != EAGAIN && errno != EINTR) { PLOG(FATAL) << "futex wait failed for " << name_; @@ -749,6 +777,19 @@ std::ostream& operator<<(std::ostream& os, const MutatorMutex& mu) { return os; } +void ReaderWriterMutex::WakeupToRespondToEmptyCheckpoint() { +#if ART_USE_FUTEXES + // Wake up all the waiters so they will respond to the emtpy checkpoint. + DCHECK(should_respond_to_empty_checkpoint_request_); + if (UNLIKELY(num_pending_readers_.LoadRelaxed() > 0 || + num_pending_writers_.LoadRelaxed() > 0)) { + futex(state_.Address(), FUTEX_WAKE, -1, nullptr, nullptr, 0); + } +#else + LOG(FATAL) << "Non futex case isn't supported."; +#endif +} + ConditionVariable::ConditionVariable(const char* name, Mutex& guard) : name_(name), guard_(guard) { #if ART_USE_FUTEXES @@ -1121,6 +1162,12 @@ void Locks::Init() { #undef UPDATE_CURRENT_LOCK_LEVEL + // List of mutexes that we may hold when accessing a weak ref. + dex_lock_->SetShouldRespondToEmptyCheckpointRequest(true); + expected_mutexes_on_weak_ref_access_.push_back(dex_lock_); + classlinker_classes_lock_->SetShouldRespondToEmptyCheckpointRequest(true); + expected_mutexes_on_weak_ref_access_.push_back(classlinker_classes_lock_); + InitConditions(); } } diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index ffe18c6a50..9b6938f9bf 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -152,6 +152,16 @@ class BaseMutex { static void DumpAll(std::ostream& os); + bool ShouldRespondToEmptyCheckpointRequest() const { + return should_respond_to_empty_checkpoint_request_; + } + + void SetShouldRespondToEmptyCheckpointRequest(bool value) { + should_respond_to_empty_checkpoint_request_ = value; + } + + virtual void WakeupToRespondToEmptyCheckpoint() = 0; + protected: friend class ConditionVariable; @@ -168,6 +178,7 @@ class BaseMutex { const LockLevel level_; // Support for lock hierarchy. const char* const name_; + bool should_respond_to_empty_checkpoint_request_; // A log entry that records contention but makes no guarantee that either tid will be held live. struct ContentionLogEntry { @@ -266,6 +277,8 @@ class LOCKABLE Mutex : public BaseMutex { // For negative capabilities in clang annotations. const Mutex& operator!() const { return *this; } + void WakeupToRespondToEmptyCheckpoint() OVERRIDE; + private: #if ART_USE_FUTEXES // 0 is unheld, 1 is held. @@ -386,6 +399,8 @@ class SHARED_LOCKABLE ReaderWriterMutex : public BaseMutex { // For negative capabilities in clang annotations. const ReaderWriterMutex& operator!() const { return *this; } + void WakeupToRespondToEmptyCheckpoint() OVERRIDE; + private: #if ART_USE_FUTEXES // Out-of-inline path for handling contention for a SharedLock. @@ -713,6 +728,12 @@ class Locks { // Have an exclusive logging thread. static Mutex* logging_lock_ ACQUIRED_AFTER(unexpected_signal_lock_); + + // List of mutexes that we expect a thread may hold when accessing weak refs. This is used to + // avoid a deadlock in the empty checkpoint while weak ref access is disabled (b/34964016). If we + // encounter an unexpected mutex on accessing weak refs, + // Thread::CheckEmptyCheckpointFromWeakRefAccess will detect it. + static std::vector<BaseMutex*> expected_mutexes_on_weak_ref_access_; }; class Roles { diff --git a/runtime/cha.cc b/runtime/cha.cc index d11b12f700..eaba01b2ce 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -200,7 +200,8 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify if (verify_method != excluded_method) { DCHECK(!verify_method->HasSingleImplementation()) << "class: " << verify_class->PrettyClass() - << " verify_method: " << verify_method->PrettyMethod(true); + << " verify_method: " << verify_method->PrettyMethod(true) + << " excluded_method: " << excluded_method->PrettyMethod(true); if (verify_method->IsAbstract()) { DCHECK(verify_method->GetSingleImplementation(image_pointer_size) == nullptr); } @@ -257,9 +258,6 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo( return; } - // Native methods don't have single-implementation flag set. - DCHECK(!method_in_super->IsNative()); - uint16_t method_index = method_in_super->GetMethodIndex(); if (method_in_super->IsAbstract()) { if (kIsDebugBuild) { @@ -374,12 +372,12 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> // used for static methods or methods of final classes. return; } - if (method->IsNative()) { - // Native method's invocation overhead is already high and it - // cannot be inlined. It's not worthwhile to devirtualize the - // call which can add a deoptimization point. - DCHECK(!method->HasSingleImplementation()); - } else if (method->IsAbstract()) { + if (method->IsAbstract()) { + // single-implementation of abstract method shares the same field + // that's used for JNI function of native method. It's fine since a method + // cannot be both abstract and native. + DCHECK(!method->IsNative()) << "Abstract method cannot be native"; + if (method->GetDeclaringClass()->IsInstantiable()) { // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). // Do not attempt to devirtualize it. diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 7db83688e2..d02cf17d44 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -70,6 +70,7 @@ #include "jni_internal.h" #include "leb128.h" #include "linear_alloc.h" +#include "mirror/call_site.h" #include "mirror/class.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" @@ -82,6 +83,7 @@ #include "mirror/method.h" #include "mirror/method_type.h" #include "mirror/method_handle_impl.h" +#include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" @@ -405,7 +407,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_); Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>( heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor())))); - CHECK(java_lang_Class.Get() != nullptr); + CHECK(java_lang_Class != nullptr); mirror::Class::SetClassClass(java_lang_Class.Get()); java_lang_Class->SetClass(java_lang_Class.Get()); if (kUseBakerReadBarrier) { @@ -425,7 +427,7 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // java_lang_Object comes next so that object_array_class can be created. Handle<mirror::Class> java_lang_Object(hs.NewHandle( AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_)))); - CHECK(java_lang_Object.Get() != nullptr); + CHECK(java_lang_Object != nullptr); // backfill Object as the super class of Class. java_lang_Class->SetSuperClass(java_lang_Object.Get()); mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self); @@ -624,9 +626,9 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b // Setup the single, global copy of "iftable". auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;")); - CHECK(java_lang_Cloneable.Get() != nullptr); + CHECK(java_lang_Cloneable != nullptr); auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;")); - CHECK(java_io_Serializable.Get() != nullptr); + CHECK(java_io_Serializable != nullptr); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get()); @@ -695,6 +697,18 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b SetClassRoot(kJavaLangInvokeMethodHandleImpl, class_root); mirror::MethodHandleImpl::SetClass(class_root); + // Create java.lang.invoke.MethodHandles.Lookup.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/MethodHandles$Lookup;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangInvokeMethodHandlesLookup, class_root); + mirror::MethodHandlesLookup::SetClass(class_root); + + // Create java.lang.invoke.CallSite.class root + class_root = FindSystemClass(self, "Ljava/lang/invoke/CallSite;"); + CHECK(class_root != nullptr); + SetClassRoot(kJavaLangInvokeCallSite, class_root); + mirror::CallSite::SetClass(class_root); + class_root = FindSystemClass(self, "Ldalvik/system/EmulatedStackFrame;"); CHECK(class_root != nullptr); SetClassRoot(kDalvikSystemEmulatedStackFrame, class_root); @@ -981,6 +995,8 @@ bool ClassLinker::InitFromBootImage(std::string* error_msg) { mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass)); mirror::MethodType::SetClass(GetClassRoot(kJavaLangInvokeMethodType)); mirror::MethodHandleImpl::SetClass(GetClassRoot(kJavaLangInvokeMethodHandleImpl)); + mirror::MethodHandlesLookup::SetClass(GetClassRoot(kJavaLangInvokeMethodHandlesLookup)); + mirror::CallSite::SetClass(GetClassRoot(kJavaLangInvokeCallSite)); mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference)); mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass)); mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass)); @@ -1231,12 +1247,13 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( if (dex_file->NumProtoIds() < num_method_types) { num_method_types = dex_file->NumProtoIds(); } - + const size_t num_call_sites = dex_file->NumCallSiteIds(); CHECK_EQ(num_strings, dex_cache->NumStrings()); CHECK_EQ(num_types, dex_cache->NumResolvedTypes()); CHECK_EQ(num_methods, dex_cache->NumResolvedMethods()); CHECK_EQ(num_fields, dex_cache->NumResolvedFields()); CHECK_EQ(num_method_types, dex_cache->NumResolvedMethodTypes()); + CHECK_EQ(num_call_sites, dex_cache->NumResolvedCallSites()); DexCacheArraysLayout layout(image_pointer_size_, dex_file); uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays(); if (num_strings != 0u) { @@ -1316,6 +1333,22 @@ bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches( mirror::MethodTypeDexCachePair::Initialize(method_types); dex_cache->SetResolvedMethodTypes(method_types); } + if (num_call_sites != 0u) { + GcRoot<mirror::CallSite>* const image_resolved_call_sites = + dex_cache->GetResolvedCallSites(); + GcRoot<mirror::CallSite>* const call_sites = + reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset()); + for (size_t j = 0; kIsDebugBuild && j < num_call_sites; ++j) { + DCHECK(call_sites[j].IsNull()); + } + CopyNonNull(image_resolved_call_sites, + num_call_sites, + call_sites, + [](const GcRoot<mirror::CallSite>& elem) { + return elem.IsNull(); + }); + dex_cache->SetResolvedCallSites(call_sites); + } } { WriterMutexLock mu2(self, *Locks::dex_lock_); @@ -1615,7 +1648,7 @@ bool ClassLinker::AddImageSpace( DCHECK(out_dex_files != nullptr); DCHECK(error_msg != nullptr); const uint64_t start_time = NanoTime(); - const bool app_image = class_loader.Get() != nullptr; + const bool app_image = class_loader != nullptr; const ImageHeader& header = space->GetImageHeader(); ObjPtr<mirror::Object> dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches); DCHECK(dex_caches_object != nullptr); @@ -1645,7 +1678,7 @@ bool ClassLinker::AddImageSpace( "Class loader should be the last image root."); MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle( app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr)); - DCHECK(class_roots.Get() != nullptr); + DCHECK(class_roots != nullptr); if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) { *error_msg = StringPrintf("Expected %d class roots but got %d", class_roots->GetLength(), @@ -2074,7 +2107,7 @@ void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) { ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type); classes.Assign( mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size)); - CHECK(classes.Get() != nullptr); // OOME. + CHECK(classes != nullptr); // OOME. GetClassInToObjectArray accumulator(classes.Get()); VisitClasses(&accumulator); if (accumulator.Succeeded()) { @@ -2115,6 +2148,8 @@ ClassLinker::~ClassLinker() { mirror::ShortArray::ResetArrayClass(); mirror::MethodType::ResetClass(); mirror::MethodHandleImpl::ResetClass(); + mirror::MethodHandlesLookup::ResetClass(); + mirror::CallSite::ResetClass(); mirror::EmulatedStackFrame::ResetClass(); Thread* const self = Thread::Current(); for (const ClassLoaderData& data : class_loaders_) { @@ -2152,7 +2187,7 @@ mirror::DexCache* ClassLinker::AllocDexCache(ObjPtr<mirror::String>* out_locatio DCHECK(out_location != nullptr); auto dex_cache(hs.NewHandle(ObjPtr<mirror::DexCache>::DownCast( GetClassRoot(kJavaLangDexCache)->AllocObject(self)))); - if (dex_cache.Get() == nullptr) { + if (dex_cache == nullptr) { self->AssertPendingOOMException(); return nullptr; } @@ -2453,7 +2488,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, return EnsureResolved(self, descriptor, klass); } // Class is not yet loaded. - if (descriptor[0] != '[' && class_loader.Get() == nullptr) { + if (descriptor[0] != '[' && class_loader == nullptr) { // Non-array class and the boot class loader, search the boot class path. ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_); if (pair.second != nullptr) { @@ -2616,14 +2651,14 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, } } - if (klass.Get() == nullptr) { + if (klass == nullptr) { // Allocate a class with the status of not ready. // Interface object should get the right size here. Regular class will // figure out the right size later and be replaced with one of the right // size when the class becomes resolved. klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); } - if (UNLIKELY(klass.Get() == nullptr)) { + if (UNLIKELY(klass == nullptr)) { self->AssertPendingOOMException(); return nullptr; } @@ -2716,7 +2751,7 @@ mirror::Class* ClassLinker::DefineClass(Thread* self, return nullptr; } self->AssertNoPendingException(); - CHECK(h_new_class.Get() != nullptr) << descriptor; + CHECK(h_new_class != nullptr) << descriptor; CHECK(h_new_class->IsResolved() && !h_new_class->IsErroneousResolved()) << descriptor; // Instrumentation may have updated entrypoints for all methods of all @@ -2997,7 +3032,7 @@ void ClassLinker::SetupClass(const DexFile& dex_file, const DexFile::ClassDef& dex_class_def, Handle<mirror::Class> klass, ObjPtr<mirror::ClassLoader> class_loader) { - CHECK(klass.Get() != nullptr); + CHECK(klass != nullptr); CHECK(klass->GetDexCache() != nullptr); CHECK_EQ(mirror::Class::kStatusNotReady, klass->GetStatus()); const char* descriptor = dex_file.GetClassDescriptor(dex_class_def); @@ -3112,6 +3147,7 @@ void ClassLinker::LoadClassMembers(Thread* self, last_field_idx = field_idx; } } + // Load instance fields. LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self, allocator, @@ -3128,6 +3164,7 @@ void ClassLinker::LoadClassMembers(Thread* self, last_field_idx = field_idx; } } + if (UNLIKELY(num_sfields != it.NumStaticFields()) || UNLIKELY(num_ifields != it.NumInstanceFields())) { LOG(WARNING) << "Duplicate fields in class " << klass->PrettyDescriptor() @@ -3367,7 +3404,7 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, WriterMutexLock mu(self, *Locks::dex_lock_); old_data = FindDexCacheDataLocked(dex_file); old_dex_cache = DecodeDexCache(self, old_data); - if (old_dex_cache == nullptr && h_dex_cache.Get() != nullptr) { + if (old_dex_cache == nullptr && h_dex_cache != nullptr) { // Do InitializeDexCache while holding dex lock to make sure two threads don't call it at the // same time with the same dex cache. Since the .bss is shared this can cause failing DCHECK // that the arrays are null. @@ -3383,12 +3420,12 @@ ObjPtr<mirror::DexCache> ClassLinker::RegisterDexFile(const DexFile& dex_file, if (old_dex_cache != nullptr) { // Another thread managed to initialize the dex cache faster, so use that DexCache. // If this thread encountered OOME, ignore it. - DCHECK_EQ(h_dex_cache.Get() == nullptr, self->IsExceptionPending()); + DCHECK_EQ(h_dex_cache == nullptr, self->IsExceptionPending()); self->ClearException(); // We cannot call EnsureSameClassLoader() while holding the dex_lock_. return EnsureSameClassLoader(self, old_dex_cache, old_data, h_class_loader.Get()); } - if (h_dex_cache.Get() == nullptr) { + if (h_dex_cache == nullptr) { self->AssertPendingOOMException(); return nullptr; } @@ -3517,12 +3554,12 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto StackHandleScope<2> hs(self); MutableHandle<mirror::Class> component_type(hs.NewHandle(FindClass(self, descriptor + 1, class_loader))); - if (component_type.Get() == nullptr) { + if (component_type == nullptr) { DCHECK(self->IsExceptionPending()); // We need to accept erroneous classes as component types. const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1); component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get())); - if (component_type.Get() == nullptr) { + if (component_type == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } else { @@ -3583,9 +3620,9 @@ mirror::Class* ClassLinker::CreateArrayClass(Thread* self, const char* descripto new_class.Assign(GetClassRoot(kLongArrayClass)); } } - if (new_class.Get() == nullptr) { + if (new_class == nullptr) { new_class.Assign(AllocClass(self, mirror::Array::ClassSize(image_pointer_size_))); - if (new_class.Get() == nullptr) { + if (new_class == nullptr) { self->AssertPendingOOMException(); return nullptr; } @@ -3818,8 +3855,8 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, Handle<mirror::Class> klass, Handle<mirror::Class> supertype) { DCHECK(self != nullptr); - DCHECK(klass.Get() != nullptr); - DCHECK(supertype.Get() != nullptr); + DCHECK(klass != nullptr); + DCHECK(supertype != nullptr); if (!supertype->IsVerified() && !supertype->IsErroneous()) { VerifyClass(self, supertype); @@ -3836,13 +3873,13 @@ bool ClassLinker::AttemptSupertypeVerification(Thread* self, LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8(); StackHandleScope<1> hs(self); Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException())); - if (cause.Get() != nullptr) { + if (cause != nullptr) { // Set during VerifyClass call (if at all). self->ClearException(); } // Change into a verify error. ThrowVerifyError(klass.Get(), "%s", error_msg.c_str()); - if (cause.Get() != nullptr) { + if (cause != nullptr) { self->GetException()->SetCause(cause.Get()); } ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex()); @@ -3921,7 +3958,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( StackHandleScope<2> hs(self); MutableHandle<mirror::Class> supertype(hs.NewHandle(klass->GetSuperClass())); // If we have a superclass and we get a hard verification failure we can return immediately. - if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) { + if (supertype != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) { CHECK(self->IsExceptionPending()) << "Verification error should be pending."; return verifier::MethodVerifier::kHardFailure; } @@ -3936,14 +3973,14 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( // but choose not to for an optimization. If the interfaces is being verified due to a class // initialization (which would need all the default interfaces to be verified) the class code // will trigger the recursive verification anyway. - if ((supertype.Get() == nullptr || supertype->IsVerified()) // See (1) + if ((supertype == nullptr || supertype->IsVerified()) // See (1) && !klass->IsInterface()) { // See (2) int32_t iftable_count = klass->GetIfTableCount(); MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr)); // Loop through all interfaces this class has defined. It doesn't matter the order. for (int32_t i = 0; i < iftable_count; i++) { iface.Assign(klass->GetIfTable()->GetInterface(i)); - DCHECK(iface.Get() != nullptr); + DCHECK(iface != nullptr); // We only care if we have default interfaces and can skip if we are already verified... if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) { continue; @@ -3963,7 +4000,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( // At this point if verification failed, then supertype is the "first" supertype that failed // verification (without a specific order). If verification succeeded, then supertype is either // null or the original superclass of klass and is verified. - DCHECK(supertype.Get() == nullptr || + DCHECK(supertype == nullptr || supertype.Get() == klass->GetSuperClass() || !supertype->IsVerified()); @@ -4004,7 +4041,7 @@ verifier::MethodVerifier::FailureKind ClassLinker::VerifyClass( if (verifier_failure == verifier::MethodVerifier::kNoFailure) { // Even though there were no verifier failures we need to respect whether the super-class and // super-default-interfaces were verified or requiring runtime reverification. - if (supertype.Get() == nullptr || supertype->IsVerified()) { + if (supertype == nullptr || supertype->IsVerified()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self); } else { CHECK_EQ(supertype->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); @@ -4187,7 +4224,7 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable& StackHandleScope<10> hs(self); MutableHandle<mirror::Class> klass(hs.NewHandle( AllocClass(self, GetClassRoot(kJavaLangClass), sizeof(mirror::Class)))); - if (klass.Get() == nullptr) { + if (klass == nullptr) { CHECK(self->IsExceptionPending()); // OOME. return nullptr; } @@ -4611,7 +4648,7 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass, MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr)); for (size_t i = 0; i < num_direct_interfaces; i++) { handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass.Get(), i)); - CHECK(handle_scope_iface.Get() != nullptr); + CHECK(handle_scope_iface != nullptr); CHECK(handle_scope_iface->IsInterface()); if (handle_scope_iface->HasBeenRecursivelyInitialized()) { // We have already done this for this interface. Skip it. @@ -4890,7 +4927,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* self, { StackHandleScope<1> hs(self); Handle<mirror::Class> return_type(hs.NewHandle(method1->GetReturnType(true /* resolve */))); - if (UNLIKELY(return_type.Get() == nullptr)) { + if (UNLIKELY(return_type == nullptr)) { ThrowSignatureCheckResolveReturnTypeException(klass, super_klass, method1, method1); return false; } @@ -4940,7 +4977,7 @@ static bool HasSameSignatureWithDifferentClassLoaders(Thread* 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 */))); - if (UNLIKELY(param_type.Get() == nullptr)) { + if (UNLIKELY(param_type == nullptr)) { ThrowSignatureCheckResolveArgException(klass, super_klass, method1, method1, i, param_type_idx); return false; @@ -5022,7 +5059,7 @@ bool ClassLinker::EnsureInitialized(Thread* self, Handle<mirror::Class> c, bool can_init_fields, bool can_init_parents) { - DCHECK(c.Get() != nullptr); + DCHECK(c != nullptr); if (c->IsInitialized()) { EnsureSkipAccessChecksMethods(c, image_pointer_size_); self->AssertNoPendingException(); @@ -5202,7 +5239,7 @@ bool ClassLinker::LinkClass(Thread* self, klass->SetMethodsPtrUnchecked(nullptr, 0, 0); klass->SetSFieldsPtrUnchecked(nullptr); klass->SetIFieldsPtrUnchecked(nullptr); - if (UNLIKELY(h_new_class.Get() == nullptr)) { + if (UNLIKELY(h_new_class == nullptr)) { self->AssertPendingOOMException(); mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); return false; @@ -5746,7 +5783,7 @@ bool ClassLinker::LinkVirtualMethods( MutableHandle<mirror::PointerArray> vtable; if (super_class->ShouldHaveEmbeddedVTable()) { vtable = hs.NewHandle(AllocPointerArray(self, max_count)); - if (UNLIKELY(vtable.Get() == nullptr)) { + if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; } @@ -5775,7 +5812,7 @@ bool ClassLinker::LinkVirtualMethods( } vtable = hs.NewHandle(down_cast<mirror::PointerArray*>( super_vtable->CopyOf(self, max_count))); - if (UNLIKELY(vtable.Get() == nullptr)) { + if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; } @@ -5911,7 +5948,7 @@ bool ClassLinker::LinkVirtualMethods( CHECK_LE(actual_count, max_count); if (actual_count < max_count) { vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, actual_count))); - if (UNLIKELY(vtable.Get() == nullptr)) { + if (UNLIKELY(vtable == nullptr)) { self->AssertPendingOOMException(); return false; } @@ -5964,8 +6001,8 @@ static bool ContainsOverridingMethodOf(Thread* self, PointerSize image_pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(self != nullptr); - DCHECK(iface.Get() != nullptr); - DCHECK(iftable.Get() != nullptr); + DCHECK(iface != nullptr); + DCHECK(iftable != nullptr); DCHECK_GE(ifstart, 0u); DCHECK_LT(ifstart, iftable->Count()); DCHECK_EQ(iface.Get(), iftable->GetInterface(ifstart)); @@ -6050,7 +6087,7 @@ ClassLinker::DefaultMethodSearchResult ClassLinker::FindDefaultMethodImplementat << "This will be a fatal error in subsequent versions of android. " << "Continuing anyway."; } - if (UNLIKELY(chosen_iface.Get() != nullptr)) { + if (UNLIKELY(chosen_iface != nullptr)) { // We have multiple default impls of the same method. This is a potential default conflict. // We need to check if this possibly conflicting method is either a superclass of the chosen // default implementation or is overridden by a non-default interface method. In either case @@ -6505,7 +6542,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> StackHandleScope<1> hs(self); const bool has_superclass = klass->HasSuperClass(); const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U; - const bool have_interfaces = interfaces.Get() != nullptr; + const bool have_interfaces = interfaces != nullptr; const size_t num_interfaces = have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces(); if (num_interfaces == 0) { @@ -6551,7 +6588,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> } // Create the interface function table. MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount))); - if (UNLIKELY(iftable.Get() == nullptr)) { + if (UNLIKELY(iftable == nullptr)) { self->AssertPendingOOMException(); return false; } @@ -6589,7 +6626,7 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> DCHECK_NE(num_interfaces, 0U); iftable.Assign(down_cast<mirror::IfTable*>( iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax))); - if (UNLIKELY(iftable.Get() == nullptr)) { + if (UNLIKELY(iftable == nullptr)) { self->AssertPendingOOMException(); return false; } @@ -6630,7 +6667,7 @@ static void CheckClassOwnsVTableEntries(Thread* self, Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking())); ObjPtr<mirror::Class> super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr; Handle<mirror::Class> superclass(hs.NewHandle(super_temp)); - int32_t super_vtable_length = (superclass.Get() != nullptr) ? superclass->GetVTableLength() : 0; + int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0; for (int32_t i = 0; i < check_vtable->GetLength(); ++i) { ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size); CHECK(m != nullptr); @@ -7289,7 +7326,7 @@ bool ClassLinker::LinkInterfaceMethods( // For a new interface, however, we need the whole vtable in case a new // interface method is implemented in the whole superclass. using_virtuals = false; - DCHECK(vtable.Get() != nullptr); + DCHECK(vtable != nullptr); input_vtable_array = vtable; input_array_length = input_vtable_array->GetLength(); } @@ -7432,7 +7469,7 @@ bool ClassLinker::LinkInterfaceMethods( if (fill_tables) { vtable.Assign(helper.UpdateVtable(default_translations, vtable.Get())); - if (UNLIKELY(vtable.Get() == nullptr)) { + if (UNLIKELY(vtable == nullptr)) { // The helper has already called self->AssertPendingOOMException(); return false; } @@ -7452,12 +7489,12 @@ bool ClassLinker::LinkInterfaceMethods( } bool ClassLinker::LinkInstanceFields(Thread* self, Handle<mirror::Class> klass) { - CHECK(klass.Get() != nullptr); + CHECK(klass != nullptr); return LinkFields(self, klass, false, nullptr); } bool ClassLinker::LinkStaticFields(Thread* self, Handle<mirror::Class> klass, size_t* class_size) { - CHECK(klass.Get() != nullptr); + CHECK(klass != nullptr); return LinkFields(self, klass, true, class_size); } @@ -7713,7 +7750,7 @@ void ClassLinker::CreateReferenceInstanceOffsets(Handle<mirror::Class> klass) { mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx); if (resolved != nullptr) { @@ -7729,7 +7766,7 @@ mirror::String* ClassLinker::ResolveString(const DexFile& dex_file, mirror::String* ClassLinker::LookupString(const DexFile& dex_file, dex::StringIndex string_idx, Handle<mirror::DexCache> dex_cache) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); ObjPtr<mirror::String> resolved = dex_cache->GetResolvedString(string_idx); if (resolved != nullptr) { return resolved.Ptr(); @@ -7783,7 +7820,7 @@ mirror::Class* ClassLinker::ResolveType(const DexFile& dex_file, dex::TypeIndex type_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); Thread::PoisonObjectPointersIfDebug(); ObjPtr<mirror::Class> resolved = dex_cache->GetResolvedType(type_idx); if (resolved == nullptr) { @@ -7821,7 +7858,7 @@ ArtMethod* ClassLinker::ResolveMethod(const DexFile& dex_file, Handle<mirror::ClassLoader> class_loader, ArtMethod* referrer, InvokeType type) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); // Check for hit in the dex cache. ArtMethod* resolved = dex_cache->GetResolvedMethod(method_idx, image_pointer_size_); Thread::PoisonObjectPointersIfDebug(); @@ -8060,7 +8097,7 @@ ArtField* ClassLinker::ResolveField(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, bool is_static) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_); Thread::PoisonObjectPointersIfDebug(); if (resolved != nullptr) { @@ -8101,7 +8138,7 @@ ArtField* ClassLinker::ResolveFieldJLS(const DexFile& dex_file, uint32_t field_idx, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); ArtField* resolved = dex_cache->GetResolvedField(field_idx, image_pointer_size_); Thread::PoisonObjectPointersIfDebug(); if (resolved != nullptr) { @@ -8132,7 +8169,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader) { DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - DCHECK(dex_cache.Get() != nullptr); + DCHECK(dex_cache != nullptr); ObjPtr<mirror::MethodType> resolved = dex_cache->GetResolvedMethodType(proto_idx); if (resolved != nullptr) { @@ -8146,7 +8183,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, const DexFile::ProtoId& proto_id = dex_file.GetProtoId(proto_idx); Handle<mirror::Class> return_type(hs.NewHandle( ResolveType(dex_file, proto_id.return_type_idx_, dex_cache, class_loader))); - if (return_type.Get() == nullptr) { + if (return_type == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } @@ -8161,7 +8198,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type); Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle( mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_method_args))); - if (method_params.Get() == nullptr) { + if (method_params == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } @@ -8172,7 +8209,7 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, for (; it.HasNext(); it.Next()) { const dex::TypeIndex type_idx = it.GetTypeIdx(); param_class.Assign(ResolveType(dex_file, type_idx, dex_cache, class_loader)); - if (param_class.Get() == nullptr) { + if (param_class == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } @@ -8189,6 +8226,148 @@ mirror::MethodType* ClassLinker::ResolveMethodType(const DexFile& dex_file, return type.Get(); } +mirror::MethodHandle* ClassLinker::ResolveMethodHandle(uint32_t method_handle_idx, + ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_) { + Thread* const self = Thread::Current(); + const DexFile* const dex_file = referrer->GetDexFile(); + const DexFile::MethodHandleItem& mh = dex_file->GetMethodHandle(method_handle_idx); + + union { + ArtField* field; + ArtMethod* method; + uintptr_t field_or_method; + } target; + uint32_t num_params; + mirror::MethodHandle::Kind kind; + DexFile::MethodHandleType handle_type = + static_cast<DexFile::MethodHandleType>(mh.method_handle_type_); + switch (handle_type) { + case DexFile::MethodHandleType::kStaticPut: { + kind = mirror::MethodHandle::Kind::kStaticPut; + target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */); + num_params = 1; + break; + } + case DexFile::MethodHandleType::kStaticGet: { + kind = mirror::MethodHandle::Kind::kStaticGet; + target.field = ResolveField(mh.field_or_method_idx_, referrer, true /* is_static */); + num_params = 0; + break; + } + case DexFile::MethodHandleType::kInstancePut: { + kind = mirror::MethodHandle::Kind::kInstancePut; + target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */); + num_params = 2; + break; + } + case DexFile::MethodHandleType::kInstanceGet: { + kind = mirror::MethodHandle::Kind::kInstanceGet; + target.field = ResolveField(mh.field_or_method_idx_, referrer, false /* is_static */); + num_params = 1; + break; + } + case DexFile::MethodHandleType::kInvokeStatic: { + kind = mirror::MethodHandle::Kind::kInvokeStatic; + target.method = ResolveMethod<kNoICCECheckForCache>(self, + mh.field_or_method_idx_, + referrer, + InvokeType::kStatic); + uint32_t shorty_length; + target.method->GetShorty(&shorty_length); + num_params = shorty_length - 1; // Remove 1 for return value. + break; + } + case DexFile::MethodHandleType::kInvokeInstance: { + kind = mirror::MethodHandle::Kind::kInvokeVirtual; + target.method = ResolveMethod<kNoICCECheckForCache>(self, + mh.field_or_method_idx_, + referrer, + InvokeType::kVirtual); + uint32_t shorty_length; + target.method->GetShorty(&shorty_length); + num_params = shorty_length - 1; // Remove 1 for return value. + break; + } + case DexFile::MethodHandleType::kInvokeConstructor: { + UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform."; + num_params = 0; + } + } + + StackHandleScope<5> hs(self); + ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass(); + ObjPtr<mirror::Class> array_of_class = FindArrayClass(self, &class_type); + Handle<mirror::ObjectArray<mirror::Class>> method_params(hs.NewHandle( + mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, num_params))); + if (method_params.Get() == nullptr) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle<mirror::Class> return_type; + switch (handle_type) { + case DexFile::MethodHandleType::kStaticPut: { + method_params->Set(0, target.field->GetType<true>()); + return_type = hs.NewHandle(FindPrimitiveClass('V')); + break; + } + case DexFile::MethodHandleType::kStaticGet: { + return_type = hs.NewHandle(target.field->GetType<true>()); + break; + } + case DexFile::MethodHandleType::kInstancePut: { + method_params->Set(0, target.field->GetDeclaringClass()); + method_params->Set(1, target.field->GetType<true>()); + return_type = hs.NewHandle(FindPrimitiveClass('V')); + break; + } + case DexFile::MethodHandleType::kInstanceGet: { + method_params->Set(0, target.field->GetDeclaringClass()); + return_type = hs.NewHandle(target.field->GetType<true>()); + break; + } + case DexFile::MethodHandleType::kInvokeStatic: + case DexFile::MethodHandleType::kInvokeInstance: { + // TODO(oth): This will not work for varargs methods as this + // requires instantiating a Transformer. This resolution step + // would be best done in managed code rather than in the run + // time (b/35235705) + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + DexFileParameterIterator it(*dex_file, target.method->GetPrototype()); + for (int32_t i = 0; it.HasNext(); i++, it.Next()) { + const dex::TypeIndex type_idx = it.GetTypeIdx(); + mirror::Class* klass = ResolveType(*dex_file, type_idx, dex_cache, class_loader); + if (nullptr == klass) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + method_params->Set(i, klass); + } + return_type = hs.NewHandle(target.method->GetReturnType(true)); + break; + } + case DexFile::MethodHandleType::kInvokeConstructor: { + // TODO(oth): b/35235705 + UNIMPLEMENTED(FATAL) << "Invoke constructor is implemented as a transform."; + } + } + + if (return_type.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle<mirror::MethodType> + mt(hs.NewHandle(mirror::MethodType::Create(self, return_type, method_params))); + if (mt.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + return mirror::MethodHandleImpl::Create(self, target.field_or_method, kind, mt); +} + bool ClassLinker::IsQuickResolutionStub(const void* entry_point) const { return (entry_point == GetQuickResolutionStub()) || (quick_resolution_trampoline_ == entry_point); @@ -8304,7 +8483,9 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) { "[Ljava/lang/reflect/Constructor;", "[Ljava/lang/reflect/Field;", "[Ljava/lang/reflect/Method;", + "Ljava/lang/invoke/CallSite;", "Ljava/lang/invoke/MethodHandleImpl;", + "Ljava/lang/invoke/MethodHandles$Lookup;", "Ljava/lang/invoke/MethodType;", "Ljava/lang/ClassLoader;", "Ljava/lang/Throwable;", @@ -8352,7 +8533,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); Handle<mirror::Class> dex_elements_class(hs.NewHandle(dex_elements_field->GetType<true>())); - DCHECK(dex_elements_class.Get() != nullptr); + DCHECK(dex_elements_class != nullptr); DCHECK(dex_elements_class->IsArrayClass()); Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements(hs.NewHandle( mirror::ObjectArray<mirror::Object>::Alloc(self, @@ -8381,21 +8562,21 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc( self, kDexFileIndexStart + 1)); - DCHECK(h_long_array.Get() != nullptr); + DCHECK(h_long_array != nullptr); h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file)); Handle<mirror::Object> h_dex_file = hs2.NewHandle( cookie_field->GetDeclaringClass()->AllocObject(self)); - DCHECK(h_dex_file.Get() != nullptr); + DCHECK(h_dex_file != nullptr); cookie_field->SetObject<false>(h_dex_file.Get(), h_long_array.Get()); Handle<mirror::String> h_file_name = hs2.NewHandle( mirror::String::AllocFromModifiedUtf8(self, dex_file->GetLocation().c_str())); - DCHECK(h_file_name.Get() != nullptr); + DCHECK(h_file_name != nullptr); file_name_field->SetObject<false>(h_dex_file.Get(), h_file_name.Get()); Handle<mirror::Object> h_element = hs2.NewHandle(h_dex_element_class->AllocObject(self)); - DCHECK(h_element.Get() != nullptr); + DCHECK(h_element != nullptr); element_file_field->SetObject<false>(h_element.Get(), h_dex_file.Get()); h_dex_elements->Set(index, h_element.Get()); @@ -8406,7 +8587,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, // Create DexPathList. Handle<mirror::Object> h_dex_path_list = hs.NewHandle( dex_elements_field->GetDeclaringClass()->AllocObject(self)); - DCHECK(h_dex_path_list.Get() != nullptr); + DCHECK(h_dex_path_list != nullptr); // Set elements. dex_elements_field->SetObject<false>(h_dex_path_list.Get(), h_dex_elements.Get()); @@ -8415,7 +8596,7 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)); Handle<mirror::Object> h_path_class_loader = hs.NewHandle( h_path_class_class->AllocObject(self)); - DCHECK(h_path_class_loader.Get() != nullptr); + DCHECK(h_path_class_loader != nullptr); // Set DexPathList. ArtField* path_list_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList); diff --git a/runtime/class_linker.h b/runtime/class_linker.h index 62d3c29a19..e27a53d15c 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -55,6 +55,8 @@ namespace mirror { class DexCacheMethodHandlesTest_Open_Test; class DexCacheTest_Open_Test; class IfTable; + class MethodHandle; + class MethodHandlesLookup; class MethodType; template<class T> class ObjectArray; class StackTraceElement; @@ -106,7 +108,9 @@ class ClassLinker { kJavaLangReflectConstructorArrayClass, kJavaLangReflectFieldArrayClass, kJavaLangReflectMethodArrayClass, + kJavaLangInvokeCallSite, kJavaLangInvokeMethodHandleImpl, + kJavaLangInvokeMethodHandlesLookup, kJavaLangInvokeMethodType, kJavaLangClassLoader, kJavaLangThrowable, @@ -366,6 +370,12 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_); + // Resolve a method handle with a given ID from the DexFile. The + // result is not cached in the DexCache as the instance will only be + // used once in most circumstances. + mirror::MethodHandle* ResolveMethodHandle(uint32_t method_handle_idx, ArtMethod* referrer) + REQUIRES_SHARED(Locks::mutator_lock_); + // Returns true on success, false if there's an exception pending. // can_run_clinit=false allows the compiler to attempt to init a class, // given the restriction that no <clinit> execution is possible. diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 03105cb6fb..07f3744b69 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -32,6 +32,7 @@ #include "entrypoints/entrypoint_utils-inl.h" #include "gc/heap.h" #include "mirror/accessible_object.h" +#include "mirror/call_site.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" #include "mirror/dex_cache.h" @@ -40,6 +41,7 @@ #include "mirror/field.h" #include "mirror/method_type.h" #include "mirror/method_handle_impl.h" +#include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/proxy.h" @@ -185,7 +187,7 @@ class ClassLinkerTest : public CommonRuntimeTest { void AssertArrayClass(const std::string& array_descriptor, Handle<mirror::Class> array) REQUIRES_SHARED(Locks::mutator_lock_) { - ASSERT_TRUE(array.Get() != nullptr); + ASSERT_TRUE(array != nullptr); ASSERT_TRUE(array->GetClass() != nullptr); ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass()); EXPECT_TRUE(array->GetClass()->GetSuperClass() != nullptr); @@ -409,7 +411,7 @@ class ClassLinkerTest : public CommonRuntimeTest { StackHandleScope<1> hs(self); Handle<mirror::Class> klass( hs.NewHandle(class_linker_->FindSystemClass(self, descriptor.c_str()))); - ASSERT_TRUE(klass.Get() != nullptr); + ASSERT_TRUE(klass != nullptr); std::string temp; EXPECT_STREQ(descriptor.c_str(), klass.Get()->GetDescriptor(&temp)); EXPECT_EQ(class_loader, klass->GetClassLoader()); @@ -669,11 +671,13 @@ struct DexCacheOffsets : public CheckOffsets<mirror::DexCache> { addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_), "dex"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings"); + addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes"); addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods"); @@ -762,6 +766,14 @@ struct MethodHandleImplOffsets : public CheckOffsets<mirror::MethodHandleImpl> { } }; +struct MethodHandlesLookupOffsets : public CheckOffsets<mirror::MethodHandlesLookup> { + MethodHandlesLookupOffsets() : CheckOffsets<mirror::MethodHandlesLookup>( + false, "Ljava/lang/invoke/MethodHandles$Lookup;") { + addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, allowed_modes_), "allowedModes"); + addOffset(OFFSETOF_MEMBER(mirror::MethodHandlesLookup, lookup_class_), "lookupClass"); + } +}; + struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFrame> { EmulatedStackFrameOffsets() : CheckOffsets<mirror::EmulatedStackFrame>( false, "Ldalvik/system/EmulatedStackFrame;") { @@ -772,6 +784,13 @@ struct EmulatedStackFrameOffsets : public CheckOffsets<mirror::EmulatedStackFram } }; +struct CallSiteOffsets : public CheckOffsets<mirror::CallSite> { + CallSiteOffsets() : CheckOffsets<mirror::CallSite>( + false, "Ljava/lang/invoke/CallSite;") { + addOffset(OFFSETOF_MEMBER(mirror::CallSite, target_), "target"); + } +}; + // C++ fields must exactly match the fields in the Java classes. If this fails, // reorder the fields in the C++ class. Managed class fields are ordered by // ClassLinker::LinkFields. @@ -794,7 +813,9 @@ TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) { EXPECT_TRUE(MethodTypeOffsets().Check()); EXPECT_TRUE(MethodHandleOffsets().Check()); EXPECT_TRUE(MethodHandleImplOffsets().Check()); + EXPECT_TRUE(MethodHandlesLookupOffsets().Check()); EXPECT_TRUE(EmulatedStackFrameOffsets().Check()); + EXPECT_TRUE(CallSiteOffsets().Check()); } TEST_F(ClassLinkerTest, FindClassNonexistent) { @@ -1411,13 +1432,13 @@ TEST_F(ClassLinkerTest, IsBootStrapClassLoaded) { // java.lang.Object is a bootstrap class. Handle<mirror::Class> jlo_class( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(jlo_class.Get() != nullptr); + ASSERT_TRUE(jlo_class != nullptr); EXPECT_TRUE(jlo_class.Get()->IsBootStrapClassLoaded()); // Statics is not a bootstrap class. Handle<mirror::Class> statics( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStatics;", class_loader))); - ASSERT_TRUE(statics.Get() != nullptr); + ASSERT_TRUE(statics != nullptr); EXPECT_FALSE(statics.Get()->IsBootStrapClassLoaded()); } @@ -1431,11 +1452,11 @@ TEST_F(ClassLinkerTest, RegisterDexFileName) { ReaderMutexLock mu(soa.Self(), *Locks::dex_lock_); for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) { dex_cache.Assign(soa.Self()->DecodeJObject(data.weak_root)->AsDexCache()); - if (dex_cache.Get() != nullptr) { + if (dex_cache != nullptr) { break; } } - ASSERT_TRUE(dex_cache.Get() != nullptr); + ASSERT_TRUE(dex_cache != nullptr); } // Make a copy of the dex cache and change the name. dex_cache.Assign(dex_cache->Clone(soa.Self())->AsDexCache()); @@ -1487,7 +1508,7 @@ TEST_F(ClassLinkerMethodHandlesTest, TestResolveMethodTypes) { class_linker_->ResolveMethodType(dex_file, method1_id.proto_idx_, dex_cache, class_loader)); // Assert that the method type was resolved successfully. - ASSERT_TRUE(method1_type.Get() != nullptr); + ASSERT_TRUE(method1_type != nullptr); // Assert that the return type and the method arguments are as we expect. Handle<mirror::Class> string_class( diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc index f1248eb00c..18c2b827fe 100644 --- a/runtime/class_table_test.cc +++ b/runtime/class_table_test.cc @@ -80,7 +80,7 @@ TEST_F(ClassTableTest, ClassTable) { Handle<mirror::Class> h_Y( hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader))); Handle<mirror::Object> obj_X = hs.NewHandle(h_X->AllocObject(soa.Self())); - ASSERT_TRUE(obj_X.Get() != nullptr); + ASSERT_TRUE(obj_X != nullptr); ClassTable table; EXPECT_EQ(table.NumZygoteClasses(class_loader.Get()), 0u); EXPECT_EQ(table.NumNonZygoteClasses(class_loader.Get()), 0u); diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc index a44f79e193..4f4bed0169 100644 --- a/runtime/common_throws.cc +++ b/runtime/common_throws.cc @@ -126,6 +126,22 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class, mirror::Class::PrettyDescriptor(array_class).c_str()).c_str()); } +// BootstrapMethodError + +void ThrowBootstrapMethodError(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args); + va_end(args); +} + +void ThrowWrappedBootstrapMethodError(const char* fmt, ...) { + va_list args; + va_start(args, fmt); + ThrowWrappedException("Ljava/lang/BootstrapMethodError;", nullptr, fmt, &args); + va_end(args); +} + // ClassCastException void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) { diff --git a/runtime/common_throws.h b/runtime/common_throws.h index 76ea2ae6c8..55a89388ea 100644 --- a/runtime/common_throws.h +++ b/runtime/common_throws.h @@ -56,6 +56,14 @@ void ThrowArrayStoreException(ObjPtr<mirror::Class> element_class, ObjPtr<mirror::Class> array_class) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; +// BootstrapMethodError + +void ThrowBootstrapMethodError(const char* fmt, ...) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + +void ThrowWrappedBootstrapMethodError(const char* fmt, ...) + REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; + // ClassCircularityError void ThrowClassCircularityError(ObjPtr<mirror::Class> c) @@ -236,7 +244,7 @@ void ThrowVerifyError(ObjPtr<mirror::Class> referrer, const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; -// WrontMethodTypeException +// WrongMethodTypeException void ThrowWrongMethodTypeException(mirror::MethodType* callee_type, mirror::MethodType* callsite_type) REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR; diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 1a0cec075c..cfdc6e1afb 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1765,13 +1765,13 @@ static JDWP::JdwpError GetFieldValueImpl(JDWP::RefTypeId ref_type_id, JDWP::Obje StackHandleScope<2> hs(self); MutableHandle<mirror::Object> o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error))); - if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) { + if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) { return JDWP::ERR_INVALID_OBJECT; } ArtField* f = FromFieldId(field_id); mirror::Class* receiver_class = c; - if (receiver_class == nullptr && o.Get() != nullptr) { + if (receiver_class == nullptr && o != nullptr) { receiver_class = o->GetClass(); } @@ -1899,7 +1899,7 @@ static JDWP::JdwpError SetFieldValueImpl(JDWP::ObjectId object_id, JDWP::FieldId StackHandleScope<2> hs(self); MutableHandle<mirror::Object> o(hs.NewHandle(Dbg::GetObjectRegistry()->Get<mirror::Object*>(object_id, &error))); - if ((!is_static && o.Get() == nullptr) || error != JDWP::ERR_NONE) { + if ((!is_static && o == nullptr) || error != JDWP::ERR_NONE) { return JDWP::ERR_INVALID_OBJECT; } ArtField* f = FromFieldId(field_id); @@ -2867,7 +2867,7 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec StackHandleScope<1> hs(self); Handle<mirror::Throwable> pending_exception(hs.NewHandle(self->GetException())); self->ClearException(); - if (kIsDebugBuild && pending_exception.Get() != nullptr) { + if (kIsDebugBuild && pending_exception != nullptr) { const DexFile::CodeItem* code_item = location.method->GetCodeItem(); const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]); CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode()); @@ -2875,7 +2875,7 @@ void Dbg::PostLocationEvent(ArtMethod* m, int dex_pc, mirror::Object* this_objec gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value); - if (pending_exception.Get() != nullptr) { + if (pending_exception != nullptr) { self->SetException(pending_exception.Get()); } } @@ -4027,7 +4027,7 @@ void Dbg::ExecuteMethod(DebugInvokeReq* pReq) { ExecuteMethodWithoutPendingException(soa, pReq); // If an exception was pending before the invoke, restore it now. - if (old_exception.Get() != nullptr) { + if (old_exception != nullptr) { soa.Self()->SetException(old_exception.Get()); } } @@ -4356,9 +4356,9 @@ void Dbg::DdmSendThreadNotification(Thread* t, uint32_t type) { ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<mirror::String> name(hs.NewHandle(t->GetThreadName())); - size_t char_count = (name.Get() != nullptr) ? name->GetLength() : 0; - const jchar* chars = (name.Get() != nullptr) ? name->GetValue() : nullptr; - bool is_compressed = (name.Get() != nullptr) ? name->IsCompressed() : false; + size_t char_count = (name != nullptr) ? name->GetLength() : 0; + const jchar* chars = (name != nullptr) ? name->GetValue() : nullptr; + bool is_compressed = (name != nullptr) ? name->IsCompressed() : false; std::vector<uint8_t> bytes; JDWP::Append4BE(bytes, t->GetThreadId()); diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h index 8b0c51c998..e58c6f541e 100644 --- a/runtime/dex2oat_environment_test.h +++ b/runtime/dex2oat_environment_test.h @@ -53,7 +53,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); // Verify the environment is as we expect - uint32_t checksum; + std::vector<uint32_t> checksums; std::string error_msg; ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); @@ -61,7 +61,7 @@ class Dex2oatEnvironmentTest : public CommonRuntimeTest { << "Expected dex file to be at: " << GetDexSrc1(); ASSERT_TRUE(OS::FileExists(GetStrippedDexSrc1().c_str())) << "Expected stripped dex file to be at: " << GetStrippedDexSrc1(); - ASSERT_FALSE(DexFile::GetChecksum(GetStrippedDexSrc1().c_str(), &checksum, &error_msg)) + ASSERT_FALSE(DexFile::GetMultiDexChecksums(GetStrippedDexSrc1().c_str(), &checksums, &error_msg)) << "Expected stripped dex file to be stripped: " << GetStrippedDexSrc1(); ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str())) << "Expected dex file to be at: " << GetDexSrc2(); diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index f59420d332..b6a2e09719 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -72,23 +72,13 @@ struct DexFile::AnnotationValue { uint8_t type_; }; -bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) { - CHECK(checksum != nullptr); +bool DexFile::GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg) { + CHECK(checksums != nullptr); uint32_t magic; - // Strip ":...", which is the location - const char* zip_entry_name = kClassesDex; - const char* file_part = filename; - std::string file_part_storage; - - if (DexFile::IsMultiDexLocation(filename)) { - file_part_storage = GetBaseLocation(filename); - file_part = file_part_storage.c_str(); - zip_entry_name = filename + file_part_storage.size() + 1; - DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator); - } - - File fd = OpenAndReadMagic(file_part, &magic, error_msg); + File fd = OpenAndReadMagic(filename, &magic, error_msg); if (fd.Fd() == -1) { DCHECK(!error_msg->empty()); return false; @@ -97,17 +87,25 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* std::unique_ptr<ZipArchive> zip_archive( ZipArchive::OpenFromFd(fd.Release(), filename, error_msg)); if (zip_archive.get() == nullptr) { - *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part, + *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename, error_msg->c_str()); return false; } - std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg)); + + uint32_t i = 0; + std::string zip_entry_name = GetMultiDexClassesDexName(i++); + std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg)); if (zip_entry.get() == nullptr) { - *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part, - zip_entry_name, error_msg->c_str()); + *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename, + zip_entry_name.c_str(), error_msg->c_str()); return false; } - *checksum = zip_entry->GetCrc32(); + + do { + checksums->push_back(zip_entry->GetCrc32()); + zip_entry_name = DexFile::GetMultiDexClassesDexName(i++); + zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg)); + } while (zip_entry.get() != nullptr); return true; } if (IsDexMagic(magic)) { @@ -116,7 +114,7 @@ bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* if (dex_file.get() == nullptr) { return false; } - *checksum = dex_file->GetHeader().checksum_; + checksums->push_back(dex_file->GetHeader().checksum_); return true; } *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename); @@ -333,7 +331,32 @@ std::unique_ptr<const DexFile> DexFile::OpenOneDexFileFromZip(const ZipArchive& *error_code = ZipOpenErrorCode::kDexFileError; return nullptr; } - std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); + + std::unique_ptr<MemMap> map; + if (zip_entry->IsUncompressed()) { + if (!zip_entry->IsAlignedTo(alignof(Header))) { + // Do not mmap unaligned ZIP entries because + // doing so would fail dex verification which requires 4 byte alignment. + LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " + << "please zipalign to " << alignof(Header) << " bytes. " + << "Falling back to extracting file."; + } else { + // Map uncompressed files within zip as file-backed to avoid a dirty copy. + map.reset(zip_entry->MapDirectlyFromFile(location.c_str(), /*out*/error_msg)); + if (map == nullptr) { + LOG(WARNING) << "Can't mmap dex file " << location << "!" << entry_name << " directly; " + << "is your ZIP file corrupted? Falling back to extraction."; + // Try again with Extraction which still has a chance of recovery. + } + } + } + + if (map == nullptr) { + // Default path for compressed ZIP entries, + // and fallback for stored ZIP entries. + map.reset(zip_entry->ExtractToMemMap(location.c_str(), entry_name, error_msg)); + } + if (map == nullptr) { *error_msg = StringPrintf("Failed to extract '%s' from '%s': %s", entry_name, location.c_str(), error_msg->c_str()); @@ -415,7 +438,7 @@ bool DexFile::OpenAllDexFilesFromZip(const ZipArchive& zip_archive, &error_code)); if (next_dex_file.get() == nullptr) { if (error_code != ZipOpenErrorCode::kEntryNotFound) { - LOG(WARNING) << error_msg; + LOG(WARNING) << "Zip open failed: " << *error_msg; } break; } else { @@ -497,9 +520,19 @@ DexFile::DexFile(const uint8_t* base, method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)), proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)), class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)), + method_handles_(nullptr), + num_method_handles_(0), + call_site_ids_(nullptr), + num_call_site_ids_(0), oat_dex_file_(oat_dex_file) { CHECK(begin_ != nullptr) << GetLocation(); CHECK_GT(size_, 0U) << GetLocation(); + // Check base (=header) alignment. + // Must be 4-byte aligned to avoid undefined behavior when accessing + // any of the sections via a pointer. + CHECK_ALIGNED(begin_, alignof(Header)); + + InitializeSectionsFromMapList(); } DexFile::~DexFile() { @@ -540,6 +573,29 @@ bool DexFile::CheckMagicAndVersion(std::string* error_msg) const { return true; } +void DexFile::InitializeSectionsFromMapList() { + const MapList* map_list = reinterpret_cast<const MapList*>(begin_ + header_->map_off_); + const size_t count = map_list->size_; + + size_t map_limit = header_->map_off_ + count * sizeof(MapItem); + if (header_->map_off_ >= map_limit || map_limit > size_) { + // Overflow or out out of bounds. The dex file verifier runs after + // this method and will reject the file as it is malformed. + return; + } + + for (size_t i = 0; i < count; ++i) { + const MapItem& map_item = map_list->list_[i]; + if (map_item.type_ == kDexTypeMethodHandleItem) { + method_handles_ = reinterpret_cast<const MethodHandleItem*>(begin_ + map_item.offset_); + num_method_handles_ = map_item.size_; + } else if (map_item.type_ == kDexTypeCallSiteIdItem) { + call_site_ids_ = reinterpret_cast<const CallSiteIdItem*>(begin_ + map_item.offset_); + num_call_site_ids_ = map_item.size_; + } + } +} + bool DexFile::IsMagicValid(const uint8_t* magic) { return (memcmp(magic, kDexMagic, sizeof(kDexMagic)) == 0); } @@ -1339,24 +1395,20 @@ void ClassDataItemIterator::ReadClassDataMethod() { } } -EncodedStaticFieldValueIterator::EncodedStaticFieldValueIterator(const DexFile& dex_file, - const DexFile::ClassDef& class_def) +EncodedArrayValueIterator::EncodedArrayValueIterator(const DexFile& dex_file, + const uint8_t* array_data) : dex_file_(dex_file), array_size_(), pos_(-1), + ptr_(array_data), type_(kByte) { - ptr_ = dex_file_.GetEncodedStaticFieldValuesArray(class_def); - if (ptr_ == nullptr) { - array_size_ = 0; - } else { - array_size_ = DecodeUnsignedLeb128(&ptr_); - } + array_size_ = (ptr_ != nullptr) ? DecodeUnsignedLeb128(&ptr_) : 0; if (array_size_ > 0) { Next(); } } -void EncodedStaticFieldValueIterator::Next() { +void EncodedArrayValueIterator::Next() { pos_++; if (pos_ >= array_size_) { return; @@ -1396,6 +1448,8 @@ void EncodedStaticFieldValueIterator::Next() { break; case kString: case kType: + case kMethodType: + case kMethodHandle: jval_.i = DexFile::ReadUnsignedInt(ptr_, value_arg, false); break; case kField: diff --git a/runtime/dex_file.h b/runtime/dex_file.h index cb7f174787..58b8e792ee 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -103,7 +103,7 @@ class DexFile { }; // Map item type codes. - enum { + enum MapItemType : uint16_t { // private kDexTypeHeaderItem = 0x0000, kDexTypeStringIdItem = 0x0001, kDexTypeTypeIdItem = 0x0002, @@ -111,6 +111,8 @@ class DexFile { kDexTypeFieldIdItem = 0x0004, kDexTypeMethodIdItem = 0x0005, kDexTypeClassDefItem = 0x0006, + kDexTypeCallSiteIdItem = 0x0007, + kDexTypeMethodHandleItem = 0x0008, kDexTypeMapList = 0x1000, kDexTypeTypeList = 0x1001, kDexTypeAnnotationSetRefList = 0x1002, @@ -260,6 +262,37 @@ class DexFile { DISALLOW_COPY_AND_ASSIGN(TypeList); }; + // MethodHandle Types + enum class MethodHandleType : uint16_t { // private + kStaticPut = 0x0000, // a setter for a given static field. + kStaticGet = 0x0001, // a getter for a given static field. + kInstancePut = 0x0002, // a setter for a given instance field. + kInstanceGet = 0x0003, // a getter for a given instance field. + kInvokeStatic = 0x0004, // an invoker for a given static method. + kInvokeInstance = 0x0005, // invoke_instance : an invoker for a given instance method. This + // can be any non-static method on any class (or interface) except + // for “<init>”. + kInvokeConstructor = 0x0006, // an invoker for a given constructor. + kLast = kInvokeConstructor + }; + + // raw method_handle_item + struct MethodHandleItem { + uint16_t method_handle_type_; + uint16_t reserved1_; // Reserved for future use. + uint16_t field_or_method_idx_; // Field index for accessors, method index otherwise. + uint16_t reserved2_; // Reserved for future use. + private: + DISALLOW_COPY_AND_ASSIGN(MethodHandleItem); + }; + + // raw call_site_id_item + struct CallSiteIdItem { + uint32_t data_off_; // Offset into data section pointing to encoded array items. + private: + DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem); + }; + // Raw code_item. struct CodeItem { uint16_t registers_size_; // the number of registers used by this code @@ -302,6 +335,8 @@ class DexFile { kDexAnnotationLong = 0x06, kDexAnnotationFloat = 0x10, kDexAnnotationDouble = 0x11, + kDexAnnotationMethodType = 0x15, + kDexAnnotationMethodHandle = 0x16, kDexAnnotationString = 0x17, kDexAnnotationType = 0x18, kDexAnnotationField = 0x19, @@ -389,11 +424,18 @@ class DexFile { struct AnnotationValue; - // Returns the checksum of a file for comparison with GetLocationChecksum(). - // For .dex files, this is the header checksum. - // For zip files, this is the classes.dex zip entry CRC32 checksum. - // Return true if the checksum could be found, false otherwise. - static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg); + // Returns the checksums of a file for comparison with GetLocationChecksum(). + // For .dex files, this is the single header checksum. + // For zip files, this is the zip entry CRC32 checksum for classes.dex and + // each additional multidex entry classes2.dex, classes3.dex, etc. + // Return true if the checksums could be found, false otherwise. + static bool GetMultiDexChecksums(const char* filename, + std::vector<uint32_t>* checksums, + std::string* error_msg); + + // Check whether a location denotes a multidex dex file. This is a very simple check: returns + // whether the string contains the separator character. + static bool IsMultiDexLocation(const char* location); // Opens .dex file, backed by existing memory static std::unique_ptr<const DexFile> Open(const uint8_t* base, @@ -683,6 +725,24 @@ class DexFile { } } + uint32_t NumMethodHandles() const { + return num_method_handles_; + } + + const MethodHandleItem& GetMethodHandle(uint32_t idx) const { + CHECK_LT(idx, NumMethodHandles()); + return method_handles_[idx]; + } + + uint32_t NumCallSiteIds() const { + return num_call_site_ids_; + } + + const CallSiteIdItem& GetCallSiteId(uint32_t idx) const { + CHECK_LT(idx, NumCallSiteIds()); + return call_site_ids_[idx]; + } + // Returns a pointer to the raw memory mapped class_data_item const uint8_t* GetClassData(const ClassDef& class_def) const { if (class_def.class_data_off_ == 0) { @@ -761,6 +821,10 @@ class DexFile { } } + const uint8_t* GetCallSiteEncodedValuesArray(const CallSiteIdItem& call_site_id) const { + return begin_ + call_site_id.data_off_; + } + static const TryItem* GetTryItems(const CodeItem& code_item, uint32_t offset); // Get the base of the encoded data for the given DexCode. @@ -1101,9 +1165,8 @@ class DexFile { // Returns true if the header magic and version numbers are of the expected values. bool CheckMagicAndVersion(std::string* error_msg) const; - // Check whether a location denotes a multidex dex file. This is a very simple check: returns - // whether the string contains the separator character. - static bool IsMultiDexLocation(const char* location); + // Initialize section info for sections only found in map. Returns true on success. + void InitializeSectionsFromMapList(); // The base address of the memory mapping. const uint8_t* const begin_; @@ -1143,6 +1206,18 @@ class DexFile { // Points to the base of the class definition list. const ClassDef* const class_defs_; + // Points to the base of the method handles list. + const MethodHandleItem* method_handles_; + + // Number of elements in the method handles list. + size_t num_method_handles_; + + // Points to the base of the call sites id list. + const CallSiteIdItem* call_site_ids_; + + // Number of elements in the call sites list. + size_t num_call_site_ids_; + // If this dex file was loaded from an oat file, oat_dex_file_ contains a // pointer to the OatDexFile it was loaded from. Otherwise oat_dex_file_ is // null. @@ -1409,32 +1484,33 @@ class ClassDataItemIterator { DISALLOW_IMPLICIT_CONSTRUCTORS(ClassDataItemIterator); }; -class EncodedStaticFieldValueIterator { +class EncodedArrayValueIterator { public: - EncodedStaticFieldValueIterator(const DexFile& dex_file, - const DexFile::ClassDef& class_def); + EncodedArrayValueIterator(const DexFile& dex_file, const uint8_t* array_data); bool HasNext() const { return pos_ < array_size_; } void Next(); enum ValueType { - kByte = 0x00, - kShort = 0x02, - kChar = 0x03, - kInt = 0x04, - kLong = 0x06, - kFloat = 0x10, - kDouble = 0x11, - kString = 0x17, - kType = 0x18, - kField = 0x19, - kMethod = 0x1a, - kEnum = 0x1b, - kArray = 0x1c, - kAnnotation = 0x1d, - kNull = 0x1e, - kBoolean = 0x1f + kByte = 0x00, + kShort = 0x02, + kChar = 0x03, + kInt = 0x04, + kLong = 0x06, + kFloat = 0x10, + kDouble = 0x11, + kMethodType = 0x15, + kMethodHandle = 0x16, + kString = 0x17, + kType = 0x18, + kField = 0x19, + kMethod = 0x1a, + kEnum = 0x1b, + kArray = 0x1c, + kAnnotation = 0x1d, + kNull = 0x1e, + kBoolean = 0x1f, }; ValueType GetValueType() const { return type_; } @@ -1452,10 +1528,38 @@ class EncodedStaticFieldValueIterator { jvalue jval_; // Value of current encoded value. private: + DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedArrayValueIterator); +}; +std::ostream& operator<<(std::ostream& os, const EncodedArrayValueIterator::ValueType& code); + +class EncodedStaticFieldValueIterator : public EncodedArrayValueIterator { + public: + EncodedStaticFieldValueIterator(const DexFile& dex_file, + const DexFile::ClassDef& class_def) + : EncodedArrayValueIterator(dex_file, + dex_file.GetEncodedStaticFieldValuesArray(class_def)) + {} + + private: DISALLOW_IMPLICIT_CONSTRUCTORS(EncodedStaticFieldValueIterator); }; std::ostream& operator<<(std::ostream& os, const EncodedStaticFieldValueIterator::ValueType& code); +class CallSiteArrayValueIterator : public EncodedArrayValueIterator { + public: + CallSiteArrayValueIterator(const DexFile& dex_file, + const DexFile::CallSiteIdItem& call_site_id) + : EncodedArrayValueIterator(dex_file, + dex_file.GetCallSiteEncodedValuesArray(call_site_id)) + {} + + uint32_t Size() const { return array_size_; } + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(CallSiteArrayValueIterator); +}; +std::ostream& operator<<(std::ostream& os, const CallSiteArrayValueIterator::ValueType& code); + class CatchHandlerIterator { public: CatchHandlerIterator(const DexFile::CodeItem& code_item, uint32_t address); diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc index 16a447b0a6..a95f94cabb 100644 --- a/runtime/dex_file_annotations.cc +++ b/runtime/dex_file_annotations.cc @@ -252,7 +252,7 @@ mirror::Object* ProcessEncodedAnnotation(Handle<mirror::Class> klass, const uint ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> annotation_class(hs.NewHandle( class_linker->ResolveType(klass->GetDexFile(), dex::TypeIndex(type_index), klass.Get()))); - if (annotation_class.Get() == nullptr) { + if (annotation_class == nullptr) { LOG(INFO) << "Unable to resolve " << klass->PrettyClass() << " annotation class " << type_index; DCHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); @@ -481,7 +481,7 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, break; } case DexFile::kDexAnnotationArray: - if (result_style == DexFile::kAllRaw || array_class.Get() == nullptr) { + if (result_style == DexFile::kAllRaw || array_class == nullptr) { return false; } else { ScopedObjectAccessUnchecked soa(self); @@ -491,7 +491,7 @@ bool ProcessAnnotationValue(Handle<mirror::Class> klass, Handle<mirror::Array> new_array(hs.NewHandle(mirror::Array::Alloc<true>( self, array_class.Get(), size, array_class->GetComponentSizeShift(), Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - if (new_array.Get() == nullptr) { + if (new_array == nullptr) { LOG(ERROR) << "Annotation element array allocation failed with size " << size; return false; } @@ -631,8 +631,8 @@ mirror::Object* CreateAnnotationMember(Handle<mirror::Class> klass, } Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr)); - if (new_member.Get() == nullptr || string_name.Get() == nullptr || - method_object.Get() == nullptr || method_return.Get() == nullptr) { + if (new_member == nullptr || string_name == nullptr || + method_object == nullptr || method_return == nullptr) { LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p", new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get()); return nullptr; @@ -740,7 +740,7 @@ mirror::ObjectArray<mirror::String>* GetSignatureValue(Handle<mirror::Class> kla ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString(); Handle<mirror::Class> string_array_class(hs.NewHandle( Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (string_array_class.Get() == nullptr) { + if (string_array_class == nullptr) { return nullptr; } mirror::Object* obj = @@ -766,7 +766,7 @@ mirror::ObjectArray<mirror::Class>* GetThrowsValue(Handle<mirror::Class> klass, ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass(); Handle<mirror::Class> class_array_class(hs.NewHandle( Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &class_class))); - if (class_array_class.Get() == nullptr) { + if (class_array_class == nullptr) { return nullptr; } mirror::Object* obj = @@ -796,7 +796,7 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSet( uint32_t size = annotation_set->size_; Handle<mirror::ObjectArray<mirror::Object>> result(hs.NewHandle( mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), size))); - if (result.Get() == nullptr) { + if (result == nullptr) { return nullptr; } @@ -854,7 +854,7 @@ mirror::ObjectArray<mirror::Object>* ProcessAnnotationSetRefList( } Handle<mirror::ObjectArray<mirror::Object>> annotation_array_array(hs.NewHandle( mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_array_class, size))); - if (annotation_array_array.Get() == nullptr) { + if (annotation_array_array == nullptr) { LOG(ERROR) << "Annotation set ref array allocation failed"; return nullptr; } @@ -1056,7 +1056,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, ObjPtr<mirror::Class> string_class = mirror::String::GetJavaLangString(); Handle<mirror::Class> string_array_class(hs.NewHandle( Runtime::Current()->GetClassLinker()->FindArrayClass(Thread::Current(), &string_class))); - if (UNLIKELY(string_array_class.Get() == nullptr)) { + if (UNLIKELY(string_array_class == nullptr)) { return false; } @@ -1067,13 +1067,13 @@ bool GetParametersMetadataForMethod(ArtMethod* method, "names", string_array_class, DexFile::kDexAnnotationArray)); - if (names_obj.Get() == nullptr) { + if (names_obj == nullptr) { return false; } // Extract the parameters' access flags int[]. Handle<mirror::Class> int_array_class(hs.NewHandle(mirror::IntArray::GetArrayClass())); - if (UNLIKELY(int_array_class.Get() == nullptr)) { + if (UNLIKELY(int_array_class == nullptr)) { return false; } Handle<mirror::Object> access_flags_obj = @@ -1082,7 +1082,7 @@ bool GetParametersMetadataForMethod(ArtMethod* method, "accessFlags", int_array_class, DexFile::kDexAnnotationArray)); - if (access_flags_obj.Get() == nullptr) { + if (access_flags_obj == nullptr) { return false; } @@ -1146,7 +1146,7 @@ mirror::ObjectArray<mirror::Class>* GetDeclaredClasses(Handle<mirror::Class> kla ObjPtr<mirror::Class> class_class = mirror::Class::GetJavaLangClass(); Handle<mirror::Class> class_array_class(hs.NewHandle( Runtime::Current()->GetClassLinker()->FindArrayClass(hs.Self(), &class_class))); - if (class_array_class.Get() == nullptr) { + if (class_array_class == nullptr) { return nullptr; } mirror::Object* obj = diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc index 9dca4c0621..9131715fec 100644 --- a/runtime/dex_file_test.cc +++ b/runtime/dex_file_test.cc @@ -326,12 +326,32 @@ TEST_F(DexFileTest, GetLocationChecksum) { } TEST_F(DexFileTest, GetChecksum) { - uint32_t checksum; + std::vector<uint32_t> checksums; ScopedObjectAccess soa(Thread::Current()); std::string error_msg; - EXPECT_TRUE(DexFile::GetChecksum(GetLibCoreDexFileNames()[0].c_str(), &checksum, &error_msg)) + EXPECT_TRUE(DexFile::GetMultiDexChecksums(GetLibCoreDexFileNames()[0].c_str(), &checksums, &error_msg)) << error_msg; - EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum); + ASSERT_EQ(1U, checksums.size()); + EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksums[0]); +} + +TEST_F(DexFileTest, GetMultiDexChecksums) { + std::string error_msg; + std::vector<uint32_t> checksums; + std::string multidex_file = GetTestDexFileName("MultiDex"); + EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(), + &checksums, + &error_msg)) << error_msg; + + std::vector<std::unique_ptr<const DexFile>> dexes = OpenTestDexFiles("MultiDex"); + ASSERT_EQ(2U, dexes.size()); + ASSERT_EQ(2U, checksums.size()); + + EXPECT_EQ(dexes[0]->GetLocation(), DexFile::GetMultiDexLocation(0, multidex_file.c_str())); + EXPECT_EQ(dexes[0]->GetLocationChecksum(), checksums[0]); + + EXPECT_EQ(dexes[1]->GetLocation(), DexFile::GetMultiDexLocation(1, multidex_file.c_str())); + EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]); } TEST_F(DexFileTest, ClassDefs) { diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 318123edcd..0b3f16a3cb 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -46,8 +46,8 @@ static bool IsValidTypeId(uint16_t low ATTRIBUTE_UNUSED, uint16_t high) { return (high == 0); } -static uint32_t MapTypeToBitMask(uint32_t map_type) { - switch (map_type) { +static uint32_t MapTypeToBitMask(DexFile::MapItemType map_item_type) { + switch (map_item_type) { case DexFile::kDexTypeHeaderItem: return 1 << 0; case DexFile::kDexTypeStringIdItem: return 1 << 1; case DexFile::kDexTypeTypeIdItem: return 1 << 2; @@ -55,23 +55,25 @@ static uint32_t MapTypeToBitMask(uint32_t map_type) { case DexFile::kDexTypeFieldIdItem: return 1 << 4; case DexFile::kDexTypeMethodIdItem: return 1 << 5; case DexFile::kDexTypeClassDefItem: return 1 << 6; - case DexFile::kDexTypeMapList: return 1 << 7; - case DexFile::kDexTypeTypeList: return 1 << 8; - case DexFile::kDexTypeAnnotationSetRefList: return 1 << 9; - case DexFile::kDexTypeAnnotationSetItem: return 1 << 10; - case DexFile::kDexTypeClassDataItem: return 1 << 11; - case DexFile::kDexTypeCodeItem: return 1 << 12; - case DexFile::kDexTypeStringDataItem: return 1 << 13; - case DexFile::kDexTypeDebugInfoItem: return 1 << 14; - case DexFile::kDexTypeAnnotationItem: return 1 << 15; - case DexFile::kDexTypeEncodedArrayItem: return 1 << 16; - case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 17; + case DexFile::kDexTypeCallSiteIdItem: return 1 << 7; + case DexFile::kDexTypeMethodHandleItem: return 1 << 8; + case DexFile::kDexTypeMapList: return 1 << 9; + case DexFile::kDexTypeTypeList: return 1 << 10; + case DexFile::kDexTypeAnnotationSetRefList: return 1 << 11; + case DexFile::kDexTypeAnnotationSetItem: return 1 << 12; + case DexFile::kDexTypeClassDataItem: return 1 << 13; + case DexFile::kDexTypeCodeItem: return 1 << 14; + case DexFile::kDexTypeStringDataItem: return 1 << 15; + case DexFile::kDexTypeDebugInfoItem: return 1 << 16; + case DexFile::kDexTypeAnnotationItem: return 1 << 17; + case DexFile::kDexTypeEncodedArrayItem: return 1 << 18; + case DexFile::kDexTypeAnnotationsDirectoryItem: return 1 << 19; } return 0; } -static bool IsDataSectionType(uint32_t map_type) { - switch (map_type) { +static bool IsDataSectionType(DexFile::MapItemType map_item_type) { + switch (map_item_type) { case DexFile::kDexTypeHeaderItem: case DexFile::kDexTypeStringIdItem: case DexFile::kDexTypeTypeIdItem: @@ -80,6 +82,20 @@ static bool IsDataSectionType(uint32_t map_type) { case DexFile::kDexTypeMethodIdItem: case DexFile::kDexTypeClassDefItem: return false; + case DexFile::kDexTypeCallSiteIdItem: + case DexFile::kDexTypeMethodHandleItem: + case DexFile::kDexTypeMapList: + case DexFile::kDexTypeTypeList: + case DexFile::kDexTypeAnnotationSetRefList: + case DexFile::kDexTypeAnnotationSetItem: + case DexFile::kDexTypeClassDataItem: + case DexFile::kDexTypeCodeItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + case DexFile::kDexTypeAnnotationsDirectoryItem: + return true; } return true; } @@ -455,7 +471,8 @@ bool DexFileVerifier::CheckMap() { return false; } - if (IsDataSectionType(item->type_)) { + DexFile::MapItemType item_type = static_cast<DexFile::MapItemType>(item->type_); + if (IsDataSectionType(item_type)) { uint32_t icount = item->size_; if (UNLIKELY(icount > data_items_left)) { ErrorStringPrintf("Too many items in data section: %ud", data_item_count + icount); @@ -465,7 +482,7 @@ bool DexFileVerifier::CheckMap() { data_item_count += icount; } - uint32_t bit = MapTypeToBitMask(item->type_); + uint32_t bit = MapTypeToBitMask(item_type); if (UNLIKELY(bit == 0)) { ErrorStringPrintf("Unknown map section type %x", item->type_); @@ -837,6 +854,28 @@ bool DexFileVerifier::CheckEncodedValue() { return false; } break; + case DexFile::kDexAnnotationMethodType: { + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value method type size %x", value_arg); + return false; + } + uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); + if (!CheckIndex(idx, header_->proto_ids_size_, "method_type value")) { + return false; + } + break; + } + case DexFile::kDexAnnotationMethodHandle: { + if (UNLIKELY(value_arg > 3)) { + ErrorStringPrintf("Bad encoded_value method handle size %x", value_arg); + return false; + } + uint32_t idx = ReadUnsignedLittleEndian(value_arg + 1); + if (!CheckIndex(idx, dex_file_->NumMethodHandles(), "method_handle value")) { + return false; + } + break; + } default: ErrorStringPrintf("Bogus encoded_value value_type %x", value_type); return false; @@ -1455,7 +1494,7 @@ bool DexFileVerifier::CheckIntraAnnotationsDirectoryItem() { } bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_count, - uint16_t type) { + DexFile::MapItemType type) { // Get the right alignment mask for the type of section. size_t alignment_mask; switch (type) { @@ -1481,6 +1520,7 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c } // Check depending on the section type. + const uint8_t* start_ptr = ptr_; switch (type) { case DexFile::kDexTypeStringIdItem: { if (!CheckListSize(ptr_, 1, sizeof(DexFile::StringId), "string_ids")) { @@ -1524,6 +1564,20 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c ptr_ += sizeof(DexFile::ClassDef); break; } + case DexFile::kDexTypeCallSiteIdItem: { + if (!CheckListSize(ptr_, 1, sizeof(DexFile::CallSiteIdItem), "call_site_ids")) { + return false; + } + ptr_ += sizeof(DexFile::CallSiteIdItem); + break; + } + case DexFile::kDexTypeMethodHandleItem: { + if (!CheckListSize(ptr_, 1, sizeof(DexFile::MethodHandleItem), "method_handles")) { + return false; + } + ptr_ += sizeof(DexFile::MethodHandleItem); + break; + } case DexFile::kDexTypeTypeList: { if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) { return false; @@ -1584,9 +1638,14 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c } break; } - default: - ErrorStringPrintf("Unknown map item type %x", type); - return false; + case DexFile::kDexTypeHeaderItem: + case DexFile::kDexTypeMapList: + break; + } + + if (start_ptr == ptr_) { + ErrorStringPrintf("Unknown map item type %x", type); + return false; } if (IsDataSectionType(type)) { @@ -1610,7 +1669,9 @@ bool DexFileVerifier::CheckIntraSectionIterate(size_t offset, uint32_t section_c return true; } -bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type) { +bool DexFileVerifier::CheckIntraIdSection(size_t offset, + uint32_t count, + DexFile::MapItemType type) { uint32_t expected_offset; uint32_t expected_size; @@ -1658,7 +1719,9 @@ bool DexFileVerifier::CheckIntraIdSection(size_t offset, uint32_t count, uint16_ return CheckIntraSectionIterate(offset, count, type); } -bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type) { +bool DexFileVerifier::CheckIntraDataSection(size_t offset, + uint32_t count, + DexFile::MapItemType type) { size_t data_start = header_->data_off_; size_t data_end = data_start + header_->data_size_; @@ -1684,16 +1747,16 @@ bool DexFileVerifier::CheckIntraDataSection(size_t offset, uint32_t count, uint1 bool DexFileVerifier::CheckIntraSection() { const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_); const DexFile::MapItem* item = map->list_; - - uint32_t count = map->size_; size_t offset = 0; + uint32_t count = map->size_; ptr_ = begin_; // Check the items listed in the map. while (count--) { + const size_t current_offset = offset; uint32_t section_offset = item->offset_; uint32_t section_count = item->size_; - uint16_t type = item->type_; + DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_); // Check for padding and overlap between items. if (!CheckPadding(offset, section_offset)) { @@ -1741,6 +1804,11 @@ bool DexFileVerifier::CheckIntraSection() { ptr_ += sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); offset = section_offset + sizeof(uint32_t) + (map->size_ * sizeof(DexFile::MapItem)); break; + case DexFile::kDexTypeMethodHandleItem: + case DexFile::kDexTypeCallSiteIdItem: + CheckIntraSectionIterate(section_offset, section_count, type); + offset = ptr_ - begin_; + break; case DexFile::kDexTypeTypeList: case DexFile::kDexTypeAnnotationSetRefList: case DexFile::kDexTypeAnnotationSetItem: @@ -1756,7 +1824,9 @@ bool DexFileVerifier::CheckIntraSection() { } offset = ptr_ - begin_; break; - default: + } + + if (offset == current_offset) { ErrorStringPrintf("Unknown map item type %x", type); return false; } @@ -2237,6 +2307,92 @@ bool DexFileVerifier::CheckInterClassDefItem() { return true; } +bool DexFileVerifier::CheckInterCallSiteIdItem() { + const DexFile::CallSiteIdItem* item = reinterpret_cast<const DexFile::CallSiteIdItem*>(ptr_); + + // Check call site referenced by item is in encoded array section. + if (!CheckOffsetToTypeMap(item->data_off_, DexFile::kDexTypeEncodedArrayItem)) { + ErrorStringPrintf("Invalid offset in CallSideIdItem"); + return false; + } + + CallSiteArrayValueIterator it(*dex_file_, *item); + + // Check Method Handle + if (!it.HasNext() || it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodHandle) { + ErrorStringPrintf("CallSiteArray missing method handle"); + return false; + } + + uint32_t handle_index = static_cast<uint32_t>(it.GetJavaValue().i); + if (handle_index >= dex_file_->NumMethodHandles()) { + ErrorStringPrintf("CallSite has bad method handle id: %x", handle_index); + return false; + } + + // Check target method name. + it.Next(); + if (!it.HasNext() || + it.GetValueType() != EncodedArrayValueIterator::ValueType::kString) { + ErrorStringPrintf("CallSiteArray missing target method name"); + return false; + } + + uint32_t name_index = static_cast<uint32_t>(it.GetJavaValue().i); + if (name_index >= dex_file_->NumStringIds()) { + ErrorStringPrintf("CallSite has bad method name id: %x", name_index); + return false; + } + + // Check method type. + it.Next(); + if (!it.HasNext() || + it.GetValueType() != EncodedArrayValueIterator::ValueType::kMethodType) { + ErrorStringPrintf("CallSiteArray missing method type"); + return false; + } + + uint32_t proto_index = static_cast<uint32_t>(it.GetJavaValue().i); + if (proto_index >= dex_file_->NumProtoIds()) { + ErrorStringPrintf("CallSite has bad method type: %x", proto_index); + return false; + } + + ptr_ += sizeof(DexFile::CallSiteIdItem); + return true; +} + +bool DexFileVerifier::CheckInterMethodHandleItem() { + const DexFile::MethodHandleItem* item = reinterpret_cast<const DexFile::MethodHandleItem*>(ptr_); + + DexFile::MethodHandleType method_handle_type = + static_cast<DexFile::MethodHandleType>(item->method_handle_type_); + if (method_handle_type > DexFile::MethodHandleType::kLast) { + ErrorStringPrintf("Bad method handle type %x", item->method_handle_type_); + return false; + } + + uint32_t index = item->field_or_method_idx_; + switch (method_handle_type) { + case DexFile::MethodHandleType::kStaticPut: + case DexFile::MethodHandleType::kStaticGet: + case DexFile::MethodHandleType::kInstancePut: + case DexFile::MethodHandleType::kInstanceGet: { + LOAD_FIELD(field, index, "method_handle_item field_idx", return false); + break; + } + case DexFile::MethodHandleType::kInvokeStatic: + case DexFile::MethodHandleType::kInvokeInstance: + case DexFile::MethodHandleType::kInvokeConstructor: { + LOAD_METHOD(method, index, "method_handle_item method_idx", return false); + break; + } + } + + ptr_ += sizeof(DexFile::MethodHandleItem); + return true; +} + bool DexFileVerifier::CheckInterAnnotationSetRefList() { const DexFile::AnnotationSetRefList* list = reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_); @@ -2386,7 +2542,9 @@ bool DexFileVerifier::CheckInterAnnotationsDirectoryItem() { return true; } -bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type) { +bool DexFileVerifier::CheckInterSectionIterate(size_t offset, + uint32_t count, + DexFile::MapItemType type) { // Get the right alignment mask for the type of section. size_t alignment_mask; switch (type) { @@ -2405,8 +2563,22 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui ptr_ = begin_ + new_offset; const uint8_t* prev_ptr = ptr_; + if (MapTypeToBitMask(type) == 0) { + ErrorStringPrintf("Unknown map item type %x", type); + return false; + } + // Check depending on the section type. switch (type) { + case DexFile::kDexTypeHeaderItem: + case DexFile::kDexTypeMapList: + case DexFile::kDexTypeTypeList: + case DexFile::kDexTypeCodeItem: + case DexFile::kDexTypeStringDataItem: + case DexFile::kDexTypeDebugInfoItem: + case DexFile::kDexTypeAnnotationItem: + case DexFile::kDexTypeEncodedArrayItem: + break; case DexFile::kDexTypeStringIdItem: { if (!CheckInterStringIdItem()) { return false; @@ -2451,6 +2623,18 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui } break; } + case DexFile::kDexTypeCallSiteIdItem: { + if (!CheckInterCallSiteIdItem()) { + return false; + } + break; + } + case DexFile::kDexTypeMethodHandleItem: { + if (!CheckInterMethodHandleItem()) { + return false; + } + break; + } case DexFile::kDexTypeAnnotationSetRefList: { if (!CheckInterAnnotationSetRefList()) { return false; @@ -2483,9 +2667,6 @@ bool DexFileVerifier::CheckInterSectionIterate(size_t offset, uint32_t count, ui } break; } - default: - ErrorStringPrintf("Unknown map item type %x", type); - return false; } previous_item_ = prev_ptr; @@ -2504,7 +2685,8 @@ bool DexFileVerifier::CheckInterSection() { while (count--) { uint32_t section_offset = item->offset_; uint32_t section_count = item->size_; - uint16_t type = item->type_; + DexFile::MapItemType type = static_cast<DexFile::MapItemType>(item->type_); + bool found = false; switch (type) { case DexFile::kDexTypeHeaderItem: @@ -2515,6 +2697,7 @@ bool DexFileVerifier::CheckInterSection() { case DexFile::kDexTypeDebugInfoItem: case DexFile::kDexTypeAnnotationItem: case DexFile::kDexTypeEncodedArrayItem: + found = true; break; case DexFile::kDexTypeStringIdItem: case DexFile::kDexTypeTypeIdItem: @@ -2522,6 +2705,8 @@ bool DexFileVerifier::CheckInterSection() { case DexFile::kDexTypeFieldIdItem: case DexFile::kDexTypeMethodIdItem: case DexFile::kDexTypeClassDefItem: + case DexFile::kDexTypeCallSiteIdItem: + case DexFile::kDexTypeMethodHandleItem: case DexFile::kDexTypeAnnotationSetRefList: case DexFile::kDexTypeAnnotationSetItem: case DexFile::kDexTypeClassDataItem: @@ -2529,11 +2714,14 @@ bool DexFileVerifier::CheckInterSection() { if (!CheckInterSectionIterate(section_offset, section_count, type)) { return false; } + found = true; break; } - default: - ErrorStringPrintf("Unknown map item type %x", type); - return false; + } + + if (!found) { + ErrorStringPrintf("Unknown map item type %x", item->type_); + return false; } item++; diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index ae206132dd..71b316c403 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -122,9 +122,9 @@ class DexFileVerifier { bool CheckIntraAnnotationItem(); bool CheckIntraAnnotationsDirectoryItem(); - bool CheckIntraSectionIterate(size_t offset, uint32_t count, uint16_t type); - bool CheckIntraIdSection(size_t offset, uint32_t count, uint16_t type); - bool CheckIntraDataSection(size_t offset, uint32_t count, uint16_t type); + bool CheckIntraSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); + bool CheckIntraIdSection(size_t offset, uint32_t count, DexFile::MapItemType type); + bool CheckIntraDataSection(size_t offset, uint32_t count, DexFile::MapItemType type); bool CheckIntraSection(); bool CheckOffsetToTypeMap(size_t offset, uint16_t type); @@ -140,12 +140,14 @@ class DexFileVerifier { bool CheckInterFieldIdItem(); bool CheckInterMethodIdItem(); bool CheckInterClassDefItem(); + bool CheckInterCallSiteIdItem(); + bool CheckInterMethodHandleItem(); bool CheckInterAnnotationSetRefList(); bool CheckInterAnnotationSetItem(); bool CheckInterClassDataItem(); bool CheckInterAnnotationsDirectoryItem(); - bool CheckInterSectionIterate(size_t offset, uint32_t count, uint16_t type); + bool CheckInterSectionIterate(size_t offset, uint32_t count, DexFile::MapItemType type); bool CheckInterSection(); // Load a string by (type) index. Checks whether the index is in bounds, printing the error if diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index c56b20057d..7736f3d615 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -1885,4 +1885,209 @@ TEST_F(DexFileVerifierTest, BadInitSignature) { &error_msg)); } +static const char* kInvokeCustomDexFiles[] = { + // TODO(oth): Revisit this test when we have smali / dx support. + // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java + "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx" + "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID" + "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA" + "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA" + "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU" + "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" + "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" + "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" + "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE" + "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" + "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA" + "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA" + "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF" + "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH" + "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK" + "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA" + "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+" + "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90" + "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv" + "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0" + "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0" + "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry" + "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T" + "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv" + "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl" + "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0" + "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph" + "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y" + "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0" + "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu" + "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l" + "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH" + "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA" + "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY" + "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA" + "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA" + "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF" + "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg" + "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==", + // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java + "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2" + "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE" + "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA" + "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA" + "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK" + "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA" + "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA" + "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA" + "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV" + "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA" + "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA" + "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA" + "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE" + "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA" + "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA" + "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA" + "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM" + "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA" + "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF" + "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv" + "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r" + "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs" + "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0" + "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq" + "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA" + "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93" + "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u" + "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh" + "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k" + "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr" + "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA" + "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l" + "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5" + "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs" + "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu" + "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA" + "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj" + "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA" + "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA" + "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA" + "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO" + "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA", + // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java + "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx" + "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD" + "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA" + "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA" + "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA" + "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" + "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" + "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" + "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE" + "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" + "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA" + "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA" + "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw" + "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA" + "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA" + "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA" + "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR" + "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W" + "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk" + "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo" + "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k" + "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM" + "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s" + "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" + "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" + "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" + "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" + "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" + "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" + "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h" + "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z" + "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr" + "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1" + "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w" + "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL" + "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA" + "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ" + "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA" + "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA" + "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA", + // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java + "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS" + "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF" + "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA" + "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA" + "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM" + "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ" + "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA" + "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA" + "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc" + "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA" + "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA" + "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA" + "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI" + "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA" + "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD" + "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA" + "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC" + "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA" + "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT" + "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ" + "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx" + "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA" + "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB" + "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA" + "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC" + "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA" + "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ" + "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk" + "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj" + "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp" + "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh" + "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U" + "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v" + "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" + "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" + "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" + "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" + "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" + "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" + "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM" + "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl" + "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh" + "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l" + "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW" + "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG" + "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK" + "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA" + "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD" + "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5" + "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H" + "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM" + "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA" + "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A" + "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA" + "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA" + "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd" + "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA" +}; + +TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) { + for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) { + size_t length; + std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &length)); + CHECK(dex_bytes != nullptr); + // Note: `dex_file` will be destroyed before `dex_bytes`. + std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length)); + std::string error_msg; + EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good checksum, verify", + /*verify_checksum*/ true, + &error_msg)); + // TODO(oth): Test corruptions (b/35308502) + } +} + } // namespace art diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc index 37f3ac92e9..091085a85c 100644 --- a/runtime/dex_instruction.cc +++ b/runtime/dex_instruction.cc @@ -407,6 +407,20 @@ std::string Instruction::DumpString(const DexFile* file) const { break; } FALLTHROUGH_INTENDED; + case INVOKE_CUSTOM: + if (file != nullptr) { + os << opcode << " {"; + uint32_t call_site_idx = VRegB_35c(); + for (size_t i = 0; i < VRegA_35c(); ++i) { + if (i != 0) { + os << ", "; + } + os << "v" << arg[i]; + } + os << "}, // call_site@" << call_site_idx; + break; + } + FALLTHROUGH_INTENDED; default: os << opcode << " {v" << arg[0] << ", v" << arg[1] << ", v" << arg[2] << ", v" << arg[3] << ", v" << arg[4] << "}, thing@" << VRegB_35c(); @@ -415,6 +429,8 @@ std::string Instruction::DumpString(const DexFile* file) const { break; } case k3rc: { + uint16_t first_reg = VRegC_3rc(); + uint16_t last_reg = VRegC_3rc() + VRegA_3rc() - 1; switch (Opcode()) { case INVOKE_VIRTUAL_RANGE: case INVOKE_SUPER_RANGE: @@ -423,7 +439,7 @@ std::string Instruction::DumpString(const DexFile* file) const { case INVOKE_INTERFACE_RANGE: if (file != nullptr) { uint32_t method_idx = VRegB_3rc(); - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1)) + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) << file->PrettyMethod(method_idx) << " // method@" << method_idx; break; } @@ -431,14 +447,22 @@ std::string Instruction::DumpString(const DexFile* file) const { case INVOKE_VIRTUAL_RANGE_QUICK: if (file != nullptr) { uint32_t method_idx = VRegB_3rc(); - os << StringPrintf("%s, {v%d .. v%d}, ", opcode, VRegC_3rc(), (VRegC_3rc() + VRegA_3rc() - 1)) + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) << "// vtable@" << method_idx; break; } FALLTHROUGH_INTENDED; + case INVOKE_CUSTOM_RANGE: + if (file != nullptr) { + uint32_t call_site_idx = VRegB_3rc(); + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) + << "// call_site@" << call_site_idx; + break; + } + FALLTHROUGH_INTENDED; default: - os << StringPrintf("%s, {v%d .. v%d}, thing@%d", opcode, VRegC_3rc(), - (VRegC_3rc() + VRegA_3rc() - 1), VRegB_3rc()); + os << StringPrintf("%s, {v%d .. v%d}, ", opcode, first_reg, last_reg) + << "thing@" << VRegB_3rc(); break; } break; diff --git a/runtime/dex_instruction.h b/runtime/dex_instruction.h index 578550cae2..d269110570 100644 --- a/runtime/dex_instruction.h +++ b/runtime/dex_instruction.h @@ -126,14 +126,15 @@ class Instruction { enum IndexType { kIndexUnknown = 0, - kIndexNone, // has no index - kIndexTypeRef, // type reference index - kIndexStringRef, // string reference index - kIndexMethodRef, // method reference index - kIndexFieldRef, // field reference index - kIndexFieldOffset, // field offset (for static linked fields) - kIndexVtableOffset, // vtable offset (for static linked methods) - kIndexMethodAndProtoRef // method and a proto reference index (for invoke-polymorphic) + kIndexNone, // has no index + kIndexTypeRef, // type reference index + kIndexStringRef, // string reference index + kIndexMethodRef, // method reference index + kIndexFieldRef, // field reference index + kIndexFieldOffset, // field offset (for static linked fields) + kIndexVtableOffset, // vtable offset (for static linked methods) + kIndexMethodAndProtoRef, // method and a proto reference index (for invoke-polymorphic) + kIndexCallSiteRef, // call site reference index }; enum Flags { @@ -165,31 +166,32 @@ class Instruction { }; enum VerifyFlag { - kVerifyNone = 0x000000, - kVerifyRegA = 0x000001, - kVerifyRegAWide = 0x000002, - kVerifyRegB = 0x000004, - kVerifyRegBField = 0x000008, - kVerifyRegBMethod = 0x000010, - kVerifyRegBNewInstance = 0x000020, - kVerifyRegBString = 0x000040, - kVerifyRegBType = 0x000080, - kVerifyRegBWide = 0x000100, - kVerifyRegC = 0x000200, - kVerifyRegCField = 0x000400, - kVerifyRegCNewArray = 0x000800, - kVerifyRegCType = 0x001000, - kVerifyRegCWide = 0x002000, - kVerifyArrayData = 0x004000, - kVerifyBranchTarget = 0x008000, - kVerifySwitchTargets = 0x010000, - kVerifyVarArg = 0x020000, - kVerifyVarArgNonZero = 0x040000, - kVerifyVarArgRange = 0x080000, - kVerifyVarArgRangeNonZero = 0x100000, - kVerifyRuntimeOnly = 0x200000, - kVerifyError = 0x400000, - kVerifyRegHPrototype = 0x800000 + kVerifyNone = 0x0000000, + kVerifyRegA = 0x0000001, + kVerifyRegAWide = 0x0000002, + kVerifyRegB = 0x0000004, + kVerifyRegBField = 0x0000008, + kVerifyRegBMethod = 0x0000010, + kVerifyRegBNewInstance = 0x0000020, + kVerifyRegBString = 0x0000040, + kVerifyRegBType = 0x0000080, + kVerifyRegBWide = 0x0000100, + kVerifyRegC = 0x0000200, + kVerifyRegCField = 0x0000400, + kVerifyRegCNewArray = 0x0000800, + kVerifyRegCType = 0x0001000, + kVerifyRegCWide = 0x0002000, + kVerifyArrayData = 0x0004000, + kVerifyBranchTarget = 0x0008000, + kVerifySwitchTargets = 0x0010000, + kVerifyVarArg = 0x0020000, + kVerifyVarArgNonZero = 0x0040000, + kVerifyVarArgRange = 0x0080000, + kVerifyVarArgRangeNonZero = 0x0100000, + kVerifyRuntimeOnly = 0x0200000, + kVerifyError = 0x0400000, + kVerifyRegHPrototype = 0x0800000, + kVerifyRegBCallSite = 0x1000000 }; static constexpr uint32_t kMaxVarArgRegs = 5; diff --git a/runtime/dex_instruction_list.h b/runtime/dex_instruction_list.h index ca2ce1d990..a5ce3c2f8a 100644 --- a/runtime/dex_instruction_list.h +++ b/runtime/dex_instruction_list.h @@ -271,8 +271,8 @@ V(0xF9, UNUSED_F9, "unused-f9", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xFA, INVOKE_POLYMORPHIC, "invoke-polymorphic", k45cc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgNonZero | kVerifyRegHPrototype) \ V(0xFB, INVOKE_POLYMORPHIC_RANGE, "invoke-polymorphic/range", k4rcc, kIndexMethodAndProtoRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRangeNonZero | kVerifyRegHPrototype) \ - V(0xFC, UNUSED_FC, "unused-fc", k10x, kIndexUnknown, 0, kVerifyError) \ - V(0xFD, UNUSED_FD, "unused-fd", k10x, kIndexUnknown, 0, kVerifyError) \ + V(0xFC, INVOKE_CUSTOM, "invoke-custom", k35c, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \ + V(0xFD, INVOKE_CUSTOM_RANGE, "invoke-custom/range", k3rc, kIndexCallSiteRef, kContinue | kThrow, kVerifyRegBCallSite) \ V(0xFE, UNUSED_FE, "unused-fe", k10x, kIndexUnknown, 0, kVerifyError) \ V(0xFF, UNUSED_FF, "unused-ff", k10x, kIndexUnknown, 0, kVerifyError) diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index fb8139b7c6..6301362e09 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -39,7 +39,7 @@ namespace art { void CheckReferenceResult(Handle<mirror::Object> o, Thread* self) { - if (o.Get() == nullptr) { + if (o == nullptr) { return; } // Make sure that the result is an instance of the type this method was expected to return. diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc index 5b1b2871c2..699cf91c70 100644 --- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc @@ -53,13 +53,18 @@ static inline void BssWriteBarrier(ArtMethod* outer_method) REQUIRES_SHARED(Lock } } +constexpr Runtime::CalleeSaveType kInitEntrypointSaveType = + // TODO: Change allocation entrypoints on MIPS and MIPS64 to kSaveEverything. + (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly + : Runtime::kSaveEverything; + extern "C" mirror::Class* artInitializeStaticStorageFromCode(uint32_t type_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { // Called to ensure static storage base is initialized for direct static field reads and writes. // A class may be accessing another class' fields when it doesn't have access, as access has been // given by inheritance. ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, true, false); @@ -73,7 +78,7 @@ extern "C" mirror::Class* artInitializeTypeFromCode(uint32_t type_idx, Thread* s REQUIRES_SHARED(Locks::mutator_lock_) { // Called when method->dex_cache_resolved_types_[] misses. ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, false); @@ -88,7 +93,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type // Called when caller isn't guaranteed to have access to a type and the dex cache may be // unpopulated. ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, Runtime::kSaveRefsOnly); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType); ArtMethod* caller = caller_and_outer.caller; mirror::Class* result = ResolveVerifyAndClinit(dex::TypeIndex(type_idx), caller, self, false, true); @@ -101,11 +106,7 @@ extern "C" mirror::Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { ScopedQuickEntrypointChecks sqec(self); - auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod( - self, - // TODO: Change art_quick_resolve_string on MIPS and MIPS64 to kSaveEverything. - (kRuntimeISA == kMips || kRuntimeISA == kMips64) ? Runtime::kSaveRefsOnly - : Runtime::kSaveEverything); + auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self, kInitEntrypointSaveType); ArtMethod* caller = caller_and_outer.caller; mirror::String* result = ResolveStringFromCode(caller, dex::StringIndex(string_idx)); if (LIKELY(result != nullptr)) { diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc index 4544aef148..822c5a8d9d 100644 --- a/runtime/entrypoints/quick/quick_field_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc @@ -48,7 +48,7 @@ ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx, StackHandleScope<1> hs(self); HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj)); ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size); - if (LIKELY(field != nullptr) && UNLIKELY(h.Get() == nullptr)) { + if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) { ThrowNullPointerExceptionForFieldAccess(field, /*is_read*/FindFieldTypeIsRead(type)); return nullptr; } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 3ef47c427e..c2bca5305d 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2435,8 +2435,8 @@ extern "C" uintptr_t artInvokePolymorphic( // Wrap raw_method_handle in a Handle for safety. StackHandleScope<5> hs(self); - Handle<mirror::MethodHandleImpl> method_handle( - hs.NewHandle(ObjPtr<mirror::MethodHandleImpl>::DownCast(MakeObjPtr(raw_method_handle)))); + Handle<mirror::MethodHandle> method_handle( + hs.NewHandle(ObjPtr<mirror::MethodHandle>::DownCast(MakeObjPtr(raw_method_handle)))); raw_method_handle = nullptr; self->EndAssertNoThreadSuspension(old_cause); @@ -2497,15 +2497,14 @@ extern "C" uintptr_t artInvokePolymorphic( // consecutive order. uint32_t unused_args[Instruction::kMaxVarArgRegs] = {}; uint32_t first_callee_arg = first_arg + 1; - const bool do_assignability_check = false; - if (!DoInvokePolymorphic<true /* is_range */, do_assignability_check>(self, - resolved_method, - *shadow_frame, - method_handle, - method_type, - unused_args, - first_callee_arg, - result)) { + if (!DoInvokePolymorphic<true /* is_range */>(self, + resolved_method, + *shadow_frame, + method_handle, + method_type, + unused_args, + first_callee_arg, + result)) { DCHECK(self->IsExceptionPending()); } diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc index e18a955251..122f7799df 100644 --- a/runtime/gc/allocation_record.cc +++ b/runtime/gc/allocation_record.cc @@ -292,7 +292,7 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::alloc_tracker_lock_); new_record_condition_.WaitHoldingLocks(self); } diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index f12ad8058d..f18ffb4aef 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -835,65 +835,9 @@ void ConcurrentCopying::ProcessFalseGrayStack() { void ConcurrentCopying::IssueEmptyCheckpoint() { Thread* self = Thread::Current(); ThreadList* thread_list = Runtime::Current()->GetThreadList(); - Barrier* barrier = thread_list->EmptyCheckpointBarrier(); - barrier->Init(self, 0); - std::vector<uint32_t> runnable_thread_ids; // Used in debug build only - size_t barrier_count = thread_list->RunEmptyCheckpoint(runnable_thread_ids); - // If there are no threads to wait which implys that all the checkpoint functions are finished, - // then no need to release the mutator lock. - if (barrier_count == 0) { - return; - } // Release locks then wait for all mutator threads to pass the barrier. Locks::mutator_lock_->SharedUnlock(self); - { - ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); - if (kIsDebugBuild) { - static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000; // 10 minutes. - bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs); - if (timed_out) { - std::ostringstream ss; - ss << "Empty checkpoint timeout\n"; - ss << "Barrier count " << barrier->GetCount(self) << "\n"; - ss << "Runnable thread IDs"; - for (uint32_t tid : runnable_thread_ids) { - ss << " " << tid; - } - ss << "\n"; - Locks::mutator_lock_->Dump(ss); - ss << "\n"; - LOG(FATAL_WITHOUT_ABORT) << ss.str(); - // 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. - { - ScopedObjectAccess soa(self); - MutexLock mu1(self, *Locks::thread_list_lock_); - for (Thread* thread : thread_list->GetList()) { - uint32_t tid = thread->GetThreadId(); - bool is_in_runnable_thread_ids = - std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) != - runnable_thread_ids.end(); - if (is_in_runnable_thread_ids && - thread->ReadFlag(kEmptyCheckpointRequest)) { - // Found a runnable thread that hasn't responded to the empty checkpoint request. - // Assume it's stuck and safe to dump its stack. - thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), - /*dump_native_stack*/ true, - /*backtrace_map*/ nullptr, - /*force_dump_stack*/ true); - } - } - } - LOG(FATAL_WITHOUT_ABORT) - << "Dumped runnable threads that haven't responded to empty checkpoint."; - // Now use ThreadList::Dump() to dump more threads, noting it may get stuck. - thread_list->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); - LOG(FATAL) << "Dumped all threads."; - } - } else { - barrier->Increment(self, barrier_count); - } - } + thread_list->RunEmptyCheckpoint(); Locks::mutator_lock_->SharedLock(self); } diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc index c1548365c7..86b152211c 100644 --- a/runtime/gc/reference_processor.cc +++ b/runtime/gc/reference_processor.cc @@ -104,7 +104,7 @@ ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self, } // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::reference_processor_lock_); condition_.WaitHoldingLocks(self); } return reference->GetReferent(); @@ -292,7 +292,7 @@ void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) { (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::reference_processor_lock_); condition_.WaitHoldingLocks(self); } } diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc index 3ca3353562..613b034f59 100644 --- a/runtime/gc/reference_queue_test.cc +++ b/runtime/gc/reference_queue_test.cc @@ -38,11 +38,11 @@ TEST_F(ReferenceQueueTest, EnqueueDequeue) { auto ref_class = hs.NewHandle( Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(ref_class.Get() != nullptr); + ASSERT_TRUE(ref_class != nullptr); auto ref1(hs.NewHandle(ref_class->AllocObject(self)->AsReference())); - ASSERT_TRUE(ref1.Get() != nullptr); + ASSERT_TRUE(ref1 != nullptr); auto ref2(hs.NewHandle(ref_class->AllocObject(self)->AsReference())); - ASSERT_TRUE(ref2.Get() != nullptr); + ASSERT_TRUE(ref2 != nullptr); queue.EnqueueReference(ref1.Get()); ASSERT_TRUE(!queue.IsEmpty()); ASSERT_EQ(queue.GetLength(), 1U); @@ -73,15 +73,15 @@ TEST_F(ReferenceQueueTest, Dump) { auto weak_ref_class = hs.NewHandle( Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/WeakReference;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(weak_ref_class.Get() != nullptr); + ASSERT_TRUE(weak_ref_class != nullptr); auto finalizer_ref_class = hs.NewHandle( Runtime::Current()->GetClassLinker()->FindClass(self, "Ljava/lang/ref/FinalizerReference;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(finalizer_ref_class.Get() != nullptr); + ASSERT_TRUE(finalizer_ref_class != nullptr); auto ref1(hs.NewHandle(weak_ref_class->AllocObject(self)->AsReference())); - ASSERT_TRUE(ref1.Get() != nullptr); + ASSERT_TRUE(ref1 != nullptr); auto ref2(hs.NewHandle(finalizer_ref_class->AllocObject(self)->AsReference())); - ASSERT_TRUE(ref2.Get() != nullptr); + ASSERT_TRUE(ref2 != nullptr); queue.EnqueueReference(ref1.Get()); oss.str(""); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index ffbca525d9..2163a20e87 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -587,15 +587,18 @@ class ImageSpaceLoader { } std::unique_ptr<MemMap> map; + // GetImageBegin is the preferred address to map the image. If we manage to map the // image at the image begin, the amount of fixup work required is minimized. + // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to + // avoid reading proc maps for a mapping failure and slowing everything down. map.reset(LoadImageFile(image_filename, image_location, *image_header, image_header->GetImageBegin(), file->Fd(), logger, - error_msg)); + image_header->IsPic() ? nullptr : error_msg)); // If the header specifies PIC mode, we can also map at a random low_4gb address since we can // relocate in-place. if (map == nullptr && image_header->IsPic()) { @@ -689,7 +692,7 @@ class ImageSpaceLoader { if (validate_oat_file) { TimingLogger::ScopedTiming timing("ValidateOatFile", &logger); CHECK(space->oat_file_ != nullptr); - if (!ValidateOatFile(*space, *space->oat_file_, error_msg)) { + if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) { DCHECK(!error_msg->empty()); return nullptr; } @@ -765,8 +768,10 @@ class ImageSpaceLoader { if (storage_mode != ImageHeader::kStorageModeLZ4 && storage_mode != ImageHeader::kStorageModeLZ4HC) { - *error_msg = StringPrintf("Invalid storage mode in image header %d", - static_cast<int>(storage_mode)); + if (error_msg != nullptr) { + *error_msg = StringPrintf("Invalid storage mode in image header %d", + static_cast<int>(storage_mode)); + } return nullptr; } @@ -790,7 +795,7 @@ class ImageSpaceLoader { image_filename, error_msg)); if (temp_map == nullptr) { - DCHECK(!error_msg->empty()); + DCHECK(error_msg == nullptr || !error_msg->empty()); return nullptr; } memcpy(map->Begin(), &image_header, sizeof(ImageHeader)); @@ -802,12 +807,18 @@ class ImageSpaceLoader { reinterpret_cast<char*>(map->Begin()) + decompress_offset, stored_size, map->Size() - decompress_offset); - VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start); + const uint64_t time = NanoTime() - start; + // Add one 1 ns to prevent possible divide by 0. + VLOG(image) << "Decompressing image took " << PrettyDuration(time) << " (" + << PrettySize(static_cast<uint64_t>(map->Size()) * MsToNs(1000) / (time + 1)) + << "/s)"; if (decompressed_size + sizeof(ImageHeader) != image_header.GetImageSize()) { - *error_msg = StringPrintf( - "Decompressed size does not match expected image size %zu vs %zu", - decompressed_size + sizeof(ImageHeader), - image_header.GetImageSize()); + if (error_msg != nullptr) { + *error_msg = StringPrintf( + "Decompressed size does not match expected image size %zu vs %zu", + decompressed_size + sizeof(ImageHeader), + image_header.GetImageSize()); + } return nullptr; } } @@ -1272,6 +1283,14 @@ class ImageSpaceLoader { } dex_cache->FixupResolvedMethodTypes<kWithoutReadBarrier>(new_method_types, fixup_adapter); } + GcRoot<mirror::CallSite>* call_sites = dex_cache->GetResolvedCallSites(); + if (call_sites != nullptr) { + GcRoot<mirror::CallSite>* new_call_sites = fixup_adapter.ForwardObject(call_sites); + if (call_sites != new_call_sites) { + dex_cache->SetResolvedCallSites(new_call_sites); + } + dex_cache->FixupResolvedCallSites<kWithoutReadBarrier>(new_call_sites, fixup_adapter); + } } } { @@ -1368,33 +1387,6 @@ class ImageSpaceLoader { return oat_file; } - - static bool ValidateOatFile(const ImageSpace& space, - const OatFile& oat_file, - std::string* error_msg) { - for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { - const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); - uint32_t dex_file_location_checksum; - if (!DexFile::GetChecksum(dex_file_location.c_str(), &dex_file_location_checksum, error_msg)) { - *error_msg = StringPrintf("Failed to get checksum of dex file '%s' referenced by image %s: " - "%s", - dex_file_location.c_str(), - space.GetName(), - error_msg->c_str()); - return false; - } - if (dex_file_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) { - *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file '%s' and " - "dex file '%s' (0x%x != 0x%x)", - oat_file.GetLocation().c_str(), - dex_file_location.c_str(), - oat_dex_file->GetDexFileLocationChecksum(), - dex_file_location_checksum); - return false; - } - } - return true; - } }; static constexpr uint64_t kLowSpaceValue = 50 * MB; @@ -1771,6 +1763,63 @@ std::string ImageSpace::GetMultiImageBootClassPath( return bootcp_oss.str(); } +bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) { + for (const OatFile::OatDexFile* oat_dex_file : oat_file.GetOatDexFiles()) { + const std::string& dex_file_location = oat_dex_file->GetDexFileLocation(); + + // Skip multidex locations - These will be checked when we visit their + // corresponding primary non-multidex location. + if (DexFile::IsMultiDexLocation(dex_file_location.c_str())) { + continue; + } + + std::vector<uint32_t> checksums; + if (!DexFile::GetMultiDexChecksums(dex_file_location.c_str(), &checksums, error_msg)) { + *error_msg = StringPrintf("ValidateOatFile failed to get checksums of dex file '%s' " + "referenced by oat file %s: %s", + dex_file_location.c_str(), + oat_file.GetLocation().c_str(), + error_msg->c_str()); + return false; + } + CHECK(!checksums.empty()); + if (checksums[0] != oat_dex_file->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file " + "'%s' and dex file '%s' (0x%x != 0x%x)", + oat_file.GetLocation().c_str(), + dex_file_location.c_str(), + oat_dex_file->GetDexFileLocationChecksum(), + checksums[0]); + return false; + } + + // Verify checksums for any related multidex entries. + for (size_t i = 1; i < checksums.size(); i++) { + std::string multi_dex_location = DexFile::GetMultiDexLocation(i, dex_file_location.c_str()); + const OatFile::OatDexFile* multi_dex = oat_file.GetOatDexFile(multi_dex_location.c_str(), + nullptr, + error_msg); + if (multi_dex == nullptr) { + *error_msg = StringPrintf("ValidateOatFile oat file '%s' is missing entry '%s'", + oat_file.GetLocation().c_str(), + multi_dex_location.c_str()); + return false; + } + + if (checksums[i] != multi_dex->GetDexFileLocationChecksum()) { + *error_msg = StringPrintf("ValidateOatFile found checksum mismatch between oat file " + "'%s' and dex file '%s' (0x%x != 0x%x)", + oat_file.GetLocation().c_str(), + multi_dex_location.c_str(), + multi_dex->GetDexFileLocationChecksum(), + checksums[i]); + return false; + } + } + } + return true; +} + void ImageSpace::ExtractMultiImageLocations(const std::string& input_image_file_name, const std::string& boot_classpath, std::vector<std::string>* image_file_names) { diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 489a2890fe..199bbdd00a 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -131,6 +131,17 @@ class ImageSpace : public MemMapSpace { const std::vector<const char*>& oat_filenames, const std::vector<const char*>& image_filenames); + // Returns true if the dex checksums in the given oat file match the + // checksums of the original dex files on disk. This is intended to be used + // to validate the boot image oat file, which may contain dex entries from + // multiple different (possibly multidex) dex files on disk. Prefer the + // OatFileAssistant for validating regular app oat files because the + // OatFileAssistant caches dex checksums that are reused to check both the + // oat and odex file. + // + // This function is exposed for testing purposes. + static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg); + // Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields. uint8_t* GetImageEnd() const { return Begin() + GetImageHeader().GetImageSize(); diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc new file mode 100644 index 0000000000..7a380746a1 --- /dev/null +++ b/runtime/gc/space/image_space_test.cc @@ -0,0 +1,111 @@ +/* + * 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 <gtest/gtest.h> + +#include "dexopt_test.h" + +namespace art { +namespace gc { +namespace space { + +TEST_F(DexoptTest, ValidateOatFile) { + std::string dex1 = GetScratchDir() + "/Dex1.jar"; + std::string multidex1 = GetScratchDir() + "/MultiDex1.jar"; + std::string dex2 = GetScratchDir() + "/Dex2.jar"; + std::string oat_location = GetScratchDir() + "/Oat.oat"; + + Copy(GetDexSrc1(), dex1); + Copy(GetMultiDexSrc1(), multidex1); + Copy(GetDexSrc2(), dex2); + + std::string error_msg; + std::vector<std::string> args; + args.push_back("--dex-file=" + dex1); + args.push_back("--dex-file=" + multidex1); + args.push_back("--dex-file=" + dex2); + args.push_back("--oat-file=" + oat_location); + ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg; + + std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(), + oat_location.c_str(), + nullptr, + nullptr, + false, + /*low_4gb*/false, + nullptr, + &error_msg)); + ASSERT_TRUE(oat != nullptr) << error_msg; + + // Originally all the dex checksums should be up to date. + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the dex1 checksum. + Copy(GetDexSrc2(), dex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex1 checksum. + Copy(GetDexSrc1(), dex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the non-main multidex checksum. + Copy(GetMultiDexSrc2(), multidex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the multidex checksum. + Copy(GetMultiDexSrc1(), multidex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Invalidate the dex2 checksum. + Copy(GetDexSrc1(), dex2); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // restore the dex2 checksum. + Copy(GetDexSrc2(), dex2); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Replace the multidex file with a non-multidex file. + Copy(GetDexSrc1(), multidex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the multidex file + Copy(GetMultiDexSrc1(), multidex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Replace dex1 with a multidex file. + Copy(GetMultiDexSrc1(), dex1); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex1 file. + Copy(GetDexSrc1(), dex1); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Remove the dex2 file. + EXPECT_EQ(0, unlink(dex2.c_str())); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); + + // Restore the dex2 file. + Copy(GetDexSrc2(), dex2); + EXPECT_TRUE(ImageSpace::ValidateOatFile(*oat, &error_msg)) << error_msg; + + // Remove the multidex file. + EXPECT_EQ(0, unlink(multidex1.c_str())); + EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg)); +} + +} // namespace space +} // namespace gc +} // namespace art diff --git a/runtime/gc/space/space_create_test.cc b/runtime/gc/space/space_create_test.cc index 7bc4dc40e4..ca5f306264 100644 --- a/runtime/gc/space/space_create_test.cc +++ b/runtime/gc/space/space_create_test.cc @@ -108,7 +108,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { &ptr1_bytes_allocated, &ptr1_usable_size, &ptr1_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_TRUE(ptr1 != nullptr); EXPECT_LE(1U * MB, ptr1_bytes_allocated); EXPECT_LE(1U * MB, ptr1_usable_size); EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); @@ -126,7 +126,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { &ptr3_bytes_allocated, &ptr3_usable_size, &ptr3_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_TRUE(ptr3 != nullptr); EXPECT_LE(8U * MB, ptr3_bytes_allocated); EXPECT_LE(8U * MB, ptr3_usable_size); EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); @@ -154,7 +154,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { &ptr6_bytes_allocated, &ptr6_usable_size, &ptr6_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr6.Get() != nullptr); + EXPECT_TRUE(ptr6 != nullptr); EXPECT_LE(9U * MB, ptr6_bytes_allocated); EXPECT_LE(9U * MB, ptr6_usable_size); EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); @@ -193,7 +193,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { &ptr1_bytes_allocated, &ptr1_usable_size, &ptr1_bytes_tl_bulk_allocated)); - EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_TRUE(ptr1 != nullptr); EXPECT_LE(1U * MB, ptr1_bytes_allocated); EXPECT_LE(1U * MB, ptr1_usable_size); EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); @@ -210,7 +210,7 @@ TEST_P(SpaceCreateTest, ZygoteSpaceTestBody) { &ptr3_bytes_allocated, &ptr3_usable_size, &ptr3_bytes_tl_bulk_allocated)); - EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_TRUE(ptr3 != nullptr); EXPECT_LE(2U * MB, ptr3_bytes_allocated); EXPECT_LE(2U * MB, ptr3_usable_size); EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); @@ -242,7 +242,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) { &ptr1_bytes_allocated, &ptr1_usable_size, &ptr1_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr1.Get() != nullptr); + EXPECT_TRUE(ptr1 != nullptr); EXPECT_LE(1U * MB, ptr1_bytes_allocated); EXPECT_LE(1U * MB, ptr1_usable_size); EXPECT_LE(ptr1_usable_size, ptr1_bytes_allocated); @@ -260,7 +260,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) { &ptr3_bytes_allocated, &ptr3_usable_size, &ptr3_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr3.Get() != nullptr); + EXPECT_TRUE(ptr3 != nullptr); EXPECT_LE(8U * MB, ptr3_bytes_allocated); EXPECT_LE(8U * MB, ptr3_usable_size); EXPECT_LE(ptr3_usable_size, ptr3_bytes_allocated); @@ -288,7 +288,7 @@ TEST_P(SpaceCreateTest, AllocAndFreeTestBody) { &ptr6_bytes_allocated, &ptr6_usable_size, &ptr6_bytes_tl_bulk_allocated))); - EXPECT_TRUE(ptr6.Get() != nullptr); + EXPECT_TRUE(ptr6 != nullptr); EXPECT_LE(9U * MB, ptr6_bytes_allocated); EXPECT_LE(9U * MB, ptr6_usable_size); EXPECT_LE(ptr6_usable_size, ptr6_bytes_allocated); diff --git a/runtime/gc/space/space_test.h b/runtime/gc/space/space_test.h index cbb3d73497..1fe3fb2e86 100644 --- a/runtime/gc/space/space_test.h +++ b/runtime/gc/space/space_test.h @@ -200,7 +200,7 @@ void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, } footprint = space->GetFootprint(); EXPECT_GE(space->Size(), footprint); // invariant - if (object.Get() != nullptr) { // allocation succeeded + if (object != nullptr) { // allocation succeeded lots_of_objects[i] = object.Get(); size_t allocation_size = space->AllocationSize(object.Get(), nullptr); EXPECT_EQ(bytes_allocated, allocation_size); @@ -296,7 +296,7 @@ void SpaceTest<Super>::SizeFootPrintGrowthLimitAndTrimBody(MallocSpace* space, large_object.Assign(AllocWithGrowth(space, self, three_quarters_space, &bytes_allocated, nullptr, &bytes_tl_bulk_allocated)); } - EXPECT_TRUE(large_object.Get() != nullptr); + EXPECT_TRUE(large_object != nullptr); // Sanity check footprint footprint = space->GetFootprint(); diff --git a/runtime/gc/system_weak.h b/runtime/gc/system_weak.h index e5cddfc6f9..60105f4e4f 100644 --- a/runtime/gc/system_weak.h +++ b/runtime/gc/system_weak.h @@ -82,7 +82,7 @@ class SystemWeakHolder : public AbstractSystemWeakHolder { (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(&allow_disallow_lock_); new_weak_condition_.WaitHoldingLocks(self); } } diff --git a/runtime/handle.h b/runtime/handle.h index e4b6d29a55..ccff575495 100644 --- a/runtime/handle.h +++ b/runtime/handle.h @@ -81,6 +81,14 @@ class Handle : public ValueObject { return reference_; } + ALWAYS_INLINE bool operator!=(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) { + return !IsNull(); + } + + ALWAYS_INLINE bool operator==(std::nullptr_t) const REQUIRES_SHARED(Locks::mutator_lock_) { + return IsNull(); + } + protected: template<typename S> explicit Handle(StackReference<S>* reference) diff --git a/runtime/imtable_test.cc b/runtime/imtable_test.cc index 8cbe2916ec..17149dfe44 100644 --- a/runtime/imtable_test.cc +++ b/runtime/imtable_test.cc @@ -53,7 +53,7 @@ class ImTableTest : public CommonRuntimeTest { ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_a))); Handle<mirror::Class> h_class_a( hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader))); - if (h_class_a.Get() == nullptr) { + if (h_class_a == nullptr) { LOG(ERROR) << self->GetException()->Dump(); CHECK(false) << "h_class_a == nullptr"; } @@ -63,7 +63,7 @@ class ImTableTest : public CommonRuntimeTest { ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(jclass_loader_b))); Handle<mirror::Class> h_class_b( hs.NewHandle(class_linker->FindClass(self, class_name.c_str(), h_class_loader))); - if (h_class_b.Get() == nullptr) { + if (h_class_b == nullptr) { LOG(ERROR) << self->GetException()->Dump(); CHECK(false) << "h_class_b == nullptr"; } diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc index bf4cab24cc..6aefe239a9 100644 --- a/runtime/indirect_reference_table_test.cc +++ b/runtime/indirect_reference_table_test.cc @@ -64,13 +64,13 @@ TEST_F(IndirectReferenceTableTest, BasicTest) { StackHandleScope<4> hs(soa.Self()); ASSERT_TRUE(c != nullptr); Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj0.Get() != nullptr); + ASSERT_TRUE(obj0 != nullptr); Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj1.Get() != nullptr); + ASSERT_TRUE(obj1 != nullptr); Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj2.Get() != nullptr); + ASSERT_TRUE(obj2 != nullptr); Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj3.Get() != nullptr); + ASSERT_TRUE(obj3 != nullptr); const IRTSegmentState cookie = kIRTFirstSegment; @@ -282,15 +282,15 @@ TEST_F(IndirectReferenceTableTest, Holes) { StackHandleScope<5> hs(soa.Self()); ASSERT_TRUE(c != nullptr); Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj0.Get() != nullptr); + ASSERT_TRUE(obj0 != nullptr); Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj1.Get() != nullptr); + ASSERT_TRUE(obj1 != nullptr); Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj2.Get() != nullptr); + ASSERT_TRUE(obj2 != nullptr); Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj3.Get() != nullptr); + ASSERT_TRUE(obj3 != nullptr); Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj4.Get() != nullptr); + ASSERT_TRUE(obj4 != nullptr); std::string error_msg; @@ -491,7 +491,7 @@ TEST_F(IndirectReferenceTableTest, Resize) { StackHandleScope<1> hs(soa.Self()); ASSERT_TRUE(c != nullptr); Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self())); - ASSERT_TRUE(obj0.Get() != nullptr); + ASSERT_TRUE(obj0 != nullptr); std::string error_msg; IndirectReferenceTable irt(kTableMax, diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index f11e2cba10..d862ff2708 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -1010,15 +1010,18 @@ void Instrumentation::FieldWriteEventImpl(Thread* thread, mirror::Object* this_o void Instrumentation::ExceptionCaughtEvent(Thread* thread, mirror::Throwable* exception_object) const { + Thread* self = Thread::Current(); + StackHandleScope<1> hs(self); + Handle<mirror::Throwable> h_exception(hs.NewHandle(exception_object)); if (HasExceptionCaughtListeners()) { - DCHECK_EQ(thread->GetException(), exception_object); + DCHECK_EQ(thread->GetException(), h_exception.Get()); thread->ClearException(); for (InstrumentationListener* listener : exception_caught_listeners_) { if (listener != nullptr) { - listener->ExceptionCaught(thread, exception_object); + listener->ExceptionCaught(thread, h_exception.Get()); } } - thread->SetException(exception_object); + thread->SetException(h_exception.Get()); } } diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc index 3991d6550d..f0d0260482 100644 --- a/runtime/intern_table_test.cc +++ b/runtime/intern_table_test.cc @@ -36,10 +36,10 @@ TEST_F(InternTableTest, Intern) { Handle<mirror::String> foo_3( hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "foo"))); Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar"))); - ASSERT_TRUE(foo_1.Get() != nullptr); - ASSERT_TRUE(foo_2.Get() != nullptr); - ASSERT_TRUE(foo_3.Get() != nullptr); - ASSERT_TRUE(bar.Get() != nullptr); + ASSERT_TRUE(foo_1 != nullptr); + ASSERT_TRUE(foo_2 != nullptr); + ASSERT_TRUE(foo_3 != nullptr); + ASSERT_TRUE(bar != nullptr); EXPECT_EQ(foo_1.Get(), foo_2.Get()); EXPECT_TRUE(foo_1->Equals("foo")); EXPECT_TRUE(foo_2->Equals("foo")); @@ -204,9 +204,9 @@ TEST_F(InternTableTest, LookupStrong) { Handle<mirror::String> foo(hs.NewHandle(intern_table.InternStrong(3, "foo"))); Handle<mirror::String> bar(hs.NewHandle(intern_table.InternStrong(3, "bar"))); Handle<mirror::String> foobar(hs.NewHandle(intern_table.InternStrong(6, "foobar"))); - ASSERT_TRUE(foo.Get() != nullptr); - ASSERT_TRUE(bar.Get() != nullptr); - ASSERT_TRUE(foobar.Get() != nullptr); + ASSERT_TRUE(foo != nullptr); + ASSERT_TRUE(bar != nullptr); + ASSERT_TRUE(foobar != nullptr); ASSERT_TRUE(foo->Equals("foo")); ASSERT_TRUE(bar->Equals("bar")); ASSERT_TRUE(foobar->Equals("foobar")); diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 28bcb97105..8978bfd5af 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -519,7 +519,7 @@ void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame, } } -template<bool is_range, bool do_access_check> +template<bool is_range> bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, @@ -539,10 +539,10 @@ bool DoInvokePolymorphic(Thread* self, // was symbolically invoked in bytecode (say MethodHandle.invoke or MethodHandle.invokeExact) // and not the method that we'll dispatch to in the end. StackHandleScope<5> hs(self); - Handle<mirror::MethodHandleImpl> method_handle(hs.NewHandle( - ObjPtr<mirror::MethodHandleImpl>::DownCast( + Handle<mirror::MethodHandle> method_handle(hs.NewHandle( + ObjPtr<mirror::MethodHandle>::DownCast( MakeObjPtr(shadow_frame.GetVRegReference(vRegC))))); - if (UNLIKELY(method_handle.Get() == nullptr)) { + if (UNLIKELY(method_handle == nullptr)) { // Note that the invoke type is kVirtual here because a call to a signature // polymorphic method is shaped like a virtual call at the bytecode level. ThrowNullPointerExceptionForMethodAccess(invoke_method_idx, InvokeType::kVirtual); @@ -564,7 +564,7 @@ bool DoInvokePolymorphic(Thread* self, hs.NewHandle<mirror::ClassLoader>(caller_class->GetClassLoader())))); // This implies we couldn't resolve one or more types in this method handle. - if (UNLIKELY(callsite_type.Get() == nullptr)) { + if (UNLIKELY(callsite_type == nullptr)) { CHECK(self->IsExceptionPending()); return false; } @@ -584,31 +584,300 @@ bool DoInvokePolymorphic(Thread* self, // VRegC is the register holding the method handle. Arguments passed // to the method handle's target do not include the method handle. uint32_t first_arg = inst->VRegC_4rcc() + 1; - return DoInvokePolymorphic<is_range, do_access_check>(self, - invoke_method, - shadow_frame, - method_handle, - callsite_type, - args /* unused */, - first_arg, - result); + return DoInvokePolymorphic<is_range>(self, + invoke_method, + shadow_frame, + method_handle, + callsite_type, + args /* unused */, + first_arg, + result); } else { // Get the register arguments for the invoke. inst->GetVarArgs(args, inst_data); // Drop the first register which is the method handle performing the invoke. memmove(args, args + 1, sizeof(args[0]) * (Instruction::kMaxVarArgRegs - 1)); args[Instruction::kMaxVarArgRegs - 1] = 0; - return DoInvokePolymorphic<is_range, do_access_check>(self, - invoke_method, - shadow_frame, - method_handle, - callsite_type, - args, - args[0], - result); + return DoInvokePolymorphic<is_range>(self, + invoke_method, + shadow_frame, + method_handle, + callsite_type, + args, + args[0], + result); } } +static ObjPtr<mirror::CallSite> InvokeBootstrapMethod(Thread* self, + ShadowFrame& shadow_frame, + uint32_t call_site_idx) + REQUIRES_SHARED(Locks::mutator_lock_) { + ArtMethod* referrer = shadow_frame.GetMethod(); + const DexFile* dex_file = referrer->GetDexFile(); + const DexFile::CallSiteIdItem& csi = dex_file->GetCallSiteId(call_site_idx); + + StackHandleScope<9> hs(self); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader())); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache())); + + CallSiteArrayValueIterator it(*dex_file, csi); + uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i); + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + Handle<mirror::MethodHandle> + bootstrap(hs.NewHandle(class_linker->ResolveMethodHandle(method_handle_idx, referrer))); + if (bootstrap.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + Handle<mirror::MethodType> bootstrap_method_type = hs.NewHandle(bootstrap->GetMethodType()); + it.Next(); + + DCHECK_EQ(static_cast<size_t>(bootstrap->GetMethodType()->GetPTypes()->GetLength()), it.Size()); + const size_t num_bootstrap_vregs = bootstrap->GetMethodType()->NumberOfVRegs(); + + // Set-up a shadow frame for invoking the bootstrap method handle. + ShadowFrameAllocaUniquePtr bootstrap_frame = + CREATE_SHADOW_FRAME(num_bootstrap_vregs, nullptr, referrer, shadow_frame.GetDexPC()); + ScopedStackedShadowFramePusher pusher( + self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction); + size_t vreg = 0; + + // The first parameter is a MethodHandles lookup instance. + { + Handle<mirror::Class> lookup_class(hs.NewHandle(bootstrap->GetTargetClass())); + ObjPtr<mirror::MethodHandlesLookup> lookup = + mirror::MethodHandlesLookup::Create(self, lookup_class); + if (lookup.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg++, lookup.Ptr()); + } + + // The second parameter is the name to lookup. + { + dex::StringIndex name_idx(static_cast<uint32_t>(it.GetJavaValue().i)); + ObjPtr<mirror::String> name = class_linker->ResolveString(*dex_file, name_idx, dex_cache); + if (name.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg++, name.Ptr()); + } + it.Next(); + + // The third parameter is the method type associated with the name. + uint32_t method_type_idx = static_cast<uint32_t>(it.GetJavaValue().i); + Handle<mirror::MethodType> + method_type(hs.NewHandle(class_linker->ResolveMethodType(*dex_file, + method_type_idx, + dex_cache, + class_loader))); + if (method_type.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg++, method_type.Get()); + it.Next(); + + // Append remaining arguments (if any). + while (it.HasNext()) { + const jvalue& jvalue = it.GetJavaValue(); + switch (it.GetValueType()) { + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kInt: + bootstrap_frame->SetVReg(vreg, jvalue.i); + vreg += 1; + break; + case EncodedArrayValueIterator::ValueType::kLong: + bootstrap_frame->SetVRegLong(vreg, jvalue.j); + vreg += 2; + break; + case EncodedArrayValueIterator::ValueType::kFloat: + bootstrap_frame->SetVRegFloat(vreg, jvalue.f); + vreg += 1; + break; + case EncodedArrayValueIterator::ValueType::kDouble: + bootstrap_frame->SetVRegDouble(vreg, jvalue.d); + vreg += 2; + break; + case EncodedArrayValueIterator::ValueType::kMethodType: { + uint32_t idx = static_cast<uint32_t>(jvalue.i); + ObjPtr<mirror::MethodType> ref = + class_linker->ResolveMethodType(*dex_file, idx, dex_cache, class_loader); + if (ref.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); + vreg += 1; + break; + } + case EncodedArrayValueIterator::ValueType::kMethodHandle: { + uint32_t idx = static_cast<uint32_t>(jvalue.i); + ObjPtr<mirror::MethodHandle> ref = + class_linker->ResolveMethodHandle(idx, referrer); + if (ref.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); + vreg += 1; + break; + } + case EncodedArrayValueIterator::ValueType::kString: { + dex::StringIndex idx(static_cast<uint32_t>(jvalue.i)); + ObjPtr<mirror::String> ref = class_linker->ResolveString(*dex_file, idx, dex_cache); + if (ref.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); + vreg += 1; + break; + } + case EncodedArrayValueIterator::ValueType::kType: { + dex::TypeIndex idx(static_cast<uint32_t>(jvalue.i)); + ObjPtr<mirror::Class> ref = + class_linker->ResolveType(*dex_file, idx, dex_cache, class_loader); + if (ref.IsNull()) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + bootstrap_frame->SetVRegReference(vreg, ref.Ptr()); + vreg += 1; + break; + } + case EncodedArrayValueIterator::ValueType::kNull: + bootstrap_frame->SetVRegReference(vreg, nullptr); + vreg += 1; + break; + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + // Unreachable based on current EncodedArrayValueIterator::Next(). + UNREACHABLE(); + } + + it.Next(); + } + + // Invoke the bootstrap method handle. + JValue result; + + // This array of arguments is unused. DoInvokePolymorphic() operates on either a + // an argument array or a range, but always takes an array argument. + uint32_t args_unused[Instruction::kMaxVarArgRegs]; + ArtMethod* invoke_exact = + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); + bool invoke_success = DoInvokePolymorphic<true /* is_range */>(self, + invoke_exact, + *bootstrap_frame, + bootstrap, + bootstrap_method_type, + args_unused, + 0, + &result); + if (!invoke_success) { + DCHECK(self->IsExceptionPending()); + return nullptr; + } + + Handle<mirror::Object> object(hs.NewHandle(result.GetL())); + + // Check the result is not null. + if (UNLIKELY(object.IsNull())) { + ThrowNullPointerException("CallSite == null"); + return nullptr; + } + + // Check the result type is a subclass of CallSite. + if (UNLIKELY(!object->InstanceOf(mirror::CallSite::StaticClass()))) { + ThrowClassCastException(object->GetClass(), mirror::CallSite::StaticClass()); + return nullptr; + } + + Handle<mirror::CallSite> call_site = + hs.NewHandle(ObjPtr<mirror::CallSite>::DownCast(ObjPtr<mirror::Object>(result.GetL()))); + + // Check the call site target is not null as we're going to invoke it. + Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget()); + if (UNLIKELY(target.IsNull())) { + ThrowNullPointerException("CallSite target == null"); + return nullptr; + } + + // Check the target method type matches the method type requested. + if (UNLIKELY(!target->GetMethodType()->IsExactMatch(method_type.Get()))) { + ThrowWrongMethodTypeException(target->GetMethodType(), method_type.Get()); + return nullptr; + } + + return call_site.Get(); +} + +template<bool is_range> +bool DoInvokeCustom(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result) + REQUIRES_SHARED(Locks::mutator_lock_) { + // invoke-custom is not supported in transactions. In transactions + // there is a limited set of types supported. invoke-custom allows + // running arbitrary code and instantiating arbitrary types. + CHECK(!Runtime::Current()->IsActiveTransaction()); + StackHandleScope<4> hs(self); + Handle<mirror::DexCache> dex_cache(hs.NewHandle(shadow_frame.GetMethod()->GetDexCache())); + const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + MutableHandle<mirror::CallSite> + call_site(hs.NewHandle(dex_cache->GetResolvedCallSite(call_site_idx))); + if (call_site.IsNull()) { + call_site.Assign(InvokeBootstrapMethod(self, shadow_frame, call_site_idx)); + if (UNLIKELY(call_site.IsNull())) { + CHECK(self->IsExceptionPending()); + ThrowWrappedBootstrapMethodError("Exception from call site #%u bootstrap method", + call_site_idx); + result->SetJ(0); + return false; + } + mirror::CallSite* winning_call_site = + dex_cache->SetResolvedCallSite(call_site_idx, call_site.Get()); + call_site.Assign(winning_call_site); + } + + // CallSite.java checks the re-assignment of the call site target + // when mutating call site targets. We only check the target is + // non-null and has the right type during bootstrap method execution. + Handle<mirror::MethodHandle> target = hs.NewHandle(call_site->GetTarget()); + Handle<mirror::MethodType> target_method_type = hs.NewHandle(target->GetMethodType()); + DCHECK_EQ(static_cast<size_t>(inst->VRegA()), target_method_type->NumberOfVRegs()); + + uint32_t args[Instruction::kMaxVarArgRegs]; + if (is_range) { + args[0] = inst->VRegC_3rc(); + } else { + inst->GetVarArgs(args, inst_data); + } + + ArtMethod* invoke_exact = + jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact); + return DoInvokePolymorphic<is_range>(self, + invoke_exact, + shadow_frame, + target, + target_method_type, + args, + args[0], + result); +} + template <bool is_range> inline void CopyRegisters(ShadowFrame& caller_frame, ShadowFrame* callee_frame, @@ -975,17 +1244,24 @@ EXPLICIT_DO_CALL_TEMPLATE_DECL(true, false); EXPLICIT_DO_CALL_TEMPLATE_DECL(true, true); #undef EXPLICIT_DO_CALL_TEMPLATE_DECL -// Explicit DoInvokePolymorphic template function declarations. -#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \ - template REQUIRES_SHARED(Locks::mutator_lock_) \ - bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \ - Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ +// Explicit DoInvokeCustom template function declarations. +#define EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(_is_range) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ + bool DoInvokeCustom<_is_range>( \ + Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ uint16_t inst_data, JValue* result) +EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(false); +EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL(true); +#undef EXPLICIT_DO_INVOKE_CUSTOM_TEMPLATE_DECL -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true); +// Explicit DoInvokePolymorphic template function declarations. +#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ + bool DoInvokePolymorphic<_is_range>( \ + Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, \ + uint16_t inst_data, JValue* result) +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true); #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL // Explicit DoFilledNewArray template function declarations. diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 7ef3508164..6b22af9829 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -40,9 +40,11 @@ #include "entrypoints/entrypoint_utils-inl.h" #include "handle_scope-inl.h" #include "jit/jit.h" +#include "mirror/call_site.h" #include "mirror/class-inl.h" #include "mirror/dex_cache.h" #include "mirror/method.h" +#include "mirror/method_handles_lookup.h" #include "mirror/object-inl.h" #include "mirror/object_array-inl.h" #include "mirror/string-inl.h" @@ -154,13 +156,21 @@ static inline bool DoInvoke(Thread* self, } // Performs a signature polymorphic invoke (invoke-polymorphic/invoke-polymorphic-range). -template<bool is_range, bool do_access_check> +template<bool is_range> bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst, uint16_t inst_data, JValue* result); +// Performs a custom invoke (invoke-custom/invoke-custom-range). +template<bool is_range> +bool DoInvokeCustom(Thread* self, + ShadowFrame& shadow_frame, + const Instruction* inst, + uint16_t inst_data, + JValue* result); + // Handles invoke-virtual-quick and invoke-virtual-quick-range instructions. // Returns true on success, otherwise throws an exception and returns false. template<bool is_range> diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index a77a3fc2b3..b191dd79a1 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -1524,7 +1524,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::INVOKE_POLYMORPHIC: { PREAMBLE(); DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokePolymorphic<false, do_access_check>( + bool success = DoInvokePolymorphic<false /* is_range */>( self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx); break; @@ -1532,11 +1532,27 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, case Instruction::INVOKE_POLYMORPHIC_RANGE: { PREAMBLE(); DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); - bool success = DoInvokePolymorphic<true, do_access_check>( + bool success = DoInvokePolymorphic<true /* is_range */>( self, shadow_frame, inst, inst_data, &result_register); POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx); break; } + case Instruction::INVOKE_CUSTOM: { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokeCustom<false /* is_range */>( + self, shadow_frame, inst, inst_data, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } + case Instruction::INVOKE_CUSTOM_RANGE: { + PREAMBLE(); + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + bool success = DoInvokeCustom<true /* is_range */>( + self, shadow_frame, inst, inst_data, &result_register); + POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx); + break; + } case Instruction::NEG_INT: PREAMBLE(); shadow_frame.SetVReg( @@ -2315,7 +2331,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, break; case Instruction::UNUSED_3E ... Instruction::UNUSED_43: case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9: - case Instruction::UNUSED_FC ... Instruction::UNUSED_FF: + case Instruction::UNUSED_FE ... Instruction::UNUSED_FF: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: UnexpectedOpcode(inst, shadow_frame); diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 545cc1ad42..c7e84420d3 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -124,7 +124,7 @@ static void UnstartedRuntimeFindClass(Thread* self, Handle<mirror::String> class const std::string& method_name, bool initialize_class, bool abort_if_not_found) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(className.Get() != nullptr); + CHECK(className != nullptr); std::string descriptor(DotToDescriptor(className->ToModifiedUtf8().c_str())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); @@ -239,7 +239,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( Handle<mirror::Class> h_klass(hs.NewHandle(klass)); // Check that it's not null. - if (h_klass.Get() == nullptr) { + if (h_klass == nullptr) { AbortTransactionOrFail(self, "Class reference is null for newInstance"); return; } @@ -263,7 +263,7 @@ void UnstartedRuntime::UnstartedClassNewInstance( auto* cons = h_klass->FindDeclaredDirectMethod("<init>", "()V", cl->GetImagePointerSize()); if (cons != nullptr) { Handle<mirror::Object> h_obj(hs.NewHandle(klass->AllocObject(self))); - CHECK(h_obj.Get() != nullptr); // We don't expect OOM at compile-time. + CHECK(h_obj != nullptr); // We don't expect OOM at compile-time. EnterInterpreterFromInvoke(self, cons, h_obj.Get(), nullptr, nullptr); if (!self->IsExceptionPending()) { result->SetL(h_obj.Get()); @@ -542,7 +542,7 @@ static void GetResourceAsStream(Thread* self, // Create byte array for content. Handle<mirror::ByteArray> h_array(hs.NewHandle(mirror::ByteArray::Alloc(self, map_size))); - if (h_array.Get() == nullptr) { + if (h_array == nullptr) { AbortTransactionOrFail(self, "Could not find/create byte array class"); return; } @@ -556,7 +556,7 @@ static void GetResourceAsStream(Thread* self, runtime->GetClassLinker()->FindClass(self, "Ljava/io/ByteArrayInputStream;", ScopedNullHandle<mirror::ClassLoader>()))); - if (h_class.Get() == nullptr) { + if (h_class == nullptr) { AbortTransactionOrFail(self, "Could not find ByteArrayInputStream class"); return; } @@ -566,7 +566,7 @@ static void GetResourceAsStream(Thread* self, } Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); - if (h_obj.Get() == nullptr) { + if (h_obj == nullptr) { AbortTransactionOrFail(self, "Could not allocate ByteArrayInputStream object"); return; } @@ -800,7 +800,7 @@ static void GetSystemProperty(Thread* self, StackHandleScope<4> hs(self); Handle<mirror::String> h_key( hs.NewHandle(reinterpret_cast<mirror::String*>(shadow_frame->GetVRegReference(arg_offset)))); - if (h_key.Get() == nullptr) { + if (h_key == nullptr) { AbortTransactionOrFail(self, "getProperty key was null"); return; } @@ -815,7 +815,7 @@ static void GetSystemProperty(Thread* self, class_linker->FindClass(self, "Ljava/lang/AndroidHardcodedSystemProperties;", ScopedNullHandle<mirror::ClassLoader>()))); - if (h_props_class.Get() == nullptr) { + if (h_props_class == nullptr) { AbortTransactionOrFail(self, "Could not find AndroidHardcodedSystemProperties"); return; } @@ -837,7 +837,7 @@ static void GetSystemProperty(Thread* self, ObjPtr<mirror::Object> props = static_properties->GetObject(h_props_class.Get()); Handle<mirror::ObjectArray<mirror::ObjectArray<mirror::String>>> h_2string_array(hs.NewHandle( props->AsObjectArray<mirror::ObjectArray<mirror::String>>())); - if (h_2string_array.Get() == nullptr) { + if (h_2string_array == nullptr) { AbortTransactionOrFail(self, "Field %s is null", kAndroidHardcodedSystemPropertiesFieldName); return; } @@ -849,7 +849,7 @@ static void GetSystemProperty(Thread* self, hs.NewHandle<mirror::ObjectArray<mirror::String>>(nullptr)); for (int32_t i = 0; i < prop_count; ++i) { h_string_array.Assign(h_2string_array->Get(i)); - if (h_string_array.Get() == nullptr || + if (h_string_array == nullptr || h_string_array->GetLength() != 2 || h_string_array->Get(0) == nullptr) { AbortTransactionOrFail(self, @@ -924,7 +924,7 @@ static ObjPtr<mirror::Object> CreateInstanceOf(Thread* self, const char* class_d StackHandleScope<2> hs(self); Handle<mirror::Class> h_class(hs.NewHandle(klass)); Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self))); - if (h_obj.Get() != nullptr) { + if (h_obj != nullptr) { ArtMethod* init_method = h_class->FindDirectMethod( "<init>", "()V", class_linker->GetImagePointerSize()); if (init_method == nullptr) { diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc index 31be587e9c..16b7b55e00 100644 --- a/runtime/interpreter/unstarted_runtime_test.cc +++ b/runtime/interpreter/unstarted_runtime_test.cc @@ -960,7 +960,7 @@ TEST_F(UnstartedRuntimeTest, ThreadLocalGet) { class_linker->FindClass(self, "Lsun/misc/FloatingDecimal;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(floating_decimal.Get() != nullptr); + ASSERT_TRUE(floating_decimal != nullptr); ASSERT_TRUE(class_linker->EnsureInitialized(self, floating_decimal, true, true)); ArtMethod* caller_method = floating_decimal->FindDeclaredDirectMethod( @@ -1014,7 +1014,7 @@ TEST_F(UnstartedRuntimeTest, FloatConversion) { class_linker->FindClass(self, "Ljava/lang/Double;", ScopedNullHandle<mirror::ClassLoader>())); - ASSERT_TRUE(double_class.Get() != nullptr); + ASSERT_TRUE(double_class != nullptr); ASSERT_TRUE(class_linker->EnsureInitialized(self, double_class, true, true)); ArtMethod* method = double_class->FindDeclaredDirectMethod("toString", diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc index e0f28adc4f..a341cdb89f 100644 --- a/runtime/java_vm_ext.cc +++ b/runtime/java_vm_ext.cc @@ -572,7 +572,7 @@ jweak JavaVMExt::AddWeakGlobalRef(Thread* self, ObjPtr<mirror::Object> obj) { while (!kUseReadBarrier && UNLIKELY(!MayAccessWeakGlobals(self))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_); weak_globals_add_condition_.WaitHoldingLocks(self); } IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj); @@ -706,7 +706,7 @@ ObjPtr<mirror::Object> JavaVMExt::DecodeWeakGlobalLocked(Thread* self, IndirectR while (UNLIKELY(!MayAccessWeakGlobals(self))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_); weak_globals_add_condition_.WaitHoldingLocks(self); } return weak_globals_.Get(ref); @@ -731,7 +731,7 @@ bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) { while (UNLIKELY(!MayAccessWeakGlobals(self))) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(Locks::jni_weak_globals_lock_); weak_globals_add_condition_.WaitHoldingLocks(self); } // When just checking a weak ref has been cleared, avoid triggering the read barrier in decode diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h index e5d34e1a2d..86af6d44db 100644 --- a/runtime/jdwp/jdwp.h +++ b/runtime/jdwp/jdwp.h @@ -22,6 +22,7 @@ #include "jdwp/jdwp_bits.h" #include "jdwp/jdwp_constants.h" #include "jdwp/jdwp_expand_buf.h" +#include "obj_ptr.h" #include <pthread.h> #include <stddef.h> @@ -286,6 +287,10 @@ struct JdwpState { REQUIRES(!event_list_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + void UnregisterLocationEventsOnClass(ObjPtr<mirror::Class> klass) + REQUIRES(!event_list_lock_) + REQUIRES_SHARED(Locks::mutator_lock_); + /* * Unregister all events. */ diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc index 172f52a974..96249f9b58 100644 --- a/runtime/jdwp/jdwp_event.cc +++ b/runtime/jdwp/jdwp_event.cc @@ -251,6 +251,43 @@ JdwpError JdwpState::RegisterEvent(JdwpEvent* pEvent) { return ERR_NONE; } +void JdwpState::UnregisterLocationEventsOnClass(ObjPtr<mirror::Class> klass) { + VLOG(jdwp) << "Removing events within " << klass->PrettyClass(); + StackHandleScope<1> hs(Thread::Current()); + Handle<mirror::Class> h_klass(hs.NewHandle(klass)); + std::vector<JdwpEvent*> to_remove; + MutexLock mu(Thread::Current(), event_list_lock_); + for (JdwpEvent* cur_event = event_list_; cur_event != nullptr; cur_event = cur_event->next) { + // Fill in the to_remove list + bool found_event = false; + for (int i = 0; i < cur_event->modCount && !found_event; i++) { + JdwpEventMod& mod = cur_event->mods[i]; + switch (mod.modKind) { + case MK_LOCATION_ONLY: { + JdwpLocation& loc = mod.locationOnly.loc; + JdwpError error; + ObjPtr<mirror::Class> breakpoint_class( + Dbg::GetObjectRegistry()->Get<art::mirror::Class*>(loc.class_id, &error)); + DCHECK_EQ(error, ERR_NONE); + if (breakpoint_class == h_klass.Get()) { + to_remove.push_back(cur_event); + found_event = true; + } + break; + } + default: + // TODO Investigate how we should handle non-locationOnly events. + break; + } + } + } + + for (JdwpEvent* event : to_remove) { + UnregisterEvent(event); + EventFree(event); + } +} + /* * Remove an event from the list. This will also remove the event from * any optimization tables, e.g. breakpoints. diff --git a/runtime/jdwp/object_registry.cc b/runtime/jdwp/object_registry.cc index 4615574947..bd7251baeb 100644 --- a/runtime/jdwp/object_registry.cc +++ b/runtime/jdwp/object_registry.cc @@ -57,7 +57,7 @@ JDWP::ObjectId ObjectRegistry::Add(ObjPtr<mirror::Object> o) { // Template instantiations must be declared below. template<class T> JDWP::ObjectId ObjectRegistry::Add(Handle<T> obj_h) { - if (obj_h.Get() == nullptr) { + if (obj_h == nullptr) { return 0; } return InternalAdd(obj_h); @@ -76,7 +76,7 @@ JDWP::ObjectId ObjectRegistry::Add(Handle<mirror::Throwable> obj_h); template<class T> JDWP::ObjectId ObjectRegistry::InternalAdd(Handle<T> obj_h) { - CHECK(obj_h.Get() != nullptr); + CHECK(obj_h != nullptr); Thread* const self = Thread::Current(); self->AssertNoPendingException(); diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc index f5151b588a..60ab275641 100644 --- a/runtime/jit/jit_code_cache.cc +++ b/runtime/jit/jit_code_cache.cc @@ -556,12 +556,13 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self, // Flush data cache, as compiled code references literals in it. FlushDataCache(reinterpret_cast<char*>(roots_data), reinterpret_cast<char*>(roots_data + data_size)); - // Flush caches before we remove write permission because on some ARMv8 hardware, - // flushing caches require write permissions. + // Flush caches before we remove write permission because some ARMv8 Qualcomm kernels may + // trigger a segfault if a page fault occurs when requesting a cache maintenance operation. + // This is a kernel bug that we need to work around until affected devices (e.g. Nexus 5X and + // 6P) stop being supported or their kernels are fixed. // - // For reference, here are kernel patches discussing about this issue: - // https://android.googlesource.com/kernel/msm/%2B/0e7f7bcc3fc87489cda5aa6aff8ce40eed912279 - // https://patchwork.kernel.org/patch/9047921/ + // For reference, this behavior is caused by this commit: + // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c FlushInstructionCache(reinterpret_cast<char*>(code_ptr), reinterpret_cast<char*>(code_ptr + code_size)); DCHECK(!Runtime::Current()->IsAotCompiler()); @@ -685,6 +686,10 @@ void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) { // shouldn't be used since it is no longer logically in the jit code cache. // TODO We should add DCHECKS that validate that the JIT is paused when this method is entered. void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) { + // Native methods have no profiling info and need no special handling from the JIT code cache. + if (old_method->IsNative()) { + return; + } MutexLock mu(Thread::Current(), lock_); // Update ProfilingInfo to the new one and remove it from the old_method. if (old_method->GetProfilingInfo(kRuntimePointerSize) != nullptr) { diff --git a/runtime/jit/profile_compilation_info.cc b/runtime/jit/profile_compilation_info.cc index 9ba2d1a355..54fc0386e1 100644 --- a/runtime/jit/profile_compilation_info.cc +++ b/runtime/jit/profile_compilation_info.cc @@ -664,6 +664,38 @@ std::string ProfileCompilationInfo::DumpInfo(const std::vector<const DexFile*>* return os.str(); } +void ProfileCompilationInfo::GetClassNames( + const std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* class_names) const { + std::unique_ptr<const std::vector<const DexFile*>> non_owning_dex_files( + MakeNonOwningVector(dex_files)); + GetClassNames(non_owning_dex_files.get(), class_names); +} + +void ProfileCompilationInfo::GetClassNames(const std::vector<const DexFile*>* dex_files, + std::set<std::string>* class_names) const { + if (info_.empty()) { + return; + } + for (const auto& it : info_) { + const std::string& location = it.first; + const DexFileData& dex_data = it.second; + const DexFile* dex_file = nullptr; + if (dex_files != nullptr) { + for (size_t i = 0; i < dex_files->size(); i++) { + if (location == (*dex_files)[i]->GetLocation()) { + dex_file = (*dex_files)[i]; + } + } + } + for (const auto class_it : dex_data.class_set) { + if (dex_file != nullptr) { + class_names->insert(std::string(dex_file->PrettyType(class_it))); + } + } + } +} + bool ProfileCompilationInfo::Equals(const ProfileCompilationInfo& other) { return info_.Equals(other.info_); } diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h index b1587c0070..758b46d74a 100644 --- a/runtime/jit/profile_compilation_info.h +++ b/runtime/jit/profile_compilation_info.h @@ -78,6 +78,11 @@ class ProfileCompilationInfo { std::string DumpInfo(const std::vector<const DexFile*>* dex_files, bool print_full_dex_location = true) const; + void GetClassNames(const std::vector<std::unique_ptr<const DexFile>>* dex_files, + std::set<std::string>* class_names) const; + void GetClassNames(const std::vector<const DexFile*>* dex_files, + std::set<std::string>* class_names) const; + bool Equals(const ProfileCompilationInfo& other); static std::string GetProfileDexFileKey(const std::string& dex_location); diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 3c641b0b75..547b5b8a2d 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -196,7 +196,7 @@ static jfieldID FindFieldID(const ScopedObjectAccess& soa, jclass jni_class, con StackHandleScope<2> hs(soa.Self()); Handle<mirror::Class> c( hs.NewHandle(EnsureInitialized(soa.Self(), soa.Decode<mirror::Class>(jni_class)))); - if (c.Get() == nullptr) { + if (c == nullptr) { return nullptr; } ArtField* field = nullptr; diff --git a/runtime/jobject_comparator.cc b/runtime/jobject_comparator.cc index 443f095d05..4c45e3839b 100644 --- a/runtime/jobject_comparator.cc +++ b/runtime/jobject_comparator.cc @@ -34,9 +34,9 @@ bool JobjectComparator::operator()(jobject jobj1, jobject jobj2) const { StackHandleScope<2> hs(soa.Self()); Handle<mirror::Object> obj1(hs.NewHandle(soa.Decode<mirror::Object>(jobj1))); Handle<mirror::Object> obj2(hs.NewHandle(soa.Decode<mirror::Object>(jobj2))); - if (obj1.Get() == nullptr) { + if (obj1 == nullptr) { return true; - } else if (obj2.Get() == nullptr) { + } else if (obj2 == nullptr) { return false; } // Sort by class... diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index dce56b3c58..93c212bafb 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -400,7 +400,7 @@ MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, // reuse means it is okay that it overlaps an existing page mapping. // Only use this if you actually made the page reservation yourself. CHECK(expected_ptr != nullptr); - + DCHECK(error_msg != nullptr); DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << ((error_msg != nullptr) ? *error_msg : std::string()); flags |= MAP_FIXED; diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc index 99886e5c2f..6ecfd8c714 100644 --- a/runtime/method_handles.cc +++ b/runtime/method_handles.cc @@ -220,7 +220,7 @@ bool ConvertJValueCommon( StackHandleScope<2> hs(Thread::Current()); Handle<mirror::Class> h_to(hs.NewHandle(to)); Handle<mirror::Object> h_obj(hs.NewHandle(src_value.GetL())); - if (h_obj.Get() != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) { + if (h_obj != nullptr && !to->IsAssignableFrom(h_obj->GetClass())) { ThrowClassCastException(h_to.Get(), h_obj->GetClass()); return false; } @@ -555,7 +555,7 @@ static inline bool DoCallTransform(ArtMethod* called_method, Handle<mirror::MethodType> callee_type, Thread* self, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> receiver, + Handle<mirror::MethodHandle> receiver, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, JValue* result) @@ -599,7 +599,7 @@ static inline bool DoCallTransform(ArtMethod* called_method, // Something went wrong while creating the emulated stack frame, we should // throw the pending exception. - if (sf.Get() == nullptr) { + if (sf == nullptr) { DCHECK(self->IsExceptionPending()); return false; } @@ -645,7 +645,7 @@ inline static ObjPtr<mirror::Class> GetAndInitializeDeclaringClass(Thread* self, template <bool is_range> bool DoInvokePolymorphicUnchecked(Thread* self, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, @@ -780,7 +780,6 @@ inline static void DoFieldGetForInvokePolymorphic(Thread* self, } // Helper for setters in invoke-polymorphic. -template <bool do_assignability_check> inline bool DoFieldPutForInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame, ObjPtr<mirror::Object>& obj, @@ -788,30 +787,33 @@ inline bool DoFieldPutForInvokePolymorphic(Thread* self, Primitive::Type field_type, const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) { - static const bool kTransaction = false; + DCHECK(!Runtime::Current()->IsActiveTransaction()); + static const bool kTransaction = false; // Not in a transaction. + static const bool kAssignabilityCheck = false; // No access check. switch (field_type) { case Primitive::kPrimBoolean: - return DoFieldPutCommon<Primitive::kPrimBoolean, do_assignability_check, kTransaction>( - self, shadow_frame, obj, field, value); + return + DoFieldPutCommon<Primitive::kPrimBoolean, kAssignabilityCheck, kTransaction>( + self, shadow_frame, obj, field, value); case Primitive::kPrimByte: - return DoFieldPutCommon<Primitive::kPrimByte, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimByte, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimChar: - return DoFieldPutCommon<Primitive::kPrimChar, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimChar, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimShort: - return DoFieldPutCommon<Primitive::kPrimShort, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimShort, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimInt: case Primitive::kPrimFloat: - return DoFieldPutCommon<Primitive::kPrimInt, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimInt, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimLong: case Primitive::kPrimDouble: - return DoFieldPutCommon<Primitive::kPrimLong, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimLong, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimNot: - return DoFieldPutCommon<Primitive::kPrimNot, do_assignability_check, kTransaction>( + return DoFieldPutCommon<Primitive::kPrimNot, kAssignabilityCheck, kTransaction>( self, shadow_frame, obj, field, value); case Primitive::kPrimVoid: LOG(FATAL) << "Unreachable: " << field_type; @@ -855,10 +857,10 @@ static JValue GetValueFromShadowFrame(const ShadowFrame& shadow_frame, return field_value; } -template <bool is_range, bool do_conversions, bool do_assignability_check> +template <bool is_range, bool do_conversions> bool DoInvokePolymorphicFieldAccess(Thread* self, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, @@ -903,12 +905,7 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, return false; } ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg); - return DoFieldPutForInvokePolymorphic<do_assignability_check>(self, - shadow_frame, - obj, - field, - field_type, - value); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); } case mirror::MethodHandle::kStaticPut: { ObjPtr<mirror::Object> obj = GetAndInitializeDeclaringClass(self, field); @@ -922,12 +919,7 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, DCHECK(self->IsExceptionPending()); return false; } - return DoFieldPutForInvokePolymorphic<do_assignability_check>(self, - shadow_frame, - obj, - field, - field_type, - value); + return DoFieldPutForInvokePolymorphic(self, shadow_frame, obj, field, field_type, value); } default: LOG(FATAL) << "Unreachable: " << handle_kind; @@ -935,10 +927,10 @@ bool DoInvokePolymorphicFieldAccess(Thread* self, } } -template <bool is_range, bool do_assignability_check> +template <bool is_range> static inline bool DoInvokePolymorphicNonExact(Thread* self, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, @@ -959,7 +951,7 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, if (IsFieldAccess(handle_kind)) { if (UNLIKELY(callsite_type->IsExactMatch(handle_type.Ptr()))) { const bool do_convert = false; - return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + return DoInvokePolymorphicFieldAccess<is_range, do_convert>( self, shadow_frame, method_handle, @@ -969,7 +961,7 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, result); } else { const bool do_convert = true; - return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + return DoInvokePolymorphicFieldAccess<is_range, do_convert>( self, shadow_frame, method_handle, @@ -999,10 +991,10 @@ static inline bool DoInvokePolymorphicNonExact(Thread* self, } } -template <bool is_range, bool do_assignability_check> +template <bool is_range> bool DoInvokePolymorphicExact(Thread* self, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, @@ -1018,13 +1010,13 @@ bool DoInvokePolymorphicExact(Thread* self, ThrowWrongMethodTypeException(nominal_type.Ptr(), callsite_type.Get()); return false; } - return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return DoInvokePolymorphicNonExact<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); } ObjPtr<mirror::MethodType> handle_type(method_handle->GetMethodType()); @@ -1036,7 +1028,7 @@ bool DoInvokePolymorphicExact(Thread* self, const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind(); if (IsFieldAccess(handle_kind)) { const bool do_convert = false; - return DoInvokePolymorphicFieldAccess<is_range, do_convert, do_assignability_check>( + return DoInvokePolymorphicFieldAccess<is_range, do_convert>( self, shadow_frame, method_handle, @@ -1057,51 +1049,49 @@ bool DoInvokePolymorphicExact(Thread* self, } // namespace -template <bool is_range, bool do_assignability_check> +template <bool is_range> bool DoInvokePolymorphic(Thread* self, ArtMethod* invoke_method, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) { if (IsMethodHandleInvokeExact(invoke_method)) { - return DoInvokePolymorphicExact<is_range, do_assignability_check>(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return DoInvokePolymorphicExact<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); } else { - return DoInvokePolymorphicNonExact<is_range, do_assignability_check>(self, - shadow_frame, - method_handle, - callsite_type, - args, - first_arg, - result); + return DoInvokePolymorphicNonExact<is_range>(self, + shadow_frame, + method_handle, + callsite_type, + args, + first_arg, + result); } } -#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range, _do_assignability_check) \ -template REQUIRES_SHARED(Locks::mutator_lock_) \ -bool DoInvokePolymorphic<_is_range, _do_assignability_check>( \ - Thread* self, \ - ArtMethod* invoke_method, \ - ShadowFrame& shadow_frame, \ - Handle<mirror::MethodHandleImpl> method_handle, \ - Handle<mirror::MethodType> callsite_type, \ - const uint32_t (&args)[Instruction::kMaxVarArgRegs], \ - uint32_t first_arg, \ - JValue* result) - -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, true); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true, false); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, true); -EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false, false); +#define EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(_is_range) \ + template REQUIRES_SHARED(Locks::mutator_lock_) \ + bool DoInvokePolymorphic<_is_range>( \ + Thread* self, \ + ArtMethod* invoke_method, \ + ShadowFrame& shadow_frame, \ + Handle<mirror::MethodHandle> method_handle, \ + Handle<mirror::MethodType> callsite_type, \ + const uint32_t (&args)[Instruction::kMaxVarArgRegs], \ + uint32_t first_arg, \ + JValue* result) + +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(true); +EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL(false); #undef EXPLICIT_DO_INVOKE_POLYMORPHIC_TEMPLATE_DECL } // namespace art diff --git a/runtime/method_handles.h b/runtime/method_handles.h index 734d7c7bf4..5bea0ab5cd 100644 --- a/runtime/method_handles.h +++ b/runtime/method_handles.h @@ -27,7 +27,7 @@ namespace art { namespace mirror { - class MethodHandleImpl; + class MethodHandle; class MethodType; } // mirror @@ -202,11 +202,11 @@ class ShadowFrameSetter { size_t arg_index_; }; -template <bool is_range, bool do_assignability_check> +template <bool is_range> bool DoInvokePolymorphic(Thread* self, ArtMethod* invoke_method, ShadowFrame& shadow_frame, - Handle<mirror::MethodHandleImpl> method_handle, + Handle<mirror::MethodHandle> method_handle, Handle<mirror::MethodType> callsite_type, const uint32_t (&args)[Instruction::kMaxVarArgRegs], uint32_t first_arg, diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc index cc548b9cc8..f283ec3e9d 100644 --- a/runtime/mirror/array.cc +++ b/runtime/mirror/array.cc @@ -52,7 +52,7 @@ static Array* RecursiveCreateMultiArray(Thread* self, Array::Alloc<true>(self, array_class.Get(), array_length, array_class->GetComponentSizeShift(), Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - if (UNLIKELY(new_array.Get() == nullptr)) { + if (UNLIKELY(new_array == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; } @@ -98,14 +98,14 @@ Array* Array::CreateMultiArray(Thread* self, Handle<Class> element_class, StackHandleScope<1> hs(self); MutableHandle<mirror::Class> array_class( hs.NewHandle(class_linker->FindArrayClass(self, &element_class_ptr))); - if (UNLIKELY(array_class.Get() == nullptr)) { + if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; } for (int32_t i = 1; i < dimensions->GetLength(); ++i) { ObjPtr<mirror::Class> array_class_ptr = array_class.Get(); array_class.Assign(class_linker->FindArrayClass(self, &array_class_ptr)); - if (UNLIKELY(array_class.Get() == nullptr)) { + if (UNLIKELY(array_class == nullptr)) { CHECK(self->IsExceptionPending()); return nullptr; } diff --git a/runtime/mirror/call_site.cc b/runtime/mirror/call_site.cc new file mode 100644 index 0000000000..eb613df4c6 --- /dev/null +++ b/runtime/mirror/call_site.cc @@ -0,0 +1,52 @@ +/* + * 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 "call_site.h" + +#include "class-inl.h" +#include "gc_root-inl.h" + +namespace art { +namespace mirror { + +GcRoot<mirror::Class> CallSite::static_class_; + +mirror::CallSite* CallSite::Create(Thread* const self, Handle<MethodHandle> target) { + StackHandleScope<1> hs(self); + Handle<mirror::CallSite> cs( + hs.NewHandle(ObjPtr<CallSite>::DownCast(StaticClass()->AllocObject(self)))); + CHECK(!Runtime::Current()->IsActiveTransaction()); + cs->SetFieldObject<false>(TargetOffset(), target.Get()); + return cs.Get(); +} + +void CallSite::SetClass(Class* klass) { + CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; + CHECK(klass != nullptr); + static_class_ = GcRoot<Class>(klass); +} + +void CallSite::ResetClass() { + CHECK(!static_class_.IsNull()); + static_class_ = GcRoot<Class>(nullptr); +} + +void CallSite::VisitRoots(RootVisitor* visitor) { + static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/call_site.h b/runtime/mirror/call_site.h new file mode 100644 index 0000000000..db244a5442 --- /dev/null +++ b/runtime/mirror/call_site.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_CALL_SITE_H_ +#define ART_RUNTIME_MIRROR_CALL_SITE_H_ + +#include "mirror/method_handle_impl.h" +#include "utils.h" + +namespace art { + +struct CallSiteOffsets; + +namespace mirror { + +// C++ mirror of java.lang.invoke.CallSite +class MANAGED CallSite : public Object { + public: + static mirror::CallSite* Create(Thread* const self, + Handle<MethodHandle> method_handle) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { + return static_class_.Read(); + } + + MethodHandle* GetTarget() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldObject<MethodHandle>(TargetOffset()); + } + + static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); + static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); + static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); + + private: + static inline MemberOffset TargetOffset() { + return MemberOffset(OFFSETOF_MEMBER(CallSite, target_)); + } + + HeapReference<mirror::MethodHandle> target_; + + static GcRoot<mirror::Class> static_class_; // java.lang.invoke.CallSite.class + + friend struct art::CallSiteOffsets; // for verifying offset information + DISALLOW_IMPLICIT_CONSTRUCTORS(CallSite); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_CALL_SITE_H_ diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 1b8f3f83e7..9a9a5d8398 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -81,7 +81,7 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) { self->ClearException(); // Allocate the ClassExt Handle<ClassExt> new_ext(hs.NewHandle(ClassExt::Alloc(self))); - if (new_ext.Get() == nullptr) { + if (new_ext == nullptr) { // OOM allocating the classExt. // TODO Should we restore the suppressed exception? self->AssertPendingOOMException(); @@ -103,7 +103,7 @@ ClassExt* Class::EnsureExtDataPresent(Thread* self) { DCHECK(!set || h_this->GetExtData() == new_ext.Get()); CHECK(!ret.IsNull()); // Restore the exception if there was one. - if (throwable.Get() != nullptr) { + if (throwable != nullptr) { self->SetException(throwable.Get()); } return ret.Ptr(); @@ -269,10 +269,10 @@ void Class::DumpClass(std::ostream& os, int flags) { os << "----- " << (IsInterface() ? "interface" : "class") << " " << "'" << GetDescriptor(&temp) << "' cl=" << GetClassLoader() << " -----\n", os << " objectSize=" << SizeOf() << " " - << "(" << (h_super.Get() != nullptr ? h_super->SizeOf() : -1) << " from super)\n", + << "(" << (h_super != nullptr ? h_super->SizeOf() : -1) << " from super)\n", os << StringPrintf(" access=0x%04x.%04x\n", GetAccessFlags() >> 16, GetAccessFlags() & kAccJavaFlagsMask); - if (h_super.Get() != nullptr) { + if (h_super != nullptr) { os << " super='" << h_super->PrettyClass() << "' (cl=" << h_super->GetClassLoader() << ")\n"; } @@ -297,7 +297,7 @@ void Class::DumpClass(std::ostream& os, int flags) { } else { // After this point, this may have moved due to GetDirectInterface. os << " vtable (" << h_this->NumVirtualMethods() << " entries, " - << (h_super.Get() != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n"; + << (h_super != nullptr ? h_super->NumVirtualMethods() : 0) << " in super):\n"; for (size_t i = 0; i < NumVirtualMethods(); ++i) { os << StringPrintf(" %2zd: %s\n", i, ArtMethod::PrettyMethod( h_this->GetVirtualMethodDuringLinking(i, image_pointer_size)).c_str()); @@ -971,7 +971,7 @@ ObjPtr<Class> Class::ResolveDirectInterface(Thread* self, Handle<Class> klass, u } ObjPtr<Class> Class::GetCommonSuperClass(Handle<Class> klass) { - DCHECK(klass.Get() != nullptr); + DCHECK(klass != nullptr); DCHECK(!klass->IsInterface()); DCHECK(!IsInterface()); ObjPtr<Class> common_super_class = this; @@ -1165,7 +1165,7 @@ ObjPtr<Method> Class::GetDeclaredMethodInternal( constexpr uint32_t kSkipModifiers = kAccMiranda | kAccSynthetic; StackHandleScope<3> hs(self); auto h_method_name = hs.NewHandle(name); - if (UNLIKELY(h_method_name.Get() == nullptr)) { + if (UNLIKELY(h_method_name == nullptr)) { ThrowNullPointerException("name == null"); return nullptr; } diff --git a/runtime/mirror/class_ext.cc b/runtime/mirror/class_ext.cc index efd949e031..7270079a8f 100644 --- a/runtime/mirror/class_ext.cc +++ b/runtime/mirror/class_ext.cc @@ -58,8 +58,8 @@ bool ClassExt::ExtendObsoleteArrays(Thread* self, uint32_t increase) { Handle<ObjectArray<DexCache>> old_dex_caches(hs.NewHandle(h_this->GetObsoleteDexCaches())); ClassLinker* cl = Runtime::Current()->GetClassLinker(); size_t new_len; - if (old_methods.Get() == nullptr) { - CHECK(old_dex_caches.Get() == nullptr); + if (old_methods == nullptr) { + CHECK(old_dex_caches == nullptr); new_len = increase; } else { CHECK_EQ(old_methods->GetLength(), old_dex_caches->GetLength()); diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index a59bb7b880..973c8ed07d 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -26,6 +26,7 @@ #include "base/logging.h" #include "gc_root.h" #include "mirror/class.h" +#include "mirror/call_site.h" #include "mirror/method_type.h" #include "runtime.h" #include "obj_ptr.h" @@ -106,6 +107,35 @@ inline void DexCache::SetResolvedMethodType(uint32_t proto_idx, MethodType* reso Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); } +inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); + GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx]; + Atomic<GcRoot<mirror::CallSite>>& ref = + reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target); + return ref.LoadSequentiallyConsistent().Read(); +} + +inline CallSite* DexCache::SetResolvedCallSite(uint32_t call_site_idx, CallSite* call_site) { + DCHECK(Runtime::Current()->IsMethodHandlesEnabled()); + DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds()); + + GcRoot<mirror::CallSite> null_call_site(nullptr); + GcRoot<mirror::CallSite> candidate(call_site); + GcRoot<mirror::CallSite>& target = GetResolvedCallSites()[call_site_idx]; + + // The first assignment for a given call site wins. + Atomic<GcRoot<mirror::CallSite>>& ref = + reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target); + if (ref.CompareExchangeStrongSequentiallyConsistent(null_call_site, candidate)) { + // TODO: Fine-grained marking, so that we don't need to go through all arrays in full. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(this); + return call_site; + } else { + return target.Read(); + } +} + inline ArtField* DexCache::GetResolvedField(uint32_t field_idx, PointerSize ptr_size) { DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), ptr_size); DCHECK_LT(field_idx, NumResolvedFields()); // NOTE: Unchecked, i.e. not throwing AIOOB. @@ -208,6 +238,11 @@ inline void DexCache::VisitReferences(ObjPtr<Class> klass, const Visitor& visito VisitDexCachePairs<mirror::MethodType, kReadBarrierOption, Visitor>( GetResolvedMethodTypes(), NumResolvedMethodTypes(), visitor); + + GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites(); + for (size_t i = 0, num_call_sites = NumResolvedCallSites(); i != num_call_sites; ++i) { + visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier()); + } } } @@ -246,6 +281,17 @@ inline void DexCache::FixupResolvedMethodTypes(mirror::MethodTypeDexCacheType* d } } +template <ReadBarrierOption kReadBarrierOption, typename Visitor> +inline void DexCache::FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, + const Visitor& visitor) { + GcRoot<mirror::CallSite>* src = GetResolvedCallSites(); + for (size_t i = 0, count = NumResolvedCallSites(); i < count; ++i) { + mirror::CallSite* source = src[i].Read<kReadBarrierOption>(); + mirror::CallSite* new_source = visitor(source); + dest[i] = GcRoot<mirror::CallSite>(new_source); + } +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index 741cf3bb47..0f6acab7e1 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -90,6 +90,10 @@ void DexCache::InitializeDexCache(Thread* self, raw_arrays + layout.MethodTypesOffset()); } + GcRoot<mirror::CallSite>* call_sites = (dex_file->NumCallSiteIds() == 0) + ? nullptr + : reinterpret_cast<GcRoot<mirror::CallSite>*>(raw_arrays + layout.CallSitesOffset()); + DCHECK_ALIGNED(raw_arrays, alignof(mirror::StringDexCacheType)) << "Expected raw_arrays to align to StringDexCacheType."; DCHECK_ALIGNED(layout.StringsOffset(), alignof(mirror::StringDexCacheType)) << @@ -117,6 +121,9 @@ void DexCache::InitializeDexCache(Thread* self, CHECK_EQ(method_types[i].load(std::memory_order_relaxed).index, 0u); CHECK(method_types[i].load(std::memory_order_relaxed).object.IsNull()); } + for (size_t i = 0; i < dex_file->NumCallSiteIds(); ++i) { + CHECK(call_sites[i].IsNull()); + } } if (strings != nullptr) { mirror::StringDexCachePair::Initialize(strings); @@ -136,6 +143,8 @@ void DexCache::InitializeDexCache(Thread* self, dex_file->NumFieldIds(), method_types, num_method_types, + call_sites, + dex_file->NumCallSiteIds(), image_pointer_size); } @@ -151,6 +160,8 @@ void DexCache::Init(const DexFile* dex_file, uint32_t num_resolved_fields, MethodTypeDexCacheType* resolved_method_types, uint32_t num_resolved_method_types, + GcRoot<CallSite>* resolved_call_sites, + uint32_t num_resolved_call_sites, PointerSize pointer_size) { CHECK(dex_file != nullptr); CHECK(location != nullptr); @@ -159,6 +170,7 @@ void DexCache::Init(const DexFile* dex_file, CHECK_EQ(num_resolved_methods != 0u, resolved_methods != nullptr); CHECK_EQ(num_resolved_fields != 0u, resolved_fields != nullptr); CHECK_EQ(num_resolved_method_types != 0u, resolved_method_types != nullptr); + CHECK_EQ(num_resolved_call_sites != 0u, resolved_call_sites != nullptr); SetDexFile(dex_file); SetLocation(location); @@ -167,11 +179,13 @@ void DexCache::Init(const DexFile* dex_file, SetResolvedMethods(resolved_methods); SetResolvedFields(resolved_fields); SetResolvedMethodTypes(resolved_method_types); + SetResolvedCallSites(resolved_call_sites); SetField32<false>(NumStringsOffset(), num_strings); SetField32<false>(NumResolvedTypesOffset(), num_resolved_types); SetField32<false>(NumResolvedMethodsOffset(), num_resolved_methods); SetField32<false>(NumResolvedFieldsOffset(), num_resolved_fields); SetField32<false>(NumResolvedMethodTypesOffset(), num_resolved_method_types); + SetField32<false>(NumResolvedCallSitesOffset(), num_resolved_call_sites); Runtime* const runtime = Runtime::Current(); if (runtime->HasResolutionMethod()) { diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 6f88cc5df4..10bb5aa01c 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -36,6 +36,7 @@ class Thread; namespace mirror { +class CallSite; class MethodType; class String; @@ -163,6 +164,10 @@ class MANAGED DexCache FINAL : public Object { void FixupResolvedMethodTypes(MethodTypeDexCacheType* dest, const Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_); + template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor> + void FixupResolvedCallSites(GcRoot<mirror::CallSite>* dest, const Visitor& visitor) + REQUIRES_SHARED(Locks::mutator_lock_); + String* GetLocation() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<String>(OFFSET_OF_OBJECT_MEMBER(DexCache, location_)); } @@ -191,6 +196,10 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_); } + static MemberOffset ResolvedCallSitesOffset() { + return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_); + } + static MemberOffset NumStringsOffset() { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_); } @@ -211,6 +220,10 @@ class MANAGED DexCache FINAL : public Object { return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_); } + static MemberOffset NumResolvedCallSitesOffset() { + return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_); + } + mirror::String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_); @@ -244,7 +257,18 @@ class MANAGED DexCache FINAL : public Object { MethodType* GetResolvedMethodType(uint32_t proto_idx) REQUIRES_SHARED(Locks::mutator_lock_); - void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) REQUIRES_SHARED(Locks::mutator_lock_); + void SetResolvedMethodType(uint32_t proto_idx, MethodType* resolved) + REQUIRES_SHARED(Locks::mutator_lock_); + + CallSite* GetResolvedCallSite(uint32_t call_site_idx) REQUIRES_SHARED(Locks::mutator_lock_); + + // Attempts to bind |call_site_idx| to the call site |resolved|. The + // caller must use the return value in place of |resolved|. This is + // because multiple threads can invoke the bootstrap method each + // producing a call site, but the method handle invocation on the + // call site must be on a common agreed value. + CallSite* SetResolvedCallSite(uint32_t call_site_idx, CallSite* resolved) WARN_UNUSED + REQUIRES_SHARED(Locks::mutator_lock_); StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr64<StringDexCacheType*>(StringsOffset()); @@ -295,6 +319,18 @@ class MANAGED DexCache FINAL : public Object { SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types); } + GcRoot<CallSite>* GetResolvedCallSites() + ALWAYS_INLINE + REQUIRES_SHARED(Locks::mutator_lock_) { + return GetFieldPtr<GcRoot<CallSite>*>(ResolvedCallSitesOffset()); + } + + void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites) + ALWAYS_INLINE + REQUIRES_SHARED(Locks::mutator_lock_) { + SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites); + } + size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) { return GetField32(NumStringsOffset()); } @@ -315,6 +351,10 @@ class MANAGED DexCache FINAL : public Object { return GetField32(NumResolvedMethodTypesOffset()); } + size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) { + return GetField32(NumResolvedCallSitesOffset()); + } + const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_)); } @@ -346,8 +386,10 @@ class MANAGED DexCache FINAL : public Object { uint32_t num_resolved_methods, ArtField** resolved_fields, uint32_t num_resolved_fields, - MethodTypeDexCacheType* resolved_methodtypes, - uint32_t num_resolved_methodtypes, + MethodTypeDexCacheType* resolved_method_types, + uint32_t num_resolved_method_types, + GcRoot<CallSite>* resolved_call_sites, + uint32_t num_resolved_call_sites, PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); @@ -362,6 +404,8 @@ class MANAGED DexCache FINAL : public Object { HeapReference<Object> dex_; HeapReference<String> location_; uint64_t dex_file_; // const DexFile* + uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_ + // elements. uint64_t resolved_fields_; // ArtField*, array with num_resolved_fields_ elements. uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with // num_resolved_method_types_ elements. @@ -370,6 +414,7 @@ class MANAGED DexCache FINAL : public Object { uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_ // elements. + uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array. uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array. uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array. uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array. diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc index 8f978e122c..5a2ab7151c 100644 --- a/runtime/mirror/dex_cache_test.cc +++ b/runtime/mirror/dex_cache_test.cc @@ -47,7 +47,7 @@ TEST_F(DexCacheTest, Open) { soa.Self(), *java_lang_dex_file_, Runtime::Current()->GetLinearAlloc()))); - ASSERT_TRUE(dex_cache.Get() != nullptr); + ASSERT_TRUE(dex_cache != nullptr); EXPECT_TRUE(dex_cache->StaticStringSize() == dex_cache->NumStrings() || java_lang_dex_file_->NumStringIds() == dex_cache->NumStrings()); @@ -95,10 +95,10 @@ TEST_F(DexCacheTest, TestResolvedFieldAccess) { soa.Decode<mirror::ClassLoader>(jclass_loader))); Handle<mirror::Class> klass1 = hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage1/Package1;", class_loader)); - ASSERT_TRUE(klass1.Get() != nullptr); + ASSERT_TRUE(klass1 != nullptr); Handle<mirror::Class> klass2 = hs.NewHandle(class_linker_->FindClass(soa.Self(), "Lpackage2/Package2;", class_loader)); - ASSERT_TRUE(klass2.Get() != nullptr); + ASSERT_TRUE(klass2 != nullptr); EXPECT_EQ(klass1->GetDexCache(), klass2->GetDexCache()); EXPECT_NE(klass1->NumStaticFields(), 0u); diff --git a/runtime/mirror/emulated_stack_frame.cc b/runtime/mirror/emulated_stack_frame.cc index 978cc32320..be0eac05c9 100644 --- a/runtime/mirror/emulated_stack_frame.cc +++ b/runtime/mirror/emulated_stack_frame.cc @@ -173,13 +173,13 @@ mirror::EmulatedStackFrame* EmulatedStackFrame::CreateFromShadowFrameAndArgs( Handle<mirror::ObjectArray<mirror::Object>> references(hs.NewHandle( mirror::ObjectArray<mirror::Object>::Alloc(self, array_class, refs_size))); - if (references.Get() == nullptr) { + if (references == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } Handle<ByteArray> stack_frame(hs.NewHandle(ByteArray::Alloc(self, frame_size))); - if (stack_frame.Get() == nullptr) { + if (stack_frame == nullptr) { DCHECK(self->IsExceptionPending()); return nullptr; } diff --git a/runtime/mirror/field-inl.h b/runtime/mirror/field-inl.h index c03f20a991..2496989337 100644 --- a/runtime/mirror/field-inl.h +++ b/runtime/mirror/field-inl.h @@ -33,7 +33,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b // Try to resolve type before allocating since this is a thread suspension point. Handle<mirror::Class> type = hs.NewHandle(field->GetType<true>()); - if (type.Get() == nullptr) { + if (type == nullptr) { if (force_resolve) { if (kIsDebugBuild) { self->AssertPendingException(); @@ -49,7 +49,7 @@ inline mirror::Field* Field::CreateFromArtField(Thread* self, ArtField* field, b } } auto ret = hs.NewHandle(ObjPtr<Field>::DownCast(StaticClass()->AllocObject(self))); - if (UNLIKELY(ret.Get() == nullptr)) { + if (UNLIKELY(ret == nullptr)) { self->AssertPendingOOMException(); return nullptr; } diff --git a/runtime/mirror/method_handle_impl.cc b/runtime/mirror/method_handle_impl.cc index 4f1c448b56..fa4d25a031 100644 --- a/runtime/mirror/method_handle_impl.cc +++ b/runtime/mirror/method_handle_impl.cc @@ -28,6 +28,18 @@ mirror::Class* MethodHandle::StaticClass() { return klass; } +void MethodHandle::Initialize(uintptr_t art_field_or_method, + Kind kind, + Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_) { + CHECK(!Runtime::Current()->IsActiveTransaction()); + SetFieldObject<false>(CachedSpreadInvokerOffset(), nullptr); + SetFieldObject<false>(NominalTypeOffset(), nullptr); + SetFieldObject<false>(MethodTypeOffset(), method_type.Get()); + SetField32<false>(HandleKindOffset(), static_cast<uint32_t>(kind)); + SetField64<false>(ArtFieldOrMethodOffset(), art_field_or_method); +} + GcRoot<mirror::Class> MethodHandleImpl::static_class_; void MethodHandleImpl::SetClass(Class* klass) { @@ -45,5 +57,17 @@ void MethodHandleImpl::VisitRoots(RootVisitor* visitor) { static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); } +mirror::MethodHandleImpl* MethodHandleImpl::Create(Thread* const self, + uintptr_t art_field_or_method, + MethodHandle::Kind kind, + Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { + StackHandleScope<1> hs(self); + Handle<mirror::MethodHandleImpl> mh( + hs.NewHandle(ObjPtr<MethodHandleImpl>::DownCast(StaticClass()->AllocObject(self)))); + mh->Initialize(art_field_or_method, kind, method_type); + return mh.Get(); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h index 53d267b52c..9938af8abf 100644 --- a/runtime/mirror/method_handle_impl.h +++ b/runtime/mirror/method_handle_impl.h @@ -17,10 +17,11 @@ #ifndef ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_ #define ART_RUNTIME_MIRROR_METHOD_HANDLE_IMPL_H_ +#include "art_field.h" +#include "art_method.h" #include "class.h" #include "gc_root.h" #include "object-inl.h" -#include "method_handles.h" #include "method_type.h" namespace art { @@ -82,10 +83,19 @@ class MANAGED MethodHandle : public Object { GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_))); } + ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_) { + Kind kind = GetHandleKind(); + return (kind <= kLastValidKind) ? + GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass(); + } + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_); + protected: + void Initialize(uintptr_t art_field_or_method, Kind kind, Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_); + private: - // NOTE: cached_spread_invoker_ isn't used by the runtime. HeapReference<mirror::MethodHandle> cached_spread_invoker_; HeapReference<mirror::MethodType> nominal_type_; HeapReference<mirror::MethodType> method_type_; @@ -93,6 +103,9 @@ class MANAGED MethodHandle : public Object { uint64_t art_field_or_method_; private: + static MemberOffset CachedSpreadInvokerOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandle, cached_spread_invoker_)); + } static MemberOffset NominalTypeOffset() { return MemberOffset(OFFSETOF_MEMBER(MethodHandle, nominal_type_)); } @@ -113,6 +126,12 @@ class MANAGED MethodHandle : public Object { // C++ mirror of java.lang.invoke.MethodHandleImpl class MANAGED MethodHandleImpl : public MethodHandle { public: + static mirror::MethodHandleImpl* Create(Thread* const self, + uintptr_t art_field_or_method, + MethodHandle::Kind kind, + Handle<MethodType> method_type) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { return static_class_.Read(); } diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc new file mode 100644 index 0000000000..c758e54dd4 --- /dev/null +++ b/runtime/mirror/method_handles_lookup.cc @@ -0,0 +1,58 @@ +/* + * 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 "method_handles_lookup.h" + +#include "class.h" +#include "gc_root-inl.h" +#include "object-inl.h" +#include "handle_scope.h" +#include "modifiers.h" + +namespace art { +namespace mirror { + +GcRoot<mirror::Class> MethodHandlesLookup::static_class_; + +void MethodHandlesLookup::SetClass(Class* klass) { + CHECK(static_class_.IsNull()) << static_class_.Read() << " " << klass; + CHECK(klass != nullptr); + static_class_ = GcRoot<Class>(klass); +} + +void MethodHandlesLookup::ResetClass() { + CHECK(!static_class_.IsNull()); + static_class_ = GcRoot<Class>(nullptr); +} + +void MethodHandlesLookup::VisitRoots(RootVisitor* visitor) { + static_class_.VisitRootIfNonNull(visitor, RootInfo(kRootStickyClass)); +} + +MethodHandlesLookup* MethodHandlesLookup::Create(Thread* const self, Handle<Class> lookup_class) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_) { + static constexpr uint32_t kAllModes = kAccPublic | kAccPrivate | kAccProtected | kAccStatic; + + StackHandleScope<1> hs(self); + Handle<MethodHandlesLookup> mhl( + hs.NewHandle(ObjPtr<MethodHandlesLookup>::DownCast(StaticClass()->AllocObject(self)))); + mhl->SetFieldObject<false>(LookupClassOffset(), lookup_class.Get()); + mhl->SetField32<false>(AllowedModesOffset(), kAllModes); + return mhl.Get(); +} + +} // namespace mirror +} // namespace art diff --git a/runtime/mirror/method_handles_lookup.h b/runtime/mirror/method_handles_lookup.h new file mode 100644 index 0000000000..63eb428f94 --- /dev/null +++ b/runtime/mirror/method_handles_lookup.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ +#define ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ + +#include "obj_ptr.h" +#include "gc_root.h" +#include "object.h" +#include "handle.h" +#include "utils.h" + +namespace art { + +struct MethodHandlesLookupOffsets; +class RootVisitor; + +namespace mirror { + +// C++ mirror of java.lang.invoke.MethodHandles.Lookup +class MANAGED MethodHandlesLookup : public Object { + public: + static mirror::MethodHandlesLookup* Create(Thread* const self, + Handle<Class> lookup_class) + REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_); + + static mirror::Class* StaticClass() REQUIRES_SHARED(Locks::mutator_lock_) { + return static_class_.Read(); + } + + static void SetClass(Class* klass) REQUIRES_SHARED(Locks::mutator_lock_); + static void ResetClass() REQUIRES_SHARED(Locks::mutator_lock_); + static void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_); + + private: + static MemberOffset AllowedModesOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, allowed_modes_)); + } + + static MemberOffset LookupClassOffset() { + return MemberOffset(OFFSETOF_MEMBER(MethodHandlesLookup, lookup_class_)); + } + + HeapReference<mirror::Class> lookup_class_; + + int32_t allowed_modes_; + + static GcRoot<mirror::Class> static_class_; // java.lang.invoke.MethodHandles.Lookup.class + + friend struct art::MethodHandlesLookupOffsets; // for verifying offset information + DISALLOW_IMPLICIT_CONSTRUCTORS(MethodHandlesLookup); +}; + +} // namespace mirror +} // namespace art + +#endif // ART_RUNTIME_MIRROR_METHOD_HANDLES_LOOKUP_H_ diff --git a/runtime/mirror/method_type.cc b/runtime/mirror/method_type.cc index 5d77a16e7d..4b8dfacec6 100644 --- a/runtime/mirror/method_type.cc +++ b/runtime/mirror/method_type.cc @@ -44,6 +44,22 @@ mirror::MethodType* MethodType::Create(Thread* const self, return mt.Get(); } +size_t MethodType::NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_) { + mirror::ObjectArray<Class>* const p_types = GetPTypes(); + const int32_t p_types_length = p_types->GetLength(); + + // Initialize |num_vregs| with number of parameters and only increment it for + // types requiring a second vreg. + size_t num_vregs = static_cast<size_t>(p_types_length); + for (int32_t i = 0; i < p_types_length; ++i) { + mirror::Class* klass = p_types->GetWithoutChecks(i); + if (klass->IsPrimitiveLong() || klass->IsPrimitiveDouble()) { + ++num_vregs; + } + } + return num_vregs; +} + bool MethodType::IsExactMatch(mirror::MethodType* target) REQUIRES_SHARED(Locks::mutator_lock_) { mirror::ObjectArray<Class>* const p_types = GetPTypes(); const int32_t params_length = p_types->GetLength(); diff --git a/runtime/mirror/method_type.h b/runtime/mirror/method_type.h index 9a98143144..374bbe5df3 100644 --- a/runtime/mirror/method_type.h +++ b/runtime/mirror/method_type.h @@ -44,6 +44,10 @@ class MANAGED MethodType : public Object { return GetFieldObject<ObjectArray<Class>>(OFFSET_OF_OBJECT_MEMBER(MethodType, p_types_)); } + // Number of virtual registers required to hold the parameters for + // this method type. + size_t NumberOfVRegs() REQUIRES_SHARED(Locks::mutator_lock_); + Class* GetRType() REQUIRES_SHARED(Locks::mutator_lock_) { return GetFieldObject<Class>(OFFSET_OF_OBJECT_MEMBER(MethodType, r_type_)); } diff --git a/runtime/mirror/method_type_test.cc b/runtime/mirror/method_type_test.cc index 637bafd75e..41231ef617 100644 --- a/runtime/mirror/method_type_test.cc +++ b/runtime/mirror/method_type_test.cc @@ -51,7 +51,7 @@ static mirror::MethodType* CreateMethodType(const std::string& return_type, Handle<mirror::Class> return_clazz = hs.NewHandle(class_linker->FindClass( soa.Self(), FullyQualifiedType(return_type).c_str(), boot_class_loader)); - CHECK(return_clazz.Get() != nullptr); + CHECK(return_clazz != nullptr); ObjPtr<mirror::Class> class_type = mirror::Class::GetJavaLangClass(); mirror::Class* class_array_type = class_linker->FindArrayClass(self, &class_type); diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc index 6a4ec9dca7..e761e4db7a 100644 --- a/runtime/mirror/object_test.cc +++ b/runtime/mirror/object_test.cc @@ -530,8 +530,8 @@ TEST_F(ObjectTest, InstanceOf) { Handle<Object> x(hs.NewHandle(X->AllocObject(soa.Self()))); Handle<Object> y(hs.NewHandle(Y->AllocObject(soa.Self()))); - ASSERT_TRUE(x.Get() != nullptr); - ASSERT_TRUE(y.Get() != nullptr); + ASSERT_TRUE(x != nullptr); + ASSERT_TRUE(y != nullptr); EXPECT_TRUE(x->InstanceOf(X)); EXPECT_FALSE(x->InstanceOf(Y)); @@ -650,7 +650,7 @@ TEST_F(ObjectTest, FindInstanceField) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); - ASSERT_TRUE(s.Get() != nullptr); + ASSERT_TRUE(s != nullptr); Class* c = s->GetClass(); ASSERT_TRUE(c != nullptr); @@ -684,9 +684,9 @@ TEST_F(ObjectTest, FindStaticField) { ScopedObjectAccess soa(Thread::Current()); StackHandleScope<4> hs(soa.Self()); Handle<String> s(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "ABC"))); - ASSERT_TRUE(s.Get() != nullptr); + ASSERT_TRUE(s != nullptr); Handle<Class> c(hs.NewHandle(s->GetClass())); - ASSERT_TRUE(c.Get() != nullptr); + ASSERT_TRUE(c != nullptr); // Wrong type. EXPECT_TRUE(c->FindDeclaredStaticField("CASE_INSENSITIVE_ORDER", "I") == nullptr); diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 9b8445dc9e..c2407d7772 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -308,7 +308,7 @@ inline int32_t String::GetHashCode() { } template<typename MemoryType> -bool String::AllASCII(const MemoryType* const chars, const int length) { +inline bool String::AllASCII(const MemoryType* chars, const int length) { static_assert(std::is_unsigned<MemoryType>::value, "Expecting unsigned MemoryType"); for (int i = 0; i < length; ++i) { // Valid ASCII characters are in range 1..0x7f. Zero is not considered ASCII @@ -320,6 +320,13 @@ bool String::AllASCII(const MemoryType* const chars, const int length) { return true; } +inline bool String::DexFileStringAllASCII(const char* chars, const int length) { + // For strings from the dex file we just need to check that + // the terminating character is at the right position. + DCHECK_EQ(AllASCII(reinterpret_cast<const uint8_t*>(chars), length), chars[length] == 0); + return chars[length] == 0; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 409c6c2896..38f6dd4b6f 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -184,7 +184,9 @@ class MANAGED String FINAL : public Object { bool IsValueNull() REQUIRES_SHARED(Locks::mutator_lock_); template<typename MemoryType> - static bool AllASCII(const MemoryType* const chars, const int length); + static bool AllASCII(const MemoryType* chars, const int length); + + static bool DexFileStringAllASCII(const char* chars, const int length); ALWAYS_INLINE static bool IsCompressed(int32_t count) { return GetCompressionFlagFromCount(count) == StringCompressionFlag::kCompressed; diff --git a/runtime/monitor.cc b/runtime/monitor.cc index a32003e81b..f3cb0df80e 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -1380,7 +1380,7 @@ void MonitorList::Add(Monitor* m) { while (!kUseReadBarrier && UNLIKELY(!allow_new_monitors_)) { // Check and run the empty checkpoint before blocking so the empty checkpoint will work in the // presence of threads blocking for weak ref access. - self->CheckEmptyCheckpoint(); + self->CheckEmptyCheckpointFromWeakRefAccess(&monitor_list_lock_); monitor_add_condition_.WaitHoldingLocks(self); } list_.push_front(m); diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc index 4fbfe4781c..27ce149342 100644 --- a/runtime/monitor_test.cc +++ b/runtime/monitor_test.cc @@ -77,7 +77,7 @@ static void FillHeap(Thread* self, ClassLinker* class_linker, while (length > 10) { MutableHandle<mirror::Object> h((*hsp)->NewHandle<mirror::Object>( mirror::ObjectArray<mirror::Object>::Alloc(self, ca.Get(), length / 4))); - if (self->IsExceptionPending() || h.Get() == nullptr) { + if (self->IsExceptionPending() || h == nullptr) { self->ClearException(); // Try a smaller length @@ -95,7 +95,7 @@ static void FillHeap(Thread* self, ClassLinker* class_linker, // Allocate simple objects till it fails. while (!self->IsExceptionPending()) { MutableHandle<mirror::Object> h = (*hsp)->NewHandle<mirror::Object>(c->AllocObject(self)); - if (!self->IsExceptionPending() && h.Get() != nullptr) { + if (!self->IsExceptionPending() && h != nullptr) { handles->push_back(h); } } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 24308d9e81..6bfccdc8fb 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -350,7 +350,7 @@ static void PreloadDexCachesResolveField(Handle<mirror::DexCache> dex_cache, uin Thread* const self = Thread::Current(); StackHandleScope<1> hs(self); Handle<mirror::Class> klass(hs.NewHandle(dex_cache->GetResolvedType(field_id.class_idx_))); - if (klass.Get() == nullptr) { + if (klass == nullptr) { return; } if (is_static) { @@ -512,7 +512,7 @@ static void VMRuntime_preloadDexCaches(JNIEnv* env, jobject) { CHECK(dex_file != nullptr); StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->RegisterDexFile(*dex_file, nullptr))); - CHECK(dex_cache.Get() != nullptr); // Boot class path dex caches are never unloaded. + CHECK(dex_cache != nullptr); // Boot class path dex caches are never unloaded. if (kPreloadDexCachesStrings) { for (size_t j = 0; j < dex_cache->NumStrings(); j++) { PreloadDexCachesResolveString(dex_cache, dex::StringIndex(j), strings); diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 5438a6ddb4..256787b2a1 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -81,7 +81,7 @@ static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> c( hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader))); - if (c.Get() == nullptr) { + if (c == nullptr) { ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred()); env->ExceptionClear(); jthrowable cnfe = reinterpret_cast<jthrowable>( @@ -137,7 +137,7 @@ static mirror::ObjectArray<mirror::Field>* GetDeclaredFields( size_t array_idx = 0; auto object_array = hs.NewHandle(mirror::ObjectArray<mirror::Field>::Alloc( self, mirror::Field::ArrayClass(), array_size)); - if (object_array.Get() == nullptr) { + if (object_array == nullptr) { return nullptr; } for (ArtField& field : ifields) { @@ -267,7 +267,7 @@ static mirror::Field* GetPublicFieldRecursive( Handle<mirror::String> h_name(hs.NewHandle(name)); // We search the current class, its direct interfaces then its superclass. - while (h_clazz.Get() != nullptr) { + while (h_clazz != nullptr) { mirror::Field* result = GetDeclaredField(self, h_clazz.Get(), h_name.Get()); if ((result != nullptr) && (result->GetAccessFlags() & kAccPublic)) { return result; @@ -319,14 +319,14 @@ static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring nam ScopedFastNativeObjectAccess soa(env); StackHandleScope<3> hs(soa.Self()); Handle<mirror::String> h_string = hs.NewHandle(soa.Decode<mirror::String>(name)); - if (h_string.Get() == nullptr) { + if (h_string == nullptr) { ThrowNullPointerException("name == null"); return nullptr; } Handle<mirror::Class> h_klass = hs.NewHandle(DecodeClass(soa, javaThis)); Handle<mirror::Field> result = hs.NewHandle(GetDeclaredField(soa.Self(), h_klass.Get(), h_string.Get())); - if (result.Get() == nullptr) { + if (result == nullptr) { std::string name_str = h_string->ToModifiedUtf8(); if (name_str == "value" && h_klass->IsStringClass()) { // We log the error for this specific case, as the user might just swallow the exception. @@ -377,7 +377,7 @@ static jobjectArray Class_getDeclaredConstructorsInternal( } auto h_constructors = hs.NewHandle(mirror::ObjectArray<mirror::Constructor>::Alloc( soa.Self(), mirror::Constructor::ArrayClass(), constructor_count)); - if (UNLIKELY(h_constructors.Get() == nullptr)) { + if (UNLIKELY(h_constructors == nullptr)) { soa.Self()->AssertPendingException(); return nullptr; } @@ -428,7 +428,7 @@ static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaT } auto ret = hs.NewHandle(mirror::ObjectArray<mirror::Method>::Alloc( soa.Self(), mirror::Method::ArrayClass(), num_methods)); - if (ret.Get() == nullptr) { + if (ret == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; } @@ -645,7 +645,7 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { // Verify that we can access the class. if (!klass->IsPublic()) { caller.Assign(GetCallingClass(soa.Self(), 1)); - if (caller.Get() != nullptr && !caller->CanAccess(klass.Get())) { + if (caller != nullptr && !caller->CanAccess(klass.Get())) { soa.Self()->ThrowNewExceptionF( "Ljava/lang/IllegalAccessException;", "%s is not accessible from %s", klass->PrettyClass().c_str(), caller->PrettyClass().c_str()); @@ -673,17 +673,17 @@ static jobject Class_newInstance(JNIEnv* env, jobject javaThis) { } } auto receiver = hs.NewHandle(klass->AllocObject(soa.Self())); - if (UNLIKELY(receiver.Get() == nullptr)) { + if (UNLIKELY(receiver == nullptr)) { soa.Self()->AssertPendingOOMException(); return nullptr; } // Verify that we can access the constructor. auto* declaring_class = constructor->GetDeclaringClass(); if (!constructor->IsPublic()) { - if (caller.Get() == nullptr) { + if (caller == nullptr) { caller.Assign(GetCallingClass(soa.Self(), 1)); } - if (UNLIKELY(caller.Get() != nullptr && !VerifyAccess(receiver.Get(), + if (UNLIKELY(caller != nullptr && !VerifyAccess(receiver.Get(), declaring_class, constructor->GetAccessFlags(), caller.Get()))) { diff --git a/runtime/native/java_lang_invoke_MethodHandleImpl.cc b/runtime/native/java_lang_invoke_MethodHandleImpl.cc index 72a37f875d..9113841909 100644 --- a/runtime/native/java_lang_invoke_MethodHandleImpl.cc +++ b/runtime/native/java_lang_invoke_MethodHandleImpl.cc @@ -57,7 +57,7 @@ static jobject MethodHandleImpl_getMemberInternal(JNIEnv* env, jobject thiz) { } } - if (UNLIKELY(h_object.Get() == nullptr)) { + if (UNLIKELY(h_object == nullptr)) { soa.Self()->AssertPendingOOMException(); return nullptr; } diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc index ee59c4a9e2..2a3942829f 100644 --- a/runtime/native/java_lang_reflect_Executable.cc +++ b/runtime/native/java_lang_reflect_Executable.cc @@ -103,7 +103,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) { } // Validate the MethodParameters system annotation data. - if (UNLIKELY(names.Get() == nullptr || access_flags.Get() == nullptr)) { + if (UNLIKELY(names == nullptr || access_flags == nullptr)) { ThrowIllegalArgumentException( StringPrintf("Missing parameter metadata for names or access flags for %s", art_method->PrettyMethod().c_str()).c_str()); @@ -132,7 +132,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) { mirror::ObjectArray<mirror::Object>::Alloc(self, parameter_array_class.Get(), names_count)); - if (UNLIKELY(parameter_array.Get() == nullptr)) { + if (UNLIKELY(parameter_array == nullptr)) { self->AssertPendingException(); return nullptr; } @@ -154,7 +154,7 @@ static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) { // Allocate / initialize the Parameter to add to parameter_array. parameter.Assign(parameter_class->AllocObject(self)); - if (UNLIKELY(parameter.Get() == nullptr)) { + if (UNLIKELY(parameter == nullptr)) { self->AssertPendingOOMException(); return nullptr; } diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc index 2590452678..e51b6d2a89 100644 --- a/runtime/native/libcore_util_CharsetUtils.cc +++ b/runtime/native/libcore_util_CharsetUtils.cc @@ -155,7 +155,7 @@ static jbyteArray charsToBytes(JNIEnv* env, jstring java_string, jint offset, ji ScopedObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string))); - if (string.Get() == nullptr) { + if (string == nullptr) { return nullptr; } @@ -192,7 +192,7 @@ static jbyteArray CharsetUtils_toUtf8Bytes(JNIEnv* env, jclass, jstring java_str ScopedObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle<mirror::String> string(hs.NewHandle(soa.Decode<mirror::String>(java_string))); - if (string.Get() == nullptr) { + if (string == nullptr) { return nullptr; } diff --git a/runtime/oat.h b/runtime/oat.h index e7e8328796..0f6657b7ed 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[] = { '1', '1', '0', '\0' }; // Clean up code info change. + static constexpr uint8_t kOatVersion[] = { '1', '1', '2', '\0' }; // Manual bump (Revert^3 hash-based DexCache types; stack maps). static constexpr const char* kImageLocationKey = "image-location"; static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 31eb1ccdc8..493da271d1 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -273,6 +273,36 @@ inline static bool ReadOatDexFileData(const OatFile& oat_file, return true; } +static bool FindDexFileMapItem(const uint8_t* dex_begin, + const uint8_t* dex_end, + DexFile::MapItemType map_item_type, + const DexFile::MapItem** result_item) { + *result_item = nullptr; + + const DexFile::Header* header = + BoundsCheckedCast<const DexFile::Header*>(dex_begin, dex_begin, dex_end); + if (nullptr == header) return false; + + if (!DexFile::IsMagicValid(header->magic_)) return true; // Not a dex file, not an error. + + const DexFile::MapList* map_list = + BoundsCheckedCast<const DexFile::MapList*>(dex_begin + header->map_off_, dex_begin, dex_end); + if (nullptr == map_list) return false; + + const DexFile::MapItem* map_item = map_list->list_; + size_t count = map_list->size_; + while (count--) { + if (map_item->type_ == static_cast<uint16_t>(map_item_type)) { + *result_item = map_item; + break; + } + map_item = BoundsCheckedCast<const DexFile::MapItem*>(map_item + 1, dex_begin, dex_end); + if (nullptr == map_item) return false; + } + + return true; +} + bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { if (!GetOatHeader().IsValid()) { std::string cause = GetOatHeader().GetValidationErrorMessage(); @@ -501,7 +531,19 @@ bool OatFileBase::Setup(const char* abs_dex_location, std::string* error_msg) { uint8_t* current_dex_cache_arrays = nullptr; if (dex_cache_arrays != nullptr) { - DexCacheArraysLayout layout(pointer_size, *header); + // All DexCache types except for CallSite have their instance counts in the + // DexFile header. For CallSites, we need to read the info from the MapList. + const DexFile::MapItem* call_sites_item = nullptr; + if (!FindDexFileMapItem(DexBegin(), + DexEnd(), + DexFile::MapItemType::kDexTypeCallSiteIdItem, + &call_sites_item)) { + *error_msg = StringPrintf("In oat file '%s' could not read data from truncated DexFile map", + GetLocation().c_str()); + return false; + } + size_t num_call_sites = call_sites_item == nullptr ? 0 : call_sites_item->size_; + DexCacheArraysLayout layout(pointer_size, *header, num_call_sites); if (layout.Size() != 0u) { if (static_cast<size_t>(dex_cache_arrays_end - dex_cache_arrays) < layout.Size()) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with " @@ -1468,77 +1510,6 @@ std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*> return out.str(); } -bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) { - if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { - // No dependencies. - return true; - } - - // Assumption: this is not performance-critical. So it's OK to do this with a std::string and - // Split() instead of manual parsing of the combined char*. - std::vector<std::string> split; - Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); - if (split.size() % 2 != 0) { - // Expected pairs of location and checksum. - *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies); - return false; - } - - for (auto it = split.begin(), end = split.end(); it != end; it += 2) { - std::string& location = *it; - std::string& checksum = *(it + 1); - int64_t converted = strtoll(checksum.c_str(), nullptr, 10); - if (converted == 0) { - // Conversion error. - *msg = StringPrintf("Conversion error for %s", checksum.c_str()); - return false; - } - - uint32_t dex_checksum; - std::string error_msg; - if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(), - &dex_checksum, - &error_msg)) { - if (converted != dex_checksum) { - *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u", - location.c_str(), converted, dex_checksum); - return false; - } - } else { - // Problem retrieving checksum. - // TODO: odex files? - *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(), - error_msg.c_str()); - return false; - } - } - - return true; -} - -bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies, - std::vector<std::string>* locations) { - DCHECK(locations != nullptr); - if (dex_dependencies == nullptr || dex_dependencies[0] == 0) { - return true; - } - - // Assumption: this is not performance-critical. So it's OK to do this with a std::string and - // Split() instead of manual parsing of the combined char*. - std::vector<std::string> split; - Split(dex_dependencies, kDexClassPathEncodingSeparator, &split); - if (split.size() % 2 != 0) { - // Expected pairs of location and checksum. - return false; - } - - for (auto it = split.begin(), end = split.end(); it != end; it += 2) { - locations->push_back(*it); - } - - return true; -} - OatFile::OatClass OatFile::FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found) { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 111755e7a1..d24283afee 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -290,15 +290,6 @@ class OatFile { // Create a dependency list (dex locations and checksums) for the given dex files. static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files); - // Check the given dependency list against their dex files - thus the name "Static," this does - // not check the class-loader environment, only whether there have been file updates. - static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg); - - // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic - // locations of multidex files. - static bool GetDexLocationsFromDependencies(const char* dex_dependencies, - std::vector<std::string>* locations); - // Finds the associated oat class for a dex_file and descriptor. Returns an invalid OatClass on // error and sets found to false. static OatClass FindOatClass(const DexFile& dex_file, uint16_t class_def_idx, bool* found); diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc index 77cdd28d3a..5ae2fc51b7 100644 --- a/runtime/oat_file_assistant.cc +++ b/runtime/oat_file_assistant.cc @@ -38,6 +38,8 @@ namespace art { +using android::base::StringPrintf; + std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) { switch (status) { case OatFileAssistant::kOatCannotOpen: @@ -264,7 +266,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( const OatFile& oat_file, const char* dex_location) { std::vector<std::unique_ptr<const DexFile>> dex_files; - // Load the primary dex file. + // Load the main dex file. std::string error_msg; const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile( dex_location, nullptr, &error_msg); @@ -280,12 +282,12 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( } dex_files.push_back(std::move(dex_file)); - // Load secondary multidex files + // Load the rest of the multidex entries for (size_t i = 1; ; i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location); - oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr); + std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location); + oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr); if (oat_dex_file == nullptr) { - // There are no more secondary dex files to load. + // There are no more multidex entries to load. break; } @@ -300,10 +302,10 @@ std::vector<std::unique_ptr<const DexFile>> OatFileAssistant::LoadDexFiles( } bool OatFileAssistant::HasOriginalDexFiles() { - // Ensure GetRequiredDexChecksum has been run so that + // Ensure GetRequiredDexChecksums has been run so that // has_original_dex_files_ is initialized. We don't care about the result of - // GetRequiredDexChecksum. - GetRequiredDexChecksum(); + // GetRequiredDexChecksums. + GetRequiredDexChecksums(); return has_original_dex_files_; } @@ -316,88 +318,66 @@ OatFileAssistant::OatStatus OatFileAssistant::OatFileStatus() { } bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) { - if (file.GetHeader().GetNumberOfDexFiles() <= 0) { - VLOG(oat) << "Vdex does not contain any dex files"; + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + if (required_dex_checksums == nullptr) { + LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; + return true; + } + + uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles(); + if (required_dex_checksums->size() != number_of_dex_files) { + *error_msg = StringPrintf("expected %zu dex files but found %u", + required_dex_checksums->size(), + number_of_dex_files); return false; } - // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not - // just the primary. Because otherwise we may fail to see a secondary - // checksum failure in the case when the original (multidex) files are - // stripped but we have a newer odex file. - const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); - if (dex_checksum_pointer != nullptr) { - uint32_t actual_checksum = file.GetLocationChecksum(0); - if (*dex_checksum_pointer != actual_checksum) { - VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_ - << ". Expected: " << *dex_checksum_pointer - << ", Actual: " << actual_checksum; + for (uint32_t i = 0; i < number_of_dex_files; i++) { + uint32_t expected_checksum = (*required_dex_checksums)[i]; + uint32_t actual_checksum = file.GetLocationChecksum(i); + if (expected_checksum != actual_checksum) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + *error_msg = StringPrintf("Dex checksum does not match for dex: %s." + "Expected: %u, actual: %u", + dex.c_str(), + expected_checksum, + actual_checksum); return false; } } - // Verify the dex checksums for any secondary multidex files - for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); - uint32_t expected_secondary_checksum = 0; - if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, - error_msg)) { - uint32_t actual_secondary_checksum = file.GetLocationChecksum(i); - if (expected_secondary_checksum != actual_secondary_checksum) { - VLOG(oat) << "Dex checksum does not match for secondary dex: " - << secondary_dex_location - << ". Expected: " << expected_secondary_checksum - << ", Actual: " << actual_secondary_checksum; - return false; - } - } else { - // If we can't get the checksum for the secondary location, we assume - // the dex checksum is up to date for this and all other secondary dex - // files. - break; - } - } return true; } bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) { - // Note: GetOatDexFile will return null if the dex checksum doesn't match - // what we provide, which verifies the primary dex checksum for us. - const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum(); - const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile( - dex_location_.c_str(), dex_checksum_pointer, error_msg); - if (oat_dex_file == nullptr) { + const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(); + if (required_dex_checksums == nullptr) { + LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date."; + return true; + } + + uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount(); + if (required_dex_checksums->size() != number_of_dex_files) { + *error_msg = StringPrintf("expected %zu dex files but found %u", + required_dex_checksums->size(), + number_of_dex_files); return false; } - // Verify the dex checksums for any secondary multidex files - for (size_t i = 1; ; i++) { - std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); - const OatFile::OatDexFile* secondary_oat_dex_file - = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr); - if (secondary_oat_dex_file == nullptr) { - // There are no more secondary dex files to check. - break; + for (uint32_t i = 0; i < number_of_dex_files; i++) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + uint32_t expected_checksum = (*required_dex_checksums)[i]; + const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr); + if (oat_dex_file == nullptr) { + *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str()); + return false; } - - uint32_t expected_secondary_checksum = 0; - if (DexFile::GetChecksum(secondary_dex_location.c_str(), - &expected_secondary_checksum, error_msg)) { - uint32_t actual_secondary_checksum - = secondary_oat_dex_file->GetDexFileLocationChecksum(); - if (expected_secondary_checksum != actual_secondary_checksum) { - VLOG(oat) << "Dex checksum does not match for secondary dex: " - << secondary_dex_location - << ". Expected: " << expected_secondary_checksum - << ", Actual: " << actual_secondary_checksum; - return false; - } - } else { - // If we can't get the checksum for the secondary location, we assume - // the dex checksum is up to date for this and all other secondary dex - // files. - break; + uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum(); + if (expected_checksum != actual_checksum) { + VLOG(oat) << "Dex checksum does not match for dex: " << dex + << ". Expected: " << expected_checksum + << ", Actual: " << actual_checksum; + return false; } } return true; @@ -710,13 +690,16 @@ std::string OatFileAssistant::ImageLocation() { return image_spaces[0]->GetImageLocation(); } -const uint32_t* OatFileAssistant::GetRequiredDexChecksum() { - if (!required_dex_checksum_attempted_) { - required_dex_checksum_attempted_ = true; - required_dex_checksum_found_ = false; +const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() { + if (!required_dex_checksums_attempted_) { + required_dex_checksums_attempted_ = true; + required_dex_checksums_found_ = false; + cached_required_dex_checksums_.clear(); std::string error_msg; - if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) { - required_dex_checksum_found_ = true; + if (DexFile::GetMultiDexChecksums(dex_location_.c_str(), + &cached_required_dex_checksums_, + &error_msg)) { + required_dex_checksums_found_ = true; has_original_dex_files_ = true; } else { // This can happen if the original dex file has been stripped from the @@ -724,19 +707,23 @@ const uint32_t* OatFileAssistant::GetRequiredDexChecksum() { VLOG(oat) << "OatFileAssistant: " << error_msg; has_original_dex_files_ = false; - // Get the checksum from the odex if we can. + // Get the checksums from the odex if we can. const OatFile* odex_file = odex_.GetFile(); if (odex_file != nullptr) { - const OatFile::OatDexFile* odex_dex_file - = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr); - if (odex_dex_file != nullptr) { - cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum(); - required_dex_checksum_found_ = true; + required_dex_checksums_found_ = true; + for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) { + std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str()); + const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr); + if (odex_dex_file == nullptr) { + required_dex_checksums_found_ = false; + break; + } + cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum()); } } } } - return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr; + return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr; } const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() { diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h index 6d47ad2228..3ede29f5e0 100644 --- a/runtime/oat_file_assistant.h +++ b/runtime/oat_file_assistant.h @@ -400,13 +400,13 @@ class OatFileAssistant { // the oat file assistant. static std::string ImageLocation(); - // Gets the dex checksum required for an up-to-date oat file. - // Returns dex_checksum if a required checksum was located. Returns - // null if the required checksum was not found. - // The caller shouldn't clean up or free the returned pointer. - // This sets the has_original_dex_files_ field to true if a checksum was - // found for the dex_location_ dex file. - const uint32_t* GetRequiredDexChecksum(); + // Gets the dex checksums required for an up-to-date oat file. + // Returns cached_required_dex_checksums if the required checksums were + // located. Returns null if the required checksums were not found. The + // caller shouldn't clean up or free the returned pointer. This sets the + // has_original_dex_files_ field to true if the checksums were found for the + // dex_location_ dex file. + const std::vector<uint32_t>* GetRequiredDexChecksums(); // Returns the loaded image info. // Loads the image info if needed. Returns null if the image info failed @@ -430,11 +430,11 @@ class OatFileAssistant { // Whether we will attempt to load oat files executable. bool load_executable_ = false; - // Cached value of the required dex checksum. - // This should be accessed only by the GetRequiredDexChecksum() method. - uint32_t cached_required_dex_checksum_; - bool required_dex_checksum_attempted_ = false; - bool required_dex_checksum_found_; + // Cached value of the required dex checksums. + // This should be accessed only by the GetRequiredDexChecksums() method. + std::vector<uint32_t> cached_required_dex_checksums_; + bool required_dex_checksums_attempted_ = false; + bool required_dex_checksums_found_; bool has_original_dex_files_; OatFileInfo odex_; diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc index f777340cfd..9b35489330 100644 --- a/runtime/oat_file_assistant_test.cc +++ b/runtime/oat_file_assistant_test.cc @@ -237,16 +237,16 @@ TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) { EXPECT_EQ(2u, dex_files.size()); } -// Case: We have a MultiDEX file where the secondary dex file is out of date. +// Case: We have a MultiDEX file where the non-main multdex entry is out of date. // Expect: The status is kDex2OatNeeded. -TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { - std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar"; +TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) { + std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar"; // Compile code for GetMultiDexSrc1. Copy(GetMultiDexSrc1(), dex_location); GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); - // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum + // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum // is out of date. Copy(GetMultiDexSrc2(), dex_location); @@ -256,6 +256,37 @@ TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) { EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles()); } +// Case: We have a stripped MultiDEX file where the non-main multidex entry is +// out of date with respect to the odex file. +TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) { + std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar"; + std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex"; + + // Compile the oat from GetMultiDexSrc1. + Copy(GetMultiDexSrc1(), dex_location); + GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed); + + // Compile the odex from GetMultiDexSrc2, which has a different non-main + // dex checksum. + Copy(GetMultiDexSrc2(), dex_location); + GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly); + + // Strip the dex file. + Copy(GetStrippedDexSrc1(), dex_location); + + OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false); + + // Because the dex file is stripped, the odex file is considered the source + // of truth for the dex checksums. The oat file should be considered + // unusable. + std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile(); + ASSERT_TRUE(best_file.get() != nullptr); + EXPECT_EQ(best_file->GetLocation(), odex_location); + EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles()); + EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus()); + EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus()); +} + // Case: We have a MultiDEX file and up-to-date OAT file for it with relative // encoded dex locations. // Expect: The oat file status is kNoDexOptNeeded. @@ -336,16 +367,16 @@ TEST_F(OatFileAssistantTest, VdexDexOutOfDate) { oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed)); } -// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is -// out of date and there is no corresponding ODEX file. -TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) { +// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry +// is out of date and there is no corresponding ODEX file. +TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) { // This test case is only meaningful if vdex is enabled. if (!kIsVdexEnabled) { return; } - std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar"; - std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat"; + std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar"; + std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat"; Copy(GetMultiDexSrc1(), dex_location); GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed); @@ -1028,7 +1059,7 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) { ClassLinker* linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> dexfile( hs.NewHandle(linker->FindSystemClass(soa.Self(), "Ldalvik/system/DexFile;"))); - ASSERT_FALSE(dexfile.Get() == nullptr); + ASSERT_FALSE(dexfile == nullptr); linker->EnsureInitialized(soa.Self(), dexfile, true, true); for (std::pair<OatFileAssistant::DexOptNeeded, const char*> field : mapping) { diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc index a46b47075c..70796148a4 100644 --- a/runtime/oat_file_manager.cc +++ b/runtime/oat_file_manager.cc @@ -342,7 +342,7 @@ static void GetDexFilesFromDexElementsArray( ScopedObjectAccessAlreadyRunnable& soa, Handle<mirror::ObjectArray<mirror::Object>> dex_elements, std::priority_queue<DexFileAndClassPair>* queue) REQUIRES_SHARED(Locks::mutator_lock_) { - if (dex_elements.Get() == nullptr) { + if (dex_elements == nullptr) { // Nothing to do. return; } @@ -463,14 +463,14 @@ bool OatFileManager::HasCollisions(const OatFile* oat_file, hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)); Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements)); - if (h_class_loader.Get() != nullptr && + if (h_class_loader != nullptr && GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) { class_loader_ok = true; // In this case, also take into account the dex_elements array, if given. We don't need to // read it otherwise, as we'll compare against all open oat files anyways. GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue); - } else if (h_class_loader.Get() != nullptr) { + } else if (h_class_loader != nullptr) { VLOG(class_linker) << "Something unsupported with " << mirror::Class::PrettyClass(h_class_loader->GetClass()); } @@ -658,7 +658,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat( Handle<mirror::ClassLoader> h_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader))); // Can not load app image without class loader. - if (h_loader.Get() != nullptr) { + if (h_loader != nullptr) { std::string temp_error_msg; // Add image space has a race condition since other threads could be reading from the // spaces array. diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc index b416b9dbad..d5fe1f382a 100644 --- a/runtime/oat_file_test.cc +++ b/runtime/oat_file_test.cc @@ -62,54 +62,4 @@ TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) { "/data/app/foo/base.apk", "o/base.apk")); } -static std::vector<const DexFile*> ToConstDexFiles( - const std::vector<std::unique_ptr<const DexFile>>& in) { - std::vector<const DexFile*> ret; - for (auto& d : in) { - ret.push_back(d.get()); - } - return ret; -} - -TEST_F(OatFileTest, DexFileDependencies) { - std::string error_msg; - - // No dependencies. - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg; - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg; - - // Ill-formed dependencies. - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg)); - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg)); - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg)); - - // Unsatisfiable dependency. - EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg)); - - // Load some dex files to be able to do a real test. - ScopedObjectAccess soa(Thread::Current()); - - std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main"); - std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1); - std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1); - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg)) - << error_msg << " " << encoding1; - std::vector<std::string> split1; - EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1)); - ASSERT_EQ(split1.size(), 1U); - EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation()); - - std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); - EXPECT_GT(dex_files2.size(), 1U); - std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2); - std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2); - EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg)) - << error_msg << " " << encoding2; - std::vector<std::string> split2; - EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2)); - ASSERT_EQ(split2.size(), 2U); - EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation()); - EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation()); -} - } // namespace art diff --git a/runtime/object_lock.cc b/runtime/object_lock.cc index 39ab52fb2d..f6db544276 100644 --- a/runtime/object_lock.cc +++ b/runtime/object_lock.cc @@ -24,7 +24,7 @@ namespace art { template <typename T> ObjectLock<T>::ObjectLock(Thread* self, Handle<T> object) : self_(self), obj_(object) { - CHECK(object.Get() != nullptr); + CHECK(object != nullptr); obj_->MonitorEnter(self_); } @@ -50,7 +50,7 @@ void ObjectLock<T>::NotifyAll() { template <typename T> ObjectTryLock<T>::ObjectTryLock(Thread* self, Handle<T> object) : self_(self), obj_(object) { - CHECK(object.Get() != nullptr); + CHECK(object != nullptr); acquired_ = obj_->MonitorTryEnter(self_) != nullptr; } diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc index c14fd84264..7ca233fb10 100644 --- a/runtime/openjdkjvmti/ti_class.cc +++ b/runtime/openjdkjvmti/ti_class.cc @@ -42,12 +42,17 @@ #include "class_linker.h" #include "common_throws.h" #include "events-inl.h" +#include "gc/heap.h" +#include "gc_root.h" #include "handle.h" #include "jni_env_ext-inl.h" #include "jni_internal.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "mirror/class_ext.h" +#include "mirror/object_reference.h" +#include "mirror/object-inl.h" +#include "mirror/reference.h" #include "runtime.h" #include "runtime_callbacks.h" #include "ScopedLocalRef.h" @@ -261,15 +266,22 @@ struct ClassCallback : public art::ClassLoadCallback { thread_jni.get(), jklass.get()); } - AddTempClass(thread, jklass.get()); + if (klass->IsTemp()) { + AddTempClass(thread, jklass.get()); + } } } - void ClassPrepare(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED, + void ClassPrepare(art::Handle<art::mirror::Class> temp_klass, art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kClassPrepare)) { art::Thread* thread = art::Thread::Current(); + if (temp_klass.Get() != klass.Get()) { + DCHECK(temp_klass->IsTemp()); + DCHECK(temp_klass->IsRetired()); + HandleTempClass(thread, temp_klass, klass); + } ScopedLocalRef<jclass> jklass(thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get())); ScopedLocalRef<jthread> thread_jni( @@ -283,32 +295,209 @@ struct ClassCallback : public art::ClassLoadCallback { } } + // To support parallel class-loading, we need to perform some locking dances here. Namely, + // the fixup stage must not be holding the temp_classes lock when it fixes up the system + // (as that requires suspending all mutators). + void AddTempClass(art::Thread* self, jclass klass) { std::unique_lock<std::mutex> mu(temp_classes_lock); - temp_classes.push_back(reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass))); + jclass global_klass = reinterpret_cast<jclass>(self->GetJniEnv()->NewGlobalRef(klass)); + temp_classes.push_back(global_klass); } - void HandleTempClass(art::Handle<art::mirror::Class> temp_klass, + void HandleTempClass(art::Thread* self, + art::Handle<art::mirror::Class> temp_klass, art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { - std::unique_lock<std::mutex> mu(temp_classes_lock); - if (temp_classes.empty()) { - return; - } + bool requires_fixup = false; + { + std::unique_lock<std::mutex> mu(temp_classes_lock); + if (temp_classes.empty()) { + return; + } - art::Thread* self = art::Thread::Current(); - for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) { - if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) { - temp_classes.erase(it); - FixupTempClass(temp_klass, klass); + for (auto it = temp_classes.begin(); it != temp_classes.end(); ++it) { + if (temp_klass.Get() == art::ObjPtr<art::mirror::Class>::DownCast(self->DecodeJObject(*it))) { + self->GetJniEnv()->DeleteGlobalRef(*it); + temp_classes.erase(it); + requires_fixup = true; + break; + } } } + if (requires_fixup) { + FixupTempClass(self, temp_klass, klass); + } } - void FixupTempClass(art::Handle<art::mirror::Class> temp_klass ATTRIBUTE_UNUSED, - art::Handle<art::mirror::Class> klass ATTRIBUTE_UNUSED) + void FixupTempClass(art::Thread* self, + art::Handle<art::mirror::Class> temp_klass, + art::Handle<art::mirror::Class> klass) REQUIRES_SHARED(art::Locks::mutator_lock_) { - // TODO: Implement. + // Suspend everything. + art::gc::Heap* heap = art::Runtime::Current()->GetHeap(); + if (heap->IsGcConcurrentAndMoving()) { + // Need to take a heap dump while GC isn't running. See the + // comment in Heap::VisitObjects(). + heap->IncrementDisableMovingGC(self); + } + { + art::ScopedThreadSuspension sts(self, art::kWaitingForVisitObjects); + art::ScopedSuspendAll ssa("FixupTempClass"); + + art::mirror::Class* input = temp_klass.Get(); + art::mirror::Class* output = klass.Get(); + + FixupGlobalReferenceTables(input, output); + FixupLocalReferenceTables(self, input, output); + FixupHeap(input, output); + } + if (heap->IsGcConcurrentAndMoving()) { + heap->DecrementDisableMovingGC(self); + } + } + + class RootUpdater : public art::RootVisitor { + public: + RootUpdater(const art::mirror::Class* input, art::mirror::Class* output) + : input_(input), output_(output) {} + + void VisitRoots(art::mirror::Object*** roots, + size_t count, + const art::RootInfo& info ATTRIBUTE_UNUSED) + OVERRIDE { + for (size_t i = 0; i != count; ++i) { + if (*roots[i] == input_) { + *roots[i] = output_; + } + } + } + + void VisitRoots(art::mirror::CompressedReference<art::mirror::Object>** roots, + size_t count, + const art::RootInfo& info ATTRIBUTE_UNUSED) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + for (size_t i = 0; i != count; ++i) { + if (roots[i]->AsMirrorPtr() == input_) { + roots[i]->Assign(output_); + } + } + } + + private: + const art::mirror::Class* input_; + art::mirror::Class* output_; + }; + + void FixupGlobalReferenceTables(art::mirror::Class* input, art::mirror::Class* output) + REQUIRES(art::Locks::mutator_lock_) { + art::JavaVMExt* java_vm = art::Runtime::Current()->GetJavaVM(); + + // Fix up the global table with a root visitor. + RootUpdater global_update(input, output); + java_vm->VisitRoots(&global_update); + + class WeakGlobalUpdate : public art::IsMarkedVisitor { + public: + WeakGlobalUpdate(art::mirror::Class* root_input, art::mirror::Class* root_output) + : input_(root_input), output_(root_output) {} + + art::mirror::Object* IsMarked(art::mirror::Object* obj) OVERRIDE { + if (obj == input_) { + return output_; + } + return obj; + } + + private: + const art::mirror::Class* input_; + art::mirror::Class* output_; + }; + WeakGlobalUpdate weak_global_update(input, output); + java_vm->SweepJniWeakGlobals(&weak_global_update); + } + + void FixupLocalReferenceTables(art::Thread* self, + art::mirror::Class* input, + art::mirror::Class* output) + REQUIRES(art::Locks::mutator_lock_) { + class LocalUpdate { + public: + LocalUpdate(const art::mirror::Class* root_input, art::mirror::Class* root_output) + : input_(root_input), output_(root_output) {} + + static void Callback(art::Thread* t, void* arg) REQUIRES(art::Locks::mutator_lock_) { + LocalUpdate* local = reinterpret_cast<LocalUpdate*>(arg); + + // Fix up the local table with a root visitor. + RootUpdater local_update(local->input_, local->output_); + t->GetJniEnv()->locals.VisitRoots( + &local_update, art::RootInfo(art::kRootJNILocal, t->GetThreadId())); + } + + private: + const art::mirror::Class* input_; + art::mirror::Class* output_; + }; + LocalUpdate local_upd(input, output); + art::MutexLock mu(self, *art::Locks::thread_list_lock_); + art::Runtime::Current()->GetThreadList()->ForEach(LocalUpdate::Callback, &local_upd); + } + + void FixupHeap(art::mirror::Class* input, art::mirror::Class* output) + REQUIRES(art::Locks::mutator_lock_) { + class HeapFixupVisitor { + public: + HeapFixupVisitor(const art::mirror::Class* root_input, art::mirror::Class* root_output) + : input_(root_input), output_(root_output) {} + + void operator()(art::mirror::Object* src, + art::MemberOffset field_offset, + bool is_static ATTRIBUTE_UNUSED) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::mirror::HeapReference<art::mirror::Object>* trg = + src->GetFieldObjectReferenceAddr(field_offset); + if (trg->AsMirrorPtr() == input_) { + DCHECK_NE(field_offset.Uint32Value(), 0u); // This shouldn't be the class field of + // an object. + trg->Assign(output_); + } + } + + void operator()(art::ObjPtr<art::mirror::Class> klass ATTRIBUTE_UNUSED, + art::ObjPtr<art::mirror::Reference> reference) const + REQUIRES_SHARED(art::Locks::mutator_lock_) { + art::mirror::Object* val = reference->GetReferent(); + if (val == input_) { + reference->SetReferent<false>(output_); + } + } + + void VisitRoot(art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED) + const { + LOG(FATAL) << "Unreachable"; + } + + void VisitRootIfNonNull( + art::mirror::CompressedReference<art::mirror::Object>* root ATTRIBUTE_UNUSED) const { + LOG(FATAL) << "Unreachable"; + } + + static void AllObjectsCallback(art::mirror::Object* obj, void* arg) + REQUIRES_SHARED(art::Locks::mutator_lock_) { + HeapFixupVisitor* hfv = reinterpret_cast<HeapFixupVisitor*>(arg); + + // Visit references, not native roots. + obj->VisitReferences<false>(*hfv, *hfv); + } + + private: + const art::mirror::Class* input_; + art::mirror::Class* output_; + }; + HeapFixupVisitor hfv(input, output); + art::Runtime::Current()->GetHeap()->VisitObjectsPaused(HeapFixupVisitor::AllObjectsCallback, + &hfv); } // A set of all the temp classes we have handed out. We have to fix up references to these. diff --git a/runtime/openjdkjvmti/ti_class_loader.cc b/runtime/openjdkjvmti/ti_class_loader.cc index afec0bfac0..d05f579407 100644 --- a/runtime/openjdkjvmti/ti_class_loader.cc +++ b/runtime/openjdkjvmti/ti_class_loader.cc @@ -119,11 +119,11 @@ art::ObjPtr<art::mirror::LongArray> ClassLoaderHelper::AllocateNewDexFileCookie( art::Handle<art::mirror::LongArray> cookie, const art::DexFile* dex_file) { art::StackHandleScope<1> hs(self); - CHECK(cookie.Get() != nullptr); + CHECK(cookie != nullptr); CHECK_GE(cookie->GetLength(), 1); art::Handle<art::mirror::LongArray> new_cookie( hs.NewHandle(art::mirror::LongArray::Alloc(self, cookie->GetLength() + 1))); - if (new_cookie.Get() == nullptr) { + if (new_cookie == nullptr) { self->AssertPendingOOMException(); return nullptr; } @@ -183,13 +183,13 @@ art::ObjPtr<art::mirror::Object> ClassLoaderHelper::FindSourceDexFileObject( // Start navigating the fields of the loader (now known to be a BaseDexClassLoader derivative) art::Handle<art::mirror::Object> path_list( hs.NewHandle(path_list_field->GetObject(loader.Get()))); - CHECK(path_list.Get() != nullptr); + CHECK(path_list != nullptr); CHECK(!self->IsExceptionPending()); art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(hs.NewHandle( dex_path_list_element_field->GetObject(path_list.Get())-> AsObjectArray<art::mirror::Object>())); CHECK(!self->IsExceptionPending()); - CHECK(dex_elements_list.Get() != nullptr); + CHECK(dex_elements_list != nullptr); size_t num_elements = dex_elements_list->GetLength(); // Iterate over the DexPathList$Element to find the right one for (size_t i = 0; i < num_elements; i++) { diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc index f0c0dbcbfc..f01acc11aa 100644 --- a/runtime/openjdkjvmti/ti_redefine.cc +++ b/runtime/openjdkjvmti/ti_redefine.cc @@ -38,12 +38,17 @@ #include "art_jvmti.h" #include "base/array_slice.h" #include "base/logging.h" +#include "debugger.h" #include "dex_file.h" #include "dex_file_types.h" #include "events-inl.h" #include "gc/allocation_listener.h" #include "gc/heap.h" #include "instrumentation.h" +#include "jdwp/jdwp.h" +#include "jdwp/jdwp_constants.h" +#include "jdwp/jdwp_event.h" +#include "jdwp/object_registry.h" #include "jit/jit.h" #include "jit/jit_code_cache.h" #include "jni_env_ext-inl.h" @@ -63,6 +68,66 @@ namespace openjdkjvmti { using android::base::StringPrintf; +// A helper that fills in a classes obsolete_methods_ and obsolete_dex_caches_ classExt fields as +// they are created. This ensures that we can always call any method of an obsolete ArtMethod object +// almost as soon as they are created since the GetObsoleteDexCache method will succeed. +class ObsoleteMap { + public: + art::ArtMethod* FindObsoleteVersion(art::ArtMethod* original) + REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) { + auto method_pair = id_map_.find(original); + if (method_pair != id_map_.end()) { + art::ArtMethod* res = obsolete_methods_->GetElementPtrSize<art::ArtMethod*>( + method_pair->second, art::kRuntimePointerSize); + DCHECK(res != nullptr); + DCHECK_EQ(original, res->GetNonObsoleteMethod()); + return res; + } else { + return nullptr; + } + } + + void RecordObsolete(art::ArtMethod* original, art::ArtMethod* obsolete) + REQUIRES(art::Locks::mutator_lock_, art::Roles::uninterruptible_) { + DCHECK(original != nullptr); + DCHECK(obsolete != nullptr); + int32_t slot = next_free_slot_++; + DCHECK_LT(slot, obsolete_methods_->GetLength()); + DCHECK(nullptr == + obsolete_methods_->GetElementPtrSize<art::ArtMethod*>(slot, art::kRuntimePointerSize)); + DCHECK(nullptr == obsolete_dex_caches_->Get(slot)); + obsolete_methods_->SetElementPtrSize(slot, obsolete, art::kRuntimePointerSize); + obsolete_dex_caches_->Set(slot, original_dex_cache_); + id_map_.insert({original, slot}); + } + + ObsoleteMap(art::ObjPtr<art::mirror::PointerArray> obsolete_methods, + art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches, + art::ObjPtr<art::mirror::DexCache> original_dex_cache) + : next_free_slot_(0), + obsolete_methods_(obsolete_methods), + obsolete_dex_caches_(obsolete_dex_caches), + original_dex_cache_(original_dex_cache) { + // Figure out where the first unused slot in the obsolete_methods_ array is. + while (obsolete_methods_->GetElementPtrSize<art::ArtMethod*>( + next_free_slot_, art::kRuntimePointerSize) != nullptr) { + DCHECK(obsolete_dex_caches_->Get(next_free_slot_) != nullptr); + next_free_slot_++; + } + // Sanity check that the same slot in obsolete_dex_caches_ is free. + DCHECK(obsolete_dex_caches_->Get(next_free_slot_) == nullptr); + } + + private: + int32_t next_free_slot_; + std::unordered_map<art::ArtMethod*, int32_t> id_map_; + // Pointers to the fields in mirror::ClassExt. These can be held as ObjPtr since this is only used + // when we have an exclusive mutator_lock_ (i.e. all threads are suspended). + art::ObjPtr<art::mirror::PointerArray> obsolete_methods_; + art::ObjPtr<art::mirror::ObjectArray<art::mirror::DexCache>> obsolete_dex_caches_; + art::ObjPtr<art::mirror::DexCache> original_dex_cache_; +}; + // This visitor walks thread stacks and allocates and sets up the obsolete methods. It also does // some basic sanity checks that the obsolete method is sane. class ObsoleteMethodStackVisitor : public art::StackVisitor { @@ -71,7 +136,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::Thread* thread, art::LinearAlloc* allocator, const std::unordered_set<art::ArtMethod*>& obsoleted_methods, - /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps) + ObsoleteMap* obsolete_maps) : StackVisitor(thread, /*context*/nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames), @@ -89,7 +154,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { art::Thread* thread, art::LinearAlloc* allocator, const std::unordered_set<art::ArtMethod*>& obsoleted_methods, - /*out*/std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps) + ObsoleteMap* obsolete_maps) REQUIRES(art::Locks::mutator_lock_) { ObsoleteMethodStackVisitor visitor(thread, allocator, @@ -99,6 +164,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { } bool VisitFrame() OVERRIDE REQUIRES(art::Locks::mutator_lock_) { + art::ScopedAssertNoThreadSuspension snts("Fixing up the stack for obsolete methods."); art::ArtMethod* old_method = GetMethod(); if (obsoleted_methods_.find(old_method) != obsoleted_methods_.end()) { // We cannot ensure that the right dex file is used in inlined frames so we don't support @@ -108,9 +174,8 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // TODO We should really support redefining intrinsics. // We don't support intrinsics so check for them here. DCHECK(!old_method->IsIntrinsic()); - art::ArtMethod* new_obsolete_method = nullptr; - auto obsolete_method_pair = obsolete_maps_->find(old_method); - if (obsolete_method_pair == obsolete_maps_->end()) { + art::ArtMethod* new_obsolete_method = obsolete_maps_->FindObsoleteVersion(old_method); + if (new_obsolete_method == nullptr) { // Create a new Obsolete Method and put it in the list. art::Runtime* runtime = art::Runtime::Current(); art::ClassLinker* cl = runtime->GetClassLinker(); @@ -124,7 +189,7 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { DCHECK_EQ(new_obsolete_method->GetDeclaringClass(), old_method->GetDeclaringClass()); new_obsolete_method->SetIsObsolete(); new_obsolete_method->SetDontCompile(); - obsolete_maps_->insert({old_method, new_obsolete_method}); + obsolete_maps_->RecordObsolete(old_method, new_obsolete_method); // Update JIT Data structures to point to the new method. art::jit::Jit* jit = art::Runtime::Current()->GetJit(); if (jit != nullptr) { @@ -132,8 +197,6 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // structures to keep track of the new obsolete method. jit->GetCodeCache()->MoveObsoleteMethod(old_method, new_obsolete_method); } - } else { - new_obsolete_method = obsolete_method_pair->second; } DCHECK(new_obsolete_method != nullptr); SetMethod(new_obsolete_method); @@ -147,9 +210,9 @@ class ObsoleteMethodStackVisitor : public art::StackVisitor { // The set of all methods which could be obsoleted. const std::unordered_set<art::ArtMethod*>& obsoleted_methods_; // A map from the original to the newly allocated obsolete method for frames on this thread. The - // values in this map must be added to the obsolete_methods_ (and obsolete_dex_caches_) fields of - // the redefined classes ClassExt by the caller. - std::unordered_map<art::ArtMethod*, art::ArtMethod*>* obsolete_maps_; + // values in this map are added to the obsolete_methods_ (and obsolete_dex_caches_) fields of + // the redefined classes ClassExt as it is filled. + ObsoleteMap* obsolete_maps_; }; jvmtiError Redefiner::IsModifiableClass(jvmtiEnv* env ATTRIBUTE_UNUSED, @@ -426,11 +489,12 @@ art::mirror::ByteArray* Redefiner::ClassRedefinition::AllocateOrGetOriginalDexFi } struct CallbackCtx { + ObsoleteMap* obsolete_map; art::LinearAlloc* allocator; - std::unordered_map<art::ArtMethod*, art::ArtMethod*> obsolete_map; std::unordered_set<art::ArtMethod*> obsolete_methods; - explicit CallbackCtx(art::LinearAlloc* alloc) : allocator(alloc) {} + explicit CallbackCtx(ObsoleteMap* map, art::LinearAlloc* alloc) + : obsolete_map(map), allocator(alloc) {} }; void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SAFETY_ANALYSIS { @@ -438,7 +502,7 @@ void DoAllocateObsoleteMethodsCallback(art::Thread* t, void* vdata) NO_THREAD_SA ObsoleteMethodStackVisitor::UpdateObsoleteFrames(t, data->allocator, data->obsolete_methods, - &data->obsolete_map); + data->obsolete_map); } // This creates any ArtMethod* structures needed for obsolete methods and ensures that the stack is @@ -449,9 +513,18 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C art::mirror::ClassExt* ext = art_klass->GetExtData(); CHECK(ext->GetObsoleteMethods() != nullptr); art::ClassLinker* linker = driver_->runtime_->GetClassLinker(); - CallbackCtx ctx(linker->GetAllocatorForClassLoader(art_klass->GetClassLoader())); + // This holds pointers to the obsolete methods map fields which are updated as needed. + ObsoleteMap map(ext->GetObsoleteMethods(), ext->GetObsoleteDexCaches(), art_klass->GetDexCache()); + CallbackCtx ctx(&map, linker->GetAllocatorForClassLoader(art_klass->GetClassLoader())); // Add all the declared methods to the map for (auto& m : art_klass->GetDeclaredMethods(art::kRuntimePointerSize)) { + // It is possible to simply filter out some methods where they cannot really become obsolete, + // such as native methods and keep their original (possibly optimized) implementations. We don't + // do this, however, since we would need to mark these functions (still in the classes + // declared_methods array) as obsolete so we will find the correct dex file to get meta-data + // from (for example about stack-frame size). Furthermore we would be unable to get some useful + // error checking from the interpreter which ensure we don't try to start executing obsolete + // methods. ctx.obsolete_methods.insert(&m); // TODO Allow this or check in IsModifiableClass. DCHECK(!m.IsIntrinsic()); @@ -461,36 +534,6 @@ void Redefiner::ClassRedefinition::FindAndAllocateObsoleteMethods(art::mirror::C art::ThreadList* list = art::Runtime::Current()->GetThreadList(); list->ForEach(DoAllocateObsoleteMethodsCallback, static_cast<void*>(&ctx)); } - FillObsoleteMethodMap(art_klass, ctx.obsolete_map); -} - -// Fills the obsolete method map in the art_klass's extData. This is so obsolete methods are able to -// figure out their DexCaches. -void Redefiner::ClassRedefinition::FillObsoleteMethodMap( - art::mirror::Class* art_klass, - const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) { - int32_t index = 0; - art::mirror::ClassExt* ext_data = art_klass->GetExtData(); - art::mirror::PointerArray* obsolete_methods = ext_data->GetObsoleteMethods(); - art::mirror::ObjectArray<art::mirror::DexCache>* obsolete_dex_caches = - ext_data->GetObsoleteDexCaches(); - int32_t num_method_slots = obsolete_methods->GetLength(); - // Find the first empty index. - for (; index < num_method_slots; index++) { - if (obsolete_methods->GetElementPtrSize<art::ArtMethod*>( - index, art::kRuntimePointerSize) == nullptr) { - break; - } - } - // Make sure we have enough space. - CHECK_GT(num_method_slots, static_cast<int32_t>(obsoletes.size() + index)); - CHECK(obsolete_dex_caches->Get(index) == nullptr); - // Fill in the map. - for (auto& obs : obsoletes) { - obsolete_methods->SetElementPtrSize(index, obs.second, art::kRuntimePointerSize); - obsolete_dex_caches->Set(index, art_klass->GetDexCache()); - index++; - } } // Try and get the declared method. First try to get a virtual method then a direct method if that's @@ -934,7 +977,7 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle( ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader))); holder->SetJavaDexFile(klass_index, dex_file_obj.Get()); - if (dex_file_obj.Get() == nullptr) { + if (dex_file_obj == nullptr) { // TODO Better error msg. RecordFailure(ERR(INTERNAL), "Unable to find dex file!"); return false; @@ -966,6 +1009,23 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations( return true; } +void Redefiner::ClassRedefinition::UnregisterBreakpoints() { + DCHECK(art::Dbg::IsDebuggerActive()); + art::JDWP::JdwpState* state = art::Dbg::GetJdwpState(); + if (state != nullptr) { + state->UnregisterLocationEventsOnClass(GetMirrorClass()); + } +} + +void Redefiner::UnregisterAllBreakpoints() { + if (LIKELY(!art::Dbg::IsDebuggerActive())) { + return; + } + for (Redefiner::ClassRedefinition& redef : redefinitions_) { + redef.UnregisterBreakpoints(); + } +} + bool Redefiner::CheckAllRedefinitionAreValid() { for (Redefiner::ClassRedefinition& redef : redefinitions_) { if (!redef.CheckRedefinitionIsValid()) { @@ -1044,6 +1104,7 @@ jvmtiError Redefiner::Run() { // cleaned up by the GC eventually. return result_; } + // At this point we can no longer fail without corrupting the runtime state. int32_t counter = 0; for (Redefiner::ClassRedefinition& redef : redefinitions_) { if (holder.GetSourceClassLoader(counter) == nullptr) { @@ -1051,6 +1112,7 @@ jvmtiError Redefiner::Run() { } counter++; } + UnregisterAllBreakpoints(); // Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done // allocating so no deadlocks. art::gc::Heap* heap = runtime_->GetHeap(); @@ -1083,9 +1145,7 @@ jvmtiError Redefiner::Run() { holder.GetOriginalDexFileBytes(counter)); counter++; } - // TODO Verify the new Class. // TODO Shrink the obsolete method maps if possible? - // TODO find appropriate class loader. // TODO Put this into a scoped thing. runtime_->GetThreadList()->ResumeAll(); // Get back shared mutator lock as expected for return. @@ -1190,13 +1250,13 @@ bool Redefiner::ClassRedefinition::EnsureClassAllocationsFinished() { art::StackHandleScope<2> hs(driver_->self_); art::Handle<art::mirror::Class> klass(hs.NewHandle( driver_->self_->DecodeJObject(klass_)->AsClass())); - if (klass.Get() == nullptr) { + if (klass == nullptr) { RecordFailure(ERR(INVALID_CLASS), "Unable to decode class argument!"); return false; } // Allocate the classExt art::Handle<art::mirror::ClassExt> ext(hs.NewHandle(klass->EnsureExtDataPresent(driver_->self_))); - if (ext.Get() == nullptr) { + if (ext == nullptr) { // No memory. Clear exception (it's not useful) and return error. // TODO This doesn't need to be fatal. We could just not support obsolete methods after hitting // this case. diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h index 421d22ef4c..65ee2912e2 100644 --- a/runtime/openjdkjvmti/ti_redefine.h +++ b/runtime/openjdkjvmti/ti_redefine.h @@ -155,12 +155,6 @@ class Redefiner { void FindAndAllocateObsoleteMethods(art::mirror::Class* art_klass) REQUIRES(art::Locks::mutator_lock_); - void FillObsoleteMethodMap( - art::mirror::Class* art_klass, - const std::unordered_map<art::ArtMethod*, art::ArtMethod*>& obsoletes) - REQUIRES(art::Locks::mutator_lock_); - - // Checks that the dex file contains only the single expected class and that the top-level class // data has not been modified in an incompatible manner. bool CheckClass() REQUIRES_SHARED(art::Locks::mutator_lock_); @@ -207,6 +201,8 @@ class Redefiner { void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_); + void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_); + private: Redefiner* driver_; jclass klass_; @@ -250,6 +246,7 @@ class Redefiner { bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_); void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_); + void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_); void RecordFailure(jvmtiError result, const std::string& class_sig, const std::string& error_msg); void RecordFailure(jvmtiError result, const std::string& error_msg) { diff --git a/runtime/openjdkjvmti/ti_threadgroup.cc b/runtime/openjdkjvmti/ti_threadgroup.cc index e63ce6576a..142387433e 100644 --- a/runtime/openjdkjvmti/ti_threadgroup.cc +++ b/runtime/openjdkjvmti/ti_threadgroup.cc @@ -155,7 +155,7 @@ jvmtiError ThreadGroupUtil::GetThreadGroupInfo(jvmtiEnv* env, 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); + CHECK(desired_thread_group != nullptr); art::ArtField* thread_group_field = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group); @@ -167,7 +167,7 @@ static bool IsInDesiredThreadGroup(art::Handle<art::mirror::Object> desired_thre 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); + CHECK(thread_group != nullptr); art::MutexLock mu(art::Thread::Current(), *art::Locks::thread_list_lock_); for (art::Thread* t : art::Runtime::Current()->GetThreadList()->GetList()) { @@ -187,7 +187,7 @@ static void GetThreads(art::Handle<art::mirror::Object> thread_group, 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); + CHECK(thread_group != nullptr); // Get the ThreadGroup[] "groups" out of this thread group... art::ArtField* groups_field = diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index 1292a819a3..5748475163 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -114,8 +114,8 @@ TEST_F(ProxyTest, ProxyClassHelper) { class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader))); Handle<mirror::Class> J(hs.NewHandle( class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader))); - ASSERT_TRUE(I.Get() != nullptr); - ASSERT_TRUE(J.Get() != nullptr); + ASSERT_TRUE(I != nullptr); + ASSERT_TRUE(J != nullptr); std::vector<mirror::Class*> interfaces; interfaces.push_back(I.Get()); @@ -123,7 +123,7 @@ TEST_F(ProxyTest, ProxyClassHelper) { Handle<mirror::Class> proxy_class(hs.NewHandle( GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces))); interfaces.clear(); // Don't least possibly stale objects in the array as good practice. - ASSERT_TRUE(proxy_class.Get() != nullptr); + ASSERT_TRUE(proxy_class != nullptr); ASSERT_TRUE(proxy_class->IsProxyClass()); ASSERT_TRUE(proxy_class->IsInitialized()); @@ -148,8 +148,8 @@ TEST_F(ProxyTest, ProxyFieldHelper) { class_linker_->FindClass(soa.Self(), "LInterfaces$I;", class_loader))); Handle<mirror::Class> J(hs.NewHandle( class_linker_->FindClass(soa.Self(), "LInterfaces$J;", class_loader))); - ASSERT_TRUE(I.Get() != nullptr); - ASSERT_TRUE(J.Get() != nullptr); + ASSERT_TRUE(I != nullptr); + ASSERT_TRUE(J != nullptr); Handle<mirror::Class> proxyClass; { @@ -159,7 +159,7 @@ TEST_F(ProxyTest, ProxyFieldHelper) { proxyClass = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1234", interfaces)); } - ASSERT_TRUE(proxyClass.Get() != nullptr); + ASSERT_TRUE(proxyClass != nullptr); ASSERT_TRUE(proxyClass->IsProxyClass()); ASSERT_TRUE(proxyClass->IsInitialized()); @@ -171,10 +171,10 @@ TEST_F(ProxyTest, ProxyFieldHelper) { Handle<mirror::Class> interfacesFieldClass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Class;"))); - ASSERT_TRUE(interfacesFieldClass.Get() != nullptr); + ASSERT_TRUE(interfacesFieldClass != nullptr); Handle<mirror::Class> throwsFieldClass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[[Ljava/lang/Class;"))); - ASSERT_TRUE(throwsFieldClass.Get() != nullptr); + ASSERT_TRUE(throwsFieldClass != nullptr); // Test "Class[] interfaces" field. ArtField* field = &static_fields->At(0); @@ -208,10 +208,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { proxyClass1 = hs.NewHandle(GenerateProxyClass(soa, jclass_loader, "$Proxy1", interfaces)); } - ASSERT_TRUE(proxyClass0.Get() != nullptr); + ASSERT_TRUE(proxyClass0 != nullptr); ASSERT_TRUE(proxyClass0->IsProxyClass()); ASSERT_TRUE(proxyClass0->IsInitialized()); - ASSERT_TRUE(proxyClass1.Get() != nullptr); + ASSERT_TRUE(proxyClass1 != nullptr); ASSERT_TRUE(proxyClass1->IsProxyClass()); ASSERT_TRUE(proxyClass1->IsInitialized()); diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc index 9523e92d7c..4ccfb6d83b 100644 --- a/runtime/reference_table_test.cc +++ b/runtime/reference_table_test.cc @@ -48,12 +48,12 @@ static mirror::Object* CreateWeakReference(mirror::Object* referent) class_linker->FindClass(self, "Ljava/lang/ref/WeakReference;", ScopedNullHandle<mirror::ClassLoader>()))); - CHECK(h_ref_class.Get() != nullptr); + CHECK(h_ref_class != nullptr); CHECK(class_linker->EnsureInitialized(self, h_ref_class, true, true)); Handle<mirror::Object> h_ref_instance(scope.NewHandle<mirror::Object>( h_ref_class->AllocObject(self))); - CHECK(h_ref_instance.Get() != nullptr); + CHECK(h_ref_instance != nullptr); ArtMethod* constructor = h_ref_class->FindDeclaredDirectMethod( "<init>", "(Ljava/lang/Object;)V", class_linker->GetImagePointerSize()); diff --git a/runtime/reflection.cc b/runtime/reflection.cc index a2b4cb37a9..3c64d40720 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -231,8 +231,8 @@ class ArgArray { hs.NewHandle<mirror::ObjectArray<mirror::Object>>(raw_args)); for (size_t i = 1, args_offset = 0; i < shorty_len_; ++i, ++args_offset) { arg.Assign(args->Get(args_offset)); - if (((shorty_[i] == 'L') && (arg.Get() != nullptr)) || - ((arg.Get() == nullptr && shorty_[i] != 'L'))) { + if (((shorty_[i] == 'L') && (arg != nullptr)) || + ((arg == nullptr && shorty_[i] != 'L'))) { // TODO: The method's parameter's type must have been previously resolved, yet // we've seen cases where it's not b/34440020. ObjPtr<mirror::Class> dst_class( @@ -242,7 +242,7 @@ class ArgArray { CHECK(self->IsExceptionPending()); return false; } - if (UNLIKELY(arg.Get() == nullptr || !arg->InstanceOf(dst_class))) { + if (UNLIKELY(arg == nullptr || !arg->InstanceOf(dst_class))) { ThrowIllegalArgumentException( StringPrintf("method %s argument %zd has type %s, got %s", m->PrettyMethod(false).c_str(), @@ -254,13 +254,13 @@ class ArgArray { } #define DO_FIRST_ARG(match_descriptor, get_fn, append) { \ - if (LIKELY(arg.Get() != nullptr && \ + if (LIKELY(arg != nullptr && \ arg->GetClass()->DescriptorEquals(match_descriptor))) { \ ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ append(primitive_field-> get_fn(arg.Get())); #define DO_ARG(match_descriptor, get_fn, append) \ - } else if (LIKELY(arg.Get() != nullptr && \ + } else if (LIKELY(arg != nullptr && \ arg->GetClass<>()->DescriptorEquals(match_descriptor))) { \ ArtField* primitive_field = arg->GetClass()->GetInstanceField(0); \ append(primitive_field-> get_fn(arg.Get())); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 9609bee022..f8f3d766c0 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -95,6 +95,7 @@ #include "mirror/field.h" #include "mirror/method.h" #include "mirror/method_handle_impl.h" +#include "mirror/method_handles_lookup.h" #include "mirror/method_type.h" #include "mirror/stack_trace_element.h" #include "mirror/throwable.h" @@ -1715,6 +1716,7 @@ void Runtime::VisitConstantRoots(RootVisitor* visitor) { mirror::Field::VisitRoots(visitor); mirror::MethodType::VisitRoots(visitor); mirror::MethodHandleImpl::VisitRoots(visitor); + mirror::MethodHandlesLookup::VisitRoots(visitor); mirror::EmulatedStackFrame::VisitRoots(visitor); mirror::ClassExt::VisitRoots(visitor); // Visit all the primitive array types classes. diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc index f1e78b40e0..abe99e0d50 100644 --- a/runtime/runtime_callbacks_test.cc +++ b/runtime/runtime_callbacks_test.cc @@ -294,7 +294,7 @@ TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) { const char* descriptor_y = "LY;"; Handle<mirror::Class> h_Y( hs.NewHandle(class_linker_->FindClass(soa.Self(), descriptor_y, class_loader))); - ASSERT_TRUE(h_Y.Get() != nullptr); + ASSERT_TRUE(h_Y != nullptr); bool expect1 = Expect({ "PreDefine:LY; <art-gtest-XandY.jar>", "PreDefine:LX; <art-gtest-XandY.jar>", diff --git a/runtime/stack.cc b/runtime/stack.cc index d7ba1d75d8..51a24e4e01 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -874,9 +874,13 @@ void StackVisitor::WalkStack(bool include_transitions) { CHECK_EQ(GetMethod(), callee) << "Expected: " << ArtMethod::PrettyMethod(callee) << " Found: " << ArtMethod::PrettyMethod(GetMethod()); } else { - CHECK_EQ(instrumentation_frame.method_, GetMethod()) - << "Expected: " << ArtMethod::PrettyMethod(instrumentation_frame.method_) - << " Found: " << ArtMethod::PrettyMethod(GetMethod()); + // Instrumentation generally doesn't distinguish between a method's obsolete and + // non-obsolete version. + CHECK_EQ(instrumentation_frame.method_->GetNonObsoleteMethod(), + GetMethod()->GetNonObsoleteMethod()) + << "Expected: " + << ArtMethod::PrettyMethod(instrumentation_frame.method_->GetNonObsoleteMethod()) + << " Found: " << ArtMethod::PrettyMethod(GetMethod()->GetNonObsoleteMethod()); } if (num_frames_ != 0) { // Check agreement of frame Ids only if num_frames_ is computed to avoid infinite @@ -903,7 +907,7 @@ void StackVisitor::WalkStack(bool include_transitions) { << " native=" << method->IsNative() << std::noboolalpha << " entrypoints=" << method->GetEntryPointFromQuickCompiledCode() - << "," << method->GetEntryPointFromJni() + << "," << (method->IsNative() ? method->GetEntryPointFromJni() : nullptr) << " next=" << *cur_quick_frame_; } diff --git a/runtime/stack_map.h b/runtime/stack_map.h index 61d6a5847c..f7a64026b7 100644 --- a/runtime/stack_map.h +++ b/runtime/stack_map.h @@ -747,6 +747,7 @@ class StackMapEncoding { return total_bit_size_; } + // Encode the encoding into the vector. template<typename Vector> void Encode(Vector* dest) const { static_assert(alignof(StackMapEncoding) == 1, "Should not require alignment"); @@ -754,6 +755,7 @@ class StackMapEncoding { dest->insert(dest->end(), ptr, ptr + sizeof(*this)); } + // Decode the encoding from a pointer, updates the pointer. void Decode(const uint8_t** ptr) { *this = *reinterpret_cast<const StackMapEncoding*>(*ptr); *ptr += sizeof(*this); @@ -924,6 +926,7 @@ class InlineInfoEncoding { void Dump(VariableIndentationOutputStream* vios) const; + // Encode the encoding into the vector. template<typename Vector> void Encode(Vector* dest) const { static_assert(alignof(InlineInfoEncoding) == 1, "Should not require alignment"); @@ -931,6 +934,7 @@ class InlineInfoEncoding { dest->insert(dest->end(), ptr, ptr + sizeof(*this)); } + // Decode the encoding from a pointer, updates the pointer. void Decode(const uint8_t** ptr) { *this = *reinterpret_cast<const InlineInfoEncoding*>(*ptr); *ptr += sizeof(*this); @@ -1171,6 +1175,7 @@ struct CodeInfoEncoding { ComputeTableOffsets(); } + // Compress is not const since it calculates cache_header_size. This is used by PrepareForFillIn. template<typename Vector> void Compress(Vector* dest) { dex_register_map.Encode(dest); @@ -1210,9 +1215,9 @@ struct CodeInfoEncoding { private: // Computed fields (not serialized). - // Header size in bytes. + // Header size in bytes, cached to avoid needing to re-decoding the encoding in HeaderSize. uint32_t cache_header_size = kInvalidSize; - // Non header size in bytes. + // Non header size in bytes, cached to avoid needing to re-decoding the encoding in NonHeaderSize. uint32_t cache_non_header_size = kInvalidSize; }; @@ -1221,7 +1226,13 @@ struct CodeInfoEncoding { * The information is of the form: * * [CodeInfoEncoding, DexRegisterMap+, DexLocationCatalog+, StackMap+, RegisterMask+, StackMask+, - * DexRegisterMap+, InlineInfo*] + * InlineInfo*] + * + * where CodeInfoEncoding is of the form: + * + * [ByteSizedTable(dex_register_map), ByteSizedTable(location_catalog), + * BitEncodingTable<StackMapEncoding>, BitEncodingTable<BitRegionEncoding>, + * BitEncodingTable<BitRegionEncoding>, BitEncodingTable<InlineInfoEncoding>] */ class CodeInfo { public: @@ -1331,7 +1342,9 @@ class CodeInfo { } InlineInfo GetInlineInfo(size_t index, const CodeInfoEncoding& encoding) const { - // Since we do not know the depth, we just return the whole remaining map. + // Since we do not know the depth, we just return the whole remaining map. The caller may + // access the inline info for arbitrary depths. To return the precise inline info we would need + // to count the depth before returning. // TODO: Clean this up. const size_t bit_offset = encoding.inline_info.bit_offset + index * encoding.inline_info.encoding.BitSize(); diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index c92305f373..8d946262e8 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -80,7 +80,34 @@ inline void Thread::CheckSuspend() { } } -inline void Thread::CheckEmptyCheckpoint() { +inline void Thread::CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mutex) { + Thread* self = Thread::Current(); + DCHECK_EQ(self, this); + for (;;) { + if (ReadFlag(kEmptyCheckpointRequest)) { + RunEmptyCheckpoint(); + // Check we hold only an expected mutex when accessing weak ref. + if (kIsDebugBuild) { + for (int i = kLockLevelCount - 1; i >= 0; --i) { + BaseMutex* held_mutex = self->GetHeldMutex(static_cast<LockLevel>(i)); + if (held_mutex != nullptr && + held_mutex != Locks::mutator_lock_ && + held_mutex != cond_var_mutex) { + std::vector<BaseMutex*>& expected_mutexes = Locks::expected_mutexes_on_weak_ref_access_; + CHECK(std::find(expected_mutexes.begin(), expected_mutexes.end(), held_mutex) != + expected_mutexes.end()) + << "Holding unexpected mutex " << held_mutex->GetName() + << " when accessing weak ref"; + } + } + } + } else { + break; + } + } +} + +inline void Thread::CheckEmptyCheckpointFromMutex() { DCHECK_EQ(Thread::Current(), this); for (;;) { if (ReadFlag(kEmptyCheckpointRequest)) { diff --git a/runtime/thread.cc b/runtime/thread.cc index eba8975754..7ee0cd1d3f 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -874,7 +874,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName())); - if (peer_thread_name.Get() == nullptr) { + if (peer_thread_name == nullptr) { // The Thread constructor should have set the Thread.name to a // non-null value. However, because we can run without code // available (in the compiler, in tests), we manually assign the @@ -887,7 +887,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) peer_thread_name.Assign(GetThreadName()); } // 'thread_name' may have been null, so don't trust 'peer_thread_name' to be non-null. - if (peer_thread_name.Get() != nullptr) { + if (peer_thread_name != nullptr) { SetThreadName(peer_thread_name->ToModifiedUtf8().c_str()); } } @@ -2284,7 +2284,7 @@ class BuildInternalStackTraceVisitor : public StackVisitor { Handle<mirror::ObjectArray<mirror::Object>> trace( hs.NewHandle( mirror::ObjectArray<mirror::Object>::Alloc(hs.Self(), array_class, depth + 1))); - if (trace.Get() == nullptr) { + if (trace == nullptr) { // Acquire uninterruptible_ in all paths. self_->StartAssertNoThreadSuspension("Building internal stack trace"); self_->AssertPendingOOMException(); @@ -2479,14 +2479,14 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( std::string class_name(PrettyDescriptor(descriptor)); class_name_object.Assign( mirror::String::AllocFromModifiedUtf8(soa.Self(), class_name.c_str())); - if (class_name_object.Get() == nullptr) { + if (class_name_object == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; } const char* source_file = method->GetDeclaringClassSourceFile(); if (source_file != nullptr) { source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file)); - if (source_name_object.Get() == nullptr) { + if (source_name_object == nullptr) { soa.Self()->AssertPendingOOMException(); return nullptr; } @@ -2496,7 +2496,7 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( CHECK(method_name != nullptr); Handle<mirror::String> method_name_object( hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name))); - if (method_name_object.Get() == nullptr) { + if (method_name_object == nullptr) { return nullptr; } ObjPtr<mirror::StackTraceElement> obj =mirror::StackTraceElement::Alloc(soa.Self(), @@ -2554,7 +2554,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, auto* cl = runtime->GetClassLinker(); Handle<mirror::Class> exception_class( hs.NewHandle(cl->FindClass(this, exception_class_descriptor, class_loader))); - if (UNLIKELY(exception_class.Get() == nullptr)) { + if (UNLIKELY(exception_class == nullptr)) { CHECK(IsExceptionPending()); LOG(ERROR) << "No exception class " << PrettyDescriptor(exception_class_descriptor); return; @@ -2570,7 +2570,7 @@ void Thread::ThrowNewWrappedException(const char* exception_class_descriptor, hs.NewHandle(ObjPtr<mirror::Throwable>::DownCast(exception_class->AllocObject(this)))); // If we couldn't allocate the exception, throw the pre-allocated out of memory exception. - if (exception.Get() == nullptr) { + if (exception == nullptr) { SetException(Runtime::Current()->GetPreAllocatedOutOfMemoryError()); return; } diff --git a/runtime/thread.h b/runtime/thread.h index 5dd6ae1dc2..e500e0b9e4 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -176,7 +176,8 @@ class Thread { void CheckSuspend() REQUIRES_SHARED(Locks::mutator_lock_); // Process a pending empty checkpoint if pending. - void CheckEmptyCheckpoint() REQUIRES_SHARED(Locks::mutator_lock_); + void CheckEmptyCheckpointFromWeakRefAccess(BaseMutex* cond_var_mutex); + void CheckEmptyCheckpointFromMutex(); static Thread* FromManagedThread(const ScopedObjectAccessAlreadyRunnable& ts, ObjPtr<mirror::Object> thread_peer) diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index df8acc37a2..caed36936a 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -379,13 +379,15 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function, Closure* callback return count; } -size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) { +void ThreadList::RunEmptyCheckpoint() { Thread* self = Thread::Current(); Locks::mutator_lock_->AssertNotExclusiveHeld(self); Locks::thread_list_lock_->AssertNotHeld(self); Locks::thread_suspend_count_lock_->AssertNotHeld(self); - + std::vector<uint32_t> runnable_thread_ids; size_t count = 0; + Barrier* barrier = empty_checkpoint_barrier_.get(); + barrier->Init(self, 0); { MutexLock mu(self, *Locks::thread_list_lock_); MutexLock mu2(self, *Locks::thread_suspend_count_lock_); @@ -415,8 +417,72 @@ size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids // checkpoint request. Otherwise we will hang as they are blocking in the kRunnable state. Runtime::Current()->GetHeap()->GetReferenceProcessor()->BroadcastForSlowPath(self); Runtime::Current()->BroadcastForNewSystemWeaks(/*broadcast_for_checkpoint*/true); - - return count; + { + ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun); + uint64_t total_wait_time = 0; + bool first_iter = true; + while (true) { + // Wake up the runnable threads blocked on the mutexes that another thread, which is blocked + // on a weak ref access, holds (indirectly blocking for weak ref access through another thread + // and a mutex.) This needs to be done periodically because the thread may be preempted + // between the CheckEmptyCheckpointFromMutex call and the subsequent futex wait in + // Mutex::ExclusiveLock, etc. when the wakeup via WakeupToRespondToEmptyCheckpoint + // arrives. This could cause a *very rare* deadlock, if not repeated. Most of the cases are + // handled in the first iteration. + for (BaseMutex* mutex : Locks::expected_mutexes_on_weak_ref_access_) { + mutex->WakeupToRespondToEmptyCheckpoint(); + } + static constexpr uint64_t kEmptyCheckpointPeriodicTimeoutMs = 100; // 100ms + static constexpr uint64_t kEmptyCheckpointTotalTimeoutMs = 600 * 1000; // 10 minutes. + size_t barrier_count = first_iter ? count : 0; + first_iter = false; // Don't add to the barrier count from the second iteration on. + bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointPeriodicTimeoutMs); + if (!timed_out) { + break; // Success + } + // This is a very rare case. + total_wait_time += kEmptyCheckpointPeriodicTimeoutMs; + if (kIsDebugBuild && total_wait_time > kEmptyCheckpointTotalTimeoutMs) { + std::ostringstream ss; + ss << "Empty checkpoint timeout\n"; + ss << "Barrier count " << barrier->GetCount(self) << "\n"; + ss << "Runnable thread IDs"; + for (uint32_t tid : runnable_thread_ids) { + ss << " " << tid; + } + ss << "\n"; + Locks::mutator_lock_->Dump(ss); + ss << "\n"; + LOG(FATAL_WITHOUT_ABORT) << ss.str(); + // 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. + { + ScopedObjectAccess soa(self); + MutexLock mu1(self, *Locks::thread_list_lock_); + for (Thread* thread : GetList()) { + uint32_t tid = thread->GetThreadId(); + bool is_in_runnable_thread_ids = + std::find(runnable_thread_ids.begin(), runnable_thread_ids.end(), tid) != + runnable_thread_ids.end(); + if (is_in_runnable_thread_ids && + thread->ReadFlag(kEmptyCheckpointRequest)) { + // Found a runnable thread that hasn't responded to the empty checkpoint request. + // Assume it's stuck and safe to dump its stack. + thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), + /*dump_native_stack*/ true, + /*backtrace_map*/ nullptr, + /*force_dump_stack*/ true); + } + } + } + LOG(FATAL_WITHOUT_ABORT) + << "Dumped runnable threads that haven't responded to empty checkpoint."; + // Now use ThreadList::Dump() to dump more threads, noting it may get stuck. + Dump(LOG_STREAM(FATAL_WITHOUT_ABORT)); + LOG(FATAL) << "Dumped all threads."; + } + } + } } // Request that a checkpoint function be run on all active (non-suspended) diff --git a/runtime/thread_list.h b/runtime/thread_list.h index b60fca1fdc..70917eb0f7 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -109,9 +109,7 @@ class ThreadList { // in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by // decrementing the empty checkpoint barrier count. This works even when the weak ref access is // disabled. Only one concurrent use is currently supported. - // In debug build, runnable_thread_ids will be populated with the thread IDS of the runnable - // thread to wait for. - size_t RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) + void RunEmptyCheckpoint() REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_); size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function) diff --git a/runtime/transaction_test.cc b/runtime/transaction_test.cc index a43c967092..97c1228038 100644 --- a/runtime/transaction_test.cc +++ b/runtime/transaction_test.cc @@ -35,7 +35,7 @@ class TransactionTest : public CommonRuntimeTest { StackHandleScope<2> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); // Load and initialize java.lang.ExceptionInInitializerError and the exception class used // to abort transaction so they can be thrown during class initialization if the transaction @@ -43,26 +43,26 @@ class TransactionTest : public CommonRuntimeTest { MutableHandle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/ExceptionInInitializerError;"))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); h_klass.Assign(class_linker_->FindSystemClass(soa.Self(), Transaction::kAbortExceptionSignature)); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(h_klass->IsInitialized()); // Load and verify utility class. h_klass.Assign(class_linker_->FindClass(soa.Self(), "LTransaction$AbortHelperClass;", class_loader)); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); // Load and verify tested class. h_klass.Assign(class_linker_->FindClass(soa.Self(), tested_class_signature, class_loader)); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); @@ -95,12 +95,12 @@ TEST_F(TransactionTest, Object_class) { StackHandleScope<2> hs(soa.Self()); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); Transaction transaction; Runtime::Current()->EnterTransactionMode(&transaction); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); Runtime::Current()->ExitTransactionMode(); @@ -115,9 +115,9 @@ TEST_F(TransactionTest, Object_monitor) { StackHandleScope<2> hs(soa.Self()); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Lock object's monitor outside the transaction. @@ -144,7 +144,7 @@ TEST_F(TransactionTest, Array_length) { StackHandleScope<2> hs(soa.Self()); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "[Ljava/lang/Object;"))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); constexpr int32_t kArraySize = 2; @@ -157,7 +157,7 @@ TEST_F(TransactionTest, Array_length) { mirror::Array::Alloc<true>(soa.Self(), h_klass.Get(), kArraySize, h_klass->GetComponentSizeShift(), Runtime::Current()->GetHeap()->GetCurrentAllocator()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); Runtime::Current()->ExitTransactionMode(); @@ -172,11 +172,11 @@ TEST_F(TransactionTest, StaticFieldsTest) { StackHandleScope<4> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticFieldsTest;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(success); ASSERT_TRUE(h_klass->IsInitialized()); @@ -232,9 +232,9 @@ TEST_F(TransactionTest, StaticFieldsTest) { // Create a java.lang.Object instance to set objectField. Handle<mirror::Class> object_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(object_klass.Get() != nullptr); + ASSERT_TRUE(object_klass != nullptr); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. @@ -270,11 +270,11 @@ TEST_F(TransactionTest, InstanceFieldsTest) { StackHandleScope<5> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LInstanceFieldsTest;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(success); ASSERT_TRUE(h_klass->IsInitialized()); @@ -282,7 +282,7 @@ TEST_F(TransactionTest, InstanceFieldsTest) { // Allocate an InstanceFieldTest object. Handle<mirror::Object> h_instance(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_instance.Get() != nullptr); + ASSERT_TRUE(h_instance != nullptr); // Lookup fields. ArtField* booleanField = h_klass->FindDeclaredInstanceField("booleanField", "Z"); @@ -334,9 +334,9 @@ TEST_F(TransactionTest, InstanceFieldsTest) { // Create a java.lang.Object instance to set objectField. Handle<mirror::Class> object_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(object_klass.Get() != nullptr); + ASSERT_TRUE(object_klass != nullptr); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. @@ -372,11 +372,11 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { StackHandleScope<4> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LStaticArrayFieldsTest;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); bool success = class_linker_->EnsureInitialized(soa.Self(), h_klass, true, true); ASSERT_TRUE(success); ASSERT_TRUE(h_klass->IsInitialized()); @@ -451,9 +451,9 @@ TEST_F(TransactionTest, StaticArrayFieldsTest) { // Create a java.lang.Object instance to set objectField. Handle<mirror::Class> object_klass( hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"))); - ASSERT_TRUE(object_klass.Get() != nullptr); + ASSERT_TRUE(object_klass != nullptr); Handle<mirror::Object> h_obj(hs.NewHandle(h_klass->AllocObject(soa.Self()))); - ASSERT_TRUE(h_obj.Get() != nullptr); + ASSERT_TRUE(h_obj != nullptr); ASSERT_EQ(h_obj->GetClass(), h_klass.Get()); // Modify fields inside transaction then rollback changes. @@ -489,15 +489,15 @@ TEST_F(TransactionTest, ResolveString) { StackHandleScope<3> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$ResolveString;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(h_klass->GetDexCache())); - ASSERT_TRUE(h_dex_cache.Get() != nullptr); + ASSERT_TRUE(h_dex_cache != nullptr); const DexFile* const dex_file = h_dex_cache->GetDexFile(); ASSERT_TRUE(dex_file != nullptr); @@ -538,12 +538,12 @@ TEST_F(TransactionTest, EmptyClass) { StackHandleScope<2> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$EmptyStatic;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); @@ -562,12 +562,12 @@ TEST_F(TransactionTest, StaticFieldClass) { StackHandleScope<2> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(LoadDex("Transaction")))); - ASSERT_TRUE(class_loader.Get() != nullptr); + ASSERT_TRUE(class_loader != nullptr); Handle<mirror::Class> h_klass( hs.NewHandle(class_linker_->FindClass(soa.Self(), "LTransaction$StaticFieldClass;", class_loader))); - ASSERT_TRUE(h_klass.Get() != nullptr); + ASSERT_TRUE(h_klass != nullptr); class_linker_->VerifyClass(soa.Self(), h_klass); ASSERT_TRUE(h_klass->IsVerified()); diff --git a/runtime/utils.h b/runtime/utils.h index 67438b5881..96e5bfa8ec 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -301,6 +301,30 @@ constexpr PointerSize ConvertToPointerSize(T any) { } } +// Returns a type cast pointer if object pointed to is within the provided bounds. +// Otherwise returns nullptr. +template <typename T> +inline static T BoundsCheckedCast(const void* pointer, + const void* lower, + const void* upper) { + const uint8_t* bound_begin = static_cast<const uint8_t*>(lower); + const uint8_t* bound_end = static_cast<const uint8_t*>(upper); + DCHECK(bound_begin <= bound_end); + + T result = reinterpret_cast<T>(pointer); + const uint8_t* begin = static_cast<const uint8_t*>(pointer); + const uint8_t* end = begin + sizeof(*result); + if (begin < bound_begin || end > bound_end || begin > end) { + return nullptr; + } + return result; +} + +template <typename T, size_t size> +constexpr size_t ArrayCount(const T (&)[size]) { + return size; +} + } // namespace art #endif // ART_RUNTIME_UTILS_H_ diff --git a/runtime/utils/dex_cache_arrays_layout-inl.h b/runtime/utils/dex_cache_arrays_layout-inl.h index bd1b044dae..98658215f7 100644 --- a/runtime/utils/dex_cache_arrays_layout-inl.h +++ b/runtime/utils/dex_cache_arrays_layout-inl.h @@ -29,7 +29,8 @@ namespace art { inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, - const DexFile::Header& header) + const DexFile::Header& header, + uint32_t num_call_sites) : pointer_size_(pointer_size), /* types_offset_ is always 0u, so it's constexpr */ methods_offset_( @@ -40,12 +41,14 @@ inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, RoundUp(strings_offset_ + StringsSize(header.string_ids_size_), FieldsAlignment())), method_types_offset_( RoundUp(fields_offset_ + FieldsSize(header.field_ids_size_), MethodTypesAlignment())), - size_( - RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), Alignment())) { + call_sites_offset_( + RoundUp(method_types_offset_ + MethodTypesSize(header.proto_ids_size_), + MethodTypesAlignment())), + size_(RoundUp(call_sites_offset_ + CallSitesSize(num_call_sites), Alignment())) { } inline DexCacheArraysLayout::DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file) - : DexCacheArraysLayout(pointer_size, dex_file->GetHeader()) { + : DexCacheArraysLayout(pointer_size, dex_file->GetHeader(), dex_file->NumCallSiteIds()) { } inline constexpr size_t DexCacheArraysLayout::Alignment() { @@ -131,10 +134,18 @@ inline size_t DexCacheArraysLayout::MethodTypesSize(size_t num_elements) const { inline size_t DexCacheArraysLayout::MethodTypesAlignment() const { static_assert(alignof(mirror::MethodTypeDexCacheType) == 8, - "alignof(MethodTypeDexCacheType) != 8"); + "Expecting alignof(MethodTypeDexCacheType) == 8"); return alignof(mirror::MethodTypeDexCacheType); } +inline size_t DexCacheArraysLayout::CallSitesSize(size_t num_elements) const { + return ArraySize(GcRootAsPointerSize<mirror::CallSite>(), num_elements); +} + +inline size_t DexCacheArraysLayout::CallSitesAlignment() const { + return alignof(GcRoot<mirror::CallSite>); +} + inline size_t DexCacheArraysLayout::ElementOffset(PointerSize element_size, uint32_t idx) { return static_cast<size_t>(element_size) * idx; } diff --git a/runtime/utils/dex_cache_arrays_layout.h b/runtime/utils/dex_cache_arrays_layout.h index 7d4b23a8dd..ed677ed3f4 100644 --- a/runtime/utils/dex_cache_arrays_layout.h +++ b/runtime/utils/dex_cache_arrays_layout.h @@ -37,11 +37,14 @@ class DexCacheArraysLayout { strings_offset_(0u), fields_offset_(0u), method_types_offset_(0u), + call_sites_offset_(0u), size_(0u) { } // Construct a layout for a particular dex file header. - DexCacheArraysLayout(PointerSize pointer_size, const DexFile::Header& header); + DexCacheArraysLayout(PointerSize pointer_size, + const DexFile::Header& header, + uint32_t num_call_sites); // Construct a layout for a particular dex file. DexCacheArraysLayout(PointerSize pointer_size, const DexFile* dex_file); @@ -104,6 +107,14 @@ class DexCacheArraysLayout { size_t MethodTypesAlignment() const; + size_t CallSitesOffset() const { + return call_sites_offset_; + } + + size_t CallSitesSize(size_t num_elements) const; + + size_t CallSitesAlignment() const; + private: static constexpr size_t types_offset_ = 0u; const PointerSize pointer_size_; // Must be first for construction initialization order. @@ -111,6 +122,7 @@ class DexCacheArraysLayout { const size_t strings_offset_; const size_t fields_offset_; const size_t method_types_offset_; + const size_t call_sites_offset_; const size_t size_; static size_t Alignment(PointerSize pointer_size); diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc index 02f1e1bbfe..634bd47f05 100644 --- a/runtime/utils_test.cc +++ b/runtime/utils_test.cc @@ -408,4 +408,23 @@ TEST_F(UtilsTest, IsValidDescriptor) { IsValidDescriptor(reinterpret_cast<char*>(&unpaired_surrogate_with_multibyte_sequence[0]))); } +TEST_F(UtilsTest, ArrayCount) { + int i[64]; + EXPECT_EQ(ArrayCount(i), 64u); + char c[7]; + EXPECT_EQ(ArrayCount(c), 7u); +} + +TEST_F(UtilsTest, BoundsCheckedCast) { + char buffer[64]; + const char* buffer_end = buffer + ArrayCount(buffer); + EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(nullptr, buffer, buffer_end), nullptr); + EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer, buffer, buffer_end), + reinterpret_cast<const uint64_t*>(buffer)); + EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 56, buffer, buffer_end), + reinterpret_cast<const uint64_t*>(buffer + 56)); + EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer - 1, buffer, buffer_end), nullptr); + EXPECT_EQ(BoundsCheckedCast<const uint64_t*>(buffer + 57, buffer, buffer_end), nullptr); +} + } // namespace art diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 5f55f3fd29..16739fa3bc 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3114,6 +3114,44 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { just_set_result = true; break; } + case Instruction::INVOKE_CUSTOM: + case Instruction::INVOKE_CUSTOM_RANGE: { + // Verify registers based on method_type in the call site. + bool is_range = (inst->Opcode() == Instruction::INVOKE_CUSTOM_RANGE); + + // Step 1. Check the call site that produces the method handle for invocation + const uint32_t call_site_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); + if (!CheckCallSite(call_site_idx)) { + DCHECK(HasFailures()); + break; + } + + // Step 2. Check the register arguments correspond to the expected arguments for the + // method handle produced by step 1. The dex file verifier has checked ranges for + // the first three arguments and CheckCallSite has checked the method handle type. + CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); + it.Next(); // Skip to name. + it.Next(); // Skip to method type of the method handle + const uint32_t proto_idx = static_cast<uint32_t>(it.GetJavaValue().i); + const DexFile::ProtoId& proto_id = dex_file_->GetProtoId(proto_idx); + 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(GetClassLoader(), return_descriptor, false); + if (!return_type.IsLowHalf()) { + work_line_->SetResultRegisterType(this, return_type); + } else { + work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(®_types_)); + } + just_set_result = true; + // TODO: Add compiler support for invoke-custom (b/35337872). + Fail(VERIFY_ERROR_FORCE_INTERPRETER); + break; + } case Instruction::NEG_INT: case Instruction::NOT_INT: work_line_->CheckUnaryOp(this, inst, reg_types_.Integer(), reg_types_.Integer()); @@ -3423,7 +3461,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { /* These should never appear during verification. */ case Instruction::UNUSED_3E ... Instruction::UNUSED_43: case Instruction::UNUSED_F3 ... Instruction::UNUSED_F9: - case Instruction::UNUSED_FC ... Instruction::UNUSED_FF: + case Instruction::UNUSED_FE ... Instruction::UNUSED_FF: case Instruction::UNUSED_79: case Instruction::UNUSED_7A: Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Unexpected opcode " << inst->DumpString(dex_file_); @@ -4094,6 +4132,116 @@ void MethodVerifier::VerifyInvocationArgsUnresolvedMethod(const Instruction* ins VerifyInvocationArgsFromIterator(&it, inst, method_type, is_range, nullptr); } +bool MethodVerifier::CheckCallSite(uint32_t call_site_idx) { + CallSiteArrayValueIterator it(*dex_file_, dex_file_->GetCallSiteId(call_site_idx)); + // Check essential arguments are provided. The dex file verifier has verified indicies of the + // main values (method handle, name, method_type). + if (it.Size() < 3) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " has too few arguments: " + << it.Size() << "< 3"; + return false; + } + + // Get and check the first argument: the method handle. + uint32_t method_handle_idx = static_cast<uint32_t>(it.GetJavaValue().i); + it.Next(); + const DexFile::MethodHandleItem& mh = dex_file_->GetMethodHandle(method_handle_idx); + if (mh.method_handle_type_ != static_cast<uint16_t>(DexFile::MethodHandleType::kInvokeStatic)) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " argument 0 method handle type is not InvokeStatic"; + return false; + } + + // Skip the second argument, the name to resolve, as checked by the + // dex file verifier. + it.Next(); + + // Skip the third argument, the method type expected, as checked by + // the dex file verifier. + it.Next(); + + // Check the bootstrap method handle and remaining arguments. + const DexFile::MethodId& method_id = dex_file_->GetMethodId(mh.field_or_method_idx_); + uint32_t length; + const char* shorty = dex_file_->GetMethodShorty(method_id, &length); + + if (it.Size() < length - 1) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " too few arguments for bootstrap method: " + << it.Size() << " < " << (length - 1); + return false; + } + + // Check the return type and first 3 arguments are references + // (CallSite, Lookup, String, MethodType). If they are not of the + // expected types (or subtypes), it will trigger a + // WrongMethodTypeException during execution. + if (shorty[0] != 'L') { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " bootstrap return type is not a reference"; + return false; + } + + for (uint32_t i = 1; i < 4; ++i) { + if (shorty[i] != 'L') { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " bootstrap method argument " << (i - 1) + << " is not a reference"; + return false; + } + } + + // Check the optional arguments. + for (uint32_t i = 4; i < length; ++i, it.Next()) { + bool match = false; + switch (it.GetValueType()) { + case EncodedArrayValueIterator::ValueType::kBoolean: + case EncodedArrayValueIterator::ValueType::kByte: + case EncodedArrayValueIterator::ValueType::kShort: + case EncodedArrayValueIterator::ValueType::kChar: + case EncodedArrayValueIterator::ValueType::kInt: + // These all fit within one register and encoders do not seem + // too exacting on the encoding type they use (ie using + // integer for all of these). + match = (strchr("ZBCSI", shorty[i]) != nullptr); + break; + case EncodedArrayValueIterator::ValueType::kLong: + match = ('J' == shorty[i]); + break; + case EncodedArrayValueIterator::ValueType::kFloat: + match = ('F' == shorty[i]); + break; + case EncodedArrayValueIterator::ValueType::kDouble: + match = ('D' == shorty[i]); + break; + case EncodedArrayValueIterator::ValueType::kMethodType: + case EncodedArrayValueIterator::ValueType::kMethodHandle: + case EncodedArrayValueIterator::ValueType::kString: + case EncodedArrayValueIterator::ValueType::kType: + case EncodedArrayValueIterator::ValueType::kNull: + match = ('L' == shorty[i]); + break; + case EncodedArrayValueIterator::ValueType::kField: + case EncodedArrayValueIterator::ValueType::kMethod: + case EncodedArrayValueIterator::ValueType::kEnum: + case EncodedArrayValueIterator::ValueType::kArray: + case EncodedArrayValueIterator::ValueType::kAnnotation: + // Unreachable based on current EncodedArrayValueIterator::Next(). + UNREACHABLE(); + } + + if (!match) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Call site #" << call_site_idx + << " bootstrap method argument " << (i - 1) + << " expected " << shorty[i] + << " got value type: " << it.GetValueType(); + return false; + } + } + return true; +} + class MethodParamListDescriptorIterator { public: explicit MethodParamListDescriptorIterator(ArtMethod* res_method) : diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index fa5a698423..7b67967c28 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -697,6 +697,11 @@ class MethodVerifier { REQUIRES_SHARED(Locks::mutator_lock_); /* + * Verify the arguments present for a call site. Returns "true" if all is well, "false" otherwise. + */ + bool CheckCallSite(uint32_t call_site_idx); + + /* * Verify that the target instruction is not "move-exception". It's important that the only way * to execute a move-exception is as the first instruction of an exception handler. * Returns "true" if all is well, "false" if the target instruction is move-exception. diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 113160785c..000cf7c393 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -890,12 +890,12 @@ bool VerifierDeps::VerifyAssignability(Handle<mirror::ClassLoader> class_loader, source.Assign( FindClassAndClearException(class_linker, self, source_desc.c_str(), class_loader)); - if (destination.Get() == nullptr) { + if (destination == nullptr) { LOG(INFO) << "VerifiersDeps: Could not resolve class " << destination_desc; return false; } - if (source.Get() == nullptr) { + if (source == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << source_desc; return false; } @@ -925,7 +925,7 @@ bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader, cls.Assign(FindClassAndClearException(class_linker, self, descriptor, class_loader)); if (entry.IsResolved()) { - if (cls.Get() == nullptr) { + if (cls == nullptr) { LOG(INFO) << "VerifierDeps: Could not resolve class " << descriptor; return false; } else if (entry.GetAccessFlags() != GetAccessFlags(cls.Get())) { @@ -939,7 +939,7 @@ bool VerifierDeps::VerifyClasses(Handle<mirror::ClassLoader> class_loader, << std::dec; return false; } - } else if (cls.Get() != nullptr) { + } else if (cls != nullptr) { LOG(INFO) << "VerifierDeps: Unexpected successful resolution of class " << descriptor; return false; } diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc index cd79bb61f3..416873f4c3 100644 --- a/runtime/zip_archive.cc +++ b/runtime/zip_archive.cc @@ -23,10 +23,16 @@ #include <unistd.h> #include <vector> +#include "android-base/stringprintf.h" #include "base/unix_file/fd_file.h" namespace art { +// Log file contents and mmap info when mapping entries directly. +static constexpr const bool kDebugZipMapDirectly = false; + +using android::base::StringPrintf; + uint32_t ZipEntry::GetUncompressedLength() { return zip_entry_->uncompressed_length; } @@ -35,6 +41,15 @@ uint32_t ZipEntry::GetCrc32() { return zip_entry_->crc32; } +bool ZipEntry::IsUncompressed() { + return zip_entry_->method == kCompressStored; +} + +bool ZipEntry::IsAlignedTo(size_t alignment) { + DCHECK(IsPowerOfTwo(alignment)) << alignment; + return IsAlignedParam(zip_entry_->offset, static_cast<int>(alignment)); +} + ZipEntry::~ZipEntry() { delete zip_entry_; } @@ -73,6 +88,102 @@ MemMap* ZipEntry::ExtractToMemMap(const char* zip_filename, const char* entry_fi return map.release(); } +MemMap* ZipEntry::MapDirectlyFromFile(const char* zip_filename, std::string* error_msg) { + const int zip_fd = GetFileDescriptor(handle_); + const char* entry_filename = entry_name_.c_str(); + + // Should not happen since we don't have a memory ZipArchive constructor. + // However the underlying ZipArchive isn't required to have an FD, + // so check to be sure. + CHECK_GE(zip_fd, 0) << + StringPrintf("Cannot map '%s' (in zip '%s') directly because the zip archive " + "is not file backed.", + entry_filename, + zip_filename); + + if (!IsUncompressed()) { + *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because it is compressed.", + entry_filename, + zip_filename); + return nullptr; + } else if (zip_entry_->uncompressed_length != zip_entry_->compressed_length) { + *error_msg = StringPrintf("Cannot map '%s' (in zip '%s') directly because " + "entry has bad size (%u != %u).", + entry_filename, + zip_filename, + zip_entry_->uncompressed_length, + zip_entry_->compressed_length); + return nullptr; + } + + std::string name(entry_filename); + name += " mapped directly in memory from "; + name += zip_filename; + + const off_t offset = zip_entry_->offset; + + if (kDebugZipMapDirectly) { + LOG(INFO) << "zip_archive: " << "make mmap of " << name << " @ offset = " << offset; + } + + std::unique_ptr<MemMap> map( + MemMap::MapFileAtAddress(nullptr, // Expected pointer address + GetUncompressedLength(), // Byte count + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + zip_fd, + offset, + false, // Don't restrict allocation to lower4GB + false, // Doesn't overlap existing map (reuse=false) + name.c_str(), + /*out*/error_msg)); + + if (map == nullptr) { + DCHECK(!error_msg->empty()); + } + + if (kDebugZipMapDirectly) { + // Dump contents of file, same format as using this shell command: + // $> od -j <offset> -t x1 <zip_filename> + static constexpr const int kMaxDumpChars = 15; + lseek(zip_fd, 0, SEEK_SET); + + int count = offset + kMaxDumpChars; + + std::string tmp; + char buf; + + // Dump file contents. + int i = 0; + while (read(zip_fd, &buf, 1) > 0 && i < count) { + tmp += StringPrintf("%3d ", (unsigned int)buf); + ++i; + } + + LOG(INFO) << "map_fd raw bytes starting at 0"; + LOG(INFO) << "" << tmp; + LOG(INFO) << "---------------------------"; + + // Dump map contents. + if (map != nullptr) { + tmp = ""; + + count = kMaxDumpChars; + + uint8_t* begin = map->Begin(); + for (i = 0; i < count; ++i) { + tmp += StringPrintf("%3d ", (unsigned int)begin[i]); + } + + LOG(INFO) << "map address " << StringPrintf("%p", begin); + LOG(INFO) << "map first " << kMaxDumpChars << " chars:"; + LOG(INFO) << tmp; + } + } + + return map.release(); +} + static void SetCloseOnExec(int fd) { // This dance is more portable than Linux's O_CLOEXEC open(2) flag. int flags = fcntl(fd, F_GETFD); @@ -129,7 +240,7 @@ ZipEntry* ZipArchive::Find(const char* name, std::string* error_msg) const { return nullptr; } - return new ZipEntry(handle_, zip_entry.release()); + return new ZipEntry(handle_, zip_entry.release(), name); } ZipArchive::~ZipArchive() { diff --git a/runtime/zip_archive.h b/runtime/zip_archive.h index 42bf55cb3f..18584447e8 100644 --- a/runtime/zip_archive.h +++ b/runtime/zip_archive.h @@ -37,19 +37,35 @@ class MemMap; class ZipEntry { public: bool ExtractToFile(File& file, std::string* error_msg); + // Extract this entry to anonymous memory (R/W). + // Returns null on failure and sets error_msg. MemMap* ExtractToMemMap(const char* zip_filename, const char* entry_filename, std::string* error_msg); + // Create a file-backed private (clean, R/W) memory mapping to this entry. + // 'zip_filename' is used for diagnostics only, + // the original file that the ZipArchive was open with is used + // for the mapping. + // + // Will only succeed if the entry is stored uncompressed. + // Returns null on failure and sets error_msg. + MemMap* MapDirectlyFromFile(const char* zip_filename, /*out*/std::string* error_msg); virtual ~ZipEntry(); uint32_t GetUncompressedLength(); uint32_t GetCrc32(); + bool IsUncompressed(); + bool IsAlignedTo(size_t alignment); + private: ZipEntry(ZipArchiveHandle handle, - ::ZipEntry* zip_entry) : handle_(handle), zip_entry_(zip_entry) {} + ::ZipEntry* zip_entry, + const std::string& entry_name) + : handle_(handle), zip_entry_(zip_entry), entry_name_(entry_name) {} ZipArchiveHandle handle_; ::ZipEntry* const zip_entry_; + std::string const entry_name_; friend class ZipArchive; DISALLOW_COPY_AND_ASSIGN(ZipEntry); diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java index df0a3ddf48..5a43a4f23f 100644 --- a/test/021-string2/src/Main.java +++ b/test/021-string2/src/Main.java @@ -117,6 +117,9 @@ public class Main { " " + $noinline$equals(s0_3, s0_1) + " " + $noinline$equals(s0_3, s0_2) + " " + $noinline$equals(s0_3, s0_3)); + + testEqualsConstString(); + testConstStringEquals(); } public static void testCompareToAndEquals() { @@ -539,6 +542,266 @@ public class Main { } } + public static void testEqualsConstString() { + Assert.assertTrue($noinline$equalsConstString0("")); + Assert.assertFalse($noinline$equalsConstString0("1")); + + Assert.assertTrue($noinline$equalsConstString7("0123456")); + Assert.assertFalse($noinline$equalsConstString7("012345")); + Assert.assertFalse($noinline$equalsConstString7("01234567")); + Assert.assertFalse($noinline$equalsConstString7("012345x")); + Assert.assertFalse($noinline$equalsConstString7("012345\u0440")); + + Assert.assertTrue($noinline$equalsConstString14("01234567890123")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012")); + Assert.assertFalse($noinline$equalsConstString14("012345678901234")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012x")); + Assert.assertFalse($noinline$equalsConstString14("0123456789012\u0440")); + + Assert.assertTrue($noinline$equalsConstString24("012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012")); + Assert.assertFalse($noinline$equalsConstString24("0123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012x")); + Assert.assertFalse($noinline$equalsConstString24("01234567890123456789012\u0440")); + + Assert.assertTrue($noinline$equalsConstString29("01234567890123456789012345678")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567")); + Assert.assertFalse($noinline$equalsConstString29("012345678901234567890123456789")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567x")); + Assert.assertFalse($noinline$equalsConstString29("0123456789012345678901234567\u0440")); + + Assert.assertTrue($noinline$equalsConstString35("01234567890123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstString35("012345678901234567890123456789012345")); + Assert.assertFalse($noinline$equalsConstString35("0123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$equalsConstString35("0123456789012345678901234567890123\u0440")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString7("\u0440123456")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u04401234567")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("\u044012345x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString7("0123456")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString14("\u04401234567890123")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u044012345678901234")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("\u0440123456789012x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString14("01234567890123")); + + Assert.assertTrue($noinline$equalsConstNonAsciiString24("\u044012345678901234567890123")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u0440123456789012345678901234")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\u04401234567890123456789012x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString24("\012345678901234567890123")); + + Assert.assertTrue( + $noinline$equalsConstNonAsciiString29("\u04401234567890123456789012345678")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u044012345678901234567890123456789")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString29("\u0440123456789012345678901234567x")); + Assert.assertFalse($noinline$equalsConstNonAsciiString29("01234567890123456789012345678")); + + Assert.assertTrue( + $noinline$equalsConstNonAsciiString35("\u04401234567890123456789012345678901234")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u044012345678901234567890123456789012345")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("\u0440123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$equalsConstNonAsciiString35("01234567890123456789012345678901234")); + } + + public static void testConstStringEquals() { + Assert.assertTrue($noinline$constString0Equals("")); + Assert.assertFalse($noinline$constString0Equals("1")); + + Assert.assertTrue($noinline$constString7Equals("0123456")); + Assert.assertFalse($noinline$constString7Equals("012345")); + Assert.assertFalse($noinline$constString7Equals("01234567")); + Assert.assertFalse($noinline$constString7Equals("012345x")); + Assert.assertFalse($noinline$constString7Equals("012345\u0440")); + + Assert.assertTrue($noinline$constString14Equals("01234567890123")); + Assert.assertFalse($noinline$constString14Equals("0123456789012")); + Assert.assertFalse($noinline$constString14Equals("012345678901234")); + Assert.assertFalse($noinline$constString14Equals("0123456789012x")); + Assert.assertFalse($noinline$constString14Equals("0123456789012\u0440")); + + Assert.assertTrue($noinline$constString24Equals("012345678901234567890123")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012")); + Assert.assertFalse($noinline$constString24Equals("0123456789012345678901234")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012x")); + Assert.assertFalse($noinline$constString24Equals("01234567890123456789012\u0440")); + + Assert.assertTrue($noinline$constString29Equals("01234567890123456789012345678")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567")); + Assert.assertFalse($noinline$constString29Equals("012345678901234567890123456789")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567x")); + Assert.assertFalse($noinline$constString29Equals("0123456789012345678901234567\u0440")); + + Assert.assertTrue($noinline$constString35Equals("01234567890123456789012345678901234")); + Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123")); + Assert.assertFalse($noinline$constString35Equals("012345678901234567890123456789012345")); + Assert.assertFalse($noinline$constString35Equals("0123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$constString35Equals("0123456789012345678901234567890123\u0040")); + + Assert.assertTrue($noinline$constNonAsciiString7Equals("\u0440123456")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u04401234567")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("\u044012345x")); + Assert.assertFalse($noinline$constNonAsciiString7Equals("0123456")); + + Assert.assertTrue($noinline$constNonAsciiString14Equals("\u04401234567890123")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u044012345678901234")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("\u0440123456789012x")); + Assert.assertFalse($noinline$constNonAsciiString14Equals("01234567890123")); + + Assert.assertTrue($noinline$constNonAsciiString24Equals("\u044012345678901234567890123")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u0440123456789012345678901234")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\u04401234567890123456789012x")); + Assert.assertFalse($noinline$constNonAsciiString24Equals("\012345678901234567890123")); + + Assert.assertTrue( + $noinline$constNonAsciiString29Equals("\u04401234567890123456789012345678")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u044012345678901234567890123456789")); + Assert.assertFalse( + $noinline$constNonAsciiString29Equals("\u0440123456789012345678901234567x")); + Assert.assertFalse($noinline$constNonAsciiString29Equals("01234567890123456789012345678")); + + Assert.assertTrue( + $noinline$constNonAsciiString35Equals("\u04401234567890123456789012345678901234")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u044012345678901234567890123456789012345")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("\u0440123456789012345678901234567890123x")); + Assert.assertFalse( + $noinline$constNonAsciiString35Equals("01234567890123456789012345678901234")); + } + + public static boolean $noinline$equalsConstString0(String s) { + if (doThrow) { throw new Error(); } + return s.equals(""); + } + + public static boolean $noinline$equalsConstString7(String s) { + if (doThrow) { throw new Error(); } + return s.equals("0123456"); + } + + public static boolean $noinline$equalsConstString14(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123"); + } + + public static boolean $noinline$equalsConstString24(String s) { + if (doThrow) { throw new Error(); } + return s.equals("012345678901234567890123"); + } + + public static boolean $noinline$equalsConstString29(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123456789012345678"); + } + + public static boolean $noinline$equalsConstString35(String s) { + if (doThrow) { throw new Error(); } + return s.equals("01234567890123456789012345678901234"); + } + + public static boolean $noinline$equalsConstNonAsciiString7(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u0440123456"); + } + + public static boolean $noinline$equalsConstNonAsciiString14(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123"); + } + + public static boolean $noinline$equalsConstNonAsciiString24(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u044012345678901234567890123"); + } + + public static boolean $noinline$equalsConstNonAsciiString29(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123456789012345678"); + } + + public static boolean $noinline$equalsConstNonAsciiString35(String s) { + if (doThrow) { throw new Error(); } + return s.equals("\u04401234567890123456789012345678901234"); + } + + public static boolean $noinline$constString0Equals(String s) { + if (doThrow) { throw new Error(); } + return s.equals(""); + } + + public static boolean $noinline$constString7Equals(String s) { + if (doThrow) { throw new Error(); } + return "0123456".equals(s); + } + + public static boolean $noinline$constString14Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123".equals(s); + } + + public static boolean $noinline$constString24Equals(String s) { + if (doThrow) { throw new Error(); } + return "012345678901234567890123".equals(s); + } + + public static boolean $noinline$constString29Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123456789012345678".equals(s); + } + + public static boolean $noinline$constString35Equals(String s) { + if (doThrow) { throw new Error(); } + return "01234567890123456789012345678901234".equals(s); + } + + public static boolean $noinline$constNonAsciiString7Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u0440123456".equals(s); + } + + public static boolean $noinline$constNonAsciiString14Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123".equals(s); + } + + public static boolean $noinline$constNonAsciiString24Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u044012345678901234567890123".equals(s); + } + + public static boolean $noinline$constNonAsciiString29Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123456789012345678".equals(s); + } + + public static boolean $noinline$constNonAsciiString35Equals(String s) { + if (doThrow) { throw new Error(); } + return "\u04401234567890123456789012345678901234".equals(s); + } + public static int $noinline$compareTo(String lhs, String rhs) { if (doThrow) { throw new Error(); } return lhs.compareTo(rhs); diff --git a/test/044-proxy/build b/test/044-proxy/build new file mode 100755 index 0000000000..ab956e26e1 --- /dev/null +++ b/test/044-proxy/build @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 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. + +./default-build "$@" --experimental java-8 diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt index 2a5f0b90db..63a46200d9 100644 --- a/test/044-proxy/expected.txt +++ b/test/044-proxy/expected.txt @@ -87,7 +87,7 @@ Got expected exception Proxy methods: [public final boolean $PROXY_CLASS_NAME1$.equals(java.lang.Object), public final java.lang.Object $PROXY_CLASS_NAME1$.foo(), public final java.lang.String $PROXY_CLASS_NAME1$.foo(), public final int $PROXY_CLASS_NAME1$.hashCode(), public final java.lang.String $PROXY_CLASS_NAME1$.toString()] Invocation of public abstract java.lang.String NarrowingTest$I2.foo() Invoking foo using I2 type: hello -Invocation of public abstract java.lang.Object NarrowingTest$I1.foo() +Invocation of public default java.lang.Object NarrowingTest$I2.foo() Invoking foo using I1 type: 1 Invocation of public abstract java.lang.String NarrowingTest$I2.foo() Got expected exception diff --git a/test/071-dexfile-map-clean/build b/test/071-dexfile-map-clean/build new file mode 100755 index 0000000000..a171fc3b3b --- /dev/null +++ b/test/071-dexfile-map-clean/build @@ -0,0 +1,21 @@ +#!/bin/bash +# +# Copyright 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. + +# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed. +# This is needed for our test correctness which validates classes.dex are mapped file-backed. +# +# In addition, align to at least 4 bytes since that's the dex alignment requirement. +./default-build "$@" --zip-compression-method store --zip-align 4 diff --git a/test/071-dexfile-map-clean/expected.txt b/test/071-dexfile-map-clean/expected.txt new file mode 100644 index 0000000000..af7fb2806b --- /dev/null +++ b/test/071-dexfile-map-clean/expected.txt @@ -0,0 +1,3 @@ +Another +Secondary dexfile mmap is clean +Another Instance diff --git a/test/071-dexfile-map-clean/info.txt b/test/071-dexfile-map-clean/info.txt new file mode 100644 index 0000000000..7e45808603 --- /dev/null +++ b/test/071-dexfile-map-clean/info.txt @@ -0,0 +1,11 @@ +Exercise Dalvik-specific DEX file feature. Will not work on RI. + +If these conditions are met: +* When we are loading in a secondary dex file +* and when dex2oat is not used +* and the dex file is stored uncompressed in a ZIP file + +Then check: +* The dex file is memory-mapped file-backed as clean memory +(i.e. there is no extraction step) + diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run new file mode 100755 index 0000000000..9c100ec497 --- /dev/null +++ b/test/071-dexfile-map-clean/run @@ -0,0 +1,31 @@ +#!/bin/bash +# +# Copyright 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. + +# Run without dex2oat so that we don't create oat/vdex files +# when trying to load the secondary dex file. + +# In this way, the secondary dex file will be forced to be +# loaded directly. +# +# In addition, make sure we call 'sync' +# before executing dalvikvm because otherwise +# it's highly likely the pushed JAR files haven't +# been committed to permanent storage yet, +# and when we mmap them the kernel will think +# the memory is dirty (despite being file-backed). +# (Note: this was reproducible 100% of the time on +# a target angler device). +./default-run "$@" --no-dex2oat --sync diff --git a/test/071-dexfile-map-clean/src-ex/Another.java b/test/071-dexfile-map-clean/src-ex/Another.java new file mode 100644 index 0000000000..58464a65ab --- /dev/null +++ b/test/071-dexfile-map-clean/src-ex/Another.java @@ -0,0 +1,21 @@ +/* + * 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 Another { + public Another() { + System.out.println("Another Instance"); + } +} diff --git a/test/071-dexfile-map-clean/src/Main.java b/test/071-dexfile-map-clean/src/Main.java new file mode 100644 index 0000000000..8a196dd52e --- /dev/null +++ b/test/071-dexfile-map-clean/src/Main.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.util.Enumeration; + +import java.nio.file.Files; +import java.nio.file.Paths; + +/** + * DexFile tests (Dalvik-specific). + */ +public class Main { + private static final String CLASS_PATH = + System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar"; + + /** + * Prep the environment then run the test. + */ + public static void main(String[] args) throws Exception { + // Load the dex file, this is a pre-requisite to mmap-ing it in. + Class<?> AnotherClass = testDexFile(); + // Check that the memory maps are clean. + testDexMemoryMaps(); + + // Prevent garbage collector from collecting our DexFile + // (and unmapping too early) by using it after we finish + // our verification. + AnotherClass.newInstance(); + } + + private static boolean checkSmapsEntry(String[] smapsLines, int offset) { + String nameDescription = smapsLines[offset]; + String[] split = nameDescription.split(" "); + + String permissions = split[1]; + // Mapped as read-only + anonymous. + if (!permissions.startsWith("r--p")) { + return false; + } + + boolean validated = false; + + // We have the right entry, now make sure it's valid. + for (int i = offset; i < smapsLines.length; ++i) { + String line = smapsLines[i]; + + if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) { + String lineTrimmed = line.trim(); + String[] lineSplit = lineTrimmed.split(" +"); + + String sizeUsuallyInKb = lineSplit[lineSplit.length - 2]; + + sizeUsuallyInKb = sizeUsuallyInKb.trim(); + + if (!sizeUsuallyInKb.equals("0")) { + System.out.println( + "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty"); + System.out.println(line); + } else { + validated = true; + } + } + + // VmFlags marks the "end" of an smaps entry. + if (line.startsWith("VmFlags")) { + break; + } + } + + if (validated) { + System.out.println("Secondary dexfile mmap is clean"); + } else { + System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries"); + } + + return true; + } + + // This test takes relies on dex2oat being skipped. + // (enforced in 'run' file by using '--no-dex2oat' + // + // This could happen in a non-test situation + // if a secondary dex file is loaded (but not yet maintenance-mode compiled) + // with JIT. + // + // Or it could also happen if a secondary dex file is loaded and forced + // into running into the interpreter (e.g. duplicate classes). + // + // Rather than relying on those weird fallbacks, + // we force the runtime not to dex2oat the dex file to ensure + // this test is repeatable and less brittle. + private static void testDexMemoryMaps() throws Exception { + // Ensure that the secondary dex file is mapped clean (directly from JAR file). + String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps"))); + + String[] smapsLines = smaps.split("\n"); + boolean found = true; + for (int i = 0; i < smapsLines.length; ++i) { + if (smapsLines[i].contains(CLASS_PATH)) { + if (checkSmapsEntry(smapsLines, i)) { + return; + } // else we found the wrong one, keep going. + } + } + + // Error case: + System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry"); + System.out.println(smaps); + } + + private static Class<?> testDexFile() throws Exception { + ClassLoader classLoader = Main.class.getClassLoader(); + Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile"); + Method DexFile_loadDex = DexFile.getMethod("loadDex", + String.class, + String.class, + Integer.TYPE); + Method DexFile_entries = DexFile.getMethod("entries"); + Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0); + Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile); + while (e.hasMoreElements()) { + String className = e.nextElement(); + System.out.println(className); + } + + Method DexFile_loadClass = DexFile.getMethod("loadClass", + String.class, + ClassLoader.class); + Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile, + "Another", Main.class.getClassLoader()); + return AnotherClass; + } +} diff --git a/test/121-modifiers/build b/test/121-modifiers/build deleted file mode 100644 index 771dd51829..0000000000 --- a/test/121-modifiers/build +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# -# Copyright (C) 2014 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. - -# Stop if something fails. -set -e - -# The classes are pre-compiled and modified with ASM. -# -# To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then -# run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them -# into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar): -# -# javac Inf.java NonInf.java Main.java -# javac -cp asm.jar:asm-tree.jar:. Asm.java -# java -cp asm.jar:asm-tree.jar:. Asm -# mv Inf.out classes/Inf.class -# mv NonInf.out classes/NonInf.class -# mv Main.class A.class A\$B.class A\$C.class classes/ - -if [ ${USE_JACK} = "true" ]; then - jar cf classes.jill.jar -C classes . - # Workaround b/19561685: disable sanity checks to produce a DEX file with invalid modifiers. - ${JACK} --sanity-checks off --import classes.jill.jar --output-dex . -else - ${DX} --debug --dex --dump-to=classes.lst --output=classes.dex classes -fi -zip $TEST_NAME.jar classes.dex diff --git a/test/121-modifiers/info.txt b/test/121-modifiers/info.txt index 943cbf8aba..129aee8ae6 100644 --- a/test/121-modifiers/info.txt +++ b/test/121-modifiers/info.txt @@ -1 +1,18 @@ This is a test checking the modifier (access flags) handling of ART. + +The classes are pre-compiled and modified with ASM. + +To reproduce, compile the source files. Asm.java needs the ASM libraries (core and tree). Then +run Asm.java, which produces Inf.out and NonInf.out. Rename these to class files and put them +into the classes directory (this assumes the ASM libraries are names asm.jar and asm-tree.jar). +Finally, compile with jack/jill or dx, and run baksmali. + +javac Inf.java NonInf.java Main.java +javac -cp asm.jar:asm-tree.jar:. Asm.java +java -cp asm.jar:asm-tree.jar:. Asm +mv Inf.out classes/Inf.class +mv NonInf.out classes/NonInf.class +mv Main.class A.class A\$B.class A\$C.class classes/ +dx --debug --dex --output=classes.dex classes +baksmali classes.dex +mv out/*.smali smali/ diff --git a/test/121-modifiers/smali/A$B.smali b/test/121-modifiers/smali/A$B.smali new file mode 100644 index 0000000000..d6addc2c94 --- /dev/null +++ b/test/121-modifiers/smali/A$B.smali @@ -0,0 +1,42 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.class LA$B; +.super Ljava/lang/Object; +.source "Main.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = LA; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0xa + name = "B" +.end annotation + + +# direct methods +.method private constructor <init>()V + .registers 1 + + .prologue + .line 19 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + return-void +.end method diff --git a/test/121-modifiers/smali/A$C.smali b/test/121-modifiers/smali/A$C.smali new file mode 100644 index 0000000000..eba4756ad6 --- /dev/null +++ b/test/121-modifiers/smali/A$C.smali @@ -0,0 +1,30 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.class public interface abstract LA$C; +.super Ljava/lang/Object; +.source "Main.java" + + +# annotations +.annotation system Ldalvik/annotation/EnclosingClass; + value = LA; +.end annotation + +.annotation system Ldalvik/annotation/InnerClass; + accessFlags = 0x60c + name = "C" +.end annotation diff --git a/test/121-modifiers/smali/A.smali b/test/121-modifiers/smali/A.smali new file mode 100644 index 0000000000..b1078bcce0 --- /dev/null +++ b/test/121-modifiers/smali/A.smali @@ -0,0 +1,41 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.class LA; +.super Ljava/lang/Object; +.source "Main.java" + + +# annotations +.annotation system Ldalvik/annotation/MemberClasses; + value = { + LA$B;, + LA$C; + } +.end annotation + + +# direct methods +.method constructor <init>()V + .registers 1 + + .prologue + .line 18 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + .line 21 + return-void +.end method diff --git a/test/121-modifiers/smali/Inf.smali b/test/121-modifiers/smali/Inf.smali new file mode 100644 index 0000000000..6a3a7ab24c --- /dev/null +++ b/test/121-modifiers/smali/Inf.smali @@ -0,0 +1,23 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.class public interface abstract LInf; +.super Ljava/lang/Object; +.source "Inf.java" + + +# static fields +.field public static final I:I diff --git a/test/121-modifiers/smali/NonInf.smali b/test/121-modifiers/smali/NonInf.smali new file mode 100644 index 0000000000..34bf0312e4 --- /dev/null +++ b/test/121-modifiers/smali/NonInf.smali @@ -0,0 +1,177 @@ +# +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +.class public abstract LNonInf; +.super Ljava/lang/Object; +.source "NonInf.java" + + +# static fields +.field static staticField:I + + +# instance fields +.field final finalField:I + +.field private privateField:I + +.field protected protectedField:I + +.field public publicField:I + +.field transient transientField:I + +.field volatile volatileField:I + + +# direct methods +.method public constructor <init>()V + .registers 2 + + .prologue + .line 11 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + + .line 12 + const/4 v0, 0x0 + + iput v0, p0, LNonInf;->publicField:I + + .line 13 + const/4 v0, 0x1 + + iput v0, p0, LNonInf;->privateField:I + + .line 14 + const/4 v0, 0x2 + + iput v0, p0, LNonInf;->protectedField:I + + .line 15 + const/4 v0, 0x3 + + sput v0, LNonInf;->staticField:I + + .line 16 + const/4 v0, 0x4 + + iput v0, p0, LNonInf;->transientField:I + + .line 17 + const/4 v0, 0x5 + + iput v0, p0, LNonInf;->volatileField:I + + .line 18 + const/4 v0, 0x6 + + iput v0, p0, LNonInf;->finalField:I + + .line 19 + return-void +.end method + +.method private privateMethod()I + .registers 2 + + .prologue + .line 24 + const/4 v0, 0x0 + + return v0 +.end method + +.method public static staticMethod()I + .registers 1 + + .prologue + .line 42 + const/4 v0, 0x0 + + return v0 +.end method + + +# virtual methods +.method public abstract abstractMethod()I +.end method + +.method public final finalMethod()I + .registers 2 + + .prologue + .line 54 + const/4 v0, 0x0 + + return v0 +.end method + +.method public native nativeMethod()V +.end method + +.method protected protectedMethod()I + .registers 2 + + .prologue + .line 28 + const/4 v0, 0x0 + + return v0 +.end method + +.method public publicMethod()I + .registers 2 + + .prologue + .line 32 + const/4 v0, 0x0 + + return v0 +.end method + +.method public strictfp strictfpMethod()D + .registers 3 + + .prologue + .line 46 + const-wide/16 v0, 0x0 + + return-wide v0 +.end method + +.method public declared-synchronized synchronizedMethod()I + .registers 2 + + .prologue + monitor-enter p0 + + .line 38 + const/4 v0, 0x0 + + monitor-exit p0 + + return v0 +.end method + +.method public varargs varargsMethod([Ljava/lang/Object;)I + .registers 3 + + .prologue + .line 50 + const/4 v0, 0x0 + + return v0 +.end method diff --git a/test/121-modifiers/src-java/A.java b/test/121-modifiers/src-java/A.java new file mode 100644 index 0000000000..d97f6b3f89 --- /dev/null +++ b/test/121-modifiers/src-java/A.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 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. + */ + +// These classes are to check the additional flags for inner classes. +class A { + private static class B { + } + protected static interface C { + } +} diff --git a/test/121-modifiers/src/Asm.java b/test/121-modifiers/src-java/Asm.java index f120622120..f120622120 100644 --- a/test/121-modifiers/src/Asm.java +++ b/test/121-modifiers/src-java/Asm.java diff --git a/test/121-modifiers/src/Inf.java b/test/121-modifiers/src-java/Inf.java index 1dadae0e47..1dadae0e47 100644 --- a/test/121-modifiers/src/Inf.java +++ b/test/121-modifiers/src-java/Inf.java diff --git a/test/121-modifiers/src/NonInf.java b/test/121-modifiers/src-java/NonInf.java index 52e4882532..52e4882532 100644 --- a/test/121-modifiers/src/NonInf.java +++ b/test/121-modifiers/src-java/NonInf.java diff --git a/test/121-modifiers/src/Main.java b/test/121-modifiers/src2/Main.java index e21b789cc7..62e65a8fbe 100644 --- a/test/121-modifiers/src/Main.java +++ b/test/121-modifiers/src2/Main.java @@ -14,14 +14,6 @@ * limitations under the License. */ -// These classes are to check the additional flags for inner classes. -class A { - private static class B { - } - protected static interface C { - } -} - public class Main { public final static int INTERFACE_DEFINED_BITS = 0x0001 | // public, may be set. diff --git a/test/536-checker-intrinsic-optimization/src/Main.java b/test/536-checker-intrinsic-optimization/src/Main.java index ed7524c7ad..52f3f84406 100644 --- a/test/536-checker-intrinsic-optimization/src/Main.java +++ b/test/536-checker-intrinsic-optimization/src/Main.java @@ -329,7 +329,7 @@ public class Main { /// CHECK-NOT: cbz // Terminate the scope for the CHECK-NOT search at the reference or length comparison, // whichever comes first. - /// CHECK: cmp {{w.*,}} {{w.*}} + /// CHECK: cmp {{w.*,}} {{w.*|#.*}} public static boolean stringArgumentNotNull(Object obj) { obj.getClass(); return "foo".equals(obj); @@ -380,10 +380,10 @@ public class Main { // so repeat the check twice. /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] - /// CHECK: cmp {{w\d+}}, {{w\d+}} + /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}] /// CHECK-NOT: ldr {{w\d+}}, [{{x\d+}}, #0] - /// CHECK: cmp {{w\d+}}, {{w\d+}} + /// CHECK: cmp {{w\d+}}, {{w\d+|#.*}} public static boolean stringArgumentIsString() { return "foo".equals(myString); } diff --git a/test/616-cha-native/expected.txt b/test/616-cha-native/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-native/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-native/info.txt b/test/616-cha-native/info.txt new file mode 100644 index 0000000000..a17bcaba9c --- /dev/null +++ b/test/616-cha-native/info.txt @@ -0,0 +1,2 @@ +Test for Class Hierarchy Analysis (CHA) single-implementation status updating +behavior on an overridden native method. diff --git a/test/616-cha-native/src/Main.java b/test/616-cha-native/src/Main.java new file mode 100644 index 0000000000..53a463ce87 --- /dev/null +++ b/test/616-cha-native/src/Main.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +abstract class A { + public abstract void foo(); +} + +class B extends A { + public native void foo(); +} + +class C extends B { + public void foo() {} +} + +public class Main { + public static void main(String[] args) { + System.loadLibrary(args[0]); + } +} diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java index b6179449df..beea90a57d 100644 --- a/test/616-cha/src/Main.java +++ b/test/616-cha/src/Main.java @@ -196,8 +196,6 @@ public class Main { // should return true for those cases. assertSingleImplementation(java.lang.String.class, "charAt", true); assertSingleImplementation(java.lang.Thread.class, "join", true); - // We don't set single-implementation modifier bit for native methods. - assertSingleImplementation(java.lang.Thread.class, "isInterrupted", false); if (isInterpreted()) { sIsOptimizing = false; diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc index c0aedc199f..b35dff48ee 100644 --- a/test/626-const-class-linking/clear_dex_cache_types.cc +++ b/test/626-const-class-linking/clear_dex_cache_types.cc @@ -49,7 +49,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_nativeDumpClasses(JNIEnv*, jclass, j StackHandleScope<1> hs(soa.Self()); Handle<mirror::ObjectArray<mirror::Object>> classes = hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(array)); - CHECK(classes.Get() != nullptr); + CHECK(classes != nullptr); for (size_t i = 0, length = classes->GetLength(); i != length; ++i) { CHECK(classes->Get(i) != nullptr) << i; CHECK(classes->Get(i)->IsClass()) diff --git a/test/636-arm64-veneer-pool/build b/test/636-arm64-veneer-pool/build new file mode 100755 index 0000000000..27cc4d6d98 --- /dev/null +++ b/test/636-arm64-veneer-pool/build @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright 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. +# Make us exit on a failure. + +set -e + +# Use javac+dx instead of jack. +export USE_JACK=false +./default-build "$@" diff --git a/test/636-arm64-veneer-pool/expected.txt b/test/636-arm64-veneer-pool/expected.txt new file mode 100644 index 0000000000..b0aad4deb5 --- /dev/null +++ b/test/636-arm64-veneer-pool/expected.txt @@ -0,0 +1 @@ +passed diff --git a/test/636-arm64-veneer-pool/info.txt b/test/636-arm64-veneer-pool/info.txt new file mode 100644 index 0000000000..2393be0538 --- /dev/null +++ b/test/636-arm64-veneer-pool/info.txt @@ -0,0 +1 @@ +Regression test for an issue with VIXL ARM64 veneer pools (b/34850123). diff --git a/test/636-arm64-veneer-pool/src/Main.java b/test/636-arm64-veneer-pool/src/Main.java new file mode 100644 index 0000000000..8229fee378 --- /dev/null +++ b/test/636-arm64-veneer-pool/src/Main.java @@ -0,0 +1,4223 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class C0 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C2 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C3 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C4 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C5 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C6 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C7 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C8 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C9 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C10 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C11 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C12 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C13 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C14 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C15 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C16 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C17 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C18 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C19 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C20 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C21 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C22 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C23 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C24 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C25 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C26 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C27 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C28 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C29 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C30 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C31 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C32 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C33 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C34 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C35 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C36 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C37 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C38 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C39 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C40 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C41 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C42 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C43 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C44 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C45 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C46 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C47 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C48 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C49 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C50 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C51 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C52 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C53 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C54 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C55 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C56 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C57 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C58 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C59 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C60 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C61 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C62 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C63 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C64 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C65 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C66 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C67 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C68 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C69 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C70 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C71 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C72 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C73 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C74 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C75 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C76 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C77 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C78 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C79 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C80 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C81 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C82 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C83 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C84 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C85 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C86 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C87 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C88 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C89 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C90 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mReport_Factory(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mApi(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C91 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C92 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C93 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C94 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C95 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C96 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C97 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C98 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C99 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C100 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C101 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C102 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C103 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C104 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C105 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void m_InMemoryScanner(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void m_Scanner(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C106 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C107 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C108 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C109 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C110 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C111 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C112 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C113 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C114 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C115 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C116 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C117 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C118 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C119 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C120 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C121 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C122 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C123 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C124 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C125 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C126 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C127 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C128 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C129 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C130 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C131 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C132 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C133 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C134 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C135 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C136 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C137 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C138 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C139 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C140 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C141 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C142 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C143 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C144 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C145 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C146 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C147 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C148 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C149 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C150 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C151 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C152 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C153 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C154 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C155 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C156 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C157 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C158 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C159 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C160 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C161 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C162 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C163 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C164 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C165 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C166 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C167 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C168 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C169 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C170 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C171 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C172 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C173 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C174 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C175 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C176 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C177 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C178 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C179 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C180 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C181 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C182 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C183 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C184 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C185 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C186 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C187 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C188 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C189 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C190 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C191 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C192 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C193 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C194 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C195 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C196 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C197 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C198 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C199 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C200 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C201 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C202 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C203 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C204 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C205 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C206 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C207 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C208 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C209 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C210 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C211 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C212 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C213 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C214 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C215 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C216 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C217 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C218 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C219 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C220 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C221 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C222 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C223 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C224 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C225 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C226 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C227 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C228 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C229 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C230 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C231 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C232 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C233 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C234 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C235 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C236 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C237 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C238 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C239 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C240 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C241 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C242 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C243 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C244 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C245 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C246 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C247 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C248 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C249 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C250 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C251 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C252 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C253 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C254 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C255 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mFactory(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C256 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C257 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C258 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C259 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C260 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C261 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C262 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C263 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C264 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C265 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C266 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C267 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C268 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C269 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C270 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C271 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C272 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C273 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C274 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C275 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C276 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C277 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C278 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C279 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C280 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C281 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C282 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C283 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C284 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C285 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C286 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C287 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C288 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C289 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C290 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C291 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C292 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C293 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C294 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C295 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C296 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C297 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C298 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C299 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C300 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C301 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C302 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C303 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C304 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C305 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C306 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C307 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C308 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C309 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C310 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C311 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C312 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C313 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C314 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C315 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C316 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C317 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C318 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C319 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C320 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C321 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C322 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C323 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C324 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C325 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C326 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C327 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C328 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C329 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C330 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C331 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C332 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C333 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C334 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C335 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C336 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C337 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C338 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C339 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C340 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C341 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C342 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C343 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C344 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C345 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C346 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C347 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C348 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C349 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C350 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C351 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C352 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C353 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C354 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C355 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C356 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C357 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C358 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C359 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C360 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C361 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C362 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C363 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C364 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C365 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C366 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C367 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C368 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C369 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C370 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C371 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C372 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C373 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C374 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C375 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C376 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C377 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C378 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C379 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C380 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C381 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C382 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C383 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C384 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C385 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C386 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C387 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C388 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C389 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C390 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C391 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C392 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C393 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C394 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C395 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C396 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C397 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C398 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C399 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C400 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C401 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C402 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C403 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C404 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C405 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C406 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C407 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C408 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C409 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C410 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C411 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C412 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C413 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C414 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C415 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C416 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C417 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C418 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C419 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C420 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C421 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C422 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C423 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C424 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C425 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C426 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C427 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C428 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C429 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C430 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C431 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C432 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C433 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C434 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C435 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C436 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C437 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C438 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C439 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C440 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C441 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C442 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C443 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C444 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C445 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C446 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C447 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C448 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C449 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C450 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C451 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C452 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C453 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C454 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C455 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C456 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C457 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mMap(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C458 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C459 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C460 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C461 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C462 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C463 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C464 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C465 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C466 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C467 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C468 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C469 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C470 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C471 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C472 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C473 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C474 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C475 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C476 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C477 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C478 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C479 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C480 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C481 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C482 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C483 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C484 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C485 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C486 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C487 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C488 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C489 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C490 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C491 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C492 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C493 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C494 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C495 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C496 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C497 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C498 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C499 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C500 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C501 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C502 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C503 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C504 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C505 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C506 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C507 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C508 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C509 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C510 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C511 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C512 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C513 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C514 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C515 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C516 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C517 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C518 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C519 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C520 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C521 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C522 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C523 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C524 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C525 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C526 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C527 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C528 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C529 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C530 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C531 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C532 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C533 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C534 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C535 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C536 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C537 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C538 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C539 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C540 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C541 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C542 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C543 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C544 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C545 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C546 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C547 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C548 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C549 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C550 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C551 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C552 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C553 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C554 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C555 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C556 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C557 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C558 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C559 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C560 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C561 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C562 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C563 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C564 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C565 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C566 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C567 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C568 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C569 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C570 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C571 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C572 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C573 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C574 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C575 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C576 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C577 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mDebug(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C578 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C579 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C580 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C581 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C582 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C583 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C584 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C585 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C586 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C587 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C588 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C589 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C590 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C591 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C592 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C593 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C594 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C595 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C596 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C597 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C598 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C599 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C600 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C601 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C602 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C603 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C604 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C605 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C606 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C607 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C608 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C609 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C610 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C611 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C612 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C613 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C614 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C615 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C616 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C617 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C618 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C619 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C620 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C621 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C622 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C623 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C624 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C625 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C626 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C627 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C628 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C629 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C630 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C631 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C632 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C633 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C634 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C635 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C636 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C637 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C638 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C639 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C640 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C641 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C642 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C643 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C644 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C645 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C646 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C647 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C648 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C649 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C650 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C651 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C652 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C653 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C654 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C655 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C656 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C657 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C658 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C659 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C660 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C661 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C662 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C663 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C664 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C665 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C666 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C667 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C668 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C669 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C670 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C671 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C672 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C673 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C674 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C675 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C676 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C677 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C678 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C679 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C680 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C681 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C682 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C683 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C684 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C685 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C686 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C687 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C688 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C689 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C690 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C691 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C692 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C693 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C694 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C695 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C696 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C697 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C698 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C699 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C700 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C701 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C702 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C703 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C704 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C705 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C706 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C707 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C708 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C709 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C710 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C711 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C712 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C713 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C714 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C715 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C716 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C717 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C718 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C719 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C720 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C721 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C722 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C723 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C724 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C725 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C726 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C727 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C728 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C729 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C730 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C731 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C732 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C733 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C734 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C735 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C736 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C737 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C738 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C739 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C740 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C741 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C742 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C743 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C744 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C745 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C746 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C747 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C748 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C749 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C750 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C751 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C752 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C753 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C754 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C755 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C756 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C757 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C758 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C759 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C760 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C761 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C762 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C763 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C764 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C765 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C766 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C767 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C768 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C769 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C770 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C771 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C772 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C773 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C774 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C775 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C776 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C777 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C778 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C779 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C780 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C781 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C782 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C783 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C784 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C785 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C786 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C787 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C788 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C789 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C790 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C791 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C792 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C793 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C794 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C795 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C796 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C797 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C798 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C799 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C800 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C801 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C802 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C803 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C804 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C805 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C806 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C807 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C808 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C809 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C810 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C811 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C812 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C813 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C814 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C815 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C816 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C817 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C818 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C819 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C820 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C821 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C822 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C823 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C824 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C825 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C826 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C827 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C828 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C829 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C830 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C831 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C832 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C833 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C834 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C835 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C836 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C837 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C838 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C839 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C840 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C841 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C842 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C843 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C844 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C845 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C846 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C847 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C848 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mMap(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C849 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C850 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C851 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C852 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C853 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C854 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C855 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C856 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C857 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C858 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C859 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C860 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C861 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C862 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C863 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C864 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C865 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C866 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C867 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C868 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C869 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C870 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mImpl(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C871 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C872 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C873 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C874 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C875 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C876 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C877 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C878 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C879 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C880 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C881 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C882 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C883 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C884 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C885 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C886 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C887 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C888 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C889 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C890 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C891 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C892 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C893 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C894 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C895 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C896 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C897 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C898 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C899 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C900 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C901 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C902 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C903 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C904 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C905 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C906 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C907 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C908 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C909 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C910 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C911 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C912 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C913 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C914 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C915 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C916 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C917 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C918 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C919 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C920 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C921 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C922 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C923 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C924 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C925 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C926 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C927 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C928 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C929 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C930 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C931 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C932 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C933 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C934 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C935 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C936 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C937 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C938 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C939 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C940 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C941 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C942 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C943 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C944 { + public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } + public static void mManager(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } +} +class C945 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C946 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C947 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C948 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C949 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C950 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C951 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C952 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C953 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C954 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C955 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C956 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C957 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C958 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C959 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C960 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C961 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C962 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C963 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C964 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C965 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C966 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C967 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C968 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C969 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C970 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C971 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C972 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C973 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C974 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C975 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C976 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C977 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C978 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C979 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C980 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C981 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C982 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C983 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C984 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C985 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C986 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C987 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C988 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C989 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C990 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C991 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C992 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C993 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C994 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C995 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C996 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C997 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C998 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C999 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1000 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1001 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1002 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1003 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1004 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1005 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1006 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1007 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1008 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1009 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1010 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1011 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1012 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1013 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1014 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1015 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1016 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1017 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1018 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1019 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1020 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1021 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1022 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1023 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1024 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1025 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1026 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1027 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1028 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1029 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1030 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1031 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1032 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1033 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1034 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1035 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1036 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1037 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1038 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1039 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1040 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1041 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1042 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1043 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1044 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1045 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1046 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1047 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1048 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1049 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1050 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1051 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1052 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1053 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1054 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1055 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1056 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1057 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1058 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1059 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1060 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1061 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1062 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1063 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1064 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1065 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1066 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1067 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1068 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1069 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1070 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1071 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1072 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1073 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1074 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1075 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1076 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1077 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1078 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1079 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1080 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1081 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1082 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1083 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1084 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1085 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1086 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1087 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1088 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1089 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1090 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1091 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1092 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1093 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1094 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1095 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1096 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1097 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1098 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1099 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1100 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1101 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1102 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1103 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1104 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1105 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1106 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1107 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1108 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1109 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1110 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1111 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1112 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1113 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1114 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1115 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1116 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1117 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1118 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1119 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1120 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1121 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1122 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1123 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1124 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1125 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1126 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1127 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1128 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1129 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1130 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1131 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1132 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1133 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1134 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1135 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1136 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1137 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1138 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1139 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1140 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1141 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1142 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1143 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1144 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1145 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1146 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1147 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1148 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1149 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1150 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1151 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1152 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1153 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1154 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1155 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1156 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1157 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1158 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1159 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1160 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1161 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1162 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1163 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1164 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1165 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1166 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1167 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1168 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1169 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1170 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1171 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1172 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1173 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1174 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1175 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1176 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1177 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1178 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1179 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1180 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1181 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1181a { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1181b { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1182 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1183 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1184 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1185 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1186 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1187 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1188 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1189 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1190 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1191 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1192 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1193 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1194 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1195 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1196 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1197 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1198 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1199 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1200 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1201 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1202 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1203 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1204 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1205 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1206 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1207 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1208 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1209 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1210 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1211 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } +class C1212 { public static void m(Context c, Binder b) { /* Defeat inlining. */ if (Main.doThrow) { throw new Error(); } } } + +class Context {} +class Binder {} + +public class Main { + + java.util.HashMap<String, Integer> typeMap; + private void buildTypeMap() {} + + // TODO: Add Checker assertions? + public void configure(Context context, Class<?> cls, Binder binder) { + if (this.typeMap == null) { + buildTypeMap(); + } + Integer num = (Integer) this.typeMap.get(cls.getName()); + if (num != null) { + switch (num.intValue()) { + case 0: + C0.m(context, binder); + return; + case 1: + C1.m(context, binder); + return; + case 2: + C2.m(context, binder); + return; + case 3: + C3.m(context, binder); + return; + case 4: + C4.m(context, binder); + return; + case 5: + C5.m(context, binder); + return; + case 6: + C6.m(context, binder); + C7.m(context, binder); + C8.m(context, binder); + return; + case 7: + C9.m(context, binder); + return; + case 8: + C10.m(context, binder); + return; + case 9: + C11.m(context, binder); + return; + case 10: + C12.m(context, binder); + return; + case 11: + C13.m(context, binder); + return; + case 12: + C14.m(context, binder); + return; + case 13: + C15.m(context, binder); + return; + case 14: + C16.m(context, binder); + return; + case 15: + C17.m(context, binder); + return; + case 16: + C18.m(context, binder); + C19.m(context, binder); + return; + case 17: + C20.m(context, binder); + return; + case 18: + C21.m(context, binder); + return; + case 19: + C22.m(context, binder); + return; + case 20: + C23.m(context, binder); + C24.m(context, binder); + C25.m(context, binder); + C26.m(context, binder); + C27.m(context, binder); + C28.m(context, binder); + C29.m(context, binder); + C30.m(context, binder); + C31.m(context, binder); + C32.m(context, binder); + C33.m(context, binder); + C34.m(context, binder); + C35.m(context, binder); + C36.m(context, binder); + C37.m(context, binder); + C38.m(context, binder); + C39.m(context, binder); + C40.m(context, binder); + C41.m(context, binder); + C42.m(context, binder); + C43.m(context, binder); + C44.m(context, binder); + C45.m(context, binder); + C46.m(context, binder); + C47.m(context, binder); + C48.m(context, binder); + C49.m(context, binder); + C50.m(context, binder); + C51.m(context, binder); + C52.m(context, binder); + C53.m(context, binder); + C54.m(context, binder); + C55.m(context, binder); + C56.m(context, binder); + C57.m(context, binder); + C58.m(context, binder); + C59.m(context, binder); + C60.m(context, binder); + C61.m(context, binder); + C62.m(context, binder); + C63.m(context, binder); + C64.m(context, binder); + C65.m(context, binder); + C66.m(context, binder); + C67.m(context, binder); + C68.m(context, binder); + C69.m(context, binder); + C70.m(context, binder); + C71.m(context, binder); + C72.m(context, binder); + C73.m(context, binder); + C74.m(context, binder); + C75.m(context, binder); + C76.m(context, binder); + C77.m(context, binder); + C78.m(context, binder); + C79.m(context, binder); + C80.m(context, binder); + C81.m(context, binder); + C82.m(context, binder); + C83.m(context, binder); + C84.m(context, binder); + C85.m(context, binder); + return; + case 21: + C86.m(context, binder); + return; + case 22: + C87.m(context, binder); + return; + case 23: + C88.m(context, binder); + C89.m(context, binder); + return; + case 24: + C90.m(context, binder); + return; + case 25: + C91.m(context, binder); + return; + case 26: + C92.m(context, binder); + return; + case 27: + C93.m(context, binder); + return; + case 28: + C94.m(context, binder); + return; + case 29: + C95.m(context, binder); + return; + case 30: + C96.m(context, binder); + return; + case 31: + C97.m(context, binder); + return; + case 32: + C98.m(context, binder); + C99.m(context, binder); + return; + case 33: + C100.m(context, binder); + return; + case 34: + C101.m(context, binder); + return; + case 35: + C102.m(context, binder); + return; + case 36: + C103.m(context, binder); + return; + case 37: + C104.m(context, binder); + return; + case 38: + C105.m(context, binder); + return; + case 39: + C106.m(context, binder); + return; + case 40: + C107.m(context, binder); + return; + case 41: + C108.m(context, binder); + return; + case 42: + C109.m(context, binder); + return; + case 43: + C110.m(context, binder); + return; + case 44: + C111.m(context, binder); + return; + case 45: + C112.m(context, binder); + return; + case 46: + C113.m(context, binder); + return; + case 47: + C114.m(context, binder); + return; + case 48: + C115.m(context, binder); + return; + case 49: + C116.m(context, binder); + return; + case 50: + C117.m(context, binder); + C118.m(context, binder); + return; + case 51: + C119.m(context, binder); + return; + case 52: + C120.m(context, binder); + return; + case 53: + C121.m(context, binder); + return; + case 54: + C122.m(context, binder); + return; + case 55: + C123.m(context, binder); + return; + case 56: + C124.m(context, binder); + return; + case 57: + C125.m(context, binder); + return; + case 58: + C126.m(context, binder); + return; + case 59: + C127.m(context, binder); + return; + case 60: + C128.m(context, binder); + return; + case 61: + C129.m(context, binder); + return; + case 62: + C130.m(context, binder); + C131.m(context, binder); + C132.m(context, binder); + C133.m(context, binder); + C134.m(context, binder); + C135.m(context, binder); + C136.m(context, binder); + C137.m(context, binder); + return; + case 63: + C138.m(context, binder); + return; + case 64: + C139.m(context, binder); + return; + case 65: + C140.m(context, binder); + return; + case 66: + C141.m(context, binder); + return; + case 67: + C142.m(context, binder); + return; + case 68: + C143.m(context, binder); + C144.m(context, binder); + C145.m(context, binder); + return; + case 69: + C146.m(context, binder); + return; + case 70: + C147.m(context, binder); + return; + case 71: + C148.m(context, binder); + return; + case 72: + C149.m(context, binder); + return; + case 73: + C150.m(context, binder); + return; + case 74: + C151.m(context, binder); + return; + case 75: + C152.m(context, binder); + return; + case 76: + C153.m(context, binder); + return; + case 77: + C154.m(context, binder); + return; + case 78: + C155.m(context, binder); + return; + case 79: + C156.m(context, binder); + return; + case 80: + C157.m(context, binder); + C158.m(context, binder); + C159.m(context, binder); + return; + case 81: + C160.m(context, binder); + return; + case 82: + C161.m(context, binder); + return; + case 83: + C162.m(context, binder); + return; + case 84: + C163.m(context, binder); + return; + case 85: + C164.m(context, binder); + C165.m(context, binder); + C166.m(context, binder); + C167.m(context, binder); + C168.m(context, binder); + C169.m(context, binder); + C170.m(context, binder); + C171.m(context, binder); + C172.m(context, binder); + C173.m(context, binder); + C174.m(context, binder); + C175.m(context, binder); + C176.m(context, binder); + C177.m(context, binder); + C178.m(context, binder); + C179.m(context, binder); + C180.m(context, binder); + C181.m(context, binder); + C182.m(context, binder); + C183.m(context, binder); + C184.m(context, binder); + return; + case 86: + C185.m(context, binder); + return; + case 87: + C186.m(context, binder); + return; + case 88: + C187.m(context, binder); + return; + case 89: + C188.m(context, binder); + return; + case 90: + C189.m(context, binder); + return; + case 91: + C190.m(context, binder); + return; + case 92: + C191.m(context, binder); + return; + case 93: + C192.m(context, binder); + return; + case 94: + C193.m(context, binder); + return; + case 95: + C194.m(context, binder); + return; + case 96: + C195.m(context, binder); + return; + case 97: + C196.m(context, binder); + return; + case 98: + C197.m(context, binder); + return; + case 99: + C198.m(context, binder); + return; + case 100: + C199.m(context, binder); + return; + case 101: + C200.m(context, binder); + return; + case 102: + C201.m(context, binder); + return; + case 103: + C202.m(context, binder); + C203.m(context, binder); + C204.m(context, binder); + C205.m(context, binder); + C206.m(context, binder); + return; + case 104: + C207.m(context, binder); + return; + case 105: + C208.m(context, binder); + return; + case 106: + C209.m(context, binder); + return; + case 107: + C210.m(context, binder); + return; + case 108: + C211.m(context, binder); + return; + case 109: + C212.m(context, binder); + return; + case 110: + C213.m(context, binder); + return; + case 111: + C214.m(context, binder); + return; + case 112: + C215.m(context, binder); + C216.m(context, binder); + C217.m(context, binder); + C218.m(context, binder); + C219.m(context, binder); + C220.m(context, binder); + C221.m(context, binder); + C222.m(context, binder); + C223.m(context, binder); + C224.m(context, binder); + C225.m(context, binder); + C226.m(context, binder); + return; + case 113: + C227.m(context, binder); + return; + case 114: + C228.m(context, binder); + return; + case 115: + C229.m(context, binder); + return; + case 116: + C230.m(context, binder); + return; + case 117: + C231.m(context, binder); + return; + case 118: + C232.m(context, binder); + return; + case 119: + C233.m(context, binder); + return; + case 120: + C234.m(context, binder); + return; + case 121: + C235.m(context, binder); + return; + case 122: + C236.m(context, binder); + return; + case 123: + C237.m(context, binder); + return; + case 124: + C238.m(context, binder); + return; + case 125: + C239.m(context, binder); + return; + case 126: + C240.m(context, binder); + return; + case 127: + C241.m(context, binder); + return; + case 128: + C242.m(context, binder); + return; + case 129: + C243.m(context, binder); + C244.m(context, binder); + C245.m(context, binder); + C246.m(context, binder); + C247.m(context, binder); + C248.m(context, binder); + C249.m(context, binder); + C250.m(context, binder); + C251.m(context, binder); + return; + case 130: + C252.m(context, binder); + return; + case 131: + C253.m(context, binder); + return; + case 132: + C254.m(context, binder); + return; + case 133: + C255.m(context, binder); + return; + case 134: + C256.m(context, binder); + return; + case 135: + C257.m(context, binder); + return; + case 136: + C258.m(context, binder); + return; + case 137: + C259.m(context, binder); + return; + case 138: + C260.m(context, binder); + return; + case 139: + C261.m(context, binder); + return; + case 140: + C262.m(context, binder); + return; + case 141: + C263.m(context, binder); + return; + case 142: + C264.m(context, binder); + return; + case 143: + C265.m(context, binder); + return; + case 144: + C266.m(context, binder); + C267.m(context, binder); + return; + case 145: + C268.m(context, binder); + return; + case 146: + C269.m(context, binder); + return; + case 147: + C270.m(context, binder); + return; + case 148: + C271.m(context, binder); + return; + case 149: + C272.m(context, binder); + return; + case 150: + C273.m(context, binder); + return; + case 151: + C274.m(context, binder); + return; + case 152: + C275.m(context, binder); + return; + case 153: + C276.m(context, binder); + return; + case 154: + C277.m(context, binder); + return; + case 155: + C278.m(context, binder); + return; + case 156: + C279.m(context, binder); + return; + case 157: + C280.m(context, binder); + return; + case 158: + C281.m(context, binder); + return; + case 159: + C282.m(context, binder); + return; + case 160: + C283.m(context, binder); + return; + case 161: + C284.m(context, binder); + return; + case 162: + C285.m(context, binder); + return; + case 163: + C286.m(context, binder); + return; + case 164: + C287.m(context, binder); + return; + case 165: + C288.m(context, binder); + return; + case 166: + C289.m(context, binder); + return; + case 167: + C290.m(context, binder); + return; + case 168: + C291.m(context, binder); + C292.m(context, binder); + C293.m(context, binder); + C294.m(context, binder); + C295.m(context, binder); + C296.m(context, binder); + C297.m(context, binder); + return; + case 169: + C298.m(context, binder); + return; + case 170: + C299.m(context, binder); + return; + case 171: + C300.m(context, binder); + return; + case 172: + C301.m(context, binder); + return; + case 173: + C302.m(context, binder); + return; + case 174: + C303.m(context, binder); + return; + case 175: + C304.m(context, binder); + return; + case 176: + C305.m(context, binder); + return; + case 177: + C306.m(context, binder); + return; + case 178: + C307.m(context, binder); + return; + case 179: + C308.m(context, binder); + return; + case 180: + C309.m(context, binder); + return; + case 181: + C310.m(context, binder); + return; + case 182: + C311.m(context, binder); + return; + case 183: + C312.m(context, binder); + return; + case 184: + C313.m(context, binder); + return; + case 185: + C314.m(context, binder); + return; + case 186: + C315.m(context, binder); + return; + case 187: + C316.m(context, binder); + return; + case 188: + C317.m(context, binder); + return; + case 189: + C318.m(context, binder); + return; + case 190: + C319.m(context, binder); + return; + case 191: + C320.m(context, binder); + return; + case 192: + C321.m(context, binder); + return; + case 193: + C322.m(context, binder); + return; + case 194: + C323.m(context, binder); + C324.m(context, binder); + C325.m(context, binder); + return; + case 195: + C326.m(context, binder); + return; + case 196: + C327.m(context, binder); + return; + case 197: + C328.m(context, binder); + return; + case 198: + C329.m(context, binder); + return; + case 199: + C330.m(context, binder); + return; + case 200: + C331.m(context, binder); + return; + case 201: + C332.m(context, binder); + return; + case 202: + C333.m(context, binder); + return; + case 203: + C334.m(context, binder); + C335.m(context, binder); + C336.m(context, binder); + C337.m(context, binder); + C338.m(context, binder); + C339.m(context, binder); + C340.m(context, binder); + C341.m(context, binder); + C342.m(context, binder); + C343.m(context, binder); + C344.m(context, binder); + C345.m(context, binder); + return; + case 204: + C346.m(context, binder); + return; + case 205: + C347.m(context, binder); + return; + case 206: + C348.m(context, binder); + return; + case 207: + C349.m(context, binder); + return; + case 208: + C350.m(context, binder); + return; + case 209: + C351.m(context, binder); + return; + case 210: + C352.m(context, binder); + C353.m(context, binder); + return; + case 211: + C354.m(context, binder); + return; + case 212: + C355.m(context, binder); + C356.m(context, binder); + C357.m(context, binder); + C358.m(context, binder); + C359.m(context, binder); + C360.m(context, binder); + C361.m(context, binder); + C362.m(context, binder); + C363.m(context, binder); + C364.m(context, binder); + C365.m(context, binder); + C366.m(context, binder); + C367.m(context, binder); + C368.m(context, binder); + C369.m(context, binder); + C370.m(context, binder); + C371.m(context, binder); + return; + case 213: + C372.m(context, binder); + return; + case 214: + C373.m(context, binder); + return; + case 215: + C374.m(context, binder); + return; + case 216: + C375.m(context, binder); + C376.m(context, binder); + C377.m(context, binder); + C378.m(context, binder); + C379.m(context, binder); + C380.m(context, binder); + C381.m(context, binder); + C382.m(context, binder); + return; + case 217: + C383.m(context, binder); + return; + case 218: + C384.m(context, binder); + return; + case 219: + C385.m(context, binder); + return; + case 220: + C386.m(context, binder); + return; + case 221: + C387.m(context, binder); + return; + case 222: + C388.m(context, binder); + return; + case 223: + C389.m(context, binder); + return; + case 224: + C390.m(context, binder); + return; + case 225: + C391.m(context, binder); + return; + case 226: + C392.m(context, binder); + return; + case 227: + C393.m(context, binder); + C394.m(context, binder); + return; + case 228: + C395.m(context, binder); + return; + case 229: + C396.m(context, binder); + return; + case 230: + C397.m(context, binder); + return; + case 231: + C398.m(context, binder); + return; + case 232: + C399.m(context, binder); + return; + case 233: + C400.m(context, binder); + return; + case 234: + C401.m(context, binder); + return; + case 235: + C402.m(context, binder); + return; + case 236: + C403.m(context, binder); + return; + case 237: + C404.m(context, binder); + return; + case 238: + C405.m(context, binder); + return; + case 239: + C406.m(context, binder); + return; + case 240: + C407.m(context, binder); + return; + case 241: + C408.m(context, binder); + return; + case 242: + C409.m(context, binder); + return; + case 243: + C410.m(context, binder); + return; + case 244: + C411.m(context, binder); + return; + case 245: + C412.m(context, binder); + return; + case 246: + C413.m(context, binder); + return; + case 247: + C414.m(context, binder); + return; + case 248: + C415.m(context, binder); + return; + case 249: + C416.m(context, binder); + return; + case 250: + C417.m(context, binder); + return; + case 251: + C418.m(context, binder); + return; + case 252: + C419.m(context, binder); + return; + case 253: + C420.m(context, binder); + return; + case 254: + C421.m(context, binder); + return; + case 255: + C422.m(context, binder); + return; + case 256: + C423.m(context, binder); + return; + case 257: + C424.m(context, binder); + return; + case 258: + C425.m(context, binder); + return; + case 259: + C426.m(context, binder); + return; + case 260: + C427.m(context, binder); + return; + case 261: + C428.m(context, binder); + return; + case 262: + C429.m(context, binder); + return; + case 263: + C430.m(context, binder); + return; + case 264: + C431.m(context, binder); + return; + case 265: + C432.m(context, binder); + return; + case 266: + C433.m(context, binder); + return; + case 267: + C434.m(context, binder); + C435.m(context, binder); + C436.m(context, binder); + C437.m(context, binder); + return; + case 268: + C438.m(context, binder); + return; + case 269: + C439.m(context, binder); + return; + case 270: + C440.m(context, binder); + return; + case 271: + C441.m(context, binder); + return; + case 272: + C442.m(context, binder); + return; + case 273: + C443.m(context, binder); + return; + case 274: + C444.m(context, binder); + return; + case 275: + C445.m(context, binder); + return; + case 276: + C446.m(context, binder); + return; + case 277: + C447.m(context, binder); + return; + case 278: + C448.m(context, binder); + return; + case 279: + C449.m(context, binder); + return; + case 280: + C450.m(context, binder); + return; + case 281: + C451.m(context, binder); + return; + case 282: + C452.m(context, binder); + return; + case 283: + C453.m(context, binder); + return; + case 284: + C454.m(context, binder); + return; + case 285: + C455.m(context, binder); + return; + case 286: + C456.m(context, binder); + return; + case 287: + C457.m(context, binder); + return; + case 288: + C458.m(context, binder); + return; + case 289: + C459.m(context, binder); + return; + case 290: + C460.m(context, binder); + return; + case 291: + C461.m(context, binder); + return; + case 292: + C462.m(context, binder); + return; + case 293: + C463.m(context, binder); + return; + case 294: + C464.m(context, binder); + return; + case 295: + C465.m(context, binder); + return; + case 296: + C466.m(context, binder); + return; + case 297: + C467.m(context, binder); + return; + case 298: + C468.m(context, binder); + return; + case 299: + C469.m(context, binder); + return; + case 300: + C470.m(context, binder); + return; + case 301: + C471.m(context, binder); + return; + case 302: + C472.m(context, binder); + return; + case 303: + C473.m(context, binder); + return; + case 304: + C474.m(context, binder); + return; + case 305: + C475.m(context, binder); + return; + case 306: + C476.m(context, binder); + return; + case 307: + C477.m(context, binder); + return; + case 308: + C478.m(context, binder); + return; + case 309: + C479.m(context, binder); + return; + case 310: + C480.m(context, binder); + return; + case 311: + C481.m(context, binder); + return; + case 312: + C482.m(context, binder); + return; + case 313: + C483.m(context, binder); + return; + case 314: + C484.m(context, binder); + return; + case 315: + C485.m(context, binder); + return; + case 316: + C486.m(context, binder); + return; + case 317: + C487.m(context, binder); + return; + case 318: + C488.m(context, binder); + return; + case 319: + C489.m(context, binder); + return; + case 320: + C490.m(context, binder); + return; + case 321: + C491.m(context, binder); + C492.m(context, binder); + C493.m(context, binder); + C494.m(context, binder); + C495.m(context, binder); + C496.m(context, binder); + C497.m(context, binder); + C498.m(context, binder); + return; + case 322: + C499.m(context, binder); + return; + case 323: + C500.m(context, binder); + return; + case 324: + C501.m(context, binder); + return; + case 325: + C502.m(context, binder); + return; + case 326: + C503.m(context, binder); + return; + case 327: + C504.m(context, binder); + return; + case 328: + C505.m(context, binder); + return; + case 329: + C506.m(context, binder); + return; + case 330: + C507.m(context, binder); + return; + case 331: + C508.m(context, binder); + return; + case 332: + C509.m(context, binder); + return; + case 333: + C510.m(context, binder); + return; + case 334: + C511.m(context, binder); + return; + case 335: + C512.m(context, binder); + return; + case 336: + C513.m(context, binder); + return; + case 337: + C514.m(context, binder); + return; + case 338: + C515.m(context, binder); + return; + case 339: + C516.m(context, binder); + return; + case 340: + C517.m(context, binder); + return; + case 341: + C518.m(context, binder); + return; + case 342: + C519.m(context, binder); + return; + case 343: + C520.m(context, binder); + return; + case 344: + C255.mFactory(context, binder); + return; + case 345: + C522.m(context, binder); + return; + case 346: + C523.m(context, binder); + return; + case 347: + C524.m(context, binder); + return; + case 348: + C525.m(context, binder); + return; + case 349: + C526.m(context, binder); + return; + case 350: + C527.m(context, binder); + return; + case 351: + C528.m(context, binder); + return; + case 352: + C529.m(context, binder); + return; + case 353: + C530.m(context, binder); + return; + case 354: + C531.m(context, binder); + return; + case 355: + C532.m(context, binder); + return; + case 356: + C533.m(context, binder); + return; + case 357: + C534.m(context, binder); + return; + case 358: + C535.m(context, binder); + return; + case 359: + C536.m(context, binder); + return; + case 360: + C537.m(context, binder); + return; + case 361: + C538.m(context, binder); + return; + case 362: + C539.m(context, binder); + return; + case 363: + C540.m(context, binder); + return; + case 364: + C541.m(context, binder); + return; + case 365: + C542.m(context, binder); + return; + case 366: + C543.m(context, binder); + return; + case 367: + C544.m(context, binder); + C545.m(context, binder); + return; + case 368: + C546.m(context, binder); + return; + case 369: + C547.m(context, binder); + return; + case 370: + C548.m(context, binder); + return; + case 371: + C549.m(context, binder); + return; + case 372: + C550.m(context, binder); + return; + case 373: + C551.m(context, binder); + return; + case 374: + C552.m(context, binder); + return; + case 375: + C553.m(context, binder); + return; + case 376: + C554.m(context, binder); + return; + case 377: + C555.m(context, binder); + return; + case 378: + C556.m(context, binder); + return; + case 379: + C557.m(context, binder); + return; + case 380: + C5.mImpl(context, binder); + return; + case 381: + C559.m(context, binder); + return; + case 382: + C560.m(context, binder); + return; + case 383: + C561.m(context, binder); + return; + case 384: + C562.m(context, binder); + return; + case 385: + C563.m(context, binder); + return; + case 386: + C564.m(context, binder); + return; + case 387: + C565.m(context, binder); + return; + case 388: + C566.m(context, binder); + return; + case 389: + C567.m(context, binder); + return; + case 390: + C568.m(context, binder); + return; + case 391: + C569.m(context, binder); + return; + case 392: + C570.m(context, binder); + return; + case 393: + C571.m(context, binder); + return; + case 394: + C572.m(context, binder); + return; + case 395: + C573.m(context, binder); + return; + case 396: + C574.m(context, binder); + return; + case 397: + C575.m(context, binder); + return; + case 398: + C576.m(context, binder); + return; + case 399: + C577.m(context, binder); + return; + case 400: + C578.m(context, binder); + return; + case 401: + C579.m(context, binder); + return; + case 402: + C580.m(context, binder); + return; + case 403: + C581.m(context, binder); + return; + case 404: + C582.m(context, binder); + return; + case 405: + C583.m(context, binder); + return; + case 406: + C584.m(context, binder); + C585.m(context, binder); + C586.m(context, binder); + C587.m(context, binder); + C588.m(context, binder); + C589.m(context, binder); + C590.m(context, binder); + C591.m(context, binder); + C592.m(context, binder); + C593.m(context, binder); + C594.m(context, binder); + return; + case 407: + C595.m(context, binder); + return; + case 408: + C596.m(context, binder); + return; + case 409: + C597.m(context, binder); + C598.m(context, binder); + return; + case 410: + C599.m(context, binder); + return; + case 411: + C600.m(context, binder); + return; + case 412: + C601.m(context, binder); + return; + case 413: + C602.m(context, binder); + return; + case 414: + C603.m(context, binder); + return; + case 415: + C604.m(context, binder); + return; + case 416: + C605.m(context, binder); + return; + case 417: + C606.m(context, binder); + return; + case 418: + C607.m(context, binder); + return; + case 419: + C608.m(context, binder); + return; + case 420: + C609.m(context, binder); + return; + case 421: + C610.m(context, binder); + return; + case 422: + C611.m(context, binder); + return; + case 423: + C612.m(context, binder); + return; + case 424: + C613.m(context, binder); + return; + case 425: + C614.m(context, binder); + C615.m(context, binder); + C616.m(context, binder); + return; + case 426: + C617.m(context, binder); + return; + case 427: + C618.m(context, binder); + return; + case 428: + C619.m(context, binder); + return; + case 429: + C620.m(context, binder); + return; + case 430: + C621.m(context, binder); + return; + case 431: + C622.m(context, binder); + return; + case 432: + C623.m(context, binder); + return; + case 433: + C624.m(context, binder); + return; + case 434: + C625.m(context, binder); + return; + case 435: + C626.m(context, binder); + return; + case 436: + C627.m(context, binder); + return; + case 437: + C628.m(context, binder); + return; + case 438: + C629.m(context, binder); + return; + case 439: + C630.m(context, binder); + return; + case 440: + C631.m(context, binder); + return; + case 441: + C632.m(context, binder); + return; + case 442: + C633.m(context, binder); + return; + case 443: + C634.m(context, binder); + return; + case 444: + C635.m(context, binder); + return; + case 445: + C636.m(context, binder); + return; + case 446: + C637.m(context, binder); + return; + case 447: + C638.m(context, binder); + return; + case 448: + C639.m(context, binder); + return; + case 449: + C640.m(context, binder); + return; + case 450: + C641.m(context, binder); + return; + case 451: + C642.m(context, binder); + return; + case 452: + C643.m(context, binder); + return; + case 453: + C644.m(context, binder); + return; + case 454: + C645.m(context, binder); + return; + case 455: + C646.m(context, binder); + return; + case 456: + C647.m(context, binder); + return; + case 457: + C648.m(context, binder); + return; + case 458: + C649.m(context, binder); + return; + case 459: + C650.m(context, binder); + return; + case 460: + C651.m(context, binder); + return; + case 461: + C652.m(context, binder); + return; + case 462: + C653.m(context, binder); + return; + case 463: + C654.m(context, binder); + return; + case 464: + C655.m(context, binder); + return; + case 465: + C656.m(context, binder); + return; + case 466: + C657.m(context, binder); + return; + case 467: + C658.m(context, binder); + return; + case 468: + C659.m(context, binder); + return; + case 469: + C660.m(context, binder); + return; + case 470: + C661.m(context, binder); + return; + case 471: + C90.mReport_Factory(context, binder); + return; + case 472: + C663.m(context, binder); + return; + case 473: + C664.m(context, binder); + return; + case 474: + C665.m(context, binder); + return; + case 475: + C666.m(context, binder); + return; + case 476: + C667.m(context, binder); + return; + case 477: + C668.m(context, binder); + return; + case 478: + C105.m_InMemoryScanner(context, binder); + return; + case 479: + C670.m(context, binder); + C671.m(context, binder); + return; + case 480: + C672.m(context, binder); + return; + case 481: + C673.m(context, binder); + return; + case 482: + C674.m(context, binder); + return; + case 483: + C675.m(context, binder); + return; + case 484: + C676.m(context, binder); + return; + case 485: + C677.m(context, binder); + return; + case 486: + C678.m(context, binder); + return; + case 487: + C679.m(context, binder); + C680.m(context, binder); + C681.m(context, binder); + C682.m(context, binder); + C683.m(context, binder); + C684.m(context, binder); + C685.m(context, binder); + return; + case 488: + C686.m(context, binder); + return; + case 489: + C687.m(context, binder); + return; + case 490: + C688.m(context, binder); + return; + case 491: + C689.m(context, binder); + return; + case 492: + C690.m(context, binder); + return; + case 493: + C691.m(context, binder); + C692.m(context, binder); + C693.m(context, binder); + C694.m(context, binder); + C695.m(context, binder); + C696.m(context, binder); + C697.m(context, binder); + C698.m(context, binder); + C699.m(context, binder); + C700.m(context, binder); + C701.m(context, binder); + C702.m(context, binder); + C703.m(context, binder); + C704.m(context, binder); + C705.m(context, binder); + C706.m(context, binder); + C707.m(context, binder); + C708.m(context, binder); + C709.m(context, binder); + C710.m(context, binder); + C711.m(context, binder); + C712.m(context, binder); + C713.m(context, binder); + C714.m(context, binder); + C715.m(context, binder); + C716.m(context, binder); + C717.m(context, binder); + C718.m(context, binder); + C719.m(context, binder); + C720.m(context, binder); + return; + case 494: + C721.m(context, binder); + C722.m(context, binder); + C723.m(context, binder); + return; + case 495: + C724.m(context, binder); + return; + case 496: + C725.m(context, binder); + return; + case 497: + C726.m(context, binder); + return; + case 498: + C727.m(context, binder); + return; + case 499: + C728.m(context, binder); + return; + case 500: + C729.m(context, binder); + return; + case 501: + C730.m(context, binder); + return; + case 502: + C731.m(context, binder); + return; + case 503: + C732.m(context, binder); + return; + case 504: + C733.m(context, binder); + return; + case 505: + C734.m(context, binder); + return; + case 506: + C735.m(context, binder); + return; + case 507: + C736.m(context, binder); + return; + case 508: + C737.m(context, binder); + return; + case 509: + C738.m(context, binder); + return; + case 510: + C739.m(context, binder); + return; + case 511: + C740.m(context, binder); + return; + case 512: + C741.m(context, binder); + return; + case 513: + C742.m(context, binder); + return; + case 514: + C743.m(context, binder); + return; + case 515: + C744.m(context, binder); + return; + case 516: + C745.m(context, binder); + return; + case 517: + C746.m(context, binder); + return; + case 518: + C747.m(context, binder); + return; + case 519: + C748.m(context, binder); + return; + case 520: + C749.m(context, binder); + return; + case 521: + C750.m(context, binder); + C751.m(context, binder); + C752.m(context, binder); + C753.m(context, binder); + C754.m(context, binder); + C755.m(context, binder); + C756.m(context, binder); + C757.m(context, binder); + C758.m(context, binder); + C759.m(context, binder); + C760.m(context, binder); + C761.m(context, binder); + return; + case 522: + C762.m(context, binder); + return; + case 523: + C763.m(context, binder); + return; + case 524: + C764.m(context, binder); + return; + case 525: + C765.m(context, binder); + return; + case 526: + C766.m(context, binder); + return; + case 527: + C767.m(context, binder); + return; + case 528: + C768.m(context, binder); + return; + case 529: + C769.m(context, binder); + return; + case 530: + C770.m(context, binder); + return; + case 531: + C771.m(context, binder); + return; + case 532: + C772.m(context, binder); + return; + case 533: + C773.m(context, binder); + return; + case 534: + C774.m(context, binder); + return; + case 535: + C775.m(context, binder); + return; + case 536: + C776.m(context, binder); + return; + case 537: + C777.m(context, binder); + return; + case 538: + C778.m(context, binder); + return; + case 539: + C779.m(context, binder); + return; + case 540: + C780.m(context, binder); + return; + case 541: + C781.m(context, binder); + return; + case 542: + C90.mApi(context, binder); + return; + case 543: + C783.m(context, binder); + return; + case 544: + C784.m(context, binder); + return; + case 545: + C785.m(context, binder); + return; + case 546: + C786.m(context, binder); + return; + case 547: + C787.m(context, binder); + return; + case 548: + C788.m(context, binder); + return; + case 549: + C789.m(context, binder); + return; + case 550: + C790.m(context, binder); + return; + case 551: + C791.m(context, binder); + return; + case 552: + C792.m(context, binder); + return; + case 553: + C793.m(context, binder); + return; + case 554: + C794.m(context, binder); + return; + case 555: + C795.m(context, binder); + C796.m(context, binder); + return; + case 556: + C797.m(context, binder); + return; + case 557: + C798.m(context, binder); + return; + case 558: + C799.m(context, binder); + return; + case 559: + C800.m(context, binder); + return; + case 560: + C801.m(context, binder); + return; + case 561: + C802.m(context, binder); + return; + case 562: + C803.m(context, binder); + C804.m(context, binder); + C805.m(context, binder); + C806.m(context, binder); + return; + case 563: + C807.m(context, binder); + return; + case 564: + C808.m(context, binder); + return; + case 565: + C809.m(context, binder); + return; + case 566: + C810.m(context, binder); + return; + case 567: + C811.m(context, binder); + return; + case 568: + C812.m(context, binder); + return; + case 569: + C813.m(context, binder); + C814.m(context, binder); + C815.m(context, binder); + C816.m(context, binder); + C817.m(context, binder); + C818.m(context, binder); + C819.m(context, binder); + C820.m(context, binder); + C821.m(context, binder); + return; + case 570: + C822.m(context, binder); + C823.m(context, binder); + return; + case 571: + C824.m(context, binder); + return; + case 572: + C825.m(context, binder); + return; + case 573: + C826.m(context, binder); + return; + case 574: + C827.m(context, binder); + return; + case 575: + C828.m(context, binder); + return; + case 576: + C829.m(context, binder); + return; + case 577: + C830.m(context, binder); + return; + case 578: + C831.m(context, binder); + return; + case 579: + C832.m(context, binder); + return; + case 580: + C833.m(context, binder); + return; + case 581: + C834.m(context, binder); + return; + case 582: + C835.m(context, binder); + return; + case 583: + C836.m(context, binder); + return; + case 584: + C837.m(context, binder); + return; + case 585: + C838.m(context, binder); + return; + case 586: + C105.m_Scanner(context, binder); + C840.m(context, binder); + return; + case 587: + C94.mImpl(context, binder); + return; + case 588: + C842.m(context, binder); + C843.m(context, binder); + C844.m(context, binder); + C845.m(context, binder); + return; + case 589: + C846.m(context, binder); + return; + case 590: + C847.m(context, binder); + return; + case 591: + C848.m(context, binder); + return; + case 592: + C849.m(context, binder); + return; + case 593: + C850.m(context, binder); + return; + case 594: + C851.m(context, binder); + return; + case 595: + C852.m(context, binder); + return; + case 596: + C853.m(context, binder); + return; + case 597: + C854.m(context, binder); + return; + case 598: + C855.m(context, binder); + return; + case 599: + C856.m(context, binder); + return; + case 600: + C857.m(context, binder); + return; + case 601: + C858.m(context, binder); + return; + case 602: + C859.m(context, binder); + return; + case 603: + C860.m(context, binder); + return; + case 604: + C861.m(context, binder); + return; + case 605: + C862.m(context, binder); + return; + case 606: + C863.m(context, binder); + return; + case 607: + C864.m(context, binder); + return; + case 608: + C865.m(context, binder); + return; + case 609: + C866.m(context, binder); + return; + case 610: + C867.m(context, binder); + return; + case 611: + C868.m(context, binder); + return; + case 612: + C869.m(context, binder); + return; + case 613: + C870.m(context, binder); + return; + case 614: + C871.m(context, binder); + return; + case 615: + C872.m(context, binder); + return; + case 616: + C873.m(context, binder); + return; + case 617: + C874.m(context, binder); + return; + case 618: + C875.m(context, binder); + return; + case 619: + C876.m(context, binder); + return; + case 620: + C877.m(context, binder); + return; + case 621: + C878.m(context, binder); + return; + case 622: + C879.m(context, binder); + C880.m(context, binder); + return; + case 623: + C881.m(context, binder); + return; + case 624: + C882.m(context, binder); + return; + case 625: + C883.m(context, binder); + return; + case 626: + C884.m(context, binder); + C885.m(context, binder); + C886.m(context, binder); + C887.m(context, binder); + C888.m(context, binder); + return; + case 627: + C889.m(context, binder); + return; + case 628: + C890.m(context, binder); + return; + case 629: + C891.m(context, binder); + return; + case 630: + C892.m(context, binder); + return; + case 631: + C893.m(context, binder); + return; + case 632: + C894.m(context, binder); + return; + case 633: + C895.m(context, binder); + return; + case 634: + C896.m(context, binder); + return; + case 635: + C897.m(context, binder); + return; + case 636: + C898.m(context, binder); + return; + case 637: + C899.m(context, binder); + return; + case 638: + C900.m(context, binder); + return; + case 639: + C901.m(context, binder); + return; + case 640: + C870.mImpl(context, binder); + return; + case 641: + C903.m(context, binder); + return; + case 642: + C904.m(context, binder); + return; + case 643: + C905.m(context, binder); + return; + case 644: + C906.m(context, binder); + C907.m(context, binder); + C908.m(context, binder); + C909.m(context, binder); + C910.m(context, binder); + return; + case 645: + C911.m(context, binder); + return; + case 646: + C912.m(context, binder); + C913.m(context, binder); + return; + case 647: + C914.m(context, binder); + return; + case 648: + C915.m(context, binder); + return; + case 649: + C916.m(context, binder); + return; + case 650: + C917.m(context, binder); + C918.m(context, binder); + return; + case 651: + C919.m(context, binder); + return; + case 652: + C920.m(context, binder); + return; + case 653: + C921.m(context, binder); + return; + case 654: + C922.m(context, binder); + return; + case 655: + C923.m(context, binder); + C924.m(context, binder); + C925.m(context, binder); + C926.m(context, binder); + C927.m(context, binder); + C928.m(context, binder); + C929.m(context, binder); + C930.m(context, binder); + C931.m(context, binder); + C932.m(context, binder); + C933.m(context, binder); + C934.m(context, binder); + C935.m(context, binder); + return; + case 656: + C936.m(context, binder); + return; + case 657: + C937.m(context, binder); + return; + case 658: + C938.m(context, binder); + return; + case 659: + C939.m(context, binder); + return; + case 660: + C940.m(context, binder); + return; + case 661: + C941.m(context, binder); + return; + case 662: + C942.m(context, binder); + return; + case 663: + C943.m(context, binder); + return; + case 664: + C944.m(context, binder); + return; + case 665: + C945.m(context, binder); + return; + case 666: + C946.m(context, binder); + return; + case 667: + C947.m(context, binder); + return; + case 668: + C948.m(context, binder); + return; + case 669: + C949.m(context, binder); + return; + case 670: + C950.m(context, binder); + return; + case 671: + C951.m(context, binder); + return; + case 672: + C952.m(context, binder); + C953.m(context, binder); + C954.m(context, binder); + C955.m(context, binder); + return; + case 673: + C956.m(context, binder); + return; + case 674: + C957.m(context, binder); + C958.m(context, binder); + return; + case 675: + C959.m(context, binder); + return; + case 676: + C960.m(context, binder); + C961.m(context, binder); + return; + case 677: + C962.m(context, binder); + return; + case 678: + C963.m(context, binder); + return; + case 679: + C964.m(context, binder); + return; + case 680: + C965.m(context, binder); + return; + case 681: + C966.m(context, binder); + return; + case 682: + C967.m(context, binder); + return; + case 683: + C968.m(context, binder); + return; + case 684: + C969.m(context, binder); + return; + case 685: + C970.m(context, binder); + return; + case 686: + C971.m(context, binder); + C972.m(context, binder); + return; + case 687: + C973.m(context, binder); + return; + case 688: + C974.m(context, binder); + return; + case 689: + C975.m(context, binder); + return; + case 690: + C976.m(context, binder); + return; + case 691: + C977.m(context, binder); + return; + case 692: + C978.m(context, binder); + return; + case 693: + C979.m(context, binder); + return; + case 694: + C980.m(context, binder); + return; + case 695: + C981.m(context, binder); + return; + case 696: + C982.m(context, binder); + return; + case 697: + C983.m(context, binder); + C984.m(context, binder); + C985.m(context, binder); + return; + case 698: + C986.m(context, binder); + return; + case 699: + C987.m(context, binder); + return; + case 700: + C988.m(context, binder); + return; + case 701: + C989.m(context, binder); + return; + case 702: + C990.m(context, binder); + return; + case 703: + C991.m(context, binder); + return; + case 704: + C992.m(context, binder); + return; + case 705: + C993.m(context, binder); + return; + case 706: + C994.m(context, binder); + return; + case 707: + C995.m(context, binder); + return; + case 708: + C996.m(context, binder); + return; + case 709: + C997.m(context, binder); + return; + case 710: + C998.m(context, binder); + return; + case 711: + C999.m(context, binder); + return; + case 712: + C1000.m(context, binder); + return; + case 713: + C1001.m(context, binder); + return; + case 714: + C1002.m(context, binder); + return; + case 715: + C1003.m(context, binder); + return; + case 716: + C1004.m(context, binder); + return; + case 717: + C1005.m(context, binder); + return; + case 718: + C1006.m(context, binder); + return; + case 719: + C1007.m(context, binder); + return; + case 720: + C1008.m(context, binder); + return; + case 721: + C1009.m(context, binder); + return; + case 722: + C1010.m(context, binder); + return; + case 723: + C1011.m(context, binder); + return; + case 724: + C1012.m(context, binder); + return; + case 725: + C1013.m(context, binder); + return; + case 726: + C1014.m(context, binder); + return; + case 727: + C1015.m(context, binder); + return; + case 728: + C577.mDebug(context, binder); + return; + case 729: + C1017.m(context, binder); + return; + case 730: + C1018.m(context, binder); + return; + case 731: + C1019.m(context, binder); + return; + case 732: + C1020.m(context, binder); + return; + case 733: + C1021.m(context, binder); + return; + case 734: + C1022.m(context, binder); + return; + case 735: + C1023.m(context, binder); + return; + case 736: + C1024.m(context, binder); + return; + case 737: + C1025.m(context, binder); + return; + case 738: + C1026.m(context, binder); + return; + case 739: + C1027.m(context, binder); + return; + case 740: + C1028.m(context, binder); + return; + case 741: + C1029.m(context, binder); + return; + case 742: + C1030.m(context, binder); + return; + case 743: + C1031.m(context, binder); + return; + case 744: + C1032.m(context, binder); + return; + case 745: + C1033.m(context, binder); + return; + case 746: + C1034.m(context, binder); + return; + case 747: + C1035.m(context, binder); + return; + case 748: + C1036.m(context, binder); + C1037.m(context, binder); + return; + case 749: + C1038.m(context, binder); + C1039.m(context, binder); + C1040.m(context, binder); + C1041.m(context, binder); + return; + case 750: + C1042.m(context, binder); + return; + case 751: + C1043.m(context, binder); + return; + case 752: + C1044.m(context, binder); + return; + case 753: + C1045.m(context, binder); + return; + case 754: + C1046.m(context, binder); + return; + case 755: + C1047.m(context, binder); + return; + case 756: + C848.mMap(context, binder); + return; + case 757: + C1049.m(context, binder); + return; + case 758: + C1050.m(context, binder); + return; + case 759: + C1051.m(context, binder); + return; + case 760: + C1052.m(context, binder); + return; + case 761: + C1053.m(context, binder); + return; + case 762: + C1054.m(context, binder); + return; + case 763: + C1055.m(context, binder); + return; + case 764: + C1056.m(context, binder); + return; + case 765: + C1057.m(context, binder); + return; + case 766: + C1058.m(context, binder); + return; + case 767: + C1059.m(context, binder); + return; + case 768: + C1060.m(context, binder); + return; + case 769: + C1061.m(context, binder); + return; + case 770: + C1062.m(context, binder); + return; + case 771: + C1063.m(context, binder); + return; + case 772: + C1064.m(context, binder); + return; + case 773: + C1065.m(context, binder); + return; + case 774: + C1066.m(context, binder); + return; + case 775: + C1067.m(context, binder); + return; + case 776: + C1068.m(context, binder); + return; + case 777: + C1069.m(context, binder); + return; + case 778: + C1070.m(context, binder); + return; + case 779: + C1071.m(context, binder); + return; + case 780: + C1072.m(context, binder); + return; + case 781: + C1073.m(context, binder); + C1074.m(context, binder); + C1075.m(context, binder); + C1076.m(context, binder); + C1077.m(context, binder); + C1078.m(context, binder); + C1079.m(context, binder); + C1080.m(context, binder); + return; + case 782: + C1081.m(context, binder); + return; + case 783: + C1082.m(context, binder); + return; + case 784: + C1083.m(context, binder); + return; + case 785: + C1084.m(context, binder); + return; + case 786: + C1085.m(context, binder); + return; + case 787: + C1086.m(context, binder); + return; + case 788: + C1087.m(context, binder); + return; + case 789: + C1088.m(context, binder); + return; + case 790: + C944.mManager(context, binder); + return; + case 791: + C1090.m(context, binder); + return; + case 792: + C1091.m(context, binder); + return; + case 793: + C1092.m(context, binder); + return; + case 794: + C1093.m(context, binder); + return; + case 795: + C1094.m(context, binder); + return; + case 796: + C1095.m(context, binder); + return; + case 797: + C1096.m(context, binder); + return; + case 798: + C1097.m(context, binder); + return; + case 799: + C1098.m(context, binder); + return; + case 800: + C1099.m(context, binder); + return; + case 801: + C1100.m(context, binder); + return; + case 802: + C1101.m(context, binder); + C1102.m(context, binder); + C1103.m(context, binder); + C1104.m(context, binder); + C1105.m(context, binder); + C1106.m(context, binder); + C1107.m(context, binder); + C1108.m(context, binder); + return; + case 803: + C1109.m(context, binder); + return; + case 804: + C1110.m(context, binder); + return; + case 805: + C1111.m(context, binder); + return; + case 806: + C1112.m(context, binder); + return; + case 807: + C1113.m(context, binder); + return; + case 808: + C1114.m(context, binder); + C1115.m(context, binder); + C1116.m(context, binder); + C1117.m(context, binder); + return; + case 809: + C1118.m(context, binder); + return; + case 810: + C1119.m(context, binder); + C1120.m(context, binder); + C1121.m(context, binder); + return; + case 811: + C1122.m(context, binder); + return; + case 812: + C1123.m(context, binder); + return; + case 813: + C1124.m(context, binder); + return; + case 814: + C1125.m(context, binder); + return; + case 815: + C1126.m(context, binder); + return; + case 816: + C1127.m(context, binder); + return; + case 817: + C1128.m(context, binder); + return; + case 818: + C1129.m(context, binder); + return; + case 819: + C1130.m(context, binder); + return; + case 820: + C1131.m(context, binder); + return; + case 821: + C1132.m(context, binder); + return; + case 822: + C1133.m(context, binder); + return; + case 823: + C1134.m(context, binder); + return; + case 824: + C1135.m(context, binder); + return; + case 825: + C1136.m(context, binder); + return; + case 826: + C1137.m(context, binder); + C1138.m(context, binder); + C1139.m(context, binder); + C1140.m(context, binder); + C1141.m(context, binder); + C1142.m(context, binder); + C1143.m(context, binder); + C1144.m(context, binder); + C1145.m(context, binder); + C1146.m(context, binder); + C1147.m(context, binder); + C1148.m(context, binder); + C1149.m(context, binder); + C1150.m(context, binder); + C1151.m(context, binder); + C1152.m(context, binder); + C1153.m(context, binder); + C1154.m(context, binder); + C1155.m(context, binder); + C1156.m(context, binder); + C1157.m(context, binder); + C1158.m(context, binder); + return; + case 827: + C1159.m(context, binder); + return; + case 828: + C457.mMap(context, binder); + return; + case 829: + C1161.m(context, binder); + C1162.m(context, binder); + C1163.m(context, binder); + C1164.m(context, binder); + C1165.m(context, binder); + C1166.m(context, binder); + C1167.m(context, binder); + C1168.m(context, binder); + C1169.m(context, binder); + C1170.m(context, binder); + C1171.m(context, binder); + C1172.m(context, binder); + C1173.m(context, binder); + C1174.m(context, binder); + C1175.m(context, binder); + C1176.m(context, binder); + C1177.m(context, binder); + C1178.m(context, binder); + C1179.m(context, binder); + C1180.m(context, binder); + C1181.m(context, binder); + C1181a.m(context, binder); + C1181b.m(context, binder); + return; + case 830: + C1184.m(context, binder); + return; + case 831: + C1185.m(context, binder); + return; + case 832: + C1186.m(context, binder); + return; + case 833: + C1187.m(context, binder); + return; + case 834: + C1188.m(context, binder); + return; + case 835: + C1189.m(context, binder); + return; + case 836: + C1190.m(context, binder); + return; + case 837: + C1191.m(context, binder); + return; + case 838: + C1192.m(context, binder); + return; + case 839: + C1193.m(context, binder); + return; + case 840: + C1194.m(context, binder); + return; + case 841: + C1195.m(context, binder); + return; + case 842: + C1196.m(context, binder); + return; + case 843: + C1197.m(context, binder); + return; + case 844: + C1198.m(context, binder); + return; + case 845: + C1199.m(context, binder); + return; + case 846: + C1200.m(context, binder); + return; + case 847: + C1201.m(context, binder); + return; + case 848: + C1202.m(context, binder); + return; + case 849: + C1203.m(context, binder); + return; + case 850: + C1204.m(context, binder); + return; + case 851: + C1205.m(context, binder); + return; + case 852: + C1206.m(context, binder); + return; + case 853: + C1207.m(context, binder); + return; + case 854: + C1208.m(context, binder); + return; + case 855: + C1209.m(context, binder); + return; + case 856: + C1210.m(context, binder); + return; + case 857: + C1211.m(context, binder); + return; + case 858: + C1212.m(context, binder); + return; + default: + return; + } + } + } + + public static void main(String[] args) { + System.out.println("passed"); + } + + static boolean doThrow = false; +} diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt index 2687f85b09..feabb2087e 100644 --- a/test/911-get-stack-trace/expected.txt +++ b/test/911-get-stack-trace/expected.txt @@ -211,52 +211,34 @@ From bottom ### Other threads (suspended) ### ################################ --------- -FinalizerDaemon -<not printed> ---------- -FinalizerWatchdogDaemon -<not printed> ---------- -HeapTaskDaemon -<not printed> ---------- -ReferenceQueueDaemon -<not printed> ---------- -Signal Catcher - ---------- -Thread-10 +AllTraces Thread 0 --------- -Thread-11 +AllTraces Thread 1 --------- -Thread-12 +AllTraces Thread 2 --------- -Thread-13 +AllTraces Thread 3 --------- -Thread-4 +AllTraces Thread 4 --------- -Thread-5 +AllTraces Thread 5 --------- -Thread-6 +AllTraces Thread 6 --------- -Thread-7 +AllTraces Thread 7 --------- -Thread-8 +AllTraces Thread 8 --------- -Thread-9 - ---------- -main +AllTraces Thread 9 --------- FinalizerDaemon @@ -274,7 +256,10 @@ ReferenceQueueDaemon Signal Catcher --------- -Thread-10 +main + +--------- +AllTraces Thread 0 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -282,7 +267,7 @@ Thread-10 foo (IIILControlData;)I 0 19 --------- -Thread-11 +AllTraces Thread 1 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -290,7 +275,7 @@ Thread-11 foo (IIILControlData;)I 0 19 --------- -Thread-12 +AllTraces Thread 2 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -298,7 +283,7 @@ Thread-12 foo (IIILControlData;)I 0 19 --------- -Thread-13 +AllTraces Thread 3 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -306,7 +291,7 @@ Thread-13 foo (IIILControlData;)I 0 19 --------- -Thread-4 +AllTraces Thread 4 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -314,7 +299,7 @@ Thread-4 foo (IIILControlData;)I 0 19 --------- -Thread-5 +AllTraces Thread 5 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -322,7 +307,7 @@ Thread-5 foo (IIILControlData;)I 0 19 --------- -Thread-6 +AllTraces Thread 6 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -330,7 +315,7 @@ Thread-6 foo (IIILControlData;)I 0 19 --------- -Thread-7 +AllTraces Thread 7 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -338,7 +323,7 @@ Thread-7 foo (IIILControlData;)I 0 19 --------- -Thread-8 +AllTraces Thread 8 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -346,7 +331,7 @@ Thread-8 foo (IIILControlData;)I 0 19 --------- -Thread-9 +AllTraces Thread 9 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -354,13 +339,6 @@ Thread-9 foo (IIILControlData;)I 0 19 --------- -main - getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 - printAll (I)V 0 73 - doTest ()V 102 57 - main ([Ljava/lang/String;)V 27 33 - ---------- FinalizerDaemon <not printed> --------- @@ -376,7 +354,14 @@ ReferenceQueueDaemon Signal Catcher --------- -Thread-10 +main + getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 + printAll (I)V 0 73 + doTest ()V 128 57 + main ([Ljava/lang/String;)V 27 33 + +--------- +AllTraces Thread 0 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -397,7 +382,7 @@ Thread-10 run ()V 4 45 --------- -Thread-11 +AllTraces Thread 1 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -418,7 +403,7 @@ Thread-11 run ()V 4 45 --------- -Thread-12 +AllTraces Thread 2 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -439,7 +424,7 @@ Thread-12 run ()V 4 45 --------- -Thread-13 +AllTraces Thread 3 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -460,7 +445,7 @@ Thread-13 run ()V 4 45 --------- -Thread-4 +AllTraces Thread 4 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -481,7 +466,7 @@ Thread-4 run ()V 4 45 --------- -Thread-5 +AllTraces Thread 5 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -502,7 +487,7 @@ Thread-5 run ()V 4 45 --------- -Thread-6 +AllTraces Thread 6 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -523,7 +508,7 @@ Thread-6 run ()V 4 45 --------- -Thread-7 +AllTraces Thread 7 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -544,7 +529,7 @@ Thread-7 run ()V 4 45 --------- -Thread-8 +AllTraces Thread 8 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -565,7 +550,7 @@ Thread-8 run ()V 4 45 --------- -Thread-9 +AllTraces Thread 9 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -586,10 +571,25 @@ Thread-9 run ()V 4 45 --------- +FinalizerDaemon +<not printed> +--------- +FinalizerWatchdogDaemon +<not printed> +--------- +HeapTaskDaemon +<not printed> +--------- +ReferenceQueueDaemon +<not printed> +--------- +Signal Catcher + +--------- main getAllStackTraces (I)[[Ljava/lang/Object; -1 -2 printAll (I)V 0 73 - doTest ()V 107 59 + doTest ()V 133 59 main ([Ljava/lang/String;)V 27 33 @@ -597,25 +597,25 @@ main ### Other select threads (suspended) ### ######################################## --------- -Thread-14 +ThreadListTraces Thread 0 --------- -Thread-16 +ThreadListTraces Thread 2 --------- -Thread-18 +ThreadListTraces Thread 4 --------- -Thread-20 +ThreadListTraces Thread 6 --------- -Thread-22 +ThreadListTraces Thread 8 --------- main --------- -Thread-14 +ThreadListTraces Thread 0 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -623,7 +623,7 @@ Thread-14 foo (IIILControlData;)I 0 19 --------- -Thread-16 +ThreadListTraces Thread 2 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -631,7 +631,7 @@ Thread-16 foo (IIILControlData;)I 0 19 --------- -Thread-18 +ThreadListTraces Thread 4 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -639,7 +639,7 @@ Thread-18 foo (IIILControlData;)I 0 19 --------- -Thread-20 +ThreadListTraces Thread 6 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -647,7 +647,7 @@ Thread-20 foo (IIILControlData;)I 0 19 --------- -Thread-22 +ThreadListTraces Thread 8 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -658,11 +658,11 @@ Thread-22 main getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 66 - doTest ()V 96 52 + doTest ()V 116 52 main ([Ljava/lang/String;)V 35 37 --------- -Thread-14 +ThreadListTraces Thread 0 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -683,7 +683,7 @@ Thread-14 run ()V 4 35 --------- -Thread-16 +ThreadListTraces Thread 2 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -704,7 +704,7 @@ Thread-16 run ()V 4 35 --------- -Thread-18 +ThreadListTraces Thread 4 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -725,7 +725,7 @@ Thread-18 run ()V 4 35 --------- -Thread-20 +ThreadListTraces Thread 6 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -746,7 +746,7 @@ Thread-20 run ()V 4 35 --------- -Thread-22 +ThreadListTraces Thread 8 wait ()V -1 -2 printOrWait (IILControlData;)V 24 45 baz (IIILControlData;)Ljava/lang/Object; 2 30 @@ -770,7 +770,7 @@ Thread-22 main getThreadListStackTraces ([Ljava/lang/Thread;I)[[Ljava/lang/Object; -1 -2 printList ([Ljava/lang/Thread;I)V 0 66 - doTest ()V 101 54 + doTest ()V 121 54 main ([Ljava/lang/String;)V 35 37 diff --git a/test/911-get-stack-trace/src/AllTraces.java b/test/911-get-stack-trace/src/AllTraces.java index adf6f38f5a..1d9aa96d6e 100644 --- a/test/911-get-stack-trace/src/AllTraces.java +++ b/test/911-get-stack-trace/src/AllTraces.java @@ -26,8 +26,8 @@ public class AllTraces { System.out.println("################################"); // Also create an unstarted and a dead thread. - RETAIN.add(new Thread()); - Thread deadThread = new Thread(); + RETAIN.add(new Thread("UNSTARTED")); + Thread deadThread = new Thread("DEAD"); RETAIN.add(deadThread); deadThread.start(); deadThread.join(); @@ -40,7 +40,7 @@ public class AllTraces { Thread threads[] = new Thread[N]; for (int i = 0; i < N; i++) { - Thread t = new Thread() { + Thread t = new Thread("AllTraces Thread " + i) { public void run() { Recurse.foo(4, 0, 0, data); } diff --git a/test/911-get-stack-trace/src/Frames.java b/test/911-get-stack-trace/src/Frames.java index a1a11c3785..54d4165130 100644 --- a/test/911-get-stack-trace/src/Frames.java +++ b/test/911-get-stack-trace/src/Frames.java @@ -59,7 +59,7 @@ public class Frames { System.out.println("################################"); final ControlData data = new ControlData(); data.waitFor = new Object(); - Thread t = new Thread() { + Thread t = new Thread("Frames doTestOtherThreadWait") { public void run() { Recurse.foo(4, 0, 0, data); } @@ -97,7 +97,7 @@ public class Frames { System.out.println("### Other thread (live) ###"); System.out.println("###########################"); final ControlData data = new ControlData(); - Thread t = new Thread() { + Thread t = new Thread("Frames doTestOtherThreadBusyLoop") { public void run() { Recurse.foo(4, 0, 0, data); } diff --git a/test/911-get-stack-trace/src/OtherThread.java b/test/911-get-stack-trace/src/OtherThread.java index 0748433a20..0a78523a91 100644 --- a/test/911-get-stack-trace/src/OtherThread.java +++ b/test/911-get-stack-trace/src/OtherThread.java @@ -21,7 +21,7 @@ public class OtherThread { System.out.println("################################"); final ControlData data = new ControlData(); data.waitFor = new Object(); - Thread t = new Thread() { + Thread t = new Thread("OtherThread doTestOtherThreadWait") { public void run() { Recurse.foo(4, 0, 0, data); } @@ -54,7 +54,7 @@ public class OtherThread { System.out.println("### Other thread (live) ###"); System.out.println("###########################"); final ControlData data = new ControlData(); - Thread t = new Thread() { + Thread t = new Thread("OtherThread doTestOtherThreadBusyLoop") { public void run() { Recurse.foo(4, 0, 0, data); } diff --git a/test/911-get-stack-trace/src/ThreadListTraces.java b/test/911-get-stack-trace/src/ThreadListTraces.java index f66557f3bd..14868e9c36 100644 --- a/test/911-get-stack-trace/src/ThreadListTraces.java +++ b/test/911-get-stack-trace/src/ThreadListTraces.java @@ -30,7 +30,7 @@ public class ThreadListTraces { Thread list[] = new Thread[N/2 + 1]; for (int i = 0; i < N; i++) { - Thread t = new Thread() { + Thread t = new Thread("ThreadListTraces Thread " + i) { public void run() { Recurse.foo(4, 0, 0, data); } diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc index e659ea3bfb..3ccfe86bed 100644 --- a/test/912-classes/classes.cc +++ b/test/912-classes/classes.cc @@ -430,5 +430,145 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_isLoadedClass( return found ? JNI_TRUE : JNI_FALSE; } +class ClassLoadPrepareEquality { + public: + static constexpr const char* kClassName = "LMain$ClassE;"; + static constexpr const char* kStorageFieldName = "STATIC"; + static constexpr const char* kStorageFieldSig = "Ljava/lang/Object;"; + static constexpr const char* kStorageWeakFieldName = "WEAK"; + static constexpr const char* kStorageWeakFieldSig = "Ljava/lang/ref/Reference;"; + static constexpr const char* kWeakClassName = "java/lang/ref/WeakReference"; + static constexpr const char* kWeakInitSig = "(Ljava/lang/Object;)V"; + static constexpr const char* kWeakGetSig = "()Ljava/lang/Object;"; + + static void JNICALL ClassLoadCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread ATTRIBUTE_UNUSED, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == kClassName) { + found_ = true; + stored_class_ = jni_env->NewGlobalRef(klass); + weakly_stored_class_ = jni_env->NewWeakGlobalRef(klass); + // The following is bad and relies on implementation details. But otherwise a test would be + // a lot more complicated. + local_stored_class_ = jni_env->NewLocalRef(klass); + // Store the value into a field in the heap. + SetOrCompare(jni_env, klass, true); + } + } + + static void JNICALL ClassPrepareCallback(jvmtiEnv* jenv, + JNIEnv* jni_env, + jthread thread ATTRIBUTE_UNUSED, + jclass klass) { + std::string name = GetClassName(jenv, jni_env, klass); + if (name == kClassName) { + CHECK(stored_class_ != nullptr); + CHECK(jni_env->IsSameObject(stored_class_, klass)); + CHECK(jni_env->IsSameObject(weakly_stored_class_, klass)); + CHECK(jni_env->IsSameObject(local_stored_class_, klass)); + // Look up the value in a field in the heap. + SetOrCompare(jni_env, klass, false); + compared_ = true; + } + } + + static void SetOrCompare(JNIEnv* jni_env, jobject value, bool set) { + CHECK(storage_class_ != nullptr); + + // Simple direct storage. + jfieldID field = jni_env->GetStaticFieldID(storage_class_, kStorageFieldName, kStorageFieldSig); + CHECK(field != nullptr); + + if (set) { + jni_env->SetStaticObjectField(storage_class_, field, value); + CHECK(!jni_env->ExceptionCheck()); + } else { + ScopedLocalRef<jobject> stored(jni_env, jni_env->GetStaticObjectField(storage_class_, field)); + CHECK(jni_env->IsSameObject(value, stored.get())); + } + + // Storage as a reference. + ScopedLocalRef<jclass> weak_ref_class(jni_env, jni_env->FindClass(kWeakClassName)); + CHECK(weak_ref_class.get() != nullptr); + jfieldID weak_field = jni_env->GetStaticFieldID(storage_class_, + kStorageWeakFieldName, + kStorageWeakFieldSig); + CHECK(weak_field != nullptr); + if (set) { + // Create a WeakReference. + jmethodID weak_init = jni_env->GetMethodID(weak_ref_class.get(), "<init>", kWeakInitSig); + CHECK(weak_init != nullptr); + ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->NewObject(weak_ref_class.get(), + weak_init, + value)); + CHECK(weak_obj.get() != nullptr); + jni_env->SetStaticObjectField(storage_class_, weak_field, weak_obj.get()); + CHECK(!jni_env->ExceptionCheck()); + } else { + // Check the reference value. + jmethodID get_referent = jni_env->GetMethodID(weak_ref_class.get(), "get", kWeakGetSig); + CHECK(get_referent != nullptr); + ScopedLocalRef<jobject> weak_obj(jni_env, jni_env->GetStaticObjectField(storage_class_, + weak_field)); + CHECK(weak_obj.get() != nullptr); + ScopedLocalRef<jobject> weak_referent(jni_env, jni_env->CallObjectMethod(weak_obj.get(), + get_referent)); + CHECK(weak_referent.get() != nullptr); + CHECK(jni_env->IsSameObject(value, weak_referent.get())); + } + } + + static void CheckFound() { + CHECK(found_); + CHECK(compared_); + } + + static void Free(JNIEnv* env) { + if (stored_class_ != nullptr) { + env->DeleteGlobalRef(stored_class_); + DCHECK(weakly_stored_class_ != nullptr); + env->DeleteWeakGlobalRef(weakly_stored_class_); + // Do not attempt to delete the local ref. It will be out of date by now. + } + } + + static jclass storage_class_; + + private: + static jobject stored_class_; + static jweak weakly_stored_class_; + static jobject local_stored_class_; + static bool found_; + static bool compared_; +}; +jclass ClassLoadPrepareEquality::storage_class_ = nullptr; +jobject ClassLoadPrepareEquality::stored_class_ = nullptr; +jweak ClassLoadPrepareEquality::weakly_stored_class_ = nullptr; +jobject ClassLoadPrepareEquality::local_stored_class_ = nullptr; +bool ClassLoadPrepareEquality::found_ = false; +bool ClassLoadPrepareEquality::compared_ = false; + +extern "C" JNIEXPORT void JNICALL Java_Main_setEqualityEventStorageClass( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jclass klass) { + ClassLoadPrepareEquality::storage_class_ = + reinterpret_cast<jclass>(env->NewGlobalRef(klass)); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_enableClassLoadPrepareEqualityEvents( + JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED, jboolean b) { + EnableEvents(env, + b, + ClassLoadPrepareEquality::ClassLoadCallback, + ClassLoadPrepareEquality::ClassPrepareCallback); + if (b == JNI_FALSE) { + ClassLoadPrepareEquality::Free(env); + ClassLoadPrepareEquality::CheckFound(); + env->DeleteGlobalRef(ClassLoadPrepareEquality::storage_class_); + ClassLoadPrepareEquality::storage_class_ = nullptr; + } +} + } // namespace Test912Classes } // namespace art diff --git a/test/912-classes/src/Main.java b/test/912-classes/src/Main.java index e3aceb9a5f..005074f8c1 100644 --- a/test/912-classes/src/Main.java +++ b/test/912-classes/src/Main.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import java.lang.ref.Reference; import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; import java.util.Arrays; @@ -290,6 +291,8 @@ public class Main { if (hasJit() && !isLoadedClass("Main$ClassD")) { testClassEventsJit(); } + + testClassLoadPrepareEquality(); } private static void testClassEventsJit() throws Exception { @@ -312,6 +315,16 @@ public class Main { } } + private static void testClassLoadPrepareEquality() throws Exception { + setEqualityEventStorageClass(ClassF.class); + + enableClassLoadPrepareEqualityEvents(true); + + Class.forName("Main$ClassE"); + + enableClassLoadPrepareEqualityEvents(false); + } + private static void printClassLoaderClasses(ClassLoader cl) { for (;;) { if (cl == null || !cl.getClass().getName().startsWith("dalvik.system")) { @@ -383,6 +396,9 @@ public class Main { private static native void enableClassLoadSeenEvents(boolean b); private static native boolean hadLoadEvent(); + private static native void setEqualityEventStorageClass(Class<?> c); + private static native void enableClassLoadPrepareEqualityEvents(boolean b); + private static class TestForNonInit { public static double dummy = Math.random(); // So it can't be compile-time initialized. } @@ -409,6 +425,18 @@ public class Main { static int x = 1; } + public static class ClassE { + public void foo() { + } + public void bar() { + } + } + + public static class ClassF { + public static Object STATIC = null; + public static Reference<Object> WEAK = null; + } + private static final String DEX1 = System.getenv("DEX_LOCATION") + "/912-classes.jar"; private static final String DEX2 = System.getenv("DEX_LOCATION") + "/912-classes-ex.jar"; diff --git a/test/945-obsolete-native/build b/test/945-obsolete-native/build new file mode 100755 index 0000000000..898e2e54a2 --- /dev/null +++ b/test/945-obsolete-native/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/945-obsolete-native/expected.txt b/test/945-obsolete-native/expected.txt new file mode 100644 index 0000000000..83efda144d --- /dev/null +++ b/test/945-obsolete-native/expected.txt @@ -0,0 +1,9 @@ +hello +Not doing anything here +goodbye +hello +transforming calling function +goodbye +Hello - Transformed +Not doing anything here +Goodbye - Transformed diff --git a/test/945-obsolete-native/info.txt b/test/945-obsolete-native/info.txt new file mode 100644 index 0000000000..c8b892cedd --- /dev/null +++ b/test/945-obsolete-native/info.txt @@ -0,0 +1 @@ +Tests basic obsolete method support diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc new file mode 100644 index 0000000000..061e7afbbc --- /dev/null +++ b/test/945-obsolete-native/obsolete_native.cc @@ -0,0 +1,51 @@ +/* + * 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 <memory> +#include <stdio.h> + +#include "android-base/stringprintf.h" + +#include "android-base/stringprintf.h" +#include "base/logging.h" +#include "base/macros.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 Test945ObsoleteNative { + +extern "C" JNIEXPORT void JNICALL Java_Main_bindTest945ObsoleteNative( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { + BindFunctions(jvmti_env, env, "Transform"); +} + +extern "C" JNIEXPORT void JNICALL Java_Transform_doExecute(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jobject runnable) { + jclass runnable_klass = env->FindClass("java/lang/Runnable"); + DCHECK(runnable_klass != nullptr); + jmethodID run_method = env->GetMethodID(runnable_klass, "run", "()V"); + env->CallVoidMethod(runnable, run_method); +} + + +} // namespace Test945ObsoleteNative +} // namespace art diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run new file mode 100755 index 0000000000..c6e62ae6cd --- /dev/null +++ b/test/945-obsolete-native/run @@ -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-run "$@" --jvmti diff --git a/test/945-obsolete-native/src/Main.java b/test/945-obsolete-native/src/Main.java new file mode 100644 index 0000000000..5e2154e9a3 --- /dev/null +++ b/test/945-obsolete-native/src/Main.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.util.Base64; + +public class Main { + // class Transform { + // public void sayHi(Runnable r) { + // System.out.println("Hello - Transformed"); + // doExecute(r); + // System.out.println("Goodbye - Transformed"); + // } + // + // private static native void doExecute(Runnable r); + // } + private static final byte[] CLASS_BYTES = Base64.getDecoder().decode( + "yv66vgAAADQAIgoACAASCQATABQIABUKABYAFwoABwAYCAAZBwAaBwAbAQAGPGluaXQ+AQADKClW" + + "AQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" + + "KVYBAAlkb0V4ZWN1dGUBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkACgcAHAwAHQAe" + + "AQATSGVsbG8gLSBUcmFuc2Zvcm1lZAcAHwwAIAAhDAAPAA4BABVHb29kYnllIC0gVHJhbnNmb3Jt" + + "ZWQBAAlUcmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291" + + "dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxu" + + "AQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACAABwAIAAAAAAADAAAACQAKAAEACwAAAB0AAQABAAAA" + + "BSq3AAGxAAAAAQAMAAAABgABAAAAEQABAA0ADgABAAsAAAA5AAIAAgAAABWyAAISA7YABCu4AAWy" + + "AAISBrYABLEAAAABAAwAAAASAAQAAAATAAgAFAAMABUAFAAWAQoADwAOAAAAAQAQAAAAAgAR"); + private static final byte[] DEX_BYTES = Base64.getDecoder().decode( + "ZGV4CjAzNQB1fZcJR/opPuXacK8mIla5shH0LSg72qJYAwAAcAAAAHhWNBIAAAAAAAAAALgCAAAR" + + "AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAUAAAD8AAAAAQAAACQBAAAUAgAARAEAAKIB" + + "AACqAQAAwQEAANYBAADjAQAA+gEAAA4CAAAkAgAAOAIAAEwCAABcAgAAXwIAAGMCAABuAgAAggIA" + + "AIcCAACQAgAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACgAAAAoAAAAGAAAAAAAAAAsAAAAGAAAA" + + "lAEAAAsAAAAGAAAAnAEAAAUAAQAOAAAAAAAAAAAAAAAAAAEADAAAAAAAAQAQAAAAAQACAA8AAAAC" + + "AAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAJAAAAAAAAAKUCAAAAAAAAAQABAAEAAACXAgAABAAAAHAQ" + + "BAAAAA4ABAACAAIAAACcAgAAFAAAAGIAAAAbAQIAAABuIAMAEABxEAEAAwBiAAAAGwEBAAAAbiAD" + + "ABAADgABAAAAAwAAAAEAAAAEAAY8aW5pdD4AFUdvb2RieWUgLSBUcmFuc2Zvcm1lZAATSGVsbG8g" + + "LSBUcmFuc2Zvcm1lZAALTFRyYW5zZm9ybTsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEv" + + "bGFuZy9PYmplY3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJM" + + "amF2YS9sYW5nL1N5c3RlbTsADlRyYW5zZm9ybS5qYXZhAAFWAAJWTAAJZG9FeGVjdXRlABJlbWl0" + + "dGVyOiBqYWNrLTQuMjUAA291dAAHcHJpbnRsbgAFc2F5SGkAEQAHDgATAQAHDoc8hwAAAAIBAICA" + + "BMQCAYoCAAIB3AIADQAAAAAAAAABAAAAAAAAAAEAAAARAAAAcAAAAAIAAAAHAAAAtAAAAAMAAAAD" + + "AAAA0AAAAAQAAAABAAAA9AAAAAUAAAAFAAAA/AAAAAYAAAABAAAAJAEAAAEgAAACAAAARAEAAAEQ" + + "AAACAAAAlAEAAAIgAAARAAAAogEAAAMgAAACAAAAlwIAAAAgAAABAAAApQIAAAAQAAABAAAAuAIA" + + "AA=="); + + public static void main(String[] args) { + bindTest945ObsoleteNative(); + doTest(new Transform()); + } + + public static void doTest(Transform t) { + t.sayHi(() -> { System.out.println("Not doing anything here"); }); + t.sayHi(() -> { + System.out.println("transforming calling function"); + doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES); + }); + t.sayHi(() -> { System.out.println("Not doing anything here"); }); + } + + // Transforms the class + private static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + private static native void bindTest945ObsoleteNative(); +} diff --git a/test/945-obsolete-native/src/Transform.java b/test/945-obsolete-native/src/Transform.java new file mode 100644 index 0000000000..2b7cc1b3a1 --- /dev/null +++ b/test/945-obsolete-native/src/Transform.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Transform { + public void sayHi(Runnable r) { + System.out.println("hello"); + doExecute(r); + System.out.println("goodbye"); + } + + private static native void doExecute(Runnable r); +} diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build new file mode 100644 index 0000000000..a423ca6b4e --- /dev/null +++ b/test/952-invoke-custom/build @@ -0,0 +1,25 @@ +#!/bin/bash +# +# Copyright 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# make us exit on a failure +set -e + +if [[ $@ != *"--jvm"* ]]; then + # Don't do anything with jvm. + export USE_JACK=true +fi + +./default-build "$@" --experimental method-handles diff --git a/test/952-invoke-custom/expected.txt b/test/952-invoke-custom/expected.txt new file mode 100644 index 0000000000..bb87296e60 --- /dev/null +++ b/test/952-invoke-custom/expected.txt @@ -0,0 +1,14 @@ +Caught exception from uninitialized call site +Caught exception from uninitialized call site +linkerMethod failure type 1 +Returning null instead of CallSite for add (int,int)int +linkerMethod failure type 2 +Throwing InstantiationException in linkerMethod() +linkerMethod failure type 3 +Throwing ArithmeticException in add() +Failure Type + 0 (1013) +Linking add (int,int)int +100 +-9000 +9000 +Winners 1 Votes 16 diff --git a/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java new file mode 100644 index 0000000000..5d5cae4147 --- /dev/null +++ b/test/952-invoke-custom/generator/TestInvokeCustomWithConcurrentThreads.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.jack.annotations.CalledByInvokeCustom; +import com.android.jack.annotations.Constant; +import com.android.jack.annotations.LinkerMethodHandle; +import com.android.jack.annotations.MethodHandleKind; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +import java.lang.Thread; +import java.lang.ThreadLocal; +import java.util.concurrent.atomic.AtomicInteger; + +public class TestInvokeCustomWithConcurrentThreads extends Thread { + private static final int NUMBER_OF_THREADS = 16; + + private static final AtomicInteger nextIndex = new AtomicInteger(0); + + private static final ThreadLocal<Integer> threadIndex = + new ThreadLocal<Integer>() { + @Override + protected Integer initialValue() { + return nextIndex.getAndIncrement(); + } + }; + + // Array of call sites instantiated, one per thread + private static CallSite[] instantiated = new CallSite[NUMBER_OF_THREADS]; + + // Array of counters for how many times each instantiated call site is called + private static AtomicInteger[] called = new AtomicInteger[NUMBER_OF_THREADS]; + + // Array of call site indicies of which call site a thread invoked + private static AtomicInteger[] targetted = new AtomicInteger[NUMBER_OF_THREADS]; + + private TestInvokeCustomWithConcurrentThreads() {} + + private static int getThreadIndex() { + return threadIndex.get().intValue(); + } + + public static int notUsed(int x) { + return x; + } + + @Override + public void run() { + int x = setCalled(-1 /* argument dropped */); + notUsed(x); + } + + @CalledByInvokeCustom( + invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC, + enclosingType = TestInvokeCustomWithConcurrentThreads.class, + name = "linkerMethod", + argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}), + name = "setCalled", + returnType = int.class, + argumentTypes = {int.class}) + private static int setCalled(int index) { + called[index].getAndIncrement(); + targetted[getThreadIndex()].set(index); + return 0; + } + + @SuppressWarnings("unused") + private static CallSite linkerMethod(MethodHandles.Lookup caller, + String name, + MethodType methodType) throws Throwable { + int threadIndex = getThreadIndex(); + MethodHandle mh = + caller.findStatic(TestInvokeCustomWithConcurrentThreads.class, name, methodType); + assertEquals(methodType, mh.type()); + assertEquals(mh.type().parameterCount(), 1); + mh = MethodHandles.insertArguments(mh, 0, threadIndex); + mh = MethodHandles.dropArguments(mh, 0, int.class); + assertEquals(mh.type().parameterCount(), 1); + assertEquals(methodType, mh.type()); + + // Sleep to try to get concurrent executions of this + // method. Multiple call sites should be created, but only one + // invoked. + Thread.sleep(125); + + instantiated[getThreadIndex()] = new ConstantCallSite(mh); + return instantiated[getThreadIndex()]; + } + + public static void test() throws Throwable { + // Initialize counters for which call site gets invoked + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + called[i] = new AtomicInteger(0); + targetted[i] = new AtomicInteger(0); + } + + // Run threads that each invoke-custom the call site + Thread [] threads = new Thread[NUMBER_OF_THREADS]; + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + threads[i] = new TestInvokeCustomWithConcurrentThreads(); + threads[i].start(); + } + + // Wait for all threads to complete + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + threads[i].join(); + } + + // Check one call site instance won + int winners = 0; + int votes = 0; + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + assertNotEquals(instantiated[i], null); + if (called[i].get() != 0) { + winners++; + votes += called[i].get(); + } + } + + System.out.println("Winners " + winners + " Votes " + votes); + + // We assert this below but output details when there's an error as + // it's non-deterministic. + if (winners != 1) { + System.out.println("Threads did not the same call-sites:"); + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + System.out.format(" Thread % 2d invoked call site instance #%02d\n", + i, targetted[i].get()); + } + } + + // We assert this below but output details when there's an error as + // it's non-deterministic. + if (votes != NUMBER_OF_THREADS) { + System.out.println("Call-sites invocations :"); + for (int i = 0; i < NUMBER_OF_THREADS; ++i) { + System.out.format(" Call site instance #%02d was invoked % 2d times\n", + i, called[i].get()); + } + } + + assertEquals(winners, 1); + assertEquals(votes, NUMBER_OF_THREADS); + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(byte b1, byte b2) { + if (b1 == b2) { return; } + throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2); + } + + public static void assertEquals(char c1, char c2) { + if (c1 == c2) { return; } + throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2); + } + + public static void assertEquals(short s1, short s2) { + if (s1 == s2) { return; } + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long l1, long l2) { + if (l1 == l2) { return; } + throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2); + } + + public static void assertEquals(float f1, float f2) { + if (f1 == f2) { return; } + throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2); + } + + public static void assertEquals(double d1, double d2) { + if (d1 == d2) { return; } + throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertNotEquals(Object o, Object p) { + if (o != p) { return; } + if (o != null && p != null && !o.equals(p)) { return; } + throw new AssertionError("assertNotEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } +} diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java new file mode 100644 index 0000000000..93d96a9a7b --- /dev/null +++ b/test/952-invoke-custom/generator/TestLinkerMethodMinimalArguments.java @@ -0,0 +1,137 @@ +/* + * 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 com.android.jack.annotations.CalledByInvokeCustom; +import com.android.jack.annotations.Constant; +import com.android.jack.annotations.LinkerMethodHandle; +import com.android.jack.annotations.MethodHandleKind; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class TestLinkerMethodMinimalArguments { + + private static int forceFailureType = 0; + + private static int FAILURE_TYPE_NONE = 0; + private static int FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL = 1; + private static int FAILURE_TYPE_LINKER_METHOD_THROWS = 2; + private static int FAILURE_TYPE_TARGET_METHOD_THROWS = 3; + + @CalledByInvokeCustom( + invokeMethodHandle = @LinkerMethodHandle( + kind = MethodHandleKind.INVOKE_STATIC, + enclosingType = TestLinkerMethodMinimalArguments.class, + argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class}, + name = "linkerMethod"), + name = "add", + returnType = int.class, + argumentTypes = {int.class, int.class}) + private static int add(int a, int b) { + if (forceFailureType == FAILURE_TYPE_TARGET_METHOD_THROWS) { + System.out.println("Throwing ArithmeticException in add()"); + throw new ArithmeticException("add"); + } + return a + b; + } + + @SuppressWarnings("unused") + private static CallSite linkerMethod(MethodHandles.Lookup caller, String name, + MethodType methodType) throws Throwable { + System.out.println("linkerMethod failure type " + forceFailureType); + MethodHandle mh_add = + caller.findStatic(TestLinkerMethodMinimalArguments.class, name, methodType); + if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_RETURNS_NULL) { + System.out.println("Returning null instead of CallSite for " + name + " " + methodType); + return null; + } else if (forceFailureType == FAILURE_TYPE_LINKER_METHOD_THROWS) { + System.out.println("Throwing InstantiationException in linkerMethod()"); + throw new InstantiationException("linkerMethod"); + } else { + return new ConstantCallSite(mh_add); + } + } + + public static void test(int failureType, int x, int y) throws Throwable { + assertTrue(failureType >= FAILURE_TYPE_NONE); + assertTrue(failureType <= FAILURE_TYPE_TARGET_METHOD_THROWS); + forceFailureType = failureType; + assertEquals(x + y, add(x, y)); + System.out.println("Failure Type + " + failureType + " (" + x + y+ ")"); + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(byte b1, byte b2) { + if (b1 == b2) { return; } + throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2); + } + + public static void assertEquals(char c1, char c2) { + if (c1 == c2) { return; } + throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2); + } + + public static void assertEquals(short s1, short s2) { + if (s1 == s2) { return; } + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long l1, long l2) { + if (l1 == l2) { return; } + throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2); + } + + public static void assertEquals(float f1, float f2) { + if (f1 == f2) { return; } + throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2); + } + + public static void assertEquals(double d1, double d2) { + if (d1 == d2) { return; } + throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } +} diff --git a/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java new file mode 100644 index 0000000000..4e4d97e15e --- /dev/null +++ b/test/952-invoke-custom/generator/TestLinkerMethodMultipleArgumentTypes.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.jack.annotations.CalledByInvokeCustom; +import com.android.jack.annotations.Constant; +import com.android.jack.annotations.LinkerMethodHandle; +import com.android.jack.annotations.MethodHandleKind; + +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class TestLinkerMethodMultipleArgumentTypes { + + private static int bootstrapRunCount = 0; + + @CalledByInvokeCustom( + invokeMethodHandle = @LinkerMethodHandle(kind = MethodHandleKind.INVOKE_STATIC, + enclosingType = TestLinkerMethodMultipleArgumentTypes.class, + name = "linkerMethod", + argumentTypes = {MethodHandles.Lookup.class, String.class, MethodType.class, + boolean.class, byte.class, char.class, short.class, int.class, + float.class, double.class, String.class, Class.class, long.class}), + methodHandleExtraArgs = {@Constant(booleanValue = true), @Constant(byteValue = 1), + @Constant(charValue = 'a'), @Constant(shortValue = 1024), + @Constant(intValue = 1), @Constant(floatValue = 11.1f), + @Constant(doubleValue = 2.2), @Constant(stringValue = "Hello"), + @Constant(classValue = TestLinkerMethodMultipleArgumentTypes.class), + @Constant(longValue = 123456789L)}, + name = "add", + returnType = int.class, + argumentTypes = {int.class, int.class}) + private static int add(int a, int b) { + return a + b; + } + + @SuppressWarnings("unused") + private static CallSite linkerMethod(MethodHandles.Lookup caller, String name, + MethodType methodType, boolean v1, byte v2, char v3, + short v4, int v5, float v6, double v7, + String v8, Class<?> v9, long v10) throws Throwable { + System.out.println("Linking " + name + " " + methodType); + assertTrue(v1); + assertEquals(1, v2); + assertEquals('a', v3); + assertEquals(1024, v4); + assertEquals(1, v5); + assertEquals(11.1f, v6); + assertEquals(2.2, v7); + assertEquals("Hello", v8); + assertEquals(TestLinkerMethodMultipleArgumentTypes.class, v9); + assertEquals(123456789L, v10); + MethodHandle mh_add = + caller.findStatic(TestLinkerMethodMultipleArgumentTypes.class, name, methodType); + return new ConstantCallSite(mh_add); + } + + public int GetBootstrapRunCount() { + return bootstrapRunCount; + } + + public static void test(int x, int y) throws Throwable { + assertEquals(x + y, add(x, y)); + System.out.println(x + y); + } + + public static void assertTrue(boolean value) { + if (!value) { + throw new AssertionError("assertTrue value: " + value); + } + } + + public static void assertEquals(byte b1, byte b2) { + if (b1 == b2) { return; } + throw new AssertionError("assertEquals b1: " + b1 + ", b2: " + b2); + } + + public static void assertEquals(char c1, char c2) { + if (c1 == c2) { return; } + throw new AssertionError("assertEquals c1: " + c1 + ", c2: " + c2); + } + + public static void assertEquals(short s1, short s2) { + if (s1 == s2) { return; } + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + public static void assertEquals(int i1, int i2) { + if (i1 == i2) { return; } + throw new AssertionError("assertEquals i1: " + i1 + ", i2: " + i2); + } + + public static void assertEquals(long l1, long l2) { + if (l1 == l2) { return; } + throw new AssertionError("assertEquals l1: " + l1 + ", l2: " + l2); + } + + public static void assertEquals(float f1, float f2) { + if (f1 == f2) { return; } + throw new AssertionError("assertEquals f1: " + f1 + ", f2: " + f2); + } + + public static void assertEquals(double d1, double d2) { + if (d1 == d2) { return; } + throw new AssertionError("assertEquals d1: " + d1 + ", d2: " + d2); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } +} diff --git a/test/952-invoke-custom/generator/build-test.sh b/test/952-invoke-custom/generator/build-test.sh new file mode 100755 index 0000000000..90a60e6f8f --- /dev/null +++ b/test/952-invoke-custom/generator/build-test.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +args="$@" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +test_dir="test-$$" +if [ -z "$TMPDIR" ]; then + tmp_dir="/tmp/$USER/${test_dir}" +else + tmp_dir="${TMPDIR}/${test_dir}" +fi + +# This is a custom drop that necessitates this complexity. +JACK_ANNOTATIONS_LIB=$HOME/Downloads/jack-test-annotations-lib.jack + +# Compile test into a base64 string that can be instantiated via +# reflection on hosts without the jack-test-annotations-lib.jack file. +mkdir $tmp_dir +for input_file in $progdir/*.java; do + i=${input_file##*/Test} + i=${i%%.java} + src_file=$progdir/Test$i.java + jack_file=./src.jack + dex_file=./classes.dex + base_64_file=$tmp_dir/TestData$i.base64 + output_file=$progdir/../src/TestData$i.java + # Compile source file to jack file. + jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack:$JACK_ANNOTATIONS_LIB -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --output-jack $jack_file $src_file + # Compile jack file to classes.dex. + jack -g -cp $ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-libart-hostdex_intermediates/classes.jack:$ANDROID_BUILD_TOP/out/host/linux-x86/../common/obj/JAVA_LIBRARIES/core-oj-hostdex_intermediates/classes.jack -D sched.runner=multi-threaded -D sched.runner.thread.kind=fixed -D sched.runner.thread.fixed.count=4 -D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b2 --import $jack_file --output-dex . + # Pack the classes.dex file into a base64 string. + base64 -w 72 $dex_file > $base_64_file + # Emit a managed source file containing the base64 string. The test can be + # run by loading this string as a dex file and invoking it via reflection. +cat > $output_file <<HEADER +/* Generated by ${prog##*/} from ${src_file##*/} */ +public class TestData$i { + public static final String BASE64_DEX_FILE = +HEADER +sed -e 's/^\(.*\)$/ "\1" +/' -e '$s/ +/;/' $base_64_file >> $output_file +cat >> $output_file <<FOOTER +} +FOOTER + rm $dex_file $jack_file +done + +rm -rf $tmp_dir diff --git a/test/952-invoke-custom/info.txt b/test/952-invoke-custom/info.txt new file mode 100644 index 0000000000..e9a9f6c4a5 --- /dev/null +++ b/test/952-invoke-custom/info.txt @@ -0,0 +1,7 @@ +A test that is only available as a DEX binary. + +This tests execution of invoke-custom. There is no bytecode to emit +invoke-custom directly. This test is generated using jack-test-annotations-lib.jack +which is not a publicly supported at this time. + +The tests and a script to generate data from them are in the generator/ directory. diff --git a/test/952-invoke-custom/src/Main.java b/test/952-invoke-custom/src/Main.java new file mode 100644 index 0000000000..2abc3122fa --- /dev/null +++ b/test/952-invoke-custom/src/Main.java @@ -0,0 +1,159 @@ +/* + * 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 dalvik.system.InMemoryDexClassLoader; + +import java.lang.invoke.CallSite; +import java.lang.invoke.MethodType; +import java.lang.invoke.MutableCallSite; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.Base64; + +// This test is a stop-gap until we have support for generating invoke-custom +// in the Android tree. + +public class Main { + + private static void TestUninitializedCallSite() throws Throwable { + CallSite callSite = new MutableCallSite(MethodType.methodType(int.class)); + try { + callSite.getTarget().invoke(); + fail(); + } catch (IllegalStateException e) { + System.out.println("Caught exception from uninitialized call site"); + } + + callSite = new MutableCallSite(MethodType.methodType(String.class, int.class, char.class)); + try { + callSite.getTarget().invoke(1535, 'd'); + fail(); + } catch (IllegalStateException e) { + System.out.println("Caught exception from uninitialized call site"); + } + } + + private static void TestLinkerMethodMultipleArgumentTypes() throws Throwable { + // This is a more comprehensive test of invoke-custom, the linker + // method takes additional arguments of types boolean, byte, char, + // short, int, float, double, String, Class, and long (in this order) + // The test asserts the values passed to the linker method match their + // expected values. + byte[] base64Data = TestDataLinkerMethodMultipleArgumentTypes.BASE64_DEX_FILE.getBytes(); + Base64.Decoder decoder = Base64.getDecoder(); + ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); + + InMemoryDexClassLoader classLoader = + new InMemoryDexClassLoader(dexBuffer, + ClassLoader.getSystemClassLoader()); + Class<?> testClass = + classLoader.loadClass("TestLinkerMethodMultipleArgumentTypes"); + Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class); + // First invocation should link via the bootstrap method (outputs "Linking add" ...). + testMethod.invoke(null, 33, 67); + // Subsequent invocations use the cached value of the CallSite and do not require linking. + testMethod.invoke(null, -10000, +1000); + testMethod.invoke(null, -1000, +10000); + } + + private static void TestLinkerMethodMinimalArguments() throws Throwable { + // This test checks various failures when running the linker + // method and during invocation of the method handle. + byte[] base64Data = TestDataLinkerMethodMinimalArguments.BASE64_DEX_FILE.getBytes(); + Base64.Decoder decoder = Base64.getDecoder(); + ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); + + InMemoryDexClassLoader classLoader = + new InMemoryDexClassLoader(dexBuffer, + ClassLoader.getSystemClassLoader()); + Class<?> testClass = + classLoader.loadClass("TestLinkerMethodMinimalArguments"); + Method testMethod = testClass.getDeclaredMethod("test", int.class, int.class, int.class); + + try { + testMethod.invoke(null, 1 /* linker method return null */, 10, 10); + } catch (InvocationTargetException e) { + assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); + assertEquals( + e.getCause().getCause().getClass().getName(), "java.lang.NullPointerException"); + } + + try { + testMethod.invoke(null, 2 /* linker method throw InstantiationException */, 10, 11); + } catch (InvocationTargetException e) { + assertEquals(e.getCause().getClass().getName(), "java.lang.BootstrapMethodError"); + assertEquals( + e.getCause().getCause().getClass().getName(), "java.lang.InstantiationException"); + } + try { + // Creating the CallSite works here, but fail invoking the method. + testMethod.invoke(null, 3 /* target throw NPE */, 10, 12); + } catch (InvocationTargetException e) { + assertEquals(e.getCause().getClass().getName(), "java.lang.ArithmeticException"); + } + + // This should succeed using already resolved CallSite. + testMethod.invoke(null, 0 /* no error */, 10, 13); + } + + private static void TestInvokeCustomWithConcurrentThreads() throws Throwable { + // This is a concurrency test that attempts to run invoke-custom on the same + // call site. + byte[] base64Data = TestDataInvokeCustomWithConcurrentThreads.BASE64_DEX_FILE.getBytes(); + Base64.Decoder decoder = Base64.getDecoder(); + ByteBuffer dexBuffer = ByteBuffer.wrap(decoder.decode(base64Data)); + + InMemoryDexClassLoader classLoader = + new InMemoryDexClassLoader(dexBuffer, + ClassLoader.getSystemClassLoader()); + Class<?> testClass = + classLoader.loadClass("TestInvokeCustomWithConcurrentThreads"); + Method testMethod = testClass.getDeclaredMethod("test"); + testMethod.invoke(null); + } + + public static void assertEquals(Object o, Object p) { + if (o == p) { return; } + if (o != null && p != null && o.equals(p)) { return; } + throw new AssertionError("assertEquals: o1: " + o + ", o2: " + p); + } + + public static void assertEquals(String s1, String s2) { + if (s1 == s2) { + return; + } + + if (s1 != null && s2 != null && s1.equals(s2)) { + return; + } + + throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2); + } + + private static void fail() { + System.out.println("fail"); + Thread.dumpStack(); + } + + public static void main(String[] args) throws Throwable { + TestUninitializedCallSite(); + TestLinkerMethodMinimalArguments(); + TestLinkerMethodMultipleArgumentTypes(); + TestInvokeCustomWithConcurrentThreads(); + } +}
\ No newline at end of file diff --git a/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java new file mode 100644 index 0000000000..9a77e67e49 --- /dev/null +++ b/test/952-invoke-custom/src/TestDataInvokeCustomWithConcurrentThreads.java @@ -0,0 +1,145 @@ +/* Generated by build-test.sh from TestInvokeCustomWithConcurrentThreads.java */ +public class TestDataInvokeCustomWithConcurrentThreads { + public static final String BASE64_DEX_FILE = + "ZGV4CjAzOADq+VhgIFlZEzwomlDWIN5pEqcVmb8SN2y8HQAAcAAAAHhWNBIAAAAAAAAAANQc" + + "AACqAAAAcAAAACoAAAAYAwAAJgAAAMADAAAJAAAAiAUAADgAAADQBQAAAgAAAJQHAADgFQAA" + + "3AcAAKQRAADXEQAABxIAABASAAAYEgAAIBIAACgSAAAwEgAAOBIAAEASAABIEgAAUBIAAFcS" + + "AABaEgAAZBIAAGwSAABwEgAAcxIAAHYSAACQEgAAkxIAAJYSAACZEgAAnRIAAKwSAACvEgAA" + + "shIAALYSAAC6EgAAvhIAAMISAADGEgAAyhIAANASAADVEgAA2xIAAAYTAAAvEwAAMxMAAGgT" + + "AACbEwAAzBMAAPATAAAQFAAAMxQAAFIUAABuFAAAhRQAAKEUAAC0FAAAyRQAAN0UAADxFAAA" + + "DBUAACAVAAA0FQAATBUAAGUVAAB8FQAAmRUAAL4VAADfFQAACBYAACoWAABJFgAAdhYAAIkW" + + "AACMFgAAkhYAAL4WAADkFgAA5xYAAOwWAADxFgAA9hYAAPsWAAD/FgAABBcAAAgXAAANFwAA" + + "ERcAABYXAAAbFwAAHxcAACkXAAAsFwAAMBcAAEQXAABZFwAAbhcAAIwXAAC6FwAAxxcAAM8X" + + "AADeFwAA7BcAAP8XAAASGAAAJRgAADgYAABLGAAAXhgAAHEYAACFGAAAlhgAAK0YAAC5GAAA" + + "zRgAANEYAADVGAAA2RgAAN0YAADlGAAA7RgAAPEYAAD1GAAABBkAABgZAAAnGQAALxkAADMZ" + + "AAA3GQAAQxkAAEsZAABQGQAAYRkAAHEZAAB0GQAAeBkAAHwZAACDGQAAkRkAAKIZAACwGQAA" + + "uhkAAM4ZAADUGQAA2hkAAN4ZAADiGQAA8BkAAPwZAAAAGgAABhoAABEaAAAaGgAAHRoAACIa" + + "AAAlGgAANRoAAD4aAABKGgAATxoAAFMaAABXGgAAXBoAAGcaAABuGgAAdRoAAIAaAACGGgAA" + + "jBoAAJkaAACiGgAArBoAALIaAAC5GgAAwhoAAMkaAADSGgAAEAAAABEAAAATAAAAFAAAABUA" + + "AAAYAAAAIwAAACQAAAAmAAAAJwAAACgAAAApAAAAKgAAACsAAAAsAAAALQAAAC4AAAAvAAAA" + + "MAAAADEAAAAyAAAAMwAAADQAAAA1AAAANgAAADgAAAA5AAAAOgAAADsAAAA8AAAAPQAAAD4A" + + "AAA/AAAAQAAAAEIAAABGAAAAVAAAAFYAAABXAAAAWAAAAFkAAABaAAAAFQAAAAQAAAAAAAAA" + + "FgAAAAQAAADcEAAAIQAAABAAAADkEAAAGQAAABMAAAAAAAAAHQAAABMAAADcEAAAGQAAABQA" + + "AAAAAAAAGQAAABUAAAAAAAAAGgAAABYAAADsEAAAGwAAABYAAAD0EAAAHAAAABYAAAD8EAAA" + + "HQAAABYAAADcEAAAHgAAABYAAAAEEQAAHwAAABYAAAAMEQAAHwAAABYAAAAUEQAAJQAAABYA" + + "AAAcEQAAIgAAABsAAAAkEQAAIgAAAB0AAAAwEQAAIAAAAB0AAAA8EQAAIAAAAB0AAABIEQAA" + + "GQAAACAAAAAAAAAAGQAAACEAAAAAAAAARgAAACMAAAAAAAAARwAAACMAAABUEQAASAAAACMA" + + "AABcEQAASQAAACMAAABkEQAASgAAACMAAABsEQAASwAAACMAAADcEAAATAAAACMAAAB0EQAA" + + "TQAAACMAAAAEEQAATgAAACMAAAB8EQAATwAAACMAAAAMEQAAUAAAACMAAACEEQAATwAAACMA" + + "AAAUEQAAUAAAACMAAACMEQAATwAAACMAAACUEQAAUQAAACMAAACcEQAAUgAAACMAAAAcEQAA" + + "VQAAACQAAAAMEQAABwAEAEEAAAAHACkAbwAAAAcAKACEAAAABwAhAI8AAAAHACkAngAAAAcA" + + "GQChAAAACgAKABcAAAATABIAQwAAABcAEACSAAAABgAVAA4AAAAGAAMAggAAAAYABQCCAAAA" + + "BwAUAAsAAAAHABUADQAAAAcAFQAOAAAABwAWAF4AAAAHABcAXgAAAAcAGABeAAAABwAZAF4A" + + "AAAHABsAXgAAAAcAHQBeAAAABwAfAF4AAAAHACEAXgAAAAcAIwBeAAAABwAfAGcAAAAHACQA" + + "aQAAAAcAAAB9AAAABwAPAIsAAAAHAAEAkAAAAAcAFQCXAAAABwABAJsAAAAHABUAnwAAABAA" + + "AgB6AAAAEAAgAJUAAAARAB4ADgAAABMAAACFAAAAEwAEAKYAAAAUACUAdgAAABUAJQB2AAAA" + + "FgAVAA4AAAAWAAcAXAAAABYACABcAAAAFgAJAFwAAAAWAAoAXAAAABYACwBcAAAAFgAMAFwA" + + "AAAWAA0AXAAAABYADgBcAAAAFgAGAKMAAAAYABUADgAAABgAFQCHAAAAGAAcAJwAAAAYABUA" + + "nQAAABkAFQAOAAAAGQAFAHsAAAAcACIADgAAAB0AEwCkAAAAHgAQAHkAAAAfABEAcwAAAB8A" + + "EgCDAAAAIAAAAJQAAAAhABoADgAAACEAAAB7AAAAIQAAAHwAAAAhABoAmgAAAFMcAAAGAAAA" + + "EAAAABkAAAAAAAAARAAAAJwQAABaHAAAAAAAAAcAAAABAAAAGAAAAAAAAABEAAAArBAAAG0c" + + "AABQHAAABAAAABIAAAADAAAA9BsAAPsbAAAEHAAAAQAAABMcAAABAAAABBwAAAEAAAAcHAAA" + + "AQAAACUcAAABAAEAAQAAANUaAAAEAAAAcBAsAAAADgACAAEAAQAAAN0aAAANAAAAcQADAAAA" + + "DABuEDYAAAAKAHEQGwAAAAwAEQAAAAIAAQABAAAA4hoAAAUAAABuEAEAAQAMABEAAAABAAAA" + + "AAAAAAAAAAADAAAAYgADABEAAAADAAAAAgAAAOcaAAAeAAAAEwIQACIAIQASAXAgNAAQAGkA" + + "AwAiAAYAcBAAAAAAaQAFACMgKABpAAIAIyApAGkAAQAjICkAaQAEAA4AAQABAAEAAAD0GgAA" + + "BAAAAHAQKAAAAA4ABQACAAIAAAD5GgAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJfAAAA" + + "biAlACEADAFuICIAMQAMARsCAwAAAG4gJQAhAAwBbiAiAEEADAFuECcAAQAMAXAgGQAQACcA" + + "BQACAAIAAAACGwAAKAAAADNDAwAOACIAEQAiARYAcBAeAAEAGwJgAAAAbiAlACEADAFuIB8A" + + "MQAMARsCBAAAAG4gJQAhAAwBbiAfAEEADAFuECcAAQAMAXAgGQAQACcACAAEAAMAAAALGwAA" + + "KgAAAC8ABAY5AAMADgAiABEAIgEWAHAQHgABABsCYQAAAG4gJQAhAAwBbjAgAEEFDAEbAgUA" + + "AABuICUAIQAMAW4wIABhBwwBbhAnAAEADAFwIBkAEAAnAAUAAgACAAAAFBsAACoAAAAtAAME" + + "OQADAA4AIgARACIBFgBwEB4AAQAbAmIAAABuICUAIQAMAW4gIQAxAAwBGwIGAAAAbiAlACEA" + + "DAFuICEAQQAMAW4QJwABAAwBcCAZABAAJwAFAAIAAgAAAB0bAAAoAAAAM0MDAA4AIgARACIB" + + "FgBwEB4AAQAbAmMAAABuICUAIQAMAW4gIgAxAAwBGwIHAAAAbiAlACEADAFuICIAQQAMAW4Q" + + "JwABAAwBcCAZABAAJwAIAAQAAwAAACgbAAAqAAAAMQAEBjkAAwAOACIAEQAiARYAcBAeAAEA" + + "GwJkAAAAbiAlACEADAFuMCMAQQUMARsCCAAAAG4gJQAhAAwBbjAjAGEHDAFuECcAAQAMAXAg" + + "GQAQACcABQACAAIAAAAzGwAAMwAAADNDAwAOADgDCwA4BAkAbiAcAEMACgA4AAMADgAiABEA" + + "IgEWAHAQHgABABsCZgAAAG4gJQAhAAwBbiAkADEADAEbAgkAAABuICUAIQAMAW4gJABBAAwB" + + "bhAnAAEADAFwIBkAEAAnAAAABQACAAIAAAA/GwAAMwAAADNDAwAOADgDCwA4BAkAbiAdAEMA" + + "CgA4AAMADgAiABEAIgEWAHAQHgABABsCZQAAAG4gJQAhAAwBbiAlADEADAEbAgoAAABuICUA" + + "IQAMAW4gJQBBAAwBbhAnAAEADAFwIBkAEAAnAAAABQACAAIAAABNGwAAKAAAADNDAwAOACIA" + + "EQAiARYAcBAeAAEAGwJlAAAAbiAlACEADAFuICIAMQAMARsCCgAAAG4gJQAhAAwBbiAiAEEA" + + "DAFuECcAAQAMAXAgGQAQACcABQACAAIAAABYGwAANQAAADJDAwAOADgDDQA4BAsAbiAcAEMA" + + "CgDfAAABOAADAA4AIgARACIBFgBwEB4AAQAbAmgAAABuICUAIQAMAW4gJAAxAAwBGwIJAAAA" + + "biAlACEADAFuICQAQQAMAW4QJwABAAwBcCAZABAAJwAAAAQAAQACAAAAZBsAAB0AAAA5AxwA" + + "IgARACIBFgBwEB4AAQAbAmoAAABuICUAIQAMAW4gJgAxAAwBbhAnAAEADAFwIBkAEAAnAA4A" + + "AAABAAAAAQAAAHAbAAANAAAAYgAFAG4QLQAAAAwAHwATAG4QGgAAAAoADwAAAAkAAwAEAAAA" + + "dRsAAGEAAAASFRIEcQARAAAACgEcAgcAbkAwACaHDABuEC8AAAAMAnEgDAAoAG4QLwAAAAwC" + + "bhAzAAIACgJxIAoAUgAjUiYAcRAbAAEADANNAwIEcTAyAEACDAAjUiUAYgMHAE0DAgRxMDEA" + + "QAIMAG4QLwAAAAwCbhAzAAIACgJxIAoAUgBuEC8AAAAMAnEgDAAoABYCfQBxICoAMgBiAgIA" + + "cQARAAAACgMiBBwAcCAuAAQATQQCA2ICAgBxABEAAAAKA0YCAgMRAgAAAQABAAAAAACTGwAA" + + "AQAAAA8AAAADAAEAAgAAAJobAAAUAAAAYgABAEYAAAJuEDYAAABiAAQAcQARAAAACgFGAAAB" + + "biA3ACAAEgAPAAwAAAADAAAAoxsAAOgAAAASKxIaEgkTCBAAEgA1gBcAYgQBACIFIQBwIDQA" + + "lQBNBQQAYgQEACIFIQBwIDQAlQBNBQQA2AAAASjqI4EnABIANYARACIEBwBwEAUABABNBAEA" + + "RgQBAG4QKwAEANgAAAEo8BIANYAKAEYEAQBuECkABADYAAABKPcSAxICEgA1gCIAYgQCAEYE" + + "BAASBXEgDwBUAGIEAQBGBAQAbhA1AAQACgQ4BA0A2AMDAWIEAQBGBAQAbhA1AAQACgSwQtgA" + + "AAEo32IECAAiBRYAcBAeAAUAGwZTAAAAbiAlAGUADAVuICIANQAMBRsGAgAAAG4gJQBlAAwF" + + "biAiACUADAVuECcABQAMBW4gGABUADKjLgBiBAgAGwVFAAAAbiAYAFQAEgA1gCMAYgQIABsF" + + "AQAAACO2JgBxEBsAAAAMB00HBgliBwQARgcHAG4QNQAHAAoHcRAbAAcADAdNBwYKbjAXAFQG" + + "2AAAASjeMoIuAGIECAAbBRIAAABuIBgAVAASADWAIwBiBAgAGwUAAAAAI7YmAHEQGwAAAAwH" + + "TQcGCWIHAQBGBwcAbhA1AAcACgdxEBsABwAMB00HBgpuMBcAVAbYAAABKN5xIAoAowBxIAoA" + + "ggAOAAMAAQABAAAA6BsAAAkAAAAS8fwQAAABAAoAcRATAAAADgAAANwHAAAAAAAAAAAAAAAA" + + "AADsBwAAAQAAAAMAAAAAAAAABQAAAPQHAAASAAAA/AcAABUAAAAECAAAFgAAAPwHAAABAAAA" + + "BAAAAAIAAAAVACYAAQAAAAEAAAABAAAAAgAAAAEAAAADAAAAAQAAAAUAAAABAAAAFAAAAAEA" + + "AAAVAAAAAQAAACQAAAADAAAAHgAVACAAAAADAAAAEgAVACAAAAADAAAAHQAEACUAAAADAAAA" + + "HQAEACYAAAACAAAAAAAAAAIAAAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAEAAQAAgAAAAUA" + + "BQACAAAAFAAUAAIAAAAVABUAAQAAAB0AAAACAAAAIgAiADEgQ2FsbCBzaXRlIGluc3RhbmNl" + + "ICMlMDJkIHdhcyBpbnZva2VkICUgMmQgdGltZXMKAC4gVGhyZWFkICUgMmQgaW52b2tlZCBj" + + "YWxsIHNpdGUgaW5zdGFuY2UgIyUwMmQKAAcgVm90ZXMgAAYsIGIyOiAABiwgYzI6IAAGLCBk" + + "MjogAAYsIGYyOiAABiwgaTI6IAAGLCBsMjogAAYsIG8yOiAABiwgczI6IAAFLWdldDAAATwA" + + "CDxjbGluaXQ+AAY8aW5pdD4AAj47AAFCAAFDABhDYWxsLXNpdGVzIGludm9jYXRpb25zIDoA" + + "AUQAAUYAAUkAAklJAA1JTlZPS0VfU1RBVElDAAFKAAFMAAJMQwACTEQAAkxGAAJMSQACTEoA" + + "AkxMAARMTElMAANMTEwABExMTEwAKUxUZXN0SW52b2tlQ3VzdG9tV2l0aENvbmN1cnJlbnRU" + + "aHJlYWRzJDE7ACdMVGVzdEludm9rZUN1c3RvbVdpdGhDb25jdXJyZW50VGhyZWFkczsAAkxa" + + "ADNMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsA" + + "MUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xpbmtlck1ldGhvZEhhbmRsZTsAL0xj" + + "b20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRsZUtpbmQ7ACJMZGFsdmlr" + + "L2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" + + "bGFzczsAIUxkYWx2aWsvYW5ub3RhdGlvbi9NZW1iZXJDbGFzc2VzOwAdTGRhbHZpay9hbm5v" + + "dGF0aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9p" + + "by9QcmludFN0cmVhbTsAGkxqYXZhL2xhbmcvQXNzZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5n" + + "L0NsYXNzOwATTGphdmEvbGFuZy9JbnRlZ2VyOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2" + + "YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcv" + + "U3lzdGVtOwASTGphdmEvbGFuZy9UaHJlYWQ7ABZMamF2YS9sYW5nL1RocmVhZExvY2FsABdM" + + "amF2YS9sYW5nL1RocmVhZExvY2FsOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" + + "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" + + "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" + + "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRs" + + "ZXM7AB1MamF2YS9sYW5nL2ludm9rZS9NZXRob2RUeXBlOwArTGphdmEvdXRpbC9jb25jdXJy" + + "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwARTlVNQkVSX09GX1RIUkVBRFMAAVMABFRZUEUA" + + "KlRlc3RJbnZva2VDdXN0b21XaXRoQ29uY3VycmVudFRocmVhZHMuamF2YQAkVGhyZWFkcyBk" + + "aWQgbm90IHRoZSBzYW1lIGNhbGwtc2l0ZXM6AAFWAANWQkIAA1ZDQwADVkREAANWRkYAAlZJ" + + "AANWSUkAAlZKAANWSkoAAlZMAANWTEwAA1ZTUwACVloACFdpbm5lcnMgAAFaAAJaTAASW0xq" + + "YXZhL2xhbmcvQ2xhc3M7ABNbTGphdmEvbGFuZy9PYmplY3Q7ABNbTGphdmEvbGFuZy9UaHJl" + + "YWQ7ABxbTGphdmEvbGFuZy9pbnZva2UvQ2FsbFNpdGU7ACxbTGphdmEvdXRpbC9jb25jdXJy" + + "ZW50L2F0b21pYy9BdG9taWNJbnRlZ2VyOwALYWNjZXNzRmxhZ3MABmFwcGVuZAANYXJndW1l" + + "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6IAARYXNzZXJ0RXF1YWxz" + + "IGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMgZjE6IAARYXNzZXJ0RXF1" + + "YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVhbHMgczE6IAASYXNzZXJ0" + + "RXF1YWxzOiBvMTogAA9hc3NlcnROb3RFcXVhbHMAFWFzc2VydE5vdEVxdWFsczogbzE6IAAK" + + "YXNzZXJ0VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAJiMQACYjIAAmMxAAJjMgAGY2FsbGVk" + + "AAZjYWxsZXIAAmQxAAJkMgANZHJvcEFyZ3VtZW50cwASZW1pdHRlcjogamFjay00LjI1AA1l" + + "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAGZm9ybWF0AANnZXQA" + + "D2dldEFuZEluY3JlbWVudAAOZ2V0VGhyZWFkSW5kZXgAAWkAAmkxAAJpMgAFaW5kZXgADGlu" + + "aXRpYWxWYWx1ZQAPaW5zZXJ0QXJndW1lbnRzAAxpbnN0YW50aWF0ZWQACGludFZhbHVlABJp" + + "bnZva2VNZXRob2RIYW5kbGUABGpvaW4ABGtpbmQAAmwxAAJsMgAMbGlua2VyTWV0aG9kAApt" + + "ZXRob2RUeXBlAAJtaAAEbmFtZQAJbmV4dEluZGV4AAdub3RVc2VkAAFvAANvdXQAAXAADnBh" + + "cmFtZXRlckNvdW50AAdwcmludGxuAApyZXR1cm5UeXBlAANydW4AAnMxAAJzMgADc2V0AAlz" + + "ZXRDYWxsZWQABXNsZWVwAAVzdGFydAAJdGFyZ2V0dGVkAAR0ZXN0AAR0aGlzAAt0aHJlYWRJ" + + "bmRleAAHdGhyZWFkcwAIdG9TdHJpbmcABHR5cGUABXZhbHVlAAd2YWx1ZU9mAAV2b3RlcwAH" + + "d2lubmVycwABeAAmAAcOAls7ACkABw4AJwAHDgAjAAcsiVg1TU0CbEoANgAHDgCqAQJsbQcO" + + "PACvAQJubwcOPADIAQJycwcOWgDDAQJ4eQcOWgC5AQKAAYEBBw48AL4BAooBiwEHDloAzQEC" + + "kgGUAQcOPLQA2QECmQGaAQcOLSClIAC0AQKZAZoBBw48ANMBApIBlAEHDjzSAKQBAaYBBw4t" + + "ARoQADkABw4AWANxjwGNAQcsTAMBogEFaQMAjgEeeLTDpbR8W9IAPQGqAQcOAE8BggEHDni0" + + "AG0AB1kBAQMAfwUtlpNBLQMBowEoPHhXQTxYQB4DA6kBBR4DAqgBBTyHpS2RQwEkEi2HPHgB" + + "FA06Qy2HPHgBFA06QTw8AEIABw5aAwCqAQU8AAILAaUBGAcCDAJbBAiOAR4CDgGlARwEFzcX" + + "DBcxFw8CDQGlARwBGAYCDwGlARwBGBoACARdHAEYBIYBHAEdCQRdHAMYHhgVGCB1GAeIARsG" + + "jgEXi44BF5uWARgEAQQQAxYAF5sVAQAAAQIAgIAEjBABBKQQAcQg0BAGABMBABoBCgEKARoB" + + "CgEaA4gg7BABiIAEhBEBgoAE0BEBCegRAQnIEgEJqBMBCYwUAQnwFAEJ0BUBCbQWAQmsFwEJ" + + "pBgBCYQZAQmAGgEKzBoBCvgaAQnMHAIK4BwBCZgdFAH4IAAAEwAAAAAAAAABAAAAAAAAAAEA" + + "AACqAAAAcAAAAAIAAAAqAAAAGAMAAAMAAAAmAAAAwAMAAAQAAAAJAAAAiAUAAAUAAAA4AAAA" + + "0AUAAAcAAAABAAAAkAcAAAYAAAACAAAAlAcAAAgAAAABAAAA1AcAAAMQAAAFAAAA3AcAAAEg" + + "AAAXAAAADAgAAAYgAAACAAAAnBAAAAEQAAAXAAAA3BAAAAIgAACqAAAApBEAAAMgAAAWAAAA" + + "1RoAAAQgAAAGAAAA9BsAAAUgAAACAAAAUBwAAAAgAAACAAAAWhwAAAAQAAABAAAA1BwAAA=="; +} diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java new file mode 100644 index 0000000000..443a7af187 --- /dev/null +++ b/test/952-invoke-custom/src/TestDataLinkerMethodMinimalArguments.java @@ -0,0 +1,106 @@ +/* Generated by build-test.sh from TestLinkerMethodMinimalArguments.java */ +public class TestDataLinkerMethodMinimalArguments { + public static final String BASE64_DEX_FILE = + "ZGV4CjAzOADnZpVEc25JsNXLCW+vh64OuLf8RymAuINwFQAAcAAAAHhWNBIAAAAAAAAAAIgU" + + "AACBAAAAcAAAAB0AAAB0AgAAHAAAAOgCAAAHAAAAOAQAACIAAABwBAAAAQAAAIQFAADEDwAA" + + "rAUAAKAMAACjDAAApwwAAKoMAACyDAAAugwAAMIMAADKDAAA0gwAANoMAADiDAAA6gwAAPQM" + + "AAD8DAAA/wwAAAINAAAFDQAACA0AADENAABUDQAAZw0AAIoNAACbDQAAng0AAKMNAACyDQAA" + + "tQ0AALgNAAC8DQAAwA0AAMQNAADIDQAAzA0AANANAADWDQAA+g0AAP4NAAAzDgAAZg4AAJcO" + + "AACzDgAAyg4AAOsOAAAHDwAAGg8AAD4PAABSDwAAZg8AAIEPAACVDwAArA8AAMkPAADuDwAA" + + "DxAAADgQAABXEAAAgBAAAIMQAACqEAAA0RAAAAQRAAAHEQAADBEAABERAAAWEQAAGxEAACAR" + + "AAAmEQAAKxEAAC8RAAA0EQAAOREAAD0RAABAEQAARBEAAEcRAABMEQAAVBEAAGMRAABxEQAA" + + "hBEAAJcRAACqEQAAvREAANARAADjEQAA9hEAAAoSAAAWEgAAKhIAAC0SAAAxEgAANRIAADkS" + + "AAA9EgAARRIAAEkSAABNEgAAYRIAAHASAAB4EgAAfBIAAIASAACNEgAAmRIAAKsSAACvEgAA" + + "sxIAAMcSAADNEgAA0RIAANUSAADjEgAA/xIAAAsTAAATEwAAGRMAABwTAAAhEwAAJBMAAC0T" + + "AAA5EwAAPRMAAEETAABHEwAATRMAAFcTAABeEwAAYRMAAA0AAAAOAAAADwAAABAAAAAWAAAA" + + "GQAAACIAAAAkAAAAJQAAACYAAAAnAAAAKAAAACkAAAAqAAAAKwAAACwAAAAtAAAALgAAAC8A" + + "AAAwAAAAMQAAADIAAAAzAAAANAAAADUAAAA2AAAAOAAAADwAAABIAAAAFwAAAAQAAADsCwAA" + + "GgAAABEAAAAAAAAAGwAAABIAAAD0CwAAHAAAABIAAAD8CwAAHQAAABIAAAAEDAAAHgAAABIA" + + "AAAMDAAAHwAAABIAAAAUDAAAIAAAABIAAAAcDAAAIAAAABIAAAAkDAAAIwAAABIAAAAsDAAA" + + "IQAAABUAAAA0DAAAIQAAABcAAABADAAAPAAAABsAAAAAAAAAPQAAABsAAABMDAAAPgAAABsA" + + "AABUDAAAPwAAABsAAABcDAAAQAAAABsAAABkDAAAQQAAABsAAADsCwAAQgAAABsAAABsDAAA" + + "QwAAABsAAAB4DAAARAAAABsAAAAcDAAARQAAABsAAACADAAARAAAABsAAAAkDAAARQAAABsA" + + "AACIDAAARAAAABsAAACQDAAARgAAABsAAACYDAAARwAAABsAAAAsDAAASQAAABwAAAAcDAAA" + + "BgAEABEAAAAGAAQAEgAAAAYABAATAAAABgAEABQAAAAGAAQAaAAAAAkACQAYAAAAEwALAHUA" + + "AAAGAAwACwAAAAYADAAMAAAABgAAAEsAAAAGAA0ATgAAAAYADgBOAAAABgAPAE4AAAAGABAA" + + "TgAAAAYAEQBOAAAABgATAE4AAAAGABUATgAAAAYAFwBOAAAABgAZAE4AAAAGABoAVwAAAAYA" + + "CgBvAAAABgASAHsAAAALABYAdwAAAAwAFgAMAAAADQAUAAwAAAAPABYADAAAABAADAAMAAAA" + + "EAAbAGMAAAARABsAYwAAABIADAAMAAAAEgACAEwAAAASAAMATAAAABIABABMAAAAEgAFAEwA" + + "AAASAAYATAAAABIABwBMAAAAEgAIAEwAAAASAAkATAAAABIAAQB9AAAAFgAYAAwAAAAYAAsA" + + "ZwAAADIUAAAGAAAAAQAAABAAAAAAAAAAOQAAAMQLAAA5FAAAAAAAAAQAAAANAAAAAQAAAAIU" + + "AAABAAAAKhQAAAEAAAAAAAAAZBMAAA8AAAASAGcABABnAAIAEhBnAAAAEiBnAAEAEjBnAAMA" + + "DgAAAAEAAQABAAAAcBMAAAQAAABwEBMAAAAOAAQAAgACAAAAdRMAABoAAABgAAQAYAEDADMQ" + + "EwBiAAYAGwE6AAAAbiAPABAAIgAMABsBSwAAAHAgEAAQACcAkAACAw8ABQACAAIAAAB/EwAA" + + "KAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJPAAAAbiAdACEADAFuIBoAMQAMARsCAwAAAG4g" + + "HQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACHEwAAKAAAADNDAwAOACIA" + + "DQAiARIAcBAWAAEAGwJQAAAAbiAdACEADAFuIBcAMQAMARsCBAAAAG4gHQAhAAwBbiAXAEEA" + + "DAFuEB8AAQAMAXAgEQAQACcACAAEAAMAAACPEwAAKgAAAC8ABAY5AAMADgAiAA0AIgESAHAQ" + + "FgABABsCUQAAAG4gHQAhAAwBbjAYAEEFDAEbAgUAAABuIB0AIQAMAW4wGABhBwwBbhAfAAEA" + + "DAFwIBEAEAAnAAUAAgACAAAAlxMAACoAAAAtAAMEOQADAA4AIgANACIBEgBwEBYAAQAbAlIA" + + "AABuIB0AIQAMAW4gGQAxAAwBGwIGAAAAbiAdACEADAFuIBkAQQAMAW4QHwABAAwBcCARABAA" + + "JwAFAAIAAgAAAJ8TAAAoAAAAM0MDAA4AIgANACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4g" + + "GgAxAAwBGwIHAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCARABAAJwAIAAQAAwAAAKcT" + + "AAAqAAAAMQAEBjkAAwAOACIADQAiARIAcBAWAAEAGwJUAAAAbiAdACEADAFuMBsAQQUMARsC" + + "CAAAAG4gHQAhAAwBbjAbAGEHDAFuEB8AAQAMAXAgEQAQACcABQACAAIAAACvEwAAMwAAADND" + + "AwAOADgDCwA4BAkAbiAUAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsCVgAAAG4gHQAhAAwB" + + "biAcADEADAEbAgkAAABuIB0AIQAMAW4gHABBAAwBbhAfAAEADAFwIBEAEAAnAAAABQACAAIA" + + "AAC4EwAAMwAAADNDAwAOADgDCwA4BAkAbiAVAEMACgA4AAMADgAiAA0AIgESAHAQFgABABsC" + + "VQAAAG4gHQAhAAwBbiAdADEADAEbAgoAAABuIB0AIQAMAW4gHQBBAAwBbhAfAAEADAFwIBEA" + + "EAAnAAAABQACAAIAAADDEwAAKAAAADNDAwAOACIADQAiARIAcBAWAAEAGwJVAAAAbiAdACEA" + + "DAFuIBoAMQAMARsCCgAAAG4gHQAhAAwBbiAaAEEADAFuEB8AAQAMAXAgEQAQACcABAABAAIA" + + "AADLEwAAHQAAADkDHAAiAA0AIgESAHAQFgABABsCWAAAAG4gHQAhAAwBbiAeADEADAFuEB8A" + + "AQAMAXAgEQAQACcADgAAAAcAAwAEAAAA1RMAAGoAAABiAQYAIgISAHAQFgACABsDcAAAAG4g" + + "HQAyAAwCYAMEAG4gGgAyAAwCbhAfAAIADAJuIA8AIQAcAQYAbkAhABRlDABgAQQAYAIAADMh" + + "KABiAQYAIgISAHAQFgACABsDNwAAAG4gHQAyAAwCbiAdAFIADAIbAwAAAABuIB0AMgAMAm4g" + + "HABiAAwCbhAfAAIADAJuIA8AIQASAREBYAEEAGACAQAzIRMAYgEGABsCOwAAAG4gDwAhACIB" + + "DwAbAm8AAABwIBIAIQAnASIBFgBwICAAAQARAQYAAwACAAAA7RMAAFAAAAASERICYAACADQD" + + "SAABEHEQDAAAAGAAAwA2A0IAcRAMAAEAZwMEAJAABAX8IAAAVAAKAXEgBwAQAGIABgAiARIA" + + "cBAWAAEAGwIVAAAAbiAdACEADAFuIBoAMQAMARsCAQAAAG4gHQAhAAwBbiAaAEEADAFuIBoA" + + "UQAMARsCAgAAAG4gHQAhAAwBbhAfAAEADAFuIA8AEAAOAAEgKLoBISi/AAAAAAAAAAADAAAA" + + "AAAAAAIAAACsBQAADQAAALQFAAAOAAAAtAUAAAIAAAAEAAQAAQAAAAEAAAABAAAAAgAAAAEA" + + "AAADAAAAAQAAAAQAAAABAAAABQAAAAEAAAAQAAAAAQAAABEAAAABAAAAHAAAAAMAAAAYABEA" + + "GQAAAAMAAAAOABEAGQAAAAIAAAAAAAAAAgAAAAEAAQACAAAAAgACAAIAAAADAAMAAwAAAAQA" + + "BAAEAAAAAgAAAAUABQACAAAAEAAQAAIAAAARABEAAQAAABcAAAACAAAAGgAaAAEgAAIgKAAB" + + "KQAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAABiwgbDI6IAAGLCBv" + + "MjogAAYsIHMyOiAACDxjbGluaXQ+AAY8aW5pdD4AAUIAAUMAAUQAAUYAJ0ZBSUxVUkVfVFlQ" + + "RV9MSU5LRVJfTUVUSE9EX1JFVFVSTlNfTlVMTAAhRkFJTFVSRV9UWVBFX0xJTktFUl9NRVRI" + + "T0RfVEhST1dTABFGQUlMVVJFX1RZUEVfTk9ORQAhRkFJTFVSRV9UWVBFX1RBUkdFVF9NRVRI" + + "T0RfVEhST1dTAA9GYWlsdXJlIFR5cGUgKyAAAUkAA0lJSQANSU5WT0tFX1NUQVRJQwABSgAB" + + "TAACTEMAAkxEAAJMRgACTEkAAkxKAAJMTAAETExMTAAiTFRlc3RMaW5rZXJNZXRob2RNaW5p" + + "bWFsQXJndW1lbnRzOwACTFoAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NhbGxl" + + "ZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTGlua2Vy" + + "TWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0aG9kSGFu" + + "ZGxlS2luZDsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0" + + "cmVhbTsAH0xqYXZhL2xhbmcvQXJpdGhtZXRpY0V4Y2VwdGlvbjsAGkxqYXZhL2xhbmcvQXNz" + + "ZXJ0aW9uRXJyb3I7ABFMamF2YS9sYW5nL0NsYXNzOwAiTGphdmEvbGFuZy9JbnN0YW50aWF0" + + "aW9uRXhjZXB0aW9uOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsA" + + "GUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRlcjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEv" + + "bGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xh" + + "bmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RI" + + "YW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZh" + + "L2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ACdSZXR1cm5pbmcgbnVsbCBpbnN0ZWFkIG9mIENh" + + "bGxTaXRlIGZvciAAAVMAJVRlc3RMaW5rZXJNZXRob2RNaW5pbWFsQXJndW1lbnRzLmphdmEA" + + "JVRocm93aW5nIEFyaXRobWV0aWNFeGNlcHRpb24gaW4gYWRkKCkAMVRocm93aW5nIEluc3Rh" + + "bnRpYXRpb25FeGNlcHRpb24gaW4gbGlua2VyTWV0aG9kKCkAAVYAA1ZCQgADVkNDAANWREQA" + + "A1ZGRgADVklJAARWSUlJAANWSkoAAlZMAANWTEwAA1ZTUwACVloAAVoAAlpMAAFhAANhZGQA" + + "BmFwcGVuZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABFhc3NlcnRFcXVhbHMgYjE6" + + "IAARYXNzZXJ0RXF1YWxzIGMxOiAAEWFzc2VydEVxdWFscyBkMTogABFhc3NlcnRFcXVhbHMg" + + "ZjE6IAARYXNzZXJ0RXF1YWxzIGkxOiAAEWFzc2VydEVxdWFscyBsMTogABFhc3NlcnRFcXVh" + + "bHMgczE6IAASYXNzZXJ0RXF1YWxzOiBvMTogAAphc3NlcnRUcnVlABJhc3NlcnRUcnVlIHZh" + + "bHVlOiAAAWIAAmIxAAJiMgACYzEAAmMyAAZjYWxsZXIAAmQxAAJkMgASZW1pdHRlcjogamFj" + + "ay00LjI1AA1lbmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgALZmFpbHVyZVR5cGUACmZp" + + "bmRTdGF0aWMAEGZvcmNlRmFpbHVyZVR5cGUAAmkxAAJpMgASaW52b2tlTWV0aG9kSGFuZGxl" + + "AARraW5kAAJsMQACbDIADGxpbmtlck1ldGhvZAAabGlua2VyTWV0aG9kIGZhaWx1cmUgdHlw" + + "ZSAACm1ldGhvZFR5cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1" + + "cm5UeXBlAAJzMQACczIABHRlc3QABHRoaXMACHRvU3RyaW5nAAV2YWx1ZQABeAABeQAeAAcd" + + "Li08PAJ5OwAcAAcOAC8CS1oHDmmHlwBWAltcBw48AFsCXV4HDjwAdAJgYQcOWgBvAmVmBw5a" + + "AGUCamsHDjwAagJubwcOWgB5AnV3Bw48tAB/Anp7Bw4tIKUgAGACensHDjwAUAF/Bw4tARoQ" + + "ADkDX3RyBw4BGxBpAwBzGGkBJA8taYeXAEgDZ4ABgQEHLId4LZYBLw8CeywtAAAHBE0cAhgE" + + "GARrHAEdCARNHAMYGBgRGBliGAZsGwVzF29zF0t4GAQCCgF+HAEYFAMWABdLFQAFAA8AAAoB" + + "CgEKAQoBCgCIgAS8CwGBgATsCwEKhAwBCcgMAQmoDQEJiA4BCewOAQnQDwEJsBABCZQRAQmM" + + "EgEJhBMBCeQTAQqwFAEJlBYAEwAAAAAAAAABAAAAAAAAAAEAAACBAAAAcAAAAAIAAAAdAAAA" + + "dAIAAAMAAAAcAAAA6AIAAAQAAAAHAAAAOAQAAAUAAAAiAAAAcAQAAAcAAAABAAAAgAUAAAYA" + + "AAABAAAAhAUAAAgAAAABAAAApAUAAAMQAAACAAAArAUAAAEgAAAPAAAAvAUAAAYgAAABAAAA" + + "xAsAAAEQAAAVAAAA7AsAAAIgAACBAAAAoAwAAAMgAAAPAAAAZBMAAAQgAAACAAAAAhQAAAUg" + + "AAABAAAAMhQAAAAgAAABAAAAORQAAAAQAAABAAAAiBQAAA=="; +} diff --git a/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java new file mode 100644 index 0000000000..b96e18486f --- /dev/null +++ b/test/952-invoke-custom/src/TestDataLinkerMethodMultipleArgumentTypes.java @@ -0,0 +1,108 @@ +/* Generated by build-test.sh from TestLinkerMethodMultipleArgumentTypes.java */ +public class TestDataLinkerMethodMultipleArgumentTypes { + public static final String BASE64_DEX_FILE = + "ZGV4CjAzOADmj8ccx56N3pWZ9IunuZvI0eWD+wmFmSnEFQAAcAAAAHhWNBIAAAAAAAAAANwU" + + "AACTAAAAcAAAAB0AAAC8AgAAHQAAADADAAADAAAAjAQAACIAAACkBAAAAQAAALgFAADkDwAA" + + "4AUAAEQMAABHDAAASgwAAFIMAABaDAAAYgwAAGoMAAByDAAAegwAAIIMAACKDAAAkgwAAJwM" + + "AACkDAAApwwAAKoMAACtDAAAsAwAAMYMAADNDAAA0AwAANUMAADkDAAA5wwAAOoMAADuDAAA" + + "8gwAAPYMAAD6DAAA/gwAAAINAAAIDQAAGA0AAEENAABFDQAAeg0AAKMNAADWDQAABw4AACYO" + + "AABCDgAATA4AAGMOAAB/DgAAkQ4AAKQOAAC6DgAAzg4AAOIOAAD9DgAAEQ8AACgPAABFDwAA" + + "ag8AAIsPAAC0DwAA0w8AANYPAAACEAAABRAAAAoQAAAPEAAAFBAAABkQAAAdEAAAIhAAACcQ" + + "AAArEAAAMBAAADUQAAA5EAAAPBAAAEUQAABJEAAATBAAAFEQAABZEAAAaBAAAHYQAACJEAAA" + + "nBAAAK8QAADCEAAA1RAAAOgQAAD7EAAADxEAABsRAAAvEQAAMhEAADYRAAA6EQAASBEAAFsR" + + "AABmEQAAahEAAG4RAAB2EQAAgREAAI0RAACREQAAlREAAKIRAAC2EQAAxREAAM0RAADREQAA" + + "1REAAOERAADtEQAA8REAAPURAAD/EQAAExIAABkSAAAdEgAAIRIAAC8SAAA6EgAAURIAAF0S" + + "AABlEgAAaxIAAG4SAABzEgAAdhIAAH8SAACLEgAAjxIAAJMSAACfEgAArBIAALISAAC4EgAA" + + "whIAAMYSAADLEgAAzxIAANMSAADXEgAA2xIAAN8SAADjEgAA5xIAAOsSAADyEgAA9RIAAA0A" + + "AAAOAAAADwAAABAAAAATAAAAFgAAACAAAAAiAAAAIwAAACQAAAAlAAAAJgAAACcAAAApAAAA" + + "KgAAACwAAAAuAAAALwAAADAAAAAxAAAAMgAAADMAAAA0AAAANQAAADYAAAA3AAAAOAAAADoA" + + "AABGAAAAEwAAAAQAAAAAAAAAFAAAAAQAAACICwAAFwAAABEAAAAAAAAAGAAAABIAAACQCwAA" + + "GQAAABIAAACYCwAAGgAAABIAAACgCwAAGwAAABIAAACoCwAAHAAAABIAAACwCwAAHQAAABIA" + + "AAC4CwAAHQAAABIAAADACwAAIQAAABIAAADICwAAHwAAABUAAADQCwAAHgAAABcAAADwCwAA" + + "OgAAABsAAAAAAAAAOwAAABsAAAD8CwAAPAAAABsAAAAEDAAAPQAAABsAAAAMDAAAPgAAABsA" + + "AAAUDAAAPwAAABsAAACoCwAAQAAAABsAAACICwAAQQAAABsAAAAcDAAAQgAAABsAAAC4CwAA" + + "QwAAABsAAAAkDAAAQgAAABsAAADACwAAQwAAABsAAAAsDAAAQgAAABsAAAA0DAAARAAAABsA" + + "AAA8DAAARQAAABsAAADICwAASAAAABwAAAC4CwAABgAEAFwAAAAKAAoAFQAAABMADQB7AAAA" + + "BgANAAsAAAAGAA0ADAAAAAYAAAARAAAABgABAEoAAAAGAA4ATQAAAAYADwBNAAAABgAQAE0A" + + "AAAGABEATQAAAAYAEwBNAAAABgAUAE0AAAAGABYATQAAAAYAGABNAAAABgAaAE0AAAAGABsA" + + "VgAAAAYACwB0AAAABgATAIMAAAANABIAfQAAAA0AFwB9AAAADgAVAAwAAAAQAA0ADAAAABAA" + + "HABoAAAAEQAcAGgAAAASAA0ADAAAABIAAwBLAAAAEgAEAEsAAAASAAUASwAAABIABgBLAAAA" + + "EgAHAEsAAAASAAgASwAAABIACQBLAAAAEgAKAEsAAAASAAIAhQAAABYAGQAMAAAAGAAMAGsA" + + "AABpFAAABgAAAAEAAAAQAAAAAAAAADkAAABgCwAAkhQAAAAAAAAEAAAADgAAAAEAAACpEwAA" + + "AgAAAEcUAABgFAAAAQAAAGAUAAABAAAAAAAAAPgSAAAEAAAAEgBnAAAADgABAAEAAQAAAP4S" + + "AAAEAAAAcBATAAAADgADAAIAAAAAAAMTAAADAAAAkAABAg8AAAAFAAIAAgAAAAoTAAAoAAAA" + + "M0MDAA4AIgAOACIBEgBwEBYAAQAbAk4AAABuIB0AIQAMAW4gGgAxAAwBGwICAAAAbiAdACEA" + + "DAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAABITAAAoAAAAM0MDAA4AIgAOACIB" + + "EgBwEBYAAQAbAk8AAABuIB0AIQAMAW4gFwAxAAwBGwIDAAAAbiAdACEADAFuIBcAQQAMAW4Q" + + "HwABAAwBcCASABAAJwAIAAQAAwAAABoTAAAqAAAALwAEBjkAAwAOACIADgAiARIAcBAWAAEA" + + "GwJQAAAAbiAdACEADAFuMBgAQQUMARsCBAAAAG4gHQAhAAwBbjAYAGEHDAFuEB8AAQAMAXAg" + + "EgAQACcABQACAAIAAAAiEwAAKgAAAC0AAwQ5AAMADgAiAA4AIgESAHAQFgABABsCUQAAAG4g" + + "HQAhAAwBbiAZADEADAEbAgUAAABuIB0AIQAMAW4gGQBBAAwBbhAfAAEADAFwIBIAEAAnAAUA" + + "AgACAAAAKhMAACgAAAAzQwMADgAiAA4AIgESAHAQFgABABsCUgAAAG4gHQAhAAwBbiAaADEA" + + "DAEbAgYAAABuIB0AIQAMAW4gGgBBAAwBbhAfAAEADAFwIBIAEAAnAAgABAADAAAAMhMAACoA" + + "AAAxAAQGOQADAA4AIgAOACIBEgBwEBYAAQAbAlMAAABuIB0AIQAMAW4wGwBBBQwBGwIHAAAA" + + "biAdACEADAFuMBsAYQcMAW4QHwABAAwBcCASABAAJwAFAAIAAgAAADoTAAAzAAAAM0MDAA4A" + + "OAMLADgECQBuIBQAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJVAAAAbiAdACEADAFuIBwA" + + "MQAMARsCCAAAAG4gHQAhAAwBbiAcAEEADAFuEB8AAQAMAXAgEgAQACcAAAAFAAIAAgAAAEMT" + + "AAAzAAAAM0MDAA4AOAMLADgECQBuIBUAQwAKADgAAwAOACIADgAiARIAcBAWAAEAGwJUAAAA" + + "biAdACEADAFuIB0AMQAMARsCCQAAAG4gHQAhAAwBbiAdAEEADAFuEB8AAQAMAXAgEgAQACcA" + + "AAAFAAIAAgAAAFETAAAoAAAAM0MDAA4AIgAOACIBEgBwEBYAAQAbAlQAAABuIB0AIQAMAW4g" + + "GgAxAAwBGwIJAAAAbiAdACEADAFuIBoAQQAMAW4QHwABAAwBcCASABAAJwAEAAEAAgAAAFsT" + + "AAAdAAAAOQMcACIADgAiARIAcBAWAAEAGwJXAAAAbiAdACEADAFuIB4AMQAMAW4QHwABAAwB" + + "cCASABAAJwAOAAAAFgAPAAQAAABmEwAAbAAAAGIDAgAiBBIAcBAWAAQAGwUoAAAAbiAdAFQA" + + "DARuIB0AhAAMBBsFAAAAAG4gHQBUAAwEbiAcAJQADARuEB8ABAAMBG4gEQBDAHEQDQAKABIT" + + "cSAIALMAEwNhAHEgBQDDABMDAARxIAgA0wASE3EgCADjABQDmpkxQXEgBwDzABgEmpmZmZmZ" + + "AUAFABAAcUAGAFQQGwMSAAAACAASAHEgCwADABwDBgAIABMAcSAKAAMAFwQVzVsHBQAUAHFA" + + "CQBUEBwDBgBuQCEAN5gMAiIDFgBwICAAIwARAwQAAgACAAAAmRMAABEAAACQAAID/CAAADIA" + + "CgFxIAgAEABiAAIAkAECA24gEAAQAA4AAAACAAEAAAAAAKQTAAADAAAAYAAAAA8AAAAAAAAA" + + "AAAAAAMAAAAAAAAAAwAAAOAFAAAOAAAA6AUAAA8AAAD0BQAAAgAAAAQABAABAAAAAQAAAAEA" + + "AAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAAFAAAAAQAAABAAAAABAAAAEQAAAAEAAAAcAAAA" + + "DQAAABgAEQAZABwAAAABABoABAADAAIAEQAPAAUAAAADAAAADwARABkAAAACAAAAAAAAAAIA" + + "AAABAAEAAgAAAAIAAgACAAAAAwADAAIAAAAFAAUAAgAAABAAEAACAAAAEQARAAEAAAAXAAAA" + + "AgAAABoAGgABIAABKAAGLCBiMjogAAYsIGMyOiAABiwgZDI6IAAGLCBmMjogAAYsIGkyOiAA" + + "BiwgbDI6IAAGLCBvMjogAAYsIHMyOiAABjwqPjtKKQAIPGNsaW5pdD4ABjxpbml0PgABQgAB" + + "QwABRAABRgAUR2V0Qm9vdHN0cmFwUnVuQ291bnQABUhlbGxvAAFJAANJSUkADUlOVk9LRV9T" + + "VEFUSUMAAUoAAUwAAkxDAAJMRAACTEYAAkxJAAJMSgACTEwABExMTEwADkxMTExaQkNTSUZE" + + "TExKACdMVGVzdExpbmtlck1ldGhvZE11bHRpcGxlQXJndW1lbnRUeXBlczsAAkxaADNMY29t" + + "L2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9DYWxsZWRCeUludm9rZUN1c3RvbTsAJ0xjb20v" + + "YW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0NvbnN0YW50OwAxTGNvbS9hbmRyb2lkL2phY2sv" + + "YW5ub3RhdGlvbnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5u" + + "b3RhdGlvbnMvTWV0aG9kSGFuZGxlS2luZDsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1" + + "cmU7ABpMZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAITGlua2luZyAAFUxqYXZhL2lvL1By" + + "aW50U3RyZWFtOwAaTGphdmEvbGFuZy9Bc3NlcnRpb25FcnJvcjsAEExqYXZhL2xhbmcvQ2xh" + + "c3MAEUxqYXZhL2xhbmcvQ2xhc3M7ABRMamF2YS9sYW5nL0NsYXNzPCo+OwASTGphdmEvbGFu" + + "Zy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVpbGRl" + + "cjsAEkxqYXZhL2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9s" + + "YW5nL2ludm9rZS9DYWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNp" + + "dGU7AB9MamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9r" + + "ZS9NZXRob2RIYW5kbGVzJExvb2t1cDsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7" + + "AAFTACpUZXN0TGlua2VyTWV0aG9kTXVsdGlwbGVBcmd1bWVudFR5cGVzLmphdmEAAVYAA1ZC" + + "QgADVkNDAANWREQAA1ZGRgACVkkAA1ZJSQADVkpKAAJWTAADVkxMAANWU1MAAlZaAAFaAAda" + + "QkNTSUZEAAJaTAABYQADYWRkAAZhcHBlbmQADWFyZ3VtZW50VHlwZXMADGFzc2VydEVxdWFs" + + "cwARYXNzZXJ0RXF1YWxzIGIxOiAAEWFzc2VydEVxdWFscyBjMTogABFhc3NlcnRFcXVhbHMg" + + "ZDE6IAARYXNzZXJ0RXF1YWxzIGYxOiAAEWFzc2VydEVxdWFscyBpMTogABFhc3NlcnRFcXVh" + + "bHMgbDE6IAARYXNzZXJ0RXF1YWxzIHMxOiAAEmFzc2VydEVxdWFsczogbzE6IAAKYXNzZXJ0" + + "VHJ1ZQASYXNzZXJ0VHJ1ZSB2YWx1ZTogAAFiAAJiMQACYjIADGJvb2xlYW5WYWx1ZQARYm9v" + + "dHN0cmFwUnVuQ291bnQACWJ5dGVWYWx1ZQACYzEAAmMyAAZjYWxsZXIACWNoYXJWYWx1ZQAK" + + "Y2xhc3NWYWx1ZQACZDEAAmQyAAtkb3VibGVWYWx1ZQASZW1pdHRlcjogamFjay00LjI1AA1l" + + "bmNsb3NpbmdUeXBlAAZlcXVhbHMAAmYxAAJmMgAKZmluZFN0YXRpYwAKZmxvYXRWYWx1ZQAC" + + "aTEAAmkyAAhpbnRWYWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAJsMQACbDIADGxp" + + "bmtlck1ldGhvZAAJbG9uZ1ZhbHVlABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MACm1ldGhvZFR5" + + "cGUABm1oX2FkZAAEbmFtZQABbwADb3V0AAFwAAdwcmludGxuAApyZXR1cm5UeXBlAAJzMQAC" + + "czIACnNob3J0VmFsdWUAC3N0cmluZ1ZhbHVlAAR0ZXN0AAR0aGlzAAh0b1N0cmluZwACdjEA" + + "A3YxMAACdjIAAnYzAAJ2NAACdjUAAnY2AAJ2NwACdjgAAnY5AAV2YWx1ZQABeAABeQAeAAcO" + + "OQAcAAcOADECSlkHDgBZAlpbBw48AF4CX2AHDjwAdwJkZQcOWgByAmprBw5aAGgCbm8HDjwA" + + "bQJzdAcOWgB8Ant9Bw48tACCAQKAAYEBBw4tIKUgAGMCgAGBAQcOPABTAZEBBw4tARoQADkN" + + "YXp4hwGJAYoBiwGMAY0BjgGPAQCIAQQTkAEQLgcOASQPPEtaWktppYd4iGkDAnkYAE4CkgGT" + + "AQcOlngASgAHDgAABwVMHAIYBBgEcBwBHQkETBwNGBgYERgZGBwYABgBGBoYBBgDGAIYERgP" + + "GAVnGAZxGwF5F3R2HAodCAFbHAE/HQgBXRwBAAEdCAFhHAEDYR0IAYEBHAEiAAQdCAFvHAEE" + + "AR0IAWwcAXCamTFBHQgBZRwB8ZqZmZmZmQFAHQgBggEcARcSHQgBYhwBGAYdCAF1HAFmFc1b" + + "B3kXSn4YBAILAZABHAkXARc2Fy8XNxdHFy8XKxcKFzMCDAGQARwBGBQNFgAXShUBBAEEAQRh" + + "JAAEBAFwmpkxQfGamZmZmZkBQBcSGAZmFc1bBwEADwEACgCIgAT8CwGBgASUDAIKrAwBCcQM" + + "AQmkDQEJhA4BCegOAQnMDwEJrBABCZARAQmIEgEJgBMBCeATAQqsFAEJlBYCAcgWEwAAAAAA" + + "AAABAAAAAAAAAAEAAACTAAAAcAAAAAIAAAAdAAAAvAIAAAMAAAAdAAAAMAMAAAQAAAADAAAA" + + "jAQAAAUAAAAiAAAApAQAAAcAAAABAAAAtAUAAAYAAAABAAAAuAUAAAgAAAABAAAA2AUAAAMQ" + + "AAADAAAA4AUAAAEgAAAQAAAA/AUAAAYgAAABAAAAYAsAAAEQAAAUAAAAiAsAAAIgAACTAAAA" + + "RAwAAAMgAAAQAAAA+BIAAAQgAAADAAAAqRMAAAUgAAABAAAAaRQAAAAgAAABAAAAkhQAAAAQ" + + "AAABAAAA3BQAAA=="; +} diff --git a/test/Android.bp b/test/Android.bp index d3244a683a..00c890a834 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -274,6 +274,7 @@ art_cc_defaults { "933-misc-events/misc_events.cc", "936-search-onload/search_onload.cc", "944-transform-classloaders/classloader.cc", + "945-obsolete-native/obsolete_native.cc", ], shared_libs: [ "libbase", diff --git a/test/dexdump/invoke-custom.dex b/test/dexdump/invoke-custom.dex Binary files differnew file mode 100644 index 0000000000..67261cad79 --- /dev/null +++ b/test/dexdump/invoke-custom.dex diff --git a/test/dexdump/invoke-custom.lst b/test/dexdump/invoke-custom.lst new file mode 100644 index 0000000000..3540bd10d5 --- /dev/null +++ b/test/dexdump/invoke-custom.lst @@ -0,0 +1,6 @@ +#invoke-custom.dex +0x000003fc 8 com.android.jack.java7.invokecustom.test004.Tests <init> ()V Tests.java 35 +0x00000414 6 com.android.jack.java7.invokecustom.test004.Tests add (II)I Tests.java 55 +0x0000042c 166 com.android.jack.java7.invokecustom.test004.Tests linkerMethod (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; Tests.java 62 +0x000004e4 24 com.android.jack.java7.invokecustom.test004.Tests main ([Ljava/lang/String;)V Tests.java 82 +0x0000050c 22 com.android.jack.java7.invokecustom.test004.Tests test ()V Tests.java 78 diff --git a/test/dexdump/invoke-custom.txt b/test/dexdump/invoke-custom.txt new file mode 100644 index 0000000000..e92549a086 --- /dev/null +++ b/test/dexdump/invoke-custom.txt @@ -0,0 +1,254 @@ +Processing 'invoke-custom.dex'... +Opened 'invoke-custom.dex', DEX version '038' +DEX file header: +magic : 'dex\n038\0' +checksum : db57516f +signature : 57be...ffc4 +file_size : 3276 +header_size : 112 +link_size : 0 +link_off : 0 (0x000000) +string_ids_size : 82 +string_ids_off : 112 (0x000070) +type_ids_size : 31 +type_ids_off : 440 (0x0001b8) +proto_ids_size : 16 +proto_ids_off : 564 (0x000234) +field_ids_size : 3 +field_ids_off : 756 (0x0002f4) +method_ids_size : 18 +method_ids_off : 780 (0x00030c) +class_defs_size : 1 +class_defs_off : 932 (0x0003a4) +data_size : 2304 +data_off : 972 (0x0003cc) + +Class #0 header: +class_idx : 10 +access_flags : 1 (0x0001) +superclass_idx : 15 +interfaces_off : 0 (0x000000) +source_file_idx : 38 +annotations_off : 1316 (0x000524) +class_data_off : 3014 (0x000bc6) +static_fields_size : 1 +instance_fields_size: 0 +direct_methods_size : 4 +virtual_methods_size: 1 + +Class #0 annotations: +Annotations on method #1 'add' + VISIBILITY_BUILD Lcom/android/jack/annotations/CalledByInvokeCustom; argumentTypes={ I I } invokeMethodHandle={ Lcom/android/jack/annotations/LinkerMethodHandle; argumentTypes={ Ljava/lang/invoke/MethodHandles$Lookup; Ljava/lang/String; Ljava/lang/invoke/MethodType; Z B C S I F D Ljava/lang/String; Ljava/lang/Class; J } enclosingType=Lcom/android/jack/java7/invokecustom/test004/Tests; kind=INVOKE_STATIC name="linkerMethod" } methodHandleExtraArgs={ Lcom/android/jack/annotations/Constant; booleanValue={ true } Lcom/android/jack/annotations/Constant; byteValue={ 1 } Lcom/android/jack/annotations/Constant; charValue={ 97 } Lcom/android/jack/annotations/Constant; shortValue={ 1024 } Lcom/android/jack/annotations/Constant; intValue={ 1 } Lcom/android/jack/annotations/Constant; floatValue={ 11.1 } Lcom/android/jack/annotations/Constant; doubleValue={ 2.2 } Lcom/android/jack/annotations/Constant; stringValue={ "Hello" } Lcom/android/jack/annotations/Constant; classValue={ Lcom/android/jack/java7/invokecustom/test004/Tests; } Lcom/android/jack/annotations/Constant; longValue={ 123456789 } } name="add" returnType=I +Annotations on method #2 'linkerMethod' + VISIBILITY_SYSTEM Ldalvik/annotation/Signature; value={ "(" "Ljava/lang/invoke/MethodHandles$Lookup;" "Ljava/lang/String;" "Ljava/lang/invoke/MethodType;" "ZBCSIFD" "Ljava/lang/String;" "Ljava/lang/Class" "<*>;J)" "Ljava/lang/invoke/CallSite;" } + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } +Annotations on method #4 'test' + VISIBILITY_SYSTEM Ldalvik/annotation/Throws; value={ Ljava/lang/Throwable; } + VISIBILITY_RUNTIME Lorg/junit/Test; + +Class #0 - + Class descriptor : 'Lcom/android/jack/java7/invokecustom/test004/Tests;' + Access flags : 0x0001 (PUBLIC) + Superclass : 'Ljava/lang/Object;' + Interfaces - + Static fields - + #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : 'fieldCallSite' + type : 'Ljava/lang/invoke/CallSite;' + access : 0x0009 (PUBLIC STATIC) + Instance fields - + Direct methods - + #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : '<init>' + type : '()V' + access : 0x10001 (PUBLIC CONSTRUCTOR) + code - + registers : 1 + ins : 1 + outs : 1 + insns size : 4 16-bit code units +0003ec: |[0003ec] com.android.jack.java7.invokecustom.test004.Tests.<init>:()V +0003fc: 7010 0600 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.<init>:()V // method@0006 +000402: 0e00 |0003: return-void + catches : (none) + positions : + 0x0000 line=35 + locals : + 0x0000 - 0x0004 reg=0 this Lcom/android/jack/java7/invokecustom/test004/Tests; + + #1 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : 'add' + type : '(II)I' + access : 0x000a (PRIVATE STATIC) + code - + registers : 3 + ins : 2 + outs : 0 + insns size : 3 16-bit code units +000404: |[000404] com.android.jack.java7.invokecustom.test004.Tests.add:(II)I +000414: 9000 0102 |0000: add-int v0, v1, v2 +000418: 0f00 |0002: return v0 + catches : (none) + positions : + 0x0000 line=55 + locals : + 0x0000 - 0x0003 reg=1 (null) I + 0x0000 - 0x0003 reg=2 (null) I + + #2 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : 'linkerMethod' + type : '(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;' + access : 0x000a (PRIVATE STATIC) + code - + registers : 24 + ins : 15 + outs : 6 + insns size : 83 16-bit code units +00041c: |[00041c] com.android.jack.java7.invokecustom.test004.Tests.linkerMethod:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; +00042c: 7110 1100 0c00 |0000: invoke-static {v12}, Ljunit/framework/Assert;.assertTrue:(Z)V // method@0011 +000432: 1212 |0003: const/4 v2, #int 1 // #1 +000434: 7120 0d00 d200 |0004: invoke-static {v2, v13}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d +00043a: 1302 6100 |0007: const/16 v2, #int 97 // #61 +00043e: 7120 0a00 e200 |0009: invoke-static {v2, v14}, Ljunit/framework/Assert;.assertEquals:(CC)V // method@000a +000444: 1302 0004 |000c: const/16 v2, #int 1024 // #400 +000448: 7120 0d00 f200 |000e: invoke-static {v2, v15}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d +00044e: 1212 |0011: const/4 v2, #int 1 // #1 +000450: 0200 1000 |0012: move/from16 v0, v16 +000454: 7120 0d00 0200 |0014: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d +00045a: 1202 |0017: const/4 v2, #int 0 // #0 +00045c: 1403 9a99 3141 |0018: const v3, #float 11.1 // #4131999a +000462: 0200 1100 |001b: move/from16 v0, v17 +000466: 7130 0c00 0302 |001d: invoke-static {v3, v0, v2}, Ljunit/framework/Assert;.assertEquals:(FFF)V // method@000c +00046c: 1606 0000 |0020: const-wide/16 v6, #int 0 // #0 +000470: 1802 9a99 9999 9999 0140 |0022: const-wide v2, #double 2.2 // #400199999999999a +00047a: 0504 1200 |0027: move-wide/from16 v4, v18 +00047e: 7706 0b00 0200 |0029: invoke-static/range {v2, v3, v4, v5, v6, v7}, Ljunit/framework/Assert;.assertEquals:(DDD)V // method@000b +000484: 1b02 0700 0000 |002c: const-string/jumbo v2, "Hello" // string@00000007 +00048a: 0800 1400 |002f: move-object/from16 v0, v20 +00048e: 7120 1000 0200 |0031: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/String;Ljava/lang/String;)V // method@0010 +000494: 1c02 0a00 |0034: const-class v2, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a +000498: 0800 1500 |0036: move-object/from16 v0, v21 +00049c: 7120 0f00 0200 |0038: invoke-static {v2, v0}, Ljunit/framework/Assert;.assertEquals:(Ljava/lang/Object;Ljava/lang/Object;)V // method@000f +0004a2: 1702 15cd 5b07 |003b: const-wide/32 v2, #float 1.6536e-34 // #075bcd15 +0004a8: 0500 1600 |003e: move-wide/from16 v0, v22 +0004ac: 7140 0e00 3210 |0040: invoke-static {v2, v3, v0, v1}, Ljunit/framework/Assert;.assertEquals:(JJ)V // method@000e +0004b2: 7100 0900 0000 |0043: invoke-static {}, Ljava/lang/invoke/MethodHandles;.lookup:()Ljava/lang/invoke/MethodHandles$Lookup; // method@0009 +0004b8: 0c02 |0046: move-result-object v2 +0004ba: 1c03 0a00 |0047: const-class v3, Lcom/android/jack/java7/invokecustom/test004/Tests; // type@000a +0004be: 6e40 0800 32ba |0049: invoke-virtual {v2, v3, v10, v11}, Ljava/lang/invoke/MethodHandles$Lookup;.findStatic:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle; // method@0008 +0004c4: 0c02 |004c: move-result-object v2 +0004c6: 2203 1400 |004d: new-instance v3, Ljava/lang/invoke/ConstantCallSite; // type@0014 +0004ca: 7020 0700 2300 |004f: invoke-direct {v3, v2}, Ljava/lang/invoke/ConstantCallSite;.<init>:(Ljava/lang/invoke/MethodHandle;)V // method@0007 +0004d0: 1103 |0052: return-object v3 + catches : (none) + positions : + 0x0000 line=62 + 0x0003 line=63 + 0x0007 line=64 + 0x000c line=65 + 0x0011 line=66 + 0x0017 line=67 + 0x0020 line=68 + 0x002c line=69 + 0x0034 line=70 + 0x003b line=71 + 0x0043 line=72 + 0x004d line=73 + locals : + 0x0000 - 0x0053 reg=9 (null) Ljava/lang/invoke/MethodHandles$Lookup; + 0x0000 - 0x0053 reg=10 (null) Ljava/lang/String; + 0x0000 - 0x0053 reg=11 (null) Ljava/lang/invoke/MethodType; + 0x0000 - 0x0053 reg=12 (null) Z + 0x0000 - 0x0053 reg=13 (null) B + 0x0000 - 0x0053 reg=14 (null) C + 0x0000 - 0x0053 reg=15 (null) S + 0x0000 - 0x0053 reg=16 (null) I + 0x0000 - 0x0053 reg=17 (null) F + 0x0000 - 0x0053 reg=18 (null) D + 0x0000 - 0x0053 reg=20 (null) Ljava/lang/String; + 0x0000 - 0x0053 reg=21 (null) Ljava/lang/Class; + 0x0000 - 0x0053 reg=22 (null) J + + #3 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : 'main' + type : '([Ljava/lang/String;)V' + access : 0x0009 (PUBLIC STATIC) + code - + registers : 4 + ins : 1 + outs : 2 + insns size : 12 16-bit code units +0004d4: |[0004d4] com.android.jack.java7.invokecustom.test004.Tests.main:([Ljava/lang/String;)V +0004e4: 6200 0200 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0002 +0004e8: 1221 |0002: const/4 v1, #int 2 // #2 +0004ea: 1232 |0003: const/4 v2, #int 3 // #3 +0004ec: fc20 0000 2100 |0004: invoke-custom {v1, v2}, call_site@0000 +0004f2: 0a01 |0007: move-result v1 +0004f4: 6e20 0500 1000 |0008: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(I)V // method@0005 +0004fa: 0e00 |000b: return-void + catches : (none) + positions : + 0x0000 line=82 + 0x000b line=83 + locals : + 0x0000 - 0x000c reg=3 (null) [Ljava/lang/String; + + Virtual methods - + #0 : (in Lcom/android/jack/java7/invokecustom/test004/Tests;) + name : 'test' + type : '()V' + access : 0x0001 (PUBLIC) + code - + registers : 3 + ins : 1 + outs : 2 + insns size : 11 16-bit code units +0004fc: |[0004fc] com.android.jack.java7.invokecustom.test004.Tests.test:()V +00050c: 1220 |0000: const/4 v0, #int 2 // #2 +00050e: 1231 |0001: const/4 v1, #int 3 // #3 +000510: fc20 0100 1000 |0002: invoke-custom {v0, v1}, call_site@0001 +000516: 0a00 |0005: move-result v0 +000518: 1251 |0006: const/4 v1, #int 5 // #5 +00051a: 7120 0d00 0100 |0007: invoke-static {v1, v0}, Ljunit/framework/Assert;.assertEquals:(II)V // method@000d +000520: 0e00 |000a: return-void + catches : (none) + positions : + 0x0000 line=78 + 0x000a line=79 + locals : + 0x0000 - 0x000b reg=2 this Lcom/android/jack/java7/invokecustom/test004/Tests; + + source_file_idx : 38 (Tests.java) + +Method handle #0: + type : invoke-static + target : Lcom/android/jack/java7/invokecustom/test004/Tests; linkerMethod + target_type : (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite; +Call site #0: + link_argument[0] : 0 (MethodHandle) + link_argument[1] : add (String) + link_argument[2] : (II)I (MethodType) + link_argument[3] : 1 (int) + link_argument[4] : 1 (int) + link_argument[5] : 97 (int) + link_argument[6] : 1024 (int) + link_argument[7] : 1 (int) + link_argument[8] : 11.1 (float) + link_argument[9] : 2.2 (double) + link_argument[10] : Hello (String) + link_argument[11] : Tests (Class) + link_argument[12] : 123456789 (long) +Call site #1: + link_argument[0] : 0 (MethodHandle) + link_argument[1] : add (String) + link_argument[2] : (II)I (MethodType) + link_argument[3] : 1 (int) + link_argument[4] : 1 (int) + link_argument[5] : 97 (int) + link_argument[6] : 1024 (int) + link_argument[7] : 1 (int) + link_argument[8] : 11.1 (float) + link_argument[9] : 2.2 (double) + link_argument[10] : Hello (String) + link_argument[11] : Tests (Class) + link_argument[12] : 123456789 (long) diff --git a/test/dexdump/invoke-custom.xml b/test/dexdump/invoke-custom.xml new file mode 100644 index 0000000000..2a2966738a --- /dev/null +++ b/test/dexdump/invoke-custom.xml @@ -0,0 +1,89 @@ +<api> +<package name="com.android.jack.java7.invokecustom.test004" +> +<class name="Tests" + extends="java.lang.Object" + interface="false" + abstract="false" + static="false" + final="false" + visibility="public" +> +<field name="fieldCallSite" + type="java.lang.invoke.CallSite" + transient="false" + volatile="false" + static="true" + final="false" + visibility="public" +> +</field> +<constructor name="Tests" + type="com.android.jack.java7.invokecustom.test004.Tests" + static="false" + final="false" + visibility="public" +> +</constructor> +<method name="main" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + visibility="public" +> +<parameter name="arg0" type="java.lang.String[]"> +</parameter> +</method> +<method name="test" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + visibility="public" +> +</method> +</class> +<method_handle index="0" + type="invoke-static" + target_class="Lcom/android/jack/java7/invokecustom/test004/Tests;" + target_member="linkerMethod" + target_member_type="(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;ZBCSIFDLjava/lang/String;Ljava/lang/Class;J)Ljava/lang/invoke/CallSite;" +> +</method_handle> +<call_site index="0"> +<link_argument index="0" type="MethodHandle" value="0"/> +<link_argument index="1" type="String" values="add"/> +<link_argument index="2" type="MethodType" value="(II)I"/> +<link_argument index="3" type="int" value="1"/> +<link_argument index="4" type="int" value="1"/> +<link_argument index="5" type="int" value="97"/> +<link_argument index="6" type="int" value="1024"/> +<link_argument index="7" type="int" value="1"/> +<link_argument index="8" type="float" value="11.1"/> +<link_argument index="9" type="double" value="2.2"/> +<link_argument index="10" type="String" value="Hello"/> +<link_argument index="11" type="Class" value="Tests"/> +<link_argument index="12" type="long" value="123456789"/> +</call_site> +<call_site index="1"> +<link_argument index="0" type="MethodHandle" value="0"/> +<link_argument index="1" type="String" values="add"/> +<link_argument index="2" type="MethodType" value="(II)I"/> +<link_argument index="3" type="int" value="1"/> +<link_argument index="4" type="int" value="1"/> +<link_argument index="5" type="int" value="97"/> +<link_argument index="6" type="int" value="1024"/> +<link_argument index="7" type="int" value="1"/> +<link_argument index="8" type="float" value="11.1"/> +<link_argument index="9" type="double" value="2.2"/> +<link_argument index="10" type="String" value="Hello"/> +<link_argument index="11" type="Class" value="Tests"/> +<link_argument index="12" type="long" value="123456789"/> +</call_site> +</package> +</api> diff --git a/test/etc/default-build b/test/etc/default-build index e9e388646a..3d7b7dd046 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -60,6 +60,12 @@ else HAS_SRC_DEX2OAT_UNRESOLVED=false fi +# Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store' +ZIP_COMPRESSION_METHOD="deflate" +# Align every ZIP file made by calling $ZIPALIGN command? +WITH_ZIP_ALIGN=false +ZIP_ALIGN_BYTES="-1" + DX_FLAGS="" SKIP_DX_MERGER="false" EXPERIMENTAL="" @@ -73,6 +79,7 @@ JACK_EXPERIMENTAL_ARGS["agents"]="-D jack.java.source.version=1.8 -D jack.androi JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" JACK_EXPERIMENTAL_ARGS["method-handles"]="-D jack.java.source.version=1.7 -D jack.android.min-api-level=o-b1" +JACK_EXPERIMENTAL_ARGS["java-8"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" declare -A SMALI_EXPERIMENTAL_ARGS SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24" @@ -118,6 +125,17 @@ while true; do DEFAULT_EXPERIMENT="" EXPERIMENTAL="${EXPERIMENTAL} $1" shift + elif [ "x$1" = "x--zip-compression-method" ]; then + # Allow using different zip compression method, e.g. 'store' + shift + ZIP_COMPRESSION_METHOD="$1" + shift + elif [ "x$1" = "x--zip-align" ]; then + # Align ZIP entries to some # of bytes. + shift + WITH_ZIP_ALIGN=true + ZIP_ALIGN_BYTES="$1" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -146,6 +164,26 @@ for experiment in ${EXPERIMENTAL}; do JAVAC_ARGS="${JAVAC_ARGS} ${JAVAC_EXPERIMENTAL_ARGS[${experiment}]}" done +######################################### + +# Catch all commands to 'ZIP' and prepend extra flags. +# Optionally, zipalign results to some alignment. +function zip() { + local zip_target="$1" + local entry_src="$2" + shift 2 + + command zip --compression-method "$ZIP_COMPRESSION_METHOD" "$zip_target" "$entry_src" "$@" + + if "$WITH_ZIP_ALIGN"; then + # zipalign does not operate in-place, so write results to a temp file. + local tmp_file="$(mktemp)" + "$ZIPALIGN" -f "$ZIP_ALIGN_BYTES" "$zip_target" "$tmp_file" + # replace original zip target with our temp file. + mv "$tmp_file" "$zip_target" + fi +} + if [ -e classes.dex ]; then zip $TEST_NAME.jar classes.dex exit 0 @@ -234,7 +272,7 @@ else fi fi -if [ "${HAS_SMALI}" = "true" ]; then +if [ "${HAS_SMALI}" = "true" -a ${NEED_DEX} = "true" ]; then # Compile Smali classes ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'` @@ -246,7 +284,7 @@ if [ "${HAS_SMALI}" = "true" ]; then fi fi -if [ "${HAS_SMALI_MULTIDEX}" = "true" ]; then +if [ "${HAS_SMALI_MULTIDEX}" = "true" -a ${NEED_DEX} = "true" ]; then # Compile Smali classes ${SMALI} -JXmx512m ${SMALI_ARGS} --output smali_classes2.dex `find smali-multidex -name '*.smali'` diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar index 186a1513ee..7d218f1d61 100755 --- a/test/etc/run-test-jar +++ b/test/etc/run-test-jar @@ -64,6 +64,10 @@ TEST_IS_NDEBUG="n" APP_IMAGE="y" VDEX_FILTER="" +# if "y", run 'sync' before dalvikvm to make sure all files from +# build step (e.g. dex2oat) were finished writing. +SYNC_BEFORE_RUN="n" + while true; do if [ "x$1" = "x--quiet" ]; then QUIET="y" @@ -262,6 +266,9 @@ while true; do option="$1" VDEX_FILTER="--compiler-filter=$option" shift + elif [ "x$1" = "x--sync" ]; then + SYNC_BEFORE_RUN="y" + shift elif expr "x$1" : "x--" >/dev/null 2>&1; then echo "unknown $0 option: $1" 1>&2 exit 1 @@ -491,6 +498,7 @@ dex2oat_cmdline="true" vdex_cmdline="true" mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA" strip_cmdline="true" +sync_cmdline="true" if [ "$PREBUILD" = "y" ]; then @@ -530,6 +538,10 @@ if [ "$STRIP_DEX" = "y" ]; then strip_cmdline="zip --quiet --delete $DEX_LOCATION/$TEST_NAME.jar classes.dex" fi +if [ "$SYNC_BEFORE_RUN" = "y" ]; then + sync_cmdline="sync" +fi + DALVIKVM_ISA_FEATURES_ARGS="" if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}" @@ -589,7 +601,8 @@ if [ "$HOST" = "n" ]; then LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH fi - PUBLIC_LIBS=libart.so:libartd.so + # System libraries needed by libarttestd.so + PUBLIC_LIBS=libart.so:libartd.so:libc++.so:libbacktrace.so:libbase.so:libnativehelper.so # Create a script with the command. The command can get longer than the longest # allowed adb command and there is no way to get the exit status from a adb shell @@ -607,6 +620,7 @@ if [ "$HOST" = "n" ]; then $dex2oat_cmdline && \ $vdex_cmdline && \ $strip_cmdline && \ + $sync_cmdline && \ $dalvikvm_cmdline" cmdfile=$(tempfile -p "cmd-" -s "-$TEST_NAME") @@ -679,7 +693,7 @@ else fi if [ "$DEV_MODE" = "y" ]; then - echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $cmdline" + echo "mkdir -p ${mkdir_locations} && $dex2oat_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline" fi cd $ANDROID_BUILD_TOP @@ -689,6 +703,7 @@ else $dex2oat_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } $vdex_cmdline || { echo "Dex2oat failed." >&2 ; exit 2; } $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; } + $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; } # For running, we must turn off logging when dex2oat or patchoat are missing. Otherwise we use # the same defaults as for prebuilt: everything when --dev, otherwise errors and above only. diff --git a/test/knownfailures.json b/test/knownfailures.json index 84df9241db..784f49c4b9 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -252,11 +252,6 @@ "variant": "jit" }, { - "test": "912-classes", - "variant": "jit", - "bug": "http://b/34655682" - }, - { "tests": ["570-checker-select", "484-checker-register-hints"], "description": ["These tests were based on the linear scan allocator,", diff --git a/test/run-test b/test/run-test index 27c700e89e..c926c115e2 100755 --- a/test/run-test +++ b/test/run-test @@ -90,6 +90,22 @@ fi export JACK="$JACK -g -cp $JACK_CLASSPATH" +# Zipalign is not on the PATH in some configs, auto-detect it. +if [ -z "$ZIPALIGN" ]; then + if which zipalign >/dev/null; then + ZIPALIGN="zipalign"; + else + # TODO: Add a dependency for zipalign in Android.run-test.mk + # once it doesn't depend on libandroidfw (b/35246701) + case "$OSTYPE" in + darwin*) ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/darwin/bin/zipalign" ;; + linux*) ZIPALIGN="$ANDROID_BUILD_TOP/prebuilts/sdk/tools/linux/bin/zipalign" ;; + *) echo "Can't find zipalign: unknown: $OSTYPE" >&2;; + esac + fi +fi +export ZIPALIGN + info="info.txt" build="build" run="run" @@ -495,7 +511,7 @@ elif [ "$runtime" = "art" ]; then run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}" else guess_target_arch_name - run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}:${android_root}/lib${suffix64}" + run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}" run_args="${run_args} --boot /data/art-test/core${image_suffix}${pic_image_suffix}${multi_image_suffix}.art" fi if [ "$relocate" = "yes" ]; then diff --git a/test/testrunner/env.py b/test/testrunner/env.py index 278980fd30..1dc8ce552c 100644 --- a/test/testrunner/env.py +++ b/test/testrunner/env.py @@ -15,6 +15,7 @@ # limitations under the License. import os +import re import tempfile import subprocess @@ -29,14 +30,52 @@ def getEnvBoolean(var, default): return False return default -def get_build_var(var_name): +_DUMP_MANY_VARS_LIST = ['HOST_2ND_ARCH_PREFIX', + 'TARGET_2ND_ARCH', + 'TARGET_ARCH', + 'HOST_PREFER_32_BIT', + 'HOST_OUT_EXECUTABLES'] +_DUMP_MANY_VARS = None # To be set to a dictionary with above list being the keys, + # and the build variable being the value. +def dump_many_vars(var_name): + """ + Reach into the Android build system to dump many build vars simultaneously. + Since the make system is so slow, we want to avoid calling into build frequently. + """ + global _DUMP_MANY_VARS + global _DUMP_MANY_VARS_LIST + + # Look up var from cache. + if _DUMP_MANY_VARS: + return _DUMP_MANY_VARS[var_name] + + all_vars=" ".join(_DUMP_MANY_VARS_LIST) + # The command is taken from build/envsetup.sh to fetch build variables. - command = ("CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " + command = ("CALLED_FROM_SETUP=true " # Enable the 'dump-many-vars' make target. + "BUILD_SYSTEM=build/core " # Set up lookup path for make includes. "make --no-print-directory -C \"%s\" -f build/core/config.mk " - "dumpvar-%s") % (ANDROID_BUILD_TOP, var_name) + "dump-many-vars DUMP_MANY_VARS=\"%s\"") % (ANDROID_BUILD_TOP, all_vars) + config = subprocess.Popen(command, stdout=subprocess.PIPE, - shell=True).communicate()[0] - return config.strip() + shell=True).communicate()[0] # read until EOF, select stdin + # Prints out something like: + # TARGET_ARCH='arm64' + # HOST_ARCH='x86_64' + _DUMP_MANY_VARS = {} + for line in config.split("\n"): + # Split out "$key='$value'" via regex. + match = re.search("([^=]+)='([^']*)", line) + if not match: + continue + key = match.group(1) + value = match.group(2) + _DUMP_MANY_VARS[key] = value + + return _DUMP_MANY_VARS[var_name] + +def get_build_var(var_name): + return dump_many_vars(var_name) def get_env(key): return env.get(key) diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index f60a6c9f2a..81b7953f3b 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -767,7 +767,10 @@ def main(): build_targets += 'test-art-host-run-test-dependencies' if 'target' in TARGET_TYPES: build_targets += 'test-art-target-run-test-dependencies' - build_command = 'make -j' + str(n_thread) + ' ' + build_targets + build_command = 'make' + build_command += ' -j' + str(n_thread) + build_command += ' -C ' + env.ANDROID_BUILD_TOP + build_command += ' ' + build_targets if subprocess.call(build_command.split()): sys.exit(1) if user_requested_test: @@ -786,7 +789,6 @@ def main(): except SystemExit: pass except: - print "hello" print_analysis() sys.exit(1) diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc index c5a93568c6..351857d1d9 100644 --- a/test/ti-agent/common_load.cc +++ b/test/ti-agent/common_load.cc @@ -122,6 +122,7 @@ static AgentLib agents[] = { { "942-private-recursive", common_redefine::OnLoad, nullptr }, { "943-private-recursive-jit", common_redefine::OnLoad, nullptr }, { "944-transform-classloaders", common_redefine::OnLoad, nullptr }, + { "945-obsolete-native", common_redefine::OnLoad, nullptr }, }; static AgentLib* FindAgent(char* name) { diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 41faa69c31..729a3e5ac4 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -123,7 +123,7 @@ done vogar_args="$vogar_args --timeout 480" # Use Jack with "1.8" configuration. -vogar_args="$vogar_args --toolchain jack --language JN" +vogar_args="$vogar_args --toolchain jack --language JO" # JIT settings. if $use_jit; then |