001: package sli.kim.classfile;
002:
003: import java.util.*;
004: import java.io.*;
005:
006: /**
007: * This class gets information from a ClassInfo and writes it out in the
008: * java classfile format.
009: *
010: * @see ClassInfo
011: */
012: public class ClassFileWriter {
013: /**
014: * Write a ClassInfo out to a stream in the java classfile format.
015: * @throws ClassFileWriteException if there is incorrect or missing
016: * information in the ClassInfo.
017: * @throws IOException if the OutputStream throws an IOException.
018: */
019: public void write(ClassInfo classInfo, OutputStream out)
020: throws IOException, ClassFileWriteException {
021: ConstPool cp = classInfo.getConstPool();
022: if (cp == null)
023: cp = new ConstPool();
024: // do a dry run to make sure ConstPool has all the entries it needs.
025: boolean debugEnabled = Debug.isEnabled();
026: Debug.setEnabled(false);
027: writeClass(cp, classInfo, new NullOutputStream());
028: Debug.setEnabled(debugEnabled);
029: // now do the real write
030: BufferedOutputStream bos = new BufferedOutputStream(out);
031: DataOutputStream dos = new DataOutputStream(bos);
032: writeClass(cp, classInfo, dos);
033: dos.flush();
034: dos.close();
035: bos.flush();
036: bos.close();
037: }
038:
039: private void writeClass(ConstPool cp, ClassInfo classInfo,
040: DataOutput out) throws IOException, ClassFileWriteException {
041: out.writeInt(0xCAFEBABE);
042: out.writeInt(0x0003002D);
043:
044: cp.write(out);
045:
046: // General Class Info
047: {
048: short flags = classInfo.getAccessFlags();
049: short classIndex = cp.getIndexOfClassAdd(classInfo
050: .getName());
051: short super ClassIndex = cp.getIndexOfClassAdd(classInfo
052: .getSuperClassName());
053:
054: if (Debug.writeClass != null)
055: Debug.println(Debug.writeClass, "flags=" + flags
056: + "; class index=" + classIndex
057: + "; super class index=" + super ClassIndex);
058:
059: out.writeShort(flags);
060: out.writeShort(classIndex);
061: out.writeShort(super ClassIndex);
062: }
063:
064: // Interfaces
065: String[] interfaces = classInfo.getInterfaces();
066: if (Debug.writeClass != null)
067: Debug.println(Debug.writeClass, "#interfaces="
068: + interfaces.length);
069: Debug.indent();
070: out.writeShort(interfaces.length);
071: for (int i = 0; i < interfaces.length; i++)
072: writeInterface(interfaces[i], cp, out);
073: Debug.outdent();
074:
075: // Fields
076: FieldInfo[] fields = classInfo.getFields();
077: if (Debug.writeClass != null)
078: Debug.println(Debug.writeClass, "#fields=" + fields.length);
079: Debug.indent();
080: out.writeShort(fields.length);
081: for (int i = 0; i < fields.length; i++)
082: writeField(fields[i], cp, out);
083: Debug.outdent();
084:
085: // Methods
086: MethodInfo[] methods = classInfo.getMethods();
087: if (Debug.writeClass != null)
088: Debug.println(Debug.writeClass, "#methods="
089: + methods.length);
090: Debug.indent();
091: out.writeShort(methods.length);
092: for (int i = 0; i < methods.length; i++)
093: writeMethod(methods[i], cp, out);
094: Debug.outdent();
095:
096: // Attributes
097: AttributeInfo[] attributes = classInfo.getAttributes();
098: String sourceFile = classInfo.getSourceFile();
099: InnerClassInfo[] innerClasses = classInfo.getInnerClasses();
100: int numAttributes = attributes.length
101: + (sourceFile != null ? 1 : 0)
102: + (innerClasses != null ? 1 : 0);
103:
104: if (Debug.writeClass != null)
105: Debug.println(Debug.writeClass, "#attributes="
106: + numAttributes);
107: Debug.indent();
108:
109: out.writeShort(numAttributes);
110:
111: // SourceFile Attribute
112: if (sourceFile != null)
113: writeSourceFile(sourceFile, cp, out);
114:
115: // InnerClasses Attribute
116: if (innerClasses != null)
117: writeInnerClasses(innerClasses, cp, out);
118:
119: // Other Attributes
120: writeUnknownAttributes(attributes, cp, out);
121:
122: Debug.outdent();
123: }
124:
125: private void writeInterface(String className, ConstPool cp,
126: DataOutput out) throws IOException {
127: short classIndex = cp.getIndexOfClassAdd(className);
128: if (Debug.writeInterface != null)
129: Debug.println(Debug.writeInterface, "class index="
130: + classIndex);
131: out.writeShort(classIndex);
132: }
133:
134: private void writeField(FieldInfo fieldInfo, ConstPool cp,
135: DataOutput out) throws IOException, ClassFileWriteException {
136: // General Field Info
137: {
138: short flags = fieldInfo.getAccessFlags();
139: short nameIndex = cp.getIndexOfUTFAdd(fieldInfo.getName());
140: short signatureIndex = cp.getIndexOfUTFAdd(fieldInfo
141: .getSignature());
142:
143: if (Debug.writeField != null)
144: Debug.println(Debug.writeField, "flags=" + flags
145: + "; name index=" + nameIndex
146: + "; signature index=" + signatureIndex);
147:
148: out.writeShort(flags);
149: out.writeShort(nameIndex);
150: out.writeShort(signatureIndex);
151: }
152:
153: // Field Attributes
154: AttributeInfo[] attributes = fieldInfo.getAttributes();
155: Object constValue = fieldInfo.getConstantValue();
156: int numAttributes = (constValue != null ? 1 : 0)
157: + attributes.length;
158:
159: if (Debug.writeField != null)
160: Debug.println(Debug.writeField, "#attributes="
161: + numAttributes);
162: Debug.indent();
163:
164: out.writeShort((short) numAttributes);
165:
166: // ConstantValue Attribute
167: if (constValue != null)
168: writeConstantValue(constValue, cp, out);
169:
170: // Synthetic Attribute
171: if (fieldInfo.isSynthetic()) {
172: writeAttributeNameIndex("Synthetic", Debug.writeField, cp,
173: out);
174: out.writeInt(0); // length == 0
175: }
176:
177: // Other Attributes
178: writeUnknownAttributes(fieldInfo.getAttributes(), cp, out);
179:
180: Debug.outdent();
181: }
182:
183: private void writeMethod(MethodInfo methodInfo, ConstPool cp,
184: DataOutput out) throws IOException {
185: // General Method Info
186: {
187: short flags = methodInfo.getAccessFlags();
188: short nameIndex = cp.getIndexOfUTFAdd(methodInfo.getName());
189: short signatureIndex = cp.getIndexOfUTFAdd(methodInfo
190: .getSignature());
191:
192: if (Debug.writeMethod != null)
193: Debug.println(Debug.writeMethod, "flags=" + flags
194: + "; name index=" + nameIndex
195: + "; signature index=" + signatureIndex);
196:
197: out.writeShort(flags);
198: out.writeShort(nameIndex);
199: out.writeShort(signatureIndex);
200: }
201:
202: // Method Attributes
203: AttributeInfo[] methodAttributes = methodInfo.getAttributes();
204: String[] exceptions = methodInfo.getExceptions();
205: boolean deprecated = methodInfo.isDeprecated();
206: if (exceptions.length == 0)
207: exceptions = null;
208: CodeInfo codeInfo = methodInfo.getCodeInfo();
209: int numAttributes = (exceptions != null ? 1 : 0)
210: + (codeInfo != null ? 1 : 0) + (deprecated ? 1 : 0)
211: + methodAttributes.length;
212:
213: if (Debug.writeMethod != null)
214: Debug.println(Debug.writeMethod, "#attributes="
215: + numAttributes);
216: Debug.indent();
217:
218: out.writeShort((short) numAttributes);
219:
220: // Exceptions Attribute
221: if (exceptions != null)
222: writeExceptions(exceptions, cp, out);
223:
224: // Code Attribute
225: if (codeInfo != null)
226: writeCode(codeInfo, cp, out);
227:
228: // Deprecated Attribute
229: if (deprecated) {
230: writeAttributeNameIndex("Deprecated", Debug.writeMethod,
231: cp, out);
232: out.writeInt(0); // length == 0
233: }
234:
235: // Other Method Attributes
236: writeUnknownAttributes(methodAttributes, cp, out);
237:
238: Debug.outdent();
239: }
240:
241: private void writeCode(CodeInfo codeInfo, ConstPool cp,
242: DataOutput out) throws IOException {
243: writeAttributeNameIndex("Code", Debug.writeMethod, cp, out);
244: out = new LengthFirstOS(out);
245:
246: // General Code Info
247: {
248: short maxStack = codeInfo.getMaxStack();
249: short maxLocals = codeInfo.getMaxLocals();
250: byte[] bytecode = codeInfo.getBytecode();
251:
252: if (Debug.writeCode != null)
253: Debug.println(Debug.writeCode, "maxStack=" + maxStack
254: + "; maxLocals=" + maxLocals
255: + "; bytecode length=" + bytecode.length);
256:
257: out.writeShort(maxStack);
258: out.writeShort(maxLocals);
259: out.writeInt(bytecode.length);
260: out.write(bytecode);
261: }
262:
263: // Exception Table
264: ExceptionInfo[] exceptionTable = codeInfo.getExceptionTable();
265:
266: if (Debug.writeCode != null)
267: Debug.println(Debug.writeCode, "exception table length="
268: + exceptionTable.length);
269: Debug.indent();
270:
271: out.writeShort(exceptionTable.length);
272: for (int i = 0; i < exceptionTable.length; i++) {
273: short startPC = exceptionTable[i].startPC;
274: short endPC = exceptionTable[i].endPC;
275: short handlerPC = exceptionTable[i].handlerPC;
276: String catchType = exceptionTable[i].catchType;
277: short catchTypeIndex = 0;
278: if (catchType != null) // catch type is null for finally blocks
279: cp.getIndexOfClassAdd(exceptionTable[i].catchType);
280:
281: if (Debug.writeCode != null)
282: Debug.println(Debug.writeCode, "startPC = " + startPC
283: + "; endPC = " + endPC + "; handlerPC = "
284: + handlerPC + "; catch type index="
285: + catchTypeIndex);
286:
287: out.writeShort(startPC);
288: out.writeShort(endPC);
289: out.writeShort(handlerPC);
290: out.writeShort(catchTypeIndex);
291: }
292:
293: Debug.outdent();
294:
295: // Code Attributes
296: AttributeInfo[] codeAttributes = codeInfo.getAttributes();
297: LineNumberInfo[] lineNumberTable = codeInfo
298: .getLineNumberTable();
299: LocalVariableInfo[] localVariableTable = codeInfo
300: .getLocalVariableTable();
301: int numCodeAttributes = (lineNumberTable != null ? 1 : 0)
302: + (localVariableTable != null ? 1 : 0)
303: + codeAttributes.length;
304:
305: if (Debug.writeCode != null)
306: Debug.println(Debug.writeCode, "#attributes="
307: + numCodeAttributes);
308: Debug.indent();
309:
310: out.writeShort(numCodeAttributes);
311:
312: // LineNumberTable Attribute
313: if (lineNumberTable != null)
314: writeLineNumberTable(lineNumberTable, cp, out);
315:
316: // LocalVariableTable Attribute
317: if (localVariableTable != null)
318: writeLocalVariableTable(localVariableTable, cp, out);
319:
320: // Other Code Attributes
321: writeUnknownAttributes(codeAttributes, cp, out);
322: ((LengthFirstOS) out).close();
323:
324: Debug.outdent();
325: }
326:
327: private void writeSourceFile(String filename, ConstPool cp,
328: DataOutput out) throws IOException {
329: writeAttributeNameIndex("SourceFile", Debug.writeClass, cp, out);
330:
331: out.writeInt(2); // length of attribute
332: short filenameIndex = cp.getIndexOfUTFAdd(filename);
333:
334: Debug.indent();
335: if (Debug.writeClass != null)
336: Debug.println(Debug.writeClass, "source file index="
337: + filenameIndex);
338: Debug.outdent();
339:
340: out.writeShort(filenameIndex);
341: }
342:
343: private void writeConstantValue(Object constValue, ConstPool cp,
344: DataOutput out) throws IOException, ClassFileWriteException {
345: writeAttributeNameIndex("ConstantValue", Debug.writeField, cp,
346: out);
347: out = new LengthFirstOS(out);
348: ConstPoolEntry searchEntry = new ConstPoolEntry();
349: if (constValue instanceof String)
350: searchEntry.setString(cp
351: .getIndexOfUTFAdd((String) constValue));
352: else if (constValue instanceof Integer)
353: searchEntry.setInt(((Integer) constValue).intValue());
354: else if (constValue instanceof Long)
355: searchEntry.setLong(((Long) constValue).longValue());
356: else if (constValue instanceof Float)
357: searchEntry.setFloat(((Float) constValue).floatValue());
358: else if (constValue instanceof Double)
359: searchEntry.setDouble(((Double) constValue).doubleValue());
360: else
361: throw new ClassFileWriteException(
362: "Unrecognized constant value " + constValue);
363: short cvIndex = cp.getIndexOfEntryAdd(searchEntry);
364:
365: Debug.indent();
366: if (Debug.writeField != null)
367: Debug.println(Debug.writeField, "constant value index="
368: + cvIndex);
369: Debug.outdent();
370:
371: out.writeShort(cvIndex);
372: ((LengthFirstOS) out).close();
373: }
374:
375: private void writeExceptions(String[] exceptions, ConstPool cp,
376: DataOutput out) throws IOException {
377: writeAttributeNameIndex("Exceptions", Debug.writeMethod, cp,
378: out);
379: out = new LengthFirstOS(out);
380:
381: if (Debug.writeMethod != null)
382: Debug.println(Debug.writeMethod, "#exceptions="
383: + exceptions.length);
384: Debug.indent();
385:
386: out.writeShort(exceptions.length);
387: for (int i = 0; i < exceptions.length; i++) {
388: short classIndex = cp.getIndexOfClassAdd(exceptions[i]);
389:
390: if (Debug.writeMethod != null)
391: Debug.println(Debug.writeMethod,
392: "exception class index = " + classIndex);
393:
394: out.writeShort(classIndex);
395: }
396:
397: Debug.outdent();
398:
399: ((LengthFirstOS) out).close();
400: }
401:
402: private void writeLocalVariableTable(
403: LocalVariableInfo[] localVariableTable, ConstPool cp,
404: DataOutput out) throws IOException {
405: writeAttributeNameIndex("LocalVariableTable", Debug.writeCode,
406: cp, out);
407: out = new LengthFirstOS(out);
408:
409: Debug.indent();
410: if (Debug.writeLocalVariables != null)
411: Debug.println(Debug.writeLocalVariables,
412: "#local variables=" + localVariableTable.length);
413:
414: out.writeShort(localVariableTable.length);
415: for (int i = 0; i < localVariableTable.length; i++) {
416: short startPC = localVariableTable[i].startPC;
417: short length = localVariableTable[i].length;
418: short nameIndex = cp
419: .getIndexOfUTFAdd(localVariableTable[i].name);
420: short signatureIndex = cp
421: .getIndexOfUTFAdd(localVariableTable[i].signature);
422: short slot = localVariableTable[i].slot;
423:
424: if (Debug.writeLocalVariables != null)
425: Debug.println(Debug.writeLocalVariables, "startPC="
426: + startPC + "; length=" + length
427: + "; nameIndex=" + nameIndex
428: + "; signatureIndex=" + signatureIndex
429: + "; slot=" + slot);
430:
431: out.writeShort(startPC);
432: out.writeShort(length);
433: out.writeShort(nameIndex);
434: out.writeShort(signatureIndex);
435: out.writeShort(slot);
436: }
437: ((LengthFirstOS) out).close();
438:
439: Debug.outdent();
440: }
441:
442: private void writeLineNumberTable(LineNumberInfo[] lineNumberTable,
443: ConstPool cp, DataOutput out) throws IOException {
444: writeAttributeNameIndex("LineNumberTable", Debug.writeCode, cp,
445: out);
446: out = new LengthFirstOS(out);
447:
448: Debug.indent();
449: if (Debug.writeLineNumbers != null)
450: Debug.println(Debug.writeLineNumbers, "#line numbers="
451: + lineNumberTable.length);
452:
453: out.writeShort(lineNumberTable.length);
454: for (int i = 0; i < lineNumberTable.length; i++) {
455: short startPC = lineNumberTable[i].startPC;
456: short lineNumber = lineNumberTable[i].lineNumber;
457:
458: if (Debug.writeLineNumbers != null)
459: Debug.println(Debug.writeLineNumbers, "startPC="
460: + startPC + "; lineNumber=" + lineNumber);
461:
462: out.writeShort(startPC);
463: out.writeShort(lineNumber);
464: }
465: ((LengthFirstOS) out).close();
466:
467: Debug.outdent();
468: }
469:
470: private void writeInnerClasses(InnerClassInfo[] innerClasses,
471: ConstPool cp, DataOutput out) throws IOException {
472: writeAttributeNameIndex("InnerClasses", Debug.writeClass, cp,
473: out);
474: out = new LengthFirstOS(out);
475:
476: Debug.indent();
477: if (Debug.writeInnerClasses != null)
478: Debug.println(Debug.writeInnerClasses, "#inner classes="
479: + innerClasses.length);
480:
481: out.writeShort(innerClasses.length);
482: for (int i = 0; i < innerClasses.length; i++) {
483: short innerClassIndex = cp
484: .getIndexOfClassAdd(innerClasses[i].innerClass);
485: short outerClassIndex = 0;
486: if (innerClasses[i].outerClass != null) // can be null if non-member class
487: outerClassIndex = cp
488: .getIndexOfClassAdd(innerClasses[i].outerClass);
489: short simpleNameIndex = 0;
490: if (innerClasses[i].simpleName != null) // can be null if anonymous class
491: simpleNameIndex = cp
492: .getIndexOfUTFAdd(innerClasses[i].simpleName);
493: short flags = innerClasses[i].flags;
494:
495: if (Debug.writeInnerClasses != null)
496: Debug.println(Debug.writeInnerClasses,
497: "inner class index=" + innerClassIndex
498: + "; outer class index="
499: + outerClassIndex
500: + "; simple name index="
501: + simpleNameIndex + "; flags=" + flags);
502:
503: out.writeShort(innerClassIndex);
504: out.writeShort(outerClassIndex);
505: out.writeShort(simpleNameIndex);
506: out.writeShort(flags);
507: }
508: ((LengthFirstOS) out).close();
509:
510: Debug.outdent();
511: }
512:
513: private void writeAttributeNameIndex(String attrName,
514: String debugMsg, ConstPool cp, DataOutput out)
515: throws IOException {
516: short index = cp.getIndexOfUTFAdd(attrName);
517:
518: if (debugMsg != null)
519: Debug.println(debugMsg, "attribute name index=" + index);
520:
521: out.writeShort(index);
522: }
523:
524: private void writeUnknownAttributes(AttributeInfo[] attributes,
525: ConstPool cp, DataOutput out) throws IOException {
526: for (int i = 0; i < attributes.length; i++) {
527: short attrNameIndex = cp.getIndexOfUTFAdd(attributes[i]
528: .getName());
529: byte[] data = attributes[i].getData();
530:
531: if (Debug.writeUnknownAttribute != null)
532: Debug.println(Debug.writeUnknownAttribute,
533: "attribute name index=" + attrNameIndex
534: + "; attribute length=" + data.length);
535:
536: out.writeShort(attrNameIndex);
537: out.writeInt(data.length);
538: out.write(data);
539: }
540: }
541: }
542:
543: // Special output stream to help with writing attributes.
544: // When we write attributes, we need to know the size of the entire
545: // attribute before we write its data, and figuring that out can be
546: // awkward. This class acts like an output stream, but it stores all
547: // output into a byte array, and when closed, it writes out the size of
548: // the stored data followed by the data itself.
549: class LengthFirstOS implements DataOutput {
550: DataOutput realOut;
551: ByteArrayOutputStream baos = new ByteArrayOutputStream();
552: DataOutputStream dos = new DataOutputStream(baos);
553:
554: public LengthFirstOS(DataOutput realOut) {
555: this .realOut = realOut;
556: }
557:
558: public void write(byte b[]) throws IOException {
559: dos.write(b);
560: }
561:
562: public void write(byte b[], int off, int len) throws IOException {
563: dos.write(b, off, len);
564: }
565:
566: public void write(int b) throws IOException {
567: dos.write(b);
568: }
569:
570: public void writeBoolean(boolean v) throws IOException {
571: dos.writeBoolean(v);
572: }
573:
574: public void writeByte(int v) throws IOException {
575: dos.writeByte(v);
576: }
577:
578: public void writeBytes(String s) throws IOException {
579: dos.writeBytes(s);
580: }
581:
582: public void writeChar(int v) throws IOException {
583: dos.writeChar(v);
584: }
585:
586: public void writeChars(String s) throws IOException {
587: dos.writeChars(s);
588: }
589:
590: public void writeDouble(double v) throws IOException {
591: dos.writeDouble(v);
592: }
593:
594: public void writeFloat(float v) throws IOException {
595: dos.writeFloat(v);
596: }
597:
598: public void writeInt(int v) throws IOException {
599: dos.writeInt(v);
600: }
601:
602: public void writeLong(long v) throws IOException {
603: dos.writeLong(v);
604: }
605:
606: public void writeShort(int v) throws IOException {
607: dos.writeShort(v);
608: }
609:
610: public void writeUTF(String str) throws IOException {
611: dos.writeUTF(str);
612: }
613:
614: public void close() throws IOException {
615: dos.flush();
616: dos.close();
617: baos.flush();
618: baos.close();
619: byte[] data = baos.toByteArray();
620: realOut.writeInt((short) data.length);
621: realOut.write(data);
622: }
623: }
|