Merge "Quick compiler: disable GVN DO NOT MERGE" into lmp-dev
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 491d72e..5817f92 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -726,6 +726,8 @@
int64_t ConstantValueWide(RegLocation loc) const {
DCHECK(IsConst(loc));
+ DCHECK(!loc.high_word); // Do not allow asking for the high partner.
+ DCHECK_LT(loc.orig_sreg + 1, GetNumSSARegs());
return (static_cast<int64_t>(constant_values_[loc.orig_sreg + 1]) << 32) |
Low32Bits(static_cast<int64_t>(constant_values_[loc.orig_sreg]));
}
diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc
index 2a51b49..ee1c467 100644
--- a/compiler/dex/quick/codegen_util.cc
+++ b/compiler/dex/quick/codegen_util.cc
@@ -56,16 +56,23 @@
bool res = false;
if (rl_src.is_const) {
if (rl_src.wide) {
+ // For wide registers, check whether we're the high partner. In that case we need to switch
+ // to the lower one for the correct value.
+ if (rl_src.high_word) {
+ rl_src.high_word = false;
+ rl_src.s_reg_low--;
+ rl_src.orig_sreg--;
+ }
if (rl_src.fp) {
- res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
+ res = InexpensiveConstantDouble(mir_graph_->ConstantValueWide(rl_src));
} else {
- res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
+ res = InexpensiveConstantLong(mir_graph_->ConstantValueWide(rl_src));
}
} else {
if (rl_src.fp) {
- res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
+ res = InexpensiveConstantFloat(mir_graph_->ConstantValue(rl_src));
} else {
- res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
+ res = InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src));
}
}
}
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index c1610c2..91c7aec 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -1457,51 +1457,6 @@
DISALLOW_COPY_AND_ASSIGN(ParallelCompilationManager);
};
-static bool SkipClassCheckClassPath(const char* descriptor, const DexFile& dex_file,
- const std::vector<const DexFile*>& classpath) {
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, classpath);
- CHECK(pair.second != NULL);
- if (pair.first != &dex_file) {
- LOG(WARNING) << "Skipping class " << descriptor << " from " << dex_file.GetLocation()
- << " previously found in " << pair.first->GetLocation();
- return true;
- }
- return false;
-}
-
-// Return true if the class should be skipped during compilation.
-//
-// The first case where we skip is for redundant class definitions in
-// the boot classpath. We skip all but the first definition in that case.
-//
-// The second case where we skip is when an app bundles classes found
-// in the boot classpath. Since at runtime we will select the class from
-// the boot classpath, we ignore the one from the app.
-//
-// The third case is if the app itself has the class defined in multiple dex files. Then we skip
-// it if it is not the first occurrence.
-static bool SkipClass(ClassLinker* class_linker, jobject class_loader, const DexFile& dex_file,
- const std::vector<const DexFile*>& dex_files,
- const DexFile::ClassDef& class_def) {
- const char* descriptor = dex_file.GetClassDescriptor(class_def);
-
- if (class_loader == NULL) {
- return SkipClassCheckClassPath(descriptor, dex_file, class_linker->GetBootClassPath());
- }
-
- if (class_linker->IsInBootClassPath(descriptor)) {
- return true;
- }
-
- if (dex_files.size() > 1) {
- // Multi-dex compilation, only take first class.
- return SkipClassCheckClassPath(descriptor, dex_file, dex_files);
- } else {
- // Single dex, take everything.
- return false;
- }
-}
-
// A fast version of SkipClass above if the class pointer is available
// that avoids the expensive FindInClassPath search.
static bool SkipClass(jobject class_loader, const DexFile& dex_file, mirror::Class* klass)
@@ -1568,81 +1523,84 @@
// definitions, since many of them many never be referenced by
// generated code.
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- if (!SkipClass(class_linker, jclass_loader, dex_file, manager->GetDexFiles(), class_def)) {
- ScopedObjectAccess soa(self);
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
- // Resolve the class.
- mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
- class_loader);
- bool resolve_fields_and_methods;
- if (klass == NULL) {
- // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
- // attempt to resolve methods and fields when there is no declaring class.
- CheckAndClearResolveException(soa.Self());
- resolve_fields_and_methods = false;
- } else {
- resolve_fields_and_methods = manager->GetCompiler()->IsImage();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file)));
+ // Resolve the class.
+ mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache,
+ class_loader);
+ bool resolve_fields_and_methods;
+ if (klass == nullptr) {
+ // Class couldn't be resolved, for example, super-class is in a different dex file. Don't
+ // attempt to resolve methods and fields when there is no declaring class.
+ CheckAndClearResolveException(soa.Self());
+ resolve_fields_and_methods = false;
+ } else {
+ // We successfully resolved a class, should we skip it?
+ if (SkipClass(jclass_loader, dex_file, klass)) {
+ return;
}
- // Note the class_data pointer advances through the headers,
- // static fields, instance fields, direct methods, and virtual
- // methods.
- const byte* class_data = dex_file.GetClassData(class_def);
- if (class_data == NULL) {
- // 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) {
- mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, true);
- if (field == NULL) {
- CheckAndClearResolveException(soa.Self());
- }
+ // 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 byte* class_data = dex_file.GetClassData(class_def);
+ if (class_data == NULL) {
+ // 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) {
+ mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, true);
+ if (field == NULL) {
+ 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.GetMemberAccessFlags() & kAccFinal) != 0) {
- requires_constructor_barrier = true;
- }
- if (resolve_fields_and_methods) {
- mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader, false);
- if (field == NULL) {
- CheckAndClearResolveException(soa.Self());
- }
- }
- it.Next();
+ it.Next();
+ }
+ // We require a constructor barrier if there are final instance fields.
+ requires_constructor_barrier = false;
+ while (it.HasNextInstanceField()) {
+ if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
+ requires_constructor_barrier = true;
}
if (resolve_fields_and_methods) {
- while (it.HasNextDirectMethod()) {
- mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader,
- NullHandle<mirror::ArtMethod>(),
- it.GetMethodInvokeType(class_def));
- if (method == NULL) {
- CheckAndClearResolveException(soa.Self());
- }
- it.Next();
+ mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader, false);
+ if (field == NULL) {
+ CheckAndClearResolveException(soa.Self());
}
- while (it.HasNextVirtualMethod()) {
- mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
- dex_cache, class_loader,
- NullHandle<mirror::ArtMethod>(),
- it.GetMethodInvokeType(class_def));
- if (method == NULL) {
- CheckAndClearResolveException(soa.Self());
- }
- it.Next();
- }
- DCHECK(!it.HasNext());
}
+ it.Next();
+ }
+ if (resolve_fields_and_methods) {
+ while (it.HasNextDirectMethod()) {
+ mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader,
+ NullHandle<mirror::ArtMethod>(),
+ it.GetMethodInvokeType(class_def));
+ if (method == NULL) {
+ CheckAndClearResolveException(soa.Self());
+ }
+ it.Next();
+ }
+ while (it.HasNextVirtualMethod()) {
+ mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(),
+ dex_cache, class_loader,
+ NullHandle<mirror::ArtMethod>(),
+ it.GetMethodInvokeType(class_def));
+ if (method == NULL) {
+ CheckAndClearResolveException(soa.Self());
+ }
+ it.Next();
+ }
+ DCHECK(!it.HasNext());
}
}
if (requires_constructor_barrier) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 75bc49b..c1fb267 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -916,7 +916,7 @@
} else if (type->IsStringClass()) {
mirror::String* string = value->AsString();
os << StringPrintf("%p String: %s\n", string,
- PrintableString(string->ToModifiedUtf8()).c_str());
+ PrintableString(string->ToModifiedUtf8().c_str()).c_str());
} else if (type->IsClassClass()) {
mirror::Class* klass = value->AsClass();
os << StringPrintf("%p Class: %s\n", klass, PrettyDescriptor(klass).c_str());
@@ -1049,7 +1049,7 @@
PrettyMethod(obj->AsArtMethod()).c_str());
} else if (obj_class->IsStringClass()) {
os << StringPrintf("%p: java.lang.String %s\n", obj,
- PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
+ PrintableString(obj->AsString()->ToModifiedUtf8().c_str()).c_str());
} else {
os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 3af90b2..d05f7af 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -29,11 +29,6 @@
namespace art {
-inline bool ClassLinker::IsInBootClassPath(const char* descriptor) {
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
- return pair.second != nullptr;
-}
-
inline mirror::Class* ClassLinker::FindSystemClass(Thread* self, const char* descriptor) {
return FindClass(self, descriptor, NullHandle<mirror::ClassLoader>());
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 51ee448..27483e7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1899,6 +1899,23 @@
return klass;
}
+typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
+
+// Search a collection of DexFiles for a descriptor
+ClassPathEntry FindInClassPath(const char* descriptor,
+ const std::vector<const DexFile*>& class_path) {
+ for (size_t i = 0; i != class_path.size(); ++i) {
+ const DexFile* dex_file = class_path[i];
+ const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
+ if (dex_class_def != NULL) {
+ return ClassPathEntry(dex_file, dex_class_def);
+ }
+ }
+ // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
+ return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
+ reinterpret_cast<const DexFile::ClassDef*>(NULL));
+}
+
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
@@ -1911,25 +1928,40 @@
}
// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(descriptor, class_loader.Get());
- if (klass != NULL) {
+ if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
return CreateArrayClass(self, descriptor, class_loader);
} else if (class_loader.Get() == nullptr) {
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, boot_class_path_);
- if (pair.second != NULL) {
+ // The boot class loader, search the boot class path.
+ ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ if (pair.second != nullptr) {
StackHandleScope<1> hs(self);
return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
+ } else {
+ // The boot class loader is searched ahead of the application class loader, failures are
+ // expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
+ // trigger the chaining with a proper stack trace.
+ mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
+ self->SetException(ThrowLocation(), pre_allocated);
+ return nullptr;
}
} else if (Runtime::Current()->UseCompileTimeClassPath()) {
- // First try the boot class path, we check the descriptor first to avoid an unnecessary
- // throw of a NoClassDefFoundError.
- if (IsInBootClassPath(descriptor)) {
- mirror::Class* system_class = FindSystemClass(self, descriptor);
- CHECK(system_class != NULL);
- return system_class;
+ // First try with the bootstrap class loader.
+ if (class_loader.Get() != nullptr) {
+ klass = LookupClass(descriptor, nullptr);
+ if (klass != nullptr) {
+ return EnsureResolved(self, descriptor, klass);
+ }
+ }
+ // If the lookup failed search the boot class path. We don't perform a recursive call to avoid
+ // a NoClassDefFoundError being allocated.
+ ClassPathEntry pair = FindInClassPath(descriptor, boot_class_path_);
+ if (pair.second != nullptr) {
+ StackHandleScope<1> hs(self);
+ return DefineClass(descriptor, NullHandle<mirror::ClassLoader>(), *pair.first, *pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
@@ -1939,38 +1971,37 @@
soa.AddLocalReference<jobject>(class_loader.Get()));
class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
}
-
- DexFile::ClassPathEntry pair = DexFile::FindInClassPath(descriptor, *class_path);
- if (pair.second != NULL) {
+ pair = FindInClassPath(descriptor, *class_path);
+ if (pair.second != nullptr) {
return DefineClass(descriptor, class_loader, *pair.first, *pair.second);
}
-
} else {
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
- ScopedLocalRef<jobject> result(soa.Env(), NULL);
+ ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
ScopedLocalRef<jobject> class_name_object(soa.Env(),
soa.Env()->NewStringUTF(class_name_string.c_str()));
- if (class_name_object.get() == NULL) {
- return NULL;
+ if (class_name_object.get() == nullptr) {
+ DCHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
}
- CHECK(class_loader_object.get() != NULL);
+ CHECK(class_loader_object.get() != nullptr);
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
- return NULL;
- } else if (result.get() == NULL) {
+ return nullptr;
+ } else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
- ThrowNullPointerException(NULL, StringPrintf("ClassLoader.loadClass returned null for %s",
- class_name_string.c_str()).c_str());
- return NULL;
+ ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
+ class_name_string.c_str()).c_str());
+ return nullptr;
} else {
// success, return mirror::Class*
return soa.Decode<mirror::Class*>(result.get());
@@ -1978,7 +2009,7 @@
}
ThrowNoClassDefFoundError("Class %s not found", PrintableString(descriptor).c_str());
- return NULL;
+ return nullptr;
}
mirror::Class* ClassLinker::DefineClass(const char* descriptor,
@@ -3084,7 +3115,7 @@
// Searching the image dex files/caches failed, we don't want to get into this situation
// often as map searches are faster, so after kMaxFailedDexCacheLookups move all image
// classes into the class table.
- const int32_t kMaxFailedDexCacheLookups = 1000;
+ constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
MoveImageClassesToClassTable();
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index bf2e781..b68b23e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -68,8 +68,6 @@
// Initialize class linker from one or more images.
void InitFromImage() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- bool IsInBootClassPath(const char* descriptor);
-
// Finds a class by its descriptor, loading it if necessary.
// If class_loader is null, searches boot_class_path_.
mirror::Class* FindClass(Thread* self, const char* descriptor,
@@ -85,7 +83,7 @@
mirror::Class* FindArrayClass(Thread* self, mirror::Class** element_class)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
- // Reutrns true if the class linker is initialized.
+ // Returns true if the class linker is initialized.
bool IsInitialized() const;
// Define a new a class based on a ClassDef from a DexFile
@@ -638,7 +636,7 @@
bool dex_cache_image_class_lookup_required_;
// Number of times we've searched dex caches for a class. After a certain number of misses we move
// the classes into the class_table_ to avoid dex cache based searches.
- AtomicInteger failed_dex_cache_class_lookups_;
+ Atomic<uint32_t> failed_dex_cache_class_lookups_;
mirror::Class* LookupClassFromTableLocked(const char* descriptor,
const mirror::ClassLoader* class_loader,
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index ddc2147..38ea754 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -50,20 +50,6 @@
const byte DexFile::kDexMagic[] = { 'd', 'e', 'x', '\n' };
const byte DexFile::kDexMagicVersion[] = { '0', '3', '5', '\0' };
-DexFile::ClassPathEntry DexFile::FindInClassPath(const char* descriptor,
- const ClassPath& class_path) {
- for (size_t i = 0; i != class_path.size(); ++i) {
- const DexFile* dex_file = class_path[i];
- const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor);
- if (dex_class_def != NULL) {
- return ClassPathEntry(dex_file, dex_class_def);
- }
- }
- // TODO: remove reinterpret_cast when issue with -std=gnu++0x host issue resolved
- return ClassPathEntry(reinterpret_cast<const DexFile*>(NULL),
- reinterpret_cast<const DexFile::ClassDef*>(NULL));
-}
-
static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != NULL);
ScopedFd fd(open(filename, O_RDONLY, 0));
@@ -366,7 +352,10 @@
field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
- class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)) {
+ class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
+ find_class_def_misses_(0),
+ class_def_index_(nullptr),
+ build_class_def_index_mutex_("DexFile index creation mutex") {
CHECK(begin_ != NULL) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
}
@@ -376,6 +365,8 @@
// that's only called after DetachCurrentThread, which means there's no JNIEnv. We could
// re-attach, but cleaning up these global references is not obviously useful. It's not as if
// the global reference table is otherwise empty!
+ // Remove the index if one were created.
+ delete class_def_index_.LoadRelaxed();
}
bool DexFile::Init(std::string* error_msg) {
@@ -424,26 +415,51 @@
}
const DexFile::ClassDef* DexFile::FindClassDef(const char* descriptor) const {
- size_t num_class_defs = NumClassDefs();
+ // If we have an index lookup the descriptor via that as its constant time to search.
+ Index* index = class_def_index_.LoadSequentiallyConsistent();
+ if (index != nullptr) {
+ auto it = index->find(descriptor);
+ return (it == index->end()) ? nullptr : it->second;
+ }
+ // Fast path for rate no class defs case.
+ uint32_t num_class_defs = NumClassDefs();
if (num_class_defs == 0) {
- return NULL;
+ return nullptr;
}
+ // Search for class def with 2 binary searches and then a linear search.
const StringId* string_id = FindStringId(descriptor);
- if (string_id == NULL) {
- return NULL;
- }
- const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
- if (type_id == NULL) {
- return NULL;
- }
- uint16_t type_idx = GetIndexForTypeId(*type_id);
- for (size_t i = 0; i < num_class_defs; ++i) {
- const ClassDef& class_def = GetClassDef(i);
- if (class_def.class_idx_ == type_idx) {
- return &class_def;
+ if (string_id != nullptr) {
+ const TypeId* type_id = FindTypeId(GetIndexForStringId(*string_id));
+ if (type_id != nullptr) {
+ uint16_t type_idx = GetIndexForTypeId(*type_id);
+ for (size_t i = 0; i < num_class_defs; ++i) {
+ const ClassDef& class_def = GetClassDef(i);
+ if (class_def.class_idx_ == type_idx) {
+ return &class_def;
+ }
+ }
}
}
- return NULL;
+ // A miss. If we've had kMaxFailedDexClassDefLookups misses then build an index to speed things
+ // up. This isn't done eagerly at construction as construction is not performed in multi-threaded
+ // sections of tools like dex2oat. If we're lazy we hopefully increase the chance of balancing
+ // out which thread builds the index.
+ find_class_def_misses_++;
+ const uint32_t kMaxFailedDexClassDefLookups = 100;
+ if (find_class_def_misses_ > kMaxFailedDexClassDefLookups) {
+ MutexLock mu(Thread::Current(), build_class_def_index_mutex_);
+ // Are we the first ones building the index?
+ if (class_def_index_.LoadSequentiallyConsistent() == nullptr) {
+ index = new Index(num_class_defs);
+ for (uint32_t i = 0; i < num_class_defs; ++i) {
+ const ClassDef& class_def = GetClassDef(i);
+ const char* descriptor = GetClassDescriptor(class_def);
+ index->insert(std::make_pair(descriptor, &class_def));
+ }
+ class_def_index_.StoreSequentiallyConsistent(index);
+ }
+ }
+ return nullptr;
}
const DexFile::ClassDef* DexFile::FindClassDef(uint16_t type_idx) const {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index a71a5e1..1da1f81 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
#include "base/logging.h"
@@ -27,7 +28,7 @@
#include "invoke_type.h"
#include "jni.h"
#include "modifiers.h"
-#include "safe_map.h"
+#include "utf.h"
namespace art {
@@ -356,13 +357,6 @@
DISALLOW_COPY_AND_ASSIGN(AnnotationItem);
};
- typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
- typedef std::vector<const DexFile*> ClassPath;
-
- // Search a collection of DexFiles for a descriptor
- static ClassPathEntry FindInClassPath(const char* descriptor,
- const ClassPath& class_path);
-
// Returns the checksum of a file for comparison with GetLocationChecksum().
// For .dex files, this is the header checksum.
// For zip files, this is the classes.dex zip entry CRC32 checksum.
@@ -478,7 +472,7 @@
const StringId* FindStringId(const uint16_t* string) const;
// Returns the number of type identifiers in the .dex file.
- size_t NumTypeIds() const {
+ uint32_t NumTypeIds() const {
DCHECK(header_ != NULL) << GetLocation();
return header_->type_ids_size_;
}
@@ -607,7 +601,7 @@
return StringDataAndUtf16LengthByIdx(GetProtoId(method_id.proto_idx_).shorty_idx_, length);
}
// Returns the number of class definitions in the .dex file.
- size_t NumClassDefs() const {
+ uint32_t NumClassDefs() const {
DCHECK(header_ != NULL) << GetLocation();
return header_->class_defs_size_;
}
@@ -969,6 +963,23 @@
// Points to the base of the class definition list.
const ClassDef* const class_defs_;
+
+ // Number of misses finding a class def from a descriptor.
+ mutable Atomic<uint32_t> find_class_def_misses_;
+
+ struct UTF16HashCmp {
+ // Hash function.
+ size_t operator()(const char* key) const {
+ return ComputeUtf8Hash(key);
+ }
+ // std::equal function.
+ bool operator()(const char* a, const char* b) const {
+ return CompareModifiedUtf8ToModifiedUtf8AsUtf16CodePointValues(a, b) == 0;
+ }
+ };
+ typedef std::unordered_map<const char*, const ClassDef*, UTF16HashCmp, UTF16HashCmp> Index;
+ mutable Atomic<Index*> class_def_index_;
+ mutable Mutex build_class_def_index_mutex_ DEFAULT_MUTEX_ACQUIRED_AFTER;
};
std::ostream& operator<<(std::ostream& os, const DexFile& dex_file);
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index d7d541a..976cac9 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -171,7 +171,7 @@
}
bool DexFileVerifier::CheckListSize(const void* start, size_t count, size_t elem_size,
- const char* label) {
+ const char* label) {
// Check that size is not 0.
CHECK_NE(elem_size, 0U);
@@ -201,6 +201,23 @@
return true;
}
+bool DexFileVerifier::CheckList(size_t element_size, const char* label, const byte* *ptr) {
+ // Check that the list is available. The first 4B are the count.
+ if (!CheckListSize(*ptr, 1, 4U, label)) {
+ return false;
+ }
+
+ uint32_t count = *reinterpret_cast<const uint32_t*>(*ptr);
+ if (count > 0) {
+ if (!CheckListSize(*ptr + 4, count, element_size, label)) {
+ return false;
+ }
+ }
+
+ *ptr += 4 + count * element_size;
+ return true;
+}
+
bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* label) {
if (UNLIKELY(field >= limit)) {
ErrorStringPrintf("Bad index for %s: %x >= %x", label, field, limit);
@@ -209,6 +226,20 @@
return true;
}
+bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label) {
+ if (size == 0) {
+ if (offset != 0) {
+ ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label);
+ return false;
+ }
+ }
+ if (size_ <= offset) {
+ ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
+ return false;
+ }
+ return true;
+}
+
bool DexFileVerifier::CheckHeader() {
// Check file size from the header.
uint32_t expected_size = header_->file_size_;
@@ -238,11 +269,29 @@
return false;
}
- return true;
+ // Check that all offsets are inside the file.
+ bool result =
+ CheckValidOffsetAndSize(header_->link_off_, header_->link_size_, "link") &&
+ CheckValidOffsetAndSize(header_->map_off_, header_->map_off_, "map") &&
+ CheckValidOffsetAndSize(header_->string_ids_off_, header_->string_ids_size_, "string-ids") &&
+ CheckValidOffsetAndSize(header_->type_ids_off_, header_->type_ids_size_, "type-ids") &&
+ CheckValidOffsetAndSize(header_->proto_ids_off_, header_->proto_ids_size_, "proto-ids") &&
+ CheckValidOffsetAndSize(header_->field_ids_off_, header_->field_ids_size_, "field-ids") &&
+ CheckValidOffsetAndSize(header_->method_ids_off_, header_->method_ids_size_, "method-ids") &&
+ CheckValidOffsetAndSize(header_->class_defs_off_, header_->class_defs_size_, "class-defs") &&
+ CheckValidOffsetAndSize(header_->data_off_, header_->data_size_, "data");
+
+ return result;
}
bool DexFileVerifier::CheckMap() {
- const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ + header_->map_off_);
+ const DexFile::MapList* map = reinterpret_cast<const DexFile::MapList*>(begin_ +
+ header_->map_off_);
+ // Check that map list content is available.
+ if (!CheckListSize(map, 1, sizeof(DexFile::MapList), "maplist content")) {
+ return false;
+ }
+
const DexFile::MapItem* item = map->list_;
uint32_t count = map->size_;
@@ -1117,48 +1166,21 @@
break;
}
case DexFile::kDexTypeTypeList: {
- const DexFile::TypeList* list = reinterpret_cast<const DexFile::TypeList*>(ptr_);
-
- // Check that at least the header (size) is available.
- if (!CheckListSize(list, 1, DexFile::TypeList::GetHeaderSize(), "type_list")) {
+ if (!CheckList(sizeof(DexFile::TypeItem), "type_list", &ptr_)) {
return false;
}
-
- // Check that the whole list is available.
- const size_t list_size = DexFile::TypeList::GetListSize(list->Size());
- if (!CheckListSize(list, 1, list_size, "type_list size")) {
- return false;
- }
-
- ptr_ += list_size;
break;
}
case DexFile::kDexTypeAnnotationSetRefList: {
- const DexFile::AnnotationSetRefList* list =
- reinterpret_cast<const DexFile::AnnotationSetRefList*>(ptr_);
- const DexFile::AnnotationSetRefItem* item = list->list_;
- uint32_t count = list->size_;
-
- if (!CheckListSize(list, 1, sizeof(DexFile::AnnotationSetRefList),
- "annotation_set_ref_list") ||
- !CheckListSize(item, count, sizeof(DexFile::AnnotationSetRefItem),
- "annotation_set_ref_list size")) {
+ if (!CheckList(sizeof(DexFile::AnnotationSetRefItem), "annotation_set_ref_list", &ptr_)) {
return false;
}
- ptr_ = reinterpret_cast<const byte*>(item + count);
break;
}
case DexFile::kDexTypeAnnotationSetItem: {
- const DexFile::AnnotationSetItem* set =
- reinterpret_cast<const DexFile::AnnotationSetItem*>(ptr_);
- const uint32_t* item = set->entries_;
- uint32_t count = set->size_;
-
- if (!CheckListSize(set, 1, sizeof(DexFile::AnnotationSetItem), "annotation_set_item") ||
- !CheckListSize(item, count, sizeof(uint32_t), "annotation_set_item size")) {
+ if (!CheckList(sizeof(uint32_t), "annotation_set_item", &ptr_)) {
return false;
}
- ptr_ = reinterpret_cast<const byte*>(item + count);
break;
}
case DexFile::kDexTypeClassDataItem: {
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 1d915b9..606da54 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -43,6 +43,12 @@
bool CheckShortyDescriptorMatch(char shorty_char, const char* descriptor, bool is_return_type);
bool CheckListSize(const void* start, size_t count, size_t element_size, const char* label);
+ // Check a list. The head is assumed to be at *ptr, and elements to be of size element_size. If
+ // successful, the ptr will be moved forward the amount covered by the list.
+ bool CheckList(size_t element_size, const char* label, const byte* *ptr);
+ // Checks whether the offset is zero (when size is zero) or that the offset falls within the area
+ // claimed by the file.
+ bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label);
bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
bool CheckHeader();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8d614e1..ce914e5 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -138,7 +138,6 @@
growth_limit_(growth_limit),
max_allowed_footprint_(initial_size),
native_footprint_gc_watermark_(initial_size),
- native_footprint_limit_(2 * initial_size),
native_need_to_run_finalization_(false),
// Initially assume we perceive jank in case the process state is never updated.
process_state_(kProcessStateJankPerceptible),
@@ -2833,7 +2832,6 @@
target_size = native_size + min_free_;
}
native_footprint_gc_watermark_ = target_size;
- native_footprint_limit_ = 2 * target_size - native_size;
}
collector::GarbageCollector* Heap::FindCollectorByGcType(collector::GcType gc_type) {
@@ -3105,14 +3103,14 @@
// The second watermark is higher than the gc watermark. If you hit this it means you are
// allocating native objects faster than the GC can keep up with.
- if (new_native_bytes_allocated > native_footprint_limit_) {
+ if (new_native_bytes_allocated > growth_limit_) {
if (WaitForGcToComplete(kGcCauseForNativeAlloc, self) != collector::kGcTypeNone) {
// Just finished a GC, attempt to run finalizers.
RunFinalization(env);
CHECK(!env->ExceptionCheck());
}
// If we still are over the watermark, attempt a GC for alloc and run finalizers.
- if (new_native_bytes_allocated > native_footprint_limit_) {
+ if (new_native_bytes_allocated > growth_limit_) {
CollectGarbageInternal(gc_type, kGcCauseForNativeAlloc, false);
RunFinalization(env);
native_need_to_run_finalization_ = false;
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d2cc350..a0fcf53 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -887,9 +887,6 @@
// The watermark at which a concurrent GC is requested by registerNativeAllocation.
size_t native_footprint_gc_watermark_;
- // The watermark at which a GC is performed inside of registerNativeAllocation.
- size_t native_footprint_limit_;
-
// Whether or not we need to run finalizers in the next native allocation.
bool native_need_to_run_finalization_;
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index b9379f5..330d235 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -921,7 +921,7 @@
ObjectId stringObject = request.ReadObjectId();
std::string str(Dbg::StringToUtf8(stringObject));
- VLOG(jdwp) << StringPrintf(" --> %s", PrintableString(str).c_str());
+ VLOG(jdwp) << StringPrintf(" --> %s", PrintableString(str.c_str()).c_str());
expandBufAddUtf8String(pReply, str);
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a68c658..c2912be 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -638,7 +638,7 @@
CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);
std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
- if (options.get() == NULL) {
+ if (options.get() == nullptr) {
LOG(ERROR) << "Failed to parse options";
return false;
}
@@ -761,9 +761,9 @@
// ClassLinker needs an attached thread, but we can't fully attach a thread without creating
// objects. We can't supply a thread group yet; it will be fixed later. Since we are the main
// thread, we do not get a java peer.
- Thread* self = Thread::Attach("main", false, NULL, false);
+ Thread* self = Thread::Attach("main", false, nullptr, false);
CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
- CHECK(self != NULL);
+ CHECK(self != nullptr);
// Set us to runnable so tools using a runtime can allocate and GC by default
self->TransitionFromSuspendedToRunnable();
@@ -793,11 +793,11 @@
}
}
} else {
- CHECK(options->boot_class_path_ != NULL);
+ CHECK(options->boot_class_path_ != nullptr);
CHECK_NE(options->boot_class_path_->size(), 0U);
class_linker_->InitWithoutImage(*options->boot_class_path_);
}
- CHECK(class_linker_ != NULL);
+ CHECK(class_linker_ != nullptr);
verifier::MethodVerifier::Init();
method_trace_ = options->method_trace_;
@@ -823,6 +823,13 @@
pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
self->ClearException();
+ // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
+ // ahead of checking the application's class loader.
+ self->ThrowNewException(ThrowLocation(), "Ljava/lang/NoClassDefFoundError;",
+ "Class not found using the boot class loader; no stack available");
+ pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(self->GetException(NULL));
+ self->ClearException();
+
// Look for a native bridge.
native_bridge_library_filename_ = options->native_bridge_library_filename_;
android::SetupNativeBridge(native_bridge_library_filename_.c_str(), &native_bridge_art_callbacks_);
@@ -1037,12 +1044,20 @@
mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryError() {
mirror::Throwable* oome = pre_allocated_OutOfMemoryError_.Read();
- if (oome == NULL) {
+ if (oome == nullptr) {
LOG(ERROR) << "Failed to return pre-allocated OOME";
}
return oome;
}
+mirror::Throwable* Runtime::GetPreAllocatedNoClassDefFoundError() {
+ mirror::Throwable* ncdfe = pre_allocated_NoClassDefFoundError_.Read();
+ if (ncdfe == nullptr) {
+ LOG(ERROR) << "Failed to return pre-allocated NoClassDefFoundError";
+ }
+ return ncdfe;
+}
+
void Runtime::VisitConstantRoots(RootCallback* callback, void* arg) {
// Visit the classes held as static in mirror classes, these can be visited concurrently and only
// need to be visited once per GC since they never change.
@@ -1081,6 +1096,10 @@
}
resolution_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
DCHECK(!resolution_method_.IsNull());
+ if (!pre_allocated_NoClassDefFoundError_.IsNull()) {
+ pre_allocated_NoClassDefFoundError_.VisitRoot(callback, arg, 0, kRootVMInternal);
+ DCHECK(!pre_allocated_NoClassDefFoundError_.IsNull());
+ }
if (HasImtConflictMethod()) {
imt_conflict_method_.VisitRoot(callback, arg, 0, kRootVMInternal);
}
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 183bf52..db7b476 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -246,6 +246,9 @@
mirror::Throwable* GetPreAllocatedOutOfMemoryError() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ mirror::Throwable* GetPreAllocatedNoClassDefFoundError()
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
const std::vector<std::string>& GetProperties() const {
return properties_;
}
@@ -501,6 +504,7 @@
GcRoot<mirror::ArtMethod> callee_save_methods_[kLastCalleeSaveType];
GcRoot<mirror::Throwable> pre_allocated_OutOfMemoryError_;
+ GcRoot<mirror::Throwable> pre_allocated_NoClassDefFoundError_;
GcRoot<mirror::ArtMethod> resolution_method_;
GcRoot<mirror::ArtMethod> imt_conflict_method_;
GcRoot<mirror::ObjectArray<mirror::ArtMethod>> default_imt_;
diff --git a/runtime/utf.cc b/runtime/utf.cc
index e48d6d2..02cbe3b 100644
--- a/runtime/utf.cc
+++ b/runtime/utf.cc
@@ -85,6 +85,14 @@
return hash;
}
+int32_t ComputeUtf8Hash(const char* chars) {
+ int32_t hash = 0;
+ while (*chars != '\0') {
+ hash = hash * 31 + GetUtf16FromUtf8(&chars);
+ }
+ return hash;
+}
+
int CompareModifiedUtf8ToUtf16AsCodePointValues(const char* utf8_1, const uint16_t* utf8_2) {
for (;;) {
if (*utf8_1 == '\0') {
diff --git a/runtime/utf.h b/runtime/utf.h
index 63cdbdc..a09db9d 100644
--- a/runtime/utf.h
+++ b/runtime/utf.h
@@ -79,6 +79,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
int32_t ComputeUtf16Hash(const uint16_t* chars, size_t char_count);
+// Compute a hash code of a UTF-8 string.
+int32_t ComputeUtf8Hash(const char* chars);
+
/*
* Retrieve the next UTF-16 character from a UTF-8 string.
*
diff --git a/runtime/utils.cc b/runtime/utils.cc
index d704eec..c4e84f3 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -568,10 +568,10 @@
return result;
}
-std::string PrintableString(const std::string& utf) {
+std::string PrintableString(const char* utf) {
std::string result;
result += '"';
- const char* p = utf.c_str();
+ const char* p = utf;
size_t char_count = CountModifiedUtf8Chars(p);
for (size_t i = 0; i < char_count; ++i) {
uint16_t ch = GetUtf16FromUtf8(&p);
diff --git a/runtime/utils.h b/runtime/utils.h
index c4a5895..a34a01d 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -261,7 +261,7 @@
// Returns an ASCII string corresponding to the given UTF-8 string.
// Java escapes are used for non-ASCII characters.
-std::string PrintableString(const std::string& utf8);
+std::string PrintableString(const char* utf8);
// Tests whether 's' starts with 'prefix'.
bool StartsWith(const std::string& s, const char* prefix);
diff --git a/test/004-NativeAllocations/src/Main.java b/test/004-NativeAllocations/src/Main.java
index 483c667..a99fe92 100644
--- a/test/004-NativeAllocations/src/Main.java
+++ b/test/004-NativeAllocations/src/Main.java
@@ -15,6 +15,7 @@
*/
import java.lang.reflect.*;
+import java.lang.Runtime;
public class Main {
static Object nativeLock = new Object();
@@ -22,7 +23,7 @@
static Object runtime;
static Method register_native_allocation;
static Method register_native_free;
- static int maxMem = 64 * 1024 * 1024;
+ static long maxMem = 0;
static class NativeAllocation {
private int bytes;
@@ -52,8 +53,9 @@
runtime = get_runtime.invoke(null);
register_native_allocation = vm_runtime.getDeclaredMethod("registerNativeAllocation", Integer.TYPE);
register_native_free = vm_runtime.getDeclaredMethod("registerNativeFree", Integer.TYPE);
+ maxMem = Runtime.getRuntime().maxMemory();
int count = 16;
- int size = 512 * 0x400;
+ int size = (int)(maxMem / 2 / count);
int allocation_count = 256;
NativeAllocation[] allocations = new NativeAllocation[count];
for (int i = 0; i < allocation_count; ++i) {