diff options
| -rw-r--r-- | compiler/dex/dex_to_dex_compiler.cc | 16 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.cc | 316 | ||||
| -rw-r--r-- | compiler/verifier_deps_test.cc | 63 | ||||
| -rw-r--r-- | libdexfile/dex/class_accessor-inl.h | 30 | ||||
| -rw-r--r-- | libdexfile/dex/class_accessor.h | 95 | ||||
| -rw-r--r-- | libdexfile/dex/dex_instruction_iterator.h | 2 | ||||
| -rw-r--r-- | test/980-redefine-object/redef_object.cc | 143 | ||||
| -rw-r--r-- | test/980-redefine-object/src/Main.java | 273 | ||||
| -rw-r--r-- | test/980-redefine-object/src/art/Redefinition.java | 91 | ||||
| -rw-r--r-- | test/Android.bp | 5 | ||||
| -rwxr-xr-x | tools/cleanup-buildbot-device.sh | 4 | ||||
| -rw-r--r-- | tools/dexanalyze/dexanalyze_experiments.cc | 6 | ||||
| -rwxr-xr-x | tools/setup-buildbot-device.sh | 5 | ||||
| -rwxr-xr-x | tools/teardown-buildbot-device.sh | 70 |
14 files changed, 474 insertions, 645 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index be8641fd86..68155d844a 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -26,6 +26,7 @@ #include "base/mutex.h" #include "compiled_method.h" #include "dex/bytecode_utils.h" +#include "dex/class_accessor-inl.h" #include "dex/dex_file-inl.h" #include "dex/dex_instruction-inl.h" #include "dex_to_dex_decompiler.h" @@ -633,21 +634,14 @@ void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) // item. std::unordered_set<const DexFile::CodeItem*> seen_code_items; for (const DexFile* dex_file : dex_files) { - for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - for (; it.HasNextMethod(); it.Next()) { - const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + for (ClassAccessor accessor : dex_file->GetClasses()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const DexFile::CodeItem* code_item = method.GetCodeItem(); // Detect the shared code items. if (!seen_code_items.insert(code_item).second) { shared_code_items_.insert(code_item); } - } + }); } } VLOG(compiler) << "Shared code items " << shared_code_items_.size(); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 7dc44fa44b..1b809d232a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -42,6 +42,7 @@ #include "compiler.h" #include "compiler_callbacks.h" #include "compiler_driver-inl.h" +#include "dex/class_accessor-inl.h" #include "dex/descriptors_names.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_annotations.h" @@ -771,39 +772,6 @@ void CompilerDriver::Resolve(jobject class_loader, } } -// Resolve const-strings in the code. Done to have deterministic allocation behavior. Right now -// this is single-threaded for simplicity. -// TODO: Collect the relevant string indices in parallel, then allocate them sequentially in a -// stable order. - -static void ResolveConstStrings(ClassLinker* class_linker, - Handle<mirror::DexCache> dex_cache, - const DexFile& dex_file, - const DexFile::CodeItem* code_item) - REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { - switch (inst->Opcode()) { - case Instruction::CONST_STRING: - case Instruction::CONST_STRING_JUMBO: { - dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) - ? inst->VRegB_21c() - : inst->VRegB_31c()); - ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache); - CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; - break; - } - - default: - break; - } - } -} - static void ResolveConstStrings(CompilerDriver* driver, const std::vector<const DexFile*>& dex_files, TimingLogger* timings) { @@ -816,33 +784,35 @@ static void ResolveConstStrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Resolve const-string Strings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not resolve const-string in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + // Resolve const-strings in the code. Done to have deterministic allocation behavior. Right + // now this is single-threaded for simplicity. + // TODO: Collect the relevant string indices in parallel, then allocate them sequentially + // in a stable order. + for (const DexInstructionPcPair& inst : method.GetInstructions()) { + switch (inst->Opcode()) { + case Instruction::CONST_STRING: + case Instruction::CONST_STRING_JUMBO: { + dex::StringIndex string_index((inst->Opcode() == Instruction::CONST_STRING) + ? inst->VRegB_21c() + : inst->VRegB_31c()); + ObjPtr<mirror::String> string = class_linker->ResolveString(string_index, dex_cache); + CHECK(string != nullptr) << "Could not allocate a string when forcing determinism"; + break; + } - // Direct and virtual methods. - while (it.HasNextMethod()) { - ResolveConstStrings(class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + default: + break; + } + } + }); } } } @@ -856,14 +826,9 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, ClassLinker* class_linker, Handle<mirror::DexCache> dex_cache, const DexFile& dex_file, - const DexFile::CodeItem* code_item) + const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) { - if (code_item == nullptr) { - // Abstract or native method. - return; - } - - for (const DexInstructionPcPair& inst : CodeItemInstructionAccessor(dex_file, code_item)) { + for (const DexInstructionPcPair& inst : method.GetInstructions()) { switch (inst->Opcode()) { case Instruction::CHECK_CAST: case Instruction::INSTANCE_OF: { @@ -907,34 +872,18 @@ static void InitializeTypeCheckBitstrings(CompilerDriver* driver, dex_cache.Assign(class_linker->FindDexCache(soa.Self(), *dex_file)); TimingLogger::ScopedTiming t("Initialize type check bitstrings", timings); - size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface - continue; - } - - ClassDataItemIterator it(*dex_file, class_data); - it.SkipAllFields(); - - bool compilation_enabled = driver->IsClassToCompile( - dex_file->StringByTypeIdx(class_def.class_idx_)); - if (!compilation_enabled) { + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (!driver->IsClassToCompile(accessor.GetDescriptor())) { // Compilation is skipped, do not look for type checks in code of this class. // FIXME: Make sure that inlining honors this. b/26687569 continue; } // Direct and virtual methods. - while (it.HasNextMethod()) { - InitializeTypeCheckBitstrings( - driver, class_linker, dex_cache, *dex_file, it.GetMethodCodeItem()); - it.Next(); - } - DCHECK(!it.HasNext()); + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + InitializeTypeCheckBitstrings(driver, class_linker, dex_cache, *dex_file, method); + }); } } } @@ -954,10 +903,8 @@ static void EnsureVerifiedOrVerifyAtRuntime(jobject jclass_loader, ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (const DexFile* dex_file : dex_files) { - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - 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)); + for (ClassAccessor accessor : dex_file->GetClasses()) { + cls.Assign(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader)); if (cls == nullptr) { soa.Self()->ClearException(); } else if (&cls->GetDexFile() == dex_file) { @@ -1740,22 +1687,16 @@ static void CheckAndClearResolveException(Thread* self) bool CompilerDriver::RequiresConstructorBarrier(const DexFile& dex_file, uint16_t class_def_idx) const { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_idx); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - return false; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipStaticFields(); + ClassAccessor accessor(dex_file, dex_file.GetClassDef(class_def_idx)); + bool has_is_final = false; // We require a constructor barrier if there are final instance fields. - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - return true; + accessor.VisitFields(/*static*/ VoidFunctor(), + [&](const ClassAccessor::Field& field) { + if (field.IsFinal()) { + has_is_final = true; } - it.Next(); - } - return false; + }); + return has_is_final; } class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { @@ -1770,11 +1711,6 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { const DexFile& dex_file = *manager_->GetDexFile(); ClassLinker* class_linker = manager_->GetClassLinker(); - // If an instance field is final then we need to have a barrier on the return, static final - // fields are assigned within the lock held for class initialization. Conservatively assume - // constructor barriers are always required. - bool requires_constructor_barrier = true; - // Method and Field are the worst. We can't resolve without either // context from the code use (to disambiguate virtual vs direct // method and instance vs static field) or from class @@ -1806,56 +1742,53 @@ class ResolveClassFieldsAndMethodsVisitor : public CompilationVisitor { // We want to resolve the methods and fields eagerly. resolve_fields_and_methods = true; } - // Note the class_data pointer advances through the headers, - // static fields, instance fields, direct methods, and virtual - // methods. - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // Empty class such as a marker interface. - requires_constructor_barrier = false; - } else { - ClassDataItemIterator it(dex_file, class_data); - while (it.HasNextStaticField()) { - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ true); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); - } + // If an instance field is final then we need to have a barrier on the return, static final + // fields are assigned within the lock held for class initialization. + bool requires_constructor_barrier = false; + + ClassAccessor accessor(dex_file, class_def); + // Optionally resolve fields and methods and figure out if we need a constructor barrier. + auto method_visitor = [&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtMethod* resolved = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( + method.GetIndex(), + dex_cache, + class_loader, + /* referrer */ nullptr, + method.GetInvokeType(class_def.access_flags_)); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); } - it.Next(); } - // We require a constructor barrier if there are final instance fields. - requires_constructor_barrier = false; - while (it.HasNextInstanceField()) { - if (it.MemberIsFinal()) { - requires_constructor_barrier = true; - } - if (resolve_fields_and_methods) { - ArtField* field = class_linker->ResolveField( - it.GetMemberIndex(), dex_cache, class_loader, /* is_static */ false); - if (field == nullptr) { - CheckAndClearResolveException(soa.Self()); + }; + accessor.VisitFieldsAndMethods( + // static fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ true); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } } - } - it.Next(); - } - if (resolve_fields_and_methods) { - while (it.HasNextMethod()) { - ArtMethod* method = class_linker->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - it.GetMemberIndex(), - dex_cache, - class_loader, - /* referrer */ nullptr, - it.GetMethodInvokeType(class_def)); - if (method == nullptr) { - CheckAndClearResolveException(soa.Self()); + }, + // instance fields + [&](ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) { + if (field.IsFinal()) { + // We require a constructor barrier if there are final instance fields. + requires_constructor_barrier = true; } - it.Next(); - } - DCHECK(!it.HasNext()); - } - } + if (resolve_fields_and_methods) { + ArtField* resolved = class_linker->ResolveField( + field.GetIndex(), dex_cache, class_loader, /* is_static */ false); + if (resolved == nullptr) { + CheckAndClearResolveException(soa.Self()); + } + } + }, + /*direct methods*/ method_visitor, + /*virtual methods*/ method_visitor); manager_->GetCompiler()->SetRequiresConstructorBarrier(self, &dex_file, class_def_index, @@ -1942,32 +1875,13 @@ void CompilerDriver::SetVerified(jobject class_loader, } } -static void PopulateVerifiedMethods(const DexFile& dex_file, - uint32_t class_def_index, - VerificationResults* verification_results) { - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - return; - } - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - - while (it.HasNextMethod()) { - verification_results->CreateVerifiedMethodFor(MethodReference(&dex_file, it.GetMemberIndex())); - it.Next(); - } - DCHECK(!it.HasNext()); -} - -static void LoadAndUpdateStatus(const DexFile& dex_file, - const DexFile::ClassDef& class_def, +static void LoadAndUpdateStatus(const ClassAccessor& accessor, ClassStatus status, Handle<mirror::ClassLoader> class_loader, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { StackHandleScope<1> hs(self); - const char* descriptor = dex_file.GetClassDescriptor(class_def); + const char* descriptor = accessor.GetDescriptor(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Handle<mirror::Class> cls(hs.NewHandle<mirror::Class>( class_linker->FindClass(self, descriptor, class_loader))); @@ -1975,7 +1889,7 @@ static void LoadAndUpdateStatus(const DexFile& dex_file, // 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. - if (&cls->GetDexFile() == &dex_file) { + if (&cls->GetDexFile() == &accessor.GetDexFile()) { ObjectLock<mirror::Class> lock(self, cls); mirror::Class::SetStatus(cls, status, self); } @@ -2014,13 +1928,13 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, // Fetch the list of unverified classes. const std::set<dex::TypeIndex>& unverified_classes = verifier_deps->GetUnverifiedClasses(*dex_file); - for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); - if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) { + uint32_t class_def_idx = 0u; + for (ClassAccessor accessor : dex_file->GetClasses()) { + if (unverified_classes.find(accessor.GetClassIdx()) == unverified_classes.end()) { if (compiler_only_verifies) { // Just update the compiled_classes_ map. The compiler doesn't need to resolve // the type. - ClassReference ref(dex_file, i); + ClassReference ref(dex_file, class_def_idx); ClassStatus existing = ClassStatus::kNotReady; DCHECK(compiled_classes_.Get(ref, &existing)) << ref.dex_file->GetLocation(); ClassStateTable::InsertResult result = @@ -2029,26 +1943,27 @@ bool CompilerDriver::FastVerify(jobject jclass_loader, } else { // Update the class status, so later compilation stages know they don't need to verify // the class. - LoadAndUpdateStatus( - *dex_file, class_def, ClassStatus::kVerified, class_loader, soa.Self()); + LoadAndUpdateStatus(accessor, ClassStatus::kVerified, class_loader, soa.Self()); // Create `VerifiedMethod`s for each methods, the compiler expects one for // quickening or compiling. // Note that this means: // - We're only going to compile methods that did verify. // - Quickening will not do checkcast ellision. // TODO(ngeoffray): Reconsider this once we refactor compiler filters. - PopulateVerifiedMethods(*dex_file, i, verification_results_); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + verification_results_->CreateVerifiedMethodFor(method.GetReference()); + }); } } else if (!compiler_only_verifies) { // Make sure later compilation stages know they should not try to verify // this class again. - LoadAndUpdateStatus(*dex_file, - class_def, + LoadAndUpdateStatus(accessor, ClassStatus::kRetryVerificationAtRuntime, class_loader, soa.Self()); } } + ++class_def_idx; } return true; } @@ -2784,22 +2699,22 @@ static void CompileDexFile(CompilerDriver* driver, auto compile = [&context, &compile_fn](size_t class_def_index) { ScopedTrace trace(__FUNCTION__); const DexFile& dex_file = *context.GetDexFile(); - const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); ClassLinker* class_linker = context.GetClassLinker(); jobject jclass_loader = context.GetClassLoader(); ClassReference ref(&dex_file, class_def_index); + const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); + ClassAccessor accessor(dex_file, class_def); // Skip compiling classes with generic verifier failures since they will still fail at runtime if (context.GetCompiler()->GetVerificationResults()->IsClassRejected(ref)) { return; } // Use a scoped object access to perform to the quick SkipClass check. - const char* descriptor = dex_file.GetClassDescriptor(class_def); ScopedObjectAccess soa(Thread::Current()); StackHandleScope<3> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader( hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader))); Handle<mirror::Class> klass( - hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader))); + hs.NewHandle(class_linker->FindClass(soa.Self(), accessor.GetDescriptor(), class_loader))); Handle<mirror::DexCache> dex_cache; if (klass == nullptr) { soa.Self()->AssertPendingException(); @@ -2814,9 +2729,8 @@ static void CompileDexFile(CompilerDriver* driver, dex_cache = hs.NewHandle(klass->GetDexCache()); } - const uint8_t* class_data = dex_file.GetClassData(class_def); - if (class_data == nullptr) { - // empty class, probably a marker interface + // Avoid suspension if there are no methods to compile. + if (accessor.NumDirectMethods() + accessor.NumVirtualMethods() == 0) { return; } @@ -2829,28 +2743,24 @@ static void CompileDexFile(CompilerDriver* driver, optimizer::DexToDexCompiler::CompilationLevel dex_to_dex_compilation_level = GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def); - ClassDataItemIterator it(dex_file, class_data); - it.SkipAllFields(); - bool compilation_enabled = driver->IsClassToCompile( - dex_file.StringByTypeIdx(class_def.class_idx_)); + const bool compilation_enabled = driver->IsClassToCompile(accessor.GetDescriptor()); // Compile direct and virtual methods. int64_t previous_method_idx = -1; - while (it.HasNextMethod()) { - uint32_t method_idx = it.GetMemberIndex(); + accessor.VisitMethods([&](const ClassAccessor::Method& method) { + const uint32_t method_idx = method.GetIndex(); if (method_idx == previous_method_idx) { // smali can create dex files with two encoded_methods sharing the same method_idx // http://code.google.com/p/smali/issues/detail?id=119 - it.Next(); - continue; + return; } previous_method_idx = method_idx; compile_fn(soa.Self(), driver, - it.GetMethodCodeItem(), - it.GetMethodAccessFlags(), - it.GetMethodInvokeType(class_def), + method.GetCodeItem(), + method.GetAccessFlags(), + method.GetInvokeType(class_def.access_flags_), class_def_index, method_idx, class_loader, @@ -2858,9 +2768,7 @@ static void CompileDexFile(CompilerDriver* driver, dex_to_dex_compilation_level, compilation_enabled, dex_cache); - it.Next(); - } - DCHECK(!it.HasNext()); + }); }; context.ForAllLambda(0, dex_file.NumClassDefs(), compile, thread_count); } diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc index 06f0bcdec7..103862beff 100644 --- a/compiler/verifier_deps_test.cc +++ b/compiler/verifier_deps_test.cc @@ -22,6 +22,8 @@ #include "class_linker.h" #include "common_compiler_test.h" #include "compiler_callbacks.h" +#include "dex/class_accessor-inl.h" +#include "dex/class_iterator.h" #include "dex/dex_file-inl.h" #include "dex/dex_file_types.h" #include "dex/verification_results.h" @@ -148,48 +150,45 @@ class VerifierDepsTest : public CommonCompilerTest { Handle<mirror::DexCache> dex_cache_handle(hs.NewHandle(klass_Main_->GetDexCache())); const DexFile::ClassDef* class_def = klass_Main_->GetClassDef(); - const uint8_t* class_data = primary_dex_file_->GetClassData(*class_def); - CHECK(class_data != nullptr); + ClassAccessor accessor(*primary_dex_file_, *class_def); - ClassDataItemIterator it(*primary_dex_file_, class_data); - it.SkipAllFields(); + bool has_failures = true; + bool found_method = false; - ArtMethod* method = nullptr; - while (it.HasNextDirectMethod()) { + accessor.VisitMethods([&](const ClassAccessor::Method& method) + REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod* resolved_method = class_linker_->ResolveMethod<ClassLinker::ResolveMode::kNoChecks>( - it.GetMemberIndex(), + method.GetIndex(), dex_cache_handle, class_loader_handle, /* referrer */ nullptr, - it.GetMethodInvokeType(*class_def)); + method.GetInvokeType(class_def->access_flags_)); CHECK(resolved_method != nullptr); if (method_name == resolved_method->GetName()) { - method = resolved_method; - break; + soa.Self()->SetVerifierDeps(callbacks_->GetVerifierDeps()); + MethodVerifier verifier(soa.Self(), + primary_dex_file_, + dex_cache_handle, + class_loader_handle, + *class_def, + method.GetCodeItem(), + method.GetIndex(), + resolved_method, + method.GetAccessFlags(), + true /* can_load_classes */, + true /* allow_soft_failures */, + true /* need_precise_constants */, + false /* verify to dump */, + true /* allow_thread_suspension */); + verifier.Verify(); + soa.Self()->SetVerifierDeps(nullptr); + has_failures = verifier.HasFailures(); + found_method = true; } - it.Next(); - } - CHECK(method != nullptr); - - Thread::Current()->SetVerifierDeps(callbacks_->GetVerifierDeps()); - MethodVerifier verifier(Thread::Current(), - primary_dex_file_, - dex_cache_handle, - class_loader_handle, - *class_def, - it.GetMethodCodeItem(), - it.GetMemberIndex(), - method, - it.GetMethodAccessFlags(), - true /* can_load_classes */, - true /* allow_soft_failures */, - true /* need_precise_constants */, - false /* verify to dump */, - true /* allow_thread_suspension */); - verifier.Verify(); - Thread::Current()->SetVerifierDeps(nullptr); - return !verifier.HasFailures(); + }); + CHECK(found_method) << "Expected to find method " << method_name; + return !has_failures; } void VerifyDexFile(const char* multidex = nullptr) { diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h index 5cfbcaa359..a082142366 100644 --- a/libdexfile/dex/class_accessor-inl.h +++ b/libdexfile/dex/class_accessor-inl.h @@ -38,14 +38,14 @@ inline ClassAccessor::ClassAccessor(const DexFile& dex_file, const DexFile::Clas num_virtual_methods_(ptr_pos_ != nullptr ? DecodeUnsignedLeb128(&ptr_pos_) : 0u) {} inline const uint8_t* ClassAccessor::Method::Read(const uint8_t* ptr) { - method_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); code_off_ = DecodeUnsignedLeb128(&ptr); return ptr; } inline const uint8_t* ClassAccessor::Field::Read(const uint8_t* ptr) { - field_idx_ += DecodeUnsignedLeb128(&ptr); + index_ += DecodeUnsignedLeb128(&ptr); access_flags_ = DecodeUnsignedLeb128(&ptr); return ptr; } @@ -54,7 +54,7 @@ template <typename StaticFieldVisitor, typename InstanceFieldVisitor, typename DirectMethodVisitor, typename VirtualMethodVisitor> -inline void ClassAccessor::VisitMethodsAndFields( +inline void ClassAccessor::VisitFieldsAndMethods( const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, @@ -75,14 +75,14 @@ inline void ClassAccessor::VisitMethodsAndFields( } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ true); for (size_t i = 0; i < num_direct_methods_; ++i) { ptr = data.Read(ptr); direct_method_visitor(data); } } { - Method data(dex_file_); + Method data(dex_file_, /*is_static_or_direct*/ false); for (size_t i = 0; i < num_virtual_methods_; ++i) { ptr = data.Read(ptr); virtual_method_visitor(data); @@ -94,12 +94,22 @@ template <typename DirectMethodVisitor, typename VirtualMethodVisitor> inline void ClassAccessor::VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const { - VisitMethodsAndFields(VoidFunctor(), + VisitFieldsAndMethods(VoidFunctor(), VoidFunctor(), direct_method_visitor, virtual_method_visitor); } +template <typename StaticFieldVisitor, + typename InstanceFieldVisitor> +inline void ClassAccessor::VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const { + VisitFieldsAndMethods(static_field_visitor, + instance_field_visitor, + VoidFunctor(), + VoidFunctor()); +} + // Visit direct and virtual methods. template <typename MethodVisitor> inline void ClassAccessor::VisitMethods(const MethodVisitor& method_visitor) const { @@ -114,6 +124,14 @@ inline CodeItemInstructionAccessor ClassAccessor::Method::GetInstructions() cons return CodeItemInstructionAccessor(dex_file_, dex_file_.GetCodeItem(GetCodeItemOffset())); } +inline const char* ClassAccessor::GetDescriptor() const { + return dex_file_.StringByTypeIdx(descriptor_index_); +} + +inline const DexFile::CodeItem* ClassAccessor::Method::GetCodeItem() const { + return dex_file_.GetCodeItem(code_off_); +} + } // namespace art #endif // ART_LIBDEXFILE_DEX_CLASS_ACCESSOR_INL_H_ diff --git a/libdexfile/dex/class_accessor.h b/libdexfile/dex/class_accessor.h index 835c4e2eb7..72bc50b98c 100644 --- a/libdexfile/dex/class_accessor.h +++ b/libdexfile/dex/class_accessor.h @@ -20,6 +20,9 @@ #include "base/utils.h" #include "code_item_accessors.h" #include "dex_file.h" +#include "invoke_type.h" +#include "method_reference.h" +#include "modifiers.h" namespace art { @@ -27,56 +30,82 @@ class ClassIteratorData; // Classes to access Dex data. class ClassAccessor { - public: - // Class method data. - class Method { + private: + class BaseItem { public: uint32_t GetIndex() const { - return method_idx_; + return index_; } uint32_t GetAccessFlags() const { return access_flags_; } + bool IsFinal() const { + return (GetAccessFlags() & kAccFinal) != 0; + } + + public: + uint32_t index_ = 0u; + uint32_t access_flags_ = 0u; + }; + + public: + // A decoded version of the method of a class_data_item. + class Method : public BaseItem { + public: uint32_t GetCodeItemOffset() const { return code_off_; } + InvokeType GetInvokeType(uint32_t class_access_flags) const { + return is_static_or_direct_ + ? GetDirectMethodInvokeType() + : GetVirtualMethodInvokeType(class_access_flags); + } + + MethodReference GetReference() const { + return MethodReference(&dex_file_, GetIndex()); + } + CodeItemInstructionAccessor GetInstructions() const; + const DexFile::CodeItem* GetCodeItem() const; + private: - explicit Method(const DexFile& dex_file) : dex_file_(dex_file) {} + explicit Method(const DexFile& dex_file, bool is_static_or_direct) + : dex_file_(dex_file), + is_static_or_direct_(is_static_or_direct) {} const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the method of a class_data_item. + InvokeType GetDirectMethodInvokeType() const { + return (GetAccessFlags() & kAccStatic) != 0 ? kStatic : kDirect; + } + + InvokeType GetVirtualMethodInvokeType(uint32_t class_access_flags) const { + DCHECK_EQ(GetAccessFlags() & kAccStatic, 0U); + if ((class_access_flags & kAccInterface) != 0) { + return kInterface; + } else if ((GetAccessFlags() & kAccConstructor) != 0) { + return kSuper; + } else { + return kVirtual; + } + } + const DexFile& dex_file_; - uint32_t method_idx_ = 0u; - uint32_t access_flags_ = 0u; + const bool is_static_or_direct_; uint32_t code_off_ = 0u; friend class ClassAccessor; }; - // Class field data. - class Field { - public: - uint32_t GetIndex() const { - return field_idx_; - } - - uint32_t GetAccessFlags() const { - return access_flags_; - } - + // A decoded version of the field of a class_data_item. + class Field : public BaseItem { private: const uint8_t* Read(const uint8_t* ptr); - // A decoded version of the field of a class_data_item. - uint32_t field_idx_ = 0u; - uint32_t access_flags_ = 0u; - friend class ClassAccessor; }; @@ -89,20 +118,27 @@ class ClassAccessor { const DexFile::CodeItem* GetCodeItem(const Method& method) const; // Iterator data is not very iterator friendly, use visitors to get around this. + // No thread safety analysis since the visitor may require capabilities. template <typename StaticFieldVisitor, typename InstanceFieldVisitor, typename DirectMethodVisitor, typename VirtualMethodVisitor> - void VisitMethodsAndFields(const StaticFieldVisitor& static_field_visitor, + void VisitFieldsAndMethods(const StaticFieldVisitor& static_field_visitor, const InstanceFieldVisitor& instance_field_visitor, const DirectMethodVisitor& direct_method_visitor, - const VirtualMethodVisitor& virtual_method_visitor) const; + const VirtualMethodVisitor& virtual_method_visitor) const + NO_THREAD_SAFETY_ANALYSIS; template <typename DirectMethodVisitor, typename VirtualMethodVisitor> void VisitMethods(const DirectMethodVisitor& direct_method_visitor, const VirtualMethodVisitor& virtual_method_visitor) const; + template <typename StaticFieldVisitor, + typename InstanceFieldVisitor> + void VisitFields(const StaticFieldVisitor& static_field_visitor, + const InstanceFieldVisitor& instance_field_visitor) const; + // Visit direct and virtual methods. template <typename MethodVisitor> void VisitMethods(const MethodVisitor& method_visitor) const; @@ -123,11 +159,16 @@ class ClassAccessor { return num_virtual_methods_; } - // TODO: Deprecate - dex::TypeIndex GetDescriptorIndex() const { + const char* GetDescriptor() const; + + dex::TypeIndex GetClassIdx() const { return descriptor_index_; } + const DexFile& GetDexFile() const { + return dex_file_; + } + protected: const DexFile& dex_file_; const dex::TypeIndex descriptor_index_ = {}; diff --git a/libdexfile/dex/dex_instruction_iterator.h b/libdexfile/dex/dex_instruction_iterator.h index db3ff95e02..b75a95bf5c 100644 --- a/libdexfile/dex/dex_instruction_iterator.h +++ b/libdexfile/dex/dex_instruction_iterator.h @@ -123,7 +123,7 @@ class DexInstructionIterator : public DexInstructionIteratorBase { using DexInstructionIteratorBase::DexInstructionIteratorBase; explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc) - : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {} + : DexInstructionIteratorBase(inst != nullptr ? Instruction::At(inst) : nullptr, dex_pc) {} explicit DexInstructionIterator(const DexInstructionPcPair& pair) : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {} diff --git a/test/980-redefine-object/redef_object.cc b/test/980-redefine-object/redef_object.cc new file mode 100644 index 0000000000..b4d82ad76d --- /dev/null +++ b/test/980-redefine-object/redef_object.cc @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2018 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 <limits> +#include <memory> + +#include "jni.h" +#include "jvmti.h" + +// Test infrastructure +#include "jvmti_helper.h" +#include "test_env.h" + +// Slicer's headers have code that triggers these warnings. b/65298177 +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-compare" +#pragma clang diagnostic ignored "-Wunused-parameter" +#include "slicer/instrumentation.h" +#include "slicer/reader.h" +#include "slicer/writer.h" +#pragma clang diagnostic pop + +namespace art { +namespace Test980RedefineObject { + +static void JNICALL RedefineObjectHook(jvmtiEnv *jvmti_env, + JNIEnv* env, + jclass class_being_redefined ATTRIBUTE_UNUSED, + jobject loader ATTRIBUTE_UNUSED, + const char* name, + jobject protection_domain ATTRIBUTE_UNUSED, + jint class_data_len, + const unsigned char* class_data, + jint* new_class_data_len, + unsigned char** new_class_data) { + if (strcmp(name, "java/lang/Object") != 0) { + return; + } + + dex::Reader reader(class_data, class_data_len); + dex::u4 class_index = reader.FindClassIndex("Ljava/lang/Object;"); + if (class_index == dex::kNoIndex) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find object in dex file!"); + return; + } + + reader.CreateClassIr(class_index); + auto dex_ir = reader.GetIr(); + + slicer::MethodInstrumenter mi(dex_ir); + mi.AddTransformation<slicer::EntryHook>(ir::MethodId("Lart/test/TestWatcher;", + "NotifyConstructed"), + /*this_as_object*/ true); + if (!mi.InstrumentMethod(ir::MethodId("Ljava/lang/Object;", + "<init>", + "()V"))) { + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "Failed to find Object;-><init>()V in dex file!"); + return; + } + + + dex::Writer writer(dex_ir); + + class JvmtiAllocator : public dex::Writer::Allocator { + public: + explicit JvmtiAllocator(jvmtiEnv* jvmti) : jvmti_(jvmti) {} + + void* Allocate(size_t size) { + unsigned char* res = nullptr; + jvmti_->Allocate(size, &res); + return res; + } + + void Free(void* ptr) { + jvmti_->Deallocate(reinterpret_cast<unsigned char*>(ptr)); + } + + private: + jvmtiEnv* jvmti_; + }; + JvmtiAllocator allocator(jvmti_env); + size_t new_size; + *new_class_data = writer.CreateImage(&allocator, &new_size); + if (new_size > std::numeric_limits<jint>::max()) { + *new_class_data = nullptr; + env->ThrowNew(env->FindClass("java/lang/RuntimeException"), + "transform result is too large!"); + return; + } + *new_class_data_len = static_cast<jint>(new_size); +} + +extern "C" JNIEXPORT void JNICALL Java_Main_addMemoryTrackingCall(JNIEnv* env, + jclass klass ATTRIBUTE_UNUSED, + jclass obj_class, + jthread thr) { + jvmtiCapabilities caps {.can_retransform_classes = 1}; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) { + return; + } + jvmtiEventCallbacks cb {.ClassFileLoadHook = RedefineObjectHook }; + if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->RetransformClasses(1, &obj_class))) { + return; + } + if (JvmtiErrorToException(env, + jvmti_env, + jvmti_env->SetEventNotificationMode(JVMTI_DISABLE, + JVMTI_EVENT_CLASS_FILE_LOAD_HOOK, + thr))) { + return; + } +} + +} // namespace Test980RedefineObject +} // namespace art + diff --git a/test/980-redefine-object/src/Main.java b/test/980-redefine-object/src/Main.java index 2428b55a4e..efbc75f6c5 100644 --- a/test/980-redefine-object/src/Main.java +++ b/test/980-redefine-object/src/Main.java @@ -14,278 +14,16 @@ * limitations under the License. */ -import static art.Redefinition.doCommonClassRedefinition; - import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Base64; import java.util.LinkedList; public class Main { - - // TODO We should make this run on the RI. - /** - * This test cannot be run on the RI. - */ - private static final byte[] CLASS_BYTES = new byte[0]; - - // TODO It might be a good idea to replace this hard-coded Object definition with a - // retransformation based test. /** - * Base64 encoding of the following smali file. - * - * .class public Ljava/lang/Object; - * .source "Object.java" - * # instance fields - * .field private transient shadow$_klass_:Ljava/lang/Class; - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .end field - * - * .field private transient shadow$_monitor_:I - * # direct methods - * .method public constructor <init>()V - * .registers 1 - * .prologue - * invoke-static {p0}, Lart/test/TestWatcher;->NotifyConstructed(Ljava/lang/Object;)V - * return-void - * .end method - * - * .method static identityHashCode(Ljava/lang/Object;)I - * .registers 7 - * .prologue - * iget v0, p0, Ljava/lang/Object;->shadow$_monitor_:I - * const/high16 v3, -0x40000000 # -2.0f - * const/high16 v2, -0x80000000 - * const v1, 0xfffffff - * const/high16 v4, -0x40000000 # -2.0f - * and-int/2addr v4, v0 - * const/high16 v5, -0x80000000 - * if-ne v4, v5, :cond_15 - * const v4, 0xfffffff - * and-int/2addr v4, v0 - * return v4 - * :cond_15 - * invoke-static {p0}, Ljava/lang/Object;->identityHashCodeNative(Ljava/lang/Object;)I - * move-result v4 - * return v4 - * .end method - * - * .method private static native identityHashCodeNative(Ljava/lang/Object;)I - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method private native internalClone()Ljava/lang/Object; - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * - * # virtual methods - * .method protected clone()Ljava/lang/Object; - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/CloneNotSupportedException; - * } - * .end annotation - * - * .prologue - * instance-of v0, p0, Ljava/lang/Cloneable; - * if-nez v0, :cond_2d - * new-instance v0, Ljava/lang/CloneNotSupportedException; - * new-instance v1, Ljava/lang/StringBuilder; - * invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V - * const-string/jumbo v2, "Class " - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v2 - * invoke-virtual {v2}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v2 - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * const-string/jumbo v2, " doesn\'t implement Cloneable" - * invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v1 - * invoke-direct {v0, v1}, Ljava/lang/CloneNotSupportedException;-><init>(Ljava/lang/String;)V - * throw v0 - * :cond_2d - * invoke-direct {p0}, Ljava/lang/Object;->internalClone()Ljava/lang/Object; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public equals(Ljava/lang/Object;)Z - * .registers 3 - * .prologue - * if-ne p0, p1, :cond_4 - * const/4 v0, 0x1 - * :goto_3 - * return v0 - * :cond_4 - * const/4 v0, 0x0 - * goto :goto_3 - * .end method - * - * .method protected finalize()V - * .registers 1 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/Throwable; - * } - * .end annotation - * .prologue - * return-void - * .end method - * - * .method public final getClass()Ljava/lang/Class; - * .registers 2 - * .annotation system Ldalvik/annotation/Signature; - * value = { - * "()", - * "Ljava/lang/Class", - * "<*>;" - * } - * .end annotation - * .prologue - * iget-object v0, p0, Ljava/lang/Object;->shadow$_klass_:Ljava/lang/Class; - * return-object v0 - * .end method - * - * .method public hashCode()I - * .registers 2 - * .prologue - * invoke-static {p0}, Ljava/lang/Object;->identityHashCode(Ljava/lang/Object;)I - * move-result v0 - * return v0 - * .end method - * - * .method public final native notify()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final native notifyAll()V - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public toString()Ljava/lang/String; - * .registers 3 - * .prologue - * new-instance v0, Ljava/lang/StringBuilder; - * invoke-direct {v0}, Ljava/lang/StringBuilder;-><init>()V - * invoke-virtual {p0}, Ljava/lang/Object;->getClass()Ljava/lang/Class; - * move-result-object v1 - * invoke-virtual {v1}, Ljava/lang/Class;->getName()Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * const-string/jumbo v1, "@" - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I - * move-result v1 - * invoke-static {v1}, Ljava/lang/Integer;->toHexString(I)Ljava/lang/String; - * move-result-object v1 - * invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder; - * move-result-object v0 - * invoke-virtual {v0}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String; - * move-result-object v0 - * return-object v0 - * .end method - * - * .method public final native wait()V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method - * - * .method public final wait(J)V - * .registers 4 - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * .prologue - * const/4 v0, 0x0 - * invoke-virtual {p0, p1, p2, v0}, Ljava/lang/Object;->wait(JI)V - * return-void - * .end method - * - * .method public final native wait(JI)V - * .annotation system Ldalvik/annotation/Throws; - * value = { - * Ljava/lang/InterruptedException; - * } - * .end annotation - * - * .annotation build Ldalvik/annotation/optimization/FastNative; - * .end annotation - * .end method + * NB This test cannot be run on the RI. + * TODO We should make this run on the RI. */ - private static final byte[] DEX_BYTES = Base64.getDecoder().decode( - "ZGV4CjAzNQDUlMR9j03MYuOKekKs2p7zJzu2IfDb7RlMCgAAcAAAAHhWNBIAAAAAAAAAAIgJAAA6" + - "AAAAcAAAABEAAABYAQAADQAAAJwBAAACAAAAOAIAABYAAABIAgAAAQAAAPgCAAA0BwAAGAMAABgD" + - "AAA2AwAAOgMAAEADAABIAwAASwMAAFMDAABWAwAAWgMAAF0DAABgAwAAZAMAAGgDAACAAwAAnwMA" + - "ALsDAADoAwAA+gMAAA0EAAA1BAAATAQAAGEEAACDBAAAlwQAAKsEAADGBAAA3QQAAPAEAAD9BAAA" + - "AAUAAAQFAAAJBQAADQUAABAFAAAUBQAAHAUAACMFAAArBQAANQUAAD8FAABIBQAAUgUAAGQFAAB8" + - "BQAAiwUAAJUFAACnBQAAugUAAM0FAADVBQAA3QUAAOgFAADtBQAA/QUAAA8GAAAcBgAAJgYAAC0G" + - "AAAGAAAACAAAAAwAAAANAAAADgAAAA8AAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAA" + - "ABkAAAAcAAAAIAAAAAYAAAAAAAAAAAAAAAcAAAAAAAAAPAYAAAkAAAAGAAAAAAAAAAkAAAALAAAA" + - "AAAAAAkAAAAMAAAAAAAAAAoAAAAMAAAARAYAAAsAAAANAAAAVAYAABwAAAAPAAAAAAAAAB0AAAAP" + - "AAAATAYAAB4AAAAPAAAANAYAAB8AAAAPAAAAPAYAAB8AAAAPAAAAVAYAACEAAAAQAAAAPAYAAAsA" + - "BgA0AAAACwAAADUAAAACAAoAGgAAAAYABAAnAAAABwALAAMAAAAJAAUANgAAAAsABwADAAAACwAD" + - "ACMAAAALAAwAJAAAAAsABwAlAAAACwACACYAAAALAAAAKAAAAAsAAQApAAAACwABACoAAAALAAMA" + - "KwAAAAsABwAxAAAACwAHADIAAAALAAQANwAAAAsABwA5AAAACwAIADkAAAALAAkAOQAAAA0ABwAD" + - "AAAADQAGACIAAAANAAQANwAAAAsAAAABAAAA/////wAAAAAbAAAA0AYAAD4JAAAAAAAAHCBkb2Vz" + - "bid0IGltcGxlbWVudCBDbG9uZWFibGUAAigpAAQ8Kj47AAY8aW5pdD4AAUAABkNsYXNzIAABSQAC" + - "SUwAAUoAAUwAAkxJAAJMTAAWTGFydC90ZXN0L1Rlc3RXYXRjaGVyOwAdTGRhbHZpay9hbm5vdGF0" + - "aW9uL1NpZ25hdHVyZTsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ACtMZGFsdmlrL2Fubm90" + - "YXRpb24vb3B0aW1pemF0aW9uL0Zhc3ROYXRpdmU7ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" + - "YW5nL0NsYXNzOwAmTGphdmEvbGFuZy9DbG9uZU5vdFN1cHBvcnRlZEV4Y2VwdGlvbjsAFUxqYXZh" + - "L2xhbmcvQ2xvbmVhYmxlOwATTGphdmEvbGFuZy9JbnRlZ2VyOwAgTGphdmEvbGFuZy9JbnRlcnJ1" + - "cHRlZEV4Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlM" + - "amF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7ABVMamF2YS9sYW5nL1Rocm93YWJsZTsAEU5vdGlmeUNv" + - "bnN0cnVjdGVkAAtPYmplY3QuamF2YQABVgACVkoAA1ZKSQACVkwAAVoAAlpMAAZhcHBlbmQABWNs" + - "b25lAAZlcXVhbHMACGZpbmFsaXplAAhnZXRDbGFzcwAHZ2V0TmFtZQAIaGFzaENvZGUAEGlkZW50" + - "aXR5SGFzaENvZGUAFmlkZW50aXR5SGFzaENvZGVOYXRpdmUADWludGVybmFsQ2xvbmUACGxvY2tX" + - "b3JkABBsb2NrV29yZEhhc2hNYXNrABFsb2NrV29yZFN0YXRlSGFzaAARbG9ja1dvcmRTdGF0ZU1h" + - "c2sABm1pbGxpcwAGbm90aWZ5AAlub3RpZnlBbGwAA29iagAOc2hhZG93JF9rbGFzc18AEHNoYWRv" + - "dyRfbW9uaXRvcl8AC3RvSGV4U3RyaW5nAAh0b1N0cmluZwAFdmFsdWUABHdhaXQAAAIAAAABAAAA" + - "AQAAAAsAAAABAAAAAAAAAAEAAAABAAAAAQAAAAwAAgQBOBwBGAcCBAE4HAEYCgIDATgcAhcQFwIC" + - "BAE4HAEYDgAFAAIDATgcAxcBFxAXAgAAAAAAAAAAAAEAAABaBgAAAgAAAGIGAAB8BgAAAQAAAGIG" + - "AAABAAAAagYAAAEAAAB0BgAAAQAAAHwGAAABAAAAfwYAAAAAAAABAAAACgAAAAAAAAAAAAAAsAYA" + - "AAUAAACUBgAABwAAALgGAAAIAAAAyAYAAAsAAADABgAADAAAAMAGAAANAAAAwAYAAA4AAADABgAA" + - "EAAAAJwGAAARAAAAqAYAABIAAACcBgAAKAAHDgBwATQHDi0DAC0BLQMDMAEtAwIvATwDAS4BeFsA" + - "7AEABw5LARoPOsYArAEBNAcOAMUEAAcOAEEABw4AaAAHDgCRAgAHDgCmAwExBw5LAAAAAQABAAEA" + - "AAA4BwAABAAAAHEQAAAAAA4ABwABAAEAAAA9BwAAGgAAAFJgAQAVAwDAFQIAgBQB////DxUEAMC1" + - "BBUFAIAzVAcAFAT///8PtQQPBHEQCwAGAAoEDwQEAAEAAgAAAFkHAAAyAAAAIDAIADkAKwAiAAcA" + - "IgENAHAQEwABABsCBQAAAG4gFAAhAAwBbhAIAAMADAJuEAEAAgAMAm4gFAAhAAwBGwIAAAAAbiAU" + - "ACEADAFuEBUAAQAMAXAgAgAQACcAcBAMAAMADAARAAMAAgAAAAAAZQcAAAYAAAAzIQQAEhAPABIA" + - "KP4BAAEAAAAAAGwHAAABAAAADgAAAAIAAQAAAAAAcgcAAAMAAABUEAAAEQAAAAIAAQABAAAAdwcA" + - "AAUAAABxEAoAAQAKAA8AAAADAAEAAgAAAHwHAAApAAAAIgANAHAQEwAAAG4QCAACAAwBbhABAAEA" + - "DAFuIBQAEAAMABsBBAAAAG4gFAAQAAwAbhAJAAIACgFxEAMAAQAMAW4gFAAQAAwAbhAVAAAADAAR" + - "AAAABAADAAQAAACCBwAABQAAABIAbkASACEDDgAAAgQLAIIBAYIBBIGABIwPBgikDwGKAgABggIA" + - "BQToDwEB3BABBPgQARGMEQEBpBEEkQIAAZECAAEBwBEBkQIAARGkEgGRAgAAABAAAAAAAAAAAQAA" + - "AAAAAAABAAAAOgAAAHAAAAACAAAAEQAAAFgBAAADAAAADQAAAJwBAAAEAAAAAgAAADgCAAAFAAAA" + - "FgAAAEgCAAAGAAAAAQAAAPgCAAACIAAAOgAAABgDAAABEAAABQAAADQGAAAEIAAABgAAAFoGAAAD" + - "EAAACQAAAIwGAAAGIAAAAQAAANAGAAADIAAACQAAADgHAAABIAAACQAAAIwHAAAAIAAAAQAAAD4J" + - "AAAAEAAAAQAAAIgJAAA="); private static final String LISTENER_LOCATION = System.getenv("DEX_LOCATION") + "/980-redefine-object-ex.jar"; @@ -361,7 +99,7 @@ public class Main { // Redefine the Object Class. safePrintln("Redefining the Object class to add a hook into the <init> method"); - doCommonClassRedefinition(Object.class, CLASS_BYTES, DEX_BYTES); + addMemoryTrackingCall(Object.class, Thread.currentThread()); safePrintln("Allocating an j.l.Object after redefining Object class"); Object o2 = new Object(); @@ -407,5 +145,10 @@ public class Main { safePrintln("Finishing test!"); } + // This is from 929-search/search.cc private static native void addToBootClassLoader(String s); + // This is from 980-redefine-object/redef_object.cc + // It will add a call to Lart/test/TestWatcher;->NotifyConstructed()V in the Object <init>()V + // function. + private static native void addMemoryTrackingCall(Class c, Thread thr); } diff --git a/test/980-redefine-object/src/art/Redefinition.java b/test/980-redefine-object/src/art/Redefinition.java deleted file mode 100644 index 56d2938a01..0000000000 --- a/test/980-redefine-object/src/art/Redefinition.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - */ - -package art; - -import java.util.ArrayList; -// Common Redefinition functions. Placed here for use by CTS -public class Redefinition { - public static final class CommonClassDefinition { - public final Class<?> target; - public final byte[] class_file_bytes; - public final byte[] dex_file_bytes; - - public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { - this.target = target; - this.class_file_bytes = class_file_bytes; - this.dex_file_bytes = dex_file_bytes; - } - } - - // A set of possible test configurations. Test should set this if they need to. - // This must be kept in sync with the defines in ti-agent/common_helper.cc - public static enum Config { - COMMON_REDEFINE(0), - COMMON_RETRANSFORM(1), - COMMON_TRANSFORM(2); - - private final int val; - private Config(int val) { - this.val = val; - } - } - - public static void setTestConfiguration(Config type) { - nativeSetTestConfiguration(type.val); - } - - private static native void nativeSetTestConfiguration(int type); - - // Transforms the class - public static native void doCommonClassRedefinition(Class<?> target, - byte[] classfile, - byte[] dexfile); - - public static void doMultiClassRedefinition(CommonClassDefinition... defs) { - ArrayList<Class<?>> classes = new ArrayList<>(); - ArrayList<byte[]> class_files = new ArrayList<>(); - ArrayList<byte[]> dex_files = new ArrayList<>(); - - for (CommonClassDefinition d : defs) { - classes.add(d.target); - class_files.add(d.class_file_bytes); - dex_files.add(d.dex_file_bytes); - } - doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), - class_files.toArray(new byte[0][]), - dex_files.toArray(new byte[0][])); - } - - public static void addMultiTransformationResults(CommonClassDefinition... defs) { - for (CommonClassDefinition d : defs) { - addCommonTransformationResult(d.target.getCanonicalName(), - d.class_file_bytes, - d.dex_file_bytes); - } - } - - public static native void doCommonMultiClassRedefinition(Class<?>[] targets, - byte[][] classfiles, - byte[][] dexfiles); - public static native void doCommonClassRetransformation(Class<?>... target); - public static native void setPopRetransformations(boolean pop); - public static native void popTransformationFor(String name); - public static native void enableCommonRetransformation(boolean enable); - public static native void addCommonTransformationResult(String target_name, - byte[] class_bytes, - byte[] dex_bytes); -} diff --git a/test/Android.bp b/test/Android.bp index 7909bf897a..e0ec286314 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -314,10 +314,15 @@ art_cc_defaults { "909-attach-agent/attach.cc", "912-classes/classes_art.cc", "936-search-onload/search_onload.cc", + "980-redefine-object/redef_object.cc", "983-source-transform-verify/source_transform_art.cc", "1940-ddms-ext/ddm_ext.cc", "1944-sudden-exit/sudden_exit.cc", ], + static_libs: [ + "libz", + "slicer", + ], } art_cc_test_library { diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh index 8c28828261..2144b02c2f 100755 --- a/tools/cleanup-buildbot-device.sh +++ b/tools/cleanup-buildbot-device.sh @@ -40,10 +40,6 @@ if [[ -n "$ART_TEST_CHROOT" ]]; then # # TODO: Reorder ART Buildbot steps so that "device cleanup" happens # before "setup device" and remove this special case. - # - # TODO: Also consider adding a "tear down device" step on the ART - # Buildbot (at the very end of a build) undoing (some of) the work - # done in the "device setup" step. adb shell test -f "$ART_TEST_CHROOT/system" \ "&&" find "$ART_TEST_CHROOT/system" \ ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \ diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc index 427a465caf..312a7b3159 100644 --- a/tools/dexanalyze/dexanalyze_experiments.cc +++ b/tools/dexanalyze/dexanalyze_experiments.cc @@ -150,7 +150,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_VIRTUAL_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE); uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_virtual_; } else { ++other_class_virtual_; @@ -162,7 +162,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_DIRECT_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_DIRECT_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_direct_; } else { ++other_class_direct_; @@ -174,7 +174,7 @@ void CountDexIndices::ProcessDexFile(const DexFile& dex_file) { case Instruction::INVOKE_STATIC_RANGE: { bool is_range = (inst->Opcode() == Instruction::INVOKE_STATIC_RANGE); uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetDescriptorIndex()) { + if (dex_file.GetMethodId(method_idx).class_idx_ == accessor.GetClassIdx()) { ++same_class_static_; } else { ++other_class_static_; diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh index f71d973925..f89197580d 100755 --- a/tools/setup-buildbot-device.sh +++ b/tools/setup-buildbot-device.sh @@ -14,10 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +# The work does by this script is (mostly) undone by tools/teardown-buildbot-device.sh. +# Make sure to keep these files in sync. + green='\033[0;32m' nc='\033[0m' -# Setup as root, as some actions performed here (e.g. setting the date) requires it. +# Setup as root, as some actions performed here require it. adb root adb wait-for-device diff --git a/tools/teardown-buildbot-device.sh b/tools/teardown-buildbot-device.sh new file mode 100755 index 0000000000..df239a28bc --- /dev/null +++ b/tools/teardown-buildbot-device.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script undoes (most of) the work done by tools/setup-buildbot-device.sh. +# Make sure to keep these files in sync. + +green='\033[0;32m' +nc='\033[0m' + +# Setup as root, as some actions performed here require it. +adb root +adb wait-for-device + +if [[ -n "$ART_TEST_CHROOT" ]]; then + # Tear down the chroot dir. + echo -e "${green}Tear down the chroot dir in $ART_TEST_CHROOT${nc}" + + # Check that ART_TEST_CHROOT is correctly defined. + [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; } + + # Remove /dev from chroot. + adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \ + && adb shell umount "$ART_TEST_CHROOT/dev" \ + && adb shell rmdir "$ART_TEST_CHROOT/dev" + + # Remove /sys/kernel/debug from chroot. + adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys/kernel/debug" + # Remove /sys from chroot. + adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \ + && adb shell umount "$ART_TEST_CHROOT/sys" \ + && adb shell rmdir "$ART_TEST_CHROOT/sys" + + # Remove /proc from chroot. + adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \ + && adb shell umount "$ART_TEST_CHROOT/proc" \ + && adb shell rmdir "$ART_TEST_CHROOT/proc" + + # Remove /etc from chroot. + adb shell rm -f "$ART_TEST_CHROOT/etc" + adb shell rm -rf "$ART_TEST_CHROOT/system/etc" + + # Remove directories used for ART testing in chroot. + adb shell rm -rf "$ART_TEST_CHROOT/data/local/tmp" + adb shell rm -rf "$ART_TEST_CHROOT/data/dalvik-cache" + adb shell rm -rf "$ART_TEST_CHROOT/tmp" + + # Remove property_contexts file(s) from chroot. + property_context_files="/property_contexts \ + /system/etc/selinux/plat_property_contexts \ + /vendor/etc/selinux/nonplat_property_context \ + /plat_property_contexts \ + /nonplat_property_contexts" + for f in $property_context_files; do + adb shell test -f "$f" "&&" rm -f "$ART_TEST_CHROOT$f" + done +fi |