Workaround invokesuper underspecified behavior.

The verifier allows invokesuper on a class unrelated
to the referring class. However, the runtime uses the vtable of
the super class of the referring class to lookup the ArtMethod.
Since the receiver has no relation to the referring class, this lead
to either jumping to a wrong method, or "luckily" throw a
NoSuchMethodError if the vtable index is out of bounds of the super
class of the referring class.

This changes the runtime behavior to always throw NoSuchMethodError
when hitting such invokesuper.

Also, we make the verifier consistent with the runtime by treating
such calls unresolved.

bug=27627004

(cherry picked from commit f663e341c550d1aa6f8f587d0ae0dbf7d254ff55)

Change-Id: Ibec5a1e3a3320c474e1e0e5634a9ef62ea734cdf
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index a9ceebb..93081b9 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");
+  }
+}