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