Handle MethodHandle call to non-default interface methods
We would check-fail when an invoke-super MethodHandle attempted to
invoke a non-default interface method. This changes the behavior to
correctly throw an IllegalAccessError.
Test: ./test.py --host
Bug: 142059356
Change-Id: I8c214ee589ba71d8ab0dd6434890a3f681031364
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 5471d38..38bd611 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -20,6 +20,7 @@
#include "class_root.h"
#include "common_dex_operations.h"
+#include "common_throws.h"
#include "interpreter/shadow_frame-inl.h"
#include "jvalue-inl.h"
#include "mirror/class-inl.h"
@@ -685,7 +686,14 @@
if (referrer_class == declaring_class) {
return target_method;
}
- if (!declaring_class->IsInterface()) {
+ if (declaring_class->IsInterface()) {
+ if (target_method->IsAbstract()) {
+ std::string msg =
+ "Method " + target_method->PrettyMethod() + " is abstract interface method!";
+ ThrowIllegalAccessException(msg.c_str());
+ return nullptr;
+ }
+ } else {
ObjPtr<mirror::Class> super_class = referrer_class->GetSuperClass();
uint16_t vtable_index = target_method->GetMethodIndex();
DCHECK(super_class != nullptr);
diff --git a/test/956-methodhandles/expected.txt b/test/956-methodhandles/expected.txt
index a8b609b..206ab20 100644
--- a/test/956-methodhandles/expected.txt
+++ b/test/956-methodhandles/expected.txt
@@ -26,3 +26,14 @@
Don't expect Hi now
[3, 2, 1]
[1, 2, 3]
+Trying to call public abstract void java.util.function.Consumer.accept(java.lang.Object)
+Called accept with foo
+Trying to call public default java.util.function.Consumer java.util.function.Consumer.andThen(java.util.function.Consumer)
+Trying to call public abstract void java.util.function.Consumer.accept(java.lang.Object)
+Called accept with bar
+and then bar
+Ignoring and then
+Got hello
+Got hello there
+Called and then with hello there
+Got expected IAE when invoke-special on an abstract interface method
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 11d6ead..78f6db7 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -24,12 +24,14 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-
+import java.util.function.Consumer;
import other.Chatty;
public class Main {
@@ -108,6 +110,7 @@
testVariableArity_MethodHandles_bind();
testRevealDirect();
testReflectiveCalls();
+ testInterfaceSpecial();
}
public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
@@ -1780,4 +1783,73 @@
}
}
}
+
+ public static void testInterfaceSpecial() throws Throwable {
+ final Method acceptMethod = Consumer.class.getDeclaredMethod("accept", Object.class);
+ final Method andThenMethod = Consumer.class.getDeclaredMethod("andThen", Consumer.class);
+ // Proxies
+ Consumer<Object> c = (Consumer<Object>)Proxy.newProxyInstance(
+ Main.class.getClassLoader(),
+ new Class<?>[] { Consumer.class },
+ (p, m, a) -> {
+ System.out.println("Trying to call " + m);
+ if (m.equals(andThenMethod)) {
+ List<Object> args = a == null ? Collections.EMPTY_LIST : Arrays.asList(a);
+ return MethodHandles.lookup()
+ .findSpecial(Consumer.class,
+ m.getName(),
+ MethodType.methodType(m.getReturnType(),
+ m.getParameterTypes()),
+ p.getClass())
+ .bindTo(p)
+ .invokeWithArguments(args);
+ } else if (m.equals(acceptMethod)) {
+ System.out.println("Called accept with " + a[0]);
+ }
+ return null;
+ });
+ c.accept("foo");
+ Consumer<Object> c2 = c.andThen((Object o) -> { System.out.println("and then " + o); });
+ c2.accept("bar");
+
+ // Non-proxies
+ Consumer<Object> c3 = new Consumer() {
+ public void accept(Object o) {
+ System.out.println("Got " + o);
+ }
+ @Override
+ public Consumer<Object> andThen(Consumer c) {
+ System.out.println("Ignoring and then");
+ return this;
+ }
+ };
+ Consumer<Object> c4 = c3.andThen((x) -> { throw new Error("Failed"); });
+ c4.accept("hello");
+ Consumer<Object> andthen = (Object o) -> { System.out.println("Called and then with " + o);};
+ Consumer<Object> c5 =
+ (Consumer<Object>)MethodHandles.lookup()
+ .findSpecial(Consumer.class,
+ andThenMethod.getName(),
+ MethodType.methodType(
+ andThenMethod.getReturnType(),
+ andThenMethod.getParameterTypes()),
+ c3.getClass())
+ .bindTo(c3)
+ .invoke(andthen);
+ c5.accept("hello there");
+
+ // Failures
+ MethodHandle abstract_target =
+ MethodHandles.lookup()
+ .findSpecial(Consumer.class,
+ acceptMethod.getName(),
+ MethodType.methodType(acceptMethod.getReturnType(),
+ acceptMethod.getParameterTypes()),
+ c3.getClass());
+ try {
+ abstract_target.invoke(c3, "hello");
+ } catch (IllegalAccessException e) {
+ System.out.println("Got expected IAE when invoke-special on an abstract interface method");
+ }
+ }
}