OSDN Git Service

Revert "Revert "Interpreter: Add support for direct handle invokes on methods.""
authorNarayan Kamath <narayan@google.com>
Thu, 20 Oct 2016 09:57:45 +0000 (10:57 +0100)
committerNarayan Kamath <narayan@google.com>
Thu, 20 Oct 2016 10:14:53 +0000 (11:14 +0100)
This reverts commit cfa61ad52077df66a448b84c360b12bc6f0e3f51.

Test flakiness in 956-methodhandles is fixed by
d08e39b6f02368aaa668b5aae6b6077b3eb44d9c.

Test: make test-art-host

Change-Id: I56e02e9a5bbc2b992cf746a92fd95ea77d32456c

runtime/interpreter/interpreter_common.cc
test/956-methodhandles/build [new file with mode: 0755]
test/956-methodhandles/expected.txt [new file with mode: 0644]
test/956-methodhandles/info.txt [new file with mode: 0644]
test/956-methodhandles/run [new file with mode: 0755]
test/956-methodhandles/src/Main.java [new file with mode: 0644]

index 09d1167..b71236b 100644 (file)
@@ -667,11 +667,39 @@ inline bool DoInvokePolymorphic(Thread* self, ShadowFrame& shadow_frame,
         return false;
       }
     } else if (handle_kind == kInvokeDirect) {
-      // TODO(narayan) : We need to handle the case where the target method is a
-      // constructor here. Also the case where we don't want to dynamically
-      // dispatch based on the type of the receiver.
-      UNIMPLEMENTED(FATAL) << "Direct invokes are not implemented yet.";
-      return false;
+      if (called_method->IsConstructor()) {
+        // TODO(narayan) : We need to handle the case where the target method is a
+        // constructor here.
+        UNIMPLEMENTED(FATAL) << "Direct invokes for constructors are not implemented yet.";
+        return false;
+      }
+
+      // Nothing special to do in the case where we're not dealing with a
+      // constructor. It's a private method, and we've already access checked at
+      // the point of creating the handle.
+    } else if (handle_kind == kInvokeSuper) {
+      mirror::Class* declaring_class = called_method->GetDeclaringClass();
+
+      // Note that we're not dynamically dispatching on the type of the receiver
+      // here. We use the static type of the "receiver" object that we've
+      // recorded in the method handle's type, which will be the same as the
+      // special caller that was specified at the point of lookup.
+      mirror::Class* referrer_class = handle_type->GetPTypes()->Get(0);
+      if (!declaring_class->IsInterface()) {
+        mirror::Class* super_class = referrer_class->GetSuperClass();
+        uint16_t vtable_index = called_method->GetMethodIndex();
+        DCHECK(super_class != nullptr);
+        DCHECK(super_class->HasVTable());
+        // Note that super_class is a super of referrer_class and called_method
+        // will always be declared by super_class (or one of its super classes).
+        DCHECK_LT(vtable_index, super_class->GetVTableLength());
+        called_method = super_class->GetVTableEntry(vtable_index, kRuntimePointerSize);
+      } else {
+        called_method = referrer_class->FindVirtualMethodForInterfaceSuper(
+            called_method, kRuntimePointerSize);
+      }
+
+      CHECK(called_method != nullptr);
     }
 
     // NOTE: handle_kind == kInvokeStatic needs no special treatment here. We
