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