Merge "Remove suspend point from field loading"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index d9d09bc..7283710 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -67,6 +67,7 @@
ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives
ART_GTEST_jni_internal_test_DEX_DEPS := AllFields StaticLeafMethods
ART_GTEST_oat_file_assistant_test_DEX_DEPS := Main MainStripped MultiDex Nested
+ART_GTEST_oat_file_test_DEX_DEPS := Main MultiDex
ART_GTEST_object_test_DEX_DEPS := ProtoCompare ProtoCompare2 StaticsFromCode XandY
ART_GTEST_proxy_test_DEX_DEPS := Interfaces
ART_GTEST_reflection_test_DEX_DEPS := Main NonStaticLeafMethods StaticLeafMethods
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index eda7ec6..7e32b43 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1237,6 +1237,11 @@
for (auto& class_path_file : class_path_files_) {
class_path_files.push_back(class_path_file.get());
}
+
+ // Store the classpath we have right now.
+ key_value_store_->Put(OatHeader::kClassPathKey,
+ OatFile::EncodeDexFileDependencies(class_path_files));
+
// Then the dex files we'll compile. Thus we'll resolve the class-path first.
class_path_files.insert(class_path_files.end(), dex_files_.begin(), dex_files_.end());
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0d92fc2..4e59217 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1259,94 +1259,124 @@
return ClassPathEntry(nullptr, nullptr);
}
-mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self, const char* descriptor,
- size_t hash,
- Handle<mirror::ClassLoader> class_loader) {
- // Can we special case for a well understood PathClassLoader with the BootClassLoader as parent?
- if (class_loader->GetClass() !=
- soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
- class_loader->GetParent()->GetClass() !=
- soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
- return nullptr;
- }
- ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
- // Check if this would be found in the parent boot class loader.
- if (pair.second != nullptr) {
- mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
- if (klass != nullptr) {
- // May return null if resolution on another thread fails.
- klass = EnsureResolved(self, descriptor, klass);
+static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ mirror::ClassLoader* class_loader)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return class_loader == nullptr ||
+ class_loader->GetClass() ==
+ soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader);
+}
+
+bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self, const char* descriptor,
+ size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ mirror::Class** result) {
+ // Termination case: boot class-loader.
+ if (IsBootClassLoader(soa, class_loader.Get())) {
+ // The boot class loader, search the boot class path.
+ ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
+ if (pair.second != nullptr) {
+ mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
+ if (klass != nullptr) {
+ *result = EnsureResolved(self, descriptor, klass);
+ } else {
+ *result = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(),
+ *pair.first, *pair.second);
+ }
+ if (*result == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ }
} else {
- // May OOME.
- klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
- *pair.second);
+ *result = nullptr;
}
- if (klass == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- }
- return klass;
- } else {
- // Handle as if this is the child PathClassLoader.
- // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
- StackHandleScope<3> hs(self);
- // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
- // We need to get the DexPathList and loop through it.
- ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- mirror::Object* dex_path_list =
- soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
- GetObject(class_loader.Get());
- if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
- // DexPathList has an array dexElements of Elements[] which each contain a dex file.
- mirror::Object* dex_elements_obj =
- soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
- // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
- // at the mCookie which is a DexFile vector.
- if (dex_elements_obj != nullptr) {
- Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
- hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
- for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
- mirror::Object* element = dex_elements->GetWithoutChecks(i);
- if (element == nullptr) {
- // Should never happen, fall back to java code to throw a NPE.
+ return true;
+ }
+
+ // Unsupported class-loader?
+ if (class_loader->GetClass() !=
+ soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
+ *result = nullptr;
+ return false;
+ }
+
+ // Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
+ StackHandleScope<4> hs(self);
+ Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
+ bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result);
+
+ if (!recursive_result) {
+ // Something wrong up the chain.
+ return false;
+ }
+
+ if (*result != nullptr) {
+ // Found the class up the chain.
+ return true;
+ }
+
+ // Handle this step.
+ // Handle as if this is the child PathClassLoader.
+ // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+ // We need to get the DexPathList and loop through it.
+ ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* const dex_file_field =
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ mirror::Object* dex_path_list =
+ soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+ GetObject(class_loader.Get());
+ if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
+ // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+ mirror::Object* dex_elements_obj =
+ soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+ GetObject(dex_path_list);
+ // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+ // at the mCookie which is a DexFile vector.
+ if (dex_elements_obj != nullptr) {
+ Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
+ hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
+ for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+ mirror::Object* element = dex_elements->GetWithoutChecks(i);
+ if (element == nullptr) {
+ // Should never happen, fall back to java code to throw a NPE.
+ break;
+ }
+ mirror::Object* dex_file = dex_file_field->GetObject(element);
+ if (dex_file != nullptr) {
+ mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+ if (long_array == nullptr) {
+ // This should never happen so log a warning.
+ LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
break;
}
- mirror::Object* dex_file = dex_file_field->GetObject(element);
- if (dex_file != nullptr) {
- mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
- if (long_array == nullptr) {
- // This should never happen so log a warning.
- LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
- break;
- }
- int32_t long_array_size = long_array->GetLength();
- for (int32_t j = 0; j < long_array_size; ++j) {
- const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
- long_array->GetWithoutChecks(j)));
- const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
- if (dex_class_def != nullptr) {
- RegisterDexFile(*cp_dex_file);
- mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
- *cp_dex_file, *dex_class_def);
- if (klass == nullptr) {
- CHECK(self->IsExceptionPending()) << descriptor;
- self->ClearException();
- return nullptr;
- }
- return klass;
+ int32_t long_array_size = long_array->GetLength();
+ for (int32_t j = 0; j < long_array_size; ++j) {
+ const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+ long_array->GetWithoutChecks(j)));
+ const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
+ if (dex_class_def != nullptr) {
+ RegisterDexFile(*cp_dex_file);
+ mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
+ *cp_dex_file, *dex_class_def);
+ if (klass == nullptr) {
+ CHECK(self->IsExceptionPending()) << descriptor;
+ self->ClearException();
+ // TODO: Is it really right to break here, and not check the other dex files?
+ return true;
}
+ *result = klass;
+ return true;
}
}
}
}
}
self->AssertNoPendingException();
- return nullptr;
}
+
+ // Result is still null from the parent call, no need to set it again...
+ return true;
}
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
@@ -1384,10 +1414,18 @@
}
} else {
ScopedObjectAccessUnchecked soa(self);
- mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash,
- class_loader);
- if (cp_klass != nullptr) {
- return cp_klass;
+ mirror::Class* cp_klass;
+ if (FindClassInPathClassLoader(soa, self, descriptor, hash, class_loader, &cp_klass)) {
+ // The chain was understood. So the value in cp_klass is either the class we were looking
+ // for, or not found.
+ if (cp_klass != nullptr) {
+ return cp_klass;
+ }
+ // TODO: We handle the boot classpath loader in FindClassInPathClassLoader. Try to unify this
+ // and the branch above. TODO: throw the right exception here.
+
+ // We'll let the Java-side rediscover all this and throw the exception with the right stack
+ // trace.
}
if (Runtime::Current()->IsAotCompiler()) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 2427462..68624b0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -117,11 +117,15 @@
Handle<mirror::ClassLoader> class_loader)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Find a class in the path class loader, loading it if necessary without using JNI. Hash
- // function is supposed to be ComputeModifiedUtf8Hash(descriptor).
- mirror::Class* FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self, const char* descriptor, size_t hash,
- Handle<mirror::ClassLoader> class_loader)
+ // Finds a class in the path class loader, loading it if necessary without using JNI. Hash
+ // function is supposed to be ComputeModifiedUtf8Hash(descriptor). Returns true if the
+ // class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
+ // was encountered while walking the parent chain (currently only BootClassLoader and
+ // PathClassLoader are supported).
+ bool FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self, const char* descriptor, size_t hash,
+ Handle<mirror::ClassLoader> class_loader,
+ mirror::Class** result)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
// Finds a class by its descriptor using the "system" class loader, ie by searching the
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index a971c1b3..fbb07e8 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -39,6 +39,7 @@
#include "thread.h"
#include "transaction.h"
#include "well_known_classes.h"
+#include "zip_archive.h"
namespace art {
namespace interpreter {
@@ -641,6 +642,100 @@
}
}
+// This allows reading security.properties in an unstarted runtime and initialize Security.
+static void UnstartedSecurityGetSecurityPropertiesReader(
+ Thread* self,
+ ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
+ JValue* result,
+ size_t arg_offset ATTRIBUTE_UNUSED)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ Runtime* runtime = Runtime::Current();
+ const std::vector<const DexFile*>& path = runtime->GetClassLinker()->GetBootClassPath();
+ std::string canonical(DexFile::GetDexCanonicalLocation(path[0]->GetLocation().c_str()));
+ mirror::String* string_data;
+
+ // Use a block to enclose the I/O and MemMap code so buffers are released early.
+ {
+ std::string error_msg;
+ std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(canonical.c_str(), &error_msg));
+ if (zip_archive.get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not open zip file %s: %s", canonical.c_str(),
+ error_msg.c_str());
+ return;
+ }
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find("java/security/security.properties",
+ &error_msg));
+ if (zip_entry.get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not find security.properties file in %s: %s",
+ canonical.c_str(), error_msg.c_str());
+ return;
+ }
+ std::unique_ptr<MemMap> map(zip_entry->ExtractToMemMap(canonical.c_str(),
+ "java/security/security.properties",
+ &error_msg));
+ if (map.get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not unzip security.properties file in %s: %s",
+ canonical.c_str(), error_msg.c_str());
+ return;
+ }
+
+ uint32_t length = zip_entry->GetUncompressedLength();
+ std::unique_ptr<char[]> tmp(new char[length + 1]);
+ memcpy(tmp.get(), map->Begin(), length);
+ tmp.get()[length] = 0; // null terminator
+
+ string_data = mirror::String::AllocFromModifiedUtf8(self, tmp.get());
+ }
+
+ if (string_data == nullptr) {
+ AbortTransactionOrFail(self, "Could not create string from file content of %s",
+ canonical.c_str());
+ return;
+ }
+
+ // Create a StringReader.
+ StackHandleScope<3> hs(self);
+ Handle<mirror::String> h_string(hs.NewHandle(string_data));
+
+ Handle<mirror::Class> h_class(hs.NewHandle(
+ runtime->GetClassLinker()->FindClass(self,
+ "Ljava/io/StringReader;",
+ NullHandle<mirror::ClassLoader>())));
+ if (h_class.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not find StringReader class");
+ return;
+ }
+
+ if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ AbortTransactionOrFail(self, "Could not initialize StringReader class");
+ return;
+ }
+
+ Handle<mirror::Object> h_obj(hs.NewHandle(h_class->AllocObject(self)));
+ if (h_obj.Get() == nullptr) {
+ AbortTransactionOrFail(self, "Could not allocate StringReader object");
+ return;
+ }
+
+ mirror::ArtMethod* constructor = h_class->FindDeclaredDirectMethod("<init>",
+ "(Ljava/lang/String;)V");
+ if (constructor == nullptr) {
+ AbortTransactionOrFail(self, "Could not find StringReader constructor");
+ return;
+ }
+
+ uint32_t args[1];
+ args[0] = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_string.Get()));
+ EnterInterpreterFromInvoke(self, constructor, h_obj.Get(), args, nullptr);
+
+ if (self->IsExceptionPending()) {
+ AbortTransactionOrFail(self, "Could not run StringReader constructor");
+ return;
+ }
+
+ result->SetL(h_obj.Get());
+}
+
static void UnstartedJNIVMRuntimeNewUnpaddedArray(Thread* self,
mirror::ArtMethod* method ATTRIBUTE_UNUSED,
mirror::Object* receiver ATTRIBUTE_UNUSED,
@@ -963,6 +1058,8 @@
&UnstartedMemoryPeekEntry },
{ "void libcore.io.Memory.peekByteArray(long, byte[], int, int)",
&UnstartedMemoryPeekArrayEntry },
+ { "java.io.Reader java.security.Security.getSecurityPropertiesReader()",
+ &UnstartedSecurityGetSecurityPropertiesReader },
};
for (auto& def : defs) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 35932e0..0c39f2b 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -44,8 +44,8 @@
if (loader != nullptr) {
// Try the common case.
StackHandleScope<1> hs(soa.Self());
- c = cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
- hs.NewHandle(loader));
+ cl->FindClassInPathClassLoader(soa, soa.Self(), descriptor.c_str(), descriptor_hash,
+ hs.NewHandle(loader), &c);
if (c != nullptr) {
return soa.AddLocalReference<jclass>(c);
}
diff --git a/runtime/oat.h b/runtime/oat.h
index de95fef..a31e09a 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -38,6 +38,7 @@
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDex2OatHostKey = "dex2oat-host";
static constexpr const char* kPicKey = "pic";
+ static constexpr const char* kClassPathKey = "classpath";
static OatHeader* Create(InstructionSet instruction_set,
const InstructionSetFeatures* instruction_set_features,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 81703b1..d3c4b49 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -20,6 +20,7 @@
#include <string.h>
#include <unistd.h>
+#include <cstdlib>
#include <sstream>
#include "base/bit_vector.h"
@@ -592,4 +593,90 @@
// TODO: Check against oat_patches. b/18144996
}
+static constexpr char kDexClassPathEncodingSeparator = '*';
+
+std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
+ std::ostringstream out;
+
+ for (const DexFile* dex_file : dex_files) {
+ out << dex_file->GetLocation().c_str();
+ out << kDexClassPathEncodingSeparator;
+ out << dex_file->GetLocationChecksum();
+ out << kDexClassPathEncodingSeparator;
+ }
+
+ return out.str();
+}
+
+bool OatFile::CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg) {
+ if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+ // No dependencies.
+ return true;
+ }
+
+ // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+ // Split() instead of manual parsing of the combined char*.
+ std::vector<std::string> split;
+ Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+ if (split.size() % 2 != 0) {
+ // Expected pairs of location and checksum.
+ *msg = StringPrintf("Odd number of elements in dependency list %s", dex_dependencies);
+ return false;
+ }
+
+ for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+ std::string& location = *it;
+ std::string& checksum = *(it + 1);
+ int64_t converted = strtoll(checksum.c_str(), nullptr, 10);
+ if (converted == 0) {
+ // Conversion error.
+ *msg = StringPrintf("Conversion error for %s", checksum.c_str());
+ return false;
+ }
+
+ uint32_t dex_checksum;
+ std::string error_msg;
+ if (DexFile::GetChecksum(DexFile::GetDexCanonicalLocation(location.c_str()).c_str(),
+ &dex_checksum,
+ &error_msg)) {
+ if (converted != dex_checksum) {
+ *msg = StringPrintf("Checksums don't match for %s: %" PRId64 " vs %u",
+ location.c_str(), converted, dex_checksum);
+ return false;
+ }
+ } else {
+ // Problem retrieving checksum.
+ // TODO: odex files?
+ *msg = StringPrintf("Could not retrieve checksum for %s: %s", location.c_str(),
+ error_msg.c_str());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool OatFile::GetDexLocationsFromDependencies(const char* dex_dependencies,
+ std::vector<std::string>* locations) {
+ DCHECK(locations != nullptr);
+ if (dex_dependencies == nullptr || dex_dependencies[0] == 0) {
+ return true;
+ }
+
+ // Assumption: this is not performance-critical. So it's OK to do this with a std::string and
+ // Split() instead of manual parsing of the combined char*.
+ std::vector<std::string> split;
+ Split(dex_dependencies, kDexClassPathEncodingSeparator, &split);
+ if (split.size() % 2 != 0) {
+ // Expected pairs of location and checksum.
+ return false;
+ }
+
+ for (auto it = split.begin(), end = split.end(); it != end; it += 2) {
+ locations->push_back(*it);
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 73a8c8e..a5d5ae8 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -248,6 +248,18 @@
static std::string ResolveRelativeEncodedDexLocation(
const char* abs_dex_location, const std::string& rel_dex_location);
+ // Create a dependency list (dex locations and checksums) for the given dex files.
+ static std::string EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files);
+
+ // Check the given dependency list against their dex files - thus the name "Static," this does
+ // not check the class-loader environment, only whether there have been file updates.
+ static bool CheckStaticDexFileDependencies(const char* dex_dependencies, std::string* msg);
+
+ // Get the dex locations of a dependency list. Note: this is *not* cleaned for synthetic
+ // locations of multidex files.
+ static bool GetDexLocationsFromDependencies(const char* dex_dependencies,
+ std::vector<std::string>* locations);
+
private:
static void CheckLocation(const std::string& location);
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index f2213e9..a88553c 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -20,9 +20,15 @@
#include <gtest/gtest.h>
+#include "common_runtime_test.h"
+#include "scoped_thread_state_change.h"
+
namespace art {
-TEST(OatFileTest, ResolveRelativeEncodedDexLocation) {
+class OatFileTest : public CommonRuntimeTest {
+};
+
+TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) {
EXPECT_EQ(std::string("/data/app/foo/base.apk"),
OatFile::ResolveRelativeEncodedDexLocation(
nullptr, "/data/app/foo/base.apk"));
@@ -56,4 +62,54 @@
"/data/app/foo/base.apk", "o/base.apk"));
}
+static std::vector<const DexFile*> ToConstDexFiles(
+ const std::vector<std::unique_ptr<const DexFile>>& in) {
+ std::vector<const DexFile*> ret;
+ for (auto& d : in) {
+ ret.push_back(d.get());
+ }
+ return ret;
+}
+
+TEST_F(OatFileTest, DexFileDependencies) {
+ std::string error_msg;
+
+ // No dependencies.
+ EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(nullptr, &error_msg)) << error_msg;
+ EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies("", &error_msg)) << error_msg;
+
+ // Ill-formed dependencies.
+ EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc", &error_msg));
+ EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*def", &error_msg));
+ EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*def*", &error_msg));
+
+ // Unsatisfiable dependency.
+ EXPECT_FALSE(OatFile::CheckStaticDexFileDependencies("abc*123*", &error_msg));
+
+ // Load some dex files to be able to do a real test.
+ ScopedObjectAccess soa(Thread::Current());
+
+ std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Main");
+ std::vector<const DexFile*> dex_files_const1 = ToConstDexFiles(dex_files1);
+ std::string encoding1 = OatFile::EncodeDexFileDependencies(dex_files_const1);
+ EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding1.c_str(), &error_msg))
+ << error_msg << " " << encoding1;
+ std::vector<std::string> split1;
+ EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding1.c_str(), &split1));
+ ASSERT_EQ(split1.size(), 1U);
+ EXPECT_EQ(split1[0], dex_files_const1[0]->GetLocation());
+
+ std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
+ EXPECT_GT(dex_files2.size(), 1U);
+ std::vector<const DexFile*> dex_files_const2 = ToConstDexFiles(dex_files2);
+ std::string encoding2 = OatFile::EncodeDexFileDependencies(dex_files_const2);
+ EXPECT_TRUE(OatFile::CheckStaticDexFileDependencies(encoding2.c_str(), &error_msg))
+ << error_msg << " " << encoding2;
+ std::vector<std::string> split2;
+ EXPECT_TRUE(OatFile::GetDexLocationsFromDependencies(encoding2.c_str(), &split2));
+ ASSERT_EQ(split2.size(), 2U);
+ EXPECT_EQ(split2[0], dex_files_const2[0]->GetLocation());
+ EXPECT_EQ(split2[1], dex_files_const2[1]->GetLocation());
+}
+
} // namespace art