1/*
2 * Copyright 2016 Google Inc.
3 *
4 * This code is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 only, as
6 * published by the Free Software Foundation.  Google designates this
7 * particular file as subject to the "Classpath" exception as provided
8 * by Google in the LICENSE file that accompanied this code.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21package java.lang.invoke;
22
23import dalvik.system.EmulatedStackFrame;
24import dalvik.system.EmulatedStackFrame.Range;
25import dalvik.system.EmulatedStackFrame.StackFrameAccessor;
26import dalvik.system.EmulatedStackFrame.StackFrameReader;
27import dalvik.system.EmulatedStackFrame.StackFrameWriter;
28import java.lang.reflect.Array;
29import java.lang.reflect.Method;
30import java.lang.reflect.Modifier;
31import sun.invoke.util.Wrapper;
32import sun.misc.Unsafe;
33import static dalvik.system.EmulatedStackFrame.StackFrameAccessor.copyNext;
34
35/**
36 * @hide Public for testing only.
37 */
38public class Transformers {
39    private Transformers() {}
40
41    static {
42        try {
43            TRANSFORM_INTERNAL = MethodHandle.class.getDeclaredMethod("transformInternal",
44                    EmulatedStackFrame.class);
45        } catch (NoSuchMethodException nsme) {
46            throw new AssertionError();
47        }
48    }
49
50    /**
51     * Method reference to the private {@code MethodHandle.transformInternal} method. This is
52     * cached here because it's the point of entry for all transformers.
53     */
54    private static final Method TRANSFORM_INTERNAL;
55
56    /** @hide */
57    public static abstract class Transformer extends MethodHandle implements Cloneable {
58        protected Transformer(MethodType type) {
59            super(TRANSFORM_INTERNAL.getArtMethod(), MethodHandle.INVOKE_TRANSFORM, type);
60        }
61
62        protected Transformer(MethodType type, int invokeKind) {
63            super(TRANSFORM_INTERNAL.getArtMethod(), invokeKind, type);
64        }
65
66        @Override
67        public Object clone() throws CloneNotSupportedException {
68            return super.clone();
69        }
70    }
71
72    /**
73     * A method handle that always throws an exception of a specified type.
74     *
75     * The handle declares a nominal return type, which is immaterial to the execution
76     * of the handle because it never returns.
77     *
78     * @hide
79     */
80    public static class AlwaysThrow extends Transformer {
81        private final Class<? extends Throwable> exceptionType;
82
83        public AlwaysThrow(Class<?> nominalReturnType, Class<? extends  Throwable> exType) {
84            super(MethodType.methodType(nominalReturnType, exType));
85            this.exceptionType = exType;
86        }
87
88        @Override
89        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
90            throw emulatedStackFrame.getReference(0, exceptionType);
91        }
92    }
93
94    /**
95     * Implements {@code MethodHandles.dropArguments}.
96     */
97    public static class DropArguments extends Transformer {
98        private final MethodHandle delegate;
99
100        private final EmulatedStackFrame.Range range1;
101
102        /**
103         * Note that {@code range2} will be null if the arguments that are being dropped
104         * are the last {@code n}.
105         */
106        /* @Nullable */ private final EmulatedStackFrame.Range range2;
107
108        public DropArguments(MethodType type, MethodHandle delegate,
109                             int startPos, int numDropped) {
110            super(type);
111
112            this.delegate = delegate;
113
114            // We pre-calculate the ranges of values we have to copy through to the delegate
115            // handle at the time of instantiation so that the actual invoke is performant.
116            this.range1 = EmulatedStackFrame.Range.of(type, 0, startPos);
117            final int numArgs = type.ptypes().length;
118            if (startPos + numDropped < numArgs) {
119                this.range2 = EmulatedStackFrame.Range.of(type, startPos + numDropped, numArgs);
120            } else {
121                this.range2 = null;
122            }
123        }
124
125        @Override
126        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
127            EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(delegate.type());
128
129            emulatedStackFrame.copyRangeTo(calleeFrame, range1,
130                    0 /* referencesStart */, 0 /* stackFrameStart */);
131
132            if (range2 != null) {
133                final int referencesStart = range1.numReferences;
134                final int stackFrameStart = range1.numBytes;
135
136                emulatedStackFrame.copyRangeTo(calleeFrame, range2,
137                        referencesStart, stackFrameStart);
138            }
139
140            delegate.invoke(calleeFrame);
141            calleeFrame.copyReturnValueTo(emulatedStackFrame);
142        }
143    }
144
145    /**
146     * Implements {@code MethodHandles.catchException}.
147     */
148    public static class CatchException extends Transformer {
149        private final MethodHandle target;
150        private final MethodHandle handler;
151        private final Class<?> exType;
152
153        private final EmulatedStackFrame.Range handlerArgsRange;
154
155        public CatchException(MethodHandle target, MethodHandle handler, Class<?> exType) {
156            super(target.type());
157
158            this.target = target;
159            this.handler = handler;
160            this.exType = exType;
161
162            // We only copy the first "count" args, dropping others if required. Note that
163            // we subtract one because the first handler arg is the exception thrown by the
164            // target.
165            handlerArgsRange = EmulatedStackFrame.Range.of(target.type(), 0,
166                    (handler.type().parameterCount() - 1));
167        }
168
169        @Override
170        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
171            try {
172                target.invoke(emulatedStackFrame);
173            } catch (Throwable th) {
174                if (th.getClass() == exType) {
175                    // We've gotten an exception of the appropriate type, so we need to call
176                    // the handler. Create a new frame of the appropriate size.
177                    EmulatedStackFrame fallback = EmulatedStackFrame.create(handler.type());
178
179                    // The first argument to the handler is the actual exception.
180                    fallback.setReference(0, th);
181
182                    // We then copy other arguments that need to be passed through to the handler.
183                    // Note that we might drop arguments at the end, if needed. Note that
184                    // referencesStart == 1 because the first argument is the exception type.
185                    emulatedStackFrame.copyRangeTo(fallback, handlerArgsRange,
186                            1 /* referencesStart */, 0 /* stackFrameStart */);
187
188                    // Perform the invoke and return the appropriate value.
189                    handler.invoke(fallback);
190                    fallback.copyReturnValueTo(emulatedStackFrame);
191                } else {
192                    // The exception is not of the expected type, we throw it.
193                    throw th;
194                }
195            }
196        }
197    }
198
199    /**
200     * Implements {@code MethodHandles.GuardWithTest}.
201     */
202    public static class GuardWithTest extends Transformer {
203        private final MethodHandle test;
204        private final MethodHandle target;
205        private final MethodHandle fallback;
206
207        private final EmulatedStackFrame.Range testArgsRange;
208
209        public GuardWithTest(MethodHandle test, MethodHandle target, MethodHandle fallback) {
210            super(target.type());
211
212            this.test = test;
213            this.target = target;
214            this.fallback = fallback;
215
216            // The test method might have a subset of the arguments of the handle / target.
217            testArgsRange = EmulatedStackFrame.Range.of(target.type(), 0, test.type().parameterCount());
218        }
219
220        @Override
221        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
222            EmulatedStackFrame testFrame = EmulatedStackFrame.create(test.type());
223            emulatedStackFrame.copyRangeTo(testFrame, testArgsRange, 0, 0);
224
225            // We know that the return value for test is going to be boolean.class, so we don't have
226            // to do the copyReturnValue dance.
227            final boolean value = (boolean) test.invoke(testFrame);
228            if (value) {
229                target.invoke(emulatedStackFrame);
230            } else {
231                fallback.invoke(emulatedStackFrame);
232            }
233        }
234    }
235
236    /**
237     * Implementation of MethodHandles.arrayElementGetter for reference types.
238     */
239    public static class ReferenceArrayElementGetter extends Transformer {
240        private final Class<?> arrayClass;
241
242        public ReferenceArrayElementGetter(Class<?> arrayClass) {
243            super(MethodType.methodType(arrayClass.getComponentType(),
244                    new Class<?>[]{arrayClass, int.class}));
245            this.arrayClass = arrayClass;
246        }
247
248        @Override
249        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
250            final StackFrameReader reader = new StackFrameReader();
251            reader.attach(emulatedStackFrame);
252
253            // Read the array object and the index from the stack frame.
254            final Object[] array = (Object[]) reader.nextReference(arrayClass);
255            final int index = reader.nextInt();
256
257            // Write the array element back to the stack frame.
258            final StackFrameWriter writer = new StackFrameWriter();
259            writer.attach(emulatedStackFrame);
260            writer.makeReturnValueAccessor();
261            writer.putNextReference(array[index], arrayClass.getComponentType());
262        }
263    }
264
265    /**
266     * Implementation of MethodHandles.arrayElementSetter for reference types.
267     */
268    public static class ReferenceArrayElementSetter extends Transformer {
269        private final Class<?> arrayClass;
270
271        public ReferenceArrayElementSetter(Class<?> arrayClass) {
272            super(MethodType.methodType(void.class,
273                    new Class<?>[] { arrayClass, int.class, arrayClass.getComponentType() }));
274            this.arrayClass = arrayClass;
275        }
276
277        @Override
278        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
279            final StackFrameReader reader = new StackFrameReader();
280            reader.attach(emulatedStackFrame);
281
282            // Read the array object, index and the value to write from the stack frame.
283            final Object[] array = (Object[]) reader.nextReference(arrayClass);
284            final int index = reader.nextInt();
285            final Object value = reader.nextReference(arrayClass.getComponentType());
286
287            array[index] = value;
288        }
289    }
290
291    /**
292     * Implementation of MethodHandles.identity() for reference types.
293     */
294    public static class ReferenceIdentity extends Transformer {
295        private final Class<?> type;
296
297        public ReferenceIdentity(Class<?> type) {
298            super(MethodType.methodType(type, type));
299            this.type = type;
300        }
301
302        @Override
303        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
304            final StackFrameReader reader = new StackFrameReader();
305            reader.attach(emulatedStackFrame);
306
307            final StackFrameWriter writer = new StackFrameWriter();
308            writer.attach(emulatedStackFrame);
309            writer.makeReturnValueAccessor();
310            writer.putNextReference(reader.nextReference(type), type);
311        }
312    }
313
314    /**
315     * Implementation of MethodHandles.constant.
316     */
317    public static class Constant extends Transformer {
318        private final Class<?> type;
319
320        // NOTE: This implementation turned out to be more awkward than expected becuase
321        // of the type system. We could simplify this considerably at the cost of making
322        // the emulated stack frame API uglier or by transitioning into JNI.
323        //
324        // We could consider implementing this in terms of bind() once that's implemented.
325        // This would then just become : MethodHandles.identity(type).bind(value).
326        private int asInt;
327        private long asLong;
328        private float asFloat;
329        private double asDouble;
330        private Object asReference;
331
332        private char typeChar;
333
334        public Constant(Class<?> type, Object value) {
335            super(MethodType.methodType(type));
336            this.type = type;
337
338            if (!type.isPrimitive()) {
339                asReference = value;
340                typeChar = 'L';
341            } else if (type == int.class) {
342                asInt = (int) value;
343                typeChar = 'I';
344            } else if (type == char.class) {
345                asInt = (int) (char) value;
346                typeChar = 'C';
347            } else if (type == short.class) {
348                asInt = (int) (short) value;
349                typeChar = 'S';
350            } else if (type == byte.class) {
351                asInt = (int) (byte) value;
352                typeChar = 'B';
353            } else if (type == boolean.class) {
354                asInt = ((boolean) value) ? 1 : 0;
355                typeChar = 'Z';
356            } else if (type == long.class) {
357                asLong = (long) value;
358                typeChar = 'J';
359            } else if (type == float.class) {
360                asFloat = (float) value;
361                typeChar = 'F';
362            } else if (type == double.class) {
363                asDouble = (double) value;
364                typeChar = 'D';
365            } else {
366                throw new AssertionError("unknown type: " + typeChar);
367            }
368        }
369
370        @Override
371        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
372            final StackFrameWriter writer = new StackFrameWriter();
373            writer.attach(emulatedStackFrame);
374            writer.makeReturnValueAccessor();
375
376            switch (typeChar) {
377                case 'L' : { writer.putNextReference(asReference, type); break; }
378                case 'I' : { writer.putNextInt(asInt); break; }
379                case 'C' : { writer.putNextChar((char) asInt); break; }
380                case 'S' : { writer.putNextShort((short) asInt); break; }
381                case 'B' : { writer.putNextByte((byte) asInt); break; }
382                case 'Z' : { writer.putNextBoolean(asInt == 1); break; }
383                case 'J' : { writer.putNextLong(asLong); break; }
384                case 'F' : { writer.putNextFloat(asFloat); break; }
385                case 'D' : { writer.putNextDouble(asDouble); break; }
386                default:
387                    throw new AssertionError("Unexpected typeChar: " + typeChar);
388            }
389        }
390    }
391
392    /*package*/ static class Construct extends Transformer {
393        private final MethodHandle constructorHandle;
394        private final EmulatedStackFrame.Range callerRange;
395
396        /*package*/ Construct(MethodHandle constructorHandle, MethodType returnedType) {
397            super(returnedType);
398            this.constructorHandle = constructorHandle;
399            this.callerRange = EmulatedStackFrame.Range.all(type());
400        }
401
402        MethodHandle getConstructorHandle() {
403            return constructorHandle;
404        }
405
406        private static boolean isAbstract(Class<?> klass) {
407            return (klass.getModifiers() & Modifier.ABSTRACT) == Modifier.ABSTRACT;
408        }
409
410        private static void checkInstantiable(Class<?> klass) throws InstantiationException {
411            if (isAbstract(klass)) {
412                String s = klass.isInterface() ? "interface " : "abstract class ";
413                throw new InstantiationException("Can't instantiate " + s + klass);
414            }
415        }
416
417        @Override
418        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
419            final Class<?> receiverType = type().rtype();
420            checkInstantiable(receiverType);
421
422            // Allocate memory for receiver.
423            Object receiver = Unsafe.getUnsafe().allocateInstance(receiverType);
424
425            // The MethodHandle type for the caller has the form of
426            // {rtype=T,ptypes=A1..An}. The constructor MethodHandle is of
427            // the form {rtype=void,ptypes=T,A1...An}. So the frame for
428            // the constructor needs to have a slot with the receiver
429            // in position 0.
430            EmulatedStackFrame constructorFrame =
431                    EmulatedStackFrame.create(constructorHandle.type());
432            constructorFrame.setReference(0, receiver);
433            emulatedStackFrame.copyRangeTo(constructorFrame, callerRange, 1, 0);
434            constructorHandle.invoke(constructorFrame);
435
436            // Set return result for caller.
437            emulatedStackFrame.setReturnValueTo(receiver);
438        }
439    }
440
441    /**
442     * Implements MethodHandle.bindTo.
443     *
444     * @hide
445     */
446    public static class BindTo extends Transformer {
447        private final MethodHandle delegate;
448        private final Object receiver;
449
450        private final EmulatedStackFrame.Range range;
451
452        public BindTo(MethodHandle delegate, Object receiver) {
453            super(delegate.type().dropParameterTypes(0, 1));
454
455            this.delegate = delegate;
456            this.receiver = receiver;
457
458            this.range = EmulatedStackFrame.Range.all(this.type());
459        }
460
461        @Override
462        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
463            // Create a new emulated stack frame with the full type (including the leading
464            // receiver reference).
465            EmulatedStackFrame stackFrame = EmulatedStackFrame.create(delegate.type());
466
467            // The first reference argument must be the receiver.
468            stackFrame.setReference(0, receiver);
469            // Copy all other arguments.
470            emulatedStackFrame.copyRangeTo(stackFrame, range,
471                    1 /* referencesStart */, 0 /* stackFrameStart */);
472
473            // Perform the invoke.
474            delegate.invoke(stackFrame);
475            stackFrame.copyReturnValueTo(emulatedStackFrame);
476        }
477    }
478
479    /**
480     * Implements MethodHandle.filterReturnValue.
481     */
482    public static class FilterReturnValue extends Transformer {
483        private final MethodHandle target;
484        private final MethodHandle filter;
485
486        private final EmulatedStackFrame.Range allArgs;
487
488        public FilterReturnValue(MethodHandle target, MethodHandle filter) {
489            super(MethodType.methodType(filter.type().rtype(), target.type().ptypes()));
490
491            this.target = target;
492            this.filter = filter;
493
494            allArgs = EmulatedStackFrame.Range.all(type());
495        }
496
497        @Override
498        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
499            // Create a new frame with the target's type and copy all arguments over.
500            // This frame differs in return type with |emulatedStackFrame| but will have
501            // the same parameter shapes.
502            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
503            emulatedStackFrame.copyRangeTo(targetFrame, allArgs, 0, 0);
504            target.invoke(targetFrame);
505
506            // Perform the invoke.
507            final StackFrameReader returnValueReader = new StackFrameReader();
508            returnValueReader.attach(targetFrame);
509            returnValueReader.makeReturnValueAccessor();
510
511            // Create an emulated frame for the filter and copy all its arguments across.
512            EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
513            final StackFrameWriter filterWriter = new StackFrameWriter();
514            filterWriter.attach(filterFrame);
515
516            final Class<?> returnType = target.type().rtype();
517            if (!returnType.isPrimitive()) {
518                filterWriter.putNextReference(returnValueReader.nextReference(returnType),
519                        returnType);
520            } else if (returnType == boolean.class) {
521                filterWriter.putNextBoolean(returnValueReader.nextBoolean());
522            } else if (returnType == byte.class) {
523                filterWriter.putNextByte(returnValueReader.nextByte());
524            } else if (returnType == char.class) {
525                filterWriter.putNextChar(returnValueReader.nextChar());
526            } else if (returnType == short.class) {
527                filterWriter.putNextShort(returnValueReader.nextShort());
528            } else if (returnType == int.class) {
529                filterWriter.putNextInt(returnValueReader.nextInt());
530            } else if (returnType == long.class) {
531                filterWriter.putNextLong(returnValueReader.nextLong());
532            } else if (returnType == float.class) {
533                filterWriter.putNextFloat(returnValueReader.nextFloat());
534            } else if (returnType == double.class) {
535                filterWriter.putNextDouble(returnValueReader.nextDouble());
536            }
537
538            // Invoke the filter and copy its return value back to the original frame.
539            filter.invoke(filterFrame);
540            filterFrame.copyReturnValueTo(emulatedStackFrame);
541        }
542    }
543
544    /*
545     * Implements MethodHandles.permuteArguments.
546     *
547     * @hide
548     */
549    public static class PermuteArguments extends Transformer {
550        private final MethodHandle target;
551        private final int[] reorder;
552
553        public PermuteArguments(MethodType type, MethodHandle target, int[] reorder) {
554            super(type);
555
556            this.target = target;
557            this.reorder = reorder;
558        }
559
560        @Override
561        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
562            final StackFrameReader reader = new StackFrameReader();
563            reader.attach(emulatedStackFrame);
564
565            // In the interests of simplicity, we box / unbox arguments while performing
566            // the permutation. We first iterate through the incoming stack frame and box
567            // each argument. We then unbox and write out the argument to the target frame
568            // according to the specified reordering.
569            Object[] arguments = new Object[reorder.length];
570            final Class<?>[] ptypes = type().ptypes();
571            for (int i = 0; i < ptypes.length; ++i) {
572                final Class<?> ptype = ptypes[i];
573                if (!ptype.isPrimitive()) {
574                    arguments[i] = reader.nextReference(ptype);
575                } else if (ptype == boolean.class) {
576                    arguments[i] = reader.nextBoolean();
577                } else if (ptype == byte.class) {
578                    arguments[i] = reader.nextByte();
579                } else if (ptype == char.class) {
580                    arguments[i] = reader.nextChar();
581                } else if (ptype == short.class) {
582                    arguments[i] = reader.nextShort();
583                } else if (ptype == int.class) {
584                    arguments[i] = reader.nextInt();
585                } else if (ptype == long.class) {
586                    arguments[i] = reader.nextLong();
587                } else if (ptype == float.class) {
588                    arguments[i] = reader.nextFloat();
589                } else if (ptype == double.class) {
590                    arguments[i] = reader.nextDouble();
591                } else {
592                    throw new AssertionError("Unexpected type: " + ptype);
593                }
594            }
595
596            EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type());
597            final StackFrameWriter writer = new StackFrameWriter();
598            writer.attach(calleeFrame);
599
600            for (int i = 0; i < ptypes.length; ++i) {
601                int idx = reorder[i];
602                final Class<?> ptype = ptypes[idx];
603                final Object argument = arguments[idx];
604
605                if (!ptype.isPrimitive()) {
606                    writer.putNextReference(argument, ptype);
607                } else if (ptype == boolean.class) {
608                    writer.putNextBoolean((boolean) argument);
609                } else if (ptype == byte.class) {
610                    writer.putNextByte((byte) argument);
611                } else if (ptype == char.class) {
612                    writer.putNextChar((char) argument);
613                } else if (ptype == short.class) {
614                    writer.putNextShort((short) argument);
615                } else if (ptype == int.class) {
616                    writer.putNextInt((int) argument);
617                } else if (ptype == long.class) {
618                    writer.putNextLong((long) argument);
619                } else if (ptype == float.class) {
620                    writer.putNextFloat((float) argument);
621                } else if (ptype == double.class) {
622                    writer.putNextDouble((double) argument);
623                } else {
624                    throw new AssertionError("Unexpected type: " + ptype);
625                }
626            }
627
628            target.invoke(calleeFrame);
629            calleeFrame.copyReturnValueTo(emulatedStackFrame);
630        }
631    }
632
633    /**
634     * Converts methods with a trailing array argument to variable arity
635     * methods. So (A,B,C[])R can be invoked with any number of convertible
636     * arguments after B, e.g. (A,B)R or (A, B, C0)R or (A, B, C0...Cn)R.
637     *
638     * @hide
639     */
640    /*package*/ static class VarargsCollector extends Transformer {
641        final MethodHandle target;
642
643        /*package*/ VarargsCollector(MethodHandle target) {
644            super(target.type(), MethodHandle.INVOKE_CALLSITE_TRANSFORM);
645            if (!lastParameterTypeIsAnArray(target.type().ptypes())) {
646                throw new IllegalArgumentException("target does not have array as last parameter");
647            }
648            this.target = target;
649        }
650
651        private static boolean lastParameterTypeIsAnArray(Class<?>[] parameterTypes) {
652            if (parameterTypes.length == 0) return false;
653            return parameterTypes[parameterTypes.length - 1].isArray();
654        }
655
656        @Override
657        public boolean isVarargsCollector() { return true; }
658
659        @Override
660        public MethodHandle asFixedArity() { return target; }
661
662        @Override
663        public void transform(EmulatedStackFrame callerFrame) throws Throwable {
664            MethodType callerFrameType = callerFrame.getMethodType();
665            Class<?>[] callerPTypes = callerFrameType.ptypes();
666            Class<?>[] targetPTypes = type().ptypes();
667
668            int lastTargetIndex = targetPTypes.length - 1;
669            if (callerPTypes.length == targetPTypes.length &&
670                targetPTypes[lastTargetIndex].isAssignableFrom(callerPTypes[lastTargetIndex])) {
671                // Caller frame matches target frame in the arity array parameter. Invoke
672                // immediately, and let the invoke() dispatch perform any necessary conversions
673                // on the other parameters present.
674                target.invoke(callerFrame);
675                return;
676            }
677
678            if (callerPTypes.length < targetPTypes.length - 1) {
679                // Too few arguments to be compatible with variable arity invocation.
680                throwWrongMethodTypeException(callerFrameType, type());
681            }
682
683            if (!MethodType.canConvert(type().rtype(), callerFrameType.rtype())) {
684                // Incompatible return type.
685                throwWrongMethodTypeException(callerFrameType, type());
686            }
687
688            Class<?> elementType = targetPTypes[lastTargetIndex].getComponentType();
689            if (!arityArgumentsConvertible(callerPTypes, lastTargetIndex, elementType)) {
690                // Wrong types to be compatible with variable arity invocation.
691                throwWrongMethodTypeException(callerFrameType, type());
692            }
693
694            // Allocate targetFrame.
695            MethodType targetFrameType = makeTargetFrameType(callerFrameType, type());
696            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(targetFrameType);
697            prepareFrame(callerFrame, targetFrame);
698
699            // Invoke target.
700            target.invoke(targetFrame);
701
702            // Copy return value to the caller's frame.
703            targetFrame.copyReturnValueTo(callerFrame);
704        }
705
706        private static void throwWrongMethodTypeException(MethodType from, MethodType to) {
707            throw new WrongMethodTypeException("Cannot convert " + from + " to " + to);
708        }
709
710        private static boolean arityArgumentsConvertible(Class<?>[] ptypes, int arityStart,
711                                                         Class<?> elementType) {
712            if (ptypes.length - 1 == arityStart) {
713                if (ptypes[arityStart].isArray() &&
714                    ptypes[arityStart].getComponentType() == elementType) {
715                    // The last ptype is in the same position as the arity
716                    // array and has the same type.
717                    return true;
718                }
719            }
720
721            for (int i = arityStart; i < ptypes.length; ++i) {
722                if (!MethodType.canConvert(ptypes[i], elementType)) {
723                    return false;
724                }
725            }
726            return true;
727        }
728
729        private static Object referenceArray(StackFrameReader reader, Class<?>[] ptypes,
730                                             Class<?> elementType, int offset, int length) {
731            Object arityArray = Array.newInstance(elementType, length);
732            for (int i = 0; i < length; ++i) {
733                Class<?> argumentType = ptypes[i + offset];
734                Object o = null;
735                switch (Wrapper.basicTypeChar(argumentType)) {
736                    case 'L': { o = reader.nextReference(argumentType); break; }
737                    case 'I': { o = reader.nextInt(); break; }
738                    case 'J': { o = reader.nextLong(); break; }
739                    case 'B': { o = reader.nextByte(); break; }
740                    case 'S': { o = reader.nextShort(); break; }
741                    case 'C': { o = reader.nextChar(); break; }
742                    case 'Z': { o = reader.nextBoolean(); break; }
743                    case 'F': { o = reader.nextFloat(); break; }
744                    case 'D': { o = reader.nextDouble(); break; }
745                }
746                Array.set(arityArray, i, elementType.cast(o));
747            }
748            return arityArray;
749        }
750
751        private static Object intArray(StackFrameReader reader, Class<?> ptypes[],
752                                       int offset, int length) {
753            int[] arityArray = new int[length];
754            for (int i = 0; i < length; ++i) {
755                Class<?> argumentType = ptypes[i + offset];
756                switch (Wrapper.basicTypeChar(argumentType)) {
757                    case 'I': { arityArray[i] = reader.nextInt(); break; }
758                    case 'S': { arityArray[i] = reader.nextShort(); break; }
759                    case 'B': { arityArray[i] = reader.nextByte(); break; }
760                    default: {
761                        arityArray[i] = (Integer) reader.nextReference(argumentType);
762                        break;
763                    }
764                }
765            }
766            return arityArray;
767        }
768
769        private static Object longArray(StackFrameReader reader, Class<?> ptypes[],
770                                        int offset, int length) {
771            long[] arityArray = new long[length];
772            for (int i = 0; i < length; ++i) {
773                Class<?> argumentType = ptypes[i + offset];
774                switch (Wrapper.basicTypeChar(argumentType)) {
775                    case 'J': { arityArray[i] = reader.nextLong(); break; }
776                    case 'I': { arityArray[i] = reader.nextInt(); break; }
777                    case 'S': { arityArray[i] = reader.nextShort(); break; }
778                    case 'B': { arityArray[i] = reader.nextByte(); break; }
779                    default: { arityArray[i] = (Long) reader.nextReference(argumentType); break; }
780                }
781            }
782            return arityArray;
783        }
784
785        private static Object byteArray(StackFrameReader reader, Class<?> ptypes[],
786                                        int offset, int length) {
787            byte[] arityArray = new byte[length];
788            for (int i = 0; i < length; ++i) {
789                Class<?> argumentType = ptypes[i + offset];
790                switch (Wrapper.basicTypeChar(argumentType)) {
791                    case 'B': { arityArray[i] = reader.nextByte(); break; }
792                    default: { arityArray[i] = (Byte) reader.nextReference(argumentType); break; }
793                }
794            }
795            return arityArray;
796        }
797
798        private static Object shortArray(StackFrameReader reader, Class<?> ptypes[],
799                                        int offset, int length) {
800            short[] arityArray = new short[length];
801            for (int i = 0; i < length; ++i) {
802                Class<?> argumentType = ptypes[i + offset];
803                switch (Wrapper.basicTypeChar(argumentType)) {
804                    case 'S': { arityArray[i] = reader.nextShort(); break; }
805                    case 'B': { arityArray[i] = reader.nextByte(); break; }
806                    default: { arityArray[i] = (Short) reader.nextReference(argumentType); break; }
807                }
808            }
809            return arityArray;
810        }
811
812        private static Object charArray(StackFrameReader reader, Class<?> ptypes[],
813                                        int offset, int length) {
814            char[] arityArray = new char[length];
815            for (int i = 0; i < length; ++i) {
816                Class<?> argumentType = ptypes[i + offset];
817                switch (Wrapper.basicTypeChar(argumentType)) {
818                    case 'C': { arityArray[i] = reader.nextChar(); break; }
819                    default: {
820                        arityArray[i] = (Character) reader.nextReference(argumentType);
821                        break;
822                    }
823                }
824            }
825            return arityArray;
826        }
827
828        private static Object booleanArray(StackFrameReader reader, Class<?> ptypes[],
829                                        int offset, int length) {
830            boolean[] arityArray = new boolean[length];
831            for (int i = 0; i < length; ++i) {
832                Class<?> argumentType = ptypes[i + offset];
833                switch (Wrapper.basicTypeChar(argumentType)) {
834                    case 'Z': { arityArray[i] = reader.nextBoolean(); break; }
835                    default:
836                        arityArray[i] = (Boolean) reader.nextReference(argumentType);
837                        break;
838                }
839            }
840            return arityArray;
841        }
842
843        private static Object floatArray(StackFrameReader reader, Class<?> ptypes[],
844                                        int offset, int length) {
845            float[] arityArray = new float[length];
846            for (int i = 0; i < length; ++i) {
847                Class<?> argumentType = ptypes[i + offset];
848                switch (Wrapper.basicTypeChar(argumentType)) {
849                    case 'F': { arityArray[i] = reader.nextFloat(); break; }
850                    case 'J': { arityArray[i] = reader.nextLong(); break; }
851                    case 'I': { arityArray[i] = reader.nextInt(); break; }
852                    case 'S': { arityArray[i] = reader.nextShort(); break; }
853                    case 'B': { arityArray[i] = reader.nextByte(); break; }
854                    default: {
855                        arityArray[i] = (Float) reader.nextReference(argumentType);
856                        break;
857                    }
858                }
859            }
860            return arityArray;
861        }
862
863        private static Object doubleArray(StackFrameReader reader, Class<?> ptypes[],
864                                        int offset, int length) {
865            double[] arityArray = new double[length];
866            for (int i = 0; i < length; ++i) {
867                Class<?> argumentType = ptypes[i + offset];
868                switch (Wrapper.basicTypeChar(argumentType)) {
869                    case 'D': { arityArray[i] = reader.nextDouble(); break; }
870                    case 'F': { arityArray[i] = reader.nextFloat(); break; }
871                    case 'J': { arityArray[i] = reader.nextLong(); break; }
872                    case 'I': { arityArray[i] = reader.nextInt(); break; }
873                    case 'S': { arityArray[i] = reader.nextShort(); break; }
874                    case 'B': { arityArray[i] = reader.nextByte(); break; }
875                    default: {
876                        arityArray[i] = (Double) reader.nextReference(argumentType);
877                        break;
878                    }
879                }
880            }
881            return arityArray;
882        }
883
884        private static Object makeArityArray(MethodType callerFrameType,
885                                             StackFrameReader callerFrameReader,
886                                             int indexOfArityArray,
887                                             Class<?> arityArrayType) {
888            int arityArrayLength = callerFrameType.ptypes().length - indexOfArityArray;
889            Class<?> elementType = arityArrayType.getComponentType();
890            Class<?>[] callerPTypes = callerFrameType.ptypes();
891
892            char elementBasicType = Wrapper.basicTypeChar(elementType);
893            switch (elementBasicType) {
894                case 'L': return referenceArray(callerFrameReader, callerPTypes, elementType,
895                                                indexOfArityArray, arityArrayLength);
896                case 'I': return intArray(callerFrameReader, callerPTypes,
897                                          indexOfArityArray, arityArrayLength);
898                case 'J': return longArray(callerFrameReader, callerPTypes,
899                                           indexOfArityArray, arityArrayLength);
900                case 'B': return byteArray(callerFrameReader, callerPTypes,
901                                           indexOfArityArray, arityArrayLength);
902                case 'S': return shortArray(callerFrameReader, callerPTypes,
903                                            indexOfArityArray, arityArrayLength);
904                case 'C': return charArray(callerFrameReader, callerPTypes,
905                                           indexOfArityArray, arityArrayLength);
906                case 'Z': return booleanArray(callerFrameReader, callerPTypes,
907                                              indexOfArityArray, arityArrayLength);
908                case 'F': return floatArray(callerFrameReader, callerPTypes,
909                                            indexOfArityArray, arityArrayLength);
910                case 'D': return doubleArray(callerFrameReader, callerPTypes,
911                                             indexOfArityArray, arityArrayLength);
912            }
913            throw new InternalError("Unexpected type: " + elementType);
914        }
915
916        public static Object collectArguments(char basicComponentType, Class<?> componentType,
917                                              StackFrameReader reader, Class<?>[] types,
918                                              int startIdx, int length) {
919            switch (basicComponentType) {
920                case 'L': return referenceArray(reader, types, componentType, startIdx, length);
921                case 'I': return intArray(reader, types, startIdx, length);
922                case 'J': return longArray(reader, types, startIdx, length);
923                case 'B': return byteArray(reader, types, startIdx, length);
924                case 'S': return shortArray(reader, types, startIdx, length);
925                case 'C': return charArray(reader, types, startIdx, length);
926                case 'Z': return booleanArray(reader, types, startIdx, length);
927                case 'F': return floatArray(reader, types, startIdx, length);
928                case 'D': return doubleArray(reader, types, startIdx, length);
929            }
930            throw new InternalError("Unexpected type: " + basicComponentType);
931        }
932
933        private static void copyParameter(StackFrameReader reader, StackFrameWriter writer,
934                                          Class<?> ptype) {
935            switch (Wrapper.basicTypeChar(ptype)) {
936                case 'L': { writer.putNextReference(reader.nextReference(ptype), ptype); break; }
937                case 'I': { writer.putNextInt(reader.nextInt()); break; }
938                case 'J': { writer.putNextLong(reader.nextLong()); break; }
939                case 'B': { writer.putNextByte(reader.nextByte()); break; }
940                case 'S': { writer.putNextShort(reader.nextShort()); break; }
941                case 'C': { writer.putNextChar(reader.nextChar()); break; }
942                case 'Z': { writer.putNextBoolean(reader.nextBoolean()); break; }
943                case 'F': { writer.putNextFloat(reader.nextFloat()); break; }
944                case 'D': { writer.putNextDouble(reader.nextDouble()); break; }
945                default: throw new InternalError("Unexpected type: " + ptype);
946            }
947        }
948
949        private static void prepareFrame(EmulatedStackFrame callerFrame,
950                                         EmulatedStackFrame targetFrame) {
951            StackFrameWriter targetWriter = new StackFrameWriter();
952            targetWriter.attach(targetFrame);
953            StackFrameReader callerReader = new StackFrameReader();
954            callerReader.attach(callerFrame);
955
956            // Copy parameters from |callerFrame| to |targetFrame| leaving room for arity array.
957            MethodType targetMethodType = targetFrame.getMethodType();
958            int indexOfArityArray = targetMethodType.ptypes().length - 1;
959            for (int i = 0; i < indexOfArityArray; ++i) {
960                Class<?> ptype = targetMethodType.ptypes()[i];
961                copyParameter(callerReader, targetWriter, ptype);
962            }
963
964            // Add arity array as last parameter in |targetFrame|.
965            Class<?> arityArrayType = targetMethodType.ptypes()[indexOfArityArray];
966            Object arityArray = makeArityArray(callerFrame.getMethodType(), callerReader,
967                                               indexOfArityArray, arityArrayType);
968            targetWriter.putNextReference(arityArray, arityArrayType);
969        }
970
971        /**
972         * Computes the frame type to invoke the target method handle with. This
973         * is the same as the caller frame type, but with the trailing argument
974         * being the array type that is the trailing argument in the target method
975         * handle.
976         *
977         * Suppose the targetType is (T0, T1, T2[])RT and the callerType is (C0, C1, C2, C3)RC
978         * then the constructed type is (C0, C1, T2[])RC.
979         */
980        private static MethodType makeTargetFrameType(MethodType callerType,
981                                                      MethodType targetType) {
982            final int ptypesLength = targetType.ptypes().length;
983            final Class<?>[] ptypes = new Class<?>[ptypesLength];
984            // Copy types from caller types to new methodType.
985            System.arraycopy(callerType.ptypes(), 0, ptypes, 0, ptypesLength - 1);
986            // Set the last element in the type array to be the
987            // varargs array of the target.
988            ptypes[ptypesLength - 1] = targetType.ptypes()[ptypesLength - 1];
989            return MethodType.methodType(callerType.rtype(), ptypes);
990        }
991    }
992
993    /**
994     * Implements MethodHandles.invoker & MethodHandles.exactInvoker.
995     */
996    static class Invoker extends Transformer {
997        private final MethodType targetType;
998        private final boolean isExactInvoker;
999        private final EmulatedStackFrame.Range copyRange;
1000
1001        Invoker(MethodType targetType, boolean isExactInvoker) {
1002            super(targetType.insertParameterTypes(0, MethodHandle.class));
1003            this.targetType = targetType;
1004            this.isExactInvoker = isExactInvoker;
1005            copyRange = EmulatedStackFrame.Range.of(type(), 1, type().parameterCount());
1006        }
1007
1008        @Override
1009        public void transform(EmulatedStackFrame emulatedStackFrame) throws Throwable {
1010            // We need to artifically throw a WrongMethodTypeException here because we
1011            // can't call invokeExact on the target inside the transformer.
1012            if (isExactInvoker) {
1013                // TODO: We should do the comparison by hand if this new type creation
1014                // on every invoke proves too expensive.
1015                MethodType callType = emulatedStackFrame.getCallsiteType().dropParameterTypes(0, 1);
1016                if (!targetType.equals(callType)) {
1017                    throw new WrongMethodTypeException("Wrong type, Expected: " + targetType
1018                            + " was: " + callType);
1019                }
1020            }
1021
1022            // The first argument to the stack frame is the handle that needs to be invoked.
1023            MethodHandle target = emulatedStackFrame.getReference(0, MethodHandle.class);
1024
1025            // All other arguments must be copied to the target frame.
1026            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(targetType);
1027            emulatedStackFrame.copyRangeTo(targetFrame, copyRange, 0, 0);
1028
1029            // Finally, invoke the handle and copy the return value.
1030            target.invoke(targetFrame);
1031            targetFrame.copyReturnValueTo(emulatedStackFrame);
1032        }
1033    }
1034
1035    /**
1036     * Implements MethodHandle.asSpreader / MethodHandles.spreadInvoker.
1037     */
1038    static class Spreader extends Transformer {
1039        /** The method handle we're delegating to. */
1040        private final MethodHandle target;
1041
1042        /**
1043         * The offset of the trailing array argument in the list of arguments to
1044         * this transformer. The array argument is always the last argument.
1045         */
1046        private final int arrayOffset;
1047
1048        /**
1049         * The type char of the component type of the array.
1050         */
1051        private final char arrayTypeChar;
1052
1053        /**
1054         * The number of input arguments that will be present in the array. In other words,
1055         * this is the expected array length.
1056         */
1057        private final int numArrayArgs;
1058
1059        /**
1060         * Range of arguments to copy verbatim from the input frame, This will cover all
1061         * arguments that aren't a part of the trailing array.
1062         */
1063        private final Range copyRange;
1064
1065        Spreader(MethodHandle target, MethodType spreaderType, int numArrayArgs) {
1066            super(spreaderType);
1067            this.target = target;
1068            // Copy all arguments except the last argument (which is the trailing array argument
1069            // that needs to be spread).
1070            arrayOffset = spreaderType.parameterCount() - 1;
1071
1072            // Get and cache the component type of the input array.
1073            final Class<?> componentType = spreaderType.ptypes()[arrayOffset].getComponentType();
1074            if (componentType == null) {
1075                throw new AssertionError("Trailing argument must be an array.");
1076            }
1077            arrayTypeChar = Wrapper.basicTypeChar(componentType);
1078
1079            this.numArrayArgs = numArrayArgs;
1080            // Copy all args except for the last argument.
1081            this.copyRange = EmulatedStackFrame.Range.of(spreaderType, 0, arrayOffset);
1082        }
1083
1084        @Override
1085        public void transform(EmulatedStackFrame callerFrame) throws Throwable {
1086            // Create a new stack frame for the callee.
1087            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
1088
1089            // Copy all arguments except for the trailing array argument.
1090            callerFrame.copyRangeTo(targetFrame, copyRange, 0, 0);
1091
1092            // Attach the writer, prepare to spread the trailing array arguments into
1093            // the callee frame.
1094            StackFrameWriter writer = new StackFrameWriter();
1095            writer.attach(targetFrame,
1096                    arrayOffset,
1097                    copyRange.numReferences,
1098                    copyRange.numBytes);
1099
1100            // Get the array reference and check that its length is as expected.
1101            Object arrayObj = callerFrame.getReference(
1102                    copyRange.numReferences, this.type().ptypes()[arrayOffset]);
1103            final int arrayLength = Array.getLength(arrayObj);
1104            if (arrayLength != numArrayArgs) {
1105                throw new IllegalArgumentException("Invalid array length: " + arrayLength);
1106            }
1107
1108            final MethodType type = target.type();
1109            switch (arrayTypeChar) {
1110                case 'L':
1111                    spreadArray((Object[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1112                    break;
1113                case 'I':
1114                    spreadArray((int[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1115                    break;
1116                case 'J':
1117                    spreadArray((long[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1118                    break;
1119                case 'B':
1120                    spreadArray((byte[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1121                    break;
1122                case 'S':
1123                    spreadArray((short[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1124                    break;
1125                case 'C':
1126                    spreadArray((char[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1127                    break;
1128                case 'Z':
1129                    spreadArray((boolean[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1130                    break;
1131                case 'F':
1132                    spreadArray((float[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1133                    break;
1134                case 'D':
1135                    spreadArray((double[]) arrayObj, writer, type, numArrayArgs, arrayOffset);
1136                    break;
1137
1138            }
1139
1140            target.invoke(targetFrame);
1141            targetFrame.copyReturnValueTo(callerFrame);
1142        }
1143
1144        public static void spreadArray(Object[] array, StackFrameWriter writer, MethodType type,
1145                                       int numArgs, int offset) {
1146            final Class<?>[] ptypes = type.ptypes();
1147            for (int i = 0; i < numArgs; ++i) {
1148                Class<?> argumentType = ptypes[i + offset];
1149                Object o = array[i];
1150                switch (Wrapper.basicTypeChar(argumentType)) {
1151                    case 'L': { writer.putNextReference(o, argumentType); break; }
1152                    case 'I': { writer.putNextInt((int) o); break; }
1153                    case 'J': { writer.putNextLong((long) o); break; }
1154                    case 'B': { writer.putNextByte((byte) o); break; }
1155                    case 'S': { writer.putNextShort((short) o); break; }
1156                    case 'C': { writer.putNextChar((char) o); break; }
1157                    case 'Z': { writer.putNextBoolean((boolean) o); break; }
1158                    case 'F': { writer.putNextFloat((float) o); break; }
1159                    case 'D': { writer.putNextDouble((double) o); break; }
1160                }
1161            }
1162        }
1163
1164        public static void spreadArray(int[] array, StackFrameWriter writer, MethodType type,
1165                                       int numArgs, int offset) {
1166            final Class<?>[] ptypes = type.ptypes();
1167            for (int i = 0; i < numArgs; ++i) {
1168                Class<?> argumentType = ptypes[i + offset];
1169                int j = array[i];
1170                switch (Wrapper.basicTypeChar(argumentType)) {
1171                    case 'L': { writer.putNextReference(j, argumentType); break; }
1172                    case 'I': { writer.putNextInt(j); break; }
1173                    case 'J': { writer.putNextLong(j); break; }
1174                    case 'F': { writer.putNextFloat(j); break; }
1175                    case 'D': { writer.putNextDouble(j); break; }
1176                    default : { throw new AssertionError(); }
1177                }
1178            }
1179        }
1180
1181        public static void spreadArray(long[] array, StackFrameWriter writer, MethodType type,
1182                                       int numArgs, int offset) {
1183            final Class<?>[] ptypes = type.ptypes();
1184            for (int i = 0; i < numArgs; ++i) {
1185                Class<?> argumentType = ptypes[i + offset];
1186                long l = array[i];
1187                switch (Wrapper.basicTypeChar(argumentType)) {
1188                    case 'L': { writer.putNextReference(l, argumentType); break; }
1189                    case 'J': { writer.putNextLong(l); break; }
1190                    case 'F': { writer.putNextFloat((float) l); break; }
1191                    case 'D': { writer.putNextDouble((double) l); break; }
1192                    default : { throw new AssertionError(); }
1193                }
1194            }
1195        }
1196
1197        public static void spreadArray(byte[] array,
1198                                       StackFrameWriter writer, MethodType type,
1199                                       int numArgs, int offset) {
1200            final Class<?>[] ptypes = type.ptypes();
1201            for (int i = 0; i < numArgs; ++i) {
1202                Class<?> argumentType = ptypes[i + offset];
1203                byte b = array[i];
1204                switch (Wrapper.basicTypeChar(argumentType)) {
1205                    case 'L': { writer.putNextReference(b, argumentType); break; }
1206                    case 'I': { writer.putNextInt(b); break; }
1207                    case 'J': { writer.putNextLong(b); break; }
1208                    case 'B': { writer.putNextByte(b); break; }
1209                    case 'S': { writer.putNextShort(b); break; }
1210                    case 'F': { writer.putNextFloat(b); break; }
1211                    case 'D': { writer.putNextDouble(b); break; }
1212                    default : { throw new AssertionError(); }
1213                }
1214            }
1215        }
1216
1217        public static void spreadArray(short[] array,
1218                                       StackFrameWriter writer, MethodType type,
1219                                       int numArgs, int offset) {
1220            final Class<?>[] ptypes = type.ptypes();
1221            for (int i = 0; i < numArgs; ++i) {
1222                Class<?> argumentType = ptypes[i + offset];
1223                short s = array[i];
1224                switch (Wrapper.basicTypeChar(argumentType)) {
1225                    case 'L': { writer.putNextReference(s, argumentType); break; }
1226                    case 'I': { writer.putNextInt(s); break; }
1227                    case 'J': { writer.putNextLong(s); break; }
1228                    case 'S': { writer.putNextShort(s); break; }
1229                    case 'F': { writer.putNextFloat(s); break; }
1230                    case 'D': { writer.putNextDouble(s); break; }
1231                    default : { throw new AssertionError(); }
1232                }
1233            }
1234        }
1235
1236        public static void spreadArray(char[] array,
1237                                       StackFrameWriter writer, MethodType type,
1238                                       int numArgs, int offset) {
1239            final Class<?>[] ptypes = type.ptypes();
1240            for (int i = 0; i < numArgs; ++i) {
1241                Class<?> argumentType = ptypes[i + offset];
1242                char c = array[i];
1243                switch (Wrapper.basicTypeChar(argumentType)) {
1244                    case 'L': { writer.putNextReference(c, argumentType); break; }
1245                    case 'I': { writer.putNextInt(c); break; }
1246                    case 'J': { writer.putNextLong(c); break; }
1247                    case 'C': { writer.putNextChar(c); break; }
1248                    case 'F': { writer.putNextFloat(c); break; }
1249                    case 'D': { writer.putNextDouble(c); break; }
1250                    default : { throw new AssertionError(); }
1251                }
1252            }
1253        }
1254
1255        public static void spreadArray(boolean[] array,
1256                                       StackFrameWriter writer, MethodType type,
1257                                       int numArgs, int offset) {
1258            final Class<?>[] ptypes = type.ptypes();
1259            for (int i = 0; i < numArgs; ++i) {
1260                Class<?> argumentType = ptypes[i + offset];
1261                boolean z = array[i];
1262                switch (Wrapper.basicTypeChar(argumentType)) {
1263                    case 'L': { writer.putNextReference(z, argumentType); break; }
1264                    case 'Z': { writer.putNextBoolean(z); break; }
1265                    default : { throw new AssertionError(); }
1266                }
1267            }
1268        }
1269
1270        public static void spreadArray(double[] array,
1271                                       StackFrameWriter writer, MethodType type,
1272                                       int numArgs, int offset) {
1273            final Class<?>[] ptypes = type.ptypes();
1274            for (int i = 0; i < numArgs; ++i) {
1275                Class<?> argumentType = ptypes[i + offset];
1276                double d = array[i];
1277                switch (Wrapper.basicTypeChar(argumentType)) {
1278                    case 'L': { writer.putNextReference(d, argumentType); break; }
1279                    case 'D': { writer.putNextDouble(d); break; }
1280                    default : { throw new AssertionError(); }
1281                }
1282            }
1283        }
1284
1285        public static void spreadArray(float[] array, StackFrameWriter writer, MethodType type,
1286                                       int numArgs, int offset) {
1287            final Class<?>[] ptypes = type.ptypes();
1288            for (int i = 0; i < numArgs; ++i) {
1289                Class<?> argumentType = ptypes[i + offset];
1290                float f = array[i];
1291                switch (Wrapper.basicTypeChar(argumentType)) {
1292                    case 'L': { writer.putNextReference(f, argumentType); break; }
1293                    case 'D': { writer.putNextDouble((double) f); break; }
1294                    case 'F': { writer.putNextFloat(f); break; }
1295                    default : { throw new AssertionError(); }
1296                }
1297            }
1298        }
1299    }
1300
1301    /**
1302     * Implements MethodHandle.asCollector.
1303     */
1304    static class Collector extends Transformer {
1305        private final MethodHandle target;
1306
1307        /**
1308         * The offset of the trailing array argument in the list of arguments to
1309         * this transformer. The array argument is always the last argument.
1310         */
1311        private final int arrayOffset;
1312
1313        /**
1314         * The number of input arguments that will be present in the array. In other words,
1315         * this is the expected array length.
1316         */
1317        private final int numArrayArgs;
1318
1319        /**
1320         * The type char of the component type of the array.
1321         */
1322        private final char arrayTypeChar;
1323
1324        /**
1325         * Range of arguments to copy verbatim from the input frame, This will cover all
1326         * arguments that aren't a part of the trailing array.
1327         */
1328        private final Range copyRange;
1329
1330        Collector(MethodHandle delegate, Class<?> arrayType, int length) {
1331            super(delegate.type().asCollectorType(arrayType, length));
1332
1333            target = delegate;
1334            // Copy all arguments except the last argument (which is the trailing array argument
1335            // that needs to be spread).
1336            arrayOffset = delegate.type().parameterCount() - 1;
1337            arrayTypeChar = Wrapper.basicTypeChar(arrayType.getComponentType());
1338            numArrayArgs = length;
1339
1340            // Copy all args except for the last argument.
1341            copyRange = EmulatedStackFrame.Range.of(delegate.type(), 0, arrayOffset);
1342        }
1343
1344        @Override
1345        public void transform(EmulatedStackFrame callerFrame) throws Throwable {
1346            // Create a new stack frame for the callee.
1347            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
1348
1349            // Copy all arguments except for the trailing array argument.
1350            callerFrame.copyRangeTo(targetFrame, copyRange, 0, 0);
1351
1352            // Attach the writer, prepare to spread the trailing array arguments into
1353            // the callee frame.
1354            final StackFrameWriter writer = new StackFrameWriter();
1355            writer.attach(targetFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
1356            final StackFrameReader reader = new StackFrameReader();
1357            reader.attach(callerFrame, arrayOffset, copyRange.numReferences, copyRange.numBytes);
1358
1359            switch (arrayTypeChar) {
1360                case 'L': {
1361                    // Reference arrays are the only case where the component type of the
1362                    // array we construct might differ from the type of the reference we read
1363                    // from the stack frame.
1364                    final Class<?> targetType = target.type().ptypes()[arrayOffset];
1365                    final Class<?> targetComponentType = targetType.getComponentType();
1366                    final Class<?> adapterComponentType = type().lastParameterType();
1367
1368                    Object[] arr = (Object[]) Array.newInstance(targetComponentType, numArrayArgs);
1369                    for (int i = 0; i < numArrayArgs; ++i) {
1370                        arr[i] = reader.nextReference(adapterComponentType);
1371                    }
1372
1373                    writer.putNextReference(arr, targetType);
1374                    break;
1375                }
1376                case 'I': {
1377                    int[] array = new int[numArrayArgs];
1378                    for (int i = 0; i < numArrayArgs; ++i) {
1379                        array[i] = reader.nextInt();
1380                    }
1381                    writer.putNextReference(array, int[].class);
1382                    break;
1383                }
1384                case 'J': {
1385                    long[] array = new long[numArrayArgs];
1386                    for (int i = 0; i < numArrayArgs; ++i) {
1387                        array[i] = reader.nextLong();
1388                    }
1389                    writer.putNextReference(array, long[].class);
1390                    break;
1391                }
1392                case 'B': {
1393                    byte[] array = new byte[numArrayArgs];
1394                    for (int i = 0; i < numArrayArgs; ++i) {
1395                        array[i] = reader.nextByte();
1396                    }
1397                    writer.putNextReference(array, byte[].class);
1398                    break;
1399                }
1400                case 'S': {
1401                    short[] array = new short[numArrayArgs];
1402                    for (int i = 0; i < numArrayArgs; ++i) {
1403                        array[i] = reader.nextShort();
1404                    }
1405                    writer.putNextReference(array, short[].class);
1406                    break;
1407                }
1408                case 'C': {
1409                    char[] array = new char[numArrayArgs];
1410                    for (int i = 0; i < numArrayArgs; ++i) {
1411                        array[i] = reader.nextChar();
1412                    }
1413                    writer.putNextReference(array, char[].class);
1414                    break;
1415                }
1416                case 'Z': {
1417                    boolean[] array = new boolean[numArrayArgs];
1418                    for (int i = 0; i < numArrayArgs; ++i) {
1419                        array[i] = reader.nextBoolean();
1420                    }
1421                    writer.putNextReference(array, boolean[].class);
1422                    break;
1423                }
1424                case 'F': {
1425                    float[] array = new float[numArrayArgs];
1426                    for (int i = 0; i < numArrayArgs; ++i) {
1427                        array[i] = reader.nextFloat();
1428                    }
1429                    writer.putNextReference(array, float[].class);
1430                    break;
1431                }
1432                case 'D': {
1433                    double[] array = new double[numArrayArgs];
1434                    for (int i = 0; i < numArrayArgs; ++i) {
1435                        array[i] = reader.nextDouble();
1436                    }
1437                    writer.putNextReference(array, double[].class);
1438                    break;
1439                }
1440            }
1441
1442            target.invoke(targetFrame);
1443            targetFrame.copyReturnValueTo(callerFrame);
1444        }
1445    }
1446
1447    /*
1448     * Implements MethodHandles.filterArguments.
1449     */
1450    static class FilterArguments extends Transformer {
1451        /** The target handle. */
1452        private final MethodHandle target;
1453        /** Index of the first argument to filter */
1454        private final int pos;
1455        /** The list of filters to apply */
1456        private final MethodHandle[] filters;
1457
1458        FilterArguments(MethodHandle target, int pos, MethodHandle[] filters) {
1459            super(deriveType(target, pos, filters));
1460
1461            this.target = target;
1462            this.pos = pos;
1463            this.filters = filters;
1464
1465        }
1466
1467        private static MethodType deriveType(MethodHandle target, int pos, MethodHandle[] filters) {
1468            final Class<?>[] filterArgs = new Class<?>[filters.length];
1469            for (int i = 0; i < filters.length; ++i) {
1470                filterArgs[i] = filters[i].type().parameterType(0);
1471            }
1472
1473            return target.type().replaceParameterTypes(pos, pos + filters.length, filterArgs);
1474        }
1475
1476        @Override
1477        public void transform(EmulatedStackFrame stackFrame) throws Throwable {
1478            final StackFrameReader reader = new StackFrameReader();
1479            reader.attach(stackFrame);
1480
1481            EmulatedStackFrame transformedFrame = EmulatedStackFrame.create(target.type());
1482            final StackFrameWriter writer = new StackFrameWriter();
1483            writer.attach(transformedFrame);
1484
1485            final Class<?>[] ptypes = target.type().ptypes();
1486            for (int i = 0; i < ptypes.length; ++i) {
1487                // Check whether the current argument has a filter associated with it.
1488                // If it has no filter, no further action need be taken.
1489                final Class<?> ptype = ptypes[i];
1490                final MethodHandle filter;
1491                if (i < pos) {
1492                    filter = null;
1493                } else if (i >= pos + filters.length) {
1494                    filter = null;
1495                } else {
1496                    filter = filters[i - pos];
1497                }
1498
1499                if (filter != null) {
1500                    // Note that filter.type() must be (ptype)ptype - this is checked before
1501                    // this transformer is created.
1502                    EmulatedStackFrame filterFrame = EmulatedStackFrame.create(filter.type());
1503
1504                    //  Copy the next argument from the stack frame to the filter frame.
1505                    final StackFrameWriter filterWriter = new StackFrameWriter();
1506                    filterWriter.attach(filterFrame);
1507                    copyNext(reader, filterWriter, filter.type().ptypes()[0]);
1508
1509                    filter.invoke(filterFrame);
1510
1511                    // Copy the argument back from the filter frame to the stack frame.
1512                    final StackFrameReader filterReader = new StackFrameReader();
1513                    filterReader.attach(filterFrame);
1514                    filterReader.makeReturnValueAccessor();
1515                    copyNext(filterReader, writer, ptype);
1516                } else {
1517                    // There's no filter associated with this frame, just copy the next argument
1518                    // over.
1519                    copyNext(reader, writer, ptype);
1520                }
1521            }
1522
1523            target.invoke(transformedFrame);
1524            transformedFrame.copyReturnValueTo(stackFrame);
1525        }
1526    }
1527
1528    /**
1529     * Implements MethodHandles.collectArguments.
1530     */
1531    static class CollectArguments extends Transformer {
1532        private final MethodHandle target;
1533        private final MethodHandle collector;
1534        private final int pos;
1535
1536        /** The range of input arguments we copy to the collector. */
1537        private final Range collectorRange;
1538
1539        /**
1540         * The first range of arguments we copy to the target. These are arguments
1541         * in the range [0, pos). Note that arg[pos] is the return value of the filter.
1542         */
1543        private final Range range1;
1544
1545        /**
1546         * The second range of arguments we copy to the target. These are arguments in the range
1547         * (pos, N], where N is the number of target arguments.
1548         */
1549        private final Range range2;
1550
1551        private final int referencesOffset;
1552        private final int stackFrameOffset;
1553
1554        CollectArguments(MethodHandle target, MethodHandle collector, int pos,
1555                         MethodType adapterType) {
1556            super(adapterType);
1557
1558            this.target = target;
1559            this.collector = collector;
1560            this.pos = pos;
1561
1562            final int numFilterArgs = collector.type().parameterCount();
1563            final int numAdapterArgs = type().parameterCount();
1564            collectorRange = Range.of(type(), pos, pos + numFilterArgs);
1565
1566            range1 = Range.of(type(), 0, pos);
1567            if (pos + numFilterArgs < numAdapterArgs) {
1568                this.range2 = Range.of(type(), pos + numFilterArgs, numAdapterArgs);
1569            } else {
1570                this.range2 = null;
1571            }
1572
1573            // Calculate the number of primitive bytes (or references) we copy to the
1574            // target frame based on the return value of the combiner.
1575            final Class<?> collectorRType = collector.type().rtype();
1576            if (collectorRType == void.class) {
1577                stackFrameOffset = 0;
1578                referencesOffset = 0;
1579            } else if (collectorRType.isPrimitive()) {
1580                stackFrameOffset = EmulatedStackFrame.getSize(collectorRType);
1581                referencesOffset = 0;
1582            } else {
1583                stackFrameOffset = 0;
1584                referencesOffset = 1;
1585            }
1586        }
1587
1588        @Override
1589        public void transform(EmulatedStackFrame stackFrame) throws Throwable {
1590            // First invoke the collector.
1591            EmulatedStackFrame filterFrame = EmulatedStackFrame.create(collector.type());
1592            stackFrame.copyRangeTo(filterFrame, collectorRange, 0, 0);
1593            collector.invoke(filterFrame);
1594
1595            // Start constructing the target frame.
1596            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
1597            stackFrame.copyRangeTo(targetFrame, range1, 0, 0);
1598
1599            // If one of these offsets is not zero, we have a return value to copy.
1600            if (referencesOffset != 0 || stackFrameOffset != 0) {
1601                final StackFrameReader reader = new StackFrameReader();
1602                reader.attach(filterFrame).makeReturnValueAccessor();
1603                final StackFrameWriter writer = new StackFrameWriter();
1604                writer.attach(targetFrame, pos, range1.numReferences, range1.numBytes);
1605                copyNext(reader, writer, target.type().ptypes()[0]);
1606            }
1607
1608            if (range2 != null) {
1609                stackFrame.copyRangeTo(targetFrame, range2,
1610                        range1.numReferences + referencesOffset,
1611                        range2.numBytes + stackFrameOffset);
1612            }
1613
1614            target.invoke(targetFrame);
1615            targetFrame.copyReturnValueTo(stackFrame);
1616        }
1617    }
1618
1619    /**
1620     * Implements MethodHandles.foldArguments.
1621     */
1622    static class FoldArguments extends Transformer {
1623        private final MethodHandle target;
1624        private final MethodHandle combiner;
1625
1626        private final Range combinerArgs;
1627        private final Range targetArgs;
1628
1629        private final int referencesOffset;
1630        private final int stackFrameOffset;
1631
1632        FoldArguments(MethodHandle target, MethodHandle combiner) {
1633            super(deriveType(target, combiner));
1634
1635            this.target = target;
1636            this.combiner = combiner;
1637
1638            combinerArgs = Range.all(combiner.type());
1639            targetArgs = Range.all(type());
1640
1641            final Class<?> combinerRType = combiner.type().rtype();
1642            if (combinerRType == void.class) {
1643                stackFrameOffset = 0;
1644                referencesOffset = 0;
1645            } else if (combinerRType.isPrimitive()) {
1646                stackFrameOffset = EmulatedStackFrame.getSize(combinerRType);
1647                referencesOffset = 0;
1648            } else {
1649                stackFrameOffset = 0;
1650                referencesOffset = 1;
1651            }
1652        }
1653
1654        @Override
1655        public void transform(EmulatedStackFrame stackFrame) throws Throwable {
1656            // First construct the combiner frame and invoke it.
1657            EmulatedStackFrame combinerFrame = EmulatedStackFrame.create(combiner.type());
1658            stackFrame.copyRangeTo(combinerFrame, combinerArgs, 0, 0);
1659            combiner.invoke(combinerFrame);
1660
1661            // Create the stack frame for the target.
1662            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
1663
1664            // If one of these offsets is not zero, we have a return value to copy.
1665            if (referencesOffset != 0 || stackFrameOffset != 0) {
1666                final StackFrameReader reader = new StackFrameReader();
1667                reader.attach(combinerFrame).makeReturnValueAccessor();
1668                final StackFrameWriter writer = new StackFrameWriter();
1669                writer.attach(targetFrame);
1670                copyNext(reader, writer, target.type().ptypes()[0]);
1671            }
1672
1673            stackFrame.copyRangeTo(targetFrame, targetArgs, referencesOffset, stackFrameOffset);
1674            target.invoke(targetFrame);
1675
1676            targetFrame.copyReturnValueTo(stackFrame);
1677        }
1678
1679        private static MethodType deriveType(MethodHandle target, MethodHandle combiner) {
1680            if (combiner.type().rtype() == void.class) {
1681                return target.type();
1682            }
1683
1684            return target.type().dropParameterTypes(0, 1);
1685        }
1686    }
1687
1688    /**
1689     * Implements MethodHandles.insertArguments.
1690     */
1691    static class InsertArguments extends Transformer {
1692        private final MethodHandle target;
1693        private final int pos;
1694        private final Object[] values;
1695
1696        private final Range range1;
1697        private final Range range2;
1698
1699        InsertArguments(MethodHandle target, int pos, Object[] values) {
1700            super(target.type().dropParameterTypes(pos, pos + values.length));
1701            this.target = target;
1702            this.pos = pos;
1703            this.values = values;
1704
1705            final MethodType type = type();
1706            range1 = EmulatedStackFrame.Range.of(type, 0, pos);
1707            range2 = Range.of(type, pos, type.parameterCount());
1708        }
1709
1710        @Override
1711        public void transform(EmulatedStackFrame stackFrame) throws Throwable {
1712            EmulatedStackFrame calleeFrame = EmulatedStackFrame.create(target.type());
1713
1714            // Copy all arguments before |pos|.
1715            stackFrame.copyRangeTo(calleeFrame, range1, 0, 0);
1716
1717            // Attach a stack frame writer so that we can copy the next |values.length|
1718            // arguments.
1719            final StackFrameWriter writer = new StackFrameWriter();
1720            writer.attach(calleeFrame, pos, range1.numReferences, range1.numBytes);
1721
1722            // Copy all the arguments supplied in |values|.
1723            int referencesCopied = 0;
1724            int bytesCopied = 0;
1725            final Class<?>[] ptypes = target.type().ptypes();
1726            for (int i = 0; i < values.length; ++i) {
1727                final Class<?> ptype = ptypes[i + pos];
1728                if (ptype.isPrimitive()) {
1729                    if (ptype == boolean.class) {
1730                        writer.putNextBoolean((boolean) values[i]);
1731                    } else if (ptype == byte.class) {
1732                        writer.putNextByte((byte) values[i]);
1733                    } else if (ptype == char.class) {
1734                        writer.putNextChar((char) values[i]);
1735                    } else if (ptype == short.class) {
1736                        writer.putNextShort((short) values[i]);
1737                    } else if (ptype == int.class) {
1738                        writer.putNextInt((int) values[i]);
1739                    } else if (ptype == long.class) {
1740                        writer.putNextLong((long) values[i]);
1741                    } else if (ptype == float.class) {
1742                        writer.putNextFloat((float) values[i]);
1743                    } else if (ptype == double.class) {
1744                        writer.putNextDouble((double) values[i]);
1745                    }
1746
1747                    bytesCopied += EmulatedStackFrame.getSize(ptype);
1748                } else {
1749                    writer.putNextReference(values[i], ptype);
1750                    referencesCopied++;
1751                }
1752            }
1753
1754            // Copy all remaining arguments.
1755            if (range2 != null) {
1756                stackFrame.copyRangeTo(calleeFrame, range2,
1757                        range1.numReferences + referencesCopied,
1758                        range1.numBytes + bytesCopied);
1759            }
1760
1761            target.invoke(calleeFrame);
1762            calleeFrame.copyReturnValueTo(stackFrame);
1763        }
1764    }
1765
1766
1767    /**
1768     * Implements {@link java.lang.invokeMethodHandles#explicitCastArguments()}.
1769     */
1770    public static class ExplicitCastArguments extends Transformer {
1771        private final MethodHandle target;
1772
1773        public ExplicitCastArguments(MethodHandle target, MethodType type) {
1774            super(type);
1775            this.target = target;
1776        }
1777
1778        @Override
1779        public void transform(EmulatedStackFrame callerFrame) throws Throwable {
1780            // Create a new stack frame for the target.
1781            EmulatedStackFrame targetFrame = EmulatedStackFrame.create(target.type());
1782
1783            explicitCastArguments(callerFrame, targetFrame);
1784            target.invoke(targetFrame);
1785            explicitCastReturnValue(callerFrame, targetFrame);
1786        }
1787
1788        private void explicitCastArguments(final EmulatedStackFrame callerFrame,
1789                                           final EmulatedStackFrame targetFrame) {
1790            final StackFrameReader reader = new StackFrameReader();
1791            reader.attach(callerFrame);
1792            final StackFrameWriter writer = new StackFrameWriter();
1793            writer.attach(targetFrame);
1794
1795            final Class<?>[] fromTypes = type().ptypes();
1796            final Class<?>[] toTypes = target.type().ptypes();
1797            for (int i = 0; i < fromTypes.length; ++i) {
1798                explicitCast(reader, fromTypes[i], writer, toTypes[i]);
1799            }
1800        }
1801
1802        private void explicitCastReturnValue(final EmulatedStackFrame callerFrame,
1803                                             final EmulatedStackFrame targetFrame) {
1804            Class<?> from = target.type().rtype();
1805            Class<?> to = type().rtype();
1806            if (to != void.class) {
1807                final StackFrameWriter writer = new StackFrameWriter();
1808                writer.attach(callerFrame);
1809                writer.makeReturnValueAccessor();
1810                if (from == void.class) {
1811                    if (to.isPrimitive()) {
1812                        unboxNull(writer, to);
1813                    } else {
1814                        writer.putNextReference(null, to);
1815                    }
1816                } else {
1817                    final StackFrameReader reader = new StackFrameReader();
1818                    reader.attach(targetFrame);
1819                    reader.makeReturnValueAccessor();
1820                    explicitCast(reader, target.type().rtype(), writer, type().rtype());
1821                }
1822            }
1823        }
1824
1825        private static void throwUnexpectedType(final Class<?> unexpectedType) {
1826            throw new InternalError("Unexpected type: " + unexpectedType);
1827        }
1828
1829        private static void explicitCastFromBoolean(boolean fromValue,
1830                                                    final StackFrameWriter writer,
1831                                                    final Class<?> to) {
1832            int value = fromValue ? 1 : 0;
1833            if (to == byte.class) {
1834                writer.putNextByte((byte) value);
1835            } else if (to == char.class) {
1836                writer.putNextChar((char) value);
1837            } else if (to == short.class) {
1838                writer.putNextShort((short) value);
1839            } else if (to == int.class) {
1840                writer.putNextInt(value);
1841            } else if (to == long.class) {
1842                writer.putNextLong(value);
1843            } else if (to == float.class) {
1844                writer.putNextFloat(value);
1845            } else if (to == double.class) {
1846                writer.putNextDouble(value);
1847            } else {
1848                throwUnexpectedType(to);
1849            }
1850        }
1851
1852        /**
1853         * Converts byte value to boolean according to
1854         * {@link java.lang.invoke.MethodHandles#explicitCast()}
1855         */
1856        private static boolean toBoolean(byte value) {
1857            return (value & 1) == 1;
1858        }
1859
1860        private static byte readPrimitiveAsByte(final StackFrameReader reader,
1861                                                final Class<?> from) {
1862            if (from == byte.class) {
1863                return (byte) reader.nextByte();
1864            } else if (from == char.class) {
1865                return (byte) reader.nextChar();
1866            } else if (from == short.class) {
1867                return (byte) reader.nextShort();
1868            } else if (from == int.class) {
1869                return (byte) reader.nextInt();
1870            } else if (from == long.class) {
1871                return (byte) reader.nextLong();
1872            } else if (from == float.class) {
1873                return (byte) reader.nextFloat();
1874            } else if (from == double.class) {
1875                return (byte) reader.nextDouble();
1876            } else {
1877                throwUnexpectedType(from);
1878                return 0;
1879            }
1880        }
1881
1882        private static char readPrimitiveAsChar(final StackFrameReader reader,
1883                                                final Class<?> from) {
1884            if (from == byte.class) {
1885                return (char) reader.nextByte();
1886            } else if (from == char.class) {
1887                return (char) reader.nextChar();
1888            } else if (from == short.class) {
1889                return (char) reader.nextShort();
1890            } else if (from == int.class) {
1891                return (char) reader.nextInt();
1892            } else if (from == long.class) {
1893                return (char) reader.nextLong();
1894            } else if (from == float.class) {
1895                return (char) reader.nextFloat();
1896            } else if (from == double.class) {
1897                return (char) reader.nextDouble();
1898            } else {
1899                throwUnexpectedType(from);
1900                return 0;
1901            }
1902        }
1903
1904        private static short readPrimitiveAsShort(final StackFrameReader reader,
1905                                                  final Class<?> from) {
1906            if (from == byte.class) {
1907                return (short) reader.nextByte();
1908            } else if (from == char.class) {
1909                return (short) reader.nextChar();
1910            } else if (from == short.class) {
1911                return (short) reader.nextShort();
1912            } else if (from == int.class) {
1913                return (short) reader.nextInt();
1914            } else if (from == long.class) {
1915                return (short) reader.nextLong();
1916            } else if (from == float.class) {
1917                return (short) reader.nextFloat();
1918            } else if (from == double.class) {
1919                return (short) reader.nextDouble();
1920            } else {
1921                throwUnexpectedType(from);
1922                return 0;
1923            }
1924        }
1925
1926        private static int readPrimitiveAsInt(final StackFrameReader reader,
1927                                              final Class<?> from) {
1928            if (from == byte.class) {
1929                return (int) reader.nextByte();
1930            } else if (from == char.class) {
1931                return (int) reader.nextChar();
1932            } else if (from == short.class) {
1933                return (int) reader.nextShort();
1934            } else if (from == int.class) {
1935                return (int) reader.nextInt();
1936            } else if (from == long.class) {
1937                return (int) reader.nextLong();
1938            } else if (from == float.class) {
1939                return (int) reader.nextFloat();
1940            } else if (from == double.class) {
1941                return (int) reader.nextDouble();
1942            } else {
1943                throwUnexpectedType(from);
1944                return 0;
1945            }
1946        }
1947
1948        private static long readPrimitiveAsLong(final StackFrameReader reader,
1949                                                final Class<?> from) {
1950            if (from == byte.class) {
1951                return (long) reader.nextByte();
1952            } else if (from == char.class) {
1953                return (long) reader.nextChar();
1954            } else if (from == short.class) {
1955                return (long) reader.nextShort();
1956            } else if (from == int.class) {
1957                return (long) reader.nextInt();
1958            } else if (from == long.class) {
1959                return (long) reader.nextLong();
1960            } else if (from == float.class) {
1961                return (long) reader.nextFloat();
1962            } else if (from == double.class) {
1963                return (long) reader.nextDouble();
1964            } else {
1965                throwUnexpectedType(from);
1966                return 0;
1967            }
1968        }
1969
1970        private static float readPrimitiveAsFloat(final StackFrameReader reader,
1971                                                  final Class<?> from) {
1972            if (from == byte.class) {
1973                return (float) reader.nextByte();
1974            } else if (from == char.class) {
1975                return (float) reader.nextChar();
1976            } else if (from == short.class) {
1977                return (float) reader.nextShort();
1978            } else if (from == int.class) {
1979                return (float) reader.nextInt();
1980            } else if (from == long.class) {
1981                return (float) reader.nextLong();
1982            } else if (from == float.class) {
1983                return (float) reader.nextFloat();
1984            } else if (from == double.class) {
1985                return (float) reader.nextDouble();
1986            } else {
1987                throwUnexpectedType(from);
1988                return 0;
1989            }
1990        }
1991
1992        private static double readPrimitiveAsDouble(final StackFrameReader reader,
1993                                                    final Class<?> from) {
1994            if (from == byte.class) {
1995                return (double) reader.nextByte();
1996            } else if (from == char.class) {
1997                return (double) reader.nextChar();
1998            } else if (from == short.class) {
1999                return (double) reader.nextShort();
2000            } else if (from == int.class) {
2001                return (double) reader.nextInt();
2002            } else if (from == long.class) {
2003                return (double) reader.nextLong();
2004            } else if (from == float.class) {
2005                return (double) reader.nextFloat();
2006            } else if (from == double.class) {
2007                return (double) reader.nextDouble();
2008            } else {
2009                throwUnexpectedType(from);
2010                return 0;
2011            }
2012        }
2013
2014        private static void explicitCastToBoolean(final StackFrameReader reader,
2015                                                  final Class<?> from,
2016                                                  final StackFrameWriter writer) {
2017            byte byteValue = readPrimitiveAsByte(reader, from);
2018            writer.putNextBoolean(toBoolean(byteValue));
2019        }
2020
2021        private static void explicitCastPrimitives(final StackFrameReader reader,
2022                                                   final Class<?> from,
2023                                                   final StackFrameWriter writer,
2024                                                   final Class<?> to) {
2025            if (to == byte.class) {
2026                byte value = readPrimitiveAsByte(reader, from);
2027                writer.putNextByte(value);
2028            } else if (to == char.class) {
2029                char value = readPrimitiveAsChar(reader, from);
2030                writer.putNextChar(value);
2031            } else if (to == short.class) {
2032                short value = readPrimitiveAsShort(reader, from);
2033                writer.putNextShort(value);
2034            } else if (to == int.class) {
2035                int value = readPrimitiveAsInt(reader, from);
2036                writer.putNextInt(value);
2037            } else if (to == long.class) {
2038                long value = readPrimitiveAsLong(reader, from);
2039                writer.putNextLong(value);
2040            } else if (to == float.class) {
2041                float value = readPrimitiveAsFloat(reader, from);
2042                writer.putNextFloat(value);
2043            } else if (to == double.class) {
2044                double value = readPrimitiveAsDouble(reader, from);
2045                writer.putNextDouble(value);
2046            } else {
2047                throwUnexpectedType(to);
2048            }
2049        }
2050
2051        private static void unboxNull(final StackFrameWriter writer, final Class<?> to) {
2052            if (to == boolean.class) {
2053                writer.putNextBoolean(false);
2054            } else if (to == byte.class) {
2055                writer.putNextByte((byte) 0);
2056            } else if (to == char.class) {
2057                writer.putNextChar((char) 0);
2058            } else if (to == short.class) {
2059                writer.putNextShort((short) 0);
2060            } else if (to == int.class) {
2061                writer.putNextInt((int) 0);
2062            } else if (to == long.class) {
2063                writer.putNextLong((long) 0);
2064            } else if (to == float.class) {
2065                writer.putNextFloat((float) 0);
2066            } else if (to == double.class) {
2067                writer.putNextDouble((double) 0);
2068            } else {
2069                throwUnexpectedType(to);
2070            }
2071        }
2072
2073        private static void unboxNonNull(final Object ref, final Class<?> from,
2074                                         final StackFrameWriter writer, final Class<?> to) {
2075            if (to == boolean.class) {
2076                if (from == Boolean.class) {
2077                    writer.putNextBoolean((boolean) ref);
2078                } else if (from == Float.class || from == Double.class) {
2079                    byte b = (byte) ((double) ref);
2080                    writer.putNextBoolean(toBoolean(b));
2081                } else {
2082                    byte b = (byte) ((long) ref);
2083                    writer.putNextBoolean(toBoolean(b));
2084                }
2085            } else if (to == byte.class) {
2086                writer.putNextByte((byte) ref);
2087            } else if (to == char.class) {
2088                writer.putNextChar((char) ref);
2089            } else if (to == short.class) {
2090                writer.putNextShort((short) ref);
2091            } else if (to == int.class) {
2092                writer.putNextInt((int) ref);
2093            } else if (to == long.class) {
2094                writer.putNextLong((long) ref);
2095            } else if (to == float.class) {
2096                writer.putNextFloat((float) ref);
2097            } else if (to == double.class) {
2098                writer.putNextDouble((double) ref);
2099            } else {
2100                throwUnexpectedType(to);
2101            }
2102        }
2103
2104        private static void unbox(final Object ref, final Class<?> from,
2105                                  final StackFrameWriter writer, final Class<?> to) {
2106            if (ref == null) {
2107                unboxNull(writer, to);
2108            } else {
2109                unboxNonNull(ref, from, writer, to);
2110            }
2111        }
2112
2113        private static void box(final StackFrameReader reader, final Class<?> from,
2114                                final StackFrameWriter writer, final Class<?> to) {
2115            Object boxed = null;
2116            if (from == boolean.class) {
2117                boxed = Boolean.valueOf(reader.nextBoolean());
2118            } else if (from == byte.class) {
2119                boxed = Byte.valueOf(reader.nextByte());
2120            } else if (from == char.class) {
2121                boxed = Character.valueOf(reader.nextChar());
2122            } else if (from == short.class) {
2123                boxed = Short.valueOf(reader.nextShort());
2124            } else if (from == int.class) {
2125                boxed = Integer.valueOf(reader.nextInt());
2126            } else if (from == long.class) {
2127                boxed = Long.valueOf(reader.nextLong());
2128            } else if (from == float.class) {
2129                boxed = Float.valueOf(reader.nextFloat());
2130            } else if (from == double.class) {
2131                boxed = Double.valueOf(reader.nextDouble());
2132            } else {
2133                throwUnexpectedType(from);
2134            }
2135            writer.putNextReference(to.cast(boxed), to);
2136        }
2137
2138        private static void explicitCast(final StackFrameReader reader, final Class<?> from,
2139                                         final StackFrameWriter writer, final Class<?> to) {
2140            if (from.equals(to)) {
2141                StackFrameAccessor.copyNext(reader, writer, from);
2142            } else if (!from.isPrimitive()) {
2143                Object ref = reader.nextReference(from);
2144                if (to.isInterface()) {
2145                    // Pass from without a cast according to description for
2146                    // {@link java.lang.invoke.MethodHandles#explicitCastArguments()}.
2147                    writer.putNextReference(ref, to);
2148                } else if (!to.isPrimitive()) {
2149                    // |to| is a reference type, perform class cast check.
2150                    writer.putNextReference(to.cast(ref), to);
2151                } else {
2152                    // |from| is a reference type, |to| is a primitive type,
2153                    unbox(ref, from, writer, to);
2154                }
2155            } else if (to.isPrimitive()) {
2156                // |from| and |to| are primitive types.
2157                if (from == boolean.class) {
2158                    explicitCastFromBoolean(reader.nextBoolean(), writer, to);
2159                } else if (to == boolean.class) {
2160                    explicitCastToBoolean(reader, from, writer);
2161                } else {
2162                    explicitCastPrimitives(reader, from, writer, to);
2163                }
2164            } else {
2165                // |from| is a primitive type, |to| is a reference type.
2166                box(reader, from, writer, to);
2167            }
2168        }
2169    }
2170}
2171