| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. The Android Open Source |
| * Project designates this particular file as subject to the "Classpath" |
| * exception as provided by The Android Open Source Project in the LICENSE |
| * file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| package java.lang.invoke; |
| |
| import dalvik.system.EmulatedStackFrame; |
| import dalvik.system.EmulatedStackFrame.Range; |
| import dalvik.system.EmulatedStackFrame.StackFrameAccessor; |
| import dalvik.system.EmulatedStackFrame.StackFrameReader; |
| import dalvik.system.EmulatedStackFrame.StackFrameWriter; |
| import java.lang.reflect.Array; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import sun.invoke.util.Wrapper; |
| import sun.misc.Unsafe; |
| import static dalvik.system.EmulatedStackFrame.StackFrameAccessor.copyNext; |
| |
| /** |
| * @hide Public for testing only. |
| */ |
| public class Transformers { |
| private Transformers() {} |
| |
| static { |
| try { |
| TRANSFORM_INTERNAL = MethodHandle.class.getDeclaredMethod("transformInternal", |
| EmulatedStackFrame.class); |
| } catch (NoSuchMethodException nsme) { |
| throw new AssertionError(); |
| } |
| } |
| |
| /** |
| * Method reference to the private {@code MethodHandle.transformInternal} method. This is |
| * cached here because it's the point of entry for all transformers. |
| */ |
| private static final Method TRANSFORM_INTERNAL; |
| |
| /** @hide */ |
| public static abstract class Transformer extends MethodHandle implements Cloneable { |
| protected Transformer(MethodType type) { |
| super(TRANSFORM_INTERNAL.getArtMethod(), MethodHandle.INVOKE_TRANSFORM, type); |
| } |
| |
| protected Transformer(MethodType type, int invokeKind) { |
| super(TRANSFORM_INTERNAL.getArtMethod(), invokeKind, type); |
| } |
| |
| @Override |
| public Object clone() throws CloneNotSupportedException { |
| return super.clone(); |
| } |
| } |
| |
| /** |
| * A method handle that always throws an exception of a specified type. |
| * |
| * The handle declares a nominal return type, which is immaterial to the execution |
| * of the handle because it never returns. |
| * |
| * @hide |
| */ |
| public static class AlwaysThrow extends Transformer { |
| private final Class<? extends Throwable> exceptionType; |
| |
| public AlwaysThrow(Class<?> nominalReturnType, Class<? extends Throwable> exType) { |
| super(MethodType.methodType(nominalReturnType, exType)); |
| this.exceptionType = exType; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| throw emulatedStackFrame.getReference(0, exceptionType); |
| } |
| } |
| |
| /** |
| * Implements {@code MethodHandles.dropArguments}. |
| */ |
| public static class DropArguments extends Transformer { |
| private final MethodHandle delegate; |
| |
| private final EmulatedStackFrame.Range range1; |
| |
| /** |
| * Note that {@code range2} will be null if the arguments that are being dropped |
| * are the last {@code n}. |
| */ |
| /* @Nullable */ private final EmulatedStackFrame.Range range2; |
| |
| public DropArguments(MethodType type, MethodHandle delegate, |
| int startPos, int numDropped) { |
| super(type); |
| |
| this.delegate = delegate; |
| |
| // We pre-calculate the ranges of values we have to copy through to the delegate |
| // handle at the time of instantiation so that the actual invoke is performant. |
| this.range1 = EmulatedStackFrame.Range.of(type, 0, startPos); |
| final int numArgs = type.ptypes().length; |
| if (startPos + numDropped < numArgs) { |
| this.range2 = EmulatedStackFrame.Range.of(type, startPos + numDropped, numArgs); |
| } else { |
| this.range2 = null; |
| } |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(delegate.type()); |
| |
| emulatedStackFrame.copyRangeTo(calleeFrame, range1, |
| 0 /* referencesStart */, 0 /* stackFrameStart */); |
| |
| if (range2 != null) { |
| final int referencesStart = range1.numReferences; |
| final int stackFrameStart = range1.numBytes; |
| |
| emulatedStackFrame.copyRangeTo(calleeFrame, range2, |
| referencesStart, stackFrameStart); |
| } |
| |
| delegate.invoke(calleeFrame); |
| calleeFrame.copyReturnValueTo(emulatedStackFrame); |
| } |
| } |
| |
| /** |
| * Implements {@code MethodHandles.catchException}. |
| */ |
| public static class CatchException extends Transformer { |
| private final MethodHandle target; |
| private final MethodHandle handler; |
| private final Class<?> exType; |
| |
| private final EmulatedStackFrame.Range handlerArgsRange; |
| |
| public CatchException(MethodHandle target, MethodHandle handler, Class<?> exType) { |
| super(target.type()); |
| |
| this.target = target; |
| this.handler = handler; |
| this.exType = exType; |
| |
| // We only copy the first "count" args, dropping others if required. Note that |
| // we subtract one because the first handler arg is the exception thrown by the |
| // target. |
| handlerArgsRange = EmulatedStackFrame.Range.of(target.type(), 0, |
| (handler.type().parameterCount() - 1)); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| try { |
| target.invoke(emulatedStackFrame); |
| } catch (Throwable th) { |
| if (th.getClass() == exType) { |
| // We've gotten an exception of the appropriate type, so we need to call |
| // the handler. Create a new frame of the appropriate size. |
| EmulatedStackFrame fallback = EmulatedStackFrame.create(handler.type()); |
| |
| // The first argument to the handler is the actual exception. |
| fallback.setReference(0, th); |
| |
| // We then copy other arguments that need to be passed through to the handler. |
| // Note that we might drop arguments at the end, if needed. Note that |
| // referencesStart == 1 because the first argument is the exception type. |
| emulatedStackFrame.copyRangeTo(fallback, handlerArgsRange, |
| 1 /* referencesStart */, 0 /* stackFrameStart */); |
| |
| // Perform the invoke and return the appropriate value. |
| handler.invoke(fallback); |
| fallback.copyReturnValueTo(emulatedStackFrame); |
| } else { |
| // The exception is not of the expected type, we throw it. |
| throw th; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Implements {@code MethodHandles.GuardWithTest}. |
| */ |
| public static class GuardWithTest extends Transformer { |
| private final MethodHandle test; |
| private final MethodHandle target; |
| private final MethodHandle fallback; |
| |
| private final EmulatedStackFrame.Range testArgsRange; |
| |
| public GuardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) { |
| super(target.type()); |
| |
| this.test = test; |
| this.target = target; |
| this.fallback = fallback; |
| |
| // The test method might have a subset of the arguments of the handle / target. |
| testArgsRange = EmulatedStackFrame.Range.of(target.type(), 0, test.type().parameterCount()); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| EmulatedStackFrame testFrame = EmulatedStackFrame.create(test.type()); |
| emulatedStackFrame.copyRangeTo(testFrame, testArgsRange, 0, 0); |
| |
| // We know that the return value for test is going to be boolean.class, so we don't have |
| // to do the copyReturnValue dance. |
| final boolean value = (boolean) test.invoke(testFrame); |
| if (value) { |
| target.invoke(emulatedStackFrame); |
| } else { |
| fallback.invoke(emulatedStackFrame); |
| } |
| } |
| } |
| |
| /** |
| * Implementation of MethodHandles.arrayElementGetter for reference types. |
| */ |
| public static class ReferenceArrayElementGetter extends Transformer { |
| private final Class<?> arrayClass; |
| |
| public ReferenceArrayElementGetter(Class<?> arrayClass) { |
| super(MethodType.methodType(arrayClass.getComponentType(), |
| new Class<?>[]{arrayClass, int.class})); |
| this.arrayClass = arrayClass; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(emulatedStackFrame); |
| |
| // Read the array object and the index from the stack frame. |
| final Object[] array = (Object[]) reader.nextReference(arrayClass); |
| final int index = reader.nextInt(); |
| |
| // Write the array element back to the stack frame. |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(emulatedStackFrame); |
| writer.makeReturnValueAccessor(); |
| writer.putNextReference(array[index], arrayClass.getComponentType()); |
| } |
| } |
| |
| /** |
| * Implementation of MethodHandles.arrayElementSetter for reference types. |
| */ |
| public static class ReferenceArrayElementSetter extends Transformer { |
| private final Class<?> arrayClass; |
| |
| public ReferenceArrayElementSetter(Class<?> arrayClass) { |
| super(MethodType.methodType(void.class, |
| new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() })); |
| this.arrayClass = arrayClass; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(emulatedStackFrame); |
| |
| // Read the array object, index and the value to write from the stack frame. |
| final Object[] array = (Object[]) reader.nextReference(arrayClass); |
| final int index = reader.nextInt(); |
| final Object value = reader.nextReference(arrayClass.getComponentType()); |
| |
| array[index] = value; |
| } |
| } |
| |
| /** |
| * Implementation of MethodHandles.identity() for reference types. |
| */ |
| public static class ReferenceIdentity extends Transformer { |
| private final Class<?> type; |
| |
| public ReferenceIdentity(Class<?> type) { |
| super(MethodType.methodType(type, type)); |
| this.type = type; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(emulatedStackFrame); |
| |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(emulatedStackFrame); |
| writer.makeReturnValueAccessor(); |
| writer.putNextReference(reader.nextReference(type), type); |
| } |
| } |
| |
| /** |
| * Implementation of MethodHandles.constant. |
| */ |
| public static class Constant extends Transformer { |
| private final Class<?> type; |
| |
| // NOTE: This implementation turned out to be more awkward than expected becuase |
| // of the type system. We could simplify this considerably at the cost of making |
| // the emulated stack frame API uglier or by transitioning into JNI. |
| // |
| // We could consider implementing this in terms of bind() once that's implemented. |
| // This would then just become : MethodHandles.identity(type).bind(value). |
| private int asInt; |
| private long asLong; |
| private float asFloat; |
| private double asDouble; |
| private Object asReference; |
| |
| private char typeChar; |
| |
| public Constant(Class<?> type, Object value) { |
| super(MethodType.methodType(type)); |
| this.type = type; |
| |
| if (!type.isPrimitive()) { |
| asReference = value; |
| typeChar = 'L'; |
| } else if (type == int.class) { |
| asInt = (int) value; |
| typeChar = 'I'; |
| } else if (type == char.class) { |
| asInt = (int) (char) value; |
| typeChar = 'C'; |
| } else if (type == short.class) { |
| asInt = (int) (short) value; |
| typeChar = 'S'; |
| } else if (type == byte.class) { |
| asInt = (int) (byte) value; |
| typeChar = 'B'; |
| } else if (type == boolean.class) { |
| asInt = ((boolean) value) ? 1 : 0; |
| typeChar = 'Z'; |
| } else if (type == long.class) { |
| asLong = (long) value; |
| typeChar = 'J'; |
| } else if (type == float.class) { |
| asFloat = (float) value; |
| typeChar = 'F'; |
| } else if (type == double.class) { |
| asDouble = (double) value; |
| typeChar = 'D'; |
| } else { |
| throw new AssertionError("unknown type: " + typeChar); |
| } |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(emulatedStackFrame); |
| writer.makeReturnValueAccessor(); |
| |
| switch (typeChar) { |
| case 'L' : { writer.putNextReference(asReference, type); break; } |
| case 'I' : { writer.putNextInt(asInt); break; } |
| case 'C' : { writer.putNextChar((char) asInt); break; } |
| case 'S' : { writer.putNextShort((short) asInt); break; } |
| case 'B' : { writer.putNextByte((byte) asInt); break; } |
| case 'Z' : { writer.putNextBoolean(asInt == 1); break; } |
| case 'J' : { writer.putNextLong(asLong); break; } |
| case 'F' : { writer.putNextFloat(asFloat); break; } |
| case 'D' : { writer.putNextDouble(asDouble); break; } |
| default: |
| throw new AssertionError("Unexpected typeChar: " + typeChar); |
| } |
| } |
| } |
| |
| /*package*/ static class Construct extends Transformer { |
| private final MethodHandle constructorHandle; |
| private final EmulatedStackFrame.Range callerRange; |
| |
| /*package*/ Construct(MethodHandle constructorHandle, MethodType returnedType) { |
| super(returnedType); |
| this.constructorHandle = constructorHandle; |
| this.callerRange = EmulatedStackFrame.Range.all(type()); |
| } |
| |
| MethodHandle getConstructorHandle() { |
| return constructorHandle; |
| } |
| |
| private static boolean isAbstract(Class<?> klass) { |
| return (klass.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT; |
| } |
| |
| private static void checkInstantiable(Class<?> klass) throws InstantiationException { |
| if (isAbstract(klass)) { |
| String s = klass.isInterface() ? "interface " : "abstract class "; |
| throw new InstantiationException("Can't instantiate " + s + klass); |
| } |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final Class<?> receiverType = constructorHandle.type().parameterType(0); |
| checkInstantiable(receiverType); |
| |
| // Allocate memory for receiver. |
| Object receiver = Unsafe.getUnsafe().allocateInstance(receiverType); |
| |
| // The MethodHandle type for the caller has the form of |
| // {rtype=T,ptypes=A1..An}. The constructor MethodHandle is of |
| // the form {rtype=void,ptypes=T,A1...An}. So the frame for |
| // the constructor needs to have a slot with the receiver |
| // in position 0. |
| EmulatedStackFrame constructorFrame = |
| EmulatedStackFrame.create(constructorHandle.type()); |
| constructorFrame.setReference(0, receiver); |
| emulatedStackFrame.copyRangeTo(constructorFrame, callerRange, 1, 0); |
| constructorHandle.invoke(constructorFrame); |
| |
| // Set return result for caller. |
| emulatedStackFrame.setReturnValueTo(receiver); |
| } |
| } |
| |
| /** |
| * Implements MethodHandle.bindTo. |
| * |
| * @hide |
| */ |
| public static class BindTo extends Transformer { |
| private final MethodHandle delegate; |
| private final Object receiver; |
| |
| private final EmulatedStackFrame.Range range; |
| |
| public BindTo(MethodHandle delegate, Object receiver) { |
| super(delegate.type().dropParameterTypes(0, 1)); |
| |
| this.delegate = delegate; |
| this.receiver = receiver; |
| |
| this.range = EmulatedStackFrame.Range.all(this.type()); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| // Create a new emulated stack frame with the full type (including the leading |
| // receiver reference). |
| EmulatedStackFrame stackFrame = EmulatedStackFrame.create(delegate.type()); |
| |
| // The first reference argument must be the receiver. |
| stackFrame.setReference(0, receiver); |
| // Copy all other arguments. |
| emulatedStackFrame.copyRangeTo(stackFrame, range, |
| 1 /* referencesStart */, 0 /* stackFrameStart */); |
| |
| // Perform the invoke. |
| delegate.invoke(stackFrame); |
| stackFrame.copyReturnValueTo(emulatedStackFrame); |
| } |
| } |
| |
| /** |
| * Implements MethodHandle.filterReturnValue. |
| */ |
| public static class FilterReturnValue extends Transformer { |
| private final MethodHandle target; |
| private final MethodHandle filter; |
| |
| private final EmulatedStackFrame.Range allArgs; |
| |
| public FilterReturnValue(MethodHandle target, MethodHandle filter) { |
| super(MethodType.methodType(filter.type().rtype(), target.type().ptypes())); |
| |
| this.target = target; |
| this.filter = filter; |
| |
| allArgs = EmulatedStackFrame.Range.all(type()); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| // Create a new frame with the target's type and copy all arguments over. |
| // This frame differs in return type with |emulatedStackFrame| but will have |
| // the same parameter shapes. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0); |
| target.invoke(targetFrame); |
| |
| // Perform the invoke. |
| final StackFrameReader returnValueReader = new StackFrameReader(); |
| returnValueReader.attach(targetFrame); |
| returnValueReader.makeReturnValueAccessor(); |
| |
| // Create an emulated frame for the filter and copy all its arguments across. |
| EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type()); |
| final StackFrameWriter filterWriter = new StackFrameWriter(); |
| filterWriter.attach(filterFrame); |
| |
| final Class<?> returnType = target.type().rtype(); |
| if (!returnType.isPrimitive()) { |
| filterWriter.putNextReference(returnValueReader.nextReference(returnType), |
| returnType); |
| } else if (returnType == boolean.class) { |
| filterWriter.putNextBoolean(returnValueReader.nextBoolean()); |
| } else if (returnType == byte.class) { |
| filterWriter.putNextByte(returnValueReader.nextByte()); |
| } else if (returnType == char.class) { |
| filterWriter.putNextChar(returnValueReader.nextChar()); |
| } else if (returnType == short.class) { |
| filterWriter.putNextShort(returnValueReader.nextShort()); |
| } else if (returnType == int.class) { |
| filterWriter.putNextInt(returnValueReader.nextInt()); |
| } else if (returnType == long.class) { |
| filterWriter.putNextLong(returnValueReader.nextLong()); |
| } else if (returnType == float.class) { |
| filterWriter.putNextFloat(returnValueReader.nextFloat()); |
| } else if (returnType == double.class) { |
| filterWriter.putNextDouble(returnValueReader.nextDouble()); |
| } |
| |
| // Invoke the filter and copy its return value back to the original frame. |
| filter.invoke(filterFrame); |
| filterFrame.copyReturnValueTo(emulatedStackFrame); |
| } |
| } |
| |
| /* |
| * Implements MethodHandles.permuteArguments. |
| * |
| * @hide |
| */ |
| public static class PermuteArguments extends Transformer { |
| private final MethodHandle target; |
| private final int[] reorder; |
| |
| public PermuteArguments(MethodType type, MethodHandle target, int[] reorder) { |
| super(type); |
| |
| this.target = target; |
| this.reorder = reorder; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(emulatedStackFrame); |
| |
| // In the interests of simplicity, we box / unbox arguments while performing |
| // the permutation. We first iterate through the incoming stack frame and box |
| // each argument. We then unbox and write out the argument to the target frame |
| // according to the specified reordering. |
| Object[] arguments = new Object[reorder.length]; |
| final Class<?>[] ptypes = type().ptypes(); |
| for (int i = 0; i < ptypes.length; ++i) { |
| final Class<?> ptype = ptypes[i]; |
| if (!ptype.isPrimitive()) { |
| arguments[i] = reader.nextReference(ptype); |
| } else if (ptype == boolean.class) { |
| arguments[i] = reader.nextBoolean(); |
| } else if (ptype == byte.class) { |
| arguments[i] = reader.nextByte(); |
| } else if (ptype == char.class) { |
| arguments[i] = reader.nextChar(); |
| } else if (ptype == short.class) { |
| arguments[i] = reader.nextShort(); |
| } else if (ptype == int.class) { |
| arguments[i] = reader.nextInt(); |
| } else if (ptype == long.class) { |
| arguments[i] = reader.nextLong(); |
| } else if (ptype == float.class) { |
| arguments[i] = reader.nextFloat(); |
| } else if (ptype == double.class) { |
| arguments[i] = reader.nextDouble(); |
| } else { |
| throw new AssertionError("Unexpected type: " + ptype); |
| } |
| } |
| |
| EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type()); |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(calleeFrame); |
| |
| for (int i = 0; i < ptypes.length; ++i) { |
| int idx = reorder[i]; |
| final Class<?> ptype = ptypes[idx]; |
| final Object argument = arguments[idx]; |
| |
| if (!ptype.isPrimitive()) { |
| writer.putNextReference(argument, ptype); |
| } else if (ptype == boolean.class) { |
| writer.putNextBoolean((boolean) argument); |
| } else if (ptype == byte.class) { |
| writer.putNextByte((byte) argument); |
| } else if (ptype == char.class) { |
| writer.putNextChar((char) argument); |
| } else if (ptype == short.class) { |
| writer.putNextShort((short) argument); |
| } else if (ptype == int.class) { |
| writer.putNextInt((int) argument); |
| } else if (ptype == long.class) { |
| writer.putNextLong((long) argument); |
| } else if (ptype == float.class) { |
| writer.putNextFloat((float) argument); |
| } else if (ptype == double.class) { |
| writer.putNextDouble((double) argument); |
| } else { |
| throw new AssertionError("Unexpected type: " + ptype); |
| } |
| } |
| |
| target.invoke(calleeFrame); |
| calleeFrame.copyReturnValueTo(emulatedStackFrame); |
| } |
| } |
| |
| /** |
| * Converts methods with a trailing array argument to variable arity |
| * methods. So (A,B,C[])R can be invoked with any number of convertible |
| * arguments after B, e.g. (A,B)R or (A, B, C0)R or (A, B, C0...Cn)R. |
| * |
| * @hide |
| */ |
| /*package*/ static class VarargsCollector extends Transformer { |
| final MethodHandle target; |
| |
| /*package*/ VarargsCollector(MethodHandle target) { |
| super(target.type(), MethodHandle.INVOKE_CALLSITE_TRANSFORM); |
| if (!lastParameterTypeIsAnArray(target.type().ptypes())) { |
| throw new IllegalArgumentException("target does not have array as last parameter"); |
| } |
| this.target = target; |
| } |
| |
| private static boolean lastParameterTypeIsAnArray(Class<?>[] parameterTypes) { |
| if (parameterTypes.length == 0) return false; |
| return parameterTypes[parameterTypes.length - 1].isArray(); |
| } |
| |
| @Override |
| public boolean isVarargsCollector() { return true; } |
| |
| @Override |
| public MethodHandle asFixedArity() { return target; } |
| |
| @Override |
| public void transform(EmulatedStackFrame callerFrame) throws Throwable { |
| MethodType callerFrameType = callerFrame.getMethodType(); |
| Class<?>[] callerPTypes = callerFrameType.ptypes(); |
| Class<?>[] targetPTypes = type().ptypes(); |
| |
| int lastTargetIndex = targetPTypes.length - 1; |
| if (callerPTypes.length == targetPTypes.length && |
| targetPTypes[lastTargetIndex].isAssignableFrom(callerPTypes[lastTargetIndex])) { |
| // Caller frame matches target frame in the arity array parameter. Invoke |
| // immediately, and let the invoke() dispatch perform any necessary conversions |
| // on the other parameters present. |
| target.invoke(callerFrame); |
| return; |
| } |
| |
| if (callerPTypes.length < targetPTypes.length - 1) { |
| // Too few arguments to be compatible with variable arity invocation. |
| throwWrongMethodTypeException(callerFrameType, type()); |
| } |
| |
| if (!MethodType.canConvert(type().rtype(), callerFrameType.rtype())) { |
| // Incompatible return type. |
| throwWrongMethodTypeException(callerFrameType, type()); |
| } |
| |
| Class<?> elementType = targetPTypes[lastTargetIndex].getComponentType(); |
| if (!arityArgumentsConvertible(callerPTypes, lastTargetIndex, elementType)) { |
| // Wrong types to be compatible with variable arity invocation. |
| throwWrongMethodTypeException(callerFrameType, type()); |
| } |
| |
| // Allocate targetFrame. |
| MethodType targetFrameType = makeTargetFrameType(callerFrameType, type()); |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(targetFrameType); |
| prepareFrame(callerFrame, targetFrame); |
| |
| // Invoke target. |
| target.invoke(targetFrame); |
| |
| // Copy return value to the caller's frame. |
| targetFrame.copyReturnValueTo(callerFrame); |
| } |
| |
| private static void throwWrongMethodTypeException(MethodType from, MethodType to) { |
| throw new WrongMethodTypeException("Cannot convert " + from + " to " + to); |
| } |
| |
| private static boolean arityArgumentsConvertible(Class<?>[] ptypes, int arityStart, |
| Class<?> elementType) { |
| if (ptypes.length - 1 == arityStart) { |
| if (ptypes[arityStart].isArray() && |
| ptypes[arityStart].getComponentType() == elementType) { |
| // The last ptype is in the same position as the arity |
| // array and has the same type. |
| return true; |
| } |
| } |
| |
| for (int i = arityStart; i < ptypes.length; ++i) { |
| if (!MethodType.canConvert(ptypes[i], elementType)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static Object referenceArray(StackFrameReader reader, Class<?>[] ptypes, |
| Class<?> elementType, int offset, int length) { |
| Object arityArray = Array.newInstance(elementType, length); |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| Object o = null; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { o = reader.nextReference(argumentType); break; } |
| case 'I': { o = reader.nextInt(); break; } |
| case 'J': { o = reader.nextLong(); break; } |
| case 'B': { o = reader.nextByte(); break; } |
| case 'S': { o = reader.nextShort(); break; } |
| case 'C': { o = reader.nextChar(); break; } |
| case 'Z': { o = reader.nextBoolean(); break; } |
| case 'F': { o = reader.nextFloat(); break; } |
| case 'D': { o = reader.nextDouble(); break; } |
| } |
| Array.set(arityArray, i, elementType.cast(o)); |
| } |
| return arityArray; |
| } |
| |
| private static Object intArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| int[] arityArray = new int[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'I': { arityArray[i] = reader.nextInt(); break; } |
| case 'S': { arityArray[i] = reader.nextShort(); break; } |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { |
| arityArray[i] = (Integer) reader.nextReference(argumentType); |
| break; |
| } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object longArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| long[] arityArray = new long[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'J': { arityArray[i] = reader.nextLong(); break; } |
| case 'I': { arityArray[i] = reader.nextInt(); break; } |
| case 'S': { arityArray[i] = reader.nextShort(); break; } |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { arityArray[i] = (Long) reader.nextReference(argumentType); break; } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object byteArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| byte[] arityArray = new byte[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { arityArray[i] = (Byte) reader.nextReference(argumentType); break; } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object shortArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| short[] arityArray = new short[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'S': { arityArray[i] = reader.nextShort(); break; } |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { arityArray[i] = (Short) reader.nextReference(argumentType); break; } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object charArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| char[] arityArray = new char[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'C': { arityArray[i] = reader.nextChar(); break; } |
| default: { |
| arityArray[i] = (Character) reader.nextReference(argumentType); |
| break; |
| } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object booleanArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| boolean[] arityArray = new boolean[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'Z': { arityArray[i] = reader.nextBoolean(); break; } |
| default: |
| arityArray[i] = (Boolean) reader.nextReference(argumentType); |
| break; |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object floatArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| float[] arityArray = new float[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'F': { arityArray[i] = reader.nextFloat(); break; } |
| case 'J': { arityArray[i] = reader.nextLong(); break; } |
| case 'I': { arityArray[i] = reader.nextInt(); break; } |
| case 'S': { arityArray[i] = reader.nextShort(); break; } |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { |
| arityArray[i] = (Float) reader.nextReference(argumentType); |
| break; |
| } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object doubleArray(StackFrameReader reader, Class<?> ptypes[], |
| int offset, int length) { |
| double[] arityArray = new double[length]; |
| for (int i = 0; i < length; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'D': { arityArray[i] = reader.nextDouble(); break; } |
| case 'F': { arityArray[i] = reader.nextFloat(); break; } |
| case 'J': { arityArray[i] = reader.nextLong(); break; } |
| case 'I': { arityArray[i] = reader.nextInt(); break; } |
| case 'S': { arityArray[i] = reader.nextShort(); break; } |
| case 'B': { arityArray[i] = reader.nextByte(); break; } |
| default: { |
| arityArray[i] = (Double) reader.nextReference(argumentType); |
| break; |
| } |
| } |
| } |
| return arityArray; |
| } |
| |
| private static Object makeArityArray(MethodType callerFrameType, |
| StackFrameReader callerFrameReader, |
| int indexOfArityArray, |
| Class<?> arityArrayType) { |
| int arityArrayLength = callerFrameType.ptypes().length - indexOfArityArray; |
| Class<?> elementType = arityArrayType.getComponentType(); |
| Class<?>[] callerPTypes = callerFrameType.ptypes(); |
| |
| char elementBasicType = Wrapper.basicTypeChar(elementType); |
| switch (elementBasicType) { |
| case 'L': return referenceArray(callerFrameReader, callerPTypes, elementType, |
| indexOfArityArray, arityArrayLength); |
| case 'I': return intArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'J': return longArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'B': return byteArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'S': return shortArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'C': return charArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'Z': return booleanArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'F': return floatArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| case 'D': return doubleArray(callerFrameReader, callerPTypes, |
| indexOfArityArray, arityArrayLength); |
| } |
| throw new InternalError("Unexpected type: " + elementType); |
| } |
| |
| public static Object collectArguments(char basicComponentType, Class<?> componentType, |
| StackFrameReader reader, Class<?>[] types, |
| int startIdx, int length) { |
| switch (basicComponentType) { |
| case 'L': return referenceArray(reader, types, componentType, startIdx, length); |
| case 'I': return intArray(reader, types, startIdx, length); |
| case 'J': return longArray(reader, types, startIdx, length); |
| case 'B': return byteArray(reader, types, startIdx, length); |
| case 'S': return shortArray(reader, types, startIdx, length); |
| case 'C': return charArray(reader, types, startIdx, length); |
| case 'Z': return booleanArray(reader, types, startIdx, length); |
| case 'F': return floatArray(reader, types, startIdx, length); |
| case 'D': return doubleArray(reader, types, startIdx, length); |
| } |
| throw new InternalError("Unexpected type: " + basicComponentType); |
| } |
| |
| private static void copyParameter(StackFrameReader reader, StackFrameWriter writer, |
| Class<?> ptype) { |
| switch (Wrapper.basicTypeChar(ptype)) { |
| case 'L': { writer.putNextReference(reader.nextReference(ptype), ptype); break; } |
| case 'I': { writer.putNextInt(reader.nextInt()); break; } |
| case 'J': { writer.putNextLong(reader.nextLong()); break; } |
| case 'B': { writer.putNextByte(reader.nextByte()); break; } |
| case 'S': { writer.putNextShort(reader.nextShort()); break; } |
| case 'C': { writer.putNextChar(reader.nextChar()); break; } |
| case 'Z': { writer.putNextBoolean(reader.nextBoolean()); break; } |
| case 'F': { writer.putNextFloat(reader.nextFloat()); break; } |
| case 'D': { writer.putNextDouble(reader.nextDouble()); break; } |
| default: throw new InternalError("Unexpected type: " + ptype); |
| } |
| } |
| |
| private static void prepareFrame(EmulatedStackFrame callerFrame, |
| EmulatedStackFrame targetFrame) { |
| StackFrameWriter targetWriter = new StackFrameWriter(); |
| targetWriter.attach(targetFrame); |
| StackFrameReader callerReader = new StackFrameReader(); |
| callerReader.attach(callerFrame); |
| |
| // Copy parameters from |callerFrame| to |targetFrame| leaving room for arity array. |
| MethodType targetMethodType = targetFrame.getMethodType(); |
| int indexOfArityArray = targetMethodType.ptypes().length - 1; |
| for (int i = 0; i < indexOfArityArray; ++i) { |
| Class<?> ptype = targetMethodType.ptypes()[i]; |
| copyParameter(callerReader, targetWriter, ptype); |
| } |
| |
| // Add arity array as last parameter in |targetFrame|. |
| Class<?> arityArrayType = targetMethodType.ptypes()[indexOfArityArray]; |
| Object arityArray = makeArityArray(callerFrame.getMethodType(), callerReader, |
| indexOfArityArray, arityArrayType); |
| targetWriter.putNextReference(arityArray, arityArrayType); |
| } |
| |
| /** |
| * Computes the frame type to invoke the target method handle with. This |
| * is the same as the caller frame type, but with the trailing argument |
| * being the array type that is the trailing argument in the target method |
| * handle. |
| * |
| * Suppose the targetType is (T0, T1, T2[])RT and the callerType is (C0, C1, C2, C3)RC |
| * then the constructed type is (C0, C1, T2[])RC. |
| */ |
| private static MethodType makeTargetFrameType(MethodType callerType, |
| MethodType targetType) { |
| final int ptypesLength = targetType.ptypes().length; |
| final Class<?>[] ptypes = new Class<?>[ptypesLength]; |
| // Copy types from caller types to new methodType. |
| System.arraycopy(callerType.ptypes(), 0, ptypes, 0, ptypesLength - 1); |
| // Set the last element in the type array to be the |
| // varargs array of the target. |
| ptypes[ptypesLength - 1] = targetType.ptypes()[ptypesLength - 1]; |
| return MethodType.methodType(callerType.rtype(), ptypes); |
| } |
| } |
| |
| /** |
| * Implements MethodHandles.invoker & MethodHandles.exactInvoker. |
| */ |
| static class Invoker extends Transformer { |
| private final MethodType targetType; |
| private final boolean isExactInvoker; |
| private final EmulatedStackFrame.Range copyRange; |
| |
| Invoker(MethodType targetType, boolean isExactInvoker) { |
| super(targetType.insertParameterTypes(0, MethodHandle.class)); |
| this.targetType = targetType; |
| this.isExactInvoker = isExactInvoker; |
| copyRange = EmulatedStackFrame.Range.of(type(), 1, type().parameterCount()); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable { |
| // We need to artifically throw a WrongMethodTypeException here because we |
| // can't call invokeExact on the target inside the transformer. |
| if (isExactInvoker) { |
| // TODO: We should do the comparison by hand if this new type creation |
| // on every invoke proves too expensive. |
| MethodType callType = emulatedStackFrame.getCallsiteType().dropParameterTypes(0, 1); |
| if (!targetType.equals(callType)) { |
| throw new WrongMethodTypeException("Wrong type, Expected: " + targetType |
| + " was: " + callType); |
| } |
| } |
| |
| // The first argument to the stack frame is the handle that needs to be invoked. |
| MethodHandle target = emulatedStackFrame.getReference(0, MethodHandle.class); |
| |
| // All other arguments must be copied to the target frame. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(targetType); |
| emulatedStackFrame.copyRangeTo(targetFrame, copyRange, 0, 0); |
| |
| // Finally, invoke the handle and copy the return value. |
| target.invoke(targetFrame); |
| targetFrame.copyReturnValueTo(emulatedStackFrame); |
| } |
| } |
| |
| /** |
| * Implements MethodHandle.asSpreader / MethodHandles.spreadInvoker. |
| */ |
| static class Spreader extends Transformer { |
| /** The method handle we're delegating to. */ |
| private final MethodHandle target; |
| |
| /** |
| * The offset of the trailing array argument in the list of arguments to |
| * this transformer. The array argument is always the last argument. |
| */ |
| private final int arrayOffset; |
| |
| /** |
| * The type char of the component type of the array. |
| */ |
| private final char arrayTypeChar; |
| |
| /** |
| * The number of input arguments that will be present in the array. In other words, |
| * this is the expected array length. |
| */ |
| private final int numArrayArgs; |
| |
| /** |
| * Range of arguments to copy verbatim from the input frame, This will cover all |
| * arguments that aren't a part of the trailing array. |
| */ |
| private final Range copyRange; |
| |
| Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) { |
| super(spreaderType); |
| this.target = target; |
| // Copy all arguments except the last argument (which is the trailing array argument |
| // that needs to be spread). |
| arrayOffset = spreaderType.parameterCount() - 1; |
| |
| // Get and cache the component type of the input array. |
| final Class<?> componentType = spreaderType.ptypes()[arrayOffset].getComponentType(); |
| if (componentType == null) { |
| throw new AssertionError("Trailing argument must be an array."); |
| } |
| arrayTypeChar = Wrapper.basicTypeChar(componentType); |
| |
| this.numArrayArgs = numArrayArgs; |
| // Copy all args except for the last argument. |
| this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame callerFrame) throws Throwable { |
| // Create a new stack frame for the callee. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| |
| // Copy all arguments except for the trailing array argument. |
| callerFrame.copyRangeTo(targetFrame, copyRange, 0, 0); |
| |
| // Attach the writer, prepare to spread the trailing array arguments into |
| // the callee frame. |
| StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(targetFrame, |
| arrayOffset, |
| copyRange.numReferences, |
| copyRange.numBytes); |
| |
| // Get the array reference and check that its length is as expected. |
| Object arrayObj = callerFrame.getReference( |
| copyRange.numReferences, this.type().ptypes()[arrayOffset]); |
| final int arrayLength = Array.getLength(arrayObj); |
| if (arrayLength != numArrayArgs) { |
| throw new IllegalArgumentException("Invalid array length: " + arrayLength); |
| } |
| |
| final MethodType type = target.type(); |
| switch (arrayTypeChar) { |
| case 'L': |
| spreadArray((Object[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'I': |
| spreadArray((int[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'J': |
| spreadArray((long[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'B': |
| spreadArray((byte[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'S': |
| spreadArray((short[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'C': |
| spreadArray((char[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'Z': |
| spreadArray((boolean[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'F': |
| spreadArray((float[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| case 'D': |
| spreadArray((double[]) arrayObj, writer, type, numArrayArgs, arrayOffset); |
| break; |
| |
| } |
| |
| target.invoke(targetFrame); |
| targetFrame.copyReturnValueTo(callerFrame); |
| } |
| |
| public static void spreadArray(Object[] array, StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| Object o = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(o, argumentType); break; } |
| case 'I': { writer.putNextInt((int) o); break; } |
| case 'J': { writer.putNextLong((long) o); break; } |
| case 'B': { writer.putNextByte((byte) o); break; } |
| case 'S': { writer.putNextShort((short) o); break; } |
| case 'C': { writer.putNextChar((char) o); break; } |
| case 'Z': { writer.putNextBoolean((boolean) o); break; } |
| case 'F': { writer.putNextFloat((float) o); break; } |
| case 'D': { writer.putNextDouble((double) o); break; } |
| } |
| } |
| } |
| |
| public static void spreadArray(int[] array, StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| int j = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(j, argumentType); break; } |
| case 'I': { writer.putNextInt(j); break; } |
| case 'J': { writer.putNextLong(j); break; } |
| case 'F': { writer.putNextFloat(j); break; } |
| case 'D': { writer.putNextDouble(j); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(long[] array, StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| long l = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(l, argumentType); break; } |
| case 'J': { writer.putNextLong(l); break; } |
| case 'F': { writer.putNextFloat((float) l); break; } |
| case 'D': { writer.putNextDouble((double) l); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(byte[] array, |
| StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| byte b = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(b, argumentType); break; } |
| case 'I': { writer.putNextInt(b); break; } |
| case 'J': { writer.putNextLong(b); break; } |
| case 'B': { writer.putNextByte(b); break; } |
| case 'S': { writer.putNextShort(b); break; } |
| case 'F': { writer.putNextFloat(b); break; } |
| case 'D': { writer.putNextDouble(b); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(short[] array, |
| StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| short s = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(s, argumentType); break; } |
| case 'I': { writer.putNextInt(s); break; } |
| case 'J': { writer.putNextLong(s); break; } |
| case 'S': { writer.putNextShort(s); break; } |
| case 'F': { writer.putNextFloat(s); break; } |
| case 'D': { writer.putNextDouble(s); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(char[] array, |
| StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| char c = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(c, argumentType); break; } |
| case 'I': { writer.putNextInt(c); break; } |
| case 'J': { writer.putNextLong(c); break; } |
| case 'C': { writer.putNextChar(c); break; } |
| case 'F': { writer.putNextFloat(c); break; } |
| case 'D': { writer.putNextDouble(c); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(boolean[] array, |
| StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| boolean z = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(z, argumentType); break; } |
| case 'Z': { writer.putNextBoolean(z); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(double[] array, |
| StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| double d = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(d, argumentType); break; } |
| case 'D': { writer.putNextDouble(d); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| |
| public static void spreadArray(float[] array, StackFrameWriter writer, MethodType type, |
| int numArgs, int offset) { |
| final Class<?>[] ptypes = type.ptypes(); |
| for (int i = 0; i < numArgs; ++i) { |
| Class<?> argumentType = ptypes[i + offset]; |
| float f = array[i]; |
| switch (Wrapper.basicTypeChar(argumentType)) { |
| case 'L': { writer.putNextReference(f, argumentType); break; } |
| case 'D': { writer.putNextDouble((double) f); break; } |
| case 'F': { writer.putNextFloat(f); break; } |
| default : { throw new AssertionError(); } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Implements MethodHandle.asCollector. |
| */ |
| static class Collector extends Transformer { |
| private final MethodHandle target; |
| |
| /** |
| * The offset of the trailing array argument in the list of arguments to |
| * this transformer. The array argument is always the last argument. |
| */ |
| private final int arrayOffset; |
| |
| /** |
| * The number of input arguments that will be present in the array. In other words, |
| * this is the expected array length. |
| */ |
| private final int numArrayArgs; |
| |
| /** |
| * The type char of the component type of the array. |
| */ |
| private final char arrayTypeChar; |
| |
| /** |
| * Range of arguments to copy verbatim from the input frame, This will cover all |
| * arguments that aren't a part of the trailing array. |
| */ |
| private final Range copyRange; |
| |
| Collector(MethodHandle delegate, Class<?> arrayType, int length) { |
| super(delegate.type().asCollectorType(arrayType, length)); |
| |
| target = delegate; |
| // Copy all arguments except the last argument (which is the trailing array argument |
| // that needs to be spread). |
| arrayOffset = delegate.type().parameterCount() - 1; |
| arrayTypeChar = Wrapper.basicTypeChar(arrayType.getComponentType()); |
| numArrayArgs = length; |
| |
| // Copy all args except for the last argument. |
| copyRange = EmulatedStackFrame.Range.of(delegate.type(), 0, arrayOffset); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame callerFrame) throws Throwable { |
| // Create a new stack frame for the callee. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| |
| // Copy all arguments except for the trailing array argument. |
| callerFrame.copyRangeTo(targetFrame, copyRange, 0, 0); |
| |
| // Attach the writer, prepare to spread the trailing array arguments into |
| // the callee frame. |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes); |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes); |
| |
| switch (arrayTypeChar) { |
| case 'L': { |
| // Reference arrays are the only case where the component type of the |
| // array we construct might differ from the type of the reference we read |
| // from the stack frame. |
| final Class<?> targetType = target.type().ptypes()[arrayOffset]; |
| final Class<?> targetComponentType = targetType.getComponentType(); |
| final Class<?> adapterComponentType = type().lastParameterType(); |
| |
| Object[] arr = (Object[]) Array.newInstance(targetComponentType, numArrayArgs); |
| for (int i = 0; i < numArrayArgs; ++i) { |
| arr[i] = reader.nextReference(adapterComponentType); |
| } |
| |
| writer.putNextReference(arr, targetType); |
| break; |
| } |
| case 'I': { |
| int[] array = new int[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextInt(); |
| } |
| writer.putNextReference(array, int[].class); |
| break; |
| } |
| case 'J': { |
| long[] array = new long[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextLong(); |
| } |
| writer.putNextReference(array, long[].class); |
| break; |
| } |
| case 'B': { |
| byte[] array = new byte[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextByte(); |
| } |
| writer.putNextReference(array, byte[].class); |
| break; |
| } |
| case 'S': { |
| short[] array = new short[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextShort(); |
| } |
| writer.putNextReference(array, short[].class); |
| break; |
| } |
| case 'C': { |
| char[] array = new char[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextChar(); |
| } |
| writer.putNextReference(array, char[].class); |
| break; |
| } |
| case 'Z': { |
| boolean[] array = new boolean[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextBoolean(); |
| } |
| writer.putNextReference(array, boolean[].class); |
| break; |
| } |
| case 'F': { |
| float[] array = new float[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextFloat(); |
| } |
| writer.putNextReference(array, float[].class); |
| break; |
| } |
| case 'D': { |
| double[] array = new double[numArrayArgs]; |
| for (int i = 0; i < numArrayArgs; ++i) { |
| array[i] = reader.nextDouble(); |
| } |
| writer.putNextReference(array, double[].class); |
| break; |
| } |
| } |
| |
| target.invoke(targetFrame); |
| targetFrame.copyReturnValueTo(callerFrame); |
| } |
| } |
| |
| /* |
| * Implements MethodHandles.filterArguments. |
| */ |
| static class FilterArguments extends Transformer { |
| /** The target handle. */ |
| private final MethodHandle target; |
| /** Index of the first argument to filter */ |
| private final int pos; |
| /** The list of filters to apply */ |
| private final MethodHandle[] filters; |
| |
| FilterArguments(MethodHandle target, int pos, MethodHandle[] filters) { |
| super(deriveType(target, pos, filters)); |
| |
| this.target = target; |
| this.pos = pos; |
| this.filters = filters; |
| |
| } |
| |
| private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) { |
| final Class<?>[] filterArgs = new Class<?>[filters.length]; |
| for (int i = 0; i < filters.length; ++i) { |
| filterArgs[i] = filters[i].type().parameterType(0); |
| } |
| |
| return target.type().replaceParameterTypes(pos, pos + filters.length, filterArgs); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame stackFrame) throws Throwable { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(stackFrame); |
| |
| EmulatedStackFrame transformedFrame = EmulatedStackFrame.create(target.type()); |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(transformedFrame); |
| |
| final Class<?>[] ptypes = target.type().ptypes(); |
| for (int i = 0; i < ptypes.length; ++i) { |
| // Check whether the current argument has a filter associated with it. |
| // If it has no filter, no further action need be taken. |
| final Class<?> ptype = ptypes[i]; |
| final MethodHandle filter; |
| if (i < pos) { |
| filter = null; |
| } else if (i >= pos + filters.length) { |
| filter = null; |
| } else { |
| filter = filters[i - pos]; |
| } |
| |
| if (filter != null) { |
| // Note that filter.type() must be (ptype)ptype - this is checked before |
| // this transformer is created. |
| EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type()); |
| |
| // Copy the next argument from the stack frame to the filter frame. |
| final StackFrameWriter filterWriter = new StackFrameWriter(); |
| filterWriter.attach(filterFrame); |
| copyNext(reader, filterWriter, filter.type().ptypes()[0]); |
| |
| filter.invoke(filterFrame); |
| |
| // Copy the argument back from the filter frame to the stack frame. |
| final StackFrameReader filterReader = new StackFrameReader(); |
| filterReader.attach(filterFrame); |
| filterReader.makeReturnValueAccessor(); |
| copyNext(filterReader, writer, ptype); |
| } else { |
| // There's no filter associated with this frame, just copy the next argument |
| // over. |
| copyNext(reader, writer, ptype); |
| } |
| } |
| |
| target.invoke(transformedFrame); |
| transformedFrame.copyReturnValueTo(stackFrame); |
| } |
| } |
| |
| /** |
| * Implements MethodHandles.collectArguments. |
| */ |
| static class CollectArguments extends Transformer { |
| private final MethodHandle target; |
| private final MethodHandle collector; |
| private final int pos; |
| |
| /** The range of input arguments we copy to the collector. */ |
| private final Range collectorRange; |
| |
| /** |
| * The first range of arguments we copy to the target. These are arguments |
| * in the range [0, pos). Note that arg[pos] is the return value of the filter. |
| */ |
| private final Range range1; |
| |
| /** |
| * The second range of arguments we copy to the target. These are arguments in the range |
| * (pos, N], where N is the number of target arguments. |
| */ |
| private final Range range2; |
| |
| private final int referencesOffset; |
| private final int stackFrameOffset; |
| |
| CollectArguments(MethodHandle target, MethodHandle collector, int pos, |
| MethodType adapterType) { |
| super(adapterType); |
| |
| this.target = target; |
| this.collector = collector; |
| this.pos = pos; |
| |
| final int numFilterArgs = collector.type().parameterCount(); |
| final int numAdapterArgs = type().parameterCount(); |
| collectorRange = Range.of(type(), pos, pos + numFilterArgs); |
| |
| range1 = Range.of(type(), 0, pos); |
| if (pos + numFilterArgs < numAdapterArgs) { |
| this.range2 = Range.of(type(), pos + numFilterArgs, numAdapterArgs); |
| } else { |
| this.range2 = null; |
| } |
| |
| // Calculate the number of primitive bytes (or references) we copy to the |
| // target frame based on the return value of the combiner. |
| final Class<?> collectorRType = collector.type().rtype(); |
| if (collectorRType == void.class) { |
| stackFrameOffset = 0; |
| referencesOffset = 0; |
| } else if (collectorRType.isPrimitive()) { |
| stackFrameOffset = EmulatedStackFrame.getSize(collectorRType); |
| referencesOffset = 0; |
| } else { |
| stackFrameOffset = 0; |
| referencesOffset = 1; |
| } |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame stackFrame) throws Throwable { |
| // First invoke the collector. |
| EmulatedStackFrame filterFrame = EmulatedStackFrame.create(collector.type()); |
| stackFrame.copyRangeTo(filterFrame, collectorRange, 0, 0); |
| collector.invoke(filterFrame); |
| |
| // Start constructing the target frame. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| stackFrame.copyRangeTo(targetFrame, range1, 0, 0); |
| |
| // If one of these offsets is not zero, we have a return value to copy. |
| if (referencesOffset != 0 || stackFrameOffset != 0) { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(filterFrame).makeReturnValueAccessor(); |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(targetFrame, pos, range1.numReferences, range1.numBytes); |
| copyNext(reader, writer, target.type().ptypes()[0]); |
| } |
| |
| if (range2 != null) { |
| stackFrame.copyRangeTo(targetFrame, range2, |
| range1.numReferences + referencesOffset, |
| range2.numBytes + stackFrameOffset); |
| } |
| |
| target.invoke(targetFrame); |
| targetFrame.copyReturnValueTo(stackFrame); |
| } |
| } |
| |
| /** |
| * Implements MethodHandles.foldArguments. |
| */ |
| static class FoldArguments extends Transformer { |
| private final MethodHandle target; |
| private final MethodHandle combiner; |
| |
| private final Range combinerArgs; |
| private final Range targetArgs; |
| |
| private final int referencesOffset; |
| private final int stackFrameOffset; |
| |
| FoldArguments(MethodHandle target, MethodHandle combiner) { |
| super(deriveType(target, combiner)); |
| |
| this.target = target; |
| this.combiner = combiner; |
| |
| combinerArgs = Range.all(combiner.type()); |
| targetArgs = Range.all(type()); |
| |
| final Class<?> combinerRType = combiner.type().rtype(); |
| if (combinerRType == void.class) { |
| stackFrameOffset = 0; |
| referencesOffset = 0; |
| } else if (combinerRType.isPrimitive()) { |
| stackFrameOffset = EmulatedStackFrame.getSize(combinerRType); |
| referencesOffset = 0; |
| } else { |
| stackFrameOffset = 0; |
| referencesOffset = 1; |
| } |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame stackFrame) throws Throwable { |
| // First construct the combiner frame and invoke it. |
| EmulatedStackFrame combinerFrame = EmulatedStackFrame.create(combiner.type()); |
| stackFrame.copyRangeTo(combinerFrame, combinerArgs, 0, 0); |
| combiner.invoke(combinerFrame); |
| |
| // Create the stack frame for the target. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| |
| // If one of these offsets is not zero, we have a return value to copy. |
| if (referencesOffset != 0 || stackFrameOffset != 0) { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(combinerFrame).makeReturnValueAccessor(); |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(targetFrame); |
| copyNext(reader, writer, target.type().ptypes()[0]); |
| } |
| |
| stackFrame.copyRangeTo(targetFrame, targetArgs, referencesOffset, stackFrameOffset); |
| target.invoke(targetFrame); |
| |
| targetFrame.copyReturnValueTo(stackFrame); |
| } |
| |
| private static MethodType deriveType(MethodHandle target, MethodHandle combiner) { |
| if (combiner.type().rtype() == void.class) { |
| return target.type(); |
| } |
| |
| return target.type().dropParameterTypes(0, 1); |
| } |
| } |
| |
| /** |
| * Implements MethodHandles.insertArguments. |
| */ |
| static class InsertArguments extends Transformer { |
| private final MethodHandle target; |
| private final int pos; |
| private final Object[] values; |
| |
| private final Range range1; |
| private final Range range2; |
| |
| InsertArguments(MethodHandle target, int pos, Object[] values) { |
| super(target.type().dropParameterTypes(pos, pos + values.length)); |
| this.target = target; |
| this.pos = pos; |
| this.values = values; |
| |
| final MethodType type = type(); |
| range1 = EmulatedStackFrame.Range.of(type, 0, pos); |
| range2 = Range.of(type, pos, type.parameterCount()); |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame stackFrame) throws Throwable { |
| EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type()); |
| |
| // Copy all arguments before |pos|. |
| stackFrame.copyRangeTo(calleeFrame, range1, 0, 0); |
| |
| // Attach a stack frame writer so that we can copy the next |values.length| |
| // arguments. |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(calleeFrame, pos, range1.numReferences, range1.numBytes); |
| |
| // Copy all the arguments supplied in |values|. |
| int referencesCopied = 0; |
| int bytesCopied = 0; |
| final Class<?>[] ptypes = target.type().ptypes(); |
| for (int i = 0; i < values.length; ++i) { |
| final Class<?> ptype = ptypes[i + pos]; |
| if (ptype.isPrimitive()) { |
| if (ptype == boolean.class) { |
| writer.putNextBoolean((boolean) values[i]); |
| } else if (ptype == byte.class) { |
| writer.putNextByte((byte) values[i]); |
| } else if (ptype == char.class) { |
| writer.putNextChar((char) values[i]); |
| } else if (ptype == short.class) { |
| writer.putNextShort((short) values[i]); |
| } else if (ptype == int.class) { |
| writer.putNextInt((int) values[i]); |
| } else if (ptype == long.class) { |
| writer.putNextLong((long) values[i]); |
| } else if (ptype == float.class) { |
| writer.putNextFloat((float) values[i]); |
| } else if (ptype == double.class) { |
| writer.putNextDouble((double) values[i]); |
| } |
| |
| bytesCopied += EmulatedStackFrame.getSize(ptype); |
| } else { |
| writer.putNextReference(values[i], ptype); |
| referencesCopied++; |
| } |
| } |
| |
| // Copy all remaining arguments. |
| if (range2 != null) { |
| stackFrame.copyRangeTo(calleeFrame, range2, |
| range1.numReferences + referencesCopied, |
| range1.numBytes + bytesCopied); |
| } |
| |
| target.invoke(calleeFrame); |
| calleeFrame.copyReturnValueTo(stackFrame); |
| } |
| } |
| |
| |
| /** |
| * Implements {@link java.lang.invokeMethodHandles#explicitCastArguments()}. |
| */ |
| public static class ExplicitCastArguments extends Transformer { |
| private final MethodHandle target; |
| |
| public ExplicitCastArguments(MethodHandle target, MethodType type) { |
| super(type); |
| this.target = target; |
| } |
| |
| @Override |
| public void transform(EmulatedStackFrame callerFrame) throws Throwable { |
| // Create a new stack frame for the target. |
| EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type()); |
| |
| explicitCastArguments(callerFrame, targetFrame); |
| target.invoke(targetFrame); |
| explicitCastReturnValue(callerFrame, targetFrame); |
| } |
| |
| private void explicitCastArguments(final EmulatedStackFrame callerFrame, |
| final EmulatedStackFrame targetFrame) { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(callerFrame); |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(targetFrame); |
| |
| final Class<?>[] fromTypes = type().ptypes(); |
| final Class<?>[] toTypes = target.type().ptypes(); |
| for (int i = 0; i < fromTypes.length; ++i) { |
| explicitCast(reader, fromTypes[i], writer, toTypes[i]); |
| } |
| } |
| |
| private void explicitCastReturnValue(final EmulatedStackFrame callerFrame, |
| final EmulatedStackFrame targetFrame) { |
| Class<?> from = target.type().rtype(); |
| Class<?> to = type().rtype(); |
| if (to != void.class) { |
| final StackFrameWriter writer = new StackFrameWriter(); |
| writer.attach(callerFrame); |
| writer.makeReturnValueAccessor(); |
| if (from == void.class) { |
| if (to.isPrimitive()) { |
| unboxNull(writer, to); |
| } else { |
| writer.putNextReference(null, to); |
| } |
| } else { |
| final StackFrameReader reader = new StackFrameReader(); |
| reader.attach(targetFrame); |
| reader.makeReturnValueAccessor(); |
| explicitCast(reader, target.type().rtype(), writer, type().rtype()); |
| } |
| } |
| } |
| |
| private static void throwUnexpectedType(final Class<?> unexpectedType) { |
| throw new InternalError("Unexpected type: " + unexpectedType); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private static void badCast(final Class<?> from, final Class<?> to) { |
| throw new ClassCastException("Cannot cast " + from.getName() + " to " + to.getName()); |
| } |
| |
| /** |
| * Converts byte value to boolean according to |
| * {@link java.lang.invoke.MethodHandles#explicitCast()} |
| */ |
| private static boolean toBoolean(byte value) { |
| return (value & 1) == 1; |
| } |
| |
| private static byte readPrimitiveAsByte(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (byte) reader.nextByte(); |
| } else if (from == char.class) { |
| return (byte) reader.nextChar(); |
| } else if (from == short.class) { |
| return (byte) reader.nextShort(); |
| } else if (from == int.class) { |
| return (byte) reader.nextInt(); |
| } else if (from == long.class) { |
| return (byte) reader.nextLong(); |
| } else if (from == float.class) { |
| return (byte) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (byte) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? (byte) 1 : (byte) 0; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static char readPrimitiveAsChar(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (char) reader.nextByte(); |
| } else if (from == char.class) { |
| return (char) reader.nextChar(); |
| } else if (from == short.class) { |
| return (char) reader.nextShort(); |
| } else if (from == int.class) { |
| return (char) reader.nextInt(); |
| } else if (from == long.class) { |
| return (char) reader.nextLong(); |
| } else if (from == float.class) { |
| return (char) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (char) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? (char) 1 : (char) 0; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static short readPrimitiveAsShort(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (short) reader.nextByte(); |
| } else if (from == char.class) { |
| return (short) reader.nextChar(); |
| } else if (from == short.class) { |
| return (short) reader.nextShort(); |
| } else if (from == int.class) { |
| return (short) reader.nextInt(); |
| } else if (from == long.class) { |
| return (short) reader.nextLong(); |
| } else if (from == float.class) { |
| return (short) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (short) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? (short) 1 : (short) 0; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static int readPrimitiveAsInt(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (int) reader.nextByte(); |
| } else if (from == char.class) { |
| return (int) reader.nextChar(); |
| } else if (from == short.class) { |
| return (int) reader.nextShort(); |
| } else if (from == int.class) { |
| return (int) reader.nextInt(); |
| } else if (from == long.class) { |
| return (int) reader.nextLong(); |
| } else if (from == float.class) { |
| return (int) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (int) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? 1 : 0; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static long readPrimitiveAsLong(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (long) reader.nextByte(); |
| } else if (from == char.class) { |
| return (long) reader.nextChar(); |
| } else if (from == short.class) { |
| return (long) reader.nextShort(); |
| } else if (from == int.class) { |
| return (long) reader.nextInt(); |
| } else if (from == long.class) { |
| return (long) reader.nextLong(); |
| } else if (from == float.class) { |
| return (long) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (long) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? 1L : 0L; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static float readPrimitiveAsFloat(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (float) reader.nextByte(); |
| } else if (from == char.class) { |
| return (float) reader.nextChar(); |
| } else if (from == short.class) { |
| return (float) reader.nextShort(); |
| } else if (from == int.class) { |
| return (float) reader.nextInt(); |
| } else if (from == long.class) { |
| return (float) reader.nextLong(); |
| } else if (from == float.class) { |
| return (float) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (float) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? 1.0f : 0.0f; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static double readPrimitiveAsDouble(final StackFrameReader reader, |
| final Class<?> from) { |
| if (from == byte.class) { |
| return (double) reader.nextByte(); |
| } else if (from == char.class) { |
| return (double) reader.nextChar(); |
| } else if (from == short.class) { |
| return (double) reader.nextShort(); |
| } else if (from == int.class) { |
| return (double) reader.nextInt(); |
| } else if (from == long.class) { |
| return (double) reader.nextLong(); |
| } else if (from == float.class) { |
| return (double) reader.nextFloat(); |
| } else if (from == double.class) { |
| return (double) reader.nextDouble(); |
| } else if (from == boolean.class) { |
| return reader.nextBoolean() ? 1.0 : 0.0; |
| } else { |
| throwUnexpectedType(from); |
| return 0; |
| } |
| } |
| |
| private static void explicitCastPrimitives(final StackFrameReader reader, |
| final Class<?> from, |
| final StackFrameWriter writer, |
| final Class<?> to) { |
| if (to == byte.class) { |
| byte value = readPrimitiveAsByte(reader, from); |
| writer.putNextByte(value); |
| } else if (to == char.class) { |
| char value = readPrimitiveAsChar(reader, from); |
| writer.putNextChar(value); |
| } else if (to == short.class) { |
| short value = readPrimitiveAsShort(reader, from); |
| writer.putNextShort(value); |
| } else if (to == int.class) { |
| int value = readPrimitiveAsInt(reader, from); |
| writer.putNextInt(value); |
| } else if (to == long.class) { |
| long value = readPrimitiveAsLong(reader, from); |
| writer.putNextLong(value); |
| } else if (to == float.class) { |
| float value = readPrimitiveAsFloat(reader, from); |
| writer.putNextFloat(value); |
| } else if (to == double.class) { |
| double value = readPrimitiveAsDouble(reader, from); |
| writer.putNextDouble(value); |
| } else if (to == boolean.class) { |
| byte byteValue = readPrimitiveAsByte(reader, from); |
| writer.putNextBoolean(toBoolean(byteValue)); |
| } else { |
| throwUnexpectedType(to); |
| } |
| } |
| |
| private static void unboxNull(final StackFrameWriter writer, final Class<?> to) { |
| if (to == boolean.class) { |
| writer.putNextBoolean(false); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) 0); |
| } else if (to == char.class) { |
| writer.putNextChar((char) 0); |
| } else if (to == short.class) { |
| writer.putNextShort((short) 0); |
| } else if (to == int.class) { |
| writer.putNextInt((int) 0); |
| } else if (to == long.class) { |
| writer.putNextLong((long) 0); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) 0); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) 0); |
| } else { |
| throwUnexpectedType(to); |
| } |
| } |
| |
| private static void unboxNonNull(final Object ref, final Class<?> from, |
| final StackFrameWriter writer, final Class<?> to) { |
| if (from == Boolean.class) { |
| boolean z = (boolean) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean(z); |
| } else if (to == byte.class) { |
| writer.putNextByte(z ? (byte) 1 : (byte) 0); |
| } else if (to == short.class) { |
| writer.putNextShort(z ? (short) 1 : (short) 0); |
| } else if (to == char.class) { |
| writer.putNextChar(z ? (char) 1 : (char) 0); |
| } else if (to == int.class) { |
| writer.putNextInt(z ? 1 : 0); |
| } else if (to == long.class) { |
| writer.putNextLong(z ? 1l : 0l); |
| } else if (to == float.class) { |
| writer.putNextFloat(z ? 1.0f : 0.0f); |
| } else if (to == double.class) { |
| writer.putNextDouble(z ? 1.0 : 0.0); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Byte.class) { |
| byte b = (byte) ref; |
| if (to == byte.class) { |
| writer.putNextByte(b); |
| } else if (to == boolean.class) { |
| writer.putNextBoolean(toBoolean(b)); |
| } else if (to == short.class) { |
| writer.putNextShort((short) b); |
| } else if (to == char.class) { |
| writer.putNextChar((char) b); |
| } else if (to == int.class) { |
| writer.putNextInt((int) b); |
| } else if (to == long.class) { |
| writer.putNextLong((long) b); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) b); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) b); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Short.class) { |
| short s = (short) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean((s & 1) == 1); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) s); |
| } else if (to == short.class) { |
| writer.putNextShort(s); |
| } else if (to == char.class) { |
| writer.putNextChar((char) s); |
| } else if (to == int.class) { |
| writer.putNextInt((int) s); |
| } else if (to == long.class) { |
| writer.putNextLong((long) s); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) s); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) s); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Character.class) { |
| char c = (char) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean((c & (char) 1) == (char) 1); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) c); |
| } else if (to == short.class) { |
| writer.putNextShort((short) c); |
| } else if (to == char.class) { |
| writer.putNextChar(c); |
| } else if (to == int.class) { |
| writer.putNextInt((int) c); |
| } else if (to == long.class) { |
| writer.putNextLong((long) c); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) c); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) c); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Integer.class) { |
| int i = (int) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean((i & 1) == 1); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) i); |
| } else if (to == short.class) { |
| writer.putNextShort((short) i); |
| } else if (to == char.class) { |
| writer.putNextChar((char) i); |
| } else if (to == int.class) { |
| writer.putNextInt(i); |
| } else if (to == long.class) { |
| writer.putNextLong((long) i); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) i); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) i); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Long.class) { |
| long j = (long) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean((j & 1l) == 1l); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) j); |
| } else if (to == short.class) { |
| writer.putNextShort((short) j); |
| } else if (to == char.class) { |
| writer.putNextChar((char) j); |
| } else if (to == int.class) { |
| writer.putNextInt((int) j); |
| } else if (to == long.class) { |
| writer.putNextLong(j); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) j); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) j); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Float.class) { |
| float f = (float) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean(((byte) f & 1) != 0); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) f); |
| } else if (to == short.class) { |
| writer.putNextShort((short) f); |
| } else if (to == char.class) { |
| writer.putNextChar((char) f); |
| } else if (to == int.class) { |
| writer.putNextInt((int) f); |
| } else if (to == long.class) { |
| writer.putNextLong((long) f); |
| } else if (to == float.class) { |
| writer.putNextFloat(f); |
| } else if (to == double.class) { |
| writer.putNextDouble((double) f); |
| } else { |
| badCast(from, to); |
| } |
| } else if (from == Double.class) { |
| double d = (double) ref; |
| if (to == boolean.class) { |
| writer.putNextBoolean(((byte) d & 1) != 0); |
| } else if (to == byte.class) { |
| writer.putNextByte((byte) d); |
| } else if (to == short.class) { |
| writer.putNextShort((short) d); |
| } else if (to == char.class) { |
| writer.putNextChar((char) d); |
| } else if (to == int.class) { |
| writer.putNextInt((int) d); |
| } else if (to == long.class) { |
| writer.putNextLong((long) d); |
| } else if (to == float.class) { |
| writer.putNextFloat((float) d); |
| } else if (to == double.class) { |
| writer.putNextDouble(d); |
| } else { |
| badCast(from, to); |
| } |
| } else { |
| badCast(from, to); |
| } |
| } |
| |
| private static void unbox(final Object ref, final Class<?> from, |
| final StackFrameWriter writer, final Class<?> to) { |
| if (ref == null) { |
| unboxNull(writer, to); |
| } else { |
| unboxNonNull(ref, from, writer, to); |
| } |
| } |
| |
| private static void box(final StackFrameReader reader, final Class<?> from, |
| final StackFrameWriter writer, final Class<?> to) { |
| Object boxed = null; |
| if (from == boolean.class) { |
| boxed = Boolean.valueOf(reader.nextBoolean()); |
| } else if (from == byte.class) { |
| boxed = Byte.valueOf(reader.nextByte()); |
| } else if (from == char.class) { |
| boxed = Character.valueOf(reader.nextChar()); |
| } else if (from == short.class) { |
| boxed = Short.valueOf(reader.nextShort()); |
| } else if (from == int.class) { |
| boxed = Integer.valueOf(reader.nextInt()); |
| } else if (from == long.class) { |
| boxed = Long.valueOf(reader.nextLong()); |
| } else if (from == float.class) { |
| boxed = Float.valueOf(reader.nextFloat()); |
| } else if (from == double.class) { |
| boxed = Double.valueOf(reader.nextDouble()); |
| } else { |
| throwUnexpectedType(from); |
| } |
| writer.putNextReference(to.cast(boxed), to); |
| } |
| |
| private static void explicitCast(final StackFrameReader reader, final Class<?> from, |
| final StackFrameWriter writer, final Class<?> to) { |
| if (from.equals(to)) { |
| StackFrameAccessor.copyNext(reader, writer, from); |
| return; |
| } |
| |
| if (from.isPrimitive()) { |
| if (to.isPrimitive()) { |
| // |from| and |to| are primitive types. |
| explicitCastPrimitives(reader, from, writer, to); |
| } else { |
| // |from| is a primitive type, |to| is a reference type. |
| box(reader, from, writer, to); |
| } |
| } else { |
| // |from| is a reference type. |
| Object ref = reader.nextReference(from); |
| if (to.isPrimitive()) { |
| // |from| is a reference type, |to| is a primitive type, |
| unbox(ref, from, writer, to); |
| } else if (to.isInterface()) { |
| // Pass from without a cast according to description for |
| // {@link java.lang.invoke.MethodHandles#explicitCastArguments()}. |
| writer.putNextReference(ref, to); |
| } else { |
| // |to| and from |from| are reference types, perform class cast check. |
| writer.putNextReference(to.cast(ref), to); |
| } |
| } |
| } |
| } |
| } |