001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package EDU.purdue.cs.bloat.file;
022:
023: import java.io.*;
024:
025: import EDU.purdue.cs.bloat.reflect.*;
026:
027: /**
028: * Code is used to store the Code attribute of a method in a class file. The
029: * Code attribute stores the raw bytecode of the method, the maximum stack
030: * height and maximum number of locals used by the method, and the exception
031: * handlers used in the method. Code may have several attributes. The local
032: * variable table and the line number table are modeled explicitly. All other
033: * attributes are modeled as generic attributes.
034: *
035: * @see EDU.purdue.cs.bloat.reflect.Catch Catch
036: * @see GenericAttribute
037: * @see EDU.purdue.cs.bloat.reflect.LineNumberDebugInfo
038: * @see LocalVariableTable
039: *
040: * @author Nate Nystrom (<a
041: * href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
042: */
043: public class Code extends Attribute {
044: private ClassInfo classInfo;
045:
046: private int maxStack;
047:
048: private int maxLocals;
049:
050: private byte[] code;
051:
052: private Catch[] handlers;
053:
054: private LineNumberTable lineNumbers;
055:
056: private LocalVariableTable locals;
057:
058: private Attribute[] attrs;
059:
060: /**
061: * Constructor for creating a <code>Code</code> from scratch
062: *
063: * @param codeIndex
064: * The index in the constant pool for the UTF8 "Code"
065: */
066: Code(final ClassInfo classInfo, final int codeIndex) {
067: // Don't know length!
068: super (codeIndex, -1);
069:
070: this .classInfo = classInfo;
071:
072: // These should get set during a commit
073: this .maxStack = -1;
074: this .maxLocals = -1;
075: this .code = new byte[0];
076: this .handlers = new Catch[0];
077: this .lineNumbers = null; // It's okay for these to be null
078: this .locals = null;
079: this .attrs = new Attribute[0];
080: }
081:
082: /**
083: * Constructor. Create a Code attribute from a data stream.
084: *
085: * @param in
086: * The data stream containing the class file.
087: * @param index
088: * The index into the constant pool of the name of the attribute.
089: * @param len
090: * The length of the attribute, excluding the header.
091: * @exception IOException
092: * If an error occurs while reading.
093: */
094: public Code(final ClassInfo classInfo, final DataInputStream in,
095: final int index, final int len) throws IOException {
096: super (index, len);
097:
098: this .classInfo = classInfo;
099:
100: maxStack = in.readUnsignedShort();
101: maxLocals = in.readUnsignedShort();
102:
103: final int codeLength = in.readInt();
104:
105: code = new byte[codeLength];
106:
107: for (int read = 0; read < codeLength;) {
108: read += in.read(code, read, codeLength - read);
109: }
110:
111: final int numHandlers = in.readUnsignedShort();
112:
113: handlers = new Catch[numHandlers];
114:
115: for (int i = 0; i < numHandlers; i++) {
116: handlers[i] = readCatch(in);
117: }
118:
119: final int numAttributes = in.readUnsignedShort();
120:
121: attrs = new Attribute[numAttributes];
122:
123: for (int i = 0; i < numAttributes; i++) {
124: final int nameIndex = in.readUnsignedShort();
125: final int length = in.readInt();
126:
127: final Constant name = classInfo.constants()[nameIndex];
128:
129: if (name != null) {
130: if ("LineNumberTable".equals(name.value())) {
131: lineNumbers = new LineNumberTable(in, nameIndex,
132: length);
133: attrs[i] = lineNumbers;
134: } else if ("LocalVariableTable".equals(name.value())) {
135: locals = new LocalVariableTable(in, nameIndex,
136: length);
137: attrs[i] = locals;
138: }
139: }
140:
141: if (attrs[i] == null) {
142: attrs[i] = new GenericAttribute(in, nameIndex, length);
143: }
144: }
145: }
146:
147: /**
148: * Write the attribute to a data stream.
149: *
150: * @param out
151: * The data stream of the class file.
152: * @exception IOException
153: * If an error occurs while writing.
154: */
155: public void writeData(final DataOutputStream out)
156: throws IOException {
157: out.writeShort(maxStack);
158: out.writeShort(maxLocals);
159:
160: out.writeInt(code.length);
161: out.write(code, 0, code.length);
162:
163: out.writeShort(handlers.length);
164:
165: for (int i = 0; i < handlers.length; i++) {
166: writeCatch(out, handlers[i]);
167: }
168:
169: out.writeShort(attrs.length);
170:
171: for (int i = 0; i < attrs.length; i++) {
172: out.writeShort(attrs[i].nameIndex());
173: out.writeInt(attrs[i].length());
174: attrs[i].writeData(out);
175: }
176: }
177:
178: /**
179: * Read an exception handler attribute.
180: *
181: * @param in
182: * The data stream of the class file.
183: * @return A Catch attribute for the handler.
184: * @exception IOException
185: * If an error occurs while reading.
186: */
187: private Catch readCatch(final DataInputStream in)
188: throws IOException {
189: final int startPC = in.readUnsignedShort();
190: final int endPC = in.readUnsignedShort();
191: final int handlerPC = in.readUnsignedShort();
192: final int catchType = in.readUnsignedShort();
193:
194: return new Catch(startPC, endPC, handlerPC, catchType);
195: }
196:
197: /**
198: * Write an exception handler attribute.
199: *
200: * @param out
201: * The data stream of the class file.
202: * @param c
203: * A Catch attribute for the handler.
204: * @exception IOException
205: * If an error occurs while writing.
206: */
207: private void writeCatch(final DataOutputStream out, final Catch c)
208: throws IOException {
209: final int startPC = c.startPC();
210: final int endPC = c.endPC();
211: final int handlerPC = c.handlerPC();
212: final int catchType = c.catchTypeIndex();
213:
214: out.writeShort(startPC);
215: out.writeShort(endPC);
216: out.writeShort(handlerPC);
217: out.writeShort(catchType);
218: }
219:
220: /**
221: * Set the maximum height of the operand stack used by the code.
222: *
223: * @param maxStack
224: * The maximum height of the stack.
225: */
226: public void setMaxStack(final int maxStack) {
227: this .maxStack = maxStack;
228: }
229:
230: /**
231: * Set the maximum number of locals used by the code.
232: *
233: * @param maxLocals
234: * The maximum number of locals.
235: */
236: public void setMaxLocals(final int maxLocals) {
237: this .maxLocals = maxLocals;
238: }
239:
240: /**
241: * Get the maximum height of the operand stack used by the code.
242: *
243: * @return The maximum number of locals.
244: */
245: public int maxStack() {
246: return maxStack;
247: }
248:
249: /**
250: * Get the maximum number of locals used by the code.
251: *
252: * @return The maximum number of locals.
253: */
254: public int maxLocals() {
255: return maxLocals;
256: }
257:
258: /**
259: * Set the exception handlers in the method.
260: *
261: * @param handlers
262: * The handlers.
263: */
264: public void setExceptionHandlers(final Catch[] handlers) {
265: this .handlers = handlers;
266: }
267:
268: /**
269: * Get the length of the attribute.
270: *
271: * @return The length of the attribute.
272: */
273: public int length() {
274: int length = 2 + 2 + 4 + code.length + 2 + handlers.length * 8
275: + 2;
276:
277: for (int i = 0; i < attrs.length; i++) {
278: length += 2 + 4 + attrs[i].length();
279: }
280:
281: return length;
282: }
283:
284: /**
285: * Get the line number debug info for the code.
286: *
287: * @return The line number debug info for the code.
288: */
289: public LineNumberDebugInfo[] lineNumbers() {
290: if (lineNumbers != null) {
291: return lineNumbers.lineNumbers();
292: }
293:
294: return new LineNumberDebugInfo[0];
295: }
296:
297: /**
298: * Get the local variable debug info for the code.
299: *
300: * @return The local variable debug info for the code.
301: */
302: public LocalDebugInfo[] locals() {
303: if (locals != null) {
304: return locals.locals();
305: }
306:
307: return new LocalDebugInfo[0];
308: }
309:
310: /**
311: * Set the line number debug info for the code.
312: *
313: * @param lineNumbers
314: * The line number debug info for the code.
315: */
316: public void setLineNumbers(final LineNumberDebugInfo[] lineNumbers) {
317: if (lineNumbers == null) {
318: for (int i = 0; i < attrs.length; i++) {
319: if (this .lineNumbers == attrs[i]) {
320: final Attribute[] a = attrs;
321: attrs = new Attribute[a.length - 1];
322: System.arraycopy(a, 0, attrs, 0, i);
323: System.arraycopy(a, i + 1, attrs, i, attrs.length
324: - i);
325: break;
326: }
327: }
328:
329: this .lineNumbers = null;
330: } else if (this .lineNumbers != null) {
331: this .lineNumbers.setLineNumbers(lineNumbers);
332: }
333: }
334:
335: /**
336: * Set the local variable debug info for the code.
337: *
338: * @param locals
339: * The local variable debug info for the code.
340: */
341: public void setLocals(final LocalDebugInfo[] locals) {
342: if (locals == null) {
343: for (int i = 0; i < attrs.length; i++) {
344: if (this .locals == attrs[i]) {
345: final Attribute[] a = attrs;
346: attrs = new Attribute[a.length - 1];
347: System.arraycopy(a, 0, attrs, 0, i);
348: System.arraycopy(a, i + 1, attrs, i, attrs.length
349: - i);
350: break;
351: }
352: }
353:
354: this .locals = null;
355: } else if (this .locals != null) {
356: this .locals.setLocals(locals);
357: }
358: }
359:
360: /**
361: * Get the exception handlers in the method.
362: *
363: * @return The handlers.
364: */
365: public Catch[] exceptionHandlers() {
366: return handlers;
367: }
368:
369: /**
370: * Get the bytes of the code.
371: *
372: * @return The code.
373: */
374: public byte[] code() {
375: return code;
376: }
377:
378: /**
379: * Return the length of the code array
380: */
381: public int codeLength() {
382: return (code.length);
383: }
384:
385: /**
386: * Set the bytes of the code.
387: *
388: * @param code
389: * The code.
390: */
391: public void setCode(final byte[] code) {
392: this .code = code;
393: }
394:
395: /**
396: * Private constructor for cloning.
397: */
398: private Code(final Code other) {
399: super (other.nameIndex, other.length);
400:
401: this .classInfo = other.classInfo;
402: this .maxStack = other.maxStack;
403: this .maxLocals = other.maxLocals;
404:
405: this .code = new byte[other.code.length];
406: System
407: .arraycopy(other.code, 0, this .code, 0,
408: other.code.length);
409: this .handlers = new Catch[other.handlers.length];
410: for (int i = 0; i < other.handlers.length; i++) {
411: this .handlers[i] = (Catch) other.handlers[i].clone();
412: }
413:
414: if (other.lineNumbers != null) {
415: this .lineNumbers = (LineNumberTable) other.lineNumbers
416: .clone();
417: }
418:
419: if (other.locals != null) {
420: this .locals = (LocalVariableTable) other.locals.clone();
421: }
422:
423: this .attrs = new Attribute[other.attrs.length];
424: for (int i = 0; i < other.attrs.length; i++) {
425: this .attrs[i] = other.attrs[i];
426: }
427: }
428:
429: public Object clone() {
430: return (new Code(this ));
431: }
432:
433: /**
434: * Returns a string representation of the attribute.
435: */
436: public String toString() {
437: String x = "";
438:
439: if (handlers != null) {
440: for (int i = 0; i < handlers.length; i++) {
441: x += "\n " + handlers[i];
442: }
443: }
444:
445: /*
446: * for (int i = 0; i < attrs.length; i++) { x += "\n " + attrs[i]; }
447: */
448:
449: return "(code " + maxStack + " " + maxLocals + " "
450: + code.length + x + ")";
451: }
452: }
|