| CLEmitter.java |
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 & ( v >> 24 ) )
1784 * (byte) ( 0xFF & ( v >> 16 ) )
1785 * (byte) ( 0xFF & ( v >> 8 ) )
1786 * (byte) ( 0xFF & 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