1 /*
   2  * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE 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  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.lang.invoke;
  27 
  28 import jdk.internal.org.objectweb.asm.*;
  29 import sun.invoke.util.BytecodeDescriptor;
  30 import sun.security.action.GetPropertyAction;
  31 import sun.security.action.GetBooleanAction;
  32 
  33 import java.io.FilePermission;
  34 import java.io.Serializable;
  35 import java.lang.invoke.MethodHandles.Lookup;
  36 import java.lang.reflect.Constructor;
  37 import java.security.AccessController;
  38 import java.security.PrivilegedAction;
  39 import java.util.LinkedHashSet;
  40 import java.util.concurrent.atomic.AtomicInteger;
  41 import java.util.PropertyPermission;
  42 import java.util.Set;
  43 
  44 import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE;
  45 import static jdk.internal.org.objectweb.asm.Opcodes.*;
  46 
  47 /**
  48  * Lambda metafactory implementation which dynamically creates an
  49  * inner-class-like class per lambda callsite.
  50  *
  51  * @see LambdaMetafactory
  52  */
  53 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
  54     private static final int CLASSFILE_VERSION = 52;
  55     private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
  56     private static final String JAVA_LANG_OBJECT = "java/lang/Object";
  57     private static final String NAME_CTOR = "<init>";
  58 
  59     //Serialization support
  60     private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
  61     private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
  62     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
  63     private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
  64     private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
  65     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
  66     private static final String NAME_METHOD_READ_OBJECT = "readObject";
  67     private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
  68 
  69     private static final String DESCR_CLASS = "Ljava/lang/Class;";
  70     private static final String DESCR_STRING = "Ljava/lang/String;";
  71     private static final String DESCR_OBJECT = "Ljava/lang/Object;";
  72     private static final String DESCR_CTOR_SERIALIZED_LAMBDA
  73             = "(" + DESCR_CLASS + DESCR_STRING + DESCR_STRING + DESCR_STRING + "I"
  74             + DESCR_STRING + DESCR_STRING + DESCR_STRING + DESCR_STRING + "[" + DESCR_OBJECT + ")V";
  75 
  76     private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION = "(Ljava/lang/String;)V";
  77     private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
  78 
  79     private static final String[] EMPTY_STRING_ARRAY = new String[0];
  80 
  81     // Used to ensure that each spun class name is unique
  82     private static final AtomicInteger counter = new AtomicInteger(0);
  83 
  84     // For dumping generated classes to disk, for debugging purposes
  85     private static final ProxyClassesDumper dumper;
  86 
  87     private static final boolean disableEagerInitialization;
  88 
  89     static {
  90         final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
  91         String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
  92         dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
  93 
  94         final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
  95         disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
  96     }
  97 
  98     // See context values in AbstractValidatingLambdaMetafactory
  99     private final String implMethodClassName;        // Name of type containing implementation "CC"
 100     private final String implMethodName;             // Name of implementation method "impl"
 101     private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
 102     private final MethodType constructorType;        // Generated class constructor type "(CC)void"
 103     private final ClassWriter cw;                    // ASM class writer
 104     private final String[] argNames;                 // Generated names for the constructor arguments
 105     private final String[] argDescs;                 // Type descriptors for the constructor arguments
 106     private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
 107 
 108     /**
 109      * General meta-factory constructor, supporting both standard cases and
 110      * allowing for uncommon options such as serialization or bridging.
 111      *
 112      * @param caller Stacked automatically by VM; represents a lookup context
 113      *               with the accessibility privileges of the caller.
 114      * @param invokedType Stacked automatically by VM; the signature of the
 115      *                    invoked method, which includes the expected static
 116      *                    type of the returned lambda object, and the static
 117      *                    types of the captured arguments for the lambda.  In
 118      *                    the event that the implementation method is an
 119      *                    instance method, the first argument in the invocation
 120      *                    signature will correspond to the receiver.
 121      * @param samMethodName Name of the method in the functional interface to
 122      *                      which the lambda or method reference is being
 123      *                      converted, represented as a String.
 124      * @param samMethodType Type of the method in the functional interface to
 125      *                      which the lambda or method reference is being
 126      *                      converted, represented as a MethodType.
 127      * @param implMethod The implementation method which should be called (with
 128      *                   suitable adaptation of argument types, return types,
 129      *                   and adjustment for captured arguments) when methods of
 130      *                   the resulting functional interface instance are invoked.
 131      * @param instantiatedMethodType The signature of the primary functional
 132      *                               interface method after type variables are
 133      *                               substituted with their instantiation from
 134      *                               the capture site
 135      * @param isSerializable Should the lambda be made serializable?  If set,
 136      *                       either the target type or one of the additional SAM
 137      *                       types must extend {@code Serializable}.
 138      * @param markerInterfaces Additional interfaces which the lambda object
 139      *                       should implement.
 140      * @param additionalBridges Method types for additional signatures to be
 141      *                          bridged to the implementation method
 142      * @throws LambdaConversionException If any of the meta-factory protocol
 143      * invariants are violated
 144      */
 145     public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
 146                                        MethodType invokedType,
 147                                        String samMethodName,
 148                                        MethodType samMethodType,
 149                                        MethodHandle implMethod,
 150                                        MethodType instantiatedMethodType,
 151                                        boolean isSerializable,
 152                                        Class<?>[] markerInterfaces,
 153                                        MethodType[] additionalBridges)
 154             throws LambdaConversionException {
 155         super(caller, invokedType, samMethodName, samMethodType,
 156               implMethod, instantiatedMethodType,
 157               isSerializable, markerInterfaces, additionalBridges);
 158         implMethodClassName = implClass.getName().replace('.', '/');
 159         implMethodName = implInfo.getName();
 160         implMethodDesc = implInfo.getMethodType().toMethodDescriptorString();
 161         constructorType = invokedType.changeReturnType(Void.TYPE);
 162         lambdaClassName = lambdaClassName(targetClass);
 163         cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
 164         int parameterCount = invokedType.parameterCount();
 165         if (parameterCount > 0) {
 166             argNames = new String[parameterCount];
 167             argDescs = new String[parameterCount];
 168             for (int i = 0; i < parameterCount; i++) {
 169                 argNames[i] = "arg$" + (i + 1);
 170                 argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
 171             }
 172         } else {
 173             argNames = argDescs = EMPTY_STRING_ARRAY;
 174         }
 175     }
 176 
 177     private static String lambdaClassName(Class<?> targetClass) {
 178         String name = targetClass.getName();
 179         if (targetClass.isHiddenClass()) {
 180             // use the original class name
 181             name = name.replace('/', '_');
 182         }
 183         return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
 184     }
 185 
 186     /**
 187      * Build the CallSite. Generate a class file which implements the functional
 188      * interface, define the class, if there are no parameters create an instance
 189      * of the class which the CallSite will return, otherwise, generate handles
 190      * which will call the class' constructor.
 191      *
 192      * @return a CallSite, which, when invoked, will return an instance of the
 193      * functional interface
 194      * @throws ReflectiveOperationException
 195      * @throws LambdaConversionException If properly formed functional interface
 196      * is not found
 197      */
 198     @Override
 199     CallSite buildCallSite() throws LambdaConversionException {
 200         final Class<?> innerClass = spinInnerClass();
 201         assert innerClass.isHiddenClass() : innerClass.toString();
 202         if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
 203             // In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
 204             // unless we've suppressed eager initialization
 205             final Constructor<?>[] ctrs = AccessController.doPrivileged(
 206                     new PrivilegedAction<>() {
 207                 @Override
 208                 public Constructor<?>[] run() {
 209                     Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
 210                     if (ctrs.length == 1) {
 211                         // The lambda implementing inner class constructor is private, set
 212                         // it accessible (by us) before creating the constant sole instance
 213                         ctrs[0].setAccessible(true);
 214                     }
 215                     return ctrs;
 216                 }
 217                     });
 218             if (ctrs.length != 1) {
 219                 throw new LambdaConversionException("Expected one lambda constructor for "
 220                         + innerClass.getCanonicalName() + ", got " + ctrs.length);
 221             }
 222 
 223             try {
 224                 Object inst = ctrs[0].newInstance();
 225                 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
 226             } catch (ReflectiveOperationException e) {
 227                 throw new LambdaConversionException("Exception instantiating lambda object", e);
 228             }
 229         } else {
 230             try {
 231                 MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
 232                 return new ConstantCallSite(mh.asType(invokedType));
 233             } catch (ReflectiveOperationException e) {
 234                 throw new LambdaConversionException("Exception finding constructor", e);
 235             }
 236         }
 237     }
 238 
 239     /**
 240      * Generate a class file which implements the functional
 241      * interface, define and return the class.
 242      *
 243      * @implNote The class that is generated does not include signature
 244      * information for exceptions that may be present on the SAM method.
 245      * This is to reduce classfile size, and is harmless as checked exceptions
 246      * are erased anyway, no one will ever compile against this classfile,
 247      * and we make no guarantees about the reflective properties of lambda
 248      * objects.
 249      *
 250      * @return a Class which implements the functional interface
 251      * @throws LambdaConversionException If properly formed functional interface
 252      * is not found
 253      */
 254     private Class<?> spinInnerClass() throws LambdaConversionException {
 255         String[] interfaces;
 256         String samIntf = samBase.getName().replace('.', '/');
 257         boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
 258         if (markerInterfaces.length == 0) {
 259             interfaces = new String[]{samIntf};
 260         } else {
 261             // Assure no duplicate interfaces (ClassFormatError)
 262             Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
 263             itfs.add(samIntf);
 264             for (Class<?> markerInterface : markerInterfaces) {
 265                 itfs.add(markerInterface.getName().replace('.', '/'));
 266                 accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
 267             }
 268             interfaces = itfs.toArray(new String[itfs.size()]);
 269         }
 270 
 271         cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
 272                  lambdaClassName, null,
 273                  JAVA_LANG_OBJECT, interfaces);
 274 
 275         // Generate final fields to be filled in by constructor
 276         for (int i = 0; i < argDescs.length; i++) {
 277             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
 278                                             argNames[i],
 279                                             argDescs[i],
 280                                             null, null);
 281             fv.visitEnd();
 282         }
 283 
 284         generateConstructor();
 285 
 286         // Forward the SAM method
 287         MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
 288                                           samMethodType.toMethodDescriptorString(), null, null);
 289         new ForwardingMethodGenerator(mv).generate(samMethodType);
 290 
 291         // Forward the bridges
 292         if (additionalBridges != null) {
 293             for (MethodType mt : additionalBridges) {
 294                 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
 295                                     mt.toMethodDescriptorString(), null, null);
 296                 new ForwardingMethodGenerator(mv).generate(mt);
 297             }
 298         }
 299 
 300         if (isSerializable)
 301             generateSerializationFriendlyMethods();
 302         else if (accidentallySerializable)
 303             generateSerializationHostileMethods();
 304 
 305         cw.visitEnd();
 306 
 307         // Define the generated class in this VM.
 308 
 309         final byte[] classBytes = cw.toByteArray();
 310         // If requested, dump out to a file for debugging purposes
 311         if (dumper != null) {
 312             AccessController.doPrivileged(new PrivilegedAction<>() {
 313                 @Override
 314                 public Void run() {
 315                     dumper.dumpClass(lambdaClassName, classBytes);
 316                     return null;
 317                 }
 318             }, null,
 319             new FilePermission("<<ALL FILES>>", "read, write"),
 320             // createDirectories may need it
 321             new PropertyPermission("user.dir", "read"));
 322         }
 323         try {
 324             // this class is linked at the indy callsite; so define a hidden nestmate
 325             return caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE).lookupClass();
 326         } catch (IllegalAccessException e) {
 327             throw new LambdaConversionException("Exception defining lambda proxy class", e);
 328         }
 329     }
 330 
 331     /**
 332      * Generate the constructor for the class
 333      */
 334     private void generateConstructor() {
 335         // Generate constructor
 336         MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
 337                                             constructorType.toMethodDescriptorString(), null, null);
 338         ctor.visitCode();
 339         ctor.visitVarInsn(ALOAD, 0);
 340         ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
 341                              METHOD_DESCRIPTOR_VOID, false);
 342         int parameterCount = invokedType.parameterCount();
 343         for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
 344             ctor.visitVarInsn(ALOAD, 0);
 345             Class<?> argType = invokedType.parameterType(i);
 346             ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 347             lvIndex += getParameterSize(argType);
 348             ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
 349         }
 350         ctor.visitInsn(RETURN);
 351         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 352         ctor.visitMaxs(-1, -1);
 353         ctor.visitEnd();
 354     }
 355 
 356     /**
 357      * Generate a writeReplace method that supports serialization
 358      */
 359     private void generateSerializationFriendlyMethods() {
 360         TypeConvertingMethodAdapter mv
 361                 = new TypeConvertingMethodAdapter(
 362                     cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 363                     NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
 364                     null, null));
 365 
 366         mv.visitCode();
 367         mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
 368         mv.visitInsn(DUP);
 369         mv.visitLdcInsn(Type.getType(targetClass));
 370         mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
 371         mv.visitLdcInsn(samMethodName);
 372         mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
 373         mv.visitLdcInsn(implInfo.getReferenceKind());
 374         mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
 375         mv.visitLdcInsn(implInfo.getName());
 376         mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
 377         mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
 378         mv.iconst(argDescs.length);
 379         mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
 380         for (int i = 0; i < argDescs.length; i++) {
 381             mv.visitInsn(DUP);
 382             mv.iconst(i);
 383             mv.visitVarInsn(ALOAD, 0);
 384             mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 385             mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
 386             mv.visitInsn(AASTORE);
 387         }
 388         mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
 389                 DESCR_CTOR_SERIALIZED_LAMBDA, false);
 390         mv.visitInsn(ARETURN);
 391         // Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
 392         mv.visitMaxs(-1, -1);
 393         mv.visitEnd();
 394     }
 395 
 396     /**
 397      * Generate a readObject/writeObject method that is hostile to serialization
 398      */
 399     private void generateSerializationHostileMethods() {
 400         MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 401                                           NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
 402                                           null, SER_HOSTILE_EXCEPTIONS);
 403         mv.visitCode();
 404         mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
 405         mv.visitInsn(DUP);
 406         mv.visitLdcInsn("Non-serializable lambda");
 407         mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
 408                            DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
 409         mv.visitInsn(ATHROW);
 410         mv.visitMaxs(-1, -1);
 411         mv.visitEnd();
 412 
 413         mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
 414                             NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
 415                             null, SER_HOSTILE_EXCEPTIONS);
 416         mv.visitCode();
 417         mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
 418         mv.visitInsn(DUP);
 419         mv.visitLdcInsn("Non-serializable lambda");
 420         mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
 421                            DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION, false);
 422         mv.visitInsn(ATHROW);
 423         mv.visitMaxs(-1, -1);
 424         mv.visitEnd();
 425     }
 426 
 427     /**
 428      * This class generates a method body which calls the lambda implementation
 429      * method, converting arguments, as needed.
 430      */
 431     private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
 432 
 433         ForwardingMethodGenerator(MethodVisitor mv) {
 434             super(mv);
 435         }
 436 
 437         void generate(MethodType methodType) {
 438             visitCode();
 439 
 440             if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
 441                 visitTypeInsn(NEW, implMethodClassName);
 442                 visitInsn(DUP);
 443             }
 444             for (int i = 0; i < argNames.length; i++) {
 445                 visitVarInsn(ALOAD, 0);
 446                 visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
 447             }
 448 
 449             convertArgumentTypes(methodType);
 450 
 451             // Invoke the method we want to forward to
 452             visitMethodInsn(invocationOpcode(), implMethodClassName,
 453                             implMethodName, implMethodDesc,
 454                             implClass.isInterface());
 455 
 456             // Convert the return value (if any) and return it
 457             // Note: if adapting from non-void to void, the 'return'
 458             // instruction will pop the unneeded result
 459             Class<?> implReturnClass = implMethodType.returnType();
 460             Class<?> samReturnClass = methodType.returnType();
 461             convertType(implReturnClass, samReturnClass, samReturnClass);
 462             visitInsn(getReturnOpcode(samReturnClass));
 463             // Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
 464             visitMaxs(-1, -1);
 465             visitEnd();
 466         }
 467 
 468         private void convertArgumentTypes(MethodType samType) {
 469             int lvIndex = 0;
 470             int samParametersLength = samType.parameterCount();
 471             int captureArity = invokedType.parameterCount();
 472             for (int i = 0; i < samParametersLength; i++) {
 473                 Class<?> argType = samType.parameterType(i);
 474                 visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
 475                 lvIndex += getParameterSize(argType);
 476                 convertType(argType, implMethodType.parameterType(captureArity + i), instantiatedMethodType.parameterType(i));
 477             }
 478         }
 479 
 480         private int invocationOpcode() throws InternalError {
 481             switch (implKind) {
 482                 case MethodHandleInfo.REF_invokeStatic:
 483                     return INVOKESTATIC;
 484                 case MethodHandleInfo.REF_newInvokeSpecial:
 485                     return INVOKESPECIAL;
 486                  case MethodHandleInfo.REF_invokeVirtual:
 487                     return INVOKEVIRTUAL;
 488                 case MethodHandleInfo.REF_invokeInterface:
 489                     return INVOKEINTERFACE;
 490                 case MethodHandleInfo.REF_invokeSpecial:
 491                     return INVOKESPECIAL;
 492                 default:
 493                     throw new InternalError("Unexpected invocation kind: " + implKind);
 494             }
 495         }
 496     }
 497 
 498     static int getParameterSize(Class<?> c) {
 499         if (c == Void.TYPE) {
 500             return 0;
 501         } else if (c == Long.TYPE || c == Double.TYPE) {
 502             return 2;
 503         }
 504         return 1;
 505     }
 506 
 507     static int getLoadOpcode(Class<?> c) {
 508         if(c == Void.TYPE) {
 509             throw new InternalError("Unexpected void type of load opcode");
 510         }
 511         return ILOAD + getOpcodeOffset(c);
 512     }
 513 
 514     static int getReturnOpcode(Class<?> c) {
 515         if(c == Void.TYPE) {
 516             return RETURN;
 517         }
 518         return IRETURN + getOpcodeOffset(c);
 519     }
 520 
 521     private static int getOpcodeOffset(Class<?> c) {
 522         if (c.isPrimitive()) {
 523             if (c == Long.TYPE) {
 524                 return 1;
 525             } else if (c == Float.TYPE) {
 526                 return 2;
 527             } else if (c == Double.TYPE) {
 528                 return 3;
 529             }
 530             return 0;
 531         } else {
 532             return 4;
 533         }
 534     }
 535 
 536 }