1
3 package jminusminus;
4
5 import java.io.BufferedOutputStream;
6 import java.io.ByteArrayOutputStream;
7 import java.io.File;
8 import java.io.FileNotFoundException;
9 import java.io.FileOutputStream;
10 import java.io.IOException;
11 import java.io.DataOutputStream;
12 import java.io.OutputStream;
13 import java.util.ArrayList;
14 import java.util.Hashtable;
15 import java.util.Stack;
16 import java.util.StringTokenizer;
17 import java.util.TreeMap;
18 import static jminusminus.CLConstants.*;
19 import static jminusminus.CLConstants.Category.*;
20
21
31
32 public class CLEmitter {
33
34
35 private String name;
36
37
41 private boolean toFile;
42
43
44 private String destDir;
45
46
47 private CLFile clFile;
48
49
50 private CLConstantPool constantPool;
51
52
53 private ArrayList<Integer> interfaces;
54
55
56 private ArrayList<CLFieldInfo> fields;
57
58
59 private ArrayList<CLAttributeInfo> fAttributes;
60
61
62 private ArrayList<CLMethodInfo> methods;
63
64
65 private ArrayList<CLAttributeInfo> mAttributes;
66
67
68 private ArrayList<CLAttributeInfo> attributes;
69
70
71 private ArrayList<CLInnerClassInfo> innerClasses;
72
73
74 private ArrayList<CLInstruction> mCode;
75
76
79 private ArrayList<CLException> mExceptionHandlers;
80
81
82 private int mAccessFlags;
83
84
88 private int mNameIndex;
89
90
94 private int mDescriptorIndex;
95
96
97 private int mArgumentCount;
98
99
100 private ArrayList<CLAttributeInfo> mCodeAttributes;
101
102
103 private boolean isMethodOpen;
104
105
111 private Hashtable<String, Integer> mLabels;
112
113
114 private int mLabelCount;
115
116
124 private boolean mInstructionAfterLabel = false;
125
126
130 private int mPC;
131
132
133 private String eCurrentMethod;
134
135
138 private boolean errorHasOccurred;
139
140
144 private static ByteClassLoader byteClassLoader;
145
146
150
151 private void initializeMethodVariables() {
152 mAccessFlags = 0;
153 mNameIndex = -1;
154 mDescriptorIndex = -1;
155 mArgumentCount = 0;
156 mPC = 0;
157 mAttributes = new ArrayList<CLAttributeInfo>();
158 mExceptionHandlers = new ArrayList<CLException>();
159 mCode = new ArrayList<CLInstruction>();
160 mCodeAttributes = new ArrayList<CLAttributeInfo>();
161 mLabels = new Hashtable<String, Integer>();
162 mLabelCount = 1;
163 mInstructionAfterLabel = false;
164 }
165
166
171
172 private void endOpenMethodIfAny() {
173 if (isMethodOpen) {
174 isMethodOpen = false;
175 if (!mInstructionAfterLabel) {
176 addNoArgInstruction(NOP);
178 }
179
180 ArrayList<CLExceptionInfo> exceptionTable = new ArrayList<CLExceptionInfo>();
182 for (int i = 0; i < mExceptionHandlers.size(); i++) {
183 CLException e = mExceptionHandlers.get(i);
184 if (!e.resolveLabels(mLabels)) {
185 reportEmitterError(
186 "%s: Unable to resolve exception handler "
187 + "label(s)", eCurrentMethod);
188 }
189
190 int catchTypeIndex = (e.catchType == null) ? 0 : constantPool
197 .constantClassInfo(e.catchType);
198 CLExceptionInfo c = new CLExceptionInfo(e.startPC, e.endPC,
199 e.handlerPC, catchTypeIndex);
200 exceptionTable.add(c);
201 }
202
203 ArrayList<Integer> byteCode = new ArrayList<Integer>();
205 int maxLocals = mArgumentCount;
206 for (int i = 0; i < mCode.size(); i++) {
207 CLInstruction instr = mCode.get(i);
208
209 int localVariableIndex = instr.localVariableIndex();
211 switch (instr.opcode()) {
212 case LLOAD:
213 case LSTORE:
214 case DSTORE:
215 case DLOAD:
216 case LLOAD_0:
217 case LLOAD_1:
218 case LLOAD_2:
219 case LLOAD_3:
220 case LSTORE_0:
221 case LSTORE_1:
222 case LSTORE_2:
223 case LSTORE_3:
224 case DLOAD_0:
225 case DLOAD_1:
226 case DLOAD_2:
227 case DLOAD_3:
228 case DSTORE_0:
229 case DSTORE_1:
230 case DSTORE_2:
231 case DSTORE_3:
232 localVariableIndex++;
236 }
237 maxLocals = Math.max(maxLocals, localVariableIndex + 1);
238
239 if (instr instanceof CLFlowControlInstruction) {
242 if (!((CLFlowControlInstruction) instr)
243 .resolveLabels(mLabels)) {
244 reportEmitterError(
245 "%s: Unable to resolve jump label(s)",
246 eCurrentMethod);
247 }
248 }
249
250 byteCode.addAll(instr.toBytes());
251 }
252
253 if (!((mAccessFlags & ACC_NATIVE) == ACC_NATIVE || (mAccessFlags & ACC_ABSTRACT) == ACC_ABSTRACT)) {
257 addMethodAttribute(codeAttribute(byteCode, exceptionTable,
258 stackDepth(), maxLocals));
259 }
260
261 methods.add(new CLMethodInfo(mAccessFlags, mNameIndex,
262 mDescriptorIndex, mAttributes.size(), mAttributes));
263 }
264
265
268 if (innerClasses.size() > 0) {
271 addClassAttribute(innerClassesAttribute());
272 }
273
274 clFile.constantPoolCount = constantPool.size() + 1;
277 clFile.constantPool = constantPool;
278 clFile.interfacesCount = interfaces.size();
279 clFile.interfaces = interfaces;
280 clFile.fieldsCount = fields.size();
281 clFile.fields = fields;
282 clFile.methodsCount = methods.size();
283 clFile.methods = methods;
284 clFile.attributesCount = attributes.size();
285 clFile.attributes = attributes;
286 }
287
288
305
306 private void addFieldInfo(ArrayList<String> accessFlags, String name,
307 String type, boolean isSynthetic, int c) {
308 if (!validTypeDescriptor(type)) {
309 reportEmitterError("'%s' is not a valid type descriptor for field",
310 type);
311 }
312 int flags = 0;
313 int nameIndex = constantPool.constantUtf8Info(name);
314 int descriptorIndex = constantPool.constantUtf8Info(type);
315 fAttributes = new ArrayList<CLAttributeInfo>();
316 if (accessFlags != null) {
317 for (int i = 0; i < accessFlags.size(); i++) {
318 flags |= CLFile.accessFlagToInt(accessFlags.get(i));
319 }
320 }
321 if (isSynthetic) {
322 addFieldAttribute(syntheticAttribute());
323 }
324 if (c != -1) {
325 addFieldAttribute(constantValueAttribute(c));
326 }
327 fields.add(new CLFieldInfo(flags, nameIndex, descriptorIndex,
328 fAttributes.size(), fAttributes));
329 }
330
331
341
342 private int typeStackResidue(String descriptor) {
343 int i = 0;
344 char c = descriptor.charAt(0);
345 switch (c) {
346 case 'B':
347 case 'C':
348 case 'I':
349 case 'F':
350 case 'L':
351 case 'S':
352 case 'Z':
353 case '[':
354 i = 1;
355 break;
356 case 'J':
357 case 'D':
358 i = 2;
359 break;
360 }
361 return i;
362 }
363
364
375
376 private int methodStackResidue(String descriptor) {
377 int i = 0;
378
379 String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
382 String returnType = descriptor
383 .substring(descriptor.lastIndexOf(")") + 1);
384
385 for (int j = 0; j < argTypes.length(); j++) {
387 char c = argTypes.charAt(j);
388 switch (c) {
389 case 'B':
390 case 'C':
391 case 'I':
392 case 'F':
393 case 'S':
394 case 'Z':
395 i -= 1;
396 break;
397 case '[':
398 break;
399 case 'J':
400 case 'D':
401 i -= 2;
402 break;
403 case 'L':
404 int k = argTypes.indexOf(";", j);
405 j = k;
406 i -= 1;
407 break;
408 }
409 }
410
411 i += typeStackResidue(returnType);
413 return i;
414 }
415
416
424
425 private int argumentCount(String descriptor) {
426 int i = 0;
427
428 String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
431
432 for (int j = 0; j < argTypes.length(); j++) {
434 char c = argTypes.charAt(j);
435 switch (c) {
436 case 'B':
437 case 'C':
438 case 'I':
439 case 'F':
440 case 'S':
441 case 'Z':
442 i += 1;
443 break;
444 case '[':
445 break;
446 case 'J':
447 case 'D':
448 i += 2;
449 break;
450 case 'L':
451 int k = argTypes.indexOf(";", j);
452 j = k;
453 i += 1;
454 break;
455 }
456 }
457 return i;
458 }
459
460
469
470 private boolean validInternalForm(String name) {
471 if ((name == null) || name.equals("") || name.startsWith("/")
472 || name.endsWith("/")) {
473 return false;
474 }
475 StringTokenizer t = new StringTokenizer(name, "/");
476 while (t.hasMoreTokens()) {
477 String s = t.nextToken();
478 for (int i = 0; i < s.length(); i++) {
479 if (i == 0) {
480 if (!Character.isJavaIdentifierStart(s.charAt(i))) {
481 return false;
482 }
483 } else {
484 if (!Character.isJavaIdentifierPart(s.charAt(i))) {
485 return false;
486 }
487 }
488 }
489 }
490 return true;
491 }
492
493
502
503 private boolean validTypeDescriptor(String descriptor) {
504 if (descriptor != null) {
505 try {
506 char c = descriptor.charAt(0);
507 switch (c) {
508 case 'B':
509 case 'C':
510 case 'I':
511 case 'F':
512 case 'S':
513 case 'Z':
514 case 'J':
515 case 'D':
516 return (descriptor.length() == 1);
517 case 'L':
518 if (descriptor.endsWith(";")) {
519 return validInternalForm(descriptor.substring(1,
520 descriptor.length() - 1));
521 }
522 return false;
523 case '[':
524 return validTypeDescriptor(descriptor.substring(1));
525 }
526 } catch (IndexOutOfBoundsException e) {
527 return false;
528 }
529 }
530 return false;
531 }
532
533
542
543 private boolean validMethodDescriptor(String descriptor) {
544 if ((descriptor != null) && (descriptor.length() > 0)) {
545 try {
546 String argTypes = descriptor.substring(1, descriptor
550 .lastIndexOf(")"));
551 String returnType = descriptor.substring(descriptor
552 .lastIndexOf(")") + 1);
553
554 if (argTypes.endsWith("[")) {
556 return false;
557 }
558 for (int i = 0; i < argTypes.length(); i++) {
559 char c = argTypes.charAt(i);
560 switch (c) {
561 case 'B':
562 case 'C':
563 case 'I':
564 case 'F':
565 case 'S':
566 case 'Z':
567 case 'J':
568 case 'D':
569 case '[':
570 break;
571 case 'L':
572 int j = argTypes.indexOf(";", i);
573 String s = argTypes.substring(i, j + 1);
574 i = j;
575 if (!validTypeDescriptor(s)) {
576 return false;
577 }
578 break;
579 default:
580 return false;
581 }
582 }
583
584 return (returnType.equals("V") || validTypeDescriptor(returnType));
586 } catch (IndexOutOfBoundsException e) {
587 return false;
588 }
589 }
590 return false;
591 }
592
593
601
602 private CLInstruction instruction(int pc) {
603 for (int j = 0; j < mCode.size(); j++) {
604 CLInstruction i = mCode.get(j);
605 if (i.pc() == pc) {
606 return i;
607 }
608 }
609 return null;
610 }
611
612
620
621 private int instructionIndex(int pc) {
622 int j = 0;
623 for (; j < mCode.size(); j++) {
624 CLInstruction i = mCode.get(j);
625 if (i.pc() == pc) {
626 return j;
627 }
628 }
629 return j;
630 }
631
632
638
639 private int stackDepth() {
640 CLBranchStack branchTargets = new CLBranchStack();
641 for (int i = 0; i < mExceptionHandlers.size(); i++) {
642 CLException e = mExceptionHandlers.get(i);
643 CLInstruction h = instruction(e.handlerPC);
644 if (h != null) {
645 branchTargets.push(h, 1);
649 }
650 }
651 int stackDepth = 0, maxStackDepth = 0, c = 0;
652 CLInstruction instr = (mCode.size() == 0) ? null : mCode.get(c);
653 while (instr != null) {
654 int opcode = instr.opcode();
655 int stackUnits = instr.stackUnits();
656 if (stackUnits == EMPTY_STACK) {
657 stackDepth = 0;
658 } else if (stackUnits == UNIT_SIZE_STACK) {
659 stackDepth = 1;
660 } else {
661 stackDepth += stackUnits;
662 }
663 if (stackDepth > maxStackDepth) {
664 maxStackDepth = stackDepth;
665 }
666
667
672 if (instr instanceof CLFlowControlInstruction) {
673 CLFlowControlInstruction b = (CLFlowControlInstruction) instr;
674 int jumpToIndex = b.pc() + b.jumpToOffset();
675 CLInstruction instrAt = null;
676 switch (opcode) {
677 case JSR:
678 case JSR_W:
679 case RET:
680 instr = null;
681 break;
682 case GOTO:
683 case GOTO_W:
684 instr = null;
685 default:
686 instrAt = instruction(jumpToIndex);
687 if (instrAt != null) {
688 branchTargets.push(instrAt, stackDepth);
689 }
690 }
691 } else {
692 if ((opcode == ATHROW)
693 || ((opcode >= IRETURN) && (opcode <= RETURN))) {
694 instr = null;
695 }
696 }
697 if (instr != null) {
698 c++;
699 instr = (c >= mCode.size()) ? null : mCode.get(c);
700 }
701 if (instr == null) {
702 CLBranchTarget bt = branchTargets.pop();
703 if (bt != null) {
704 instr = bt.target;
705 stackDepth = bt.stackDepth;
706 c = instructionIndex(instr.pc());
707 }
708 }
709 }
710 return maxStackDepth;
711 }
712
713
722
723 private void ldcInstruction(int index) {
724 CLLoadStoreInstruction instr = null;
725 if (index <= 255) {
726 instr = new CLLoadStoreInstruction(LDC, mPC++, index);
727 } else {
728 instr = new CLLoadStoreInstruction(LDC_W, mPC++, index);
729 }
730 mPC += instr.operandCount();
731 mCode.add(instr);
732 mInstructionAfterLabel = true;
733 }
734
735
743
744 private void ldc2wInstruction(int index) {
745 CLLoadStoreInstruction instr = new CLLoadStoreInstruction(LDC2_W,
746 mPC++, index);
747 mPC += instr.operandCount();
748 mCode.add(instr);
749 mInstructionAfterLabel = true;
750 }
751
752
761
762 private CLConstantValueAttribute constantValueAttribute(int c) {
763 int attributeNameIndex = constantPool
764 .constantUtf8Info(ATT_CONSTANT_VALUE);
765 return new CLConstantValueAttribute(attributeNameIndex, 2, c);
766 }
767
768
784
785 private CLCodeAttribute codeAttribute(ArrayList<Integer> byteCode,
786 ArrayList<CLExceptionInfo> exceptionTable, int stackDepth,
787 int maxLocals) {
788 int codeLength = byteCode.size();
789 int attributeNameIndex = constantPool.constantUtf8Info(ATT_CODE);
790 int attributeLength = codeLength + 8 * exceptionTable.size() + 12;
791 for (int i = 0; i < mCodeAttributes.size(); i++) {
792 attributeLength += 6 + mCodeAttributes.get(i).attributeLength;
793 }
794 return new CLCodeAttribute(attributeNameIndex, attributeLength,
795 stackDepth, maxLocals, (long) codeLength, byteCode,
796 exceptionTable.size(), exceptionTable, mCodeAttributes.size(),
797 mCodeAttributes);
798 }
799
800
807
808 private CLExceptionsAttribute exceptionsAttribute(
809 ArrayList<String> exceptions) {
810 int attributeNameIndex = constantPool.constantUtf8Info(ATT_EXCEPTIONS);
811 ArrayList<Integer> exceptionIndexTable = new ArrayList<Integer>();
812 for (int i = 0; i < exceptions.size(); i++) {
813 String e = exceptions.get(i);
814 exceptionIndexTable.add(new Integer(constantPool
815 .constantClassInfo(e)));
816 }
817 return new CLExceptionsAttribute(attributeNameIndex,
818 exceptionIndexTable.size() * 2 + 2, exceptionIndexTable.size(),
819 exceptionIndexTable);
820 }
821
822
827
828 private CLInnerClassesAttribute innerClassesAttribute() {
829 int attributeNameIndex = constantPool
830 .constantUtf8Info(ATT_INNER_CLASSES);
831 long attributeLength = innerClasses.size() * 8 + 2;
832 return new CLInnerClassesAttribute(attributeNameIndex, attributeLength,
833 innerClasses.size(), innerClasses);
834 }
835
836
842
843 private CLAttributeInfo syntheticAttribute() {
844 int attributeNameIndex = constantPool.constantUtf8Info(ATT_SYNTHETIC);
845 return new CLSyntheticAttribute(attributeNameIndex, 0);
846 }
847
848
856
857 private void reportOpcodeError(int opcode) {
858 if (!CLInstruction.isValid(opcode)) {
859 reportEmitterError("%s: Invalid opcode '%d'", eCurrentMethod,
860 opcode);
861 } else {
862 reportEmitterError(
863 "%s: Incorrect method used to add instruction '%s'",
864 eCurrentMethod,
865 CLInstruction.instructionInfo[opcode].mnemonic);
866 }
867 }
868
869
877
878 private void reportEmitterError(String message, Object... args) {
879 System.err.printf(message, args);
880 System.err.println();
881 errorHasOccurred = true;
882 }
883
884
888
896
897 public CLEmitter(boolean toFile) {
898 destDir = ".";
899 this.toFile = toFile;
900 }
901
902
908
909 public void destinationDir(String destDir) {
910 this.destDir = destDir;
911 }
912
913
918
919 public boolean errorHasOccurred() {
920 return errorHasOccurred;
921 }
922
923
941
942 public void addClass(ArrayList<String> accessFlags, String thisClass,
943 String superClass, ArrayList<String> superInterfaces,
944 boolean isSynthetic) {
945 clFile = new CLFile();
946 constantPool = new CLConstantPool();
947 interfaces = new ArrayList<Integer>();
948 fields = new ArrayList<CLFieldInfo>();
949 methods = new ArrayList<CLMethodInfo>();
950 attributes = new ArrayList<CLAttributeInfo>();
951 innerClasses = new ArrayList<CLInnerClassInfo>();
952 errorHasOccurred = false;
953 clFile.magic = MAGIC;
954 clFile.majorVersion = MAJOR_VERSION;
955 clFile.minorVersion = MINOR_VERSION;
956 if (!validInternalForm(thisClass)) {
957 reportEmitterError("'%s' is not in internal form", thisClass);
958 }
959 if (!validInternalForm(superClass)) {
960 reportEmitterError("'%s' is not in internal form", superClass);
961 }
962 if (accessFlags != null) {
963 for (int i = 0; i < accessFlags.size(); i++) {
964 clFile.accessFlags |= CLFile
965 .accessFlagToInt(accessFlags.get(i));
966 }
967 }
968 name = thisClass;
969 clFile.thisClass = constantPool.constantClassInfo(thisClass);
970 clFile.superClass = constantPool.constantClassInfo(superClass);
971 for (int i = 0; superInterfaces != null && i < superInterfaces.size(); i++) {
972 if (!validInternalForm(superInterfaces.get(i))) {
973 reportEmitterError("'%s' is not in internal form",
974 superInterfaces.get(i));
975 }
976 interfaces.add(new Integer(constantPool
977 .constantClassInfo(superInterfaces.get(i))));
978 }
979 if (isSynthetic) {
980 addClassAttribute(syntheticAttribute());
981 }
982 }
983
984
997
998 public void addInnerClass(ArrayList<String> accessFlags, String innerClass,
999 String outerClass, String innerName) {
1000 int flags = 0;
1001 if (accessFlags != null) {
1002 for (int j = 0; j < accessFlags.size(); j++) {
1003 flags |= CLFile.accessFlagToInt(accessFlags.get(j));
1004 }
1005 }
1006 CLInnerClassInfo innerClassInfo = new CLInnerClassInfo(constantPool
1007 .constantClassInfo(innerClass), constantPool
1008 .constantClassInfo(outerClass), constantPool
1009 .constantUtf8Info(innerName), flags);
1010 innerClasses.add(innerClassInfo);
1011 }
1012
1013
1025
1026 public void addField(ArrayList<String> accessFlags, String name,
1027 String type, boolean isSynthetic) {
1028 addFieldInfo(accessFlags, name, type, isSynthetic, -1);
1029 }
1030
1031
1048
1049 public void addField(ArrayList<String> accessFlags, String name,
1050 String type, boolean isSynthetic, int i) {
1051 addFieldInfo(accessFlags, name, type, isSynthetic, constantPool
1052 .constantIntegerInfo(i));
1053 }
1054
1055
1068
1069 public void addField(ArrayList<String> accessFlags, String name,
1070 boolean isSynthetic, float f) {
1071 addFieldInfo(accessFlags, name, "F", isSynthetic, constantPool
1072 .constantFloatInfo(f));
1073 }
1074
1075
1088
1089 public void addField(ArrayList<String> accessFlags, String name,
1090 boolean isSynthetic, long l) {
1091 addFieldInfo(accessFlags, name, "J", isSynthetic, constantPool
1092 .constantLongInfo(l));
1093 }
1094
1095
1108
1109 public void addField(ArrayList<String> accessFlags, String name,
1110 boolean isSynthetic, double d) {
1111 addFieldInfo(accessFlags, name, "D", isSynthetic, constantPool
1112 .constantDoubleInfo(d));
1113 }
1114
1115
1128
1129 public void addField(ArrayList<String> accessFlags, String name,
1130 boolean isSynthetic, String s) {
1131 addFieldInfo(accessFlags, name, "Ljava/lang/String;", isSynthetic,
1132 constantPool.constantStringInfo(s));
1133 }
1134
1135
1152
1153 public void addMethod(ArrayList<String> accessFlags, String name,
1154 String descriptor, ArrayList<String> exceptions, boolean isSynthetic) {
1155 if (!validMethodDescriptor(descriptor)) {
1156 reportEmitterError(
1157 "'%s' is not a valid type descriptor for method",
1158 descriptor);
1159 }
1160 endOpenMethodIfAny(); isMethodOpen = true;
1162 initializeMethodVariables();
1163 eCurrentMethod = name + descriptor;
1164 if (accessFlags != null) {
1165 for (int i = 0; i < accessFlags.size(); i++) {
1166 mAccessFlags |= CLFile.accessFlagToInt(accessFlags.get(i));
1167 }
1168 }
1169 mArgumentCount = argumentCount(descriptor)
1170 + (accessFlags.contains("static") ? 0 : 1);
1171 mNameIndex = constantPool.constantUtf8Info(name);
1172 mDescriptorIndex = constantPool.constantUtf8Info(descriptor);
1173 if (exceptions != null && exceptions.size() > 0) {
1174 addMethodAttribute(exceptionsAttribute(exceptions));
1175 }
1176 if (isSynthetic) {
1177 addMethodAttribute(syntheticAttribute());
1178 }
1179 }
1180
1181
1200
1201 public void addExceptionHandler(String startLabel, String endLabel,
1202 String handlerLabel, String catchType) {
1203 if (catchType != null && !validInternalForm(catchType)) {
1204 reportEmitterError("'%s' is not in internal form", catchType);
1205 }
1206 CLException e = new CLException(startLabel, endLabel, handlerLabel,
1207 catchType);
1208 mExceptionHandlers.add(e);
1209 }
1210
1211
1298
1299 public void addNoArgInstruction(int opcode) {
1300 CLInstruction instr = null;
1301 switch (CLInstruction.instructionInfo[opcode].category) {
1302 case ARITHMETIC1:
1303 instr = new CLArithmeticInstruction(opcode, mPC++);
1304 break;
1305 case ARRAY2:
1306 instr = new CLArrayInstruction(opcode, mPC++);
1307 break;
1308 case BIT:
1309 instr = new CLBitInstruction(opcode, mPC++);
1310 break;
1311 case COMPARISON:
1312 instr = new CLComparisonInstruction(opcode, mPC++);
1313 break;
1314 case CONVERSION:
1315 instr = new CLConversionInstruction(opcode, mPC++);
1316 break;
1317 case LOAD_STORE1:
1318 instr = new CLLoadStoreInstruction(opcode, mPC++);
1319 break;
1320 case METHOD2:
1321 instr = new CLMethodInstruction(opcode, mPC++);
1322 break;
1323 case MISC:
1324 instr = new CLMiscInstruction(opcode, mPC++);
1325 break;
1326 case STACK:
1327 instr = new CLStackInstruction(opcode, mPC++);
1328 break;
1329 default:
1330 reportOpcodeError(opcode);
1331 }
1332 if (instr != null) {
1333 mPC += instr.operandCount();
1334 mCode.add(instr);
1335 mInstructionAfterLabel = true;
1336 }
1337 }
1338
1339
1369
1370 public void addOneArgInstruction(int opcode, int arg) {
1371 CLInstruction instr = null;
1372 boolean isWidened = false;
1373 switch (CLInstruction.instructionInfo[opcode].category) {
1374 case LOAD_STORE2:
1375 isWidened = arg > 255;
1376 if (isWidened) {
1377 CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(
1378 WIDE, mPC++);
1379 mCode.add(wideInstr);
1380 }
1381 instr = new CLLoadStoreInstruction(opcode, mPC++, arg, isWidened);
1382 break;
1383 case LOAD_STORE3:
1384 instr = new CLLoadStoreInstruction(opcode, mPC++, arg);
1385 break;
1386 case FLOW_CONTROL2:
1387 isWidened = arg > 255;
1388 if (isWidened) {
1389 CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(
1390 WIDE, mPC++);
1391 mCode.add(wideInstr);
1392 }
1393 instr = new CLFlowControlInstruction(mPC++, arg, isWidened);
1394 break;
1395 default:
1396 reportOpcodeError(opcode);
1397 }
1398 if (instr != null) {
1399 mPC += instr.operandCount();
1400 mCode.add(instr);
1401 mInstructionAfterLabel = true;
1402 }
1403 }
1404
1405
1415
1416 public void addIINCInstruction(int index, int constVal) {
1417 boolean isWidened = index > 255 || constVal < Byte.MIN_VALUE
1418 || constVal > Byte.MAX_VALUE;
1419 if (isWidened) {
1420 CLLoadStoreInstruction wideInstr = new CLLoadStoreInstruction(WIDE,
1421 mPC++);
1422 mCode.add(wideInstr);
1423 }
1424 CLArithmeticInstruction instr = new CLArithmeticInstruction(IINC,
1425 mPC++, index, constVal, isWidened);
1426 mPC += instr.operandCount();
1427 mCode.add(instr);
1428 mInstructionAfterLabel = true;
1429 }
1430
1431
1462
1463 public void addMemberAccessInstruction(int opcode, String target,
1464 String name, String type) {
1465 if (!validInternalForm(target)) {
1466 reportEmitterError("%s: '%s' is not in internal form",
1467 eCurrentMethod, target);
1468 }
1469 CLInstruction instr = null;
1470 int index, stackUnits;
1471 switch (CLInstruction.instructionInfo[opcode].category) {
1472 case FIELD:
1473 if (!validTypeDescriptor(type)) {
1474 reportEmitterError(
1475 "%s: '%s' is not a valid type descriptor for field",
1476 eCurrentMethod, type);
1477 }
1478 index = constantPool.constantFieldRefInfo(target, name, type);
1479 stackUnits = typeStackResidue(type);
1480 if ((opcode == GETFIELD) || (opcode == PUTFIELD)) {
1481 stackUnits--;
1484 }
1485 instr = new CLFieldInstruction(opcode, mPC++, index, stackUnits);
1486 break;
1487 case METHOD1:
1488 if (!validMethodDescriptor(type)) {
1489 reportEmitterError(
1490 "%s: '%s' is not a valid type descriptor for "
1491 + "method", eCurrentMethod, type);
1492 }
1493 if (opcode == INVOKEINTERFACE) {
1494 index = constantPool.constantInterfaceMethodRefInfo(target,
1495 name, type);
1496 } else {
1497 index = constantPool.constantMethodRefInfo(target, name, type);
1498 }
1499 stackUnits = methodStackResidue(type);
1500 if (opcode != INVOKESTATIC) {
1501 stackUnits--;
1504 }
1505 instr = new CLMethodInstruction(opcode, mPC++, index, stackUnits);
1506
1507 if (opcode == INVOKEINTERFACE) {
1510 ((CLMethodInstruction) instr)
1512 .setArgumentCount(argumentCount(type) + 1);
1513 }
1514 break;
1515 default:
1516 reportOpcodeError(opcode);
1517 }
1518 if (instr != null) {
1519 mPC += instr.operandCount();
1520 mCode.add(instr);
1521 }
1522 }
1523
1524
1539
1540 public void addReferenceInstruction(int opcode, String type) {
1541 if (!validTypeDescriptor(type) && !validInternalForm(type)) {
1542 reportEmitterError("%s: '%s' is neither a type descriptor nor in "
1543 + "internal form", eCurrentMethod, type);
1544 }
1545 CLInstruction instr = null;
1546 switch (CLInstruction.instructionInfo[opcode].category) {
1547 case OBJECT:
1548 int index = constantPool.constantClassInfo(type);
1549 instr = new CLObjectInstruction(opcode, mPC++, index);
1550 break;
1551 default:
1552 reportOpcodeError(opcode);
1553 }
1554 if (instr != null) {
1555 mPC += instr.operandCount();
1556 mCode.add(instr);
1557 }
1558 }
1559
1560
1579
1580 public void addArrayInstruction(int opcode, String type) {
1581 CLInstruction instr = null;
1582 switch (CLInstruction.instructionInfo[opcode].category) {
1583 case ARRAY1:
1584 int index = 0;
1585 if (opcode == NEWARRAY) {
1586 if (type.equalsIgnoreCase("Z")) {
1587 index = 4;
1588 } else if (type.equalsIgnoreCase("C")) {
1589 index = 5;
1590 } else if (type.equalsIgnoreCase("F")) {
1591 index = 6;
1592 } else if (type.equalsIgnoreCase("D")) {
1593 index = 7;
1594 } else if (type.equalsIgnoreCase("B")) {
1595 index = 8;
1596 } else if (type.equalsIgnoreCase("S")) {
1597 index = 9;
1598 } else if (type.equalsIgnoreCase("I")) {
1599 index = 10;
1600 } else if (type.equalsIgnoreCase("J")) {
1601 index = 11;
1602 } else {
1603 reportEmitterError(
1604 "%s: '%s' is not a valid primitive type",
1605 eCurrentMethod, type);
1606 }
1607 } else {
1608 if (!validTypeDescriptor(type) && !validInternalForm(type)) {
1609 reportEmitterError(
1610 "%s: '%s' is not a valid type descriptor "
1611 + "for an array", eCurrentMethod, type);
1612 }
1613 index = constantPool.constantClassInfo(type);
1614 }
1615 instr = new CLArrayInstruction(opcode, mPC++, index);
1616 break;
1617 default:
1618 reportOpcodeError(opcode);
1619 }
1620 if (instr != null) {
1621 mPC += instr.operandCount();
1622 mCode.add(instr);
1623 }
1624 }
1625
1626
1634
1635 public void addMULTIANEWARRAYInstruction(String type, int dim) {
1636 CLInstruction instr = null;
1637 if (!validTypeDescriptor(type)) {
1638 reportEmitterError(
1639 "%s: '%s' is not a valid type descriptor for an array",
1640 eCurrentMethod, type);
1641 }
1642 int index = constantPool.constantClassInfo(type);
1643 instr = new CLArrayInstruction(MULTIANEWARRAY, mPC++, index, dim);
1644 if (instr != null) {
1645 mPC += instr.operandCount();
1646 mCode.add(instr);
1647 }
1648 }
1649
1650
1667
1668 public void addBranchInstruction(int opcode, String label) {
1669 CLInstruction instr = null;
1670 switch (CLInstruction.instructionInfo[opcode].category) {
1671 case FLOW_CONTROL1:
1672 instr = new CLFlowControlInstruction(opcode, mPC++, label);
1673 break;
1674 default:
1675 reportOpcodeError(opcode);
1676 }
1677 if (instr != null) {
1678 mPC += instr.operandCount();
1679 mCode.add(instr);
1680 mInstructionAfterLabel = true;
1681 }
1682 }
1683
1684
1697
1698 public void addTABLESWITCHInstruction(String defaultLabel, int low,
1699 int high, ArrayList<String> labels) {
1700 CLFlowControlInstruction instr = new CLFlowControlInstruction(
1701 TABLESWITCH, mPC++, defaultLabel, low, high, labels);
1702 mPC += instr.operandCount();
1703 mCode.add(instr);
1704 mInstructionAfterLabel = true;
1705 }
1706
1707
1717
1718 public void addLOOKUPSWITCHInstruction(String defaultLabel, int numPairs,
1719 TreeMap<Integer, String> matchLabelPairs) {
1720 CLFlowControlInstruction instr = new CLFlowControlInstruction(
1721 LOOKUPSWITCH, mPC++, defaultLabel, numPairs, matchLabelPairs);
1722 mPC += instr.operandCount();
1723 mCode.add(instr);
1724 mInstructionAfterLabel = true;
1725 }
1726
1727
1733
1734 public void addLDCInstruction(int i) {
1735 ldcInstruction(constantPool.constantIntegerInfo(i));
1736 }
1737
1738
1744
1745 public void addLDCInstruction(float f) {
1746 ldcInstruction(constantPool.constantFloatInfo(f));
1747 }
1748
1749
1755
1756 public void addLDCInstruction(long l) {
1757 ldc2wInstruction(constantPool.constantLongInfo(l));
1758 }
1759
1760
1766
1767 public void addLDCInstruction(double d) {
1768 ldc2wInstruction(constantPool.constantDoubleInfo(d));
1769 }
1770
1771
1777
1778 public void addLDCInstruction(String s) {
1779 ldcInstruction(constantPool.constantStringInfo(s));
1780 }
1781
1782
1788
1789 public void addClassAttribute(CLAttributeInfo attribute) {
1790 if (attributes != null) {
1791 attributes.add(attribute);
1792 }
1793 }
1794
1795
1802
1803 public void addMethodAttribute(CLAttributeInfo attribute) {
1804 if (mAttributes != null) {
1805 mAttributes.add(attribute);
1806 }
1807 }
1808
1809
1816
1817 public void addFieldAttribute(CLAttributeInfo attribute) {
1818 if (fAttributes != null) {
1819 fAttributes.add(attribute);
1820 }
1821 }
1822
1823
1830
1831 public void addCodeAttribute(CLAttributeInfo attribute) {
1832 if (mCodeAttributes != null) {
1833 mCodeAttributes.add(attribute);
1834 }
1835 }
1836
1837
1845
1846 public void addLabel(String label) {
1847 mLabels.put(label, mPC);
1848 mInstructionAfterLabel = false;
1849 }
1850
1851
1856
1857 public String createLabel() {
1858 return "Label" + mLabelCount++;
1859 }
1860
1861
1867
1868 public int pc() {
1869 return mPC;
1870 }
1871
1872
1877
1878 public CLConstantPool constantPool() {
1879 return constantPool;
1880 }
1881
1882
1885
1886 public static void initializeByteClassLoader() {
1887 byteClassLoader = new ByteClassLoader();
1888 }
1889
1890
1894
1895 public CLFile clFile() {
1896 return clFile;
1897 }
1898
1899
1904 public Class toClass() {
1905 endOpenMethodIfAny();
1906 Class theClass = null;
1907 try {
1908 ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
1911 CLOutputStream out = new CLOutputStream(new BufferedOutputStream(
1912 byteStream));
1913 clFile.write(out);
1914 out.close();
1915 byte[] classBytes = byteStream.toByteArray();
1916 byteStream.close();
1917
1918 byteClassLoader.setClassBytes(classBytes);
1921 theClass = byteClassLoader.loadClass(name, true);
1922 } catch (IOException e) {
1923 reportEmitterError("Cannot write class to byte stream");
1924 } catch (ClassNotFoundException e) {
1925 reportEmitterError("Cannot load class from byte stream");
1926 }
1927 return theClass;
1928 }
1929
1930
1935
1936 public void write() {
1937 endOpenMethodIfAny();
1938 if (!toFile) {
1939 return;
1940 }
1941 String outFile = destDir + File.separator + name + ".class";
1942 try {
1943 File file = new File(destDir + File.separator
1944 + name.substring(0, name.lastIndexOf("/") + 1));
1945 file.mkdirs();
1946 CLOutputStream out = new CLOutputStream(new BufferedOutputStream(
1947 new FileOutputStream(outFile)));
1948 clFile.write(out);
1949 out.close();
1950 } catch (FileNotFoundException e) {
1951 reportEmitterError("File %s not found", outFile);
1952 } catch (IOException e) {
1953 reportEmitterError("Cannot write to file %s", outFile);
1954 }
1955 }
1956}
1957
1958
1961
1962class CLException {
1963
1964
1968 public String startLabel;
1969
1970
1974 public String endLabel;
1975
1976
1979 public String handlerLabel;
1980
1981
1985 public String catchType;
1986
1987
1988 public int startPC;
1989
1990
1991 public int endPC;
1992
1993
1994 public int handlerPC;
1995
1996
2013
2014 public CLException(String startLabel, String endLabel, String handlerLabel,
2015 String catchType) {
2016 this.startLabel = startLabel;
2017 this.endLabel = endLabel;
2018 this.handlerLabel = handlerLabel;
2019 this.catchType = catchType;
2020 }
2021
2022
2031
2032 public boolean resolveLabels(Hashtable<String, Integer> labelToPC) {
2033 boolean allLabelsResolved = true;
2034 if (labelToPC.containsKey(startLabel)) {
2035 startPC = labelToPC.get(startLabel);
2036 } else {
2037 startPC = 0;
2038 allLabelsResolved = false;
2039 }
2040 if (labelToPC.containsKey(endLabel)) {
2041 endPC = labelToPC.get(endLabel);
2042 } else {
2043 endPC = 0;
2044 allLabelsResolved = false;
2045 }
2046 if (labelToPC.containsKey(handlerLabel)) {
2047 handlerPC = labelToPC.get(handlerLabel);
2048 } else {
2049 handlerPC = 0;
2050 allLabelsResolved = false;
2051 }
2052 return allLabelsResolved;
2053 }
2054
2055}
2056
2057
2062
2063class CLBranchTarget {
2064
2065
2066 public CLInstruction target;
2067
2068
2069 public int stackDepth;
2070
2071
2079
2080 public CLBranchTarget(CLInstruction target, int stackDepth) {
2081 this.target = target;
2082 this.stackDepth = stackDepth;
2083 }
2084
2085}
2086
2087
2091
2092class CLBranchStack {
2093
2094
2095 private Stack<CLBranchTarget> branchTargets;
2096
2097
2098 private Hashtable<CLInstruction, CLBranchTarget> visitedTargets;
2099
2100
2110
2111 private CLBranchTarget visit(CLInstruction target, int stackDepth) {
2112 CLBranchTarget bt = new CLBranchTarget(target, stackDepth);
2113 visitedTargets.put(target, bt);
2114 return bt;
2115 }
2116
2117
2126
2127 private boolean visited(CLInstruction target) {
2128 return (visitedTargets.get(target) != null);
2129 }
2130
2131
2134
2135 public CLBranchStack() {
2136 this.branchTargets = new Stack<CLBranchTarget>();
2137 this.visitedTargets = new Hashtable<CLInstruction, CLBranchTarget>();
2138 }
2139
2140
2149
2150 public void push(CLInstruction target, int stackDepth) {
2151 if (visited(target)) {
2152 return;
2153 }
2154 branchTargets.push(visit(target, stackDepth));
2155 }
2156
2157
2163
2164 public CLBranchTarget pop() {
2165 if (!branchTargets.empty()) {
2166 CLBranchTarget bt = (CLBranchTarget) branchTargets.pop();
2167 return bt;
2168 }
2169 return null;
2170 }
2171
2172}
2173
2174
2177
2178class ByteClassLoader extends ClassLoader {
2179
2180
2181 private byte[] bytes;
2182
2183
2184 private boolean pkgDefined = false;
2185
2186
2192
2193 public void setClassBytes(byte[] bytes) {
2194 this.bytes = bytes;
2195 }
2196
2197
2200
2201 public Class<?> loadClass(String name, boolean resolve)
2202 throws ClassNotFoundException {
2203 Class cls = findLoadedClass(name);
2204 if (cls == null) {
2205 try {
2206 cls = findSystemClass(name);
2207 } catch (Exception e) {
2208 }
2210 }
2211 if (cls == null) {
2212 name = name.replace("/", ".");
2213 String pkg = name.lastIndexOf('.') == -1 ? "" : name.substring(0,
2214 name.lastIndexOf('.'));
2215 if (!pkgDefined) {
2216 definePackage(pkg, "", "", "", "", "", "", null);
2222 pkgDefined = true;
2223 }
2224 cls = defineClass(name, bytes, 0, bytes.length);
2225 if (resolve && cls != null) {
2226 resolveClass(cls);
2227 }
2228 }
2229 return cls;
2230 }
2231
2232}
2233
2234
2239
2240class CLOutputStream extends DataOutputStream {
2241
2242
2248
2249 public CLOutputStream(OutputStream out) {
2250 super(out);
2251 }
2252
2253
2269
2270 public final void writeInt(long v) throws IOException {
2271 long mask = 0xFF;
2272 out.write((byte) (mask & (v >> 24)));
2273 out.write((byte) (mask & (v >> 16)));
2274 out.write((byte) (mask & (v >> 8)));
2275 out.write((byte) (mask & v));
2276 }
2277
2278}
2279