diff --git a/test/956-methodhandles/build b/test/956-methodhandles/build
new file mode 100755 (executable)
index 0000000..613e97c
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+  # Don't do anything with jvm.
+  export USE_JACK=true
+  export JACK_SERVER=false
+  export JACK_REPOSITORY="${ANDROID_BUILD_TOP}/prebuilts/sdk/tools/jacks"
+  export JACK_VERSION=4.11.BETA
+fi
+
+./default-build "$@" --experimental method-handles
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
new file mode 100644 (file)
index 0000000..ddc1cb0
--- /dev/null
@@ -0,0 +1,5 @@
+foo_A
+foo_A
+foo_A
+foo_B
+privateRyan_D
diff --git a/test/956-methodhandles/info.txt b/test/956-methodhandles/info.txt
new file mode 100644 (file)
index 0000000..f1dbb61
--- /dev/null
@@ -0,0 +1,3 @@
+Tests for method handle invocations.
+
+NOTE: needs to run under ART or a Java 8 Language runtime and compiler.
diff --git a/test/956-methodhandles/run b/test/956-methodhandles/run
new file mode 100755 (executable)
index 0000000..a9f1822
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+./default-run "$@" --experimental method-handles
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
new file mode 100644 (file)
index 0000000..2802dfa
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * 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.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.WrongMethodTypeException;
+
+public class Main {
+
+  public static class A {
+    public void foo() {
+      System.out.println("foo_A");
+    }
+
+    public static final Lookup lookup = MethodHandles.lookup();
+  }
+
+  public static class B extends A {
+    public void foo() {
+      System.out.println("foo_B");
+    }
+
+    public static final Lookup lookup = MethodHandles.lookup();
+  }
+
+  public static class C extends B {
+    public static final Lookup lookup = MethodHandles.lookup();
+  }
+
+  public static class D {
+    private final void privateRyan() {
+      System.out.println("privateRyan_D");
+    }
+
+    public static final Lookup lookup = MethodHandles.lookup();
+  }
+
+  public static class E extends D {
+    public static final Lookup lookup = MethodHandles.lookup();
+  }
+
+  public static void main(String[] args) throws Throwable {
+    testfindSpecial_invokeSuperBehaviour();
+    testfindSpecial_invokeDirectBehaviour();
+  }
+
+  public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
+    // This is equivalent to an invoke-super instruction where the referrer
+    // is B.class.
+    MethodHandle mh1 = B.lookup.findSpecial(A.class /* refC */, "foo",
+        MethodType.methodType(void.class), B.class /* specialCaller */);
+
+    A aInstance = new A();
+    B bInstance = new B();
+    C cInstance = new C();
+
+    // This should be as if an invoke-super was called from one of B's methods.
+    mh1.invokeExact(bInstance);
+    mh1.invoke(bInstance);
+
+    // This should not work. The receiver type in the handle will be suitably
+    // restricted to B and subclasses.
+    try {
+      mh1.invoke(aInstance);
+      System.out.println("mh1.invoke(aInstance) should not succeeed");
+    } catch (ClassCastException expected) {
+    }
+
+    try {
+      mh1.invokeExact(aInstance);
+      System.out.println("mh1.invoke(aInstance) should not succeeed");
+    } catch (WrongMethodTypeException expected) {
+    } catch (ClassCastException workaround) {
+      // TODO(narayan): ART treats all invokes as if they were non-exact. We
+      // should throw a WMTE if we execute an invoke-polymorphic instruction whose
+      // target method is MethodHandle.invokeExact.
+    }
+
+    // This should *still* be as if an invoke-super was called from one of C's
+    // methods, despite the fact that we're operating on a C.
+    mh1.invoke(cInstance);
+
+    // Now that C is the special caller, the next invoke will call B.foo.
+    MethodHandle mh2 = C.lookup.findSpecial(A.class /* refC */, "foo",
+        MethodType.methodType(void.class), C.class /* specialCaller */);
+    mh2.invokeExact(cInstance);
+
+    // Shouldn't allow invoke-super semantics from an unrelated special caller.
+    try {
+      C.lookup.findSpecial(A.class, "foo",
+        MethodType.methodType(void.class), D.class /* specialCaller */);
+      System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded.");
+    } catch (IllegalAccessException expected) {
+    }
+  }
+
+  public static void testfindSpecial_invokeDirectBehaviour() throws Throwable {
+    D dInstance = new D();
+
+    MethodHandle mh3 = D.lookup.findSpecial(D.class, "privateRyan",
+        MethodType.methodType(void.class), D.class /* specialCaller */);
+    mh3.invoke(dInstance);
+
+    // The private method shouldn't be accessible from any special caller except
+    // itself...
+    try {
+      D.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), C.class);
+      System.out.println("findSpecial(privateRyan, C.class) unexpectedly succeeded");
+    } catch (IllegalAccessException expected) {
+    }
+
+    // ... or from any lookup context except its own.
+    try {
+      E.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), E.class);
+      System.out.println("findSpecial(privateRyan, E.class) unexpectedly succeeded");
+    } catch (IllegalAccessException expected) {
+    }
+  }
+}
+
+