blob: 1b5dc4fd03f3a51083c145e8040fdffda063b1d0 [file] [log] [blame]
/*
* Copyright (C) 2024 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.WrongMethodTypeException;
import java.util.Objects;
public abstract class AbstractInvokeExactTest {
public static String STATUS = "";
public final void runAll() throws Throwable {
STATUS = "";
Multi.$noinline$testMHFromMain(optionalGet());
$noinline$privateMethods();
$noinline$testNoArgsCalls();
$noinline$nullchecks();
$noinline$testWithArgs();
$noinline$interfaceChecks();
$noinline$abstractClass();
}
// There is no privateLookupIn for const-method-handle.
abstract void $noinline$privateMethods() throws Throwable;
private void $noinline$testNoArgsCalls() throws Throwable {
voidMethod().invokeExact(new A());
assertEquals("A.voidMethod", STATUS);
int returnedInt = (int) returnInt().invokeExact(new A());
assertEquals(42, returnedInt);
double returnedDouble = (double) returnDouble().invokeExact(new A());
assertEquals(42.0d, returnedDouble);
try {
interfaceDefaultMethod().invokeExact(new A());
unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V");
} catch (WrongMethodTypeException expected) {}
interfaceDefaultMethod().invokeExact((I) new A());
assertEquals("I.defaultMethod", STATUS);
overwrittenInterfaceDefaultMethod().invokeExact((I) new A());
assertEquals("A.overrideMe", STATUS);
try {
exceptionThrowingMethod().invokeExact(new A());
unreachable("Target method always throws");
} catch (MyRuntimeException expected) {
assertEquals("A.throwException", STATUS);
}
try {
returnInt().invokeExact(new A());
unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V");
} catch (WrongMethodTypeException expected) {}
String returnedString = (String) staticMethod().invokeExact(new A());
assertEquals("staticMethod", returnedString);
}
private void $noinline$nullchecks() throws Throwable {
try {
voidMethod().invokeExact((A) null);
unreachable("Receiver is null, should throw NPE");
} catch (NullPointerException expected) {}
try {
voidMethod().invokeExact((Main) null);
unreachable("Should throw WMTE: input is of wrong type");
} catch (WrongMethodTypeException expected) {}
try {
interfaceDefaultMethod().invokeExact((I) null);
unreachable("Receiver is null, should throw NPE");
} catch (NullPointerException expected) {}
try {
interfaceDefaultMethod().invokeExact((A) null);
unreachable("Should throw WMTE: input is of wrong type");
} catch (WrongMethodTypeException expected) {}
try {
MethodHandle mh = $noinline$nullMethodHandle();
mh.invokeExact();
unreachable("MethodHandle object is null, should throw NPE");
} catch (NullPointerException expected) {}
}
private static MethodHandle $noinline$nullMethodHandle() {
return null;
}
private void $noinline$testWithArgs() throws Throwable {
int sum = (int) sumI().invokeExact(new Sums(), 1);
assertEquals(1, sum);
sum = (int) sum2I().invokeExact(new Sums(), 1, 2);
assertEquals(3, sum);
sum = (int) sum3I().invokeExact(new Sums(), 1, 2, 3);
assertEquals(6, sum);
sum = (int) sum4I().invokeExact(new Sums(), 1, 2, 3, 4);
assertEquals(10, sum);
sum = (int) sum5I().invokeExact(new Sums(), 1, 2, 3, 4, 5);
assertEquals(15, sum);
sum = (int) sum6I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6);
assertEquals(21, sum);
sum = (int) sum7I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7);
assertEquals(28, sum);
sum = (int) sum8I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8);
assertEquals(36, sum);
sum = (int) sum9I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9);
assertEquals(45, sum);
sum = (int) sum10I().invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
assertEquals(55, sum);
long lsum = (long) sumIJ().invokeExact(new Sums(), 1, 2L);
assertEquals(3L, lsum);
lsum = (long) sum2IJ().invokeExact(new Sums(), 1, 2L, 3, 4L);
assertEquals(10L, lsum);
lsum = (long) sum3IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L);
assertEquals(21L, lsum);
lsum = (long) sum4IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L);
assertEquals(36L, lsum);
lsum = (long) sum5IJ().invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L);
assertEquals(55L, lsum);
}
private void $noinline$interfaceChecks() throws Throwable {
FooBarImpl instance = new FooBarImpl();
String result = null;
result = (String) fooNonDefault().invokeExact((Foo) instance);
assertEquals("FooBarImpl.nonDefault", result);
result = (String) fooBarImplNonDefault().invokeExact(instance);
assertEquals("FooBarImpl.nonDefault", result);
result = (String) barDefault().invokeExact((Bar) instance);
assertEquals("Bar.defaultToOverride", result);
result = (String) fooDefault().invokeExact((Foo) instance);
assertEquals("Bar.defaultToOverride", result);
result = (String) fooBarImplDefault().invokeExact(instance);
assertEquals("Bar.defaultToOverride", result);
result = (String) fooNonOverriddenDefault().invokeExact((Foo) instance);
assertEquals("Foo.nonOverriddenDefault", result);
result = (String) barNonOverriddenDefault().invokeExact((Bar) instance);
assertEquals("Foo.nonOverriddenDefault", result);
ToStringable toStringable = new ToStringableImpl();
result = (String) toStringDefinedInAnInterface().invokeExact(toStringable);
assertEquals("ToStringableImpl", result);
try {
String ignored = (String) toStringDefinedInAnInterface().invokeExact(instance);
unreachable("Should throw WMTE");
} catch (WrongMethodTypeException expected) {}
Impl1 impl1 = new Impl1();
result = (String) interfaceOneMethod().invokeExact((Interface1) impl1);
assertEquals("Impl1.methodOne", result);
Impl2 impl2 = new Impl2();
result = (String) interfaceTwoMethod().invokeExact((Interface2) impl2);
assertEquals("Impl2.methodTwo", result);
result = (String) interfaceOneMethod().invokeExact((Interface1) impl2);
assertEquals("Impl2.methodOne", result);
Impl3 impl3 = new Impl3();
result = (String) interfaceThreeMethod().invokeExact((Interface3) impl3);
assertEquals("Impl3.methodThree", result);
result = (String) interfaceTwoMethod().invokeExact((Interface2) impl3);
assertEquals("Impl3.methodTwo", result);
result = (String) interfaceOneMethod().invokeExact((Interface1) impl3);
assertEquals("Impl3.methodOne", result);
Impl4 impl4 = new Impl4();
result = (String) interfaceFourMethod().invokeExact((Interface4) impl4);
assertEquals("Impl4.methodFour", result);
result = (String) interfaceThreeMethod().invokeExact((Interface3) impl4);
assertEquals("Impl4.methodThree", result);
result = (String) interfaceTwoMethod().invokeExact((Interface2) impl4);
assertEquals("Impl4.methodTwo", result);
result = (String) interfaceOneMethod().invokeExact((Interface1) impl4);
assertEquals("Impl4.methodOne", result);
FooAndFooConflictImpl conflictImpl = new FooAndFooConflictImpl();
result = (String) fooDefault().invokeExact((Foo) conflictImpl);
assertEquals("DefaultMethodConflictImpl.defaultToOverride", result);
FooAndFooConflict fooAndFooConflict = new FooAndFooConflict() {
public String nonDefault() {
throw new UnsupportedOperationException();
}
};
// TODO(b/297147201): The RI throws AbsractMethodError in these cases, just like plain
// fooAndFooConflict.defaultToOverride() call. So RI's invokeExact follows "equivalent of a
// particular bytecode behavior". ART throws ICCE in both cases, so current implementation of
// invokeExact does not break "equivalent ..." part of the javadoc.
// Need to check what the spec says about the error thrown when a conflicting default method is
// attempted to be called.
try {
String ignored = (String) fooAndFooConflictDefault().invokeExact(fooAndFooConflict);
unreachable("Non-overridden default conflict method");
} catch (IncompatibleClassChangeError expected) {}
try {
String ignored = (String) fooDefault().invokeExact((Foo) fooAndFooConflict);
unreachable("Non-overridden default conflict method");
} catch (IncompatibleClassChangeError expected) {}
BaseInterface baseClassImpl = new BaseClassImpl();
try {
String ignored = (String) baseInterface().invokeExact(baseClassImpl);
unreachable("Calling unimplemented interface method");
} catch (AbstractMethodError expected) {}
}
private void $noinline$abstractClass() throws Throwable {
FooBarImpl instance = new FooBarImpl();
String result = null;
result = (String) fooBarDefinedInAbstract().invokeExact((FooBar) instance);
assertEquals("FooBar.definedInAbstract", result);
result = (String) fooBarImplDefinedInAbstract().invokeExact(instance);
assertEquals("FooBar.definedInAbstract", result);
FooBar fooBar = new FooBar() {
@Override
public String nonDefault() {
return "anonymous.nonDefault";
}
};
result = (String) fooBarDefinedInAbstract().invokeExact(fooBar);
assertEquals("FooBar.definedInAbstract", result);
result = (String) fooBarNonDefault().invokeExact(fooBar);
assertEquals("anonymous.nonDefault", result);
}
static void assertEquals(Object expected, Object actual) {
if (!Objects.equals(expected, actual)) {
throw new AssertionError("Expected: " + expected + ", got: " + actual);
}
}
private static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", got: " + actual);
}
}
private static void assertEquals(long expected, long actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", got: " + actual);
}
}
private static void assertEquals(double expected, double actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", got: " + actual);
}
}
static void unreachable(String msg) {
throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg);
}
public abstract MethodHandle optionalGet();
public abstract MethodHandle voidMethod();
public abstract MethodHandle returnInt();
public abstract MethodHandle returnDouble();
public abstract MethodHandle interfaceDefaultMethod();
public abstract MethodHandle overwrittenInterfaceDefaultMethod();
public abstract MethodHandle exceptionThrowingMethod();
public abstract MethodHandle staticMethod();
public abstract MethodHandle sumI();
public abstract MethodHandle sum2I();
public abstract MethodHandle sum3I();
public abstract MethodHandle sum4I();
public abstract MethodHandle sum5I();
public abstract MethodHandle sum6I();
public abstract MethodHandle sum7I();
public abstract MethodHandle sum8I();
public abstract MethodHandle sum9I();
public abstract MethodHandle sum10I();
public abstract MethodHandle sumIJ();
public abstract MethodHandle sum2IJ();
public abstract MethodHandle sum3IJ();
public abstract MethodHandle sum4IJ();
public abstract MethodHandle sum5IJ();
public abstract MethodHandle fooNonDefault();
public abstract MethodHandle fooBarImplNonDefault();
public abstract MethodHandle barDefault();
public abstract MethodHandle fooDefault();
public abstract MethodHandle fooBarImplDefault();
public abstract MethodHandle fooNonOverriddenDefault();
public abstract MethodHandle barNonOverriddenDefault();
public abstract MethodHandle toStringDefinedInAnInterface();
public abstract MethodHandle interfaceOneMethod();
public abstract MethodHandle interfaceTwoMethod();
public abstract MethodHandle interfaceThreeMethod();
public abstract MethodHandle interfaceFourMethod();
public abstract MethodHandle fooBarDefinedInAbstract();
public abstract MethodHandle fooBarImplDefinedInAbstract();
public abstract MethodHandle fooBarNonDefault();
public abstract MethodHandle fooAndFooConflictDefault();
public abstract MethodHandle baseInterface();
}