ClassEmitter.java revision 1013:578f8ca1336a
1/* 2 * Copyright (c) 2010, 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 26package jdk.nashorn.internal.codegen; 27 28import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 31import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 32import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 33import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 34import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE; 35import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL; 36import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; 37import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; 38import static jdk.internal.org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL; 39import static jdk.internal.org.objectweb.asm.Opcodes.V1_7; 40import static jdk.nashorn.internal.codegen.CompilerConstants.CLINIT; 41import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 42import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_PREFIX; 43import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_SUFFIX; 44import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP; 45import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING; 46import static jdk.nashorn.internal.codegen.CompilerConstants.INIT; 47import static jdk.nashorn.internal.codegen.CompilerConstants.SET_MAP; 48import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 49import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 50import static jdk.nashorn.internal.codegen.CompilerConstants.className; 51import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor; 52import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor; 53import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup; 54 55import java.io.ByteArrayOutputStream; 56import java.io.PrintWriter; 57import java.security.AccessController; 58import java.security.PrivilegedAction; 59import java.util.EnumSet; 60import java.util.HashSet; 61import java.util.Set; 62 63import jdk.internal.org.objectweb.asm.ClassWriter; 64import jdk.internal.org.objectweb.asm.MethodVisitor; 65import jdk.internal.org.objectweb.asm.util.TraceClassVisitor; 66import jdk.nashorn.internal.codegen.types.Type; 67import jdk.nashorn.internal.ir.FunctionNode; 68import jdk.nashorn.internal.ir.SplitNode; 69import jdk.nashorn.internal.ir.debug.NashornClassReader; 70import jdk.nashorn.internal.ir.debug.NashornTextifier; 71import jdk.nashorn.internal.runtime.Context; 72import jdk.nashorn.internal.runtime.PropertyMap; 73import jdk.nashorn.internal.runtime.RewriteException; 74import jdk.nashorn.internal.runtime.ScriptObject; 75import jdk.nashorn.internal.runtime.Source; 76 77/** 78 * The interface responsible for speaking to ASM, emitting classes, 79 * fields and methods. 80 * <p> 81 * This file contains the ClassEmitter, which is the master object 82 * responsible for writing byte codes. It utilizes a MethodEmitter 83 * for method generation, which also the NodeVisitors own, to keep 84 * track of the current code generator and what it is doing. 85 * <p> 86 * There is, however, nothing stopping you from using this in a 87 * completely self contained environment, for example in ObjectGenerator 88 * where there are no visitors or external hooks. 89 * <p> 90 * MethodEmitter makes it simple to generate code for methods without 91 * having to do arduous type checking. It maintains a type stack 92 * and will pick the appropriate operation for all operations sent to it 93 * We also allow chained called to a MethodEmitter for brevity, e.g. 94 * it is legal to write _new(className).dup() or 95 * load(slot).load(slot2).xor().store(slot3); 96 * <p> 97 * If running with assertions enabled, any type conflict, such as different 98 * bytecode stack sizes or operating on the wrong type will be detected 99 * and an error thrown. 100 * <p> 101 * There is also a very nice debug interface that can emit formatted 102 * bytecodes that have been written. This is enabled by setting the 103 * environment "nashorn.codegen.debug" to true, or --log=codegen:{@literal <level>} 104 * <p> 105 * A ClassEmitter implements an Emitter - i.e. it needs to have 106 * well defined start and end calls for whatever it is generating. Assertions 107 * detect if this is not true 108 * 109 * @see Compiler 110 */ 111public class ClassEmitter implements Emitter { 112 /** Default flags for class generation - public class */ 113 private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC); 114 115 /** Sanity check flag - have we started on a class? */ 116 private boolean classStarted; 117 118 /** Sanity check flag - have we ended this emission? */ 119 private boolean classEnded; 120 121 /** 122 * Sanity checks - which methods have we currently 123 * started for generation in this class? 124 */ 125 private final HashSet<MethodEmitter> methodsStarted; 126 127 /** The ASM classwriter that we use for all bytecode operations */ 128 protected final ClassWriter cw; 129 130 /** The script environment */ 131 protected final Context context; 132 133 /** Compile unit class name. */ 134 private String unitClassName; 135 136 /** Set of constants access methods required. */ 137 private Set<Class<?>> constantMethodNeeded; 138 139 private int methodCount; 140 141 private int initCount; 142 143 private int clinitCount; 144 145 private int fieldCount; 146 147 private final Set<String> methodNames; 148 149 /** 150 * Constructor - only used internally in this class as it breaks 151 * abstraction towards ASM or other code generator below 152 * 153 * @param env script environment 154 * @param cw ASM classwriter 155 */ 156 private ClassEmitter(final Context context, final ClassWriter cw) { 157 this.context = context; 158 this.cw = cw; 159 this.methodsStarted = new HashSet<>(); 160 this.methodNames = new HashSet<>(); 161 } 162 163 public Set<String> getMethodNames() { 164 return methodNames; 165 } 166 167 /** 168 * Constructor 169 * 170 * @param env script environment 171 * @param className name of class to weave 172 * @param superClassName super class name for class 173 * @param interfaceNames names of interfaces implemented by this class, or null if none 174 */ 175 ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) { 176 this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS)); 177 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames); 178 } 179 180 /** 181 * Constructor from the compiler 182 * 183 * @param env Script environment 184 * @param sourceName Source name 185 * @param unitClassName Compile unit class name. 186 * @param strictMode Should we generate this method in strict mode 187 */ 188 ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) { 189 this(context, 190 new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 191 private static final String OBJECT_CLASS = "java/lang/Object"; 192 193 @Override 194 protected String getCommonSuperClass(final String type1, final String type2) { 195 try { 196 return super.getCommonSuperClass(type1, type2); 197 } catch (final RuntimeException e) { 198 if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) { 199 return className(ScriptObject.class); 200 } 201 return OBJECT_CLASS; 202 } 203 } 204 }); 205 206 this.unitClassName = unitClassName; 207 this.constantMethodNeeded = new HashSet<>(); 208 209 cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS.class.getName()), null); 210 cw.visitSource(sourceName, null); 211 212 defineCommonStatics(strictMode); 213 } 214 215 Context getContext() { 216 return context; 217 } 218 219 /** 220 * Returns the name of the compile unit class name. 221 * @return the name of the compile unit class name. 222 */ 223 String getUnitClassName() { 224 return unitClassName; 225 } 226 227 /** 228 * Get the method count, including init and clinit methods 229 * @return method count 230 */ 231 public int getMethodCount() { 232 return methodCount; 233 } 234 235 /** 236 * Get the clinit count 237 * @return clinit count 238 */ 239 public int getClinitCount() { 240 return clinitCount; 241 } 242 243 /** 244 * Get the init count 245 * @return init count 246 */ 247 public int getInitCount() { 248 return initCount; 249 } 250 251 /** 252 * Get the field count 253 * @return field count 254 */ 255 public int getFieldCount() { 256 return fieldCount; 257 } 258 259 /** 260 * Convert a binary name to a package/class name. 261 * 262 * @param name Binary name. 263 * @return Package/class name. 264 */ 265 private static String pathName(final String name) { 266 return name.replace('.', '/'); 267 } 268 269 /** 270 * Define the static fields common in all scripts. 271 * @param strictMode Should we generate this method in strict mode 272 */ 273 private void defineCommonStatics(final boolean strictMode) { 274 // source - used to store the source data (text) for this script. Shared across 275 // compile units. Set externally by the compiler. 276 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class); 277 278 // constants - used to the constants array for this script. Shared across 279 // compile units. Set externally by the compiler. 280 field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class); 281 282 // strictMode - was this script compiled in strict mode. Set externally by the compiler. 283 field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode); 284 } 285 286 /** 287 * Define static utilities common needed in scripts. These are per compile unit 288 * and therefore have to be defined here and not in code gen. 289 */ 290 private void defineCommonUtilities() { 291 assert unitClassName != null; 292 293 if (constantMethodNeeded.contains(String.class)) { 294 // $getString - get the ith entry from the constants table and cast to String. 295 final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class); 296 getStringMethod.begin(); 297 getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor()) 298 .load(Type.INT, 0) 299 .arrayload() 300 .checkcast(String.class) 301 ._return(); 302 getStringMethod.end(); 303 } 304 305 if (constantMethodNeeded.contains(PropertyMap.class)) { 306 // $getMap - get the ith entry from the constants table and cast to PropertyMap. 307 final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class); 308 getMapMethod.begin(); 309 getMapMethod.loadConstants() 310 .load(Type.INT, 0) 311 .arrayload() 312 .checkcast(PropertyMap.class) 313 ._return(); 314 getMapMethod.end(); 315 316 // $setMap - overwrite an existing map. 317 final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class); 318 setMapMethod.begin(); 319 setMapMethod.loadConstants() 320 .load(Type.INT, 0) 321 .load(Type.OBJECT, 1) 322 .arraystore(); 323 setMapMethod.returnVoid(); 324 setMapMethod.end(); 325 } 326 327 // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[]. 328 for (final Class<?> clazz : constantMethodNeeded) { 329 if (clazz.isArray()) { 330 defineGetArrayMethod(clazz); 331 } 332 } 333 } 334 335 /** 336 * Constructs a primitive specific method for getting the ith entry from the constants table as an array. 337 * @param clazz Array class. 338 */ 339 private void defineGetArrayMethod(final Class<?> clazz) { 340 assert unitClassName != null; 341 342 final String methodName = getArrayMethodName(clazz); 343 final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class); 344 345 getArrayMethod.begin(); 346 getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor()) 347 .load(Type.INT, 0) 348 .arrayload() 349 .checkcast(clazz) 350 .invoke(virtualCallNoLookup(clazz, "clone", Object.class)) 351 .checkcast(clazz) 352 ._return(); 353 getArrayMethod.end(); 354 } 355 356 357 /** 358 * Generate the name of a get array from constant pool method. 359 * @param clazz Name of array class. 360 * @return Method name. 361 */ 362 static String getArrayMethodName(final Class<?> clazz) { 363 assert clazz.isArray(); 364 return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName(); 365 } 366 367 /** 368 * Ensure a get constant method is issued for the class. 369 * @param clazz Class of constant. 370 */ 371 void needGetConstantMethod(final Class<?> clazz) { 372 constantMethodNeeded.add(clazz); 373 } 374 375 /** 376 * Inspect class name and decide whether we are generating a ScriptObject class 377 * 378 * @param scriptPrefix the script class prefix for the current script 379 * @param type the type to check 380 * 381 * @return true if type is ScriptObject 382 */ 383 private static boolean isScriptObject(final String scriptPrefix, final String type) { 384 if (type.startsWith(scriptPrefix)) { 385 return true; 386 } else if (type.equals(CompilerConstants.className(ScriptObject.class))) { 387 return true; 388 } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) { 389 return true; 390 } 391 392 return false; 393 } 394 395 /** 396 * Call at beginning of class emission 397 * @see Emitter 398 */ 399 @Override 400 public void begin() { 401 classStarted = true; 402 } 403 404 /** 405 * Call at end of class emission 406 * @see Emitter 407 */ 408 @Override 409 public void end() { 410 assert classStarted : "class not started for " + unitClassName; 411 412 if (unitClassName != null) { 413 final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE)); 414 initMethod.begin(); 415 initMethod.load(Type.OBJECT, 0); 416 initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class); 417 initMethod.returnVoid(); 418 initMethod.end(); 419 420 defineCommonUtilities(); 421 } 422 423 cw.visitEnd(); 424 classStarted = false; 425 classEnded = true; 426 assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted; 427 } 428 429 /** 430 * Disassemble an array of byte code. 431 * @param bytecode byte array representing bytecode 432 * @return disassembly as human readable string 433 */ 434 static String disassemble(final byte[] bytecode) { 435 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 436 try (final PrintWriter pw = new PrintWriter(baos)) { 437 final NashornClassReader cr = new NashornClassReader(bytecode); 438 final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() { 439 @Override 440 public Context run() { 441 return Context.getContext(); 442 } 443 }); 444 final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw); 445 cr.accept(tcv, 0); 446 } 447 448 final String str = new String(baos.toByteArray()); 449 return str; 450 } 451 452 /** 453 * Call back from MethodEmitter for method start 454 * 455 * @see MethodEmitter 456 * 457 * @param method method emitter. 458 */ 459 void beginMethod(final MethodEmitter method) { 460 assert !methodsStarted.contains(method); 461 methodsStarted.add(method); 462 } 463 464 /** 465 * Call back from MethodEmitter for method end 466 * 467 * @see MethodEmitter 468 * 469 * @param method 470 */ 471 void endMethod(final MethodEmitter method) { 472 assert methodsStarted.contains(method); 473 methodsStarted.remove(method); 474 } 475 476 SplitMethodEmitter method(final SplitNode splitNode, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 477 methodCount++; 478 methodNames.add(methodName); 479 return new SplitMethodEmitter(this, methodVisitor(EnumSet.of(Flag.PUBLIC, Flag.STATIC), methodName, rtype, ptypes), splitNode); 480 } 481 482 /** 483 * Add a new method to the class - defaults to public method 484 * 485 * @param methodName name of method 486 * @param rtype return type of the method 487 * @param ptypes parameter types the method 488 * 489 * @return method emitter to use for weaving this method 490 */ 491 MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 492 return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ? 493 } 494 495 /** 496 * Add a new method to the class - defaults to public method 497 * 498 * @param methodFlags access flags for the method 499 * @param methodName name of method 500 * @param rtype return type of the method 501 * @param ptypes parameter types the method 502 * 503 * @return method emitter to use for weaving this method 504 */ 505 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 506 methodCount++; 507 methodNames.add(methodName); 508 return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes)); 509 } 510 511 /** 512 * Add a new method to the class - defaults to public method 513 * 514 * @param methodName name of method 515 * @param descriptor descriptor of method 516 * 517 * @return method emitter to use for weaving this method 518 */ 519 MethodEmitter method(final String methodName, final String descriptor) { 520 return method(DEFAULT_METHOD_FLAGS, methodName, descriptor); 521 } 522 523 /** 524 * Add a new method to the class - defaults to public method 525 * 526 * @param methodFlags access flags for the method 527 * @param methodName name of method 528 * @param descriptor descriptor of method 529 * 530 * @return method emitter to use for weaving this method 531 */ 532 MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) { 533 methodCount++; 534 methodNames.add(methodName); 535 return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null)); 536 } 537 538 /** 539 * Add a new method to the class, representing a function node 540 * 541 * @param functionNode the function node to generate a method for 542 * @return method emitter to use for weaving this method 543 */ 544 MethodEmitter method(final FunctionNode functionNode) { 545 methodCount++; 546 methodNames.add(functionNode.getName()); 547 final FunctionSignature signature = new FunctionSignature(functionNode); 548 final MethodVisitor mv = cw.visitMethod( 549 ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0), 550 functionNode.getName(), 551 signature.toString(), 552 null, 553 null); 554 555 return new MethodEmitter(this, mv, functionNode); 556 } 557 558 /** 559 * Add a new method to the class, representing a rest-of version of the function node 560 * 561 * @param functionNode the function node to generate a method for 562 * @return method emitter to use for weaving this method 563 */ 564 MethodEmitter restOfMethod(final FunctionNode functionNode) { 565 methodCount++; 566 methodNames.add(functionNode.getName()); 567 final MethodVisitor mv = cw.visitMethod( 568 ACC_PUBLIC | ACC_STATIC, 569 functionNode.getName(), 570 Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class), 571 null, 572 null); 573 574 return new MethodEmitter(this, mv, functionNode); 575 } 576 577 578 /** 579 * Start generating the <clinit> method in the class 580 * 581 * @return method emitter to use for weaving <clinit> 582 */ 583 MethodEmitter clinit() { 584 clinitCount++; 585 return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class); 586 } 587 588 /** 589 * Start generating an <init>()V method in the class 590 * 591 * @return method emitter to use for weaving <init>()V 592 */ 593 MethodEmitter init() { 594 initCount++; 595 return method(INIT.symbolName(), void.class); 596 } 597 598 /** 599 * Start generating an <init>()V method in the class 600 * 601 * @param ptypes parameter types for constructor 602 * @return method emitter to use for weaving <init>()V 603 */ 604 MethodEmitter init(final Class<?>... ptypes) { 605 initCount++; 606 return method(INIT.symbolName(), void.class, ptypes); 607 } 608 609 /** 610 * Start generating an <init>(...)V method in the class 611 * 612 * @param flags access flags for the constructor 613 * @param ptypes parameter types for the constructor 614 * 615 * @return method emitter to use for weaving <init>(...)V 616 */ 617 MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) { 618 initCount++; 619 return method(flags, INIT.symbolName(), void.class, ptypes); 620 } 621 622 /** 623 * Add a field to the class, initialized to a value 624 * 625 * @param fieldFlags flags, e.g. should it be static or public etc 626 * @param fieldName name of field 627 * @param fieldType the type of the field 628 * @param value the value 629 * 630 * @see ClassEmitter.Flag 631 */ 632 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) { 633 fieldCount++; 634 cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd(); 635 } 636 637 /** 638 * Add a field to the class 639 * 640 * @param fieldFlags access flags for the field 641 * @param fieldName name of field 642 * @param fieldType type of the field 643 * 644 * @see ClassEmitter.Flag 645 */ 646 final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) { 647 field(fieldFlags, fieldName, fieldType, null); 648 } 649 650 /** 651 * Add a field to the class - defaults to public 652 * 653 * @param fieldName name of field 654 * @param fieldType type of field 655 */ 656 final void field(final String fieldName, final Class<?> fieldType) { 657 field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null); 658 } 659 660 /** 661 * Return a bytecode array from this ClassEmitter. The ClassEmitter must 662 * have been ended (having its end function called) for this to work. 663 * 664 * @return byte code array for generated class, null if class generation hasn't been ended with {@link ClassEmitter#end()} 665 */ 666 byte[] toByteArray() { 667 assert classEnded; 668 if (!classEnded) { 669 return null; 670 } 671 672 return cw.toByteArray(); 673 } 674 675 /** 676 * Abstraction for flags used in class emission 677 * 678 * We provide abstraction separating these from the underlying bytecode 679 * emitter. 680 * 681 * Flags are provided for method handles, protection levels, static/virtual 682 * fields/methods. 683 */ 684 static enum Flag { 685 /** method handle with static access */ 686 HANDLE_STATIC(H_INVOKESTATIC), 687 /** method handle with new invoke special access */ 688 HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL), 689 /** method handle with invoke special access */ 690 HANDLE_SPECIAL(H_INVOKESPECIAL), 691 /** method handle with invoke virtual access */ 692 HANDLE_VIRTUAL(H_INVOKEVIRTUAL), 693 /** method handle with invoke interface access */ 694 HANDLE_INTERFACE(H_INVOKEINTERFACE), 695 696 /** final access */ 697 FINAL(ACC_FINAL), 698 /** static access */ 699 STATIC(ACC_STATIC), 700 /** public access */ 701 PUBLIC(ACC_PUBLIC), 702 /** private access */ 703 PRIVATE(ACC_PRIVATE); 704 705 private int value; 706 707 private Flag(final int value) { 708 this.value = value; 709 } 710 711 /** 712 * Get the value of this flag 713 * @return the int value 714 */ 715 int getValue() { 716 return value; 717 } 718 719 /** 720 * Return the corresponding ASM flag value for an enum set of flags 721 * 722 * @param flags enum set of flags 723 * @return an integer value representing the flags intrinsic values or:ed together 724 */ 725 static int getValue(final EnumSet<Flag> flags) { 726 int v = 0; 727 for (final Flag flag : flags) { 728 v |= flag.getValue(); 729 } 730 return v; 731 } 732 } 733 734 private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) { 735 return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null); 736 } 737 738} 739