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