summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2016-09-09 21:56:37 +0000
committer android-build-merger <android-build-merger@google.com> 2016-09-09 21:56:37 +0000
commit76e172277a650f818b10ec1fc586ca40f18cf149 (patch)
treebe2d01e890d908ace99f7da284ea62121665b847
parentee5c399e87ccd3ab0441c8987319dc05215f55a9 (diff)
parentb47a1cc17f53951b900e56bb68c58c972517cb07 (diff)
Fix vtable corruption issue
am: b47a1cc17f Change-Id: I0f94ef0789b8a15525e38765e45c4a3383af643e
-rw-r--r--runtime/class_linker.cc218
-rw-r--r--test/960-default-smali/expected.txt31
-rw-r--r--test/960-default-smali/src/Foo2.java25
-rw-r--r--test/960-default-smali/src/Foo3.java22
-rw-r--r--test/960-default-smali/src/N.java21
-rw-r--r--test/960-default-smali/src/O.java21
-rw-r--r--test/960-default-smali/src/P.java25
-rw-r--r--test/960-default-smali/src/Q.java21
-rw-r--r--test/960-default-smali/src/classes.xml49
-rw-r--r--test/960-default-smali/src2/Foo.java20
-rw-r--r--test/960-default-smali/src2/Foo3.java25
-rwxr-xr-xtest/utils/python/generate_java_main.py6
12 files changed, 424 insertions, 60 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ce8abee88f..a34e029089 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -6539,39 +6539,90 @@ 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.
+// Finds the method with a name/signature that matches cmp in the given lists of methods. The list
+// of methods must be unique.
+static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp ATTRIBUTE_UNUSED) {
+ return nullptr;
+}
+
+template <typename ... Types>
static ArtMethod* FindSameNameAndSignature(MethodNameAndSignatureComparator& cmp,
- const ScopedArenaVector<ArtMethod*>& list)
+ const ScopedArenaVector<ArtMethod*>& list,
+ const Types& ... rest)
SHARED_REQUIRES(Locks::mutator_lock_) {
for (ArtMethod* method : list) {
if (cmp.HasSameNameAndSignature(method)) {
return method;
}
}
- return nullptr;
+ return FindSameNameAndSignature(cmp, rest...);
}
-static void SanityCheckVTable(Handle<mirror::Class> klass, uint32_t pointer_size)
+// Check that all vtable entries are present in this class's virtuals or are the same as a
+// superclasses vtable entry.
+static void CheckClassOwnsVTableEntries(Thread* self,
+ Handle<mirror::Class> klass,
+ size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_) {
- mirror::PointerArray* check_vtable = klass->GetVTableDuringLinking();
- mirror::Class* superclass = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
- int32_t super_vtable_length = (superclass != nullptr) ? superclass->GetVTableLength() : 0;
+ StackHandleScope<2> hs(self);
+ Handle<mirror::PointerArray> check_vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
+ mirror::Class* super_temp = (klass->HasSuperClass()) ? klass->GetSuperClass() : nullptr;
+ Handle<mirror::Class> superclass(hs.NewHandle(super_temp));
+ int32_t super_vtable_length = (superclass.Get() != nullptr) ? superclass->GetVTableLength() : 0;
for (int32_t i = 0; i < check_vtable->GetLength(); ++i) {
ArtMethod* m = check_vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
CHECK(m != nullptr);
+ CHECK_EQ(m->GetMethodIndexDuringLinking(), i)
+ << PrettyMethod(m) << " has an unexpected method index for its spot in the vtable for class"
+ << PrettyClass(klass.Get());
ArraySlice<ArtMethod> virtuals = klass->GetVirtualMethodsSliceUnchecked(pointer_size);
auto is_same_method = [m] (const ArtMethod& meth) {
return &meth == m;
};
CHECK((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) ||
std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())
- << "While linking class '" << PrettyClass(klass.Get()) << "' unable to find owning class "
- << "of '" << PrettyMethod(m) << "' (vtable index: " << i << ").";
+ << PrettyMethod(m) << " does not seem to be owned by current class "
+ << PrettyClass(klass.Get()) << " or any of its superclasses!";
+ }
+}
+
+// Check to make sure the vtable does not have duplicates. Duplicates could cause problems when a
+// method is overridden in a subclass.
+static void CheckVTableHasNoDuplicates(Thread* self,
+ Handle<mirror::Class> klass,
+ size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ StackHandleScope<1> hs(self);
+ Handle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
+ int32_t num_entries = vtable->GetLength();
+ for (int32_t i = 0; i < num_entries; i++) {
+ ArtMethod* vtable_entry = vtable->GetElementPtrSize<ArtMethod*>(i, pointer_size);
+ // Don't bother if we cannot 'see' the vtable entry (i.e. it is a package-private member maybe).
+ if (!klass->CanAccessMember(vtable_entry->GetDeclaringClass(),
+ vtable_entry->GetAccessFlags())) {
+ continue;
+ }
+ MethodNameAndSignatureComparator name_comparator(
+ vtable_entry->GetInterfaceMethodIfProxy(pointer_size));
+ for (int32_t j = i+1; j < num_entries; j++) {
+ ArtMethod* other_entry = vtable->GetElementPtrSize<ArtMethod*>(j, pointer_size);
+ CHECK(vtable_entry != other_entry &&
+ !name_comparator.HasSameNameAndSignature(
+ other_entry->GetInterfaceMethodIfProxy(pointer_size)))
+ << "vtable entries " << i << " and " << j << " are identical for "
+ << PrettyClass(klass.Get()) << " in method " << PrettyMethod(vtable_entry) << " and "
+ << PrettyMethod(other_entry);
+ }
}
}
+static void SanityCheckVTable(Thread* self, Handle<mirror::Class> klass, size_t pointer_size)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ CheckClassOwnsVTableEntries(self, klass, pointer_size);
+ CheckVTableHasNoDuplicates(self, klass, pointer_size);
+}
+
void ClassLinker::FillImtFromSuperClass(Handle<mirror::Class> klass,
ArtMethod* unimplemented_method,
ArtMethod* imt_conflict_method,
@@ -6630,8 +6681,10 @@ bool ClassLinker::LinkInterfaceMethods(
ScopedArenaAllocator allocator(&stack);
ScopedArenaVector<ArtMethod*> default_conflict_methods(allocator.Adapter());
+ ScopedArenaVector<ArtMethod*> overriding_default_conflict_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> miranda_methods(allocator.Adapter());
ScopedArenaVector<ArtMethod*> default_methods(allocator.Adapter());
+ ScopedArenaVector<ArtMethod*> overriding_default_methods(allocator.Adapter());
MutableHandle<mirror::PointerArray> vtable(hs.NewHandle(klass->GetVTableDuringLinking()));
ArtMethod* const unimplemented_method = runtime->GetImtUnimplementedMethod();
@@ -6833,8 +6886,10 @@ bool ClassLinker::LinkInterfaceMethods(
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);
+ ArtMethod* preexisting_conflict = FindSameNameAndSignature(
+ interface_name_comparator,
+ default_conflict_methods,
+ overriding_default_conflict_methods);
if (LIKELY(preexisting_conflict != nullptr)) {
// We already have another conflict we can reuse.
default_conflict_method = preexisting_conflict;
@@ -6845,7 +6900,13 @@ bool ClassLinker::LinkInterfaceMethods(
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);
+ if (vtable_impl == nullptr) {
+ // Save the conflict method. We need to add it to the vtable.
+ default_conflict_methods.push_back(default_conflict_method);
+ } else {
+ // Save the conflict method but it is already in the vtable.
+ overriding_default_conflict_methods.push_back(default_conflict_method);
+ }
}
}
current_method = default_conflict_method;
@@ -6865,11 +6926,18 @@ bool ClassLinker::LinkInterfaceMethods(
// TODO It might be worthwhile to copy default methods on interfaces anyway since it
// would make lookup for interface super much faster. (We would only need to scan
// the iftable to find if there is a NSME or AME.)
- ArtMethod* old = FindSameNameAndSignature(interface_name_comparator, default_methods);
+ ArtMethod* old = FindSameNameAndSignature(interface_name_comparator,
+ default_methods,
+ overriding_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);
+ if (vtable_impl == nullptr) {
+ // Save the default method. We need to add it to the vtable.
+ default_methods.push_back(current_method);
+ } else {
+ // Save the default method but it is already in the vtable.
+ overriding_default_methods.push_back(current_method);
+ }
} else {
CHECK(old == current_method) << "Multiple default implementations selected!";
}
@@ -6924,6 +6992,8 @@ bool ClassLinker::LinkInterfaceMethods(
} // For each interface.
const bool has_new_virtuals = !(miranda_methods.empty() &&
default_methods.empty() &&
+ overriding_default_methods.empty() &&
+ overriding_default_conflict_methods.empty() &&
default_conflict_methods.empty());
// TODO don't extend virtuals of interface unless necessary (when is it?).
if (has_new_virtuals) {
@@ -6931,11 +7001,16 @@ bool ClassLinker::LinkInterfaceMethods(
<< "Interfaces should only have default-conflict methods appended to them.";
VLOG(class_linker) << PrettyClass(klass.Get()) << ": miranda_methods=" << miranda_methods.size()
<< " default_methods=" << default_methods.size()
- << " default_conflict_methods=" << default_conflict_methods.size();
+ << " overriding_default_methods=" << overriding_default_methods.size()
+ << " default_conflict_methods=" << default_conflict_methods.size()
+ << " overriding_default_conflict_methods="
+ << overriding_default_conflict_methods.size();
const size_t old_method_count = klass->NumMethods();
const size_t new_method_count = old_method_count +
miranda_methods.size() +
default_methods.size() +
+ overriding_default_conflict_methods.size() +
+ overriding_default_methods.size() +
default_conflict_methods.size();
// Attempt to realloc to save RAM if possible.
LengthPrefixedArray<ArtMethod>* old_methods = klass->GetMethodsPtr();
@@ -6990,36 +7065,42 @@ bool ClassLinker::LinkInterfaceMethods(
// 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_);
- // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been verified
- // yet it shouldn't have methods that are skipping access checks.
- // TODO This is rather arbitrary. We should maybe support classes where only some of its
- // methods are skip_access_checks.
- constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
- constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
- new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
- move_table.emplace(def_method, &new_method);
- ++out;
+ for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
+ overriding_default_methods}) {
+ for (ArtMethod* def_method : methods_vec) {
+ ArtMethod& new_method = *out;
+ new_method.CopyFrom(def_method, image_pointer_size_);
+ // Clear the kAccSkipAccessChecks flag if it is present. Since this class hasn't been
+ // verified yet it shouldn't have methods that are skipping access checks.
+ // TODO This is rather arbitrary. We should maybe support classes where only some of its
+ // methods are skip_access_checks.
+ constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
+ constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
+ 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
- // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
- // methods that are skipping access checks.
- constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
- constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
- 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;
+ for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_conflict_methods,
+ overriding_default_conflict_methods}) {
+ for (ArtMethod* conf_method : methods_vec) {
+ 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
+ // kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
+ // methods that are skipping access checks.
+ constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
+ constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
+ 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;
+ }
}
methods->SetSize(new_method_count);
UpdateClassMethods(klass.Get(), methods);
@@ -7035,22 +7116,31 @@ bool ClassLinker::LinkInterfaceMethods(
miranda_methods.size() +
default_methods.size() +
default_conflict_methods.size();
+
vtable.Assign(down_cast<mirror::PointerArray*>(vtable->CopyOf(self, new_vtable_count)));
if (UNLIKELY(vtable.Get() == nullptr)) {
self->AssertPendingOOMException();
return false;
}
- out = methods->begin(method_size, method_alignment) + old_method_count;
size_t vtable_pos = old_vtable_count;
// Update all the newly copied method's indexes so they denote their placement in the vtable.
- for (size_t i = old_method_count; i < new_method_count; ++i) {
- // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_
- // fields are references into the dex file the method was defined in. Since the ArtMethod
- // does not store that information it uses declaring_class_->dex_cache_.
- out->SetMethodIndex(0xFFFF & vtable_pos);
- vtable->SetElementPtrSize(vtable_pos, &*out, image_pointer_size_);
- ++out;
- ++vtable_pos;
+ for (const ScopedArenaVector<ArtMethod*>& methods_vec : {default_methods,
+ default_conflict_methods,
+ miranda_methods}) {
+ // These are the functions that are not already in the vtable!
+ for (ArtMethod* new_method : methods_vec) {
+ auto translated_method_it = move_table.find(new_method);
+ CHECK(translated_method_it != move_table.end())
+ << "We must have a translation for methods added to the classes methods_ array! We "
+ << "could not find the ArtMethod added for " << PrettyMethod(new_method);
+ ArtMethod* new_vtable_method = translated_method_it->second;
+ // Leave the declaring class alone the method's dex_code_item_offset_ and dex_method_index_
+ // fields are references into the dex file the method was defined in. Since the ArtMethod
+ // does not store that information it uses declaring_class_->dex_cache_.
+ new_vtable_method->SetMethodIndex(0xFFFF & vtable_pos);
+ vtable->SetElementPtrSize(vtable_pos, new_vtable_method, image_pointer_size_);
+ ++vtable_pos;
+ }
}
CHECK_EQ(vtable_pos, new_vtable_count);
// Update old vtable methods. We use the default_translations map to figure out what each
@@ -7066,8 +7156,10 @@ bool ClassLinker::LinkInterfaceMethods(
// 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);
+ // We only need to look through overriding_default_conflict_methods since this is an
+ // overridden method we are fixing up here.
+ ArtMethod* new_conflict_method = FindSameNameAndSignature(
+ old_method_comparator, overriding_default_conflict_methods);
CHECK(new_conflict_method != nullptr) << "Expected a conflict method!";
translated_method = new_conflict_method;
} else if (translation_it->second.IsAbstract()) {
@@ -7075,7 +7167,7 @@ bool ClassLinker::LinkInterfaceMethods(
MethodNameAndSignatureComparator old_method_comparator(
translated_method->GetInterfaceMethodIfProxy(image_pointer_size_));
ArtMethod* miranda_method = FindSameNameAndSignature(old_method_comparator,
- miranda_methods);
+ miranda_methods);
DCHECK(miranda_method != nullptr);
translated_method = miranda_method;
} else {
@@ -7090,6 +7182,14 @@ bool ClassLinker::LinkInterfaceMethods(
if (it != move_table.end()) {
auto* new_method = it->second;
DCHECK(new_method != nullptr);
+ // Make sure the new_methods index is set.
+ if (new_method->GetMethodIndexDuringLinking() != i) {
+ DCHECK_LE(reinterpret_cast<uintptr_t>(&*methods->begin(method_size, method_alignment)),
+ reinterpret_cast<uintptr_t>(new_method));
+ DCHECK_LT(reinterpret_cast<uintptr_t>(new_method),
+ reinterpret_cast<uintptr_t>(&*methods->end(method_size, method_alignment)));
+ new_method->SetMethodIndex(0xFFFF & i);
+ }
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
@@ -7153,7 +7253,7 @@ bool ClassLinker::LinkInterfaceMethods(
self->EndAssertNoThreadSuspension(old_cause);
}
if (kIsDebugBuild && !is_interface) {
- SanityCheckVTable(klass, image_pointer_size_);
+ SanityCheckVTable(self, klass, image_pointer_size_);
}
return true;
}
diff --git a/test/960-default-smali/expected.txt b/test/960-default-smali/expected.txt
index f3db93f87f..8153d7d4eb 100644
--- a/test/960-default-smali/expected.txt
+++ b/test/960-default-smali/expected.txt
@@ -98,3 +98,34 @@ M-virtual K.bar()='BAZ!'
M-virtual L.bar()='BAZ!'
M-virtual M.bar()='BAZ!'
End testing for type M
+Testing for type N
+N-interface Foo.bar()='foobar'
+N-virtual N.bar()='foobar'
+End testing for type N
+Testing for type O
+O-interface Foo.bar()='foobar foobar'
+O-interface Foo2.bar()='foobar foobar'
+O-virtual N.bar()='foobar foobar'
+O-virtual O.bar()='foobar foobar'
+End testing for type O
+Testing for type P
+P-interface Foo.bar()='not foobar!'
+P-interface Foo2.bar()='not foobar!'
+P-virtual N.bar()='not foobar!'
+P-virtual O.bar()='not foobar!'
+P-virtual P.bar()='not foobar!'
+End testing for type P
+Testing for type Q
+Q-interface on Foo: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-interface on Foo2: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-interface on Foo3: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on N: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on O: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+Q-virtual on Q: bar() threw exception!
+Exception is of type java.lang.IncompatibleClassChangeError
+End testing for type Q
diff --git a/test/960-default-smali/src/Foo2.java b/test/960-default-smali/src/Foo2.java
new file mode 100644
index 0000000000..2a1bbc0bea
--- /dev/null
+++ b/test/960-default-smali/src/Foo2.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo2 extends Foo {
+ @Override
+ public default String bar() {
+ return "foobar foobar";
+ }
+}
diff --git a/test/960-default-smali/src/Foo3.java b/test/960-default-smali/src/Foo3.java
new file mode 100644
index 0000000000..4c00425937
--- /dev/null
+++ b/test/960-default-smali/src/Foo3.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo3 extends Foo {
+ default void doNothing() {}
+}
diff --git a/test/960-default-smali/src/N.java b/test/960-default-smali/src/N.java
new file mode 100644
index 0000000000..9d33320fa7
--- /dev/null
+++ b/test/960-default-smali/src/N.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class N implements Foo {
+}
diff --git a/test/960-default-smali/src/O.java b/test/960-default-smali/src/O.java
new file mode 100644
index 0000000000..55126af3ae
--- /dev/null
+++ b/test/960-default-smali/src/O.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class O extends N implements Foo2 {
+}
diff --git a/test/960-default-smali/src/P.java b/test/960-default-smali/src/P.java
new file mode 100644
index 0000000000..1ee6c26a6b
--- /dev/null
+++ b/test/960-default-smali/src/P.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class P extends O implements Foo2 {
+ @Override
+ public String bar() {
+ return "not foobar!";
+ }
+}
diff --git a/test/960-default-smali/src/Q.java b/test/960-default-smali/src/Q.java
new file mode 100644
index 0000000000..bc1e1640cd
--- /dev/null
+++ b/test/960-default-smali/src/Q.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+class Q extends O implements Foo2, Foo3 {
+}
diff --git a/test/960-default-smali/src/classes.xml b/test/960-default-smali/src/classes.xml
index f3e50c570b..c66d35b305 100644
--- a/test/960-default-smali/src/classes.xml
+++ b/test/960-default-smali/src/classes.xml
@@ -102,6 +102,37 @@
<method>bar</method>
</methods>
</class>
+
+ <class name="N" super="java/lang/Object">
+ <implements>
+ <item>Foo</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="O" super="N">
+ <implements>
+ <item>Foo2</item>
+ </implements>
+ <methods> </methods>
+ </class>
+
+ <class name="P" super="O">
+ <implements>
+ <item>Foo2</item>
+ </implements>
+ <methods>
+ <method>bar</method>
+ </methods>
+ </class>
+
+ <class name="Q" super="O">
+ <implements>
+ <item>Foo2</item>
+ <item>Foo3</item>
+ </implements>
+ <methods> </methods>
+ </class>
</classes>
<interfaces>
@@ -153,6 +184,24 @@
</methods>
</interface>
+ <interface name="Foo2" super="java/lang/Object">
+ <implements>
+ <item>Foo</item>
+ </implements>
+ <methods>
+ <method type="default">bar</method>
+ </methods>
+ </interface>
+
+ <interface name="Foo3" super="java/lang/Object">
+ <implements>
+ <item>Foo</item>
+ </implements>
+ <methods>
+ <method type="default">bar</method>
+ </methods>
+ </interface>
+
<interface name="Fooer" super="java/lang/Object">
<implements>
<item>Foo</item>
diff --git a/test/960-default-smali/src2/Foo.java b/test/960-default-smali/src2/Foo.java
new file mode 100644
index 0000000000..ed5b35f47b
--- /dev/null
+++ b/test/960-default-smali/src2/Foo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+interface Foo {
+ public default String bar() {
+ return "foobar";
+ }
+}
diff --git a/test/960-default-smali/src2/Foo3.java b/test/960-default-smali/src2/Foo3.java
new file mode 100644
index 0000000000..e96f98a48a
--- /dev/null
+++ b/test/960-default-smali/src2/Foo3.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Regression test for b/31280371
+ */
+interface Foo3 extends Foo {
+ @Override
+ public default String bar() {
+ return "I'm in conflict";
+ }
+}
diff --git a/test/utils/python/generate_java_main.py b/test/utils/python/generate_java_main.py
index f66d0dd372..1c76b06dda 100755
--- a/test/utils/python/generate_java_main.py
+++ b/test/utils/python/generate_java_main.py
@@ -175,7 +175,11 @@ class Func(mixins.Named, mixins.NameComparableMixin):
return;
}} catch (Error e) {{
System.out.printf("%s-{invoke_type} on {farg}: {callfunc}() threw exception!\\n", s);
- e.printStackTrace(System.out);
+ if (e instanceof IncompatibleClassChangeError) {{
+ System.out.printf("Exception is of type %s\\n", e.getClass().getName());
+ }} else {{
+ e.printStackTrace(System.out);
+ }}
}}
}}
"""