Merge "Workaround invokesuper underspecified behavior."
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index a834216..aaddc01 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -721,6 +721,11 @@
DCHECK(Runtime::Current()->IsAotCompiler());
return nullptr;
}
+ if (!methods_class->IsAssignableFrom(compiling_class.Get())) {
+ // We cannot statically determine the target method. The runtime will throw a
+ // NoSuchMethodError on this one.
+ return nullptr;
+ }
ArtMethod* actual_method;
if (methods_class->IsInterface()) {
actual_method = methods_class->FindVirtualMethodForInterfaceSuper(
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 16fbfaa..fc62573 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -514,12 +514,18 @@
CHECK(self->IsExceptionPending());
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referring_class->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we throw
+ // a NoSuchMethodError.
+ mirror::Class* super_class = nullptr;
+ if (method_reference_class->IsAssignableFrom(referring_class)) {
+ super_class = referring_class->GetSuperClass();
+ }
uint16_t vtable_index = resolved_method->GetMethodIndex();
if (access_check) {
// Check existence of super class.
- if (super_class == nullptr || !super_class->HasVTable() ||
+ if (super_class == nullptr ||
+ !super_class->HasVTable() ||
vtable_index >= static_cast<uint32_t>(super_class->GetVTableLength())) {
// Behavior to agree with that of the verifier.
ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
@@ -693,8 +699,13 @@
// Need to do full type resolution...
return nullptr;
} else if (!method_reference_class->IsInterface()) {
- // It is not an interface.
- mirror::Class* super_class = referrer->GetDeclaringClass()->GetSuperClass();
+ // It is not an interface. If the referring class is in the class hierarchy of the
+ // referenced class in the bytecode, we use its super class. Otherwise, we cannot
+ // resolve the method.
+ if (!method_reference_class->IsAssignableFrom(referring_class)) {
+ return nullptr;
+ }
+ mirror::Class* super_class = referring_class->GetSuperClass();
if (resolved_method->GetMethodIndex() >= super_class->GetVTableLength()) {
// The super class does not have the method.
return nullptr;
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index d05ae42..2b96328 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4101,8 +4101,8 @@
<< " to super " << PrettyMethod(res_method);
return nullptr;
}
- mirror::Class* super_klass = super.GetClass();
- if (res_method->GetMethodIndex() >= super_klass->GetVTableLength()) {
+ if (!reference_class->IsAssignableFrom(GetDeclaringClass().GetClass()) ||
+ (res_method->GetMethodIndex() >= super.GetClass()->GetVTableLength())) {
Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
<< PrettyMethod(dex_method_idx_, *dex_file_)
<< " to super " << super
diff --git a/test/594-invoke-super/expected.txt b/test/594-invoke-super/expected.txt
new file mode 100644
index 0000000..de26026
--- /dev/null
+++ b/test/594-invoke-super/expected.txt
@@ -0,0 +1,7 @@
+new A
+I am A's foo
+new B
+I am B's foo
+new A
+new B
+passed
diff --git a/test/594-invoke-super/info.txt b/test/594-invoke-super/info.txt
new file mode 100644
index 0000000..440d8b8
--- /dev/null
+++ b/test/594-invoke-super/info.txt
@@ -0,0 +1 @@
+Invoke-super on various references.
diff --git a/test/594-invoke-super/smali/invoke-super.smali b/test/594-invoke-super/smali/invoke-super.smali
new file mode 100644
index 0000000..6f787dd
--- /dev/null
+++ b/test/594-invoke-super/smali/invoke-super.smali
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 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.
+
+.class public LZ;
+.super LA;
+
+.method public constructor <init>()V
+.registers 1
+ invoke-direct {v0}, LA;-><init>()V
+ return-void
+.end method
+
+.method public foo()V
+.registers 3
+ new-instance v0, LY;
+ invoke-direct {v0}, LY;-><init>()V
+ invoke-super {v0}, LY;->foo()V
+ return-void
+.end method
diff --git a/test/594-invoke-super/src/Main.java b/test/594-invoke-super/src/Main.java
new file mode 100644
index 0000000..53f2bbf
--- /dev/null
+++ b/test/594-invoke-super/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 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.
+ */
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+//
+// Two classes A and B with method foo().
+//
+
+class A {
+ A() { System.out.println("new A"); }
+
+ public void foo() { System.out.println("I am A's foo"); }
+
+ // We previously used to invoke this method with a Y instance, due
+ // to invoke-super underspecified behavior.
+ public void bar() { System.out.println("I am A's bar"); }
+}
+
+class B {
+ B() { System.out.println("new B"); }
+
+ public void foo() { System.out.println("I am B's foo"); }
+}
+
+//
+// Two subclasses X and Y that call foo() on super.
+//
+
+class X extends A {
+ public void foo() { super.foo(); }
+}
+
+class Y extends B {
+ public void foo() { super.foo(); }
+}
+
+//
+// Driver class.
+//
+
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+ // The normal stuff, X's super goes to A, Y's super goes to B.
+ new X().foo();
+ new Y().foo();
+
+ // And now it gets interesting.
+
+ // In bytecode, we define a class Z that is a subclass of A, and we call
+ // invoke-super on an instance of Y.
+ Class<?> z = Class.forName("Z");
+ Method m = z.getMethod("foo");
+ try {
+ m.invoke(z.newInstance());
+ throw new Error("Expected InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NoSuchMethodError)) {
+ throw new Error("Expected NoSuchMethodError");
+ }
+ }
+
+ System.out.println("passed");
+ }
+}