diff options
| -rw-r--r-- | luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java | 48 | ||||
| -rw-r--r-- | ojluni/src/main/java/java/lang/invoke/MethodHandles.java | 17 |
2 files changed, 61 insertions, 4 deletions
diff --git a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java index fc18ab8efa..8e08a59394 100644 --- a/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java +++ b/luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java @@ -29,6 +29,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -299,6 +300,18 @@ public class MethodHandlesTest extends TestCase { public static final Lookup lookup = MethodHandles.lookup(); } + public interface F { + public default void callInner(Consumer<Class<?>> c) { + c.accept(F.class); + } + } + + public class G implements F { + public void callInner(Consumer<Class<?>> c) { + c.accept(G.class); + } + } + public void testfindSpecial_invokeSuperBehaviour() throws Throwable { // This is equivalent to an invoke-super instruction where the referrer // is B.class. @@ -364,6 +377,41 @@ public class MethodHandlesTest extends TestCase { } catch (NoSuchMethodException e) {} } + public void testfindSpecial_invokeSuperInterfaceBehaviour() throws Throwable { + // Check interface invoke super on unrelated lookup (with some private access). + Class<?>[] res = new Class<?>[2]; + MethodHandle mh = MethodHandles.lookup().findSpecial(F.class /* refC */, "callInner", + MethodType.methodType(void.class, Consumer.class), G.class /* specialCaller */); + G g = new G(); + Consumer<Class<?>> oc = (Class<?> c) -> { res[0] = c; }; + mh.invokeExact(g, oc); + g.callInner((Class<?> c) -> { res[1] = c; }); + // Make sure the method-handle calls the default implementatoin + assertTrue(res[0] == F.class); + // Make sure the normal one works as we expect. + assertTrue(res[1] == G.class); + + // Check findSpecial always fails if the lookup has only public access + try { + MethodHandles.publicLookup().findSpecial(F.class /* refC */, "callInner", + MethodType.methodType(void.class, Consumer.class), G.class /* specialCaller */); + fail(); + } catch (IllegalAccessException e) {} + + // Check doing invokeSpecial on abstract interface methods gets appropriate errors. We + // expect it to throw an IllegalAccessError. + MethodHandle mh2 = + MethodHandles.lookup().findSpecial( + Foo.class /* refC */, + "foo", + MethodType.methodType(String.class), + Bar.class /* specialCaller */); + try { + mh2.invoke(new BarImpl()); + fail(); + } catch (IllegalAccessException e) {} + } + public void testfindSpecial_invokeDirectBehaviour() throws Throwable { D dInstance = new D(); diff --git a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java index 17c5f57d1d..3b39fbd22d 100644 --- a/ojluni/src/main/java/java/lang/invoke/MethodHandles.java +++ b/ojluni/src/main/java/java/lang/invoke/MethodHandles.java @@ -1129,7 +1129,8 @@ assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method // Make sure that the special caller is identical to the lookup class or that we have // private access. - checkSpecialCaller(specialCaller); + // Android-changed: Also allow access to any interface methods. + checkSpecialCaller(specialCaller, refc); // Even though constructors are invoked using a "special" invoke, handles to them can't // be created using findSpecial. Callers must use findConstructor instead. Similarly, @@ -1655,7 +1656,9 @@ return mh1; } if (!m.isAccessible()) { - checkSpecialCaller(specialCaller); + // Android-changed: Match Java language 9 behavior where unreflectSpecial continues + // to require exact caller lookupClass match. + checkSpecialCaller(specialCaller, null); } final MethodType methodType = MethodType.methodType(m.getReturnType(), @@ -1937,13 +1940,19 @@ return mh1; // // private static final boolean ALLOW_NESTMATE_ACCESS = false; - private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException { + // Android-changed: Match java language 9 behavior allowing special access if the reflected + // class (called 'refc', the class from which the method is being accessed) is an interface + // and is implemented by the caller. + private void checkSpecialCaller(Class<?> specialCaller, Class<?> refc) throws IllegalAccessException { // Android-changed: No support for TRUSTED lookups. Also construct the // IllegalAccessException by hand because the upstream code implicitly assumes // that the lookupClass == specialCaller. // // if (allowedModes == TRUSTED) return; - if (!hasPrivateAccess() || (specialCaller != lookupClass())) { + boolean isInterfaceLookup = (refc != null && + refc.isInterface() && + refc.isAssignableFrom(specialCaller)); + if (!hasPrivateAccess() || (specialCaller != lookupClass() && !isInterfaceLookup)) { throw new IllegalAccessException("no private access for invokespecial : " + specialCaller + ", from" + this); } |
