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