1   // Copyright 2012- Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.io.BufferedOutputStream;
6   import java.io.ByteArrayOutputStream;
7   import java.io.DataOutputStream;
8   import java.io.File;
9   import java.io.FileNotFoundException;
10  import java.io.FileOutputStream;
11  import java.io.IOException;
12  import java.io.OutputStream;
13  
14  import java.util.ArrayList;
15  import java.util.Hashtable;
16  import java.util.Stack;
17  import java.util.StringTokenizer;
18  import java.util.TreeMap;
19  
20  import static jminusminus.CLConstants.*;
21  
22  /**
23   * This class provides a high level interface for creating (in-memory and file based)
24   * representation of Java classes.
25   * <p>
26   * j-- uses this interface to produce target JVM bytecode from a j-- source program. During the
27   * pre-analysis and analysis phases, j-- produces partial (in-memory) classes for the type
28   * declarations within the compilation unit, and during the code generation phase, it produces
29   * file-based classes for the declarations.
30   */
31  public class CLEmitter {
32      // Name of the class.
33      private String name;
34  
35      // If true, the in-memory representation of the class will be written to the file system.
36      // Otherwise, it won't be saved as a file.
37      private boolean toFile;
38  
39      // Destination directory for the class.
40      private String destDir;
41  
42      // In-memory representation of the class.
43      private CLFile clFile;
44  
45      // Constant pool of the class.
46      private CLConstantPool constantPool;
47  
48      // Direct super interfaces of the class.
49      private ArrayList<Integer> interfaces;
50  
51      // Fields in the class.
52      private ArrayList<CLFieldInfo> fields;
53  
54      // Attributes of the field last added.
55      private ArrayList<CLAttributeInfo> fAttributes;
56  
57      // Methods in the class.
58      private ArrayList<CLMethodInfo> methods;
59  
60      // Attributes of the method last added.
61      private ArrayList<CLAttributeInfo> mAttributes;
62  
63      // Attributes of the class.
64      private ArrayList<CLAttributeInfo> attributes;
65  
66      // Inner classes of the class.
67      private ArrayList<CLInnerClassInfo> innerClasses;
68  
69      // Code (instruction) section of the method last added.
70      private ArrayList<CLInstruction> mCode;
71  
72      // Table containing exception handlers in the method last added.
73      private ArrayList<CLException> mExceptionHandlers;
74  
75      // Access flags of the method last added.
76      private int mAccessFlags;
77  
78      // Index into the constant pool, the item at which specifies the name of the method last added.
79      private int mNameIndex;
80  
81      // Index into the constant pool, the item at which specifies the descriptor of the method
82      // last added.
83      private int mDescriptorIndex;
84  
85      // Number of arguments for the method last added.
86      private int mArgumentCount;
87  
88      // Code attributes of the method last added.
89      private ArrayList<CLAttributeInfo> mCodeAttributes;
90  
91      // Whether the method last added needs closing.
92      private boolean isMethodOpen;
93  
94      // Stores jump labels for the method last added. When a label is created, a mapping from the
95      // label to an Integer representing the pc of the next instruction is created. Later on, when
96      // the label is added, its Integer value is replaced by the value of pc then.
97      private Hashtable<String, Integer> mLabels;
98  
99      // Counter for creating unique jump labels.
100     private int mLabelCount;
101 
102     // Whether there was an instruction added after the last call to addLabel( String label). If
103     // not, the branch instruction that was added with that label would jump beyond the code
104     // section, which is not acceptable to the runtime class loader. Therefore, if this flag is
105     // false, we add a NOP instruction at the end of the code section to make the jump valid.
106     private boolean mInstructionAfterLabel = false;
107 
108     // Location counter; index of the next instruction within the code section of the method last
109     // added.
110     private int mPC;
111 
112     // Name of the method last added; used for error reporting.
113     private String eCurrentMethod;
114 
115     // Whether an error occurred while creating/writing the class.
116     private boolean errorHasOccurred;
117 
118     // Class loader to use for creating in-memory representation of classes from byte streams.
119     private static ByteClassLoader byteClassLoader;
120 
121     /**
122      * Constructs a CLEmitter instance given a boolean on whether or not the class file will be
123      * written to the file system.
124      *
125      * @param toFile if true the in-memory representation of the class file will be written to
126      *               the file system. Otherwise, it won't be saved as a file.
127      */
128     public CLEmitter(boolean toFile) {
129         destDir = ".";
130         this.toFile = toFile;
131     }
132 
133     /**
134      * Sets the destination directory for the class file to the specified value.
135      *
136      * @param destDir destination directory.
137      */
138     public void destinationDir(String destDir) {
139         this.destDir = destDir;
140     }
141 
142     /**
143      * Returns true if an emitter error has occurred up to now, and false otherwise.
144      *
145      * @return true or true if an emitter error has occurred up to now, and false otherwise.
146      */
147     public boolean errorHasOccurred() {
148         return errorHasOccurred;
149     }
150 
151     /**
152      * Adds a class or interface to the class file.
153      * <p>
154      * This method instantiates a class file representation in memory, so this method
155      * <em>must</em> be called prior to methods that add information (fields, methods,
156      * instructions, etc.) to the class.
157      *
158      * @param accessFlags     the access flags for the class or interface.
159      * @param thisClass       fully qualified name of the class or interface in internal form.
160      * @param superClass      fully qualified name of the parent class in internal form.
161      * @param superInterfaces list of direct super interfaces of this class or interface as fully
162      *                        qualified names in internal form.
163      * @param isSynthetic     whether the class or interface is synthetic.
164      */
165     public void addClass(ArrayList<String> accessFlags, String thisClass, String superClass,
166                          ArrayList<String> superInterfaces, boolean isSynthetic) {
167         clFile = new CLFile();
168         constantPool = new CLConstantPool();
169         interfaces = new ArrayList<Integer>();
170         fields = new ArrayList<CLFieldInfo>();
171         methods = new ArrayList<CLMethodInfo>();
172         attributes = new ArrayList<CLAttributeInfo>();
173         innerClasses = new ArrayList<CLInnerClassInfo>();
174         errorHasOccurred = false;
175         clFile.magic = MAGIC;
176         clFile.majorVersion = MAJOR_VERSION;
177         clFile.minorVersion = MINOR_VERSION;
178         if (!validInternalForm(thisClass)) {
179             reportEmitterError("'%s' is not in internal form", thisClass);
180         }
181         if (!validInternalForm(superClass)) {
182             reportEmitterError("'%s' is not in internal form", superClass);
183         }
184         if (accessFlags != null) {
185             for (String s : accessFlags) {
186                 clFile.accessFlags |= CLFile.accessFlagToInt(s);
187             }
188         }
189         name = thisClass;
190         clFile.thisClass = constantPool.constantClassInfo(thisClass);
191         clFile.superClass = constantPool.constantClassInfo(superClass);
192         if (superInterfaces != null) {
193             for (String s : superInterfaces) {
194                 if (!validInternalForm(s)) {
195                     reportEmitterError("'%s' is not in internal form", s);
196                 }
197                 interfaces.add(constantPool.constantClassInfo(s));
198             }
199         }
200         if (isSynthetic) {
201             addClassAttribute(syntheticAttribute());
202         }
203     }
204 
205     /**
206      * Adds an inner class. Note that this only registers the inner class with its parent and
207      * does not create the class.
208      *
209      * @param accessFlags access flags for the inner class.
210      * @param innerClass  fully qualified name of the inner class in internal form.
211      * @param outerClass  fully qualified name of the outer class in internal form.
212      * @param innerName   simple name of the inner class.
213      */
214     public void addInnerClass(ArrayList<String> accessFlags, String innerClass, String outerClass,
215                               String innerName) {
216         int flags = 0;
217         if (accessFlags != null) {
218             for (String s : accessFlags) {
219                 flags |= CLFile.accessFlagToInt(s);
220             }
221         }
222         CLInnerClassInfo innerClassInfo = new CLInnerClassInfo(constantPool
223                 .constantClassInfo(innerClass), constantPool
224                 .constantClassInfo(outerClass), constantPool
225                 .constantUtf8Info(innerName), flags);
226         innerClasses.add(innerClassInfo);
227     }
228 
229     /**
230      * Adds a field without initialization.
231      *
232      * @param accessFlags access flags for the field.
233      * @param name        name of the field.
234      * @param type        type descriptor of the field.
235      * @param isSynthetic is this a synthetic field?
236      */
237     public void addField(ArrayList<String> accessFlags, String name, String type,
238                          boolean isSynthetic) {
239         addFieldInfo(accessFlags, name, type, isSynthetic, -1);
240     }
241 
242     /**
243      * Adds an int, short, char, byte, or boolean field with initialization. If the field is
244      * final, the initialization is added to the constant pool. The initializations are
245      * all stored as ints, where boolean true and false are 1 and 0 respectively, and short,
246      * char, and byte must be cast to int.
247      *
248      * @param accessFlags access flags for the field.
249      * @param name        name of the field.
250      * @param type        type descriptor of the field.
251      * @param isSynthetic is this a synthetic field?
252      * @param i           int value.
253      */
254     public void addField(ArrayList<String> accessFlags, String name, String type,
255                          boolean isSynthetic, int i) {
256         addFieldInfo(accessFlags, name, type, isSynthetic, constantPool.constantIntegerInfo(i));
257     }
258 
259     /**
260      * Adds a float field with initialization. If the field is final, the initialization is added
261      * to the constant pool.
262      *
263      * @param accessFlags access flags for the field.
264      * @param name        name of the field.
265      * @param isSynthetic is this a synthetic field?
266      * @param f           float value.
267      */
268     public void addField(ArrayList<String> accessFlags, String name, boolean isSynthetic, float f) {
269         addFieldInfo(accessFlags, name, "F", isSynthetic, constantPool.constantFloatInfo(f));
270     }
271 
272     /**
273      * Adds a long field with initialization. If the field is final, the initialization is added
274      * to the constant pool.
275      *
276      * @param accessFlags access flags for the field.
277      * @param name        name of the field.
278      * @param isSynthetic is this a synthetic field?
279      * @param l           long value.
280      */
281     public void addField(ArrayList<String> accessFlags, String name, boolean isSynthetic, long l) {
282         addFieldInfo(accessFlags, name, "J", isSynthetic, constantPool.constantLongInfo(l));
283     }
284 
285     /**
286      * Adds a double field with initialization. If the field is final, the initialization is
287      * added to the constant pool.
288      *
289      * @param accessFlags access flags for the field.
290      * @param name        name of the field.
291      * @param isSynthetic is this a synthetic field?
292      * @param d           double value.
293      */
294     public void addField(ArrayList<String> accessFlags, String name, boolean isSynthetic,
295                          double d) {
296         addFieldInfo(accessFlags, name, "D", isSynthetic, constantPool.constantDoubleInfo(d));
297     }
298 
299     /**
300      * Adds a String type field with initialization. If the field is final, the initialization is
301      * added to the constant pool.
302      *
303      * @param accessFlags access flags for the field.
304      * @param name        name of the field.
305      * @param isSynthetic is this a synthetic field?
306      * @param s           String value.
307      */
308     public void addField(ArrayList<String> accessFlags, String name, boolean isSynthetic,
309                          String s) {
310         addFieldInfo(accessFlags, name, "Ljava/lang/String;", isSynthetic,
311                 constantPool.constantStringInfo(s));
312     }
313 
314     /**
315      * Adds a method. Instructions can subsequently be added to this method using the appropriate
316      * methods for adding instructions.
317      *
318      * @param accessFlags access flags for the method.
319      * @param name        name of the method.
320      * @param descriptor  descriptor specifying the return type and the types of the formal
321      *                    parameters of the method.
322      * @param exceptions  exceptions thrown by the method, each being a name in fully qualified
323      *                    internal form.
324      * @param isSynthetic whether this is a synthetic method?
325      */
326     public void addMethod(ArrayList<String> accessFlags, String name, String descriptor,
327                           ArrayList<String> exceptions, boolean isSynthetic) {
328         if (!validMethodDescriptor(descriptor)) {
329             reportEmitterError("'%s' is not a valid type descriptor for method", descriptor);
330         }
331         endOpenMethodIfAny(); // close any previous method
332         isMethodOpen = true;
333         initializeMethodVariables();
334         eCurrentMethod = name + descriptor;
335         if (accessFlags != null) {
336             for (String s : accessFlags) {
337                 mAccessFlags |= CLFile.accessFlagToInt(s);
338             }
339         }
340         mArgumentCount = argumentCount(descriptor) + (accessFlags.contains("static") ? 0 : 1);
341         mNameIndex = constantPool.constantUtf8Info(name);
342         mDescriptorIndex = constantPool.constantUtf8Info(descriptor);
343         if (exceptions != null && exceptions.size() > 0) {
344             addMethodAttribute(exceptionsAttribute(exceptions));
345         }
346         if (isSynthetic) {
347             addMethodAttribute(syntheticAttribute());
348         }
349     }
350 
351     /**
352      * Adds an exception handler.
353      *
354      * @param startLabel   the exception handler is active from the instruction following this
355      *                     label in the code section of the current method being added ...
356      * @param endLabel     to the instruction following this label. Formally, the handler is
357      *                     active while the program counter is within the interval [startLabel,
358      *                     endLabel).
359      * @param handlerLabel the handler begins with instruction following this label.
360      * @param catchType    the exception type that this exception handler is designated to catch,
361      *                     as a fully qualified name in internal form. If null, this exception
362      *                     handler is called for all exceptions; this is used to implement
363      *                     "finally".
364      */
365     public void addExceptionHandler(String startLabel, String endLabel, String handlerLabel,
366                                     String catchType) {
367         if (catchType != null && !validInternalForm(catchType)) {
368             reportEmitterError("'%s' is not in internal form", catchType);
369         }
370         CLException e = new CLException(startLabel, endLabel, handlerLabel, catchType);
371         mExceptionHandlers.add(e);
372     }
373 
374     /**
375      * Adds a no argument instruction. The following instructions can be added using this method:
376      *
377      * <p>
378      * Arithmetic Instructions:
379      *
380      * <pre>
381      *   IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV,
382      *   DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG
383      * </pre>
384      *
385      * <p>
386      * Array Instructions:
387      *
388      * <pre>
389      *   IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE,
390      *   DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, ARRAYLENGTH
391      * </pre>
392      *
393      * <p>
394      * Bit Instructions:
395      *
396      * <pre>
397      *   ISHL, ISHR, IUSHR, LSHL, LSHR, LUSHR, IOR, LOR, IAND, LAND, IXOR, LXOR
398      * </pre>
399      *
400      * <p>
401      * Comparison Instructions:
402      *
403      * <pre>
404      *   DCMPG, DCMPL, FCMPG, FCMPL, LCMP
405      * </pre>
406      *
407      * <p>
408      * Conversion Instructions:
409      *
410      * <pre>
411      *   I2B, I2C, I2S, I2L, I2F, I2D, L2F, L2D, L2I, F2D, F2I, F2L, D2I, D2L, D2F
412      * </pre>
413      *
414      * <p>
415      * Load Store Instructions:
416      *
417      * <pre>
418      *   ILOAD_0, ILOAD_1, ILOAD_2, ILOAD_3, LLOAD_0, LLOAD_1, LLOAD_2, LLOAD_3, FLOAD_0, FLOAD_1,
419      *   FLOAD_2, FLOAD_3, DLOAD_0, DLOAD_1, DLOAD_2, DLOAD_3, ALOAD_0, ALOAD_1, ALOAD_2, ALOAD_3,
420      *   ISTORE_0, ISTORE_1, ISTORE_2, ISTORE_3, LSTORE_0, LSTORE_1, LSTORE_2, LSTORE_3, FSTORE_0,
421      *   FSTORE_1, FSTORE_2, FSTORE_3, DSTORE_0, DSTORE_1, DSTORE_2, DSTORE_3, ASTORE_0, ASTORE_1,
422      *   ASTORE_2, ASTORE_3, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5, ICONST_M1,
423      *   LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, ACONST_NULL,
424      *   WIDE (added automatically where necesary)
425      * </pre>
426      *
427      * <p>
428      * Method Instructions:
429      *
430      * <pre>
431      *   IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, RETURN
432      * </pre>
433      *
434      * <p>
435      * Stack Instructions:
436      *
437      * <pre>
438      *   POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
439      * </pre>
440      *
441      * <p>
442      * Miscellaneous Instructions:
443      *
444      * <pre>
445      *   NOP, ATHROW, MONITORENTER, MONITOREXIT
446      * </pre>
447      * <p>
448      * The opcodes for instructions are defined in CLConstants class.
449      *
450      * @param opcode opcode of the instruction.
451      */
452     public void addNoArgInstruction(int opcode) {
453         CLInstruction instr = null;
454         switch (CLInstruction.instructionInfo[opcode].category) {
455             case ARITHMETIC1:
456                 instr = new CLArithmeticInstruction(opcode, mPC++);
457                 break;
458             case ARRAY2:
459                 instr = new CLArrayInstruction(opcode, mPC++);
460                 break;
461             case BIT:
462                 instr = new CLBitInstruction(opcode, mPC++);
463                 break;
464             case COMPARISON:
465                 instr = new CLComparisonInstruction(opcode, mPC++);
466                 break;
467             case CONVERSION:
468                 instr = new CLConversionInstruction(opcode, mPC++);
469                 break;
470             case LOAD_STORE1:
471                 instr = new CLLoadStoreInstruction(opcode, mPC++);
472                 break;
473             case METHOD2:
474                 instr = new CLMethodInstruction(opcode, mPC++);
475                 break;
476             case MISC:
477                 instr = new CLMiscInstruction(opcode, mPC++);
478                 break;
479             case STACK:
480                 instr = new CLStackInstruction(opcode, mPC++);
481                 break;
482             default:
483                 reportOpcodeError(opcode);
484         }
485         if (instr != null) {
486             mPC += instr.operandCount();
487             mCode.add(instr);
488             mInstructionAfterLabel = true;
489         }
490     }
491 
492     /**
493      * Adds a one argument instruction. Wideable instructions are widened if necessary by adding
494      * a WIDE instruction before the instruction. The following instructions can be added using
495      * this method:
496      *
497      * <p>
498      * Load Store Instructions:
499      *
500      * <pre>
501      *   ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE, BIPUSH, SIPUSH
502      * </pre>
503      *
504      * <p>
505      * Flow Control Instructions:
506      *
507      * <pre>
508      *   RET
509      * </pre>
510      * <p>
511      * The opcodes for instructions are defined in CLConstants class.
512      *
513      * @param opcode opcode of the instruction.
514      * @param arg    the argument. For the instructions that deal with local variables, the
515      *               argument is the local variable index; for BIPUSH and SIPUSH instructions,
516      *               the argument is the constant byte or short value.
517      */
518     public void addOneArgInstruction(int opcode, int arg) {
519         CLInstruction instr = null;
520         boolean isWidened = false;
521         switch (CLInstruction.instructionInfo[opcode].category) {
522             case LOAD_STORE2:
523                 isWidened = arg > 255;
524                 if (isWidened) {
525                     CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(WIDE, mPC++);
526                     mCode.add(wideInstr);
527                 }
528                 instr = new CLLoadStoreInstruction(opcode, mPC++, arg, isWidened);
529                 break;
530             case LOAD_STORE3:
531                 instr = new CLLoadStoreInstruction(opcode, mPC++, arg);
532                 break;
533             case FLOW_CONTROL2:
534                 isWidened = arg > 255;
535                 if (isWidened) {
536                     CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(WIDE, mPC++);
537                     mCode.add(wideInstr);
538                 }
539                 instr = new CLFlowControlInstruction(mPC++, arg, isWidened);
540                 break;
541             default:
542                 reportOpcodeError(opcode);
543         }
544         if (instr != null) {
545             mPC += instr.operandCount();
546             mCode.add(instr);
547             mInstructionAfterLabel = true;
548         }
549     }
550 
551     /**
552      * Adds an IINC instruction to increment a variable by a constant. The instruction is widened
553      * if necessary by adding a WIDE instruction before the instruction.
554      *
555      * @param index    local variable index.
556      * @param constVal increment value.
557      */
558     public void addIINCInstruction(int index, int constVal) {
559         boolean isWidened = index > 255 || constVal < Byte.MIN_VALUE || constVal > Byte.MAX_VALUE;
560         if (isWidened) {
561             CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(WIDE, mPC++);
562             mCode.add(wideInstr);
563         }
564         CLArithmeticInstruction instr = new CLArithmeticInstruction(IINC, mPC++, index, constVal,
565                 isWidened);
566         mPC += instr.operandCount();
567         mCode.add(instr);
568         mInstructionAfterLabel = true;
569     }
570 
571     /**
572      * Adds a member (field and method) access instruction. The following instructions can be
573      * added using this method:
574      *
575      * <p>
576      * Field Instructions:
577      *
578      * <pre>
579      *   GETSTATIC, PUTSTATIC, GETFIELD, PUTFIELD
580      * </pre>
581      *
582      * <p>
583      * Method Instructions:
584      *
585      * <pre>
586      *   INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, INVOKEDYNAMIC
587      * </pre>
588      * <p>
589      * The opcodes for instructions are defined in CLConstants class.
590      *
591      * @param opcode opcode of the instruction.
592      * @param target fully qualified name in internal form of the class to which the member belongs.
593      * @param name   name of the member.
594      * @param type   type descriptor of the member.
595      */
596     public void addMemberAccessInstruction(int opcode, String target, String name, String type) {
597         if (!validInternalForm(target)) {
598             reportEmitterError("%s: '%s' is not in internal form", eCurrentMethod, target);
599         }
600         CLInstruction instr = null;
601         int index, stackUnits;
602         switch (CLInstruction.instructionInfo[opcode].category) {
603             case FIELD:
604                 if (!validTypeDescriptor(type)) {
605                     reportEmitterError("%s: '%s' is not a valid type descriptor for field",
606                             eCurrentMethod, type);
607                 }
608                 index = constantPool.constantFieldRefInfo(target, name, type);
609                 stackUnits = typeStackResidue(type);
610                 if ((opcode == GETFIELD) || (opcode == PUTFIELD)) {
611                     // This is because target of this method is also consumed from the operand
612                     // stack.
613                     stackUnits--;
614                 }
615                 instr = new CLFieldInstruction(opcode, mPC++, index, stackUnits);
616                 break;
617             case METHOD1:
618                 if (!validMethodDescriptor(type)) {
619                     reportEmitterError("%s: '%s' is not a valid type descriptor for method",
620                             eCurrentMethod, type);
621                 }
622                 if (opcode == INVOKEINTERFACE) {
623                     index = constantPool.constantInterfaceMethodRefInfo(target, name, type);
624                 } else {
625                     index = constantPool.constantMethodRefInfo(target, name, type);
626                 }
627                 stackUnits = methodStackResidue(type);
628                 if (opcode != INVOKESTATIC) {
629                     // This is because target of this method is also consumed from the operand
630                     // stack.
631                     stackUnits--;
632                 }
633                 instr = new CLMethodInstruction(opcode, mPC++, index, stackUnits);
634 
635                 // INVOKEINTERFACE expects the number of arguments in the method to be specified
636                 // explicitly.
637                 if (opcode == INVOKEINTERFACE) {
638                     // We add 1 to account for "this".
639                     ((CLMethodInstruction) instr).setArgumentCount(argumentCount(type) + 1);
640                 }
641                 break;
642             default:
643                 reportOpcodeError(opcode);
644         }
645         if (instr != null) {
646             mPC += instr.operandCount();
647             mCode.add(instr);
648         }
649     }
650 
651     /**
652      * Adds a reference (object) instruction. The following instructions can be added using this
653      * method:
654      *
655      * <pre>
656      *   NEW, CHECKCAST, INSTANCEOF
657      * </pre>
658      * <p>
659      * The opcodes for instructions are defined in CLConstants class.
660      *
661      * @param opcode opcode of the instruction.
662      * @param type   reference type in internal form.
663      */
664     public void addReferenceInstruction(int opcode, String type) {
665         if (!validTypeDescriptor(type) && !validInternalForm(type)) {
666             reportEmitterError("%s: '%s' is neither a type descriptor nor in internal form",
667                     eCurrentMethod, type);
668         }
669         CLInstruction instr = null;
670         switch (CLInstruction.instructionInfo[opcode].category) {
671             case OBJECT:
672                 int index = constantPool.constantClassInfo(type);
673                 instr = new CLObjectInstruction(opcode, mPC++, index);
674                 break;
675             default:
676                 reportOpcodeError(opcode);
677         }
678         if (instr != null) {
679             mPC += instr.operandCount();
680             mCode.add(instr);
681         }
682     }
683 
684     /**
685      * Adds an array instruction. The following instructions can be added using this method:
686      *
687      * <pre>
688      *   NEWARRAY, ANEWARRAY
689      * </pre>
690      * <p>
691      * The opcodes for instructions are defined in CLConstants class.
692      *
693      * @param opcode opcode of the instruction.
694      * @param type   array type. In case of NEWARRAY, the primitive types are specified as: "Z"
695      *               for boolean, "C" for char, "F" for float, "D" for double, "B" for byte, "S"
696      *               for short, "I" for int, "J" for long. In case of ANEWARRAY, reference types
697      *               are specified in internal form.
698      */
699     public void addArrayInstruction(int opcode, String type) {
700         CLInstruction instr = null;
701         switch (CLInstruction.instructionInfo[opcode].category) {
702             case ARRAY1:
703                 int index = 0;
704                 if (opcode == NEWARRAY) {
705                     if (type.equalsIgnoreCase("Z")) {
706                         index = 4;
707                     } else if (type.equalsIgnoreCase("C")) {
708                         index = 5;
709                     } else if (type.equalsIgnoreCase("F")) {
710                         index = 6;
711                     } else if (type.equalsIgnoreCase("D")) {
712                         index = 7;
713                     } else if (type.equalsIgnoreCase("B")) {
714                         index = 8;
715                     } else if (type.equalsIgnoreCase("S")) {
716                         index = 9;
717                     } else if (type.equalsIgnoreCase("I")) {
718                         index = 10;
719                     } else if (type.equalsIgnoreCase("J")) {
720                         index = 11;
721                     } else {
722                         reportEmitterError("%s: '%s' is not a valid primitive type",
723                                 eCurrentMethod, type);
724                     }
725                 } else {
726                     if (!validTypeDescriptor(type) && !validInternalForm(type)) {
727                         reportEmitterError("%s: '%s' is not a valid type descriptor for an array",
728                                 eCurrentMethod, type);
729                     }
730                     index = constantPool.constantClassInfo(type);
731                 }
732                 instr = new CLArrayInstruction(opcode, mPC++, index);
733                 break;
734             default:
735                 reportOpcodeError(opcode);
736         }
737         if (instr != null) {
738             mPC += instr.operandCount();
739             mCode.add(instr);
740         }
741     }
742 
743     /**
744      * Adds a MULTIANEWARRAY instruction for creating multi-dimensional arrays.
745      *
746      * @param type array type in internal form.
747      * @param dim  number of dimensions.
748      */
749     public void addMULTIANEWARRAYInstruction(String type, int dim) {
750         CLInstruction instr = null;
751         if (!validTypeDescriptor(type)) {
752             reportEmitterError("%s: '%s' is not a valid type descriptor for an array",
753                     eCurrentMethod, type);
754         }
755         int index = constantPool.constantClassInfo(type);
756         instr = new CLArrayInstruction(MULTIANEWARRAY, mPC++, index, dim);
757         if (instr != null) {
758             mPC += instr.operandCount();
759             mCode.add(instr);
760         }
761     }
762 
763     /**
764      * Adds a branch instruction. The following instructions can be added using this method:
765      *
766      * <pre>
767      *   IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT,
768      *   IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IF_NULL, IF_NONNULL, GOTO_W, JSR_W
769      * </pre>
770      * <p>
771      * The opcodes for instructions are defined in CLConstants class.
772      *
773      * @param opcode opcode of the instruction.
774      * @param label  branch label.
775      */
776     public void addBranchInstruction(int opcode, String label) {
777         CLInstruction instr = null;
778         switch (CLInstruction.instructionInfo[opcode].category) {
779             case FLOW_CONTROL1:
780                 instr = new CLFlowControlInstruction(opcode, mPC++, label);
781                 break;
782             default:
783                 reportOpcodeError(opcode);
784         }
785         if (instr != null) {
786             mPC += instr.operandCount();
787             mCode.add(instr);
788             mInstructionAfterLabel = true;
789         }
790     }
791 
792     /**
793      * Adds a TABLESWITCH instruction (used for switch statements).
794      *
795      * @param defaultLabel jump label for default value.
796      * @param low          smallest value of index.
797      * @param high         highest value of index.
798      * @param labels       list of jump labels for each index value from low to high, end
799      *                     values included.
800      */
801     public void addTABLESWITCHInstruction(String defaultLabel, int low, int high,
802                                           ArrayList<String> labels) {
803         CLFlowControlInstruction instr = new CLFlowControlInstruction(TABLESWITCH, mPC++,
804                 defaultLabel, low, high, labels);
805         mPC += instr.operandCount();
806         mCode.add(instr);
807         mInstructionAfterLabel = true;
808     }
809 
810     /**
811      * Adds a LOOKUPSWITCH instruction (used for switch statements).
812      *
813      * @param defaultLabel    jump label for default value.
814      * @param numPairs        number of pairs in the match table.
815      * @param matchLabelPairs key match table.
816      */
817     public void addLOOKUPSWITCHInstruction(String defaultLabel, int numPairs, TreeMap<Integer,
818             String> matchLabelPairs) {
819         CLFlowControlInstruction instr = new CLFlowControlInstruction(LOOKUPSWITCH, mPC++,
820                 defaultLabel, numPairs, matchLabelPairs);
821         mPC += instr.operandCount();
822         mCode.add(instr);
823         mInstructionAfterLabel = true;
824     }
825 
826     /**
827      * Adds an LDC instruction to load an int constant on the operand stack.
828      *
829      * @param i int constant.
830      */
831     public void addLDCInstruction(int i) {
832         ldcInstruction(constantPool.constantIntegerInfo(i));
833     }
834 
835     /**
836      * Adds an LDC instruction to load a float constant on the operand stack.
837      *
838      * @param f float constant.
839      */
840     public void addLDCInstruction(float f) {
841         ldcInstruction(constantPool.constantFloatInfo(f));
842     }
843 
844     /**
845      * Adds an LDC instruction to load a long constant on the operand stack.
846      *
847      * @param l long constant.
848      */
849     public void addLDCInstruction(long l) {
850         ldc2wInstruction(constantPool.constantLongInfo(l));
851     }
852 
853     /**
854      * Adds an LDC instruction to load a double constant on the operand stack.
855      *
856      * @param d double constant.
857      */
858     public void addLDCInstruction(double d) {
859         ldc2wInstruction(constantPool.constantDoubleInfo(d));
860     }
861 
862     /**
863      * Adds an LDC instruction to load a String constant on the operand stack.
864      *
865      * @param s String constant.
866      */
867     public void addLDCInstruction(String s) {
868         ldcInstruction(constantPool.constantStringInfo(s));
869     }
870 
871     /**
872      * Adds the specified class attribute to the attribute section of the class.
873      *
874      * @param attribute class attribute.
875      */
876     public void addClassAttribute(CLAttributeInfo attribute) {
877         if (attributes != null) {
878             attributes.add(attribute);
879         }
880     }
881 
882     /**
883      * Adds the specified method attribute to the attribute section of the method last added.
884      *
885      * @param attribute method attribute.
886      */
887     public void addMethodAttribute(CLAttributeInfo attribute) {
888         if (mAttributes != null) {
889             mAttributes.add(attribute);
890         }
891     }
892 
893     /**
894      * Adds the specified field attribute the attribute section of the field last added.
895      *
896      * @param attribute field attribute.
897      */
898     public void addFieldAttribute(CLAttributeInfo attribute) {
899         if (fAttributes != null) {
900             fAttributes.add(attribute);
901         }
902     }
903 
904     /**
905      * Adds the specified code attribute to the attribute section of the code for the method last
906      * added.
907      *
908      * @param attribute code attribute.
909      */
910     public void addCodeAttribute(CLAttributeInfo attribute) {
911         if (mCodeAttributes != null) {
912             mCodeAttributes.add(attribute);
913         }
914     }
915 
916     /**
917      * Adds a jump label to the code section of the method being added. A flow control
918      * instruction that was added with this label will jump to the instruction right after the
919      * label.
920      *
921      * @param label jump label.
922      */
923     public void addLabel(String label) {
924         mLabels.put(label, mPC);
925         mInstructionAfterLabel = false;
926     }
927 
928     /**
929      * Constructs and returns a unique jump label.
930      *
931      * @return unique jump label.
932      */
933     public String createLabel() {
934         return "Label" + mLabelCount++;
935     }
936 
937     /**
938      * Returns the pc (location counter). The next instruction will be added with this pc.
939      *
940      * @return the pc.
941      */
942     public int pc() {
943         return mPC;
944     }
945 
946     /**
947      * Returns the constant pool of the class being built.
948      *
949      * @return constant pool.
950      */
951     public CLConstantPool constantPool() {
952         return constantPool;
953     }
954 
955     /**
956      * Sets a new ByteClassLoader for loading classes from byte streams.
957      */
958     public static void initializeByteClassLoader() {
959         byteClassLoader = new ByteClassLoader();
960     }
961 
962     /**
963      * Returns the CLFile instance corresponding to the class built by this emitter.
964      *
965      * @return the CLFile corresponding to the class built by this emitter.
966      */
967     public CLFile clFile() {
968         return clFile;
969     }
970 
971     /**
972      * Returns the class being constructed as a Java Class instance.
973      *
974      * @return Java Class instance.
975      */
976     public Class toClass() {
977         endOpenMethodIfAny();
978         Class theClass = null;
979         try {
980             // Extract the bytes from the class representation in memory into an array of bytes.
981             ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
982             CLOutputStream out = new CLOutputStream(new BufferedOutputStream(byteStream));
983             clFile.write(out);
984             out.close();
985             byte[] classBytes = byteStream.toByteArray();
986             byteStream.close();
987 
988             // Load a Java Class instance from its byte representation.
989             byteClassLoader.setClassBytes(classBytes);
990             theClass = byteClassLoader.loadClass(name, true);
991         } catch (IOException e) {
992             reportEmitterError("Cannot write class to byte stream");
993         } catch (ClassNotFoundException e) {
994             reportEmitterError("Cannot load class from byte stream");
995         }
996         return theClass;
997     }
998 
999     /**
1000     * Writes out the class to the file system as a .class file if toFile is true. The
1001     * destination directory for the file can be set using the destinationDir() method.
1002     */
1003    public void write() {
1004        endOpenMethodIfAny();
1005        if (!toFile) {
1006            return;
1007        }
1008        String outFile = destDir + File.separator + name + ".class";
1009        try {
1010            File file = new File(destDir + File.separator +
1011                    name.substring(0, name.lastIndexOf("/") + 1));
1012            file.mkdirs();
1013            CLOutputStream out =
1014                    new CLOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));
1015            clFile.write(out);
1016            out.close();
1017        } catch (FileNotFoundException e) {
1018            reportEmitterError("File %s not found", outFile);
1019        } catch (IOException e) {
1020            reportEmitterError("Cannot write to file %s", outFile);
1021        }
1022    }
1023
1024    // Initializes all variables used for adding a method to the ClassFile structure to their
1025    // appropriate values.
1026    private void initializeMethodVariables() {
1027        mAccessFlags = 0;
1028        mNameIndex = -1;
1029        mDescriptorIndex = -1;
1030        mArgumentCount = 0;
1031        mPC = 0;
1032        mAttributes = new ArrayList<CLAttributeInfo>();
1033        mExceptionHandlers = new ArrayList<CLException>();
1034        mCode = new ArrayList<CLInstruction>();
1035        mCodeAttributes = new ArrayList<CLAttributeInfo>();
1036        mLabels = new Hashtable<String, Integer>();
1037        mLabelCount = 1;
1038        mInstructionAfterLabel = false;
1039    }
1040
1041    // Adds the method created using addMethod() to the ClassFile structure. This involves adding
1042    // an instance of CLMethodInfo to ClassFile.methods for the method.
1043    private void endOpenMethodIfAny() {
1044        if (isMethodOpen) {
1045            isMethodOpen = false;
1046            if (!mInstructionAfterLabel) {
1047                // Must jump to an instruction.
1048                addNoArgInstruction(NOP);
1049            }
1050
1051            // Resolve jump labels in exception handlers.
1052            ArrayList<CLExceptionInfo> exceptionTable = new ArrayList<CLExceptionInfo>();
1053            for (CLException e : mExceptionHandlers) {
1054                if (!e.resolveLabels(mLabels)) {
1055                    reportEmitterError("%s: Unable to resolve exception handler label(s)",
1056                            eCurrentMethod);
1057                }
1058
1059                // We allow catchType to be null (mapping to index 0), implying this exception
1060                // handler is called for all exceptions. This is used to implement "finally"
1061                int catchTypeIndex = (e.catchType == null) ?
1062                        0 : constantPool.constantClassInfo(e.catchType);
1063                CLExceptionInfo c = new CLExceptionInfo(e.startPC, e.endPC, e.handlerPC,
1064                        catchTypeIndex);
1065                exceptionTable.add(c);
1066            }
1067
1068            // Convert Instruction objects to bytes.
1069            ArrayList<Integer> byteCode = new ArrayList<Integer>();
1070            int maxLocals = mArgumentCount;
1071            for (CLInstruction instr : mCode) {
1072                // Compute maxLocals.
1073                int localVariableIndex = instr.localVariableIndex();
1074                switch (instr.opcode()) {
1075                    case LLOAD:
1076                    case LSTORE:
1077                    case DSTORE:
1078                    case DLOAD:
1079                    case LLOAD_0:
1080                    case LLOAD_1:
1081                    case LLOAD_2:
1082                    case LLOAD_3:
1083                    case LSTORE_0:
1084                    case LSTORE_1:
1085                    case LSTORE_2:
1086                    case LSTORE_3:
1087                    case DLOAD_0:
1088                    case DLOAD_1:
1089                    case DLOAD_2:
1090                    case DLOAD_3:
1091                    case DSTORE_0:
1092                    case DSTORE_1:
1093                    case DSTORE_2:
1094                    case DSTORE_3:
1095                        // Each long and double occupies two slots in the local variable table.
1096                        localVariableIndex++;
1097                }
1098                maxLocals = Math.max(maxLocals, localVariableIndex + 1);
1099
1100                // Resolve jump labels in flow control instructions.
1101                if (instr instanceof CLFlowControlInstruction) {
1102                    if (!((CLFlowControlInstruction) instr).resolveLabels(mLabels)) {
1103                        reportEmitterError("%s: Unable to resolve jump label(s)", eCurrentMethod);
1104                    }
1105                }
1106
1107                byteCode.addAll(instr.toBytes());
1108            }
1109
1110            // Code attribute; add only if method is neither native nor abstract.
1111            if (!((mAccessFlags & ACC_NATIVE) == ACC_NATIVE ||
1112                    (mAccessFlags & ACC_ABSTRACT) == ACC_ABSTRACT)) {
1113                addMethodAttribute(codeAttribute(byteCode, exceptionTable, stackDepth(),
1114                        maxLocals));
1115            }
1116
1117            methods.add(new CLMethodInfo(mAccessFlags, mNameIndex, mDescriptorIndex,
1118                    mAttributes.size(), mAttributes));
1119        }
1120
1121        // This method could be the last method, so we need the following wrap up code:
1122
1123        // Add the InnerClass attribute if this class has inner classes.
1124        if (innerClasses.size() > 0) {
1125            addClassAttribute(innerClassesAttribute());
1126        }
1127
1128        // Set the members of the ClassFile structure to their appropriate values.
1129        clFile.constantPoolCount = constantPool.size() + 1;
1130        clFile.constantPool = constantPool;
1131        clFile.interfacesCount = interfaces.size();
1132        clFile.interfaces = interfaces;
1133        clFile.fieldsCount = fields.size();
1134        clFile.fields = fields;
1135        clFile.methodsCount = methods.size();
1136        clFile.methods = methods;
1137        clFile.attributesCount = attributes.size();
1138        clFile.attributes = attributes;
1139    }
1140
1141    // Adds a field.
1142    private void addFieldInfo(ArrayList<String> accessFlags, String name, String type,
1143                              boolean isSynthetic, int c) {
1144        if (!validTypeDescriptor(type)) {
1145            reportEmitterError("'%s' is not a valid type descriptor for field", type);
1146        }
1147        int flags = 0;
1148        int nameIndex = constantPool.constantUtf8Info(name);
1149        int descriptorIndex = constantPool.constantUtf8Info(type);
1150        fAttributes = new ArrayList<CLAttributeInfo>();
1151        if (accessFlags != null) {
1152            for (String s : accessFlags) {
1153                flags |= CLFile.accessFlagToInt(s);
1154            }
1155        }
1156        if (isSynthetic) {
1157            addFieldAttribute(syntheticAttribute());
1158        }
1159        if (c != -1) {
1160            addFieldAttribute(constantValueAttribute(c));
1161        }
1162        fields.add(new CLFieldInfo(flags, nameIndex, descriptorIndex, fAttributes.size(),
1163                fAttributes));
1164    }
1165
1166    // Returns the number of units a type with the specified descriptor produces or consumes from
1167    // the operand stack; 0 is returned if the specified descriptor is invalid.
1168    private int typeStackResidue(String descriptor) {
1169        int i = 0;
1170        char c = descriptor.charAt(0);
1171        switch (c) {
1172            case 'B':
1173            case 'C':
1174            case 'I':
1175            case 'F':
1176            case 'L':
1177            case 'S':
1178            case 'Z':
1179            case '[':
1180                i = 1;
1181                break;
1182            case 'J':
1183            case 'D':
1184                i = 2;
1185                break;
1186        }
1187        return i;
1188    }
1189
1190    // Returns the difference between the number of units consumed from the operand stack by a
1191    // method with the specified descriptor and the number of units produced by the method in the
1192    // operand stack; 0 is returned if the descriptor is invalid.
1193    private int methodStackResidue(String descriptor) {
1194        int i = 0;
1195
1196        // Extract types of arguments and the return type from the method descriptor.
1197        String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
1198        String returnType = descriptor.substring(descriptor.lastIndexOf(")") + 1);
1199
1200        // Units consumed.
1201        for (int j = 0; j < argTypes.length(); j++) {
1202            char c = argTypes.charAt(j);
1203            switch (c) {
1204                case 'B':
1205                case 'C':
1206                case 'I':
1207                case 'F':
1208                case 'S':
1209                case 'Z':
1210                    i -= 1;
1211                    break;
1212                case '[':
1213                    break;
1214                case 'J':
1215                case 'D':
1216                    i -= 2;
1217                    break;
1218                case 'L':
1219                    int k = argTypes.indexOf(";", j);
1220                    j = k;
1221                    i -= 1;
1222                    break;
1223            }
1224        }
1225
1226        // Units produced.
1227        i += typeStackResidue(returnType);
1228        return i;
1229    }
1230
1231    // Returns the argument count (number of formal parameters) for the specified method. 0 is
1232    // returned if the descriptor is invalid.
1233    private int argumentCount(String descriptor) {
1234        int i = 0;
1235
1236        // Extract types of arguments and the return type from the method descriptor.
1237        String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
1238
1239        // Find number of arguments,
1240        for (int j = 0; j < argTypes.length(); j++) {
1241            char c = argTypes.charAt(j);
1242            switch (c) {
1243                case 'B':
1244                case 'C':
1245                case 'I':
1246                case 'F':
1247                case 'S':
1248                case 'Z':
1249                    i += 1;
1250                    break;
1251                case '[':
1252                    break;
1253                case 'J':
1254                case 'D':
1255                    i += 2;
1256                    break;
1257                case 'L':
1258                    int k = argTypes.indexOf(";", j);
1259                    j = k;
1260                    i += 1;
1261                    break;
1262            }
1263        }
1264        return i;
1265    }
1266
1267    // Returns true if the specified name is in the internal form of a fully qualified class or
1268    // interface name, and false otherwise.
1269    private boolean validInternalForm(String name) {
1270        if ((name == null) || name.equals("") || name.startsWith("/") || name.endsWith("/")) {
1271            return false;
1272        }
1273        StringTokenizer t = new StringTokenizer(name, "/");
1274        while (t.hasMoreTokens()) {
1275            String s = t.nextToken();
1276            for (int i = 0; i < s.length(); i++) {
1277                if (i == 0) {
1278                    if (!Character.isJavaIdentifierStart(s.charAt(i))) {
1279                        return false;
1280                    }
1281                } else {
1282                    if (!Character.isJavaIdentifierPart(s.charAt(i))) {
1283                        return false;
1284                    }
1285                }
1286            }
1287        }
1288        return true;
1289    }
1290
1291    // Returns true if the specified string is a valid type descriptor, and false otherwise.
1292    private boolean validTypeDescriptor(String s) {
1293        if (s != null) {
1294            try {
1295                char c = s.charAt(0);
1296                switch (c) {
1297                    case 'B':
1298                    case 'C':
1299                    case 'I':
1300                    case 'F':
1301                    case 'S':
1302                    case 'Z':
1303                    case 'J':
1304                    case 'D':
1305                        return (s.length() == 1);
1306                    case 'L':
1307                        if (s.endsWith(";")) {
1308                            return validInternalForm(s.substring(1, s.length() - 1));
1309                        }
1310                        return false;
1311                    case '[':
1312                        return validTypeDescriptor(s.substring(1));
1313                }
1314            } catch (IndexOutOfBoundsException e) {
1315                return false;
1316            }
1317        }
1318        return false;
1319    }
1320
1321    // Returns true if the specified string is a valid method descriptor, and false otherwise.
1322    private boolean validMethodDescriptor(String s) {
1323        if ((s != null) && (s.length() > 0)) {
1324            try {
1325                // Extract types of arguments and the return type from the string.
1326                String argTypes = s.substring(1, s.lastIndexOf(")"));
1327                String returnType = s.substring(s.lastIndexOf(")") + 1);
1328
1329                // Validate argument type syntax.
1330                if (argTypes.endsWith("[")) {
1331                    return false;
1332                }
1333                for (int i = 0; i < argTypes.length(); i++) {
1334                    char c = argTypes.charAt(i);
1335                    switch (c) {
1336                        case 'B':
1337                        case 'C':
1338                        case 'I':
1339                        case 'F':
1340                        case 'S':
1341                        case 'Z':
1342                        case 'J':
1343                        case 'D':
1344                        case '[':
1345                            break;
1346                        case 'L':
1347                            int j = argTypes.indexOf(";", i);
1348                            String t = argTypes.substring(i, j + 1);
1349                            i = j;
1350                            if (!validTypeDescriptor(t)) {
1351                                return false;
1352                            }
1353                            break;
1354                        default:
1355                            return false;
1356                    }
1357                }
1358
1359                // Validate return type syntax.
1360                return (returnType.equals("V") || validTypeDescriptor(returnType));
1361            } catch (IndexOutOfBoundsException e) {
1362                return false;
1363            }
1364        }
1365        return false;
1366    }
1367
1368    // Returns the instruction with the specified pc within the code array of the current method
1369    // being added, or null.
1370    private CLInstruction instruction(int pc) {
1371        for (CLInstruction instruction : mCode) {
1372            if (instruction.pc() == pc) {
1373                return instruction;
1374            }
1375        }
1376        return null;
1377    }
1378
1379    // Returns the index of the instruction with the specified pc, within the code array of the
1380    // current method being added, or -1.
1381    private int instructionIndex(int pc) {
1382        for (int i = 0; i < mCode.size(); i++) {
1383            if (mCode.get(i).pc() == pc) {
1384                return i;
1385            }
1386        }
1387        return -1;
1388    }
1389
1390    // Computes and returns the maximum depth of the operand stack for the method last added.
1391    private int stackDepth() {
1392        CLBranchStack branchTargets = new CLBranchStack();
1393        for (CLException e : mExceptionHandlers) {
1394            CLInstruction h = instruction(e.handlerPC);
1395            if (h != null) {
1396                // 1 because the exception that is thrown is pushed on top of the operand stack.
1397                branchTargets.push(h, 1);
1398            }
1399        }
1400        int stackDepth = 0, maxStackDepth = 0, c = 0;
1401        CLInstruction instr = (mCode.size() == 0) ? null : mCode.get(c);
1402        while (instr != null) {
1403            int opcode = instr.opcode();
1404            int stackUnits = instr.stackUnits();
1405            if (stackUnits == EMPTY_STACK) {
1406                stackDepth = 0;
1407            } else if (stackUnits == UNIT_SIZE_STACK) {
1408                stackDepth = 1;
1409            } else {
1410                stackDepth += stackUnits;
1411            }
1412            if (stackDepth > maxStackDepth) {
1413                maxStackDepth = stackDepth;
1414            }
1415            if (instr instanceof CLFlowControlInstruction) {
1416                CLFlowControlInstruction b = (CLFlowControlInstruction) instr;
1417                int jumpToIndex = b.pc() + b.jumpToOffset();
1418                CLInstruction instrAt = null;
1419                switch (opcode) {
1420                    case JSR:
1421                    case JSR_W:
1422                    case RET:
1423                        instr = null;
1424                        break;
1425                    case GOTO:
1426                    case GOTO_W:
1427                        instr = null;
1428                    default:
1429                        instrAt = instruction(jumpToIndex);
1430                        if (instrAt != null) {
1431                            branchTargets.push(instrAt, stackDepth);
1432                        }
1433                }
1434            } else {
1435                if ((opcode == ATHROW) || ((opcode >= IRETURN) && (opcode <= RETURN))) {
1436                    instr = null;
1437                }
1438            }
1439            if (instr != null) {
1440                c++;
1441                instr = (c >= mCode.size()) ? null : mCode.get(c);
1442            }
1443            if (instr == null) {
1444                CLBranchTarget bt = branchTargets.pop();
1445                if (bt != null) {
1446                    instr = bt.target;
1447                    stackDepth = bt.stackDepth;
1448                    c = instructionIndex(instr.pc());
1449                }
1450            }
1451        }
1452        return maxStackDepth;
1453    }
1454
1455    // Adds LDC (LDC_W if index is wide) instruction.
1456    private void ldcInstruction(int index) {
1457        CLLoadStoreInstruction instr = index <= 255 ?
1458                new CLLoadStoreInstruction(LDC, mPC++, index) :
1459                new CLLoadStoreInstruction(LDC_W, mPC++, index);
1460        mPC += instr.operandCount();
1461        mCode.add(instr);
1462        mInstructionAfterLabel = true;
1463    }
1464
1465    // Adds LDC2_W instruction (used for long and double constants). Note that only a wide-index
1466    // version of LDC2_W instruction exists.
1467    private void ldc2wInstruction(int index) {
1468        CLLoadStoreInstruction instr = new CLLoadStoreInstruction(LDC2_W, mPC++, index);
1469        mPC += instr.operandCount();
1470        mCode.add(instr);
1471        mInstructionAfterLabel = true;
1472    }
1473
1474    // Constructs and returns a ConstantValue attribute given the constant value index.
1475    private CLConstantValueAttribute constantValueAttribute(int c) {
1476        int attributeNameIndex = constantPool.constantUtf8Info(ATT_CONSTANT_VALUE);
1477        return new CLConstantValueAttribute(attributeNameIndex, 2, c);
1478    }
1479
1480    // Constructs and returns a Code attribute given the list of bytes that make up the
1481    // instructions and their operands, exception table, maximum depth of operand stack, and
1482    // maximum number of local variables.
1483    private CLCodeAttribute codeAttribute(ArrayList<Integer> byteCode,
1484                                          ArrayList<CLExceptionInfo> exceptionTable, int stackDepth,
1485                                          int maxLocals) {
1486        int codeLength = byteCode.size();
1487        int attributeNameIndex = constantPool.constantUtf8Info(ATT_CODE);
1488        int attributeLength = codeLength + 8 * exceptionTable.size() + 12;
1489        for (CLAttributeInfo info : mCodeAttributes) {
1490            attributeLength += 6 + info.attributeLength;
1491        }
1492        return new CLCodeAttribute(attributeNameIndex, attributeLength, stackDepth, maxLocals,
1493                (long) codeLength, byteCode, exceptionTable.size(), exceptionTable,
1494                mCodeAttributes.size(), mCodeAttributes);
1495    }
1496
1497    // Constructs and returns an ExceptionsAttribute given the list of exceptions.
1498    private CLExceptionsAttribute exceptionsAttribute(ArrayList<String> exceptions) {
1499        int attributeNameIndex = constantPool.constantUtf8Info(ATT_EXCEPTIONS);
1500        ArrayList<Integer> exceptionIndexTable = new ArrayList<Integer>();
1501        for (String exception : exceptions) {
1502            exceptionIndexTable.add(constantPool.constantClassInfo(exception));
1503        }
1504        return new CLExceptionsAttribute(attributeNameIndex, exceptionIndexTable.size() * 2 + 2,
1505                exceptionIndexTable.size(), exceptionIndexTable);
1506    }
1507
1508    // Constructs and returns an InnerClasses attribute.
1509    private CLInnerClassesAttribute innerClassesAttribute() {
1510        int attributeNameIndex = constantPool.constantUtf8Info(ATT_INNER_CLASSES);
1511        long attributeLength = innerClasses.size() * 8 + 2;
1512        return new CLInnerClassesAttribute(attributeNameIndex, attributeLength,
1513                innerClasses.size(), innerClasses);
1514    }
1515
1516    // Constructs and returns a Synthetic attribute.
1517    private CLAttributeInfo syntheticAttribute() {
1518        int attributeNameIndex = constantPool.constantUtf8Info(ATT_SYNTHETIC);
1519        return new CLSyntheticAttribute(attributeNameIndex, 0);
1520    }
1521
1522    // Used to report an error if the opcode used for adding an instruction is invalid, or if an
1523    // incorrect method from CLEmitter is used to add the opcode.
1524    private void reportOpcodeError(int opcode) {
1525        if (!CLInstruction.isValid(opcode)) {
1526            reportEmitterError("%s: Invalid opcode '%d'", eCurrentMethod, opcode);
1527        } else {
1528            reportEmitterError("%s: Incorrect method used to add instruction '%s'",
1529                    eCurrentMethod, CLInstruction.instructionInfo[opcode].mnemonic);
1530        }
1531    }
1532
1533    // Used to report any error that occurs while creating/writing the class, to STDERR.
1534    private void reportEmitterError(String message, Object... args) {
1535        System.err.printf(message, args);
1536        System.err.println();
1537        errorHasOccurred = true;
1538    }
1539}
1540
1541/**
1542 * Representation of an exception handler.
1543 */
1544class CLException {
1545    // The exception handler is active from this instruction in the code section of the current
1546    // method being added to ...
1547    public String startLabel;
1548
1549    // this instruction. Formally, the handler is active while the program counter is within the
1550    // interval [startPC, endPC).
1551    public String endLabel;
1552
1553    // Instruction after this label is first instruction of the handler.
1554    public String handlerLabel;
1555
1556    // The class of exceptions that this exception handler is designated to catch.
1557    public String catchType;
1558
1559    // startLabel is resolved to this value.
1560    public int startPC;
1561
1562    // endLabel is resolved to this value.
1563    public int endPC;
1564
1565    // handlerLabel is resolved to this value.
1566    public int handlerPC;
1567
1568    /**
1569     * Constructs a CLException object.
1570     *
1571     * @param startLabel   the exception handler is active from the instruction following this
1572     *                     label in the code section of the current method being
1573     *                     added ...
1574     * @param endLabel     to the instruction following this label. Formally, the handler is
1575     *                     active while the program counter is within the interval [startLabel,
1576     *                     endLabel).
1577     * @param handlerLabel the handler begins with instruction following this label.
1578     * @param catchType    the exception type that this exception handler is designated to catch,
1579     *                     as a fully qualified name in internal form.
1580     */
1581    public CLException(String startLabel, String endLabel, String handlerLabel, String catchType) {
1582        this.startLabel = startLabel;
1583        this.endLabel = endLabel;
1584        this.handlerLabel = handlerLabel;
1585        this.catchType = catchType;
1586    }
1587
1588    /**
1589     * Resolves the jump labels to the corresponding pc values using the given label to pc
1590     * mapping. If unable to resolve a label, the corresponding pc is set to 0.
1591     *
1592     * @param labelToPC label to pc mapping.
1593     * @return true if all labels were resolved successfully, and false otherwise.
1594     */
1595    public boolean resolveLabels(Hashtable<String, Integer> labelToPC) {
1596        boolean allLabelsResolved = true;
1597        if (labelToPC.containsKey(startLabel)) {
1598            startPC = labelToPC.get(startLabel);
1599        } else {
1600            startPC = 0;
1601            allLabelsResolved = false;
1602        }
1603        if (labelToPC.containsKey(endLabel)) {
1604            endPC = labelToPC.get(endLabel);
1605        } else {
1606            endPC = 0;
1607            allLabelsResolved = false;
1608        }
1609        if (labelToPC.containsKey(handlerLabel)) {
1610            handlerPC = labelToPC.get(handlerLabel);
1611        } else {
1612            handlerPC = 0;
1613            allLabelsResolved = false;
1614        }
1615        return allLabelsResolved;
1616    }
1617}
1618
1619/**
1620 * Instances of this class form the elements of the CLBranchStack which is used for control flow
1621 * analysis to compute maximum depth of operand stack for a method.
1622 */
1623class CLBranchTarget {
1624    /**
1625     * Target instruction.
1626     */
1627    public CLInstruction target;
1628
1629    /**
1630     * Depth of stack before the target instruction is executed.
1631     */
1632    public int stackDepth;
1633
1634    /**
1635     * Constructs a CLBranchTarget object.
1636     *
1637     * @param target     the target instruction.
1638     * @param stackDepth depth of stack before the target instruction is executed.
1639     */
1640    public CLBranchTarget(CLInstruction target, int stackDepth) {
1641        this.target = target;
1642        this.stackDepth = stackDepth;
1643    }
1644}
1645
1646/**
1647 * This class is used for control flow analysis to compute maximum depth of operand stack for a
1648 * method.
1649 */
1650class CLBranchStack {
1651    // Branch targets yet to visit.
1652    private Stack<CLBranchTarget> branchTargets;
1653
1654    // Branch targets already visited.
1655    private Hashtable<CLInstruction, CLBranchTarget> visitedTargets;
1656
1657    /**
1658     * Constructs a CLBranchStack object.
1659     */
1660    public CLBranchStack() {
1661        this.branchTargets = new Stack<CLBranchTarget>();
1662        this.visitedTargets = new Hashtable<CLInstruction, CLBranchTarget>();
1663    }
1664
1665    /**
1666     * Pushes the specified information into the stack as a CLBranchTarget instance if the target
1667     * has not been visited yet.
1668     *
1669     * @param target     the target instruction.
1670     * @param stackDepth depth of stack before the target instruction is executed.
1671     */
1672    public void push(CLInstruction target, int stackDepth) {
1673        if (visited(target)) {
1674            return;
1675        }
1676        branchTargets.push(visit(target, stackDepth));
1677    }
1678
1679    /**
1680     * Pops and returns an element from the stack, or null.
1681     *
1682     * @return an element from the stack, or null.
1683     */
1684    public CLBranchTarget pop() {
1685        if (!branchTargets.empty()) {
1686            CLBranchTarget bt = (CLBranchTarget) branchTargets.pop();
1687            return bt;
1688        }
1689        return null;
1690    }
1691
1692    // Returns an instance of CLBranchTarget with the specified information and records the
1693    // target as visited.
1694    private CLBranchTarget visit(CLInstruction target, int stackDepth) {
1695        CLBranchTarget bt = new CLBranchTarget(target, stackDepth);
1696        visitedTargets.put(target, bt);
1697        return bt;
1698    }
1699
1700    // Returns true if the specified instruction has been visited, and false otherwise.
1701    private boolean visited(CLInstruction target) {
1702        return (visitedTargets.get(target) != null);
1703    }
1704}
1705
1706/**
1707 * A class loader to be able to load a class from a byte stream.
1708 */
1709class ByteClassLoader extends ClassLoader {
1710    // Bytes representing the class.
1711    private byte[] bytes;
1712
1713    // Has a package been defined for this class loader?
1714    private boolean pkgDefined = false;
1715
1716    /**
1717     * Sets the bytes representing the class.
1718     *
1719     * @param bytes bytes representing the class.
1720     */
1721    public void setClassBytes(byte[] bytes) {
1722        this.bytes = bytes;
1723    }
1724
1725    /**
1726     * Loads the class with the specified fully qualified name.
1727     *
1728     * @param name    the fully qualified name of the class.
1729     * @param resolve if true then resolve the class.
1730     * @throws ClassNotFoundException if the class could not be found.
1731     */
1732    public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
1733        Class cls = findLoadedClass(name);
1734        if (cls == null) {
1735            try {
1736                cls = findSystemClass(name);
1737            } catch (Exception e) {
1738                // Ignore these.
1739            } catch (NoClassDefFoundError e) {
1740                // If we get here we know the class exists, so change the name to its binary name
1741                // as defined in the Java Language Specifications.
1742                cls = findSystemClass(name.replace("/", "."));
1743            }
1744        }
1745        if (cls == null) {
1746            name = name.replace("/", ".");
1747            String pkg = name.lastIndexOf('.') == -1 ? "" : name.substring(0,
1748                    name.lastIndexOf('.'));
1749            if (!pkgDefined) {
1750                // Packages must be created before the class is defined, and package names must
1751                // be unique within a class loader and cannot be redefined or changed once created.
1752                definePackage(pkg, "", "", "", "", "", "", null);
1753                pkgDefined = true;
1754            }
1755            cls = defineClass(name, bytes, 0, bytes.length);
1756            if (resolve && cls != null) {
1757                resolveClass(cls);
1758            }
1759        }
1760        return cls;
1761    }
1762}
1763
1764/**
1765 * Inherits from java.out.DataOutputStream and provides an extra function for writing unsigned
1766 * int to the output stream, which is required for writing Java class files.
1767 */
1768class CLOutputStream extends DataOutputStream {
1769    /**
1770     * Constructs a CLOutputStream from the specified output stream.
1771     *
1772     * @param out output stream.
1773     */
1774    public CLOutputStream(OutputStream out) {
1775        super(out);
1776    }
1777
1778    /**
1779     * Writes four bytes to the output stream to represent the value of the argument. The byte
1780     * values to be written, in the order shown, are:
1781     *
1782     * <pre>
1783     *     (byte) ( 0xFF &amp; ( v &gt;&gt; 24 ) )
1784     *     (byte) ( 0xFF &amp; ( v &gt;&gt; 16 ) )
1785     *     (byte) ( 0xFF &amp; ( v &gt;&gt; 8 ) )
1786     *     (byte) ( 0xFF &amp; v )
1787     * </pre>
1788     *
1789     * @param v the int value to be written.
1790     * @throws IOException if an error occurs while writing.
1791     */
1792    public final void writeInt(long v) throws IOException {
1793        long mask = 0xFF;
1794        out.write((byte) (mask & (v >> 24)));
1795        out.write((byte) (mask & (v >> 16)));
1796        out.write((byte) (mask & (v >> 8)));
1797        out.write((byte) (mask & v));
1798    }
1799}
1800