/* * Copyright (C) 2018 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.BootstrapMethod; import annotations.CalledByIndy; import annotations.Constant; import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.Arrays; public class TestVariableArityLinkerMethod extends TestBase { private static void printBsmArgs(String method, Object... args) { System.out.print(method); System.out.print("("); for (int i = 0; i < args.length; ++i) { if (i != 0) { System.out.print(", "); } if (args[i] != null && args[i].getClass().isArray()) { Object array = args[i]; if (array.getClass() == int[].class) { System.out.print(Arrays.toString((int[]) array)); } else if (array.getClass() == long[].class) { System.out.print(Arrays.toString((long[]) array)); } else if (array.getClass() == float[].class) { System.out.print(Arrays.toString((float[]) array)); } else if (array.getClass() == double[].class) { System.out.print(Arrays.toString((double[]) array)); } else { System.out.print(Arrays.toString((Object[]) array)); } } else { System.out.print(args[i]); } } System.out.println(");"); } private static CallSite bsmWithStringArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, String... arityArgs) throws Throwable { printBsmArgs("bsmWithStringArray", lookup, methodName, methodType, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, String[].class } ), fieldOrMethodName = "methodA", constantArgumentsForBootstrapMethod = { @Constant(stringValue = "Aachen"), @Constant(stringValue = "Aalborg"), @Constant(stringValue = "Aalto") } ) private static void methodA() { System.out.println("methodA"); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, String[].class } ), fieldOrMethodName = "methodB", constantArgumentsForBootstrapMethod = {@Constant(stringValue = "barium")} ) private static void methodB() { System.out.println("methodB"); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, String[].class } ), fieldOrMethodName = "methodC" ) private static void methodC() { System.out.println("methodC"); } private static CallSite bsmWithIntAndStringArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, int extraInt, String... extraArityArgs) throws Throwable { printBsmArgs( "bsmWithIntAndStringArray", lookup, methodName, methodType, extraInt, extraArityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithIntAndStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String[].class } ), fieldOrMethodName = "methodD", constantArgumentsForBootstrapMethod = { @Constant(intValue = 101), @Constant(stringValue = "zoo"), @Constant(stringValue = "zoogene"), @Constant(stringValue = "zoogenic") } ) private static void methodD() { System.out.println("methodD"); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithIntAndStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String[].class } ), fieldOrMethodName = "methodE", constantArgumentsForBootstrapMethod = { @Constant(intValue = 102), @Constant(stringValue = "zonic") } ) private static void methodE() { System.out.println("methodE"); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithIntAndStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String[].class } ), fieldOrMethodName = "methodF", constantArgumentsForBootstrapMethod = {@Constant(intValue = 103)} ) private static void methodF() { System.out.println("methodF"); } private static CallSite bsmWithLongAndIntArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, long extraArg, int... arityArgs) throws Throwable { printBsmArgs("bsmWithLongAndIntArray", lookup, methodName, methodType, extraArg, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithLongAndIntArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, long.class, int[].class } ), fieldOrMethodName = "methodG", constantArgumentsForBootstrapMethod = { @Constant(longValue = 0x123456789abcdefl), @Constant(intValue = +1), @Constant(intValue = -1), @Constant(intValue = +2), @Constant(intValue = -2) } ) private static void methodG() { System.out.println("methodG"); } private static CallSite bsmWithFloatAndLongArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, float extraArg, long... arityArgs) throws Throwable { printBsmArgs( "bsmWithFloatAndLongArray", lookup, methodName, methodType, extraArg, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithFloatAndLongArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, float.class, long[].class } ), fieldOrMethodName = "methodH", constantArgumentsForBootstrapMethod = { @Constant(floatValue = (float) -Math.E), @Constant(longValue = 999999999999l), @Constant(longValue = -8888888888888l) } ) private static void methodH() { System.out.println("methodH"); } private static CallSite bsmWithClassAndFloatArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Class extraArg, float... arityArgs) throws Throwable { printBsmArgs( "bsmWithClassAndFloatArray", lookup, methodName, methodType, extraArg, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithClassAndFloatArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, Class.class, float[].class } ), fieldOrMethodName = "methodI", constantArgumentsForBootstrapMethod = { @Constant(classValue = Throwable.class), @Constant(floatValue = Float.MAX_VALUE), @Constant(floatValue = Float.MIN_VALUE), @Constant(floatValue = (float) Math.PI), @Constant(floatValue = (float) -Math.PI) } ) private static void methodI() { System.out.println("methodI"); } private static CallSite bsmWithDoubleArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, double... arityArgs) throws Throwable { printBsmArgs("bsmWithDoubleArray", lookup, methodName, methodType, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithDoubleArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, double[].class } ), fieldOrMethodName = "methodJ", constantArgumentsForBootstrapMethod = { @Constant(doubleValue = Double.MAX_VALUE), @Constant(doubleValue = Double.MIN_VALUE), @Constant(doubleValue = Math.E), @Constant(doubleValue = -Math.PI) } ) private static void methodJ() { System.out.println("methodJ"); } private static CallSite bsmWithClassArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Class... arityArgs) throws Throwable { printBsmArgs("bsmWithClassArray", lookup, methodName, methodType, arityArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithClassArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, Class[].class } ), fieldOrMethodName = "methodK", constantArgumentsForBootstrapMethod = { @Constant(classValue = Integer.class), @Constant(classValue = MethodHandles.class), @Constant(classValue = Arrays.class) } ) private static void methodK() { System.out.println("methodK"); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithIntAndStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String[].class } ), fieldOrMethodName = "methodO", constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 104)} ) private static void methodO() { // Arguments are not compatible assertNotReached(); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithIntAndStringArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, int.class, String[].class } ), fieldOrMethodName = "methodP", constantArgumentsForBootstrapMethod = { @Constant(intValue = 103), @Constant(stringValue = "A"), @Constant(stringValue = "B"), @Constant(intValue = 42) } ) private static void methodP() { // Arguments are not compatible - specifically, the third // component of potential collector array is an integer // argument (42). assertNotReached(); } private static CallSite bsmWithWiderArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, long[] extraArgs) throws Throwable { printBsmArgs("bsmWithWiderArray", lookup, methodName, methodType, extraArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithWiderArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, long[].class } ), fieldOrMethodName = "methodQ", constantArgumentsForBootstrapMethod = {@Constant(intValue = 103), @Constant(intValue = 42)} ) private static void methodQ() { assertNotReached(); } private static CallSite bsmWithBoxedArray( MethodHandles.Lookup lookup, String methodName, MethodType methodType, Integer[] extraArgs) throws Throwable { printBsmArgs("bsmWithBoxedArray", lookup, methodName, methodType, extraArgs); MethodHandle mh = lookup.findStatic(lookup.lookupClass(), methodName, methodType); return new ConstantCallSite(mh); } @CalledByIndy( bootstrapMethod = @BootstrapMethod( enclosingType = TestVariableArityLinkerMethod.class, name = "bsmWithBoxedArray", parameterTypes = { MethodHandles.Lookup.class, String.class, MethodType.class, Integer[].class } ), fieldOrMethodName = "methodR", constantArgumentsForBootstrapMethod = { @Constant(intValue = 1030), @Constant(intValue = 420) } ) private static void methodR() { assertNotReached(); } static void test() { // Happy cases for (int i = 0; i < 2; ++i) { methodA(); methodB(); methodC(); } for (int i = 0; i < 2; ++i) { methodD(); methodE(); methodF(); } methodG(); methodH(); methodI(); methodJ(); methodK(); // Broken cases try { // bsm has incompatible static methods. Collector // component type is String, the corresponding static // arguments are int values. methodO(); assertNotReached(); } catch (BootstrapMethodError expected) { System.out.print("methodO => "); System.out.print(expected.getClass()); System.out.print(" => "); System.out.println(expected.getCause().getClass()); } try { // bsm has a trailing String array for the collector array. // There is an int value amongst the String values. methodP(); assertNotReached(); } catch (BootstrapMethodError expected) { System.out.print("methodP => "); System.out.print(expected.getClass()); System.out.print(" => "); System.out.println(expected.getCause().getClass()); } try { // bsm has as trailing long[] element for the collector array. // The corresponding static bsm arguments are of type int. methodQ(); assertNotReached(); } catch (BootstrapMethodError expected) { System.out.print("methodQ => "); System.out.print(expected.getClass()); System.out.print(" => "); System.out.println(expected.getCause().getClass()); } try { // bsm has as trailing Integer[] element for the collector array. // The corresponding static bsm arguments are of type int. methodR(); assertNotReached(); } catch (BootstrapMethodError expected) { System.out.print("methodR => "); System.out.print(expected.getClass()); System.out.print(" => "); System.out.println(expected.getCause().getClass()); } } }