summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Almaz Mingaleev <mingaleev@google.com> 2024-11-07 10:01:49 +0000
committer Almaz Mingaleev <mingaleev@google.com> 2024-11-12 14:27:31 +0000
commit3051a5e03f32ca7aaae6041025ec60f90d41e34c (patch)
tree6d253e5c533e032ef1a43a914410d32f7e31081a
parent20cdc427d5f5875c2d58f9fde775957ad2e28cfd (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
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc11
-rw-r--r--test/2277-methodhandle-invokeexact/build.py19
-rwxr-xr-xtest/2277-methodhandle-invokeexact/generate-sources29
-rwxr-xr-xtest/2277-methodhandle-invokeexact/javac_post.sh30
-rw-r--r--test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodHandle.java58
-rw-r--r--test/2277-methodhandle-invokeexact/src-util/annotations/ConstantMethodType.java38
-rw-r--r--test/2277-methodhandle-invokeexact/src-util/transformer/ConstantTransformer.java229
-rw-r--r--test/2277-methodhandle-invokeexact/src/A.java52
-rw-r--r--test/2277-methodhandle-invokeexact/src/AbstractInvokeExactTest.java270
-rw-r--r--test/2277-methodhandle-invokeexact/src/B.java21
-rw-r--r--test/2277-methodhandle-invokeexact/src/Bar.java22
-rw-r--r--test/2277-methodhandle-invokeexact/src/ConstMethodHandleTest.java532
-rw-r--r--test/2277-methodhandle-invokeexact/src/Foo.java27
-rw-r--r--test/2277-methodhandle-invokeexact/src/FooBar.java21
-rw-r--r--test/2277-methodhandle-invokeexact/src/FooBarImpl.java22
-rw-r--r--test/2277-methodhandle-invokeexact/src/I.java29
-rw-r--r--test/2277-methodhandle-invokeexact/src/JavaApiTest.java351
-rw-r--r--test/2277-methodhandle-invokeexact/src/Main.java544
-rw-r--r--test/2277-methodhandle-invokeexact/src/MyRuntimeException.java17
-rw-r--r--test/2277-methodhandle-invokeexact/src/Sums.java78
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;
+ }
+}