summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--luni/src/test/java/libcore/java/lang/invoke/MethodHandlesTest.java48
-rw-r--r--ojluni/src/main/java/java/lang/invoke/MethodHandles.java17
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);
}