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  *
 104  * @see Compiler
 105  */
 106 public 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 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 }