summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/driver/compiler_driver.cc36
-rw-r--r--compiler/image_test.h3
-rw-r--r--compiler/image_writer.cc13
-rw-r--r--compiler/image_writer.h7
-rw-r--r--compiler/verifier_deps_test.cc38
-rw-r--r--dex2oat/dex2oat.cc37
-rw-r--r--dex2oat/dex2oat_image_test.cc9
-rw-r--r--imgdiag/imgdiag.cc74
-rw-r--r--runtime/aot_class_linker.cc22
-rw-r--r--runtime/aot_class_linker.h7
-rw-r--r--runtime/class_linker.cc6
-rw-r--r--runtime/class_linker.h6
-rw-r--r--runtime/vdex_file.h4
-rw-r--r--runtime/verifier/verifier_deps.cc20
-rw-r--r--runtime/verifier/verifier_deps.h4
-rw-r--r--test/660-clinit/expected.txt1
-rw-r--r--test/660-clinit/profile5
-rw-r--r--test/660-clinit/src/Main.java63
-rw-r--r--test/906-iterate-heap/expected.txt2
-rw-r--r--test/906-iterate-heap/src/art/Test906.java29
-rw-r--r--test/913-heaps/expected.txt2
-rw-r--r--test/913-heaps/src/art/Test913.java29
-rw-r--r--test/knownfailures.json6
-rw-r--r--tools/art71
24 files changed, 416 insertions, 78 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 0b1bce62c9..5eee7e003b 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1935,14 +1935,12 @@ bool CompilerDriver::FastVerify(jobject jclass_loader,
// time. So instead we assume these classes still need to be verified at
// runtime.
for (const DexFile* dex_file : dex_files) {
- // Fetch the list of unverified classes and turn it into a set for faster
- // lookups.
- const std::vector<dex::TypeIndex>& unverified_classes =
+ // Fetch the list of unverified classes.
+ const std::set<dex::TypeIndex>& unverified_classes =
verifier_deps->GetUnverifiedClasses(*dex_file);
- std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- if (set.find(class_def.class_idx_) == set.end()) {
+ if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
if (compiler_only_verifies) {
// Just update the compiled_classes_ map. The compiler doesn't need to resolve
// the type.
@@ -2253,6 +2251,7 @@ class InitializeClassVisitor : public CompilationVisitor {
const char* descriptor = dex_file.StringDataByIdx(class_type_id.descriptor_idx_);
ScopedObjectAccessUnchecked soa(Thread::Current());
StackHandleScope<3> hs(soa.Self());
+ ClassLinker *class_linker = manager_->GetClassLinker();
const bool is_boot_image = manager_->GetCompiler()->GetCompilerOptions().IsBootImage();
const bool is_app_image = manager_->GetCompiler()->GetCompilerOptions().IsAppImage();
@@ -2266,7 +2265,7 @@ class InitializeClassVisitor : public CompilationVisitor {
if (klass->IsVerified()) {
// Attempt to initialize the class but bail if we either need to initialize the super-class
// or static fields.
- manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, false);
+ class_linker->EnsureInitialized(soa.Self(), klass, false, false);
old_status = klass->GetStatus();
if (!klass->IsInitialized()) {
// We don't want non-trivial class initialization occurring on multiple threads due to
@@ -2285,7 +2284,7 @@ class InitializeClassVisitor : public CompilationVisitor {
bool is_superclass_initialized = !is_app_image ? true :
InitializeDependencies(klass, class_loader, soa.Self());
if (!is_app_image || (is_app_image && is_superclass_initialized)) {
- manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, false, true);
+ class_linker->EnsureInitialized(soa.Self(), klass, false, true);
}
// Otherwise it's in app image but superclasses can't be initialized, no need to proceed.
old_status = klass->GetStatus();
@@ -2311,10 +2310,13 @@ class InitializeClassVisitor : public CompilationVisitor {
CHECK(is_app_image);
// The boot image case doesn't need to recursively initialize the dependencies with
// special logic since the class linker already does this.
+ // Optimization will be disabled in debuggable build, because in debuggable mode we
+ // want the <clinit> behavior to be observable for the debugger, so we don't do the
+ // <clinit> at compile time.
can_init_static_fields =
+ !manager_->GetCompiler()->GetCompilerOptions().GetDebuggable() &&
!soa.Self()->IsExceptionPending() &&
- is_superclass_initialized &&
- NoClinitInDependency(klass, soa.Self(), &class_loader);
+ is_superclass_initialized;
// TODO The checking for clinit can be removed since it's already
// checked when init superclass. Currently keep it because it contains
// processing of intern strings. Will be removed later when intern strings
@@ -2328,6 +2330,18 @@ class InitializeClassVisitor : public CompilationVisitor {
// a ReaderWriterMutex but we're holding the mutator lock so we fail mutex sanity
// checks in Thread::AssertThreadSuspensionIsAllowable.
Runtime* const runtime = Runtime::Current();
+ // Resolve and initialize the exception type before enabling the transaction in case
+ // the transaction aborts and cannot resolve the type.
+ // TransactionAbortError is not initialized ant not in boot image, needed only by
+ // compiler and will be pruned by ImageWriter.
+ Handle<mirror::Class> exception_class =
+ hs.NewHandle(class_linker->FindClass(Thread::Current(),
+ Transaction::kAbortExceptionSignature,
+ class_loader));
+ bool exception_initialized =
+ class_linker->EnsureInitialized(soa.Self(), exception_class, true, true);
+ DCHECK(exception_initialized);
+
// Run the class initializer in transaction mode.
runtime->EnterTransactionMode(is_app_image, klass.Get());
bool success = manager_->GetClassLinker()->EnsureInitialized(soa.Self(), klass, true,
@@ -2365,10 +2379,12 @@ class InitializeClassVisitor : public CompilationVisitor {
}
}
- if (!success) {
+ if (!success && is_boot_image) {
// On failure, still intern strings of static fields and seen in <clinit>, as these
// will be created in the zygote. This is separated from the transaction code just
// above as we will allocate strings, so must be allowed to suspend.
+ // We only need to intern strings for boot image because classes that failed to be
+ // initialized will not appear in app image.
if (&klass->GetDexFile() == manager_->GetDexFile()) {
InternStrings(klass, class_loader);
} else {
diff --git a/compiler/image_test.h b/compiler/image_test.h
index 57d0987982..daa4b11967 100644
--- a/compiler/image_test.h
+++ b/compiler/image_test.h
@@ -214,7 +214,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver,
/*compile_app_image*/false,
storage_mode,
oat_filename_vector,
- dex_file_to_oat_index_map));
+ dex_file_to_oat_index_map,
+ /*dirty_image_objects*/nullptr));
{
{
jobject class_loader = nullptr;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f4e8a89e92..9e4971ce75 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -579,7 +579,12 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) {
}
}
- if (klass->GetStatus() == Class::kStatusInitialized) {
+ // Move known dirty objects into their own sections. This includes:
+ // - classes with dirty static fields.
+ if (dirty_image_objects_ != nullptr &&
+ dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) {
+ bin = kBinKnownDirty;
+ } else if (klass->GetStatus() == Class::kStatusInitialized) {
bin = kBinClassInitialized;
// If the class's static fields are all final, put it into a separate bin
@@ -2774,7 +2779,8 @@ ImageWriter::ImageWriter(
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map)
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects)
: compiler_driver_(compiler_driver),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
@@ -2786,7 +2792,8 @@ ImageWriter::ImageWriter(
clean_methods_(0u),
image_storage_mode_(image_storage_mode),
oat_filenames_(oat_filenames),
- dex_file_oat_index_map_(dex_file_oat_index_map) {
+ dex_file_oat_index_map_(dex_file_oat_index_map),
+ dirty_image_objects_(dirty_image_objects) {
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 34bbbad75d..866e2042f7 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -75,7 +75,8 @@ class ImageWriter FINAL {
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map);
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects);
bool PrepareImageAddressSpace();
@@ -159,6 +160,7 @@ class ImageWriter FINAL {
// Classify different kinds of bins that objects end up getting packed into during image writing.
// Ordered from dirtiest to cleanest (until ArtMethods).
enum Bin {
+ kBinKnownDirty, // Known dirty objects from --dirty-image-objects list
kBinMiscDirty, // Dex caches, object locks, etc...
kBinClassVerified, // Class verified, but initializers haven't been run
// Unknown mix of clean/dirty:
@@ -599,6 +601,9 @@ class ImageWriter FINAL {
// Map of dex files to the indexes of oat files that they were compiled into.
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
+ // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+ const std::unordered_set<std::string>* dirty_image_objects_;
+
class ComputeLazyFieldsForClassesVisitor;
class FixupClassVisitor;
class FixupRootVisitor;
diff --git a/compiler/verifier_deps_test.cc b/compiler/verifier_deps_test.cc
index e9f3f8022d..65389252e2 100644
--- a/compiler/verifier_deps_test.cc
+++ b/compiler/verifier_deps_test.cc
@@ -229,8 +229,7 @@ class VerifierDepsTest : public CommonCompilerTest {
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader_)));
MutableHandle<mirror::Class> cls(hs.NewHandle<mirror::Class>(nullptr));
for (const DexFile* dex_file : dex_files_) {
- const std::vector<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
- std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
+ const std::set<dex::TypeIndex>& unverified_classes = deps.GetUnverifiedClasses(*dex_file);
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);
@@ -238,7 +237,7 @@ class VerifierDepsTest : public CommonCompilerTest {
if (cls == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
soa.Self()->ClearException();
- } else if (set.find(class_def.class_idx_) == set.end()) {
+ } else if (unverified_classes.find(class_def.class_idx_) == unverified_classes.end()) {
ASSERT_EQ(cls->GetStatus(), mirror::Class::kStatusVerified);
} else {
ASSERT_LT(cls->GetStatus(), mirror::Class::kStatusVerified);
@@ -1145,6 +1144,39 @@ TEST_F(VerifierDepsTest, UnverifiedClasses) {
ASSERT_TRUE(HasUnverifiedClass("LMyClassWithNoSuperButFailures;"));
}
+TEST_F(VerifierDepsTest, UnverifiedOrder) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject loader = LoadDex("VerifierDeps");
+ std::vector<const DexFile*> dex_files = GetDexFiles(loader);
+ ASSERT_GT(dex_files.size(), 0u);
+ const DexFile* dex_file = dex_files[0];
+ VerifierDeps deps1(dex_files);
+ Thread* const self = Thread::Current();
+ ASSERT_TRUE(self->GetVerifierDeps() == nullptr);
+ self->SetVerifierDeps(&deps1);
+ deps1.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(0),
+ verifier::FailureKind::kHardFailure);
+ deps1.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(1),
+ verifier::FailureKind::kHardFailure);
+ VerifierDeps deps2(dex_files);
+ self->SetVerifierDeps(nullptr);
+ self->SetVerifierDeps(&deps2);
+ deps2.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(1),
+ verifier::FailureKind::kHardFailure);
+ deps2.MaybeRecordVerificationStatus(*dex_file,
+ dex::TypeIndex(0),
+ verifier::FailureKind::kHardFailure);
+ self->SetVerifierDeps(nullptr);
+ std::vector<uint8_t> buffer1;
+ deps1.Encode(dex_files, &buffer1);
+ std::vector<uint8_t> buffer2;
+ deps2.Encode(dex_files, &buffer2);
+ EXPECT_EQ(buffer1, buffer2);
+}
+
TEST_F(VerifierDepsTest, VerifyDeps) {
VerifyDexFile();
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 0826fa1488..d57bf6cab3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -408,17 +408,17 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError("");
UsageError(" --class-loader-context=<string spec>: a string specifying the intended");
UsageError(" runtime loading context for the compiled dex files.");
- UsageError(" ");
+ UsageError("");
UsageError(" It describes how the class loader chain should be built in order to ensure");
UsageError(" classes are resolved during dex2aot as they would be resolved at runtime.");
UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is");
UsageError(" loaded in a different context, the oat file will be rejected.");
- UsageError(" ");
+ UsageError("");
UsageError(" The chain is interpreted in the natural 'parent order', meaning that class");
UsageError(" loader 'i+1' will be the parent of class loader 'i'.");
UsageError(" The compilation sources will be appended to the classpath of the first class");
UsageError(" loader.");
- UsageError(" ");
+ UsageError("");
UsageError(" E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and ");
UsageError(" --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
UsageError(" 'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
@@ -428,9 +428,12 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" with --dex-file are found in the classpath. The source dex files will be");
UsageError(" removed from any class loader's classpath possibly resulting in empty");
UsageError(" class loaders.");
- UsageError(" ");
+ UsageError("");
UsageError(" Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
UsageError("");
+ UsageError(" --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
+ UsageError(" The image writer will group them together.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -1307,6 +1310,8 @@ class Dex2Oat FINAL {
if (class_loader_context_== nullptr) {
Usage("Option --class-loader-context has an incorrect format: %s", option.data());
}
+ } else if (option.starts_with("--dirty-image-objects=")) {
+ dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1508,7 +1513,8 @@ class Dex2Oat FINAL {
dex2oat::ReturnCode Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+ if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() ||
+ !PrepareDirtyObjects()) {
return dex2oat::ReturnCode::kOther;
}
@@ -2002,7 +2008,8 @@ class Dex2Oat FINAL {
IsAppImage(),
image_storage_mode_,
oat_filenames_,
- dex_file_oat_index_map_));
+ dex_file_oat_index_map_,
+ dirty_image_objects_.get()));
// We need to prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -2428,6 +2435,22 @@ class Dex2Oat FINAL {
return true;
}
+ bool PrepareDirtyObjects() {
+ if (dirty_image_objects_filename_ != nullptr) {
+ dirty_image_objects_.reset(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+ dirty_image_objects_filename_,
+ nullptr));
+ if (dirty_image_objects_ == nullptr) {
+ LOG(ERROR) << "Failed to create list of dirty objects from '"
+ << dirty_image_objects_filename_ << "'";
+ return false;
+ }
+ } else {
+ dirty_image_objects_.reset(nullptr);
+ }
+ return true;
+ }
+
void PruneNonExistentDexFiles() {
DCHECK_EQ(dex_filenames_.size(), dex_locations_.size());
size_t kept = 0u;
@@ -2845,9 +2868,11 @@ class Dex2Oat FINAL {
const char* compiled_methods_zip_filename_;
const char* compiled_methods_filename_;
const char* passes_to_run_filename_;
+ const char* dirty_image_objects_filename_;
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
+ std::unique_ptr<std::unordered_set<std::string>> dirty_image_objects_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool multi_image_;
bool is_host_;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 95fb16dfd7..46c5f581b6 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -340,6 +340,15 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) {
// EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
}
+ // Test dirty image objects.
+ {
+ ScratchFile classes;
+ GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
+ image_classes_sizes = CompileImageAndGetSizes(
+ {"--dirty-image-objects=" + classes.GetFilename()});
+ classes.Close();
+ std::cout << "Dirty image object sizes " << image_classes_sizes << std::endl;
+ }
}
} // namespace art
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 7ef24c700e..9ffc4149ab 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -333,9 +333,11 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
std::vector<uint8_t>* remote_contents,
std::vector<uint8_t>* zygote_contents,
const backtrace_map_t& boot_map,
- const ImageHeader& image_header) :
- RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
- os_(*os) { }
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
+ os_(*os),
+ dump_dirty_objects_(dump_dirty_objects) { }
void CheckEntrySanity(const uint8_t* current) const
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -396,7 +398,10 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
class_data_[klass].AddDirtyObject(entry, entry_remote);
}
- void DiffEntryContents(mirror::Object* entry, uint8_t* remote_bytes, const uint8_t* base_ptr)
+ void DiffEntryContents(mirror::Object* entry,
+ uint8_t* remote_bytes,
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
const char* tabs = " ";
// Attempt to find fields for all dirty bytes.
@@ -453,6 +458,9 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
}
}
if (!dirty_static_fields.empty()) {
+ if (dump_dirty_objects_ && log_dirty_objects) {
+ dirty_objects_.insert(entry);
+ }
os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
for (ArtField* field : dirty_static_fields) {
os_ << tabs << ArtField::PrettyField(field)
@@ -463,6 +471,14 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
os_ << "\n";
}
+ void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (mirror::Object* obj : dirty_objects_) {
+ if (obj->IsClass()) {
+ os_ << "Private dirty object: " << obj->AsClass()->PrettyDescriptor() << "\n";
+ }
+ }
+ }
+
void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
// vector of pairs (size_t count, Class*)
auto dirty_object_class_values =
@@ -592,6 +608,8 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
};
std::ostream& os_;
+ bool dump_dirty_objects_;
+ std::unordered_set<mirror::Object*> dirty_objects_;
std::map<mirror::Class*, ClassData> class_data_;
DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase);
@@ -720,9 +738,15 @@ class RegionData : public RegionSpecializedBase<T> {
std::vector<uint8_t>* remote_contents,
std::vector<uint8_t>* zygote_contents,
const backtrace_map_t& boot_map,
- const ImageHeader& image_header) :
- RegionSpecializedBase<T>(os, remote_contents, zygote_contents, boot_map, image_header),
- os_(*os) {
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionSpecializedBase<T>(os,
+ remote_contents,
+ zygote_contents,
+ boot_map,
+ image_header,
+ dump_dirty_objects),
+ os_(*os) {
CHECK(remote_contents != nullptr);
CHECK(zygote_contents != nullptr);
}
@@ -773,7 +797,8 @@ class RegionData : public RegionSpecializedBase<T> {
DiffDirtyEntries(ProcessType::kRemote,
begin_image_ptr,
RegionCommon<T>::remote_contents_,
- base_ptr);
+ base_ptr,
+ /*log_dirty_objects*/true);
// Print shared dirty after since it's less important.
if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
// We only reach this point if both pids were specified. Furthermore,
@@ -784,8 +809,10 @@ class RegionData : public RegionSpecializedBase<T> {
DiffDirtyEntries(ProcessType::kZygote,
begin_image_ptr,
RegionCommon<T>::zygote_contents_,
- begin_image_ptr);
+ begin_image_ptr,
+ /*log_dirty_objects*/false);
}
+ RegionSpecializedBase<T>::DumpDirtyObjects();
RegionSpecializedBase<T>::DumpDirtyEntries();
RegionSpecializedBase<T>::DumpFalseDirtyEntries();
RegionSpecializedBase<T>::DumpCleanEntries();
@@ -797,7 +824,8 @@ class RegionData : public RegionSpecializedBase<T> {
void DiffDirtyEntries(ProcessType process_type,
const uint8_t* begin_image_ptr,
std::vector<uint8_t>* contents,
- const uint8_t* base_ptr)
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
const std::set<T*>& entries =
@@ -808,7 +836,10 @@ class RegionData : public RegionSpecializedBase<T> {
uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
ptrdiff_t offset = entry_bytes - begin_image_ptr;
uint8_t* remote_bytes = &(*contents)[offset];
- RegionSpecializedBase<T>::DiffEntryContents(entry, remote_bytes, &base_ptr[offset]);
+ RegionSpecializedBase<T>::DiffEntryContents(entry,
+ remote_bytes,
+ &base_ptr[offset],
+ log_dirty_objects);
}
}
@@ -872,12 +903,14 @@ class ImgDiagDumper {
const ImageHeader& image_header,
const std::string& image_location,
pid_t image_diff_pid,
- pid_t zygote_diff_pid)
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects)
: os_(os),
image_header_(image_header),
image_location_(image_location),
image_diff_pid_(image_diff_pid),
zygote_diff_pid_(zygote_diff_pid),
+ dump_dirty_objects_(dump_dirty_objects),
zygote_pid_only_(false) {}
bool Init() {
@@ -1207,7 +1240,8 @@ class ImgDiagDumper {
&remote_contents_,
&zygote_contents_,
boot_map_,
- image_header_);
+ image_header_,
+ dump_dirty_objects_);
RemoteProcesses remotes;
if (zygote_pid_only_) {
@@ -1364,6 +1398,7 @@ class ImgDiagDumper {
const std::string image_location_;
pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative
pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative
+ bool dump_dirty_objects_; // Adds dumping of objects that are dirty.
bool zygote_pid_only_; // The user only specified a pid for the zygote.
// BacktraceMap used for finding the memory mapping of the image file.
@@ -1391,7 +1426,8 @@ class ImgDiagDumper {
static int DumpImage(Runtime* runtime,
std::ostream* os,
pid_t image_diff_pid,
- pid_t zygote_diff_pid) {
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects) {
ScopedObjectAccess soa(Thread::Current());
gc::Heap* heap = runtime->GetHeap();
std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
@@ -1407,7 +1443,8 @@ static int DumpImage(Runtime* runtime,
image_header,
image_space->GetImageLocation(),
image_diff_pid,
- zygote_diff_pid);
+ zygote_diff_pid,
+ dump_dirty_objects);
if (!img_diag_dumper.Init()) {
return EXIT_FAILURE;
}
@@ -1445,6 +1482,8 @@ struct ImgDiagArgs : public CmdlineArgs {
*error_msg = "Zygote diff pid out of range";
return kParseError;
}
+ } else if (option == "--dump-dirty-objects") {
+ dump_dirty_objects_ = true;
} else {
return kParseUnknownArgument;
}
@@ -1497,6 +1536,7 @@ struct ImgDiagArgs : public CmdlineArgs {
" --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff "
"against.\n"
" Example: --zygote-diff-pid=$(pid zygote)\n"
+ " --dump-dirty-objects: additionally output dirty objects of interest.\n"
"\n";
return usage;
@@ -1505,6 +1545,7 @@ struct ImgDiagArgs : public CmdlineArgs {
public:
pid_t image_diff_pid_ = -1;
pid_t zygote_diff_pid_ = -1;
+ bool dump_dirty_objects_ = false;
};
struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
@@ -1514,7 +1555,8 @@ struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
return DumpImage(runtime,
args_->os_,
args_->image_diff_pid_,
- args_->zygote_diff_pid_) == EXIT_SUCCESS;
+ args_->zygote_diff_pid_,
+ args_->dump_dirty_objects_) == EXIT_SUCCESS;
}
};
diff --git a/runtime/aot_class_linker.cc b/runtime/aot_class_linker.cc
index b1bc3f8f2e..d8f9e22962 100644
--- a/runtime/aot_class_linker.cc
+++ b/runtime/aot_class_linker.cc
@@ -27,6 +27,16 @@ AotClassLinker::AotClassLinker(InternTable *intern_table) : ClassLinker(intern_t
AotClassLinker::~AotClassLinker() {}
+bool AotClassLinker::CanAllocClass() {
+ // AllocClass doesn't work under transaction, so we abort.
+ if (Runtime::Current()->IsActiveTransaction()) {
+ Runtime::Current()->AbortTransactionAndThrowAbortError(Thread::Current(), "Can't resolve this "
+ "type within a transaction.");
+ return false;
+ }
+ return ClassLinker::CanAllocClass();
+}
+
// Wrap the original InitializeClass with creation of transaction when in strict mode.
bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
bool can_init_statics, bool can_init_parents) {
@@ -38,6 +48,13 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
return ClassLinker::InitializeClass(self, klass, can_init_statics, can_init_parents);
}
+ // When in strict_mode, don't initialize a class if it belongs to boot but not initialized.
+ if (strict_mode_ && klass->IsBootStrapClassLoaded()) {
+ runtime->AbortTransactionAndThrowAbortError(self, "Can't resolve "
+ + klass->PrettyTypeOf() + " because it is an uninitialized boot class.");
+ return false;
+ }
+
// Don't initialize klass if it's superclass is not initialized, because superclass might abort
// the transaction and rolled back after klass's change is commited.
if (strict_mode_ && !klass->IsInterface() && klass->HasSuperClass()) {
@@ -58,9 +75,8 @@ bool AotClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
// Exit Transaction if success.
runtime->ExitTransactionMode();
} else {
- // If not successfully initialized, the last transaction must abort. Don't rollback
- // immediately, leave the cleanup to compiler driver which needs abort message and exception.
- DCHECK(runtime->IsTransactionAborted());
+ // If not successfully initialized, don't rollback immediately, leave the cleanup to compiler
+ // driver which needs abort message and exception.
DCHECK(self->IsExceptionPending());
}
}
diff --git a/runtime/aot_class_linker.h b/runtime/aot_class_linker.h
index 11bea86fc4..e9a96fae20 100644
--- a/runtime/aot_class_linker.h
+++ b/runtime/aot_class_linker.h
@@ -27,6 +27,13 @@ class AotClassLinker : public ClassLinker {
explicit AotClassLinker(InternTable *intern_table);
~AotClassLinker();
+ // Override AllocClass because aot compiler will need to perform a transaction check to determine
+ // can we allocate class from heap.
+ bool CanAllocClass()
+ OVERRIDE
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Roles::uninterruptible_);
+
bool InitializeClass(Thread *self,
Handle<mirror::Class> klass,
bool can_run_clinit,
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 3ac87c5137..46b0113cdd 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -2854,7 +2854,11 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
- klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
+ if (CanAllocClass()) {
+ klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
+ } else {
+ return nullptr;
+ }
}
if (UNLIKELY(klass == nullptr)) {
self->AssertPendingOOMException();
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index bf14aebb52..584bd1d5ce 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -710,6 +710,12 @@ class ClassLinker {
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ virtual bool CanAllocClass()
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_) {
+ return true;
+ }
+
private:
class LinkInterfaceMethodsHelper;
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 0351fd3afb..63058cfe6f 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -72,8 +72,8 @@ class VdexFile {
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Change method lookup.
- static constexpr uint8_t kVdexVersion[] = { '0', '0', '9', '\0' };
+ // Last update: Use set for unverified_classes_.
+ static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 470b0b3d58..0481f24c45 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -59,9 +59,7 @@ void VerifierDeps::MergeWith(const VerifierDeps& other,
MergeSets(my_deps->classes_, other_deps.classes_);
MergeSets(my_deps->fields_, other_deps.fields_);
MergeSets(my_deps->methods_, other_deps.methods_);
- for (dex::TypeIndex entry : other_deps.unverified_classes_) {
- my_deps->unverified_classes_.push_back(entry);
- }
+ MergeSets(my_deps->unverified_classes_, other_deps.unverified_classes_);
}
}
@@ -507,7 +505,7 @@ void VerifierDeps::MaybeRecordVerificationStatus(const DexFile& dex_file,
VerifierDeps* thread_deps = GetThreadLocalVerifierDeps();
if (thread_deps != nullptr) {
DexFileDeps* dex_deps = thread_deps->GetDexFileDeps(dex_file);
- dex_deps->unverified_classes_.push_back(type_idx);
+ dex_deps->unverified_classes_.insert(type_idx);
}
}
@@ -586,6 +584,16 @@ template<> inline dex::StringIndex Decode<dex::StringIndex>(uint32_t in) {
return dex::StringIndex(in);
}
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void EncodeTuple(std::vector<uint8_t>* out, const dex::TypeIndex& t) {
+ EncodeUnsignedLeb128(out, Encode(t));
+}
+
+// TODO: Clean this up, if we use a template arg here it confuses the compiler.
+static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, dex::TypeIndex* t) {
+ *t = Decode<dex::TypeIndex>(DecodeUint32WithOverflowCheck(in, end));
+}
+
template<typename T1, typename T2>
static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) {
EncodeUnsignedLeb128(out, Encode(std::get<0>(t)));
@@ -692,7 +700,7 @@ void VerifierDeps::Encode(const std::vector<const DexFile*>& dex_files,
EncodeSet(buffer, deps.classes_);
EncodeSet(buffer, deps.fields_);
EncodeSet(buffer, deps.methods_);
- EncodeUint16Vector(buffer, deps.unverified_classes_);
+ EncodeSet(buffer, deps.unverified_classes_);
}
}
@@ -715,7 +723,7 @@ VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files,
DecodeSet(&data_start, data_end, &deps->classes_);
DecodeSet(&data_start, data_end, &deps->fields_);
DecodeSet(&data_start, data_end, &deps->methods_);
- DecodeUint16Vector(&data_start, data_end, &deps->unverified_classes_);
+ DecodeSet(&data_start, data_end, &deps->unverified_classes_);
}
CHECK_LE(data_start, data_end);
}
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 2d452f6d6b..4069a1188a 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -117,7 +117,7 @@ class VerifierDeps {
bool ValidateDependencies(Handle<mirror::ClassLoader> class_loader, Thread* self) const
REQUIRES_SHARED(Locks::mutator_lock_);
- const std::vector<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
+ const std::set<dex::TypeIndex>& GetUnverifiedClasses(const DexFile& dex_file) const {
return GetDexFileDeps(dex_file)->unverified_classes_;
}
@@ -197,7 +197,7 @@ class VerifierDeps {
std::set<MethodResolution> methods_;
// List of classes that were not fully verified in that dex file.
- std::vector<dex::TypeIndex> unverified_classes_;
+ std::set<dex::TypeIndex> unverified_classes_;
bool Equals(const DexFileDeps& rhs) const;
};
diff --git a/test/660-clinit/expected.txt b/test/660-clinit/expected.txt
index 9eb4941276..ee1b47929e 100644
--- a/test/660-clinit/expected.txt
+++ b/test/660-clinit/expected.txt
@@ -1,4 +1,5 @@
JNI_OnLoad called
+hello world
A.a: 5
A.a: 10
B.b: 10
diff --git a/test/660-clinit/profile b/test/660-clinit/profile
index 0239f22039..9eb4924ab1 100644
--- a/test/660-clinit/profile
+++ b/test/660-clinit/profile
@@ -4,7 +4,10 @@ LDay;
LA;
LB;
LC;
+LE;
LG;
LGs;
LObjectRef;
-
+LInvokeStatic;
+LClinitE;
+LPrint;
diff --git a/test/660-clinit/src/Main.java b/test/660-clinit/src/Main.java
index f9b068e110..51b4d604f5 100644
--- a/test/660-clinit/src/Main.java
+++ b/test/660-clinit/src/Main.java
@@ -24,19 +24,28 @@ public class Main {
if (!checkAppImageLoaded()) {
System.out.println("AppImage not loaded.");
}
+ if (!checkAppImageContains(ClInit.class)) {
+ System.out.println("ClInit class is not in app image!");
+ }
- expectNotPreInit(Day.class);
- expectNotPreInit(ClInit.class); // should pass
- expectNotPreInit(A.class); // should pass
- expectNotPreInit(B.class); // should fail
- expectNotPreInit(C.class); // should fail
- expectNotPreInit(G.class); // should fail
- expectNotPreInit(Gs.class); // should fail
- expectNotPreInit(Gss.class); // should fail
+ expectPreInit(ClInit.class);
+ expectPreInit(A.class);
+ expectPreInit(E.class);
+ expectNotPreInit(B.class);
+ expectNotPreInit(C.class);
+ expectNotPreInit(G.class);
+ expectNotPreInit(Gs.class);
+ expectNotPreInit(Gss.class);
+ expectPreInit(InvokeStatic.class);
+ expectNotPreInit(ClinitE.class);
expectNotPreInit(Add.class);
expectNotPreInit(Mul.class);
expectNotPreInit(ObjectRef.class);
+ expectNotPreInit(Print.class);
+
+ Print p = new Print();
+ Gs gs = new Gs();
A x = new A();
System.out.println("A.a: " + A.a);
@@ -62,6 +71,10 @@ public class Main {
System.out.println("a != 101");
}
+ try {
+ ClinitE e = new ClinitE();
+ } catch (Error err) { }
+
return;
}
@@ -154,6 +167,13 @@ class C {
}
}
+class E {
+ public static final int e;
+ static {
+ e = 100;
+ }
+}
+
class G {
static G g;
static int i;
@@ -182,9 +202,36 @@ class Add {
}
}
+// test of INVOKE_STATIC instruction
+class InvokeStatic {
+ static int a;
+ static int b;
+ static {
+ a = Add.exec(10, 20);
+ b = Mul.exec(10, 20);
+ }
+}
+
// non-image
class Mul {
static int exec(int a, int b) {
return a * b;
}
}
+
+class ClinitE {
+ static {
+ if (Math.sin(3) < 0.5) {
+ // throw anyway, can't initialized
+ throw new ExceptionInInitializerError("Can't initialize this class!");
+ }
+ }
+}
+
+// fail because JNI
+class Print {
+ static {
+ System.out.println("hello world");
+ }
+}
+
diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt
index 73b7129bba..85391fa25a 100644
--- a/test/906-iterate-heap/expected.txt
+++ b/test/906-iterate-heap/expected.txt
@@ -19,9 +19,7 @@
1@0 (32, 2xD '0000000000000000000000000000f03f')
2
doTestPrimitiveFieldsClasses
-10000@0 (static, int, index=3) 0000000000000000
10001
-10000@0 (static, int, index=11) 0000000000000000
10001
10001
10001
diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java
index 65c2c8c560..1878687e26 100644
--- a/test/906-iterate-heap/src/art/Test906.java
+++ b/test/906-iterate-heap/src/art/Test906.java
@@ -143,19 +143,40 @@ public class Test906 {
private static void doTestPrimitiveFieldsClasses() {
System.out.println("doTestPrimitiveFieldsClasses");
+ boolean correctHeapValue = false;
+
setTag(IntObject.class, 10000);
- System.out.println(iterateThroughHeapPrimitiveFields(10000));
+ String heapTrace = iterateThroughHeapPrimitiveFields(10000);
+
+ if (!checkInitialized(IntObject.class)) {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000000");
+ } else {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000005");
+ }
+
+ if (!correctHeapValue)
+ System.out.println("Heap Trace for IntObject is not as expected:\n" + heapTrace);
+
System.out.println(getTag(IntObject.class));
setTag(IntObject.class, 0);
setTag(FloatObject.class, 10000);
- System.out.println(iterateThroughHeapPrimitiveFields(10000));
+ heapTrace = iterateThroughHeapPrimitiveFields(10000);
+
+ if (!checkInitialized(FloatObject.class)) {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000000");
+ } else {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000006");
+ }
+
+ if (!correctHeapValue)
+ System.out.println("Heap Trace for FloatObject is not as expected:\n" + heapTrace);
+
System.out.println(getTag(FloatObject.class));
setTag(FloatObject.class, 0);
- boolean correctHeapValue = false;
setTag(Inf1.class, 10000);
- String heapTrace = iterateThroughHeapPrimitiveFields(10000);
+ heapTrace = iterateThroughHeapPrimitiveFields(10000);
if (!checkInitialized(Inf1.class)) {
correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000");
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index 6144881a55..844afe8172 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -136,9 +136,7 @@ root@root --(thread)--> 3000@0 [size=136, length=-1]
4@0 (18, 3xS '010002000300')
1@0 (14, 2xZ '0001')
23456789
-10000@0 (static, int, index=3) 0000000000000000
10001
-10000@0 (static, int, index=11) 0000000000000000
10001
10001
10001
diff --git a/test/913-heaps/src/art/Test913.java b/test/913-heaps/src/art/Test913.java
index b9990010ff..28f9546d95 100644
--- a/test/913-heaps/src/art/Test913.java
+++ b/test/913-heaps/src/art/Test913.java
@@ -185,19 +185,40 @@ public class Test913 {
}
private static void doTestPrimitiveFieldsClasses() {
+ boolean correctHeapValue = false;
+
setTag(IntObject.class, 10000);
- System.out.println(followReferencesPrimitiveFields(IntObject.class));
+ String heapTrace = followReferencesPrimitiveFields(IntObject.class);
+
+ if (!checkInitialized(IntObject.class)) {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000000");
+ } else {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=3) 0000000000000005");
+ }
+
+ if (!correctHeapValue)
+ System.out.println("Heap Trace for IntObject is not as expected:\n" + heapTrace);
+
System.out.println(getTag(IntObject.class));
setTag(IntObject.class, 0);
setTag(FloatObject.class, 10000);
- System.out.println(followReferencesPrimitiveFields(FloatObject.class));
+ heapTrace = followReferencesPrimitiveFields(FloatObject.class);
+
+ if (!checkInitialized(FloatObject.class)) {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000000");
+ } else {
+ correctHeapValue = heapTrace.equals("10000@0 (static, int, index=11) 0000000000000006");
+ }
+
+ if (!correctHeapValue)
+ System.out.println("Heap Trace for FloatObject is not as expected:\n" + heapTrace);
+
System.out.println(getTag(FloatObject.class));
setTag(FloatObject.class, 0);
- boolean correctHeapValue = false;
setTag(Inf1.class, 10000);
- String heapTrace = followReferencesPrimitiveFields(Inf1.class);
+ heapTrace = followReferencesPrimitiveFields(Inf1.class);
if (!checkInitialized(Inf1.class)) {
correctHeapValue = heapTrace.equals("10000@0 (static, int, index=0) 0000000000000000");
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 5a67fbcc45..04de7a1a74 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -708,8 +708,8 @@
},
{
"tests": "660-clinit",
- "variant": "no-image | no-dex2oat | no-prebuild",
- "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild and",
- "--no-dex2oat do not create"]
+ "variant": "no-image | no-dex2oat | no-prebuild | interp-ac",
+ "description": ["Tests <clinit> for app images, which --no-image, --no-prebuild, interp-ac",
+ "and --no-dex2oat do not create"]
}
]
diff --git a/tools/art b/tools/art
index 18c5c84ed0..561b019be8 100644
--- a/tools/art
+++ b/tools/art
@@ -119,6 +119,71 @@ function verbose_run() {
env "$@"
}
+# Parse a colon-separated list into an array (e.g. "foo.dex:bar.dex" -> (foo.dex bar.dex))
+PARSE_CLASSPATH_RESULT=() # Return value will be here due to shell limitations.
+parse_classpath() {
+ local cp="$1"
+ local oldifs=$IFS
+
+ local cp_array
+ cp_array=()
+
+ IFS=":"
+ for part in $cp; do
+ cp_array+=("$part")
+ done
+ IFS=$oldifs
+
+ PARSE_CLASSPATH_RESULT=("${cp_array[@]}")
+}
+
+# Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+# e.g. (-cp foo/classes.dex:bar/classes.dex) -> (foo/classes.dex bar/classes.dex)
+find_cp_in_args() {
+ local found="false"
+ local index=0
+ local what
+
+ while [[ $# -gt 0 ]]; do
+ case "$1" in
+ -cp|-classpath)
+ parse_classpath "$2"
+ # Sets 'PARSE_CLASSPATH_RESULT' to an array of class path dex files.
+ # Subsequent parses will overwrite the preceding.
+ shift
+ ;;
+ esac
+ shift
+ done
+}
+
+# Delete the 'oat' directories relative to the classpath's dex files.
+# e.g. (foo/classes.dex bar/classes.dex) would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory() {
+ local classpath
+ classpath=("$@")
+
+ local dirpath
+
+ for path in "${classpath[@]}"; do
+ dirpath="$(dirname "$path")"
+ [[ -d "$dirpath" ]] && verbose_run rm -rf "$dirpath/oat"
+ done
+}
+
+# Parse -cp <CP>, -classpath <CP>, and $CLASSPATH to find the dex files.
+# Each dex file's directory will have an 'oat' file directory, delete it.
+# Input: Command line arguments to the art script.
+# e.g. -cp foo/classes.dex:bar/classes.dex would delete (foo/oat bar/oat) directories.
+cleanup_oat_directory_for_classpath() {
+ # First try: Use $CLASSPATH environment variable.
+ parse_classpath "$CLASSPATH"
+ # Second try: Look for latest -cp or -classpath arg which will take precedence.
+ find_cp_in_args "$@"
+
+ cleanup_oat_directory "${PARSE_CLASSPATH_RESULT[@]}"
+}
+
# Attempt to find $ANDROID_ROOT/framework/<isa>/core.art' without knowing what <isa> is.
function check_if_boot_image_file_exists() {
local image_location_dir="$1"
@@ -154,6 +219,9 @@ function detect_boot_image_location() {
function run_art() {
local image_location="$(detect_boot_image_location)"
+ # First cleanup any left-over 'oat' files from the last time dalvikvm was run.
+ cleanup_oat_directory_for_classpath "$@"
+ # Run dalvikvm.
verbose_run ANDROID_DATA=$ANDROID_DATA \
ANDROID_ROOT=$ANDROID_ROOT \
LD_LIBRARY_PATH=$LD_LIBRARY_PATH \
@@ -164,6 +232,9 @@ function run_art() {
-Xnorelocate \
-Ximage:"$image_location" \
"$@"
+
+ # Avoid polluting disk with 'oat' files after dalvikvm has finished.
+ cleanup_oat_directory_for_classpath "$@"
}
while [[ "$1" = "-"* ]]; do