001: package spoon.support.util;
002:
003: import java.io.BufferedOutputStream;
004: import java.io.File;
005: import java.io.FileOutputStream;
006: import java.io.IOException;
007: import java.util.Map;
008: import java.util.StringTokenizer;
009:
010: import org.eclipse.jdt.internal.compiler.util.Messages;
011:
012: public class ClassFileUtil {
013:
014: static int u2(byte[] bytes, int offset) {
015: int i = 0;
016: i |= bytes[offset] & 0xFF;
017: i <<= 8;
018: i |= bytes[offset + 1] & 0xFF;
019: return i;
020: }
021:
022: static long u4(byte[] bytes, int offset) {
023: long l = 0;
024: l |= bytes[offset] & 0xFF;
025: l <<= 8;
026: l |= bytes[offset + 1] & 0xFF;
027: l <<= 8;
028: l |= bytes[offset + 2] & 0xFF;
029: l <<= 8;
030: l |= bytes[offset + 3] & 0xFF;
031: return l;
032: }
033:
034: public static void printBytes(byte[] bytes, int offset) {
035: for (int i = offset; i < bytes.length; i++) {
036: System.out.print("(" + i + "):" + bytes[i]);
037: }
038: System.out.println();
039: }
040:
041: public static void adjustLineNumbers(byte[] bytes,
042: int methodsOffset, Map<Integer, Integer> lineNumberMapping) {
043: if (lineNumberMapping == null) {
044: return;
045: }
046: // printBytes(bytes, methodsOffset);
047: int offset = methodsOffset + 2;
048: int methodCount = u2(bytes, methodsOffset);
049: // System.out.println("- adjusting " + methodCount + " methods...");
050: for (int i = 0; i < methodCount; i++) {
051: offset = adjustMethod(bytes, offset, lineNumberMapping);
052: }
053: }
054:
055: static int adjustMethod(byte[] bytes, int offset,
056: Map<Integer, Integer> lineNumberMapping) {
057: // System.out.println("adjusting method "
058: // + getPoolString(bytes, u2(bytes, offset + 2)));
059: int attrCount = u2(bytes, offset + 6);
060: offset += 8;
061: for (int i = 0; i < attrCount; i++) {
062: offset = adjustCodeAttribute(bytes, offset,
063: lineNumberMapping);
064: }
065: return offset;
066: }
067:
068: static int adjustCodeAttribute(byte[] bytes, int offset,
069: Map<Integer, Integer> lineNumberMapping) {
070: String attrName = getPoolString(bytes, u2(bytes, offset));
071: // System.out.println("attribute: "+attrName);
072: if ("Code".equals(attrName)) {
073: // printBytes(bytes, offset);
074: int offset2 = offset + 14 + (int) u4(bytes, offset + 10);
075: offset2 += u2(bytes, offset2) * 8;
076: offset2 += 2;
077: int attrCount = u2(bytes, offset2);
078: // System.out.println("attrcount: "+attrCount);
079: offset2 += 2;
080: for (int i = 0; i < attrCount; i++) {
081: offset2 = adjustLineNumberAttribute(bytes, offset2,
082: lineNumberMapping);
083: }
084: }
085: return offset + 6 + (int) u4(bytes, offset + 2);
086: }
087:
088: static int adjustLineNumberAttribute(byte[] bytes, int offset,
089: Map<Integer, Integer> lineNumberMapping) {
090: String attrName = getPoolString(bytes, u2(bytes, offset));
091: if ("LineNumberTable".equals(attrName)) {
092: // System.out.println("found line number table");
093: int offset2 = offset + 6;
094: int lineCount = u2(bytes, offset2);
095: offset2 += 2;
096: for (int i = 0; i < lineCount; i++) {
097: int org = u2(bytes, offset2 + 2);
098: Integer pos = lineNumberMapping.get(org);
099: if (pos != null) {
100: int ipos = pos;
101: // System.out.println("adjusting " + org + " -> "
102: // + pos);
103: bytes[offset2 + 2] = (byte) (ipos >> 8);
104: bytes[offset2 + 3] = (byte) (ipos);
105: // ln.setLineNumber(pos.getLine());
106: } else {
107: // System.out.println("WARNING: no position for " + org);
108: }
109: offset2 += 4;
110: }
111: }
112: return offset + 6 + (int) u4(bytes, offset + 2);
113: }
114:
115: // static int adjustCodeAttribute(byte[] bytes, int offset,
116: // Map<Integer, SourcePosition> lineNumberMapping) {
117: // String attrName = getPoolString(bytes, u2(bytes, offset));
118: // System.out.println("attribute: "+attrName);
119: // if ("Code".equals(attrName)) {
120: // }
121: // return offset+6+(int)u4(bytes,offset+2);
122: // }
123:
124: static String getPoolString(byte[] bytes, int index) {
125: int offset = 10;
126: int i = index;
127: while (i > 1) {
128: if ((bytes[offset] == ConstantType.Double)
129: || (bytes[offset] == ConstantType.Long)) {
130: // stupid adjustment because double and long take 2 entries
131: // many thanks to the guy who made this !$@% choice!
132: i--;
133: }
134: offset = skipPoolConstant(bytes, offset);
135: i--;
136: }
137: if (bytes[offset] != ConstantType.Utf8) {
138: throw new RuntimeException(
139: "error in pool constant: unable to get utf8 at "
140: + index + " (o=" + offset + ")");
141: }
142: int length = u2(bytes, offset + 1);
143: byte[] string = new byte[length];
144: try {
145: System.arraycopy(bytes, offset + 3, string, 0, length);
146: } catch (Exception e) {
147: System.err.println("error getting the string: l=" + length
148: + ", o=" + offset);
149: printBytes(bytes, offset + 3);
150: e.printStackTrace();
151: }
152: return new String(string);
153: }
154:
155: static int skipPoolConstant(byte[] bytes, int offset) {
156: byte constantType = bytes[offset];
157: switch (constantType) {
158: case ConstantType.String:
159: case ConstantType.Class:
160: return offset + 3;
161: case ConstantType.Methodref:
162: case ConstantType.InterfaceMethodref:
163: case ConstantType.Fieldref:
164: case ConstantType.Integer:
165: case ConstantType.Float:
166: case ConstantType.NameAndType:
167: return offset + 5;
168: case ConstantType.Double:
169: case ConstantType.Long:
170: return offset + 9;
171: // return skipPoolConstant(bytes, offset);
172: case ConstantType.Utf8:
173: // int length = u2(bytes, offset + 1);
174: // byte[] string = new byte[length];
175: // System.arraycopy(bytes, offset + 3, string, 0, length);
176: // String s=new String(string);
177: return offset + 3 + u2(bytes, offset + 1);
178: default:
179: throw new RuntimeException("invalid constant pool");
180: }
181: }
182:
183: public static void writeToDisk(boolean generatePackagesStructure,
184: String outputPath, String relativeFileName, byte[] bytes)
185: throws IOException {
186:
187: BufferedOutputStream output = null;
188: if (generatePackagesStructure) {
189: output = new BufferedOutputStream(new FileOutputStream(
190: new File(buildAllDirectoriesInto(outputPath,
191: relativeFileName))));
192: } else {
193: String fileName = null;
194: char fileSeparatorChar = File.separatorChar;
195: String fileSeparator = File.separator;
196: // First we ensure that the outputPath exists
197: outputPath = outputPath.replace('/', fileSeparatorChar);
198: // To be able to pass the mkdirs() method we need to remove the
199: // extra file separator at the end of the outDir name
200: int indexOfPackageSeparator = relativeFileName
201: .lastIndexOf(fileSeparatorChar);
202: if (indexOfPackageSeparator == -1) {
203: if (outputPath.endsWith(fileSeparator)) {
204: fileName = outputPath + relativeFileName;
205: } else {
206: fileName = outputPath + fileSeparator
207: + relativeFileName;
208: }
209: } else {
210: int length = relativeFileName.length();
211: if (outputPath.endsWith(fileSeparator)) {
212: fileName = outputPath
213: + relativeFileName
214: .substring(
215: indexOfPackageSeparator + 1,
216: length);
217: } else {
218: fileName = outputPath
219: + fileSeparator
220: + relativeFileName
221: .substring(
222: indexOfPackageSeparator + 1,
223: length);
224: }
225: }
226: output = new BufferedOutputStream(new FileOutputStream(
227: new File(fileName)));
228: }
229: try {
230: output.write(bytes, 0, bytes.length);
231: // output.write(classFile.contents, 0, classFile.contentsOffset);
232: } finally {
233: output.flush();
234: output.close();
235: }
236: }
237:
238: static String buildAllDirectoriesInto(String outputPath,
239: String relativeFileName) throws IOException {
240: char fileSeparatorChar = File.separatorChar;
241: String fileSeparator = File.separator;
242: File f;
243: // First we ensure that the outputPath exists
244: outputPath = outputPath.replace('/', fileSeparatorChar);
245: // To be able to pass the mkdirs() method we need to remove the extra
246: // file separator at the end of the outDir name
247: if (outputPath.endsWith(fileSeparator)) {
248: outputPath = outputPath.substring(0,
249: outputPath.length() - 1);
250: }
251: f = new File(outputPath);
252: if (f.exists()) {
253: if (!f.isDirectory()) {
254: final String message = Messages.bind(
255: Messages.output_isFile, f.getAbsolutePath());
256: throw new IOException(message);
257: }
258: } else {
259: // we have to create that directory
260: if (!f.mkdirs()) {
261: final String message = Messages.bind(
262: Messages.output_notValidAll, f
263: .getAbsolutePath());
264: throw new IOException(message);
265: }
266: }
267: StringBuffer outDir = new StringBuffer(outputPath);
268: outDir.append(fileSeparator);
269: StringTokenizer tokenizer = new StringTokenizer(
270: relativeFileName, fileSeparator);
271: String token = tokenizer.nextToken();
272: while (tokenizer.hasMoreTokens()) {
273: f = new File(outDir.append(token).append(fileSeparator)
274: .toString());
275: if (f.exists()) {
276: // The outDir already exists, so we proceed the next entry
277: // System.out.println("outDir: " + outDir + " already exists.");
278: } else {
279: // Need to add the outDir
280: if (!f.mkdir()) {
281: throw new IOException(Messages.bind(
282: Messages.output_notValid, f.getName()));
283: }
284: }
285: token = tokenizer.nextToken();
286: }
287: // token contains the last one
288: return outDir.append(token).toString();
289: }
290:
291: }
292:
293: class ConstantType {
294: public static final byte Class = 7;
295:
296: public static final byte Fieldref = 9;
297:
298: public static final byte Methodref = 10;
299:
300: public static final byte InterfaceMethodref = 11;
301:
302: public static final byte String = 8;
303:
304: public static final byte Integer = 3;
305:
306: public static final byte Float = 4;
307:
308: public static final byte Long = 5;
309:
310: public static final byte Double = 6;
311:
312: public static final byte NameAndType = 12;
313:
314: public static final byte Utf8 = 1;
315: }
|