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