1
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
31 public class CLEmitter {
32 private String name;
34
35 private boolean toFile;
38
39 private String destDir;
41
42 private CLFile clFile;
44
45 private CLConstantPool constantPool;
47
48 private ArrayList<Integer> interfaces;
50
51 private ArrayList<CLFieldInfo> fields;
53
54 private ArrayList<CLAttributeInfo> fAttributes;
56
57 private ArrayList<CLMethodInfo> methods;
59
60 private ArrayList<CLAttributeInfo> mAttributes;
62
63 private ArrayList<CLAttributeInfo> attributes;
65
66 private ArrayList<CLInnerClassInfo> innerClasses;
68
69 private ArrayList<CLInstruction> mCode;
71
72 private ArrayList<CLException> mExceptionHandlers;
74
75 private int mAccessFlags;
77
78 private int mNameIndex;
80
81 private int mDescriptorIndex;
84
85 private int mArgumentCount;
87
88 private ArrayList<CLAttributeInfo> mCodeAttributes;
90
91 private boolean isMethodOpen;
93
94 private Hashtable<String, Integer> mLabels;
98
99 private int mLabelCount;
101
102 private boolean mInstructionAfterLabel = false;
107
108 private int mPC;
111
112 private String eCurrentMethod;
114
115 private boolean errorHasOccurred;
117
118 private static ByteClassLoader byteClassLoader;
120
121
128 public CLEmitter(boolean toFile) {
129 destDir = ".";
130 this.toFile = toFile;
131 }
132
133
138 public void destinationDir(String destDir) {
139 this.destDir = destDir;
140 }
141
142
147 public boolean errorHasOccurred() {
148 return errorHasOccurred;
149 }
150
151
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
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
237 public void addField(ArrayList<String> accessFlags, String name, String type,
238 boolean isSynthetic) {
239 addFieldInfo(accessFlags, name, type, isSynthetic, -1);
240 }
241
242
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
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
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
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
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
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(); 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
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
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
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
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
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 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 stackUnits--;
632 }
633 instr = new CLMethodInstruction(opcode, mPC++, index, stackUnits);
634
635 if (opcode == INVOKEINTERFACE) {
638 ((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
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
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
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
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
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
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
831 public void addLDCInstruction(int i) {
832 ldcInstruction(constantPool.constantIntegerInfo(i));
833 }
834
835
840 public void addLDCInstruction(float f) {
841 ldcInstruction(constantPool.constantFloatInfo(f));
842 }
843
844
849 public void addLDCInstruction(long l) {
850 ldc2wInstruction(constantPool.constantLongInfo(l));
851 }
852
853
858 public void addLDCInstruction(double d) {
859 ldc2wInstruction(constantPool.constantDoubleInfo(d));
860 }
861
862
867 public void addLDCInstruction(String s) {
868 ldcInstruction(constantPool.constantStringInfo(s));
869 }
870
871
876 public void addClassAttribute(CLAttributeInfo attribute) {
877 if (attributes != null) {
878 attributes.add(attribute);
879 }
880 }
881
882
887 public void addMethodAttribute(CLAttributeInfo attribute) {
888 if (mAttributes != null) {
889 mAttributes.add(attribute);
890 }
891 }
892
893
898 public void addFieldAttribute(CLAttributeInfo attribute) {
899 if (fAttributes != null) {
900 fAttributes.add(attribute);
901 }
902 }
903
904
910 public void addCodeAttribute(CLAttributeInfo attribute) {
911 if (mCodeAttributes != null) {
912 mCodeAttributes.add(attribute);
913 }
914 }
915
916
923 public void addLabel(String label) {
924 mLabels.put(label, mPC);
925 mInstructionAfterLabel = false;
926 }
927
928
933 public String createLabel() {
934 return "Label" + mLabelCount++;
935 }
936
937
942 public int pc() {
943 return mPC;
944 }
945
946
951 public CLConstantPool constantPool() {
952 return constantPool;
953 }
954
955
958 public static void initializeByteClassLoader() {
959 byteClassLoader = new ByteClassLoader();
960 }
961
962
967 public CLFile clFile() {
968 return clFile;
969 }
970
971
976 public Class toClass() {
977 endOpenMethodIfAny();
978 Class theClass = null;
979 try {
980 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 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
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 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 private void endOpenMethodIfAny() {
1044 if (isMethodOpen) {
1045 isMethodOpen = false;
1046 if (!mInstructionAfterLabel) {
1047 addNoArgInstruction(NOP);
1049 }
1050
1051 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 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 ArrayList<Integer> byteCode = new ArrayList<Integer>();
1070 int maxLocals = mArgumentCount;
1071 for (CLInstruction instr : mCode) {
1072 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 localVariableIndex++;
1097 }
1098 maxLocals = Math.max(maxLocals, localVariableIndex + 1);
1099
1100 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 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
1123 if (innerClasses.size() > 0) {
1125 addClassAttribute(innerClassesAttribute());
1126 }
1127
1128 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 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 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 private int methodStackResidue(String descriptor) {
1194 int i = 0;
1195
1196 String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
1198 String returnType = descriptor.substring(descriptor.lastIndexOf(")") + 1);
1199
1200 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 i += typeStackResidue(returnType);
1228 return i;
1229 }
1230
1231 private int argumentCount(String descriptor) {
1234 int i = 0;
1235
1236 String argTypes = descriptor.substring(1, descriptor.lastIndexOf(")"));
1238
1239 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 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 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 private boolean validMethodDescriptor(String s) {
1323 if ((s != null) && (s.length() > 0)) {
1324 try {
1325 String argTypes = s.substring(1, s.lastIndexOf(")"));
1327 String returnType = s.substring(s.lastIndexOf(")") + 1);
1328
1329 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 return (returnType.equals("V") || validTypeDescriptor(returnType));
1361 } catch (IndexOutOfBoundsException e) {
1362 return false;
1363 }
1364 }
1365 return false;
1366 }
1367
1368 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 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 private int stackDepth() {
1392 CLBranchStack branchTargets = new CLBranchStack();
1393 for (CLException e : mExceptionHandlers) {
1394 CLInstruction h = instruction(e.handlerPC);
1395 if (h != null) {
1396 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 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 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 private CLConstantValueAttribute constantValueAttribute(int c) {
1476 int attributeNameIndex = constantPool.constantUtf8Info(ATT_CONSTANT_VALUE);
1477 return new CLConstantValueAttribute(attributeNameIndex, 2, c);
1478 }
1479
1480 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 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 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 private CLAttributeInfo syntheticAttribute() {
1518 int attributeNameIndex = constantPool.constantUtf8Info(ATT_SYNTHETIC);
1519 return new CLSyntheticAttribute(attributeNameIndex, 0);
1520 }
1521
1522 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 private void reportEmitterError(String message, Object... args) {
1535 System.err.printf(message, args);
1536 System.err.println();
1537 errorHasOccurred = true;
1538 }
1539}
1540
1541
1544class CLException {
1545 public String startLabel;
1548
1549 public String endLabel;
1552
1553 public String handlerLabel;
1555
1556 public String catchType;
1558
1559 public int startPC;
1561
1562 public int endPC;
1564
1565 public int handlerPC;
1567
1568
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
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
1623class CLBranchTarget {
1624
1627 public CLInstruction target;
1628
1629
1632 public int stackDepth;
1633
1634
1640 public CLBranchTarget(CLInstruction target, int stackDepth) {
1641 this.target = target;
1642 this.stackDepth = stackDepth;
1643 }
1644}
1645
1646
1650class CLBranchStack {
1651 private Stack<CLBranchTarget> branchTargets;
1653
1654 private Hashtable<CLInstruction, CLBranchTarget> visitedTargets;
1656
1657
1660 public CLBranchStack() {
1661 this.branchTargets = new Stack<CLBranchTarget>();
1662 this.visitedTargets = new Hashtable<CLInstruction, CLBranchTarget>();
1663 }
1664
1665
1672 public void push(CLInstruction target, int stackDepth) {
1673 if (visited(target)) {
1674 return;
1675 }
1676 branchTargets.push(visit(target, stackDepth));
1677 }
1678
1679
1684 public CLBranchTarget pop() {
1685 if (!branchTargets.empty()) {
1686 CLBranchTarget bt = (CLBranchTarget) branchTargets.pop();
1687 return bt;
1688 }
1689 return null;
1690 }
1691
1692 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 private boolean visited(CLInstruction target) {
1702 return (visitedTargets.get(target) != null);
1703 }
1704}
1705
1706
1709class ByteClassLoader extends ClassLoader {
1710 private byte[] bytes;
1712
1713 private boolean pkgDefined = false;
1715
1716
1721 public void setClassBytes(byte[] bytes) {
1722 this.bytes = bytes;
1723 }
1724
1725
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 } catch (NoClassDefFoundError e) {
1740 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 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
1768class CLOutputStream extends DataOutputStream {
1769
1774 public CLOutputStream(OutputStream out) {
1775 super(out);
1776 }
1777
1778
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