From: Narayan Kamath Date: Thu, 20 Oct 2016 09:57:45 +0000 (+0100) Subject: Revert "Revert "Interpreter: Add support for direct handle invokes on methods."" X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=9bdaeebe6aca491b11a44999f571f0122ad87499;p=android-x86%2Fart.git Revert "Revert "Interpreter: Add support for direct handle invokes on methods."" This reverts commit cfa61ad52077df66a448b84c360b12bc6f0e3f51. Test flakiness in 956-methodhandles is fixed by d08e39b6f02368aaa668b5aae6b6077b3eb44d9c. Test: make test-art-host Change-Id: I56e02e9a5bbc2b992cf746a92fd95ea77d32456c --- diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc index 09d11678f..b71236b51 100644 --- a/runtime/interpreter/interpreter_common.cc +++ b/runtime/interpreter/interpreter_common.cc @@ -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 index 000000000..613e97c71 --- /dev/null +++ b/test/956-methodhandles/build @@ -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 index 000000000..ddc1cb013 --- /dev/null +++ b/test/956-methodhandles/expected.txt @@ -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 index 000000000..f1dbb6164 --- /dev/null +++ b/test/956-methodhandles/info.txt @@ -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 index 000000000..a9f182288 --- /dev/null +++ b/test/956-methodhandles/run @@ -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 index 000000000..2802dfa4c --- /dev/null +++ b/test/956-methodhandles/src/Main.java @@ -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) { + } + } +} + +