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