001: /*
002: * Copyright 2004 Brian S O'Neill
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.cojen.classfile.attribute;
018:
019: import java.util.ArrayList;
020: import java.util.List;
021: import java.io.DataInput;
022: import java.io.DataOutput;
023: import java.io.IOException;
024: import org.cojen.classfile.Attribute;
025: import org.cojen.classfile.AttributeFactory;
026: import org.cojen.classfile.CodeBuffer;
027: import org.cojen.classfile.ConstantPool;
028: import org.cojen.classfile.ExceptionHandler;
029: import org.cojen.classfile.LocalVariable;
030: import org.cojen.classfile.Location;
031:
032: /**
033: * This class corresponds to the Code_attribute structure as defined in
034: * section 4.7.4 of <i>The Java Virtual Machine Specification</i>.
035: * To make it easier to create bytecode for the CodeAttr, use the
036: * CodeBuilder.
037: *
038: * @author Brian S O'Neill
039: * @see cojen.classfile.Opcode
040: * @see cojen.classfile.CodeBuilder
041: */
042: public class CodeAttr extends Attribute {
043:
044: private CodeBuffer mCodeBuffer;
045: private List mAttributes = new ArrayList(2);
046:
047: private LineNumberTableAttr mLineNumberTable;
048: private LocalVariableTableAttr mLocalVariableTable;
049:
050: private LineNumberTableAttr mOldLineNumberTable;
051: private LocalVariableTableAttr mOldLocalVariableTable;
052:
053: public CodeAttr(ConstantPool cp) {
054: super (cp, CODE);
055: }
056:
057: public CodeAttr(ConstantPool cp, String name) {
058: super (cp, name);
059: }
060:
061: public CodeAttr(ConstantPool cp, String name, int length,
062: DataInput din, AttributeFactory attrFactory)
063: throws IOException {
064: super (cp, name);
065:
066: final int maxStackDepth = din.readUnsignedShort();
067: final int maxLocals = din.readUnsignedShort();
068:
069: final byte[] byteCodes = new byte[din.readInt()];
070: din.readFully(byteCodes);
071:
072: int exceptionHandlerCount = din.readUnsignedShort();
073: final ExceptionHandler[] handlers = new ExceptionHandler[exceptionHandlerCount];
074:
075: for (int i = 0; i < exceptionHandlerCount; i++) {
076: handlers[i] = ExceptionHandler.readFrom(cp, din);
077: }
078:
079: mCodeBuffer = new CodeBuffer() {
080: public int getMaxStackDepth() {
081: return maxStackDepth;
082: }
083:
084: public int getMaxLocals() {
085: return maxLocals;
086: }
087:
088: public byte[] getByteCodes() {
089: return (byte[]) byteCodes.clone();
090: }
091:
092: public ExceptionHandler[] getExceptionHandlers() {
093: return (ExceptionHandler[]) handlers.clone();
094: }
095: };
096:
097: int attributeCount = din.readUnsignedShort();
098: for (int i = 0; i < attributeCount; i++) {
099: addAttribute(Attribute.readFrom(cp, din, attrFactory));
100: }
101: }
102:
103: /**
104: * Returns null if no CodeBuffer is defined for this CodeAttr.
105: */
106: public CodeBuffer getCodeBuffer() {
107: return mCodeBuffer;
108: }
109:
110: /**
111: * As a side effect of calling this method, new line number and local
112: * variable tables are created.
113: */
114: public void setCodeBuffer(CodeBuffer code) {
115: mCodeBuffer = code;
116: mOldLineNumberTable = mLineNumberTable;
117: mOldLocalVariableTable = mLocalVariableTable;
118: mAttributes.remove(mLineNumberTable);
119: mAttributes.remove(mLocalVariableTable);
120: mLineNumberTable = null;
121: mLocalVariableTable = null;
122: }
123:
124: /**
125: * Returns the line number in the source code from the given bytecode
126: * address (start_pc).
127: *
128: * @return -1 if no line number is mapped for the start_pc.
129: */
130: public int getLineNumber(Location start) {
131: LineNumberTableAttr table = mOldLineNumberTable;
132: if (table == null) {
133: table = mLineNumberTable;
134: }
135:
136: if (table == null || start.getLocation() < 0) {
137: return -1;
138: } else {
139: return table.getLineNumber(start);
140: }
141: }
142:
143: /**
144: * Map a bytecode address (start_pc) to a line number in the source code
145: * as a debugging aid.
146: */
147: public void mapLineNumber(Location start, int line_number) {
148: if (mLineNumberTable == null) {
149: addAttribute(new LineNumberTableAttr(getConstantPool()));
150: }
151: mLineNumberTable.addEntry(start, line_number);
152: }
153:
154: /**
155: * Indicate a local variable's use information be recorded in the
156: * ClassFile as a debugging aid. If the LocalVariable doesn't provide
157: * both a start and end location, then its information is not recorded.
158: * This method should be called at most once per LocalVariable instance.
159: */
160: public void localVariableUse(LocalVariable localVar) {
161: if (mLocalVariableTable == null) {
162: addAttribute(new LocalVariableTableAttr(getConstantPool()));
163: }
164: mLocalVariableTable.addEntry(localVar);
165: }
166:
167: public void addAttribute(Attribute attr) {
168: if (attr instanceof LineNumberTableAttr) {
169: if (mLineNumberTable != null) {
170: mAttributes.remove(mLineNumberTable);
171: }
172: mLineNumberTable = (LineNumberTableAttr) attr;
173: } else if (attr instanceof LocalVariableTableAttr) {
174: if (mLocalVariableTable != null) {
175: mAttributes.remove(mLocalVariableTable);
176: }
177: mLocalVariableTable = (LocalVariableTableAttr) attr;
178: }
179:
180: mAttributes.add(attr);
181: }
182:
183: public Attribute[] getAttributes() {
184: Attribute[] attrs = new Attribute[mAttributes.size()];
185: return (Attribute[]) mAttributes.toArray(attrs);
186: }
187:
188: /**
189: * Returns the length (in bytes) of this object in the class file.
190: */
191: public int getLength() {
192: int length = 12;
193:
194: if (mCodeBuffer != null) {
195: length += mCodeBuffer.getByteCodes().length;
196: ExceptionHandler[] handlers = mCodeBuffer
197: .getExceptionHandlers();
198: if (handlers != null) {
199: length += 8 * handlers.length;
200: }
201: }
202:
203: int size = mAttributes.size();
204: for (int i = 0; i < size; i++) {
205: length += ((Attribute) mAttributes.get(i)).getLength();
206: length += 6; // attributes have an intial 6 byte length
207: }
208:
209: return length;
210: }
211:
212: public void writeDataTo(DataOutput dout) throws IOException {
213: if (mCodeBuffer == null) {
214: throw new IllegalStateException(
215: "CodeAttr has no CodeBuffer set");
216: }
217:
218: ExceptionHandler[] handlers = mCodeBuffer
219: .getExceptionHandlers();
220:
221: dout.writeShort(mCodeBuffer.getMaxStackDepth());
222: dout.writeShort(mCodeBuffer.getMaxLocals());
223:
224: byte[] byteCodes = mCodeBuffer.getByteCodes();
225: dout.writeInt(byteCodes.length);
226: dout.write(byteCodes);
227:
228: if (handlers != null) {
229: int exceptionHandlerCount = handlers.length;
230: dout.writeShort(exceptionHandlerCount);
231:
232: for (int i = 0; i < exceptionHandlerCount; i++) {
233: handlers[i].writeTo(dout);
234: }
235: } else {
236: dout.writeShort(0);
237: }
238:
239: int size = mAttributes.size();
240: dout.writeShort(size);
241: for (int i = 0; i < size; i++) {
242: Attribute attr = (Attribute) mAttributes.get(i);
243: attr.writeTo(dout);
244: }
245:
246: mOldLineNumberTable = null;
247: mOldLocalVariableTable = null;
248: }
249: }
|