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 
  26 package jdk.nashorn.internal.codegen;
  27 
  28 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
  29 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
  30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  31 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
  32 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
  33 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
  34 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE;
  35 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL;
  36 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
  37 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
  38 import static jdk.internal.org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
  39 import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
  40 import static jdk.nashorn.internal.codegen.CompilerConstants.CLINIT;
  41 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
  42 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_PREFIX;
  43 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_SUFFIX;
  44 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
  45 import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
  46 import static jdk.nashorn.internal.codegen.CompilerConstants.INIT;
  47 import static jdk.nashorn.internal.codegen.CompilerConstants.SET_MAP;
  48 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
  49 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
  50 import static jdk.nashorn.internal.codegen.CompilerConstants.className;
  51 import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
  52 import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
  53 import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
  54 
  55 import java.io.ByteArrayOutputStream;
  56 import java.io.PrintWriter;
  57 import java.security.AccessController;
  58 import java.security.PrivilegedAction;
  59 import java.util.Collections;
  60 import java.util.EnumSet;
  61 import java.util.HashSet;
  62 import java.util.Set;
  63 import jdk.internal.org.objectweb.asm.ClassWriter;
  64 import jdk.internal.org.objectweb.asm.MethodVisitor;
  65 import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
  66 import jdk.nashorn.internal.codegen.types.Type;
  67 import jdk.nashorn.internal.ir.FunctionNode;
  68 import jdk.nashorn.internal.ir.debug.NashornClassReader;
  69 import jdk.nashorn.internal.ir.debug.NashornTextifier;
  70 import jdk.nashorn.internal.runtime.Context;
  71 import jdk.nashorn.internal.runtime.PropertyMap;
  72 import jdk.nashorn.internal.runtime.RewriteException;
  73 import jdk.nashorn.internal.runtime.ScriptObject;
  74 import 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  * <p>
 104  *
 105  * @see Compiler
 106  */
 107 public class ClassEmitter {
 108     /** Default flags for class generation - public class */
 109     private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
 110 
 111     /** Sanity check flag - have we started on a class? */
 112     private boolean classStarted;
 113 
 114     /** Sanity check flag - have we ended this emission? */
 115     private boolean classEnded;
 116 
 117     /**
 118      * Sanity checks - which methods have we currently
 119      * started for generation in this class?
 120      */
 121     private final HashSet<MethodEmitter> methodsStarted;
 122 
 123     /** The ASM classwriter that we use for all bytecode operations */
 124     protected final ClassWriter cw;
 125 
 126     /** The script environment */
 127     protected final Context context;
 128 
 129     /** Compile unit class name. */
 130     private String unitClassName;
 131 
 132     /** Set of constants access methods required. */
 133     private Set<Class<?>> constantMethodNeeded;
 134 
 135     private int methodCount;
 136 
 137     private int initCount;
 138 
 139     private int clinitCount;
 140 
 141     private int fieldCount;
 142 
 143     private final Set<String> methodNames;
 144 
 145     /**
 146      * Constructor - only used internally in this class as it breaks
 147      * abstraction towards ASM or other code generator below
 148      *
 149      * @param env script environment
 150      * @param cw  ASM classwriter
 151      */
 152     private ClassEmitter(final Context context, final ClassWriter cw) {
 153         this.context        = context;
 154         this.cw             = cw;
 155         this.methodsStarted = new HashSet<>();
 156         this.methodNames    = new HashSet<>();
 157     }
 158 
 159     /**
 160      * Return the method names encountered
 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 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      */
 398     public void begin() {
 399         classStarted = true;
 400     }
 401 
 402     /**
 403      * Call at end of class emission
 404      */
 405     public void end() {
 406         assert classStarted : "class not started for " + unitClassName;
 407 
 408         if (unitClassName != null) {
 409             final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
 410             initMethod.begin();
 411             initMethod.load(Type.OBJECT, 0);
 412             initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
 413             initMethod.returnVoid();
 414             initMethod.end();
 415 
 416             defineCommonUtilities();
 417         }
 418 
 419         cw.visitEnd();
 420         classStarted = false;
 421         classEnded   = true;
 422         assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
 423     }
 424 
 425     /**
 426      * Disassemble an array of byte code.
 427      * @param bytecode  byte array representing bytecode
 428      * @return disassembly as human readable string
 429      */
 430     static String disassemble(final byte[] bytecode) {
 431         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 432         try (final PrintWriter pw = new PrintWriter(baos)) {
 433             final NashornClassReader cr = new NashornClassReader(bytecode);
 434             final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
 435                 @Override
 436                 public Context run() {
 437                     return Context.getContext();
 438                 }
 439             });
 440             final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
 441             cr.accept(tcv, 0);
 442         }
 443 
 444         final String str = new String(baos.toByteArray());
 445         return str;
 446     }
 447 
 448     /**
 449      * Call back from MethodEmitter for method start
 450      *
 451      * @see MethodEmitter
 452      *
 453      * @param method method emitter.
 454      */
 455     void beginMethod(final MethodEmitter method) {
 456         assert !methodsStarted.contains(method);
 457         methodsStarted.add(method);
 458     }
 459 
 460     /**
 461      * Call back from MethodEmitter for method end
 462      *
 463      * @see MethodEmitter
 464      *
 465      * @param method
 466      */
 467     void endMethod(final MethodEmitter method) {
 468         assert methodsStarted.contains(method);
 469         methodsStarted.remove(method);
 470     }
 471 
 472     /**
 473      * Add a new method to the class - defaults to public method
 474      *
 475      * @param methodName name of method
 476      * @param rtype      return type of the method
 477      * @param ptypes     parameter types the method
 478      *
 479      * @return method emitter to use for weaving this method
 480      */
 481     MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 482         return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
 483     }
 484 
 485     /**
 486      * Add a new method to the class - defaults to public method
 487      *
 488      * @param methodFlags access flags for the method
 489      * @param methodName  name of method
 490      * @param rtype       return type of the method
 491      * @param ptypes      parameter types the method
 492      *
 493      * @return method emitter to use for weaving this method
 494      */
 495     MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 496         methodCount++;
 497         methodNames.add(methodName);
 498         return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
 499     }
 500 
 501     /**
 502      * Add a new method to the class - defaults to public method
 503      *
 504      * @param methodName name of method
 505      * @param descriptor descriptor of method
 506      *
 507      * @return method emitter to use for weaving this method
 508      */
 509     MethodEmitter method(final String methodName, final String descriptor) {
 510         return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
 511     }
 512 
 513     /**
 514      * Add a new method to the class - defaults to public method
 515      *
 516      * @param methodFlags access flags for the method
 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 EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
 523         methodCount++;
 524         methodNames.add(methodName);
 525         return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
 526     }
 527 
 528     /**
 529      * Add a new method to the class, representing a function node
 530      *
 531      * @param functionNode the function node to generate a method for
 532      * @return method emitter to use for weaving this method
 533      */
 534     MethodEmitter method(final FunctionNode functionNode) {
 535         methodCount++;
 536         methodNames.add(functionNode.getName());
 537         final FunctionSignature signature = new FunctionSignature(functionNode);
 538         final MethodVisitor mv = cw.visitMethod(
 539             ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
 540             functionNode.getName(),
 541             signature.toString(),
 542             null,
 543             null);
 544 
 545         return new MethodEmitter(this, mv, functionNode);
 546     }
 547 
 548     /**
 549      * Add a new method to the class, representing a rest-of version of the function node
 550      *
 551      * @param functionNode the function node to generate a method for
 552      * @return method emitter to use for weaving this method
 553      */
 554     MethodEmitter restOfMethod(final FunctionNode functionNode) {
 555         methodCount++;
 556         methodNames.add(functionNode.getName());
 557         final MethodVisitor mv = cw.visitMethod(
 558             ACC_PUBLIC | ACC_STATIC,
 559             functionNode.getName(),
 560             Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
 561             null,
 562             null);
 563 
 564         return new MethodEmitter(this, mv, functionNode);
 565     }
 566 
 567 
 568     /**
 569      * Start generating the <clinit> method in the class
 570      *
 571      * @return method emitter to use for weaving <clinit>
 572      */
 573     MethodEmitter clinit() {
 574         clinitCount++;
 575         return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
 576     }
 577 
 578     /**
 579      * Start generating an <init>()V method in the class
 580      *
 581      * @return method emitter to use for weaving <init>()V
 582      */
 583     MethodEmitter init() {
 584         initCount++;
 585         return method(INIT.symbolName(), void.class);
 586     }
 587 
 588     /**
 589      * Start generating an <init>()V method in the class
 590      *
 591      * @param ptypes parameter types for constructor
 592      * @return method emitter to use for weaving <init>()V
 593      */
 594     MethodEmitter init(final Class<?>... ptypes) {
 595         initCount++;
 596         return method(INIT.symbolName(), void.class, ptypes);
 597     }
 598 
 599     /**
 600      * Start generating an <init>(...)V method in the class
 601      *
 602      * @param flags  access flags for the constructor
 603      * @param ptypes parameter types for the constructor
 604      *
 605      * @return method emitter to use for weaving <init>(...)V
 606      */
 607     MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
 608         initCount++;
 609         return method(flags, INIT.symbolName(), void.class, ptypes);
 610     }
 611 
 612     /**
 613      * Add a field to the class, initialized to a value
 614      *
 615      * @param fieldFlags flags, e.g. should it be static or public etc
 616      * @param fieldName  name of field
 617      * @param fieldType  the type of the field
 618      * @param value      the value
 619      *
 620      * @see ClassEmitter.Flag
 621      */
 622     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
 623         fieldCount++;
 624         cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
 625     }
 626 
 627     /**
 628      * Add a field to the class
 629      *
 630      * @param fieldFlags access flags for the field
 631      * @param fieldName  name of field
 632      * @param fieldType  type of the field
 633      *
 634      * @see ClassEmitter.Flag
 635      */
 636     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
 637         field(fieldFlags, fieldName, fieldType, null);
 638     }
 639 
 640     /**
 641      * Add a field to the class - defaults to public
 642      *
 643      * @param fieldName  name of field
 644      * @param fieldType  type of field
 645      */
 646     final void field(final String fieldName, final Class<?> fieldType) {
 647         field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
 648     }
 649 
 650     /**
 651      * Return a bytecode array from this ClassEmitter. The ClassEmitter must
 652      * have been ended (having its end function called) for this to work.
 653      *
 654      * @return byte code array for generated class, null if class generation hasn't been ended with {@link ClassEmitter#end()}
 655      */
 656     byte[] toByteArray() {
 657         assert classEnded;
 658         if (!classEnded) {
 659             return null;
 660         }
 661 
 662         return cw.toByteArray();
 663     }
 664 
 665     /**
 666      * Abstraction for flags used in class emission
 667      *
 668      * We provide abstraction separating these from the underlying bytecode
 669      * emitter.
 670      *
 671      * Flags are provided for method handles, protection levels, static/virtual
 672      * fields/methods.
 673      */
 674     static enum Flag {
 675         /** method handle with static access */
 676         HANDLE_STATIC(H_INVOKESTATIC),
 677         /** method handle with new invoke special access */
 678         HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
 679         /** method handle with invoke special access */
 680         HANDLE_SPECIAL(H_INVOKESPECIAL),
 681         /** method handle with invoke virtual access */
 682         HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
 683         /** method handle with invoke interface access */
 684         HANDLE_INTERFACE(H_INVOKEINTERFACE),
 685 
 686         /** final access */
 687         FINAL(ACC_FINAL),
 688         /** static access */
 689         STATIC(ACC_STATIC),
 690         /** public access */
 691         PUBLIC(ACC_PUBLIC),
 692         /** private access */
 693         PRIVATE(ACC_PRIVATE);
 694 
 695         private int value;
 696 
 697         private Flag(final int value) {
 698             this.value = value;
 699         }
 700 
 701         /**
 702          * Get the value of this flag
 703          * @return the int value
 704          */
 705         int getValue() {
 706             return value;
 707         }
 708 
 709         /**
 710          * Return the corresponding ASM flag value for an enum set of flags
 711          *
 712          * @param flags enum set of flags
 713          * @return an integer value representing the flags intrinsic values or:ed together
 714          */
 715         static int getValue(final EnumSet<Flag> flags) {
 716             int v = 0;
 717             for (final Flag flag : flags) {
 718                 v |= flag.getValue();
 719             }
 720             return v;
 721         }
 722     }
 723 
 724     private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 725         return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
 726     }
 727 
 728 }