diff options
author | 2024-11-07 10:01:49 +0000 | |
---|---|---|
committer | 2024-11-12 14:27:31 +0000 | |
commit | 3051a5e03f32ca7aaae6041025ec60f90d41e34c (patch) | |
tree | 6d253e5c533e032ef1a43a914410d32f7e31081a | |
parent | 20cdc427d5f5875c2d58f9fde775957ad2e28cfd (diff) |
Remove now unnecessary checks from invokeExact intrinsic.
Also rewrote tests to make sure that MethodHandle-s created via
const-method-handle and Java API behave identically. They can
be slightly different even if lookup is done in the same class
for the same method as const-method-handle can target copied
methods.
Bug: 297147201
Test: ./art/test/testrunner/testrunner.py -b --host --64
Test: ./art/test/testrunner/testrunner.py -b --jvm
Change-Id: I69cdf2c44f2838ee0e57eaa2e93a6ed5276c39fb
20 files changed, 1866 insertions, 534 deletions
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc index 85c33d312f..b9d1a9f2c7 100644 --- a/compiler/optimizing/intrinsics_x86_64.cc +++ b/compiler/optimizing/intrinsics_x86_64.cc @@ -4288,17 +4288,6 @@ void IntrinsicCodeGeneratorX86_64::VisitMethodHandleInvokeExact(HInvoke* invoke) // If method is defined in the receiver's class, execute it as it is. __ j(kEqual, &execute_target_method); - __ testl(Address(temp, mirror::Class::AccessFlagsOffset()), Immediate(kAccInterface)); - // If `method`'s declaring class is not an interface, do virtual dispatch. - __ j(kZero, &do_virtual_dispatch); - - __ movl(temp, Address(method, ArtMethod::AccessFlagsOffset())); - // These flags are uint32_t and their signed value doesn't fit into int32_t (see b/377275405). - __ andl(temp, Immediate(bit_cast<int32_t, uint32_t>(kAccIntrinsic | kAccCopied))); - __ cmpl(temp, Immediate(kAccCopied)); - // If method is defined in an interface and is not copied it should be interface dispatched. - __ j(kNotEqual, slow_path->GetEntryLabel()); - __ Bind(&do_virtual_dispatch); // MethodIndex is uint16_t. __ movzxw(temp, Address(method, ArtMethod::MethodIndexOffset())); diff --git a/test/2277-methodhandle-invokeexact/build.py b/test/2277-methodhandle-invokeexact/build.py index d71324961b..966582b660 100644 --- a/test/2277-methodhandle-invokeexact/build.py +++ b/test/2277-methodhandle-invokeexact/build.py @@ -1,4 +1,21 @@ +# +# 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. + def build(ctx): + ctx.bash("./generate-sources") # To allow private interface methods. - ctx.default_build(javac_source_arg="17", + ctx.default_build(api_level="const-method-type", + javac_source_arg="17", javac_target_arg="17") diff --git a/test/2277-methodhandle-invokeexact/generate-sources b/test/2277-methodhandle-invokeexact/generate-sources new file mode 100755 index 0000000000..d74ca98a8c --- /dev/null +++ b/test/2277-methodhandle-invokeexact/generate-sources @@ -0,0 +1,29 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.6.jar" + +# Build the transformer to apply to compiled classes. +mkdir classes +${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find src-util -name '*.java') +${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes +rm -rf classes + +# Add annotation src files to our compiler inputs. +cp -r src-util/annotations src/ diff --git a/test/2277-methodhandle-invokeexact/javac_post.sh b/test/2277-methodhandle-invokeexact/javac_post.sh new file mode 100755 index 0000000000..2193f1dd13 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/javac_post.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# +# 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. + +set -e + +export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.6.jar" + +# Move original classes to intermediate location. +mv $1 $1-intermediate-classes +mkdir $1 + +# Transform intermediate classes. +transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer" +for class in $1-intermediate-classes/*.class ; do + transformed_class=$1/$(basename ${class}) + ${JAVA:-java} ${transformer_args} ${class} ${transformed_class} +done diff --git a/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodHandle.java b/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodHandle.java new file mode 100644 index 0000000000..ffc88b65fb --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodHandle.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodHandle constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodHandle { + /* Method handle kinds */ + public static final int STATIC_PUT = 0; + public static final int STATIC_GET = 1; + public static final int INSTANCE_PUT = 2; + public static final int INSTANCE_GET = 3; + public static final int INVOKE_STATIC = 4; + public static final int INVOKE_VIRTUAL = 5; + public static final int INVOKE_SPECIAL = 6; + public static final int NEW_INVOKE_SPECIAL = 7; + public static final int INVOKE_INTERFACE = 8; + + /** Kind of method handle. */ + int kind(); + + /** Class name owning the field or method. */ + String owner(); + + /** The field or method name addressed by the MethodHandle. */ + String fieldOrMethodName(); + + /** Descriptor for the field (type) or method (method-type) */ + String descriptor(); + + /** Whether the owner is an interface. */ + boolean ownerIsInterface() default false; +} diff --git a/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodType.java b/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodType.java new file mode 100644 index 0000000000..55b1536e6c --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodType.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be set on method to specify that if this method + * is statically invoked then the invocation is replaced by a + * load-constant bytecode with the MethodType constant described by + * the annotation. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ConstantMethodType { + /** Return type of method() or field getter() */ + Class<?> returnType() default void.class; + + /** Types of parameters for method or field setter() */ + Class<?>[] parameterTypes() default {}; +} diff --git a/test/2277-methodhandle-invokeexact/src-util/transformer/ConstantTransformer.java b/test/2277-methodhandle-invokeexact/src-util/transformer/ConstantTransformer.java new file mode 100644 index 0000000000..45e7b9ccdb --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src-util/transformer/ConstantTransformer.java @@ -0,0 +1,229 @@ +/* + * 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. + */ + +package transformer; + +import annotations.ConstantMethodHandle; +import annotations.ConstantMethodType; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodType; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +/** + * Class for transforming invoke static bytecodes into constant method handle loads and constant + * method type loads. + * + * <p>When a parameterless private static method returning a MethodHandle is defined and annotated + * with {@code ConstantMethodHandle}, this transformer will replace static invocations of the method + * with a load constant bytecode with a method handle in the constant pool. + * + * <p>Suppose a method is annotated as: <code> + * @ConstantMethodHandle( + * kind = ConstantMethodHandle.STATIC_GET, + * owner = "java/lang/Math", + * fieldOrMethodName = "E", + * descriptor = "D" + * ) + * private static MethodHandle getMathE() { + * unreachable(); + * return null; + * } + * </code> Then invocations of {@code getMathE} will be replaced by a load from the constant pool + * with the constant method handle described in the {@code ConstantMethodHandle} annotation. + * + * <p>Similarly, a parameterless private static method returning a {@code MethodType} and annotated + * with {@code ConstantMethodType}, will have invocations replaced by a load constant bytecode with + * a method type in the constant pool. + */ +class ConstantTransformer { + static class ConstantBuilder extends ClassVisitor { + private final Map<String, ConstantMethodHandle> constantMethodHandles; + private final Map<String, ConstantMethodType> constantMethodTypes; + + ConstantBuilder( + int api, + ClassVisitor cv, + Map<String, ConstantMethodHandle> constantMethodHandles, + Map<String, ConstantMethodType> constantMethodTypes) { + super(api, cv); + this.constantMethodHandles = constantMethodHandles; + this.constantMethodTypes = constantMethodTypes; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); + return new MethodVisitor(this.api, mv) { + @Override + public void visitMethodInsn( + int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) { + ConstantMethodHandle constantMethodHandle = constantMethodHandles.get(name); + if (constantMethodHandle != null) { + insertConstantMethodHandle(constantMethodHandle); + return; + } + ConstantMethodType constantMethodType = constantMethodTypes.get(name); + if (constantMethodType != null) { + insertConstantMethodType(constantMethodType); + return; + } + } + mv.visitMethodInsn(opcode, owner, name, desc, itf); + } + + private Type buildMethodType(Class<?> returnType, Class<?>[] parameterTypes) { + Type rType = Type.getType(returnType); + Type[] pTypes = new Type[parameterTypes.length]; + for (int i = 0; i < pTypes.length; ++i) { + pTypes[i] = Type.getType(parameterTypes[i]); + } + return Type.getMethodType(rType, pTypes); + } + + private int getHandleTag(int kind) { + switch (kind) { + case ConstantMethodHandle.STATIC_PUT: + return Opcodes.H_PUTSTATIC; + case ConstantMethodHandle.STATIC_GET: + return Opcodes.H_GETSTATIC; + case ConstantMethodHandle.INSTANCE_PUT: + return Opcodes.H_PUTFIELD; + case ConstantMethodHandle.INSTANCE_GET: + return Opcodes.H_GETFIELD; + case ConstantMethodHandle.INVOKE_STATIC: + return Opcodes.H_INVOKESTATIC; + case ConstantMethodHandle.INVOKE_VIRTUAL: + return Opcodes.H_INVOKEVIRTUAL; + case ConstantMethodHandle.INVOKE_SPECIAL: + return Opcodes.H_INVOKESPECIAL; + case ConstantMethodHandle.NEW_INVOKE_SPECIAL: + return Opcodes.H_NEWINVOKESPECIAL; + case ConstantMethodHandle.INVOKE_INTERFACE: + return Opcodes.H_INVOKEINTERFACE; + } + throw new Error("Unhandled kind " + kind); + } + + private void insertConstantMethodHandle(ConstantMethodHandle constantMethodHandle) { + Handle handle = + new Handle( + getHandleTag(constantMethodHandle.kind()), + constantMethodHandle.owner(), + constantMethodHandle.fieldOrMethodName(), + constantMethodHandle.descriptor(), + constantMethodHandle.ownerIsInterface()); + mv.visitLdcInsn(handle); + } + + private void insertConstantMethodType(ConstantMethodType constantMethodType) { + Type methodType = + buildMethodType( + constantMethodType.returnType(), + constantMethodType.parameterTypes()); + mv.visitLdcInsn(methodType); + } + }; + } + } + + private static void throwAnnotationError( + Method method, Class<?> annotationClass, String reason) { + StringBuilder sb = new StringBuilder(); + sb.append("Error in annotation ") + .append(annotationClass) + .append(" on method ") + .append(method) + .append(": ") + .append(reason); + throw new Error(sb.toString()); + } + + private static void checkMethodToBeReplaced( + Method method, Class<?> annotationClass, Class<?> returnType) { + final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE; + if ((method.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) { + throwAnnotationError(method, annotationClass, " method is not private and static"); + } + if (method.getTypeParameters().length != 0) { + throwAnnotationError(method, annotationClass, " method expects parameters"); + } + if (!method.getReturnType().equals(returnType)) { + throwAnnotationError(method, annotationClass, " wrong return type"); + } + } + + private static void transform(Path inputClassPath, Path outputClassPath) throws Throwable { + Path classLoadPath = inputClassPath.toAbsolutePath().getParent(); + URLClassLoader classLoader = + new URLClassLoader(new URL[] {classLoadPath.toUri().toURL()}, + ClassLoader.getSystemClassLoader()); + String inputClassName = inputClassPath.getFileName().toString().replace(".class", ""); + Class<?> inputClass = classLoader.loadClass(inputClassName); + + final Map<String, ConstantMethodHandle> constantMethodHandles = new HashMap<>(); + final Map<String, ConstantMethodType> constantMethodTypes = new HashMap<>(); + + for (Method m : inputClass.getDeclaredMethods()) { + ConstantMethodHandle constantMethodHandle = m.getAnnotation(ConstantMethodHandle.class); + if (constantMethodHandle != null) { + checkMethodToBeReplaced(m, ConstantMethodHandle.class, MethodHandle.class); + constantMethodHandles.put(m.getName(), constantMethodHandle); + continue; + } + + ConstantMethodType constantMethodType = m.getAnnotation(ConstantMethodType.class); + if (constantMethodType != null) { + checkMethodToBeReplaced(m, ConstantMethodType.class, MethodType.class); + constantMethodTypes.put(m.getName(), constantMethodType); + continue; + } + } + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + try (InputStream is = Files.newInputStream(inputClassPath)) { + ClassReader cr = new ClassReader(is); + ConstantBuilder cb = + new ConstantBuilder( + Opcodes.ASM7, cw, constantMethodHandles, constantMethodTypes); + cr.accept(cb, 0); + } + try (OutputStream os = Files.newOutputStream(outputClassPath)) { + os.write(cw.toByteArray()); + } + } + + public static void main(String[] args) throws Throwable { + transform(Paths.get(args[0]), Paths.get(args[1])); + } +} diff --git a/test/2277-methodhandle-invokeexact/src/A.java b/test/2277-methodhandle-invokeexact/src/A.java new file mode 100644 index 0000000000..b6fc317cb0 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/A.java @@ -0,0 +1,52 @@ +/* + * 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. + */ + +public class A extends B implements I { + public int field; + public void voidMethod() { + AbstractInvokeExactTest.STATUS = "A.voidMethod"; + } + + @Override + public void overrideMe() { + AbstractInvokeExactTest.STATUS = "A.overrideMe"; + } + + public void throwException() { + AbstractInvokeExactTest.STATUS = "A.throwException"; + throw new MyRuntimeException(); + } + + public double returnDouble() { + return 42.0d; + } + + public int returnInt() { + return 42; + } + + private int privateReturnInt() { + return 1042; + } + + public static String staticMethod(A a) { + return "staticMethod"; + } + + public static double staticMethod() { + return 41.0d; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java b/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java new file mode 100644 index 0000000000..569bff61b2 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java @@ -0,0 +1,270 @@ +/* + * 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); + } + + 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 fooBarDefinedInAbstract(); + public abstract MethodHandle fooBarImplDefinedInAbstract(); + public abstract MethodHandle fooBarNonDefault(); +} diff --git a/test/2277-methodhandle-invokeexact/src/B.java b/test/2277-methodhandle-invokeexact/src/B.java new file mode 100644 index 0000000000..f398f8ed8e --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/B.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +public class B { + private int privateReturnInt() { + return 9999; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/Bar.java b/test/2277-methodhandle-invokeexact/src/Bar.java new file mode 100644 index 0000000000..b278cd8c52 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Bar.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +interface Bar extends Foo { + @Override + default String defaultToOverride() { + return "Bar.defaultToOverride"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java b/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java new file mode 100644 index 0000000000..38b7eeb10f --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java @@ -0,0 +1,532 @@ +/* + * 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 annotations.ConstantMethodHandle; + +import java.lang.invoke.MethodHandle; + +public class ConstMethodHandleTest extends AbstractInvokeExactTest { + + @Override + void $noinline$privateMethods() throws Throwable { + // TODO(b/378051428): can't create const-method-handle targeting private methods of + // inner classes. + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "java/util/Optional", + fieldOrMethodName = "get", + descriptor = "()Ljava/lang/Object;") + private static MethodHandle constOptionalGet() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle optionalGet() { + return constOptionalGet(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "voidMethod", + descriptor = "()V") + private static MethodHandle constVoidMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle voidMethod() { + return constVoidMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "returnInt", + descriptor = "()I") + private static MethodHandle constReturnInt() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle returnInt() { + return constReturnInt(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "returnDouble", + descriptor = "()D") + private static MethodHandle constReturnDouble() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle returnDouble() { + return constReturnDouble(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "I", + fieldOrMethodName = "defaultMethod", + descriptor = "()V", + ownerIsInterface = true) + private static MethodHandle constInterfaceDefaultMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle interfaceDefaultMethod() { + return constInterfaceDefaultMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "I", + fieldOrMethodName = "overrideMe", + descriptor = "()V", + ownerIsInterface = true) + private static MethodHandle constOverwrittenInterfaceDefaultMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle overwrittenInterfaceDefaultMethod() { + return constOverwrittenInterfaceDefaultMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "A", + fieldOrMethodName = "throwException", + descriptor = "()V") + private static MethodHandle constExceptionThrowingMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle exceptionThrowingMethod() { + return constExceptionThrowingMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_STATIC, + owner = "A", + fieldOrMethodName = "staticMethod", + descriptor = "(LA;)Ljava/lang/String;") + private static MethodHandle constStaticMethod() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle staticMethod() { + return constStaticMethod(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(I)I") + private static MethodHandle constSumI() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sumI() { + return constSumI(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(II)I") + private static MethodHandle constSum2I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum2I() { + return constSum2I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(III)I") + private static MethodHandle constSum3I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum3I() { + return constSum3I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIII)I") + private static MethodHandle constSum4I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum4I() { + return constSum4I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIII)I") + private static MethodHandle constSum5I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum5I() { + return constSum5I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIII)I") + private static MethodHandle constSum6I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum6I() { + return constSum6I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIII)I") + private static MethodHandle constSum7I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum7I() { + return constSum7I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIII)I") + private static MethodHandle constSum8I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum8I() { + return constSum8I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIIII)I") + private static MethodHandle constSum9I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum9I() { + return constSum9I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IIIIIIIIII)I") + private static MethodHandle constSum10I() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum10I() { + return constSum10I(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJ)J") + private static MethodHandle constSumIJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sumIJ() { + return constSumIJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJ)J") + private static MethodHandle constSum2IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum2IJ() { + return constSum2IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJ)J") + private static MethodHandle constSum3IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum3IJ() { + return constSum3IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJIJ)J") + private static MethodHandle constSum4IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum4IJ() { + return constSum4IJ(); + } + + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "Sums", + fieldOrMethodName = "sum", + descriptor = "(IJIJIJIJIJ)J") + private static MethodHandle constSum5IJ() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle sum5IJ() { + return constSum5IJ(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooNonDefault() { + return constFooNonDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplNonDefault() { + return constFooBarImplNonDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Bar", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constBarDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle barDefault() { + return constBarDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooDefault() { + return constFooDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "defaultToOverride", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplDefault() { + return constFooBarImplDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Foo", + fieldOrMethodName = "nonOverriddenDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constFooNonOverriddenDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooNonOverriddenDefault() { + return constFooNonOverriddenDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_INTERFACE, + owner = "Bar", + fieldOrMethodName = "nonOverriddenDefault", + descriptor = "()Ljava/lang/String;", + ownerIsInterface = true) + private static MethodHandle constBarNonOverriddenDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle barNonOverriddenDefault() { + return constBarNonOverriddenDefault(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBar", + fieldOrMethodName = "definedInAbstract", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarDefinedInAbstract() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarDefinedInAbstract() { + return constFooBarDefinedInAbstract(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBarImpl", + fieldOrMethodName = "definedInAbstract", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarImplDefinedInAbstract() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarImplDefinedInAbstract() { + return constFooBarImplDefinedInAbstract(); + } + + @ConstantMethodHandle( + kind = ConstantMethodHandle.INVOKE_VIRTUAL, + owner = "FooBar", + fieldOrMethodName = "nonDefault", + descriptor = "()Ljava/lang/String;") + private static MethodHandle constFooBarNonDefault() { + unreachable("should be replaced by const-method-handle"); + return null; + } + + @Override + public MethodHandle fooBarNonDefault() { + return constFooBarNonDefault(); + } + +} diff --git a/test/2277-methodhandle-invokeexact/src/Foo.java b/test/2277-methodhandle-invokeexact/src/Foo.java new file mode 100644 index 0000000000..2a79b2175d --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Foo.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +interface Foo { + default String defaultToOverride() { + return "Foo.defaultToOverride"; + } + + default String nonOverriddenDefault() { + return "Foo.nonOverriddenDefault"; + } + + String nonDefault(); +} diff --git a/test/2277-methodhandle-invokeexact/src/FooBar.java b/test/2277-methodhandle-invokeexact/src/FooBar.java new file mode 100644 index 0000000000..46372e0f8e --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/FooBar.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +abstract class FooBar implements Foo, Bar { + public String definedInAbstract() { + return "FooBar.definedInAbstract"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/FooBarImpl.java b/test/2277-methodhandle-invokeexact/src/FooBarImpl.java new file mode 100644 index 0000000000..19d9a0ae64 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/FooBarImpl.java @@ -0,0 +1,22 @@ +/* + * 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. + */ + +class FooBarImpl extends FooBar { + @Override + public String nonDefault() { + return "FooBarImpl.nonDefault"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/I.java b/test/2277-methodhandle-invokeexact/src/I.java new file mode 100644 index 0000000000..eeb41ce2a9 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/I.java @@ -0,0 +1,29 @@ +/* + * 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. + */ + +public interface I { + public default void defaultMethod() { + AbstractInvokeExactTest.STATUS = "I.defaultMethod"; + } + + public default void overrideMe() { + throw new RuntimeException("should be overwritten"); + } + + private String innerPrivateMethod() { + return "boo"; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/JavaApiTest.java b/test/2277-methodhandle-invokeexact/src/JavaApiTest.java new file mode 100644 index 0000000000..77b4bc0c73 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/JavaApiTest.java @@ -0,0 +1,351 @@ +/* + * 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 static java.lang.invoke.MethodType.methodType; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.util.Arrays; +import java.util.Optional; + +public class JavaApiTest extends AbstractInvokeExactTest { + + private static final MethodHandle OPTIONAL_GET; + + private static final MethodHandle VOID_METHOD; + private static final MethodHandle RETURN_INT; + private static final MethodHandle RETURN_DOUBLE; + private static final MethodHandle PRIVATE_INTERFACE_METHOD; + private static final MethodHandle B_PRIVATE_RETURN_INT; + private static final MethodHandle A_PRIVATE_RETURN_INT; + private static final MethodHandle STATIC_METHOD; + private static final MethodHandle EXCEPTION_THROWING_METHOD; + private static final MethodHandle INTERFACE_DEFAULT_METHOD; + private static final MethodHandle OVERWRITTEN_INTERFACE_DEFAULT_METHOD; + + private static final MethodHandle SUM_I; + private static final MethodHandle SUM_2I; + private static final MethodHandle SUM_3I; + private static final MethodHandle SUM_4I; + private static final MethodHandle SUM_5I; + private static final MethodHandle SUM_6I; + private static final MethodHandle SUM_7I; + private static final MethodHandle SUM_8I; + private static final MethodHandle SUM_9I; + private static final MethodHandle SUM_10I; + + private static final MethodHandle SUM_IJ; + private static final MethodHandle SUM_2IJ; + private static final MethodHandle SUM_3IJ; + private static final MethodHandle SUM_4IJ; + private static final MethodHandle SUM_5IJ; + + private static final MethodHandle FOO_NONDEFAULT; + private static final MethodHandle FOOBARIMPL_NONDEFAULT; + private static final MethodHandle FOO_DEFAULT; + private static final MethodHandle BAR_DEFAULT; + private static final MethodHandle FOOBAR_DEFINEDINABSTRACT; + private static final MethodHandle FOOBAR_NONDEFAULT; + private static final MethodHandle FOOBARIMPL_DEFINEDINABSTRACT; + private static final MethodHandle FOOBARIMPL_DEFAULT; + private static final MethodHandle FOO_NONOVERRIDDEN_DEFAULT; + private static final MethodHandle BAR_NONOVERRIDDEN_DEFAULT; + + static { + try { + OPTIONAL_GET = MethodHandles.lookup() + .findVirtual(Optional.class, "get", methodType(Object.class)); + + VOID_METHOD = MethodHandles.lookup() + .findVirtual(A.class, "voidMethod", methodType(void.class)); + RETURN_DOUBLE = MethodHandles.lookup() + .findVirtual(A.class, "returnDouble", methodType(double.class)); + RETURN_INT = MethodHandles.lookup() + .findVirtual(A.class, "returnInt", methodType(int.class)); + PRIVATE_INTERFACE_METHOD = MethodHandles.privateLookupIn(I.class, MethodHandles.lookup()) + .findVirtual(I.class, "innerPrivateMethod", methodType(String.class)); + A_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(A.class, MethodHandles.lookup()) + .findVirtual(A.class, "privateReturnInt", methodType(int.class)); + B_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(B.class, MethodHandles.lookup()) + .findVirtual(B.class, "privateReturnInt", methodType(int.class)); + STATIC_METHOD = MethodHandles.lookup() + .findStatic(A.class, "staticMethod", methodType(String.class, A.class)); + EXCEPTION_THROWING_METHOD = MethodHandles.lookup() + .findVirtual(A.class, "throwException", methodType(void.class)); + INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() + .findVirtual(I.class, "defaultMethod", methodType(void.class)); + OVERWRITTEN_INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() + .findVirtual(I.class, "overrideMe", methodType(void.class)); + + SUM_I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(1, int.class))); + SUM_2I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(2, int.class))); + SUM_3I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(3, int.class))); + SUM_4I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(4, int.class))); + SUM_5I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(5, int.class))); + SUM_6I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(6, int.class))); + SUM_7I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(7, int.class))); + SUM_8I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(8, int.class))); + SUM_9I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(9, int.class))); + SUM_10I = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class))); + + SUM_IJ = MethodHandles.lookup() + .findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class)); + SUM_2IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(2, int.class, long.class))); + SUM_3IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(3, int.class, long.class))); + SUM_4IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(4, int.class, long.class))); + SUM_5IJ = MethodHandles.lookup() + .findVirtual(Sums.class, + "sum", + methodType(long.class, repeat(5, int.class, long.class))); + + FOO_NONDEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "nonDefault", methodType(String.class)); + FOOBARIMPL_NONDEFAULT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "nonDefault", methodType(String.class)); + FOO_DEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "defaultToOverride", methodType(String.class)); + BAR_DEFAULT = MethodHandles.lookup() + .findVirtual(Bar.class, "defaultToOverride", methodType(String.class)); + FOOBAR_DEFINEDINABSTRACT = MethodHandles.lookup() + .findVirtual(FooBar.class, "definedInAbstract", methodType(String.class)); + FOOBAR_NONDEFAULT = MethodHandles.lookup() + .findVirtual(FooBar.class, "nonDefault", methodType(String.class)); + FOOBARIMPL_DEFINEDINABSTRACT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "definedInAbstract", methodType(String.class)); + FOOBARIMPL_DEFAULT = MethodHandles.lookup() + .findVirtual(FooBarImpl.class, "defaultToOverride", methodType(String.class)); + FOO_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() + .findVirtual(Foo.class, "nonOverriddenDefault", methodType(String.class)); + BAR_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() + .findVirtual(Bar.class, "nonOverriddenDefault", methodType(String.class)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Class<?>[] repeat(int times, Class<?> clazz) { + Class<?>[] classes = new Class<?>[times]; + Arrays.fill(classes, clazz); + return classes; + } + + private static Class<?>[] repeat(int times, Class<?> first, Class<?> second) { + Class<?>[] classes = new Class<?>[times * 2]; + for (int i = 0; i < 2 * times;) { + classes[i++] = first; + classes[i++] = second; + } + return classes; + } + + @Override + void $noinline$privateMethods() throws Throwable { + assertEquals("boo", (String) PRIVATE_INTERFACE_METHOD.invokeExact((I) new A())); + + int privateIntA = (int) A_PRIVATE_RETURN_INT.invokeExact(new A()); + assertEquals(1042, privateIntA); + + int privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact(new B()); + assertEquals(9999, privateIntB); + + privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact((B) new A()); + assertEquals(9999, privateIntB); + } + + @Override + public MethodHandle voidMethod() { + return VOID_METHOD; + } + + @Override + public MethodHandle returnDouble() { + return RETURN_DOUBLE; + } + + @Override + public MethodHandle returnInt() { + return RETURN_INT; + } + + @Override + public MethodHandle interfaceDefaultMethod() { + return INTERFACE_DEFAULT_METHOD; + } + + @Override + public MethodHandle overwrittenInterfaceDefaultMethod() { + return OVERWRITTEN_INTERFACE_DEFAULT_METHOD; + } + + @Override + public MethodHandle exceptionThrowingMethod() { + return EXCEPTION_THROWING_METHOD; + } + + @Override + public MethodHandle staticMethod() { + return STATIC_METHOD; + } + + @Override + public MethodHandle sumI() { + return SUM_I; + } + + @Override + public MethodHandle sum2I() { + return SUM_2I; + } + + @Override + public MethodHandle sum3I() { + return SUM_3I; + } + + @Override + public MethodHandle sum4I() { + return SUM_4I; + } + + @Override + public MethodHandle sum5I() { + return SUM_5I; + } + + @Override + public MethodHandle sum6I() { + return SUM_6I; + } + + @Override + public MethodHandle sum7I() { + return SUM_7I; + } + + @Override + public MethodHandle sum8I() { + return SUM_8I; + } + + @Override + public MethodHandle sum9I() { + return SUM_9I; + } + + @Override + public MethodHandle sum10I() { + return SUM_10I; + } + + @Override + public MethodHandle sumIJ() { + return SUM_IJ; + } + + @Override + public MethodHandle sum2IJ() { + return SUM_2IJ; + } + + @Override + public MethodHandle sum3IJ() { + return SUM_3IJ; + } + + @Override + public MethodHandle sum4IJ() { + return SUM_4IJ; + } + + @Override + public MethodHandle sum5IJ() { + return SUM_5IJ; + } + + @Override + public MethodHandle fooNonDefault() { + return FOO_NONDEFAULT; + } + + @Override + public MethodHandle fooBarImplNonDefault() { + return FOOBARIMPL_NONDEFAULT; + } + + @Override + public MethodHandle barDefault() { + return BAR_DEFAULT; + } + + @Override + public MethodHandle fooDefault() { + return FOO_DEFAULT; + } + + @Override + public MethodHandle fooBarImplDefault() { + return FOOBARIMPL_DEFAULT; + } + + @Override + public MethodHandle fooNonOverriddenDefault() { + return FOO_NONOVERRIDDEN_DEFAULT; + } + + @Override + public MethodHandle barNonOverriddenDefault() { + return BAR_NONOVERRIDDEN_DEFAULT; + } + + @Override + public MethodHandle fooBarDefinedInAbstract() { + return FOOBAR_DEFINEDINABSTRACT; + } + + @Override + public MethodHandle fooBarImplDefinedInAbstract() { + return FOOBARIMPL_DEFINEDINABSTRACT; + } + + @Override + public MethodHandle fooBarNonDefault() { + return FOOBAR_NONDEFAULT; + } + + @Override + public MethodHandle optionalGet() { + return OPTIONAL_GET; + } +} diff --git a/test/2277-methodhandle-invokeexact/src/Main.java b/test/2277-methodhandle-invokeexact/src/Main.java index a34e5eef60..5d4bfad519 100644 --- a/test/2277-methodhandle-invokeexact/src/Main.java +++ b/test/2277-methodhandle-invokeexact/src/Main.java @@ -14,539 +14,39 @@ * limitations under the License. */ - import static java.lang.invoke.MethodType.methodType; - -import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandle; import java.lang.invoke.WrongMethodTypeException; -import java.util.Arrays; import java.util.Objects; import java.util.Optional; public class Main { - private static String STATUS = ""; - - public static void main(String[] args) throws Throwable { - $noinline$testNoArgsCalls(); - $noinline$testMethodHandleFromOtherDex(); - Multi.$noinline$testMHFromMain(OPTIONAL_GET); - $noinline$testWithArgs(); - $noinline$nullchecks(); - $noinline$interfaceChecks(); - $noinline$abstractClass(); - } - - private static void $noinline$nullchecks() throws Throwable { - try { - VOID_METHOD.invokeExact((A) null); - unreachable("Receiver is null, should throw NPE"); - } catch (NullPointerException expected) {} - - try { - VOID_METHOD.invokeExact((Main) null); - unreachable("Should throw WMTE: input is of wrong type"); - } catch (WrongMethodTypeException expected) {} - - try { - INTERFACE_DEFAULT_METHOD.invokeExact((I) null); - unreachable("Receiver is null, should throw NPE"); - } catch (NullPointerException expected) {} - - try { - INTERFACE_DEFAULT_METHOD.invokeExact((A) null); - unreachable("Should throw WMTE: input is of wrong type"); - } catch (WrongMethodTypeException expected) {} - - try { - MethodHandle mh = null; - mh.invokeExact(); - unreachable("MethodHandle object is null, should throw NPE"); - } catch (NullPointerException expected) {} - } - - private static void $noinline$testMethodHandleFromOtherDex() throws Throwable { - MethodHandle mh = Multi.$noinline$getMethodHandle(); - Optional<String> nonEmpty = Optional.<String>of("hello"); - Object returnedObject = mh.invokeExact(nonEmpty); - assertEquals("hello", returnedObject); - - try { - mh.invokeExact(nonEmpty); - unreachable("mh.type() is (Optional)Object, but callsite is (Optional)V"); - } catch (WrongMethodTypeException expected) {} - } - - private static void $noinline$testNoArgsCalls() throws Throwable { - VOID_METHOD.invokeExact(new A()); - assertEquals("A.voidMethod", STATUS); - - int returnedInt = (int) RETURN_INT.invokeExact(new A()); - assertEquals(42, returnedInt); - - double returnedDouble = (double) RETURN_DOUBLE.invokeExact(new A()); - assertEquals(42.0d, returnedDouble); - - try { - INTERFACE_DEFAULT_METHOD.invokeExact(new A()); - unreachable("MethodHandle's type is (Main$I)V, but callsite is (Main$A)V"); - } catch (WrongMethodTypeException expected) {} - - INTERFACE_DEFAULT_METHOD.invokeExact((I) new A()); - assertEquals("I.defaultMethod", STATUS); - - OVERWRITTEN_INTERFACE_DEFAULT_METHOD.invokeExact((I) new A()); - assertEquals("A.overrideMe", STATUS); - - assertEquals("boo", (String) PRIVATE_INTERFACE_METHOD.invokeExact((I) new A())); - - int privateIntA = (int) A_PRIVATE_RETURN_INT.invokeExact(new A()); - assertEquals(1042, privateIntA); - - int privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact(new B()); - assertEquals(9999, privateIntB); - - privateIntB = (int) B_PRIVATE_RETURN_INT.invokeExact((B) new A()); - assertEquals(9999, privateIntB); - - try { - EXCEPTION_THROWING_METHOD.invokeExact(new A()); - unreachable("Target method always throws"); - } catch (MyRuntimeException expected) { - assertEquals("A.throwException", STATUS); - } - - try { - RETURN_INT.invokeExact(new A()); - unreachable("MethodHandle's type is (Main$A)I, but callsite type is (Main$A)V"); - } catch (WrongMethodTypeException expected) {} - - String returnedString = (String) STATIC_METHOD.invokeExact(new A()); - assertEquals("staticMethod", returnedString); - } - - private static void $noinline$testWithArgs() throws Throwable { - int sum = (int) SUM_I.invokeExact(new Sums(), 1); - assertEquals(1, sum); - - sum = (int) SUM_2I.invokeExact(new Sums(), 1, 2); - assertEquals(3, sum); - - sum = (int) SUM_3I.invokeExact(new Sums(), 1, 2, 3); - assertEquals(6, sum); - - sum = (int) SUM_4I.invokeExact(new Sums(), 1, 2, 3, 4); - assertEquals(10, sum); + public static void main(String[] args) throws Throwable { + $noinline$testMethodHandleFromOtherDex(); - sum = (int) SUM_5I.invokeExact(new Sums(), 1, 2, 3, 4, 5); - assertEquals(15, sum); + new ConstMethodHandleTest().runAll(); + new JavaApiTest().runAll(); + } - sum = (int) SUM_6I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6); - assertEquals(21, sum); + private static void $noinline$testMethodHandleFromOtherDex() throws Throwable { + MethodHandle mh = Multi.$noinline$getMethodHandle(); + Optional<String> nonEmpty = Optional.<String>of("hello"); + Object returnedObject = mh.invokeExact(nonEmpty); + assertEquals("hello", returnedObject); - sum = (int) SUM_7I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7); - assertEquals(28, sum); - - sum = (int) SUM_8I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8); - assertEquals(36, sum); - - sum = (int) SUM_9I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9); - assertEquals(45, sum); - - sum = (int) SUM_10I.invokeExact(new Sums(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - assertEquals(55, sum); - - long lsum = (long) SUM_IJ.invokeExact(new Sums(), 1, 2L); - assertEquals(3L, lsum); - - lsum = (long) SUM_2IJ.invokeExact(new Sums(), 1, 2L, 3, 4L); - assertEquals(10L, lsum); - - lsum = (long) SUM_3IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L); - assertEquals(21L, lsum); - - lsum = (long) SUM_4IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L); - assertEquals(36L, lsum); - - lsum = (long) SUM_5IJ.invokeExact(new Sums(), 1, 2L, 3, 4L, 5, 6L, 7, 8L, 9, 10L); - assertEquals(55L, lsum); - } - - private static void $noinline$interfaceChecks() throws Throwable { - FooBarImpl instance = new FooBarImpl(); - - String result = null; - result = (String) FOO_NONDEFAULT.invokeExact((Foo) instance); - assertEquals("FooBarImpl.nonDefault", result); - - result = (String) FOOBARIMPL_NONDEFAULT.invokeExact(instance); - assertEquals("FooBarImpl.nonDefault", result); - - result = (String) BAR_DEFAULT.invokeExact((Bar) instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOO_DEFAULT.invokeExact((Foo) instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOOBARIMPL_DEFAULT.invokeExact(instance); - assertEquals("Bar.defaultToOverride", result); - - result = (String) FOO_NONOVERRIDDEN_DEFAULT.invokeExact((Foo) instance); - assertEquals("Foo.nonOverriddenDefault", result); - - result = (String) BAR_NONOVERRIDDEN_DEFAULT.invokeExact((Bar) instance); - assertEquals("Foo.nonOverriddenDefault", result); - } - - private static void $noinline$abstractClass() throws Throwable { - FooBarImpl instance = new FooBarImpl(); - - String result = null; - result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact((FooBar) instance); - assertEquals("FooBar.definedInAbstract", result); - - result = (String) FOOBARIMPL_DEFINEDINABSTRACT.invokeExact(instance); - assertEquals("FooBar.definedInAbstract", result); - - FooBar fooBar = new FooBar() { - @Override - public String nonDefault() { - return "anonymous.nonDefault"; - } - }; - - result = (String) FOOBAR_DEFINEDINABSTRACT.invokeExact(fooBar); - assertEquals("FooBar.definedInAbstract", result); - - result = (String) FOOBAR_NONDEFAULT.invokeExact(fooBar); - assertEquals("anonymous.nonDefault", result); - } - - private 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); - } - } + try { + mh.invokeExact(nonEmpty); + unreachable("mh.type() is (Optional)Object, but callsite is (Optional)V"); + } catch (WrongMethodTypeException expected) {} + } - private static void assertEquals(long expected, long actual) { - if (expected != actual) { - throw new AssertionError("Expected: " + expected + ", got: " + actual); - } + private static void assertEquals(Object expected, Object actual) { + if (!Objects.equals(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); - } - } - - private static void unreachable(String msg) { - throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg); - } - - private static final MethodHandle VOID_METHOD; - private static final MethodHandle RETURN_DOUBLE; - private static final MethodHandle RETURN_INT; - private static final MethodHandle PRIVATE_INTERFACE_METHOD; - private static final MethodHandle B_PRIVATE_RETURN_INT; - private static final MethodHandle A_PRIVATE_RETURN_INT; - private static final MethodHandle STATIC_METHOD; - private static final MethodHandle EXCEPTION_THROWING_METHOD; - private static final MethodHandle INTERFACE_DEFAULT_METHOD; - private static final MethodHandle OVERWRITTEN_INTERFACE_DEFAULT_METHOD; - private static final MethodHandle OPTIONAL_GET; - - private static final MethodHandle SUM_I; - private static final MethodHandle SUM_2I; - private static final MethodHandle SUM_3I; - private static final MethodHandle SUM_4I; - private static final MethodHandle SUM_5I; - private static final MethodHandle SUM_6I; - private static final MethodHandle SUM_7I; - private static final MethodHandle SUM_8I; - private static final MethodHandle SUM_9I; - private static final MethodHandle SUM_10I; - - private static final MethodHandle SUM_IJ; - private static final MethodHandle SUM_2IJ; - private static final MethodHandle SUM_3IJ; - private static final MethodHandle SUM_4IJ; - private static final MethodHandle SUM_5IJ; - - private static final MethodHandle FOO_NONDEFAULT; - private static final MethodHandle FOOBARIMPL_NONDEFAULT; - private static final MethodHandle FOO_DEFAULT; - private static final MethodHandle BAR_DEFAULT; - private static final MethodHandle FOOBAR_DEFINEDINABSTRACT; - private static final MethodHandle FOOBAR_NONDEFAULT; - private static final MethodHandle FOOBARIMPL_DEFINEDINABSTRACT; - private static final MethodHandle FOOBARIMPL_DEFAULT; - private static final MethodHandle FOO_NONOVERRIDDEN_DEFAULT; - private static final MethodHandle BAR_NONOVERRIDDEN_DEFAULT; - - static { - try { - VOID_METHOD = MethodHandles.lookup() - .findVirtual(A.class, "voidMethod", methodType(void.class)); - RETURN_DOUBLE = MethodHandles.lookup() - .findVirtual(A.class, "returnDouble", methodType(double.class)); - RETURN_INT = MethodHandles.lookup() - .findVirtual(A.class, "returnInt", methodType(int.class)); - PRIVATE_INTERFACE_METHOD = MethodHandles.privateLookupIn(I.class, MethodHandles.lookup()) - .findVirtual(I.class, "innerPrivateMethod", methodType(String.class)); - A_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(A.class, MethodHandles.lookup()) - .findVirtual(A.class, "privateReturnInt", methodType(int.class)); - B_PRIVATE_RETURN_INT = MethodHandles.privateLookupIn(B.class, MethodHandles.lookup()) - .findVirtual(B.class, "privateReturnInt", methodType(int.class)); - STATIC_METHOD = MethodHandles.lookup() - .findStatic(A.class, "staticMethod", methodType(String.class, A.class)); - EXCEPTION_THROWING_METHOD = MethodHandles.lookup() - .findVirtual(A.class, "throwException", methodType(void.class)); - INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() - .findVirtual(I.class, "defaultMethod", methodType(void.class)); - OVERWRITTEN_INTERFACE_DEFAULT_METHOD = MethodHandles.lookup() - .findVirtual(I.class, "overrideMe", methodType(void.class)); - OPTIONAL_GET = MethodHandles.lookup() - .findVirtual(Optional.class, "get", methodType(Object.class)); - - SUM_I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(1, int.class))); - SUM_2I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(2, int.class))); - SUM_3I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(3, int.class))); - SUM_4I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(4, int.class))); - SUM_5I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(5, int.class))); - SUM_6I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(6, int.class))); - SUM_7I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(7, int.class))); - SUM_8I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(8, int.class))); - SUM_9I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(9, int.class))); - SUM_10I = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(int.class, repeat(10, int.class))); - - SUM_IJ = MethodHandles.lookup() - .findVirtual(Sums.class, "sum", methodType(long.class, int.class, long.class)); - SUM_2IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(2, int.class, long.class))); - SUM_3IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(3, int.class, long.class))); - SUM_4IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(4, int.class, long.class))); - SUM_5IJ = MethodHandles.lookup() - .findVirtual(Sums.class, - "sum", - methodType(long.class, repeat(5, int.class, long.class))); - - FOO_NONDEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "nonDefault", methodType(String.class)); - FOOBARIMPL_NONDEFAULT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "nonDefault", methodType(String.class)); - FOO_DEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "defaultToOverride", methodType(String.class)); - BAR_DEFAULT = MethodHandles.lookup() - .findVirtual(Bar.class, "defaultToOverride", methodType(String.class)); - FOOBAR_DEFINEDINABSTRACT = MethodHandles.lookup() - .findVirtual(FooBar.class, "definedInAbstract", methodType(String.class)); - FOOBAR_NONDEFAULT = MethodHandles.lookup() - .findVirtual(FooBar.class, "nonDefault", methodType(String.class)); - FOOBARIMPL_DEFINEDINABSTRACT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "definedInAbstract", methodType(String.class)); - FOOBARIMPL_DEFAULT = MethodHandles.lookup() - .findVirtual(FooBarImpl.class, "defaultToOverride", methodType(String.class)); - FOO_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() - .findVirtual(Foo.class, "nonOverriddenDefault", methodType(String.class)); - BAR_NONOVERRIDDEN_DEFAULT = MethodHandles.lookup() - .findVirtual(Bar.class, "nonOverriddenDefault", methodType(String.class)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - private static Class<?>[] repeat(int times, Class<?> clazz) { - Class<?>[] classes = new Class<?>[times]; - Arrays.fill(classes, clazz); - return classes; - } - - private static Class<?>[] repeat(int times, Class<?> first, Class<?> second) { - Class<?>[] classes = new Class<?>[times * 2]; - for (int i = 0; i < 2 * times;) { - classes[i++] = first; - classes[i++] = second; - } - return classes; - } - - static interface Foo { - default String defaultToOverride() { - return "Foo.defaultToOverride"; - } - - default String nonOverriddenDefault() { - return "Foo.nonOverriddenDefault"; - } - - String nonDefault(); - } - - static interface Bar extends Foo { - @Override - default String defaultToOverride() { - return "Bar.defaultToOverride"; - } - } - - static abstract class FooBar implements Foo, Bar { - public String definedInAbstract() { - return "FooBar.definedInAbstract"; - } - } - - static class FooBarImpl extends FooBar { - @Override - public String nonDefault() { - return "FooBarImpl.nonDefault"; - } - } - - static class MyRuntimeException extends RuntimeException {} - - static interface I { - public default void defaultMethod() { - STATUS = "I.defaultMethod"; - } - - public default void overrideMe() { - throw new RuntimeException("should be overwritten"); - } - - private String innerPrivateMethod() { - return "boo"; - } - } - - static class A extends B implements I { - public int field; - public void voidMethod() { - STATUS = "A.voidMethod"; - } - - @Override - public void overrideMe() { - STATUS = "A.overrideMe"; - } - - public void throwException() { - STATUS = "A.throwException"; - throw new MyRuntimeException(); - } - - public double returnDouble() { - return 42.0d; - } - - public int returnInt() { - return 42; - } - - private int privateReturnInt() { - return 1042; - } - - public static String staticMethod(A a) { - return "staticMethod"; - } - - public static double staticMethod() { - return 41.0d; - } - } - - static class B { - private int privateReturnInt() { - return 9999; - } - } - - static class Sums { - public int sum(int a) { - return a; - } - - public int sum(int a1, int a2) { - return a1 + a2; - } - - public int sum(int a1, int a2, int a3) { - return a1 + a2 + a3; - } - - public int sum(int a1, int a2, int a3, int a4) { - return a1 + a2 + a3 + a4; - } - - public int sum(int a1, int a2, int a3, int a4, int a5) { - return a1 + a2 + a3 + a4 + a5; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6) { - return a1 + a2 + a3 + a4 + a5 + a6; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; - } - - public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, - int a10) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; - } - - public long sum(int a1, long a2) { - return a1 + a2; - } - - public long sum(int a1, long a2, int a3, long a4) { - return a1 + a2 + a3 + a4; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6) { - return a1 + a2 + a3 + a4 + a5 + a6; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; - } - - public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8, int a9, - long a10) { - return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; - } - } + private static void unreachable(String msg) { + throw new AssertionError("Unexpectedly reached this point, but shouldn't: " + msg); + } } diff --git a/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java b/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java new file mode 100644 index 0000000000..623843d4e6 --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/MyRuntimeException.java @@ -0,0 +1,17 @@ +/* + * 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. + */ + +public class MyRuntimeException extends RuntimeException {} diff --git a/test/2277-methodhandle-invokeexact/src/Sums.java b/test/2277-methodhandle-invokeexact/src/Sums.java new file mode 100644 index 0000000000..cb5d852d9d --- /dev/null +++ b/test/2277-methodhandle-invokeexact/src/Sums.java @@ -0,0 +1,78 @@ +/* + * 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. + */ +public class Sums { + public int sum(int a) { + return a; + } + + public int sum(int a1, int a2) { + return a1 + a2; + } + + public int sum(int a1, int a2, int a3) { + return a1 + a2 + a3; + } + + public int sum(int a1, int a2, int a3, int a4) { + return a1 + a2 + a3 + a4; + } + + public int sum(int a1, int a2, int a3, int a4, int a5) { + return a1 + a2 + a3 + a4 + a5; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6) { + return a1 + a2 + a3 + a4 + a5 + a6; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9; + } + + public int sum(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, + int a10) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; + } + + public long sum(int a1, long a2) { + return a1 + a2; + } + + public long sum(int a1, long a2, int a3, long a4) { + return a1 + a2 + a3 + a4; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6) { + return a1 + a2 + a3 + a4 + a5 + a6; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8; + } + + public long sum(int a1, long a2, int a3, long a4, int a5, long a6, int a7, long a8, int a9, + long a10) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10; + } +} |