summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc16
-rw-r--r--compiler/driver/compiler_driver.cc316
-rw-r--r--compiler/verifier_deps_test.cc63
-rw-r--r--libdexfile/dex/class_accessor-inl.h30
-rw-r--r--libdexfile/dex/class_accessor.h95
-rw-r--r--libdexfile/dex/dex_instruction_iterator.h2
-rw-r--r--test/980-redefine-object/redef_object.cc143
-rw-r--r--test/980-redefine-object/src/Main.java273
-rw-r--r--test/980-redefine-object/src/art/Redefinition.java91
-rw-r--r--test/Android.bp5
-rwxr-xr-xtools/cleanup-buildbot-device.sh4
-rw-r--r--tools/dexanalyze/dexanalyze_experiments.cc6
-rwxr-xr-xtools/setup-buildbot-device.sh5
-rwxr-xr-xtools/teardown-buildbot-device.sh70
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