summaryrefslogtreecommitdiff
path: root/runtime/class_linker.cc
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/class_linker.cc')
-rw-r--r--runtime/class_linker.cc1175
1 files changed, 664 insertions, 511 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index acb39c5402..4ce52f10f3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -16,12 +16,15 @@
#include "class_linker.h"
+#include <algorithm>
#include <deque>
#include <iostream>
#include <memory>
#include <queue>
#include <string>
+#include <tuple>
#include <unistd.h>
+#include <unordered_map>
#include <utility>
#include <vector>
@@ -41,24 +44,20 @@
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
+#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
-#include "handle_scope.h"
+#include "handle_scope-inl.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "leb128.h"
#include "linear_alloc.h"
-#include "oat.h"
-#include "oat_file.h"
-#include "oat_file-inl.h"
-#include "oat_file_assistant.h"
-#include "object_lock.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
@@ -72,12 +71,17 @@
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
+#include "native/dalvik_system_DexFile.h"
+#include "oat.h"
+#include "oat_file.h"
+#include "oat_file-inl.h"
+#include "oat_file_assistant.h"
+#include "oat_file_manager.h"
+#include "object_lock.h"
#include "os.h"
#include "runtime.h"
-#include "entrypoints/entrypoint_utils.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
-#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "trace.h"
#include "utils.h"
@@ -89,9 +93,6 @@ namespace art {
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
-// For b/21333911.
-static constexpr bool kDuplicateClassesCheck = false;
-
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
SHARED_REQUIRES(Locks::mutator_lock_);
@@ -696,343 +697,6 @@ void ClassLinker::RunRootClinits() {
}
}
-const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
- WriterMutexLock mu(Thread::Current(), dex_lock_);
- if (kIsDebugBuild) {
- for (size_t i = 0; i < oat_files_.size(); ++i) {
- CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation();
- }
- }
- VLOG(class_linker) << "Registering " << oat_file->GetLocation();
- oat_files_.push_back(oat_file);
- return oat_file;
-}
-
-OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
- VLOG(startup) << "ClassLinker::GetImageOatFile entering";
- OatFile* oat_file = space->ReleaseOatFile();
- CHECK_EQ(RegisterOatFile(oat_file), oat_file);
- VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
- return *oat_file;
-}
-
-class DexFileAndClassPair : ValueObject {
- public:
- DexFileAndClassPair(const DexFile* dex_file, size_t current_class_index, bool from_loaded_oat)
- : cached_descriptor_(GetClassDescriptor(dex_file, current_class_index)),
- dex_file_(dex_file),
- current_class_index_(current_class_index),
- from_loaded_oat_(from_loaded_oat) {}
-
- DexFileAndClassPair(const DexFileAndClassPair&) = default;
-
- DexFileAndClassPair& operator=(const DexFileAndClassPair& rhs) {
- cached_descriptor_ = rhs.cached_descriptor_;
- dex_file_ = rhs.dex_file_;
- current_class_index_ = rhs.current_class_index_;
- from_loaded_oat_ = rhs.from_loaded_oat_;
- return *this;
- }
-
- const char* GetCachedDescriptor() const {
- return cached_descriptor_;
- }
-
- bool operator<(const DexFileAndClassPair& rhs) const {
- const char* lhsDescriptor = cached_descriptor_;
- const char* rhsDescriptor = rhs.cached_descriptor_;
- int cmp = strcmp(lhsDescriptor, rhsDescriptor);
- if (cmp != 0) {
- // Note that the order must be reversed. We want to iterate over the classes in dex files.
- // They are sorted lexicographically. Thus, the priority-queue must be a min-queue.
- return cmp > 0;
- }
- return dex_file_ < rhs.dex_file_;
- }
-
- bool DexFileHasMoreClasses() const {
- return current_class_index_ + 1 < dex_file_->NumClassDefs();
- }
-
- DexFileAndClassPair GetNext() const {
- return DexFileAndClassPair(dex_file_, current_class_index_ + 1, from_loaded_oat_);
- }
-
- size_t GetCurrentClassIndex() const {
- return current_class_index_;
- }
-
- bool FromLoadedOat() const {
- return from_loaded_oat_;
- }
-
- const DexFile* GetDexFile() const {
- return dex_file_;
- }
-
- void DeleteDexFile() {
- delete dex_file_;
- dex_file_ = nullptr;
- }
-
- private:
- static const char* GetClassDescriptor(const DexFile* dex_file, size_t index) {
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(static_cast<uint16_t>(index));
- return dex_file->StringByTypeIdx(class_def.class_idx_);
- }
-
- const char* cached_descriptor_;
- const DexFile* dex_file_;
- size_t current_class_index_;
- bool from_loaded_oat_; // We only need to compare mismatches between what we load now
- // and what was loaded before. Any old duplicates must have been
- // OK, and any new "internal" duplicates are as well (they must
- // be from multidex, which resolves correctly).
-};
-
-static void AddDexFilesFromOat(const OatFile* oat_file,
- bool already_loaded,
- std::priority_queue<DexFileAndClassPair>* heap) {
- const std::vector<const OatDexFile*>& oat_dex_files = oat_file->GetOatDexFiles();
- for (const OatDexFile* oat_dex_file : oat_dex_files) {
- std::string error;
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error);
- if (dex_file.get() == nullptr) {
- LOG(WARNING) << "Could not create dex file from oat file: " << error;
- } else {
- if (dex_file->NumClassDefs() > 0U) {
- heap->emplace(dex_file.release(), 0U, already_loaded);
- }
- }
- }
-}
-
-static void AddNext(DexFileAndClassPair* original,
- std::priority_queue<DexFileAndClassPair>* heap) {
- if (original->DexFileHasMoreClasses()) {
- heap->push(original->GetNext());
- } else {
- // Need to delete the dex file.
- original->DeleteDexFile();
- }
-}
-
-static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap) {
- while (!heap->empty()) {
- delete heap->top().GetDexFile();
- heap->pop();
- }
-}
-
-const OatFile* ClassLinker::GetBootOatFile() {
- gc::space::ImageSpace* image_space = Runtime::Current()->GetHeap()->GetImageSpace();
- if (image_space == nullptr) {
- return nullptr;
- }
- return image_space->GetOatFile();
-}
-
-const OatFile* ClassLinker::GetPrimaryOatFile() {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- const OatFile* boot_oat_file = GetBootOatFile();
- if (boot_oat_file != nullptr) {
- for (const OatFile* oat_file : oat_files_) {
- if (oat_file != boot_oat_file) {
- return oat_file;
- }
- }
- }
- return nullptr;
-}
-
-// Check for class-def collisions in dex files.
-//
-// This works by maintaining a heap with one class from each dex file, sorted by the class
-// descriptor. Then a dex-file/class pair is continually removed from the heap and compared
-// against the following top element. If the descriptor is the same, it is now checked whether
-// the two elements agree on whether their dex file was from an already-loaded oat-file or the
-// new oat file. Any disagreement indicates a collision.
-bool ClassLinker::HasCollisions(const OatFile* oat_file, std::string* error_msg) {
- if (!kDuplicateClassesCheck) {
- return false;
- }
-
- // Dex files are registered late - once a class is actually being loaded. We have to compare
- // against the open oat files. Take the dex_lock_ that protects oat_files_ accesses.
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
-
- std::priority_queue<DexFileAndClassPair> queue;
-
- // Add dex files from already loaded oat files, but skip boot.
- {
- const OatFile* boot_oat = GetBootOatFile();
- for (const OatFile* loaded_oat_file : oat_files_) {
- if (loaded_oat_file == boot_oat) {
- continue;
- }
- AddDexFilesFromOat(loaded_oat_file, true, &queue);
- }
- }
-
- if (queue.empty()) {
- // No other oat files, return early.
- return false;
- }
-
- // Add dex files from the oat file to check.
- AddDexFilesFromOat(oat_file, false, &queue);
-
- // Now drain the queue.
- while (!queue.empty()) {
- DexFileAndClassPair compare_pop = queue.top();
- queue.pop();
-
- // Compare against the following elements.
- while (!queue.empty()) {
- DexFileAndClassPair top = queue.top();
-
- if (strcmp(compare_pop.GetCachedDescriptor(), top.GetCachedDescriptor()) == 0) {
- // Same descriptor. Check whether it's crossing old-oat-files to new-oat-files.
- if (compare_pop.FromLoadedOat() != top.FromLoadedOat()) {
- *error_msg =
- StringPrintf("Found duplicated class when checking oat files: '%s' in %s and %s",
- compare_pop.GetCachedDescriptor(),
- compare_pop.GetDexFile()->GetLocation().c_str(),
- top.GetDexFile()->GetLocation().c_str());
- FreeDexFilesInHeap(&queue);
- return true;
- }
- // Pop it.
- queue.pop();
- AddNext(&top, &queue);
- } else {
- // Something else. Done here.
- break;
- }
- }
- AddNext(&compare_pop, &queue);
- }
-
- return false;
-}
-
-std::vector<std::unique_ptr<const DexFile>> ClassLinker::OpenDexFilesFromOat(
- const char* dex_location, const char* oat_location,
- std::vector<std::string>* error_msgs) {
- CHECK(error_msgs != nullptr);
-
- // Verify we aren't holding the mutator lock, which could starve GC if we
- // have to generate or relocate an oat file.
- Locks::mutator_lock_->AssertNotHeld(Thread::Current());
-
- OatFileAssistant oat_file_assistant(dex_location, oat_location, kRuntimeISA,
- !Runtime::Current()->IsAotCompiler());
-
- // Lock the target oat location to avoid races generating and loading the
- // oat file.
- std::string error_msg;
- if (!oat_file_assistant.Lock(&error_msg)) {
- // Don't worry too much if this fails. If it does fail, it's unlikely we
- // can generate an oat file anyway.
- VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
- }
-
- // Check if we already have an up-to-date oat file open.
- const OatFile* source_oat_file = nullptr;
- {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (const OatFile* oat_file : oat_files_) {
- CHECK(oat_file != nullptr);
- if (oat_file_assistant.GivenOatFileIsUpToDate(*oat_file)) {
- source_oat_file = oat_file;
- break;
- }
- }
- }
-
- // If we didn't have an up-to-date oat file open, try to load one from disk.
- if (source_oat_file == nullptr) {
- // Update the oat file on disk if we can. This may fail, but that's okay.
- // Best effort is all that matters here.
- if (!oat_file_assistant.MakeUpToDate(&error_msg)) {
- LOG(WARNING) << error_msg;
- }
-
- // Get the oat file on disk.
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- if (oat_file.get() != nullptr) {
- // Take the file only if it has no collisions, or we must take it because of preopting.
- bool accept_oat_file = !HasCollisions(oat_file.get(), &error_msg);
- if (!accept_oat_file) {
- // Failed the collision check. Print warning.
- if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- LOG(WARNING) << "Found duplicate classes, falling back to interpreter mode for "
- << dex_location;
- } else {
- LOG(WARNING) << "Found duplicate classes, dex-file-fallback disabled, will be failing to "
- " load classes for " << dex_location;
- }
- LOG(WARNING) << error_msg;
-
- // However, if the app was part of /system and preopted, there is no original dex file
- // available. In that case grudgingly accept the oat file.
- if (!DexFile::MaybeDex(dex_location)) {
- accept_oat_file = true;
- LOG(WARNING) << "Dex location " << dex_location << " does not seem to include dex file. "
- << "Allow oat file use. This is potentially dangerous.";
- }
- }
-
- if (accept_oat_file) {
- source_oat_file = oat_file.release();
- RegisterOatFile(source_oat_file);
- }
- }
- }
-
- std::vector<std::unique_ptr<const DexFile>> dex_files;
-
- // Load the dex files from the oat file.
- if (source_oat_file != nullptr) {
- dex_files = oat_file_assistant.LoadDexFiles(*source_oat_file, dex_location);
- if (dex_files.empty()) {
- error_msgs->push_back("Failed to open dex files from "
- + source_oat_file->GetLocation());
- }
- }
-
- // Fall back to running out of the original dex file if we couldn't load any
- // dex_files from the oat file.
- if (dex_files.empty()) {
- if (oat_file_assistant.HasOriginalDexFiles()) {
- if (Runtime::Current()->IsDexFileFallbackEnabled()) {
- if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
- LOG(WARNING) << error_msg;
- error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
- }
- } else {
- error_msgs->push_back("Fallback mode disabled, skipping dex files.");
- }
- } else {
- error_msgs->push_back("No original dex files found for dex location "
- + std::string(dex_location));
- }
- }
- return dex_files;
-}
-
-const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (size_t i = 0; i < oat_files_.size(); i++) {
- const OatFile* oat_file = oat_files_[i];
- DCHECK(oat_file != nullptr);
- if (oat_file->GetLocation() == oat_location) {
- return oat_file;
- }
- }
- return nullptr;
-}
-
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
gc::space::ImageSpace* space)
@@ -1169,16 +833,17 @@ void ClassLinker::InitFromImage() {
CHECK(space != nullptr);
image_pointer_size_ = space->GetImageHeader().GetPointerSize();
dex_cache_image_class_lookup_required_ = true;
- OatFile& oat_file = GetImageOatFile(space);
- CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
- CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
- const char* image_file_location = oat_file.GetOatHeader().
+ const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space);
+ DCHECK(oat_file != nullptr);
+ CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
+ CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
+ const char* image_file_location = oat_file->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
- quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
- quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
- quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
- quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
+ quick_resolution_trampoline_ = oat_file->GetOatHeader().GetQuickResolutionTrampoline();
+ quick_imt_conflict_trampoline_ = oat_file->GetOatHeader().GetQuickImtConflictTrampoline();
+ quick_generic_jni_trampoline_ = oat_file->GetOatHeader().GetQuickGenericJniTrampoline();
+ quick_to_interpreter_bridge_trampoline_ = oat_file->GetOatHeader().GetQuickToInterpreterBridge();
StackHandleScope<2> hs(self);
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
@@ -1200,20 +865,20 @@ void ClassLinker::InitFromImage() {
java_lang_Object->GetObjectSize(),
VoidFunctor()));
- CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
+ CHECK_EQ(oat_file->GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
StackHandleScope<1> hs2(self);
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
- const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
- nullptr);
- CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
+ nullptr);
+ CHECK(oat_dex_file != nullptr) << oat_file->GetLocation() << " " << dex_file_location;
std::string error_msg;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
- if (dex_file.get() == nullptr) {
+ if (dex_file == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
- << " from within oat file " << oat_file.GetLocation()
+ << " from within oat file " << oat_file->GetLocation()
<< " error '" << error_msg << "'";
UNREACHABLE();
}
@@ -1361,9 +1026,9 @@ void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
VisitClassRoots(visitor, flags);
array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
- for (GcRoot<mirror::Class>& root : find_array_class_cache_) {
- root.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
- }
+ // Instead of visiting the find_array_class_cache_ drop it so that it doesn't prevent class
+ // unloading if we are marking roots.
+ DropFindArrayClassCache();
}
class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor {
@@ -1508,7 +1173,6 @@ ClassLinker::~ClassLinker() {
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
- STLDeleteElements(&oat_files_);
Thread* const self = Thread::Current();
JavaVMExt* const vm = Runtime::Current()->GetJavaVM();
for (const ClassLoaderData& data : class_loaders_) {
@@ -1525,7 +1189,9 @@ mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length
static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length)));
}
-mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
+mirror::DexCache* ClassLinker::AllocDexCache(Thread* self,
+ const DexFile& dex_file,
+ LinearAlloc* linear_alloc) {
StackHandleScope<6> hs(self);
auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
@@ -1540,22 +1206,20 @@ mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_fi
}
DexCacheArraysLayout layout(image_pointer_size_, &dex_file);
uint8_t* raw_arrays = nullptr;
- if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
+ if (dex_file.GetOatDexFile() != nullptr &&
+ dex_file.GetOatDexFile()->GetDexCacheArrays() != nullptr) {
+ raw_arrays = const_cast<uint8_t*>(dex_file.GetOatDexFile()->GetDexCacheArrays());
+ } else if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
// NOTE: We "leak" the raw_arrays because we never destroy the dex cache.
DCHECK(image_pointer_size_ == 4u || image_pointer_size_ == 8u);
- if (sizeof(void*) == 8u && image_pointer_size_ == 4u) {
- // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
- // in the low 4GiB address space so that we can store pointers in 32-bit fields.
- // This is conveniently provided by the linear allocator.
- raw_arrays = reinterpret_cast<uint8_t*>(
- Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size())); // Zero-initialized.
- } else {
- raw_arrays = reinterpret_cast<uint8_t*>(calloc(layout.Size(), 1u)); // Zero-initialized.
- if (raw_arrays == nullptr) {
- return nullptr;
- }
- }
+ // When cross-compiling for a 32-bit target on a 64-bit host, we need these arrays
+ // in the low 4GiB address space so that we can store pointers in 32-bit fields.
+ // This is conveniently provided by the linear allocator.
+ raw_arrays = reinterpret_cast<uint8_t*>(
+ (sizeof(void*) == 8u && image_pointer_size_ == 4u)
+ ? Runtime::Current()->GetLinearAlloc()->Alloc(self, layout.Size()) // Zero-initialized.
+ : linear_alloc->Alloc(self, layout.Size())); // Zero-initialized.
}
GcRoot<mirror::String>* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
@@ -1768,13 +1432,18 @@ bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable&
break;
}
int32_t long_array_size = long_array->GetLength();
- for (int32_t j = 0; j < long_array_size; ++j) {
+ // First element is the oat file.
+ for (int32_t j = kDexFileIndexStart; 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) {
- mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
- *cp_dex_file, *dex_class_def);
+ mirror::Class* klass = DefineClass(self,
+ descriptor,
+ hash,
+ class_loader,
+ *cp_dex_file,
+ *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
@@ -1921,7 +1590,9 @@ mirror::Class* ClassLinker::DefineClass(Thread* self,
self->AssertPendingOOMException();
return nullptr;
}
- mirror::DexCache* dex_cache = RegisterDexFile(dex_file);
+ mirror::DexCache* dex_cache = RegisterDexFile(
+ dex_file,
+ GetOrCreateAllocatorForClassLoader(class_loader.Get()));
if (dex_cache == nullptr) {
self->AssertPendingOOMException();
return nullptr;
@@ -2424,6 +2095,19 @@ LinearAlloc* ClassLinker::GetAllocatorForClassLoader(mirror::ClassLoader* class_
return allocator;
}
+LinearAlloc* ClassLinker::GetOrCreateAllocatorForClassLoader(mirror::ClassLoader* class_loader) {
+ if (class_loader == nullptr) {
+ return Runtime::Current()->GetLinearAlloc();
+ }
+ WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
+ LinearAlloc* allocator = class_loader->GetAllocator();
+ if (allocator == nullptr) {
+ allocator = Runtime::Current()->CreateLinearAlloc();
+ class_loader->SetAllocator(allocator);
+ }
+ return allocator;
+}
+
void ClassLinker::LoadClassMembers(Thread* self,
const DexFile& dex_file,
const uint8_t* class_data,
@@ -2582,7 +2266,10 @@ void ClassLinker::LoadMethod(Thread* self,
void ClassLinker::AppendToBootClassPath(Thread* self, const DexFile& dex_file) {
StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(AllocDexCache(
+ self,
+ dex_file,
+ Runtime::Current()->GetLinearAlloc())));
CHECK(dex_cache.Get() != nullptr) << "Failed to allocate dex cache for "
<< dex_file.GetLocation();
AppendToBootClassPath(dex_file, dex_cache);
@@ -2618,7 +2305,7 @@ void ClassLinker::RegisterDexFileLocked(const DexFile& dex_file,
dex_cache->SetDexFile(&dex_file);
}
-mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file) {
+mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file, LinearAlloc* linear_alloc) {
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
@@ -2631,7 +2318,7 @@ mirror::DexCache* ClassLinker::RegisterDexFile(const DexFile& dex_file) {
// suspend all threads and another thread may need the dex_lock_ to
// get to a suspend point.
StackHandleScope<1> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file)));
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(AllocDexCache(self, dex_file, linear_alloc)));
WriterMutexLock mu(self, dex_lock_);
mirror::DexCache* dex_cache = FindDexCacheLocked(self, dex_file, true);
if (dex_cache != nullptr) {
@@ -3428,6 +3115,9 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
std::string descriptor(GetDescriptorForProxy(klass.Get()));
const size_t hash = ComputeModifiedUtf8Hash(descriptor.c_str());
+ // Needs to be before we insert the class so that the allocator field is set.
+ LinearAlloc* const allocator = GetOrCreateAllocatorForClassLoader(klass->GetClassLoader());
+
// Insert the class before loading the fields as the field roots
// (ArtField::declaring_class_) are only visited from the class
// table. There can't be any suspend points between inserting the
@@ -3435,9 +3125,6 @@ mirror::Class* ClassLinker::CreateProxyClass(ScopedObjectAccessAlreadyRunnable&
mirror::Class* existing = InsertClass(descriptor.c_str(), klass.Get(), hash);
CHECK(existing == nullptr);
- // Needs to be after we insert the class so that the allocator field is set.
- LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
-
// Instance fields are inherited, but we add a couple of static fields...
const size_t num_fields = 2;
LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, num_fields);
@@ -3676,6 +3363,18 @@ bool ClassLinker::CanWeInitializeClass(mirror::Class* klass, bool can_init_stati
return false;
}
}
+ // If we are a class we need to initialize all interfaces with default methods when we are
+ // initialized. Check all of them.
+ if (!klass->IsInterface()) {
+ size_t num_interfaces = klass->GetIfTableCount();
+ for (size_t i = 0; i < num_interfaces; i++) {
+ mirror::Class* iface = klass->GetIfTable()->GetInterface(i);
+ if (iface->HasDefaultMethods() &&
+ !CanWeInitializeClass(iface, can_init_statics, can_init_parents)) {
+ return false;
+ }
+ }
+ }
}
if (klass->IsInterface() || !klass->HasSuperClass()) {
return true;
@@ -3802,6 +3501,38 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
}
}
+ if (!klass->IsInterface()) {
+ // Initialize interfaces with default methods for the JLS.
+ size_t num_direct_interfaces = klass->NumDirectInterfaces();
+ // Only setup the (expensive) handle scope if we actually need to.
+ if (UNLIKELY(num_direct_interfaces > 0)) {
+ StackHandleScope<1> hs_iface(self);
+ MutableHandle<mirror::Class> handle_scope_iface(hs_iface.NewHandle<mirror::Class>(nullptr));
+ for (size_t i = 0; i < num_direct_interfaces; i++) {
+ handle_scope_iface.Assign(mirror::Class::GetDirectInterface(self, klass, i));
+ CHECK(handle_scope_iface.Get() != nullptr);
+ CHECK(handle_scope_iface->IsInterface());
+ if (handle_scope_iface->HasBeenRecursivelyInitialized()) {
+ // We have already done this for this interface. Skip it.
+ continue;
+ }
+ // We cannot just call initialize class directly because we need to ensure that ALL
+ // interfaces with default methods are initialized. Non-default interface initialization
+ // will not affect other non-default super-interfaces.
+ bool iface_initialized = InitializeDefaultInterfaceRecursive(self,
+ handle_scope_iface,
+ can_init_statics,
+ can_init_parents);
+ if (!iface_initialized) {
+ ObjectLock<mirror::Class> lock(self, klass);
+ // Initialization failed because one of our interfaces with default methods is erroneous.
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ return false;
+ }
+ }
+ }
+ }
+
const size_t num_static_fields = klass->NumStaticFields();
if (num_static_fields > 0) {
const DexFile::ClassDef* dex_class_def = klass->GetClassDef();
@@ -3891,6 +3622,52 @@ bool ClassLinker::InitializeClass(Thread* self, Handle<mirror::Class> klass,
return success;
}
+// We recursively run down the tree of interfaces. We need to do this in the order they are declared
+// and perform the initialization only on those interfaces that contain default methods.
+bool ClassLinker::InitializeDefaultInterfaceRecursive(Thread* self,
+ Handle<mirror::Class> iface,
+ bool can_init_statics,
+ bool can_init_parents) {
+ CHECK(iface->IsInterface());
+ size_t num_direct_ifaces = iface->NumDirectInterfaces();
+ // Only create the (expensive) handle scope if we need it.
+ if (UNLIKELY(num_direct_ifaces > 0)) {
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::Class> handle_super_iface(hs.NewHandle<mirror::Class>(nullptr));
+ // First we initialize all of iface's super-interfaces recursively.
+ for (size_t i = 0; i < num_direct_ifaces; i++) {
+ mirror::Class* super_iface = mirror::Class::GetDirectInterface(self, iface, i);
+ if (!super_iface->HasBeenRecursivelyInitialized()) {
+ // Recursive step
+ handle_super_iface.Assign(super_iface);
+ if (!InitializeDefaultInterfaceRecursive(self,
+ handle_super_iface,
+ can_init_statics,
+ can_init_parents)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ bool result = true;
+ // Then we initialize 'iface' if it has default methods. We do not need to (and in fact must not)
+ // initialize if we don't have default methods.
+ if (iface->HasDefaultMethods()) {
+ result = EnsureInitialized(self, iface, can_init_statics, can_init_parents);
+ }
+
+ // Mark that this interface has undergone recursive default interface initialization so we know we
+ // can skip it on any later class initializations. We do this even if we are not a default
+ // interface since we can still avoid the traversal. This is purely a performance optimization.
+ if (result) {
+ // TODO This should be done in a better way
+ ObjectLock<mirror::Class> lock(self, iface);
+ iface->SetRecursivelyInitialized();
+ }
+ return result;
+}
+
bool ClassLinker::WaitForInitializeClass(Handle<mirror::Class> klass,
Thread* self,
ObjectLock<mirror::Class>& lock)
@@ -4193,13 +3970,13 @@ ClassTable* ClassLinker::InsertClassTableForClassLoader(mirror::ClassLoader* cla
ClassLoaderData data;
data.weak_root = self->GetJniEnv()->vm->AddWeakGlobalRef(self, class_loader);
data.class_table = class_table;
- data.allocator = Runtime::Current()->CreateLinearAlloc();
- class_loaders_.push_back(data);
// Don't already have a class table, add it to the class loader.
CHECK(class_loader->GetClassTable() == nullptr);
- CHECK(class_loader->GetAllocator() == nullptr);
class_loader->SetClassTable(data.class_table);
- class_loader->SetAllocator(data.allocator);
+ // Should have been set when we registered the dex file.
+ data.allocator = class_loader->GetAllocator();
+ CHECK(data.allocator != nullptr);
+ class_loaders_.push_back(data);
}
return class_table;
}
@@ -4623,20 +4400,16 @@ bool ClassLinker::LinkMethods(Thread* self,
Handle<mirror::ObjectArray<mirror::Class>> interfaces,
ArtMethod** out_imt) {
self->AllowThreadSuspension();
- if (klass->IsInterface()) {
- // No vtable.
- size_t count = klass->NumVirtualMethods();
- if (!IsUint<16>(count)) {
- ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zd", count);
- return false;
- }
- for (size_t i = 0; i < count; ++i) {
- klass->GetVirtualMethodDuringLinking(i, image_pointer_size_)->SetMethodIndex(i);
- }
- } else if (!LinkVirtualMethods(self, klass)) { // Link virtual methods first.
- return false;
- }
- return LinkInterfaceMethods(self, klass, interfaces, out_imt); // Link interface method last.
+ // A map from vtable indexes to the method they need to be updated to point to. Used because we
+ // need to have default methods be in the virtuals array of each class but we don't set that up
+ // until LinkInterfaceMethods.
+ std::unordered_map<size_t, ArtMethod*> default_translations;
+ // Link virtual methods then interface methods.
+ // We set up the interface lookup table first because we need it to determine if we need to update
+ // any vtable entries with new default method implementations.
+ return SetupInterfaceLookupTable(self, klass, interfaces)
+ && LinkVirtualMethods(self, klass, /*out*/ &default_translations)
+ && LinkInterfaceMethods(self, klass, default_translations, out_imt);
}
// Comparator for name and signature of a method, used in finding overriding methods. Implementation
@@ -4760,9 +4533,36 @@ class LinkVirtualHashTable {
const uint32_t LinkVirtualHashTable::invalid_index_ = std::numeric_limits<uint32_t>::max();
const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32_t>::max() - 1;
-bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass) {
+bool ClassLinker::LinkVirtualMethods(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ /*out*/std::unordered_map<size_t, ArtMethod*>* default_translations) {
const size_t num_virtual_methods = klass->NumVirtualMethods();
- if (klass->HasSuperClass()) {
+ if (klass->IsInterface()) {
+ // No vtable.
+ if (!IsUint<16>(num_virtual_methods)) {
+ ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zu", num_virtual_methods);
+ return false;
+ }
+ bool has_defaults = false;
+ // TODO May need to replace this with real VTable for invoke_super
+ // Assign each method an IMT index and set the default flag.
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
+ m->SetMethodIndex(i);
+ if (!m->IsAbstract()) {
+ m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
+ has_defaults = true;
+ }
+ }
+ // Mark that we have default methods so that we won't need to scan the virtual_methods_ array
+ // during initialization. This is a performance optimization. We could simply traverse the
+ // virtual_methods_ array again during initialization.
+ if (has_defaults) {
+ klass->SetHasDefaultMethods();
+ }
+ return true;
+ } else if (klass->HasSuperClass()) {
const size_t super_vtable_length = klass->GetSuperClass()->GetVTableLength();
const size_t max_count = num_virtual_methods + super_vtable_length;
StackHandleScope<2> hs(self);
@@ -4778,14 +4578,22 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
vtable->SetElementPtrSize(
i, super_class->GetEmbeddedVTableEntry(i, image_pointer_size_), image_pointer_size_);
}
- if (num_virtual_methods == 0) {
+ // We might need to change vtable if we have new virtual methods or new interfaces (since that
+ // might give us new default methods). If no new interfaces then we can skip the rest since
+ // the class cannot override any of the super-class's methods. This is required for
+ // correctness since without it we might not update overridden default method vtable entries
+ // correctly.
+ if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) {
klass->SetVTable(vtable.Get());
return true;
}
} else {
+ DCHECK(super_class->IsAbstract() && !super_class->IsArrayClass());
auto* super_vtable = super_class->GetVTable();
CHECK(super_vtable != nullptr) << PrettyClass(super_class.Get());
- if (num_virtual_methods == 0) {
+ // We might need to change vtable if we have new virtual methods or new interfaces (since that
+ // might give us new default methods). See comment above.
+ if (num_virtual_methods == 0 && super_class->GetIfTableCount() == klass->GetIfTableCount()) {
klass->SetVTable(super_vtable);
return true;
}
@@ -4806,7 +4614,9 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
// the need for the initial vtable which we later shrink back down).
// 3. Add non overridden methods to the end of the vtable.
static constexpr size_t kMaxStackHash = 250;
- const size_t hash_table_size = num_virtual_methods * 3;
+ // + 1 so that even if we only have new default methods we will still be able to use this hash
+ // table (i.e. it will never have 0 size).
+ const size_t hash_table_size = num_virtual_methods * 3 + 1;
uint32_t* hash_table_ptr;
std::unique_ptr<uint32_t[]> hash_heap_storage;
if (hash_table_size <= kMaxStackHash) {
@@ -4823,10 +4633,10 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
i, image_pointer_size_)->GetDeclaringClass() != nullptr);
hash_table.Add(i);
}
- // Loop through each super vtable method and see if they are overriden by a method we added to
+ // Loop through each super vtable method and see if they are overridden by a method we added to
// the hash table.
for (size_t j = 0; j < super_vtable_length; ++j) {
- // Search the hash table to see if we are overidden by any method.
+ // Search the hash table to see if we are overridden by any method.
ArtMethod* super_method = vtable->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
MethodNameAndSignatureComparator super_method_name_comparator(
super_method->GetInterfaceMethodIfProxy(image_pointer_size_));
@@ -4849,10 +4659,51 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
<< " would have incorrectly overridden the package-private method in "
<< PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
}
+ } else if (super_method->IsDefault()) {
+ // We didn't directly override this method but we might through default methods...
+ // Check for default method update.
+ ArtMethod* default_method = nullptr;
+ std::string icce_message;
+ if (!FindDefaultMethodImplementation(self,
+ super_method,
+ klass,
+ /*out*/&default_method,
+ /*out*/&icce_message)) {
+ // An error occurred while finding default methods.
+ // TODO This should actually be thrown when we attempt to invoke this method.
+ ThrowIncompatibleClassChangeError(klass.Get(), "%s", icce_message.c_str());
+ return false;
+ }
+ // This should always work because we inherit superclass interfaces. We should either get
+ // 1) An IncompatibleClassChangeError because of conflicting default method
+ // implementations.
+ // 2) The same default method implementation as the superclass.
+ // 3) A default method that overrides the superclass's.
+ // Therefore this check should never fail.
+ CHECK(default_method != nullptr);
+ if (UNLIKELY(default_method->GetDeclaringClass() != super_method->GetDeclaringClass())) {
+ // TODO Refactor this add default methods to virtuals here and not in
+ // LinkInterfaceMethods maybe.
+ // The problem is default methods might override previously present default-method or
+ // miranda-method vtable entries from the superclass. Unfortunately we need these to
+ // be entries in this class's virtuals. We do not give these entries there until
+ // LinkInterfaceMethods so we pass this map around to let it know which vtable
+ // entries need to be updated.
+ // Make a note that vtable entry j must be updated, store what it needs to be updated to.
+ // We will allocate a virtual method slot in LinkInterfaceMethods and fix it up then.
+ default_translations->insert({j, default_method});
+ VLOG(class_linker) << "Method " << PrettyMethod(super_method) << " overridden by default "
+ << PrettyMethod(default_method) << " in " << PrettyClass(klass.Get());
+ } else {
+ // They are the same method/no override
+ // Cannot do direct comparison because we had to copy the ArtMethod object into the
+ // superclass's vtable.
+ continue;
+ }
}
}
- // Add the non overridden methods at the end.
size_t actual_count = super_vtable_length;
+ // Add the non-overridden methods at the end.
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, image_pointer_size_);
size_t method_idx = local_method->GetMethodIndexDuringLinking();
@@ -4900,20 +4751,223 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle<mirror::Class> klass)
return true;
}
-bool ClassLinker::LinkInterfaceMethods(Thread* self,
- Handle<mirror::Class> klass,
- Handle<mirror::ObjectArray<mirror::Class>> interfaces,
- ArtMethod** out_imt) {
- StackHandleScope<3> hs(self);
- Runtime* const runtime = Runtime::Current();
- const bool has_superclass = klass->HasSuperClass();
- const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+// Find the default method implementation for 'interface_method' in 'klass'. Stores it into
+// out_default_method and returns true on success. If no default method was found stores nullptr
+// into out_default_method and returns true. If an error occurs (such as a default_method conflict)
+// it will fill the icce_message with an appropriate message for an IncompatibleClassChangeError,
+// which should then be thrown by the caller.
+bool ClassLinker::FindDefaultMethodImplementation(Thread* self,
+ ArtMethod* target_method,
+ Handle<mirror::Class> klass,
+ /*out*/ArtMethod** out_default_method,
+ /*out*/std::string* icce_message) const {
+ DCHECK(self != nullptr);
+ DCHECK(target_method != nullptr);
+ DCHECK(out_default_method != nullptr);
+ DCHECK(icce_message != nullptr);
+
+ *out_default_method = nullptr;
+ mirror::Class* chosen_iface = nullptr;
+
+ // We organize the interface table so that, for interface I any subinterfaces J follow it in the
+ // table. This lets us walk the table backwards when searching for default methods. The first one
+ // we encounter is the best candidate since it is the most specific. Once we have found it we keep
+ // track of it and then continue checking all other interfaces, since we need to throw an error if
+ // we encounter conflicting default method implementations (one is not a subtype of the other).
+ //
+ // The order of unrelated interfaces does not matter and is not defined.
+ size_t iftable_count = klass->GetIfTableCount();
+ if (iftable_count == 0) {
+ // No interfaces. We have already reset out to null so just return true.
+ return true;
+ }
+
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
+ MethodNameAndSignatureComparator target_name_comparator(
+ target_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ // Iterates over the klass's iftable in reverse
+ // We have a break at the end because size_t is unsigned.
+ for (size_t k = iftable_count - 1; /* break if k == 0 at end */; --k) {
+ DCHECK_LT(k, iftable->Count());
+ mirror::Class* iface = iftable->GetInterface(k);
+ size_t num_instance_methods = iface->NumVirtualMethods();
+ // Iterate through every method on this interface. The order does not matter so we go forwards.
+ for (size_t m = 0; m < num_instance_methods; m++) {
+ ArtMethod* current_method = iface->GetVirtualMethodUnchecked(m, image_pointer_size_);
+ // Skip abstract methods and methods with different names.
+ if (current_method->IsAbstract() ||
+ !target_name_comparator.HasSameNameAndSignature(
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size_))) {
+ continue;
+ }
+ // The verifier should have caught the non-public method.
+ DCHECK(current_method->IsPublic()) << "Interface method is not public!";
+ if (UNLIKELY(chosen_iface != nullptr)) {
+ // We have multiple default impls of the same method. We need to check they do not
+ // conflict and throw an error if they do. Conflicting means that the current iface is not
+ // masked by the chosen interface.
+ if (!iface->IsAssignableFrom(chosen_iface)) {
+ *icce_message = StringPrintf("Conflicting default method implementations: '%s' and '%s'",
+ PrettyMethod(current_method).c_str(),
+ PrettyMethod(*out_default_method).c_str());
+ return false;
+ } else {
+ break; // Continue checking at the next interface.
+ }
+ } else {
+ *out_default_method = current_method;
+ chosen_iface = iface;
+ // We should now finish traversing the graph to find if we have default methods that
+ // conflict.
+ break;
+ }
+ }
+ if (k == 0) {
+ break;
+ }
+ }
+ return true;
+}
+
+// Sets imt_ref appropriately for LinkInterfaceMethods.
+// If there is no method in the imt location of imt_ref it will store the given method there.
+// Otherwise it will set the conflict method which will figure out which method to use during
+// runtime.
+static void SetIMTRef(ArtMethod* unimplemented_method,
+ ArtMethod* conflict_method,
+ size_t image_pointer_size,
+ ArtMethod* current_method,
+ /*out*/ArtMethod** imt_ref)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // Place method in imt if entry is empty, place conflict otherwise.
+ if (*imt_ref == unimplemented_method) {
+ *imt_ref = current_method;
+ } else if (*imt_ref != conflict_method) {
+ // If we are not a conflict and we have the same signature and name as the imt
+ // entry, it must be that we overwrote a superclass vtable entry.
+ MethodNameAndSignatureComparator imt_comparator(
+ (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size));
+ if (imt_comparator.HasSameNameAndSignature(
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size))) {
+ *imt_ref = current_method;
+ } else {
+ *imt_ref = conflict_method;
+ }
+ }
+}
+
+// Simple helper function that checks that no subtypes of 'val' are contained within the 'classes'
+// set.
+static bool NotSubinterfaceOfAny(const std::unordered_set<mirror::Class*>& classes,
+ mirror::Class* val)
+ REQUIRES(Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(val != nullptr);
+ for (auto c : classes) {
+ if (val->IsAssignableFrom(&*c)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Fills in and flattens the interface inheritance hierarchy.
+//
+// By the end of this function all interfaces in the transitive closure of to_process are added to
+// the iftable and every interface precedes all of its sub-interfaces in this list.
+//
+// all I, J: Interface | I <: J implies J precedes I
+//
+// (note A <: B means that A is a subtype of B)
+//
+// This returns the total number of items in the iftable. The iftable might be resized down after
+// this call.
+//
+// We order this backwards so that we do not need to reorder superclass interfaces when new
+// interfaces are added in subclass's interface tables.
+//
+// Upon entry into this function iftable is a copy of the superclass's iftable with the first
+// super_ifcount entries filled in with the transitive closure of the interfaces of the superclass.
+// The other entries are uninitialized. We will fill in the remaining entries in this function. The
+// iftable must be large enough to hold all interfaces without changing its size.
+static size_t FillIfTable(mirror::IfTable* iftable,
+ size_t super_ifcount,
+ std::vector<mirror::Class*> to_process)
+ REQUIRES(Roles::uninterruptible_)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ // This is the set of all class's already in the iftable. Used to make checking if a class has
+ // already been added quicker.
+ std::unordered_set<mirror::Class*> classes_in_iftable;
+ // The first super_ifcount elements are from the superclass. We note that they are already added.
+ for (size_t i = 0; i < super_ifcount; i++) {
+ mirror::Class* iface = iftable->GetInterface(i);
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, iface)) << "Bad ordering.";
+ classes_in_iftable.insert(iface);
+ }
+ size_t filled_ifcount = super_ifcount;
+ for (mirror::Class* interface : to_process) {
+ // Let us call the first filled_ifcount elements of iftable the current-iface-list.
+ // At this point in the loop current-iface-list has the invariant that:
+ // for every pair of interfaces I,J within it:
+ // if index_of(I) < index_of(J) then I is not a subtype of J
+
+ // If we have already seen this element then all of its super-interfaces must already be in the
+ // current-iface-list so we can skip adding it.
+ if (!ContainsElement(classes_in_iftable, interface)) {
+ // We haven't seen this interface so add all of its super-interfaces onto the
+ // current-iface-list, skipping those already on it.
+ int32_t ifcount = interface->GetIfTableCount();
+ for (int32_t j = 0; j < ifcount; j++) {
+ mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
+ if (!ContainsElement(classes_in_iftable, super_interface)) {
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, super_interface)) << "Bad ordering.";
+ classes_in_iftable.insert(super_interface);
+ iftable->SetInterface(filled_ifcount, super_interface);
+ filled_ifcount++;
+ }
+ }
+ DCHECK(NotSubinterfaceOfAny(classes_in_iftable, interface)) << "Bad ordering";
+ // Place this interface onto the current-iface-list after all of its super-interfaces.
+ classes_in_iftable.insert(interface);
+ iftable->SetInterface(filled_ifcount, interface);
+ filled_ifcount++;
+ } else if (kIsDebugBuild) {
+ // Check all super-interfaces are already in the list.
+ int32_t ifcount = interface->GetIfTableCount();
+ for (int32_t j = 0; j < ifcount; j++) {
+ mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
+ DCHECK(ContainsElement(classes_in_iftable, super_interface))
+ << "Iftable does not contain " << PrettyClass(super_interface)
+ << ", a superinterface of " << PrettyClass(interface);
+ }
+ }
+ }
+ if (kIsDebugBuild) {
+ // Check that the iftable is ordered correctly.
+ for (size_t i = 0; i < filled_ifcount; i++) {
+ mirror::Class* if_a = iftable->GetInterface(i);
+ for (size_t j = i + 1; j < filled_ifcount; j++) {
+ mirror::Class* if_b = iftable->GetInterface(j);
+ // !(if_a <: if_b)
+ CHECK(!if_b->IsAssignableFrom(if_a))
+ << "Bad interface order: " << PrettyClass(if_a) << " (index " << i << ") extends "
+ << PrettyClass(if_b) << " (index " << j << ") and so should be after it in the "
+ << "interface list.";
+ }
+ }
+ }
+ return filled_ifcount;
+}
+
+bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class> klass,
+ Handle<mirror::ObjectArray<mirror::Class>> interfaces) {
+ StackHandleScope<1> hs(self);
+ const size_t super_ifcount =
+ klass->HasSuperClass() ? klass->GetSuperClass()->GetIfTableCount() : 0U;
const bool have_interfaces = interfaces.Get() != nullptr;
- const size_t num_interfaces = have_interfaces
- ? interfaces->GetLength()
- : klass->NumDirectInterfaces();
- const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
- const size_t method_size = ArtMethod::Size(image_pointer_size_);
+ const size_t num_interfaces =
+ have_interfaces ? interfaces->GetLength() : klass->NumDirectInterfaces();
if (num_interfaces == 0) {
if (super_ifcount == 0) {
// Class implements no interfaces.
@@ -4937,6 +4991,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
}
}
size_t ifcount = super_ifcount + num_interfaces;
+ // Check that every class being implemented is an interface.
for (size_t i = 0; i < num_interfaces; i++) {
mirror::Class* interface = have_interfaces
? interfaces->GetWithoutChecks(i)
@@ -4952,11 +5007,13 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
}
ifcount += interface->GetIfTableCount();
}
+ // Create the interface function table.
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(AllocIfTable(self, ifcount)));
if (UNLIKELY(iftable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
+ // Fill in table with superclass's iftable.
if (super_ifcount != 0) {
mirror::IfTable* super_iftable = klass->GetSuperClass()->GetIfTable();
for (size_t i = 0; i < super_ifcount; i++) {
@@ -4964,56 +5021,59 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
iftable->SetInterface(i, super_interface);
}
}
+
+ // Note that AllowThreadSuspension is to thread suspension as pthread_testcancel is to pthread
+ // cancellation. That is it will suspend if one has a pending suspend request but otherwise
+ // doesn't really do anything.
self->AllowThreadSuspension();
- // Flatten the interface inheritance hierarchy.
- size_t idx = super_ifcount;
- for (size_t i = 0; i < num_interfaces; i++) {
- mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
- mirror::Class::GetDirectInterface(self, klass, i);
- // Check if interface is already in iftable
- bool duplicate = false;
- for (size_t j = 0; j < idx; j++) {
- mirror::Class* existing_interface = iftable->GetInterface(j);
- if (existing_interface == interface) {
- duplicate = true;
- break;
- }
- }
- if (!duplicate) {
- // Add this non-duplicate interface.
- iftable->SetInterface(idx++, interface);
- // Add this interface's non-duplicate super-interfaces.
- for (int32_t j = 0; j < interface->GetIfTableCount(); j++) {
- mirror::Class* super_interface = interface->GetIfTable()->GetInterface(j);
- bool super_duplicate = false;
- for (size_t k = 0; k < idx; k++) {
- mirror::Class* existing_interface = iftable->GetInterface(k);
- if (existing_interface == super_interface) {
- super_duplicate = true;
- break;
- }
- }
- if (!super_duplicate) {
- iftable->SetInterface(idx++, super_interface);
- }
- }
+
+ size_t new_ifcount;
+ {
+ ScopedAssertNoThreadSuspension nts(self, "Copying mirror::Class*'s for FillIfTable");
+ std::vector<mirror::Class*> to_add;
+ for (size_t i = 0; i < num_interfaces; i++) {
+ mirror::Class* interface = have_interfaces ? interfaces->Get(i) :
+ mirror::Class::GetDirectInterface(self, klass, i);
+ to_add.push_back(interface);
}
+
+ new_ifcount = FillIfTable(iftable.Get(), super_ifcount, std::move(to_add));
}
+
self->AllowThreadSuspension();
+
// Shrink iftable in case duplicates were found
- if (idx < ifcount) {
+ if (new_ifcount < ifcount) {
DCHECK_NE(num_interfaces, 0U);
iftable.Assign(down_cast<mirror::IfTable*>(
- iftable->CopyOf(self, idx * mirror::IfTable::kMax)));
+ iftable->CopyOf(self, new_ifcount * mirror::IfTable::kMax)));
if (UNLIKELY(iftable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
- ifcount = idx;
+ ifcount = new_ifcount;
} else {
- DCHECK_EQ(idx, ifcount);
+ DCHECK_EQ(new_ifcount, ifcount);
}
klass->SetIfTable(iftable.Get());
+ return true;
+}
+
+bool ClassLinker::LinkInterfaceMethods(
+ Thread* self,
+ Handle<mirror::Class> klass,
+ const std::unordered_map<size_t, ArtMethod*>& default_translations,
+ ArtMethod** out_imt) {
+ StackHandleScope<3> hs(self);
+ Runtime* const runtime = Runtime::Current();
+ const bool has_superclass = klass->HasSuperClass();
+ const size_t super_ifcount = has_superclass ? klass->GetSuperClass()->GetIfTableCount() : 0U;
+ const size_t method_alignment = ArtMethod::Alignment(image_pointer_size_);
+ const size_t method_size = ArtMethod::Size(image_pointer_size_);
+ const size_t ifcount = klass->GetIfTableCount();
+
+ MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
+
// If we're an interface, we don't need the vtable pointers, so we're done.
if (klass->IsInterface()) {
return true;
@@ -5026,6 +5086,7 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
ArenaStack stack(runtime->GetLinearAlloc()->GetArenaPool());
ScopedArenaAllocator allocator(&stack);
ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter());
+ ScopedArenaVector<ArtMethod*> default_methods(allocator.Adapter());
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
@@ -5055,7 +5116,9 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
for (size_t j = 0; j < num_virtuals; ++j) {
auto method = method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
DCHECK(method != nullptr) << PrettyClass(super_class);
- if (method->IsMiranda()) {
+ // Miranda methods cannot be used to implement an interface method and defaults should be
+ // skipped in case we override it.
+ if (method->IsDefault() || method->IsMiranda()) {
continue;
}
ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size_);
@@ -5076,6 +5139,8 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
const bool is_super = i < super_ifcount;
+ // This is an interface implemented by a super-class. Therefore we can just copy the method
+ // array from the superclass.
const bool super_interface = is_super && extend_super_iftable;
mirror::PointerArray* method_array;
if (super_interface) {
@@ -5119,16 +5184,13 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
input_vtable_array = vtable;
input_array_length = input_vtable_array->GetLength();
}
- if (input_array_length == 0) {
- // If the added virtual methods is empty, do nothing.
- DCHECK(super_interface);
- continue;
- }
+ // For each method in interface
for (size_t j = 0; j < num_methods; ++j) {
auto* interface_method = iftable->GetInterface(i)->GetVirtualMethod(j, image_pointer_size_);
MethodNameAndSignatureComparator interface_name_comparator(
interface_method->GetInterfaceMethodIfProxy(image_pointer_size_));
- int32_t k;
+ uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
+ ArtMethod** imt_ptr = &out_imt[imt_index];
// For each method listed in the interface's method list, find the
// matching method in our class's method list. We want to favor the
// subclass over the superclass, which just requires walking
@@ -5137,7 +5199,12 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
// it -- otherwise it would use the same vtable slot. In .dex files
// those don't end up in the virtual method table, so it shouldn't
// matter which direction we go. We walk it backward anyway.)
- for (k = input_array_length - 1; k >= 0; --k) {
+ //
+ // To find defaults we need to do the same but also go over interfaces.
+ bool found_impl = false;
+ ArtMethod* default_impl = nullptr;
+ bool found_default_impl = false;
+ for (int32_t k = input_array_length - 1; k >= 0; --k) {
ArtMethod* vtable_method = input_virtual_methods != nullptr ?
&input_virtual_methods->At(k, method_size, method_alignment) :
input_vtable_array->GetElementPtrSize<ArtMethod*>(k, image_pointer_size_);
@@ -5153,25 +5220,69 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
"Method '%s' implementing interface method '%s' is not public",
PrettyMethod(vtable_method).c_str(), PrettyMethod(interface_method).c_str());
return false;
+ } else if (vtable_method->IsDefault()) {
+ // We might have a newer, better, default method for this, so we just skip it. If we
+ // are still using this we will select it again when scanning for default methods. To
+ // obviate the need to copy the method again we will make a note that we already found
+ // a default here.
+ // TODO This should be much cleaner.
+ found_default_impl = true;
+ default_impl = vtable_method;
+ break;
+ } else {
+ found_impl = true;
}
method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
// Place method in imt if entry is empty, place conflict otherwise.
- uint32_t imt_index = interface_method->GetDexMethodIndex() % mirror::Class::kImtSize;
- auto** imt_ref = &out_imt[imt_index];
- if (*imt_ref == unimplemented_method) {
- *imt_ref = vtable_method;
- } else if (*imt_ref != conflict_method) {
- // If we are not a conflict and we have the same signature and name as the imt entry,
- // it must be that we overwrote a superclass vtable entry.
- MethodNameAndSignatureComparator imt_comparator(
- (*imt_ref)->GetInterfaceMethodIfProxy(image_pointer_size_));
- *imt_ref = imt_comparator.HasSameNameAndSignature(vtable_method_for_name_comparison) ?
- vtable_method : conflict_method;
- }
+ SetIMTRef(unimplemented_method,
+ conflict_method,
+ image_pointer_size_,
+ vtable_method,
+ /*out*/imt_ptr);
break;
}
}
- if (k < 0 && !super_interface) {
+ // We should only search for default implementations when the class does not implement the
+ // method directly and either (1) the interface is newly implemented on this class and not
+ // on any of its superclasses, (2) the superclass's implementation is a default method, or
+ // (3) the superclass does not have an implementation.
+ if (!found_impl && (!super_interface ||
+ method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_)
+ ->IsOverridableByDefaultMethod())) {
+ ArtMethod* current_method = nullptr;
+ std::string icce_message;
+ if (!FindDefaultMethodImplementation(self,
+ interface_method,
+ klass,
+ /*out*/&current_method,
+ /*out*/&icce_message)) {
+ // There was a conflict with default method implementations.
+ self->EndAssertNoThreadSuspension(old_cause);
+ // TODO This should actually be thrown when we attempt to invoke this method.
+ ThrowIncompatibleClassChangeError(klass.Get(), "%s", icce_message.c_str());
+ return false;
+ } else if (current_method != nullptr) {
+ if (found_default_impl &&
+ current_method->GetDeclaringClass() == default_impl->GetDeclaringClass()) {
+ // We found a default method but it was the same one we already have from our
+ // superclass. Don't bother adding it to our vtable again.
+ current_method = default_impl;
+ } else {
+ // We found a default method implementation and there were no conflicts.
+ // Save the default method. We need to add it to the vtable.
+ default_methods.push_back(current_method);
+ }
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
+ found_impl = true;
+ }
+ }
+ if (!found_impl && !super_interface) {
+ // It is defined in this class or any of its subclasses.
ArtMethod* miranda_method = nullptr;
for (auto& mir_method : miranda_methods) {
if (interface_name_comparator.HasSameNameAndSignature(mir_method)) {
@@ -5191,9 +5302,10 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
}
}
}
- if (!miranda_methods.empty()) {
+ if (!miranda_methods.empty() || !default_methods.empty()) {
const size_t old_method_count = klass->NumVirtualMethods();
- const size_t new_method_count = old_method_count + miranda_methods.size();
+ const size_t new_method_count =
+ old_method_count + miranda_methods.size() + default_methods.size();
// Attempt to realloc to save RAM if possible.
LengthPrefixedArray<ArtMethod>* old_virtuals = klass->GetVirtualMethodsPtr();
// The Realloced virtual methods aren't visiblef from the class roots, so there is no issue
@@ -5228,13 +5340,36 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
++out;
}
}
- StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment) + old_method_count);
+ StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment)
+ + old_method_count);
// Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and
// we want the roots of the miranda methods to get visited.
for (ArtMethod* mir_method : miranda_methods) {
- out->CopyFrom(mir_method, image_pointer_size_);
- out->SetAccessFlags(out->GetAccessFlags() | kAccMiranda);
- move_table.emplace(mir_method, &*out);
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(mir_method, image_pointer_size_);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda);
+ DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
+ << "Miranda method should be abstract!";
+ move_table.emplace(mir_method, &new_method);
+ ++out;
+ }
+ // We need to copy the default methods into our own virtual method table since the runtime
+ // requires that every method on a class's vtable be in that respective class's virtual method
+ // table.
+ // NOTE This means that two classes might have the same implementation of a method from the same
+ // interface but will have different ArtMethod*s for them. This also means we cannot compare a
+ // default method found on a class with one found on the declaring interface directly and must
+ // look at the declaring class to determine if they are the same.
+ for (ArtMethod* def_method : default_methods) {
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(def_method, image_pointer_size_);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccDefault);
+ // Clear the preverified flag if it is present. Since this class hasn't been verified yet it
+ // shouldn't have methods that are preverified.
+ // TODO This is rather arbitrary. We should maybe support classes where only some of its
+ // methods are preverified.
+ new_method.SetAccessFlags(new_method.GetAccessFlags() & ~kAccPreverified);
+ move_table.emplace(def_method, &new_method);
++out;
}
virtuals->SetLength(new_method_count);
@@ -5244,7 +5379,8 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
self->EndAssertNoThreadSuspension(old_cause);
const size_t old_vtable_count = vtable->GetLength();
- const size_t new_vtable_count = old_vtable_count + miranda_methods.size();
+ const size_t new_vtable_count =
+ old_vtable_count + miranda_methods.size() + default_methods.size();
miranda_methods.clear();
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
if (UNLIKELY(vtable.Get() == nullptr)) {
@@ -5261,15 +5397,29 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
++vtable_pos;
}
CHECK_EQ(vtable_pos, new_vtable_count);
- // Update old vtable methods.
+ // Update old vtable methods. We use the default_translations map to figure out what each vtable
+ // entry should be updated to, if they need to be at all.
for (size_t i = 0; i < old_vtable_count; ++i) {
- auto* m = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
- DCHECK(m != nullptr) << PrettyClass(klass.Get());
- auto it = move_table.find(m);
+ ArtMethod* translated_method = vtable->GetElementPtrSize<ArtMethod*>(i, image_pointer_size_);
+ // Try and find what we need to change this method to.
+ auto translation_it = default_translations.find(i);
+ bool found_translation = false;
+ if (translation_it != default_translations.end()) {
+ size_t vtable_index;
+ std::tie(vtable_index, translated_method) = *translation_it;
+ DCHECK_EQ(vtable_index, i);
+ found_translation = true;
+ }
+ DCHECK(translated_method != nullptr);
+ auto it = move_table.find(translated_method);
if (it != move_table.end()) {
- auto* new_m = it->second;
- DCHECK(new_m != nullptr) << PrettyClass(klass.Get());
- vtable->SetElementPtrSize(i, new_m, image_pointer_size_);
+ auto* new_method = it->second;
+ DCHECK(new_method != nullptr);
+ vtable->SetElementPtrSize(i, new_method, image_pointer_size_);
+ } else {
+ // If it was not going to be updated we wouldn't have put it into the default_translations
+ // map.
+ CHECK(!found_translation) << "We were asked to update this vtable entry. Must not fail.";
}
}
@@ -5300,7 +5450,11 @@ bool ClassLinker::LinkInterfaceMethods(Thread* self,
auto* resolved_methods = klass->GetDexCache()->GetResolvedMethods();
for (size_t i = 0, count = klass->GetDexCache()->NumResolvedMethods(); i < count; ++i) {
auto* m = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size_);
- CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
+ // We don't remove default methods from the move table since we need them to update the
+ // vtable. Therefore just skip them for this check.
+ if (!m->IsDefault()) {
+ CHECK(move_table.find(m) == move_table.end()) << PrettyMethod(m);
+ }
}
}
// Put some random garbage in old virtuals to help find stale pointers.
@@ -6075,7 +6229,8 @@ const char* ClassLinker::GetClassRootDescriptor(ClassRoot class_root) {
}
bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
- if (Runtime::Current()->UseJit()) {
+ Runtime* const runtime = Runtime::Current();
+ if (runtime->UseJit()) {
// JIT can have direct code pointers from any method to any other method.
return true;
}
@@ -6097,13 +6252,7 @@ bool ClassLinker::MayBeCalledWithDirectCodePointer(ArtMethod* m) {
} else {
// The method can be called outside its own oat file. Therefore it won't be called using its
// direct code pointer only if all loaded oat files have been compiled in PIC mode.
- ReaderMutexLock mu(Thread::Current(), dex_lock_);
- for (const OatFile* oat_file : oat_files_) {
- if (!oat_file->IsPic()) {
- return true;
- }
- }
- return false;
+ return runtime->GetOatFileManager().HaveNonPicOatFile();
}
}
@@ -6138,9 +6287,13 @@ jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFi
for (const DexFile* dex_file : dex_files) {
StackHandleScope<3> hs2(self);
- Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(self, 1));
+ // CreatePathClassLoader is only used by gtests. Index 0 of h_long_array is supposed to be the
+ // oat file but we can leave it null.
+ Handle<mirror::LongArray> h_long_array = hs2.NewHandle(mirror::LongArray::Alloc(
+ self,
+ kDexFileIndexStart + 1));
DCHECK(h_long_array.Get() != nullptr);
- h_long_array->Set(0, reinterpret_cast<intptr_t>(dex_file));
+ h_long_array->Set(kDexFileIndexStart, reinterpret_cast<intptr_t>(dex_file));
Handle<mirror::Object> h_dex_file = hs2.NewHandle(
cookie_field->GetDeclaringClass()->AllocObject(self));