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