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.cc752
1 files changed, 524 insertions, 228 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 5dac95d3b3..f649972096 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -303,7 +303,7 @@ static void ShuffleForward(size_t* current_field_idx,
ClassLinker::ClassLinker(InternTable* intern_table)
// dex_lock_ is recursive as it may be used in stack dumping.
: dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
- dex_cache_image_class_lookup_required_(false),
+ dex_cache_boot_image_class_lookup_required_(false),
failed_dex_cache_class_lookups_(0),
class_roots_(nullptr),
array_iftable_(nullptr),
@@ -794,7 +794,7 @@ static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_
CHECK_EQ(field.GetDeclaringClass(), klass);
}
auto* runtime = Runtime::Current();
- auto* image_space = runtime->GetHeap()->GetImageSpace();
+ auto* image_space = runtime->GetHeap()->GetBootImageSpace();
auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetDirectMethods(pointer_size)) {
SanityCheckArtMethod(&m, klass, image_space);
@@ -855,10 +855,10 @@ void ClassLinker::InitFromImage() {
Runtime* const runtime = Runtime::Current();
Thread* const self = Thread::Current();
gc::Heap* const heap = runtime->GetHeap();
- gc::space::ImageSpace* const space = heap->GetImageSpace();
+ gc::space::ImageSpace* const space = heap->GetBootImageSpace();
CHECK(space != nullptr);
image_pointer_size_ = space->GetImageHeader().GetPointerSize();
- dex_cache_image_class_lookup_required_ = true;
+ dex_cache_boot_image_class_lookup_required_ = true;
const OatFile* oat_file = runtime->GetOatFileManager().RegisterImageOatFile(space);
DCHECK(oat_file != nullptr);
CHECK_EQ(oat_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
@@ -1086,8 +1086,8 @@ void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
}
void ClassLinker::VisitClasses(ClassVisitor* visitor) {
- if (dex_cache_image_class_lookup_required_) {
- MoveImageClassesToClassTable();
+ if (dex_cache_boot_image_class_lookup_required_) {
+ AddBootImageClassesToClassTable();
}
Thread* const self = Thread::Current();
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -1858,7 +1858,7 @@ const OatFile::OatMethod ClassLinker::FindOatMethodFor(ArtMethod* method, bool*
// Special case to get oat code without overwriting a trampoline.
const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
- CHECK(!method->IsAbstract()) << PrettyMethod(method);
+ CHECK(method->IsInvokable()) << PrettyMethod(method);
if (method->IsProxyMethod()) {
return GetQuickProxyInvokeHandler();
}
@@ -1878,7 +1878,7 @@ const void* ClassLinker::GetQuickOatCodeFor(ArtMethod* method) {
}
const void* ClassLinker::GetOatMethodQuickCodeFor(ArtMethod* method) {
- if (method->IsNative() || method->IsAbstract() || method->IsProxyMethod()) {
+ if (method->IsNative() || !method->IsInvokable() || method->IsProxyMethod()) {
return nullptr;
}
bool found;
@@ -1973,6 +1973,13 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) {
// Ignore virtual methods on the iterator.
}
+void ClassLinker::EnsureThrowsInvocationError(ArtMethod* method) {
+ DCHECK(method != nullptr);
+ DCHECK(!method->IsInvokable());
+ method->SetEntryPointFromQuickCompiledCodePtrSize(quick_to_interpreter_bridge_trampoline_,
+ image_pointer_size_);
+}
+
void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class,
uint32_t class_def_method_index) {
Runtime* const runtime = Runtime::Current();
@@ -1992,8 +1999,8 @@ void ClassLinker::LinkCode(ArtMethod* method, const OatFile::OatClass* oat_class
// Install entry point from interpreter.
bool enter_interpreter = NeedsInterpreter(method, method->GetEntryPointFromQuickCompiledCode());
- if (method->IsAbstract()) {
- method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
+ if (!method->IsInvokable()) {
+ EnsureThrowsInvocationError(method);
return;
}
@@ -2636,11 +2643,13 @@ mirror::Class* ClassLinker::InsertClass(const char* descriptor, mirror::Class* k
if (existing != nullptr) {
return existing;
}
- if (kIsDebugBuild && !klass->IsTemp() && class_loader == nullptr &&
- dex_cache_image_class_lookup_required_) {
+ if (kIsDebugBuild &&
+ !klass->IsTemp() &&
+ class_loader == nullptr &&
+ dex_cache_boot_image_class_lookup_required_) {
// Check a class loaded with the system class loader matches one in the image if the class
// is in the image.
- existing = LookupClassFromImage(descriptor);
+ existing = LookupClassFromBootImage(descriptor);
if (existing != nullptr) {
CHECK_EQ(klass, existing);
}
@@ -2684,11 +2693,11 @@ mirror::Class* ClassLinker::LookupClass(Thread* self,
}
}
}
- if (class_loader != nullptr || !dex_cache_image_class_lookup_required_) {
+ if (class_loader != nullptr || !dex_cache_boot_image_class_lookup_required_) {
return nullptr;
}
// Lookup failed but need to search dex_caches_.
- mirror::Class* result = LookupClassFromImage(descriptor);
+ mirror::Class* result = LookupClassFromBootImage(descriptor);
if (result != nullptr) {
result = InsertClass(descriptor, result, hash);
} else {
@@ -2697,37 +2706,43 @@ mirror::Class* ClassLinker::LookupClass(Thread* self,
// classes into the class table.
constexpr uint32_t kMaxFailedDexCacheLookups = 1000;
if (++failed_dex_cache_class_lookups_ > kMaxFailedDexCacheLookups) {
- MoveImageClassesToClassTable();
+ AddBootImageClassesToClassTable();
}
}
return result;
}
-static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches()
+static mirror::ObjectArray<mirror::DexCache>* GetImageDexCaches(gc::space::ImageSpace* image_space)
SHARED_REQUIRES(Locks::mutator_lock_) {
- gc::space::ImageSpace* image = Runtime::Current()->GetHeap()->GetImageSpace();
- CHECK(image != nullptr);
- mirror::Object* root = image->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ CHECK(image_space != nullptr);
+ mirror::Object* root = image_space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
+ DCHECK(root != nullptr);
return root->AsObjectArray<mirror::DexCache>();
}
-void ClassLinker::MoveImageClassesToClassTable() {
+void ClassLinker::AddBootImageClassesToClassTable() {
+ if (dex_cache_boot_image_class_lookup_required_) {
+ AddImageClassesToClassTable(Runtime::Current()->GetHeap()->GetBootImageSpace(),
+ /*class_loader*/nullptr);
+ dex_cache_boot_image_class_lookup_required_ = false;
+ }
+}
+
+void ClassLinker::AddImageClassesToClassTable(gc::space::ImageSpace* image_space,
+ mirror::ClassLoader* class_loader) {
Thread* self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- if (!dex_cache_image_class_lookup_required_) {
- return; // All dex cache classes are already in the class table.
- }
ScopedAssertNoThreadSuspension ants(self, "Moving image classes to class table");
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(image_space);
std::string temp;
- ClassTable* const class_table = InsertClassTableForClassLoader(nullptr);
+ ClassTable* const class_table = InsertClassTableForClassLoader(class_loader);
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
GcRoot<mirror::Class>* types = dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = dex_cache->NumResolvedTypes(); j < num_types; j++) {
mirror::Class* klass = types[j].Read();
if (klass != nullptr) {
- DCHECK(klass->GetClassLoader() == nullptr);
+ DCHECK_EQ(klass->GetClassLoader(), class_loader);
const char* descriptor = klass->GetDescriptor(&temp);
size_t hash = ComputeModifiedUtf8Hash(descriptor);
mirror::Class* existing = class_table->Lookup(descriptor, hash);
@@ -2743,7 +2758,6 @@ void ClassLinker::MoveImageClassesToClassTable() {
}
}
}
- dex_cache_image_class_lookup_required_ = false;
}
class MoveClassTableToPreZygoteVisitor : public ClassLoaderVisitor {
@@ -2767,9 +2781,10 @@ void ClassLinker::MoveClassTableToPreZygote() {
VisitClassLoaders(&visitor);
}
-mirror::Class* ClassLinker::LookupClassFromImage(const char* descriptor) {
+mirror::Class* ClassLinker::LookupClassFromBootImage(const char* descriptor) {
ScopedAssertNoThreadSuspension ants(Thread::Current(), "Image class lookup");
- mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches();
+ mirror::ObjectArray<mirror::DexCache>* dex_caches = GetImageDexCaches(
+ Runtime::Current()->GetHeap()->GetBootImageSpace());
for (int32_t i = 0; i < dex_caches->GetLength(); ++i) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
const DexFile* dex_file = dex_cache->GetDexFile();
@@ -2811,8 +2826,8 @@ class LookupClassesVisitor : public ClassLoaderVisitor {
void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Class*>& result) {
result.clear();
- if (dex_cache_image_class_lookup_required_) {
- MoveImageClassesToClassTable();
+ if (dex_cache_boot_image_class_lookup_required_) {
+ AddBootImageClassesToClassTable();
}
Thread* const self = Thread::Current();
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
@@ -2825,6 +2840,48 @@ void ClassLinker::LookupClasses(const char* descriptor, std::vector<mirror::Clas
VisitClassLoaders(&visitor);
}
+bool ClassLinker::AttemptSupertypeVerification(Thread* self,
+ Handle<mirror::Class> klass,
+ Handle<mirror::Class> supertype) {
+ DCHECK(self != nullptr);
+ DCHECK(klass.Get() != nullptr);
+ DCHECK(supertype.Get() != nullptr);
+
+ StackHandleScope<1> hs(self);
+ // Acquire lock to prevent races on verifying the super class.
+ ObjectLock<mirror::Class> super_lock(self, supertype);
+
+ if (!supertype->IsVerified() && !supertype->IsErroneous()) {
+ VerifyClass(self, supertype);
+ }
+ if (supertype->IsCompileTimeVerified()) {
+ // Either we are verified or we soft failed and need to retry at runtime.
+ return true;
+ }
+ // If we got this far then we have a hard failure.
+ std::string error_msg =
+ StringPrintf("Rejecting class %s that attempts to sub-type erroneous class %s",
+ PrettyDescriptor(klass.Get()).c_str(),
+ PrettyDescriptor(supertype.Get()).c_str());
+ LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
+ Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException()));
+ if (cause.Get() != nullptr) {
+ // Set during VerifyClass call (if at all).
+ self->ClearException();
+ }
+ // Change into a verify error.
+ ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
+ if (cause.Get() != nullptr) {
+ self->GetException()->SetCause(cause.Get());
+ }
+ ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
+ if (Runtime::Current()->IsAotCompiler()) {
+ Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+ }
+ mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
+ return false;
+}
+
void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
// TODO: assert that the monitor on the Class is held
ObjectLock<mirror::Class> lock(self, klass);
@@ -2875,56 +2932,70 @@ void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
// Verify super class.
StackHandleScope<2> hs(self);
- Handle<mirror::Class> super(hs.NewHandle(klass->GetSuperClass()));
- if (super.Get() != nullptr) {
- // Acquire lock to prevent races on verifying the super class.
- ObjectLock<mirror::Class> super_lock(self, super);
-
- if (!super->IsVerified() && !super->IsErroneous()) {
- VerifyClass(self, super);
- }
- if (!super->IsCompileTimeVerified()) {
- std::string error_msg(
- StringPrintf("Rejecting class %s that attempts to sub-class erroneous class %s",
- PrettyDescriptor(klass.Get()).c_str(),
- PrettyDescriptor(super.Get()).c_str()));
- LOG(WARNING) << error_msg << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
- Handle<mirror::Throwable> cause(hs.NewHandle(self->GetException()));
- if (cause.Get() != nullptr) {
- self->ClearException();
- }
- ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
- if (cause.Get() != nullptr) {
- self->GetException()->SetCause(cause.Get());
- }
- ClassReference ref(klass->GetDexCache()->GetDexFile(), klass->GetDexClassDefIndex());
- if (Runtime::Current()->IsAotCompiler()) {
- Runtime::Current()->GetCompilerCallbacks()->ClassRejected(ref);
+ MutableHandle<mirror::Class> supertype(hs.NewHandle(klass->GetSuperClass()));
+ // If we have a superclass and we get a hard verification failure we can return immediately.
+ if (supertype.Get() != nullptr && !AttemptSupertypeVerification(self, klass, supertype)) {
+ CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
+ return;
+ }
+
+ // Verify all default super-interfaces.
+ //
+ // (1) Don't bother if the superclass has already had a soft verification failure.
+ //
+ // (2) Interfaces shouldn't bother to do this recursive verification because they cannot cause
+ // recursive initialization by themselves. This is because when an interface is initialized
+ // directly it must not initialize its superinterfaces. We are allowed to verify regardless
+ // but choose not to for an optimization. If the interfaces is being verified due to a class
+ // initialization (which would need all the default interfaces to be verified) the class code
+ // will trigger the recursive verification anyway.
+ if ((supertype.Get() == nullptr || supertype->IsVerified()) // See (1)
+ && !klass->IsInterface()) { // See (2)
+ int32_t iftable_count = klass->GetIfTableCount();
+ MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr));
+ // Loop through all interfaces this class has defined. It doesn't matter the order.
+ for (int32_t i = 0; i < iftable_count; i++) {
+ iface.Assign(klass->GetIfTable()->GetInterface(i));
+ DCHECK(iface.Get() != nullptr);
+ // We only care if we have default interfaces and can skip if we are already verified...
+ if (LIKELY(!iface->HasDefaultMethods() || iface->IsVerified())) {
+ continue;
+ } else if (UNLIKELY(!AttemptSupertypeVerification(self, klass, iface))) {
+ // We had a hard failure while verifying this interface. Just return immediately.
+ CHECK(self->IsExceptionPending()) << "Verification error should be pending.";
+ return;
+ } else if (UNLIKELY(!iface->IsVerified())) {
+ // We softly failed to verify the iface. Stop checking and clean up.
+ // Put the iface into the supertype handle so we know what caused us to fail.
+ supertype.Assign(iface.Get());
+ break;
}
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
- return;
}
}
+ // At this point if verification failed, then supertype is the "first" supertype that failed
+ // verification (without a specific order). If verification succeeded, then supertype is either
+ // null or the original superclass of klass and is verified.
+ DCHECK(supertype.Get() == nullptr ||
+ supertype.Get() == klass->GetSuperClass() ||
+ !supertype->IsVerified());
+
// Try to use verification information from the oat file, otherwise do runtime verification.
const DexFile& dex_file = *klass->GetDexCache()->GetDexFile();
mirror::Class::Status oat_file_class_status(mirror::Class::kStatusNotReady);
bool preverified = VerifyClassUsingOatFile(dex_file, klass.Get(), oat_file_class_status);
- if (oat_file_class_status == mirror::Class::kStatusError) {
- VLOG(class_linker) << "Skipping runtime verification of erroneous class "
- << PrettyDescriptor(klass.Get()) << " in "
- << klass->GetDexCache()->GetLocation()->ToModifiedUtf8();
- ThrowVerifyError(klass.Get(), "Rejecting class %s because it failed compile-time verification",
- PrettyDescriptor(klass.Get()).c_str());
- mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
- return;
- }
+ // If the oat file says the class had an error, re-run the verifier. That way we will get a
+ // precise error message. To ensure a rerun, test:
+ // oat_file_class_status == mirror::Class::kStatusError => !preverified
+ DCHECK(!(oat_file_class_status == mirror::Class::kStatusError) || !preverified);
+
verifier::MethodVerifier::FailureKind verifier_failure = verifier::MethodVerifier::kNoFailure;
std::string error_msg;
if (!preverified) {
verifier_failure = verifier::MethodVerifier::VerifyClass(self,
klass.Get(),
Runtime::Current()->IsAotCompiler(),
+ Runtime::Current()->IsAotCompiler(),
&error_msg);
}
if (preverified || verifier_failure != verifier::MethodVerifier::kHardFailure) {
@@ -2937,14 +3008,14 @@ void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
// Make sure all classes referenced by catch blocks are resolved.
ResolveClassExceptionHandlerTypes(dex_file, klass);
if (verifier_failure == verifier::MethodVerifier::kNoFailure) {
- // Even though there were no verifier failures we need to respect whether the super-class
- // was verified or requiring runtime reverification.
- if (super.Get() == nullptr || super->IsVerified()) {
+ // Even though there were no verifier failures we need to respect whether the super-class and
+ // super-default-interfaces were verified or requiring runtime reverification.
+ if (supertype.Get() == nullptr || supertype->IsVerified()) {
mirror::Class::SetStatus(klass, mirror::Class::kStatusVerified, self);
} else {
- CHECK_EQ(super->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
+ CHECK_EQ(supertype->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime);
mirror::Class::SetStatus(klass, mirror::Class::kStatusRetryVerificationAtRuntime, self);
- // Pretend a soft failure occured so that we don't consider the class verified below.
+ // Pretend a soft failure occurred so that we don't consider the class verified below.
verifier_failure = verifier::MethodVerifier::kSoftFailure;
}
} else {
@@ -2962,9 +3033,9 @@ void ClassLinker::VerifyClass(Thread* self, Handle<mirror::Class> klass) {
}
}
} else {
- LOG(WARNING) << "Verification failed on class " << PrettyDescriptor(klass.Get())
- << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
- << " because: " << error_msg;
+ VLOG(verifier) << "Verification failed on class " << PrettyDescriptor(klass.Get())
+ << " in " << klass->GetDexCache()->GetLocation()->ToModifiedUtf8()
+ << " because: " << error_msg;
self->AssertNoPendingException();
ThrowVerifyError(klass.Get(), "%s", error_msg.c_str());
mirror::Class::SetStatus(klass, mirror::Class::kStatusError, self);
@@ -3348,7 +3419,7 @@ void ClassLinker::CheckProxyMethod(ArtMethod* method, ArtMethod* prototype) cons
// Basic sanity
CHECK(!prototype->IsFinal());
CHECK(method->IsFinal());
- CHECK(!method->IsAbstract());
+ CHECK(method->IsInvokable());
// The proxy method doesn't have its own dex cache or dex file and so it steals those of its
// interface prototype. The exception to this are Constructors and the Class of the Proxy itself.
@@ -4079,10 +4150,10 @@ bool ClassLinker::LinkClass(Thread* self,
Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader);
}
CHECK_EQ(existing, klass.Get());
- if (kIsDebugBuild && class_loader == nullptr && dex_cache_image_class_lookup_required_) {
+ if (kIsDebugBuild && class_loader == nullptr && dex_cache_boot_image_class_lookup_required_) {
// Check a class loaded with the system class loader matches one in the image if the class
// is in the image.
- mirror::Class* const image_class = LookupClassFromImage(descriptor);
+ mirror::Class* const image_class = LookupClassFromBootImage(descriptor);
if (image_class != nullptr) {
CHECK_EQ(klass.Get(), existing) << descriptor;
}
@@ -4424,7 +4495,7 @@ bool ClassLinker::LinkMethods(Thread* self,
// 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;
+ std::unordered_map<size_t, ClassLinker::MethodTranslation> 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.
@@ -4557,7 +4628,7 @@ const uint32_t LinkVirtualHashTable::removed_index_ = std::numeric_limits<uint32
bool ClassLinker::LinkVirtualMethods(
Thread* self,
Handle<mirror::Class> klass,
- /*out*/std::unordered_map<size_t, ArtMethod*>* default_translations) {
+ /*out*/std::unordered_map<size_t, ClassLinker::MethodTranslation>* default_translations) {
const size_t num_virtual_methods = klass->NumVirtualMethods();
if (klass->IsInterface()) {
// No vtable.
@@ -4680,46 +4751,55 @@ bool ClassLinker::LinkVirtualMethods(
<< " would have incorrectly overridden the package-private method in "
<< PrettyDescriptor(super_method->GetDeclaringClassDescriptor());
}
- } else if (super_method->IsDefault()) {
+ } else if (super_method->IsOverridableByDefaultMethod()) {
// 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;
+ switch (FindDefaultMethodImplementation(self,
+ super_method,
+ klass,
+ /*out*/&default_method)) {
+ case DefaultMethodSearchResult::kDefaultConflict: {
+ // A conflict was found looking for default methods. Note this (assuming it wasn't
+ // pre-existing) in the translations map.
+ if (UNLIKELY(!super_method->IsDefaultConflicting())) {
+ // Don't generate another conflict method to reduce memory use as an optimization.
+ default_translations->insert(
+ {j, ClassLinker::MethodTranslation::CreateConflictingMethod()});
+ }
+ break;
+ }
+ case DefaultMethodSearchResult::kAbstractFound: {
+ // No conflict but method is abstract.
+ // We note that this vtable entry must be made abstract.
+ if (UNLIKELY(!super_method->IsAbstract())) {
+ default_translations->insert(
+ {j, ClassLinker::MethodTranslation::CreateAbstractMethod()});
+ }
+ break;
+ }
+ case DefaultMethodSearchResult::kDefaultFound: {
+ if (UNLIKELY(super_method->IsDefaultConflicting() ||
+ default_method->GetDeclaringClass() != super_method->GetDeclaringClass())) {
+ // Found a default method implementation that is new.
+ // 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, ClassLinker::MethodTranslation::CreateTranslatedMethod(default_method)});
+ VLOG(class_linker) << "Method " << PrettyMethod(super_method)
+ << " overridden by default " << PrettyMethod(default_method)
+ << " in " << PrettyClass(klass.Get());
+ }
+ break;
+ }
}
}
}
@@ -4772,23 +4852,75 @@ bool ClassLinker::LinkVirtualMethods(
return true;
}
+// Determine if the given iface has any subinterface in the given list that declares the method
+// specified by 'target'.
+//
+// Arguments
+// - self: The thread we are running on
+// - target: A comparator that will match any method that overrides the method we are checking for
+// - iftable: The iftable we are searching for an overriding method on.
+// - ifstart: The index of the interface we are checking to see if anything overrides
+// - iface: The interface we are checking to see if anything overrides.
+// - image_pointer_size:
+// The image pointer size.
+//
+// Returns
+// - True: There is some method that matches the target comparator defined in an interface that
+// is a subtype of iface.
+// - False: There is no method that matches the target comparator in any interface that is a subtype
+// of iface.
+static bool ContainsOverridingMethodOf(Thread* self,
+ MethodNameAndSignatureComparator& target,
+ Handle<mirror::IfTable> iftable,
+ size_t ifstart,
+ Handle<mirror::Class> iface,
+ size_t image_pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(self != nullptr);
+ DCHECK(iface.Get() != nullptr);
+ DCHECK(iftable.Get() != nullptr);
+ DCHECK_GE(ifstart, 0u);
+ DCHECK_LT(ifstart, iftable->Count());
+ DCHECK_EQ(iface.Get(), iftable->GetInterface(ifstart));
+ DCHECK(iface->IsInterface());
+
+ size_t iftable_count = iftable->Count();
+ StackHandleScope<1> hs(self);
+ MutableHandle<mirror::Class> current_iface(hs.NewHandle<mirror::Class>(nullptr));
+ for (size_t k = ifstart + 1; k < iftable_count; k++) {
+ // Skip ifstart since our current interface obviously cannot override itself.
+ current_iface.Assign(iftable->GetInterface(k));
+ size_t num_instance_methods = current_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 = current_iface->GetVirtualMethodUnchecked(m, image_pointer_size);
+ if (UNLIKELY(target.HasSameNameAndSignature(
+ current_method->GetInterfaceMethodIfProxy(image_pointer_size)))) {
+ // Check if the i'th interface is a subtype of this one.
+ if (iface->IsAssignableFrom(current_iface.Get())) {
+ return true;
+ }
+ break;
+ }
+ }
+ }
+ return false;
+}
+
// 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 {
+// out_default_method and returns kDefaultFound on success. If no default method was found return
+// kAbstractFound and store nullptr into out_default_method. If an error occurs (such as a
+// default_method conflict) it will return kDefaultConflict.
+ClassLinker::DefaultMethodSearchResult ClassLinker::FindDefaultMethodImplementation(
+ Thread* self,
+ ArtMethod* target_method,
+ Handle<mirror::Class> klass,
+ /*out*/ArtMethod** out_default_method) 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
@@ -4799,19 +4931,23 @@ bool ClassLinker::FindDefaultMethodImplementation(Thread* self,
// 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;
+ // No interfaces. We have already reset out to null so just return kAbstractFound.
+ return DefaultMethodSearchResult::kAbstractFound;
}
- StackHandleScope<1> hs(self);
+ StackHandleScope<3> hs(self);
+ MutableHandle<mirror::Class> chosen_iface(hs.NewHandle<mirror::Class>(nullptr));
MutableHandle<mirror::IfTable> iftable(hs.NewHandle(klass->GetIfTable()));
+ MutableHandle<mirror::Class> iface(hs.NewHandle<mirror::Class>(nullptr));
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) {
+ for (size_t k = iftable_count; k != 0; ) {
+ --k;
+
DCHECK_LT(k, iftable->Count());
- mirror::Class* iface = iftable->GetInterface(k);
+
+ iface.Assign(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++) {
@@ -4824,31 +4960,60 @@ bool ClassLinker::FindDefaultMethodImplementation(Thread* self,
}
// 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;
+ if (UNLIKELY(chosen_iface.Get() != nullptr)) {
+ // We have multiple default impls of the same method. This is a potential default conflict.
+ // We need to check if this possibly conflicting method is either a superclass of the chosen
+ // default implementation or is overridden by a non-default interface method. In either case
+ // there is no conflict.
+ if (!iface->IsAssignableFrom(chosen_iface.Get()) &&
+ !ContainsOverridingMethodOf(self,
+ target_name_comparator,
+ iftable,
+ k,
+ iface,
+ image_pointer_size_)) {
+ LOG(WARNING) << "Conflicting default method implementations found: "
+ << PrettyMethod(current_method) << " and "
+ << PrettyMethod(*out_default_method) << " in class "
+ << PrettyClass(klass.Get()) << " conflict.";
+ *out_default_method = nullptr;
+ return DefaultMethodSearchResult::kDefaultConflict;
} 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;
+ // chosen_iface == null
+ if (!ContainsOverridingMethodOf(self,
+ target_name_comparator,
+ iftable,
+ k,
+ iface,
+ image_pointer_size_)) {
+ // Don't set this as the chosen interface if something else is overriding it (because that
+ // other interface would be potentially chosen instead if it was default). If the other
+ // interface was abstract then we wouldn't select this interface as chosen anyway since
+ // the abstract method masks it.
+ *out_default_method = current_method;
+ chosen_iface.Assign(iface.Get());
+ // We should now finish traversing the graph to find if we have default methods that
+ // conflict.
+ } else {
+ VLOG(class_linker) << "A default method '" << PrettyMethod(current_method) << "' was "
+ << "skipped because it was overridden by an abstract method in a "
+ << "subinterface on class '" << PrettyClass(klass.Get()) << "'";
+ }
}
- }
- if (k == 0) {
break;
}
}
- return true;
+ if (*out_default_method != nullptr) {
+ VLOG(class_linker) << "Default method '" << PrettyMethod(*out_default_method) << "' selected "
+ << "as the implementation for '" << PrettyMethod(target_method) << "' "
+ << "in '" << PrettyClass(klass.Get()) << "'";
+ return DefaultMethodSearchResult::kDefaultFound;
+ } else {
+ return DefaultMethodSearchResult::kAbstractFound;
+ }
}
// Sets imt_ref appropriately for LinkInterfaceMethods.
@@ -4856,7 +5021,7 @@ bool ClassLinker::FindDefaultMethodImplementation(Thread* self,
// 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,
+ ArtMethod* imt_conflict_method,
size_t image_pointer_size,
ArtMethod* current_method,
/*out*/ArtMethod** imt_ref)
@@ -4864,7 +5029,7 @@ static void SetIMTRef(ArtMethod* unimplemented_method,
// 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) {
+ } else if (*imt_ref != imt_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(
@@ -4873,7 +5038,7 @@ static void SetIMTRef(ArtMethod* unimplemented_method,
current_method->GetInterfaceMethodIfProxy(image_pointer_size))) {
*imt_ref = current_method;
} else {
- *imt_ref = conflict_method;
+ *imt_ref = imt_conflict_method;
}
}
}
@@ -5080,10 +5245,23 @@ bool ClassLinker::SetupInterfaceLookupTable(Thread* self, Handle<mirror::Class>
return true;
}
+// Finds the method with a name/signature that matches cmp in the given list of methods. The list of
+// methods must be unique.
+static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp,
+ const ScopedArenaVector<ArtMethod*>& list)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ for (ArtMethod* method : list) {
+ if (cmp.HasSameNameAndSignature(method)) {
+ return method;
+ }
+ }
+ return nullptr;
+}
+
bool ClassLinker::LinkInterfaceMethods(
Thread* self,
Handle<mirror::Class> klass,
- const std::unordered_map<size_t, ArtMethod*>& default_translations,
+ const std::unordered_map<size_t, ClassLinker::MethodTranslation>& default_translations,
ArtMethod** out_imt) {
StackHandleScope<3> hs(self);
Runtime* const runtime = Runtime::Current();
@@ -5106,12 +5284,14 @@ bool ClassLinker::LinkInterfaceMethods(
// Use the linear alloc pool since this one is in the low 4gb for the compiler.
ArenaStack stack(runtime->GetLinearAlloc()->GetArenaPool());
ScopedArenaAllocator allocator(&stack);
+
+ ScopedArenaVector<ArtMethod*> default_conflict_methods(allocator.Adapter());
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();
- ArtMethod* const conflict_method = runtime->GetImtConflictMethod();
+ ArtMethod* const imt_conflict_method = runtime->GetImtConflictMethod();
// Copy the IMT from the super class if possible.
bool extend_super_iftable = false;
if (has_superclass) {
@@ -5147,8 +5327,8 @@ bool ClassLinker::LinkInterfaceMethods(
auto** imt_ref = &out_imt[imt_index];
if (*imt_ref == unimplemented_method) {
*imt_ref = method;
- } else if (*imt_ref != conflict_method) {
- *imt_ref = conflict_method;
+ } else if (*imt_ref != imt_conflict_method) {
+ *imt_ref = imt_conflict_method;
}
}
}
@@ -5183,7 +5363,16 @@ bool ClassLinker::LinkInterfaceMethods(
auto* old_cause = self->StartAssertNoThreadSuspension(
"Copying ArtMethods for LinkInterfaceMethods");
- for (size_t i = 0; i < ifcount; ++i) {
+ // Going in reverse to ensure that we will hit abstract methods that override defaults before the
+ // defaults. This means we don't need to do any trickery when creating the Miranda methods, since
+ // they will already be null. This has the additional benefit that the declarer of a miranda
+ // method will actually declare an abstract method.
+ for (size_t i = ifcount; i != 0; ) {
+ --i;
+
+ DCHECK_GE(i, 0u);
+ DCHECK_LT(i, ifcount);
+
size_t num_methods = iftable->GetInterface(i)->NumVirtualMethods();
if (num_methods > 0) {
StackHandleScope<2> hs2(self);
@@ -5194,6 +5383,11 @@ bool ClassLinker::LinkInterfaceMethods(
LengthPrefixedArray<ArtMethod>* input_virtual_methods = nullptr;
Handle<mirror::PointerArray> input_vtable_array = NullHandle<mirror::PointerArray>();
int32_t input_array_length = 0;
+ // TODO Cleanup Needed: In the presence of default methods this optimization is rather dirty
+ // and confusing. Default methods should always look through all the superclasses
+ // because they are the last choice of an implementation. We get around this by looking
+ // at the super-classes iftable methods (copied into method_array previously) when we are
+ // looking for the implementation of a super-interface method but that is rather dirty.
if (super_interface) {
// We are overwriting a super class interface, try to only virtual methods instead of the
// whole vtable.
@@ -5223,8 +5417,7 @@ bool ClassLinker::LinkInterfaceMethods(
//
// 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;
+ ArtMethod* vtable_impl = nullptr;
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) :
@@ -5241,77 +5434,138 @@ bool ClassLinker::LinkInterfaceMethods(
"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()) {
+ } else if (UNLIKELY(vtable_method->IsOverridableByDefaultMethod())) {
// 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;
+ vtable_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.
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ vtable_method,
+ /*out*/imt_ptr);
+ break;
}
- method_array->SetElementPtrSize(j, vtable_method, image_pointer_size_);
- // Place method in imt if entry is empty, place conflict otherwise.
- SetIMTRef(unimplemented_method,
- conflict_method,
- image_pointer_size_,
- vtable_method,
- /*out*/imt_ptr);
- break;
}
}
- // 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()) {
+ // Continue on to the next method if we are done.
+ if (LIKELY(found_impl)) {
+ continue;
+ } else if (LIKELY(super_interface)) {
+ // Don't look for a default implementation when the super-method is implemented directly
+ // by the class.
+ //
+ // See if we can use the superclasses method and skip searching everything else.
+ // Note: !found_impl && super_interface
+ CHECK(extend_super_iftable);
+ // If this is a super_interface method it is possible we shouldn't override it because a
+ // superclass could have implemented it directly. We get the method the superclass used
+ // to implement this to know if we can override it with a default method. Doing this is
+ // safe since we know that the super_iftable is filled in so we can simply pull it from
+ // there. We don't bother if this is not a super-classes interface since in that case we
+ // have scanned the entire vtable anyway and would have found it.
+ // TODO This is rather dirty but it is faster than searching through the entire vtable
+ // every time.
+ ArtMethod* supers_method =
+ method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size_);
+ DCHECK(supers_method != nullptr);
+ DCHECK(interface_name_comparator.HasSameNameAndSignature(supers_method));
+ if (!supers_method->IsOverridableByDefaultMethod()) {
+ // The method is not overridable by a default method (i.e. it is directly implemented
+ // in some class). Therefore move onto the next interface method.
+ continue;
+ }
+ }
+ // If we haven't found it yet we should search through the interfaces for default methods.
+ ArtMethod* current_method = nullptr;
+ switch (FindDefaultMethodImplementation(self,
+ interface_method,
+ klass,
+ /*out*/&current_method)) {
+ case DefaultMethodSearchResult::kDefaultConflict: {
+ // Default method conflict.
+ DCHECK(current_method == nullptr);
+ ArtMethod* default_conflict_method = nullptr;
+ if (vtable_impl != nullptr && vtable_impl->IsDefaultConflicting()) {
+ // We can reuse the method from the superclass, don't bother adding it to virtuals.
+ default_conflict_method = vtable_impl;
+ } else {
+ // See if we already have a conflict method for this method.
+ ArtMethod* preexisting_conflict = FindSameNameAndSignature(interface_name_comparator,
+ default_conflict_methods);
+ if (LIKELY(preexisting_conflict != nullptr)) {
+ // We already have another conflict we can reuse.
+ default_conflict_method = preexisting_conflict;
+ } else {
+ // Create a new conflict method for this to use.
+ default_conflict_method =
+ reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
+ new(default_conflict_method) ArtMethod(interface_method, image_pointer_size_);
+ default_conflict_methods.push_back(default_conflict_method);
+ }
+ }
+ current_method = default_conflict_method;
+ break;
+ }
+ case DefaultMethodSearchResult::kDefaultFound: {
+ DCHECK(current_method != nullptr);
+ // Found a default method.
+ if (vtable_impl != nullptr &&
+ current_method->GetDeclaringClass() == vtable_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;
+ current_method = vtable_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);
+ // Only record this default method if it is new to save space.
+ ArtMethod* old = FindSameNameAndSignature(interface_name_comparator, default_methods);
+ if (old == nullptr) {
+ // 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);
+ } else {
+ CHECK(old == current_method) << "Multiple default implementations selected!";
+ }
}
- 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;
+ break;
}
- }
- 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)) {
- miranda_method = mir_method;
- break;
+ case DefaultMethodSearchResult::kAbstractFound: {
+ DCHECK(current_method == nullptr);
+ // Abstract method masks all defaults.
+ if (vtable_impl != nullptr &&
+ vtable_impl->IsAbstract() &&
+ !vtable_impl->IsDefaultConflicting()) {
+ // We need to make this an abstract method but the version in the vtable already is so
+ // don't do anything.
+ current_method = vtable_impl;
}
+ break;
}
+ }
+ if (current_method != nullptr) {
+ // We found a default method implementation. Record it in the iftable and IMT.
+ method_array->SetElementPtrSize(j, current_method, image_pointer_size_);
+ SetIMTRef(unimplemented_method,
+ imt_conflict_method,
+ image_pointer_size_,
+ current_method,
+ /*out*/imt_ptr);
+ } else if (!super_interface) {
+ // We could not find an implementation for this method and since it is a brand new
+ // interface we searched the entire vtable (and all default methods) for an implementation
+ // but couldn't find one. We therefore need to make a miranda method.
+ //
+ // Find out if there is already a miranda method we can use.
+ ArtMethod* miranda_method = FindSameNameAndSignature(interface_name_comparator,
+ miranda_methods);
if (miranda_method == nullptr) {
+ DCHECK(interface_method->IsAbstract()) << PrettyMethod(interface_method);
miranda_method = reinterpret_cast<ArtMethod*>(allocator.Alloc(method_size));
CHECK(miranda_method != nullptr);
// Point the interface table at a phantom slot.
@@ -5323,10 +5577,15 @@ bool ClassLinker::LinkInterfaceMethods(
}
}
}
- if (!miranda_methods.empty() || !default_methods.empty()) {
+ if (!miranda_methods.empty() || !default_methods.empty() || !default_conflict_methods.empty()) {
+ VLOG(class_linker) << PrettyClass(klass.Get()) << ": miranda_methods=" << miranda_methods.size()
+ << " default_methods=" << default_methods.size()
+ << " default_conflict_methods=" << default_conflict_methods.size();
const size_t old_method_count = klass->NumVirtualMethods();
- const size_t new_method_count =
- old_method_count + miranda_methods.size() + default_methods.size();
+ const size_t new_method_count = old_method_count +
+ miranda_methods.size() +
+ default_methods.size() +
+ default_conflict_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
@@ -5384,15 +5643,32 @@ bool ClassLinker::LinkInterfaceMethods(
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);
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccPreverified);
move_table.emplace(def_method, &new_method);
++out;
}
+ for (ArtMethod* conf_method : default_conflict_methods) {
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(conf_method, image_pointer_size_);
+ // This is a type of default method (there are default method impls, just a conflict) so mark
+ // this as a default, non-abstract method, since thats what it is. Also clear the preverified
+ // bit since this class hasn't been verified yet it shouldn't have methods that are
+ // preverified.
+ constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict;
+ constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccPreverified);
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
+ DCHECK(new_method.IsDefaultConflicting());
+ // The actual method might or might not be marked abstract since we just copied it from a
+ // (possibly default) interface method. We need to set it entry point to be the bridge so that
+ // the compiler will not invoke the implementation of whatever method we copied from.
+ EnsureThrowsInvocationError(&new_method);
+ move_table.emplace(conf_method, &new_method);
+ ++out;
+ }
virtuals->SetSize(new_method_count);
UpdateClassVirtualMethods(klass.Get(), virtuals);
// Done copying methods, they are all roots in the class now, so we can end the no thread
@@ -5400,8 +5676,10 @@ bool ClassLinker::LinkInterfaceMethods(
self->EndAssertNoThreadSuspension(old_cause);
const size_t old_vtable_count = vtable->GetLength();
- const size_t new_vtable_count =
- old_vtable_count + miranda_methods.size() + default_methods.size();
+ const size_t new_vtable_count = old_vtable_count +
+ miranda_methods.size() +
+ default_methods.size() +
+ default_conflict_methods.size();
miranda_methods.clear();
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
if (UNLIKELY(vtable.Get() == nullptr)) {
@@ -5426,9 +5704,27 @@ bool ClassLinker::LinkInterfaceMethods(
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);
+ if (translation_it->second.IsInConflict()) {
+ // Find which conflict method we are to use for this method.
+ MethodNameAndSignatureComparator old_method_comparator(
+ translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ ArtMethod* new_conflict_method = FindSameNameAndSignature(old_method_comparator,
+ default_conflict_methods);
+ CHECK(new_conflict_method != nullptr) << "Expected a conflict method!";
+ translated_method = new_conflict_method;
+ } else if (translation_it->second.IsAbstract()) {
+ // Find which miranda method we are to use for this method.
+ MethodNameAndSignatureComparator old_method_comparator(
+ translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
+ ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator,
+ miranda_methods);
+ DCHECK(miranda_method != nullptr);
+ translated_method = miranda_method;
+ } else {
+ // Normal default method (changed from an older default or abstract interface method).
+ DCHECK(translation_it->second.IsTranslation());
+ translated_method = translation_it->second.GetTranslation();
+ }
found_translation = true;
}
DCHECK(translated_method != nullptr);
@@ -6135,8 +6431,8 @@ void ClassLinker::SetEntryPointsToInterpreter(ArtMethod* method) const {
void ClassLinker::DumpForSigQuit(std::ostream& os) {
ScopedObjectAccess soa(Thread::Current());
- if (dex_cache_image_class_lookup_required_) {
- MoveImageClassesToClassTable();
+ if (dex_cache_boot_image_class_lookup_required_) {
+ AddBootImageClassesToClassTable();
}
ReaderMutexLock mu(soa.Self(), *Locks::classlinker_classes_lock_);
os << "Zygote loaded classes=" << NumZygoteClasses() << " post zygote classes="
@@ -6173,8 +6469,8 @@ size_t ClassLinker::NumNonZygoteClasses() const {
}
size_t ClassLinker::NumLoadedClasses() {
- if (dex_cache_image_class_lookup_required_) {
- MoveImageClassesToClassTable();
+ if (dex_cache_boot_image_class_lookup_required_) {
+ AddBootImageClassesToClassTable();
}
ReaderMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
// Only return non zygote classes since these are the ones which apps which care about.