001 package edu.rice.cs.cunit.classFile.attributes;
002
003 import edu.rice.cs.cunit.classFile.attributes.visitors.IAttributeVisitor;
004 import edu.rice.cs.cunit.classFile.code.instructions.LineNumberTable;
005 import edu.rice.cs.cunit.classFile.constantPool.AUTFPoolInfo;
006 import edu.rice.cs.cunit.classFile.constantPool.ConstantPool;
007 import edu.rice.cs.cunit.util.Types;
008
009 import java.io.*;
010
011 /**
012 * Represents the Code attribute in a class file.
013 *
014 * @author Mathias Ricken
015 */
016 public class CodeAttributeInfo extends AAttributeInfo {
017 /**
018 * Storage class for code properties.
019 */
020 public static class CodeProperties {
021 public int maxStack;
022 public int maxLocals;
023 public int codeLength;
024 public int exceptionTableLength;
025 public int attributesCount;
026
027 public CodeProperties(int maxStack,
028 int maxLocals,
029 int codeLength,
030 int exceptionTableLength,
031 int attributesCount) {
032 this.maxStack = maxStack;
033 this.maxLocals = maxLocals;
034 this.codeLength = codeLength;
035 this.exceptionTableLength = exceptionTableLength;
036 this.attributesCount = attributesCount;
037 }
038 }
039
040 public static class ExceptionTableEntry {
041 public int startPC;
042 public int endPC;
043 public int handlerPC;
044 public int catchType;
045
046 public ExceptionTableEntry(int startPC, int endPC, int handlerPC, int catchType) {
047 this.startPC = startPC;
048 this.endPC = endPC;
049 this.handlerPC = handlerPC;
050 this.catchType = catchType;
051 }
052 }
053
054 /**
055 * Properties read out from _data field.
056 */
057 protected CodeProperties _props = null;
058
059 /**
060 * Constructor.
061 *
062 * @param name attribute name
063 * @param data attribute data
064 * @param cp constant pool
065 *
066 * @throws ClassFormatError
067 */
068 public CodeAttributeInfo(AUTFPoolInfo name, byte[] data, ConstantPool cp) throws ClassFormatError {
069 super(name, data, cp);
070 }
071
072 /**
073 * Constructor.
074 *
075 * @param name attribute name, must be a UTF item "Code"
076 * @param maxStack maximum state size
077 * @param maxLocals maximum number of locals
078 * @param code bytecode
079 * @param exceptionTableEntries exception table entries
080 * @param attributes attributes
081 * @param cp constant pool
082 *
083 * @throws ClassFormatError
084 * @throws IOException
085 */
086 public CodeAttributeInfo(AUTFPoolInfo name, int maxStack, int maxLocals, byte[] code,
087 ExceptionTableEntry[] exceptionTableEntries, AAttributeInfo[] attributes,
088 ConstantPool cp) throws ClassFormatError, IOException {
089 super(name, null, cp);
090 ByteArrayOutputStream bos = new ByteArrayOutputStream();
091 DataOutputStream dos = new DataOutputStream(bos);
092 dos.writeShort(maxStack);
093 dos.writeShort(maxLocals);
094 dos.writeInt(0); // no code
095 dos.writeShort(0); // no exceptions
096 dos.writeShort(0); // no attributes
097 setData(bos.toByteArray());
098
099 setCode(code);
100 setExceptionTableEntries(exceptionTableEntries);
101 setAttributes(attributes);
102 }
103
104 /**
105 * Mutator for data.
106 *
107 * @param data data
108 */
109 public void setData(byte[] data) {
110 _props = null;
111 super.setData(data);
112 }
113
114 /**
115 * Return a copy of the code properties.
116 *
117 * @return code properties
118 *
119 * @throws ClassFormatError
120 */
121 public CodeProperties getProperties() throws ClassFormatError {
122 if (_props == null) {
123 int maxStack = Types.ushortFromBytes(_data, 0);
124 int maxLocals = Types.ushortFromBytes(_data, 2);
125 int codeLength = Types.uintFromBytes(_data, 4);
126 int exceptionTableLength = Types.ushortFromBytes(_data, 8 + codeLength);
127 int attributesCount = Types.ushortFromBytes(_data, 10 + codeLength + 8 * exceptionTableLength);
128 _props = new CodeProperties(maxStack, maxLocals, codeLength, exceptionTableLength, attributesCount);
129 }
130
131 return new CodeProperties(_props.maxStack, _props.maxLocals, _props.codeLength, _props.exceptionTableLength,
132 _props.attributesCount);
133 }
134
135 /**
136 * Set the code properties.
137 *
138 * @param maxStack new maximum state
139 * @param maxLocals new maximum locals
140 *
141 * @throws ClassFormatError
142 */
143 public void setProperties(int maxStack, int maxLocals) {
144 assert(maxStack <= 0xffff);
145 assert(maxLocals <= 0xffff);
146
147 getProperties();
148
149 _props.maxStack = maxStack;
150 _props.maxLocals = maxLocals;
151 Types.bytesFromShort((short)(maxStack & 0xffff), _data, 0);
152 Types.bytesFromShort((short)(maxLocals & 0xffff), _data, 2);
153 }
154
155 /**
156 * Return a copy of the code bytes.
157 *
158 * @return code bytes
159 */
160 public byte[] getCode() {
161 getProperties();
162
163 byte[] b = new byte[_props.codeLength];
164 System.arraycopy(_data, 8, b, 0, _props.codeLength);
165
166 return b;
167 }
168
169 /**
170 * Set the code bytes.
171 *
172 * @param code new code bytes
173 */
174 public void setCode(byte[] code) throws IllegalArgumentException {
175 if (code.length == 0) {
176 throw new IllegalArgumentException("Code block cannot be empty");
177 }
178
179 getProperties();
180
181 byte[] newData = new byte[_data.length - _props.codeLength + code.length];
182 System.arraycopy(_data, 0, newData, 0, 4);
183 Types.bytesFromInt(code.length, newData, 4);
184 System.arraycopy(code, 0, newData, 8, code.length);
185 System.arraycopy(_data, 8 + _props.codeLength, newData, 8 + code.length, _data.length - 8 - _props.codeLength);
186 setData(newData);
187 }
188
189 /**
190 * Return a copy of the exception table entries.
191 *
192 * @return entries
193 */
194 public ExceptionTableEntry[] getExceptionTableEntries() {
195 getProperties();
196
197 ExceptionTableEntry[] e = new ExceptionTableEntry[_props.exceptionTableLength];
198 for(int i = 0, index = 10 + _props.codeLength; i < _props.exceptionTableLength; ++i, index += 8) {
199 int startPC = Types.ushortFromBytes(_data, index);
200 int endPC = Types.ushortFromBytes(_data, index + 2);
201 int handlerPC = Types.ushortFromBytes(_data, index + 4);
202 int catchType = Types.ushortFromBytes(_data, index + 6);
203 e[i] = new ExceptionTableEntry(startPC, endPC, handlerPC, catchType);
204 }
205
206 return e;
207 }
208
209 /**
210 * Set the exception table entries.
211 *
212 * @param e entries
213 *
214 * @throws IllegalArgumentException
215 */
216 public void setExceptionTableEntries(ExceptionTableEntry[] e) throws IllegalArgumentException {
217 if (e.length > 0xffff) {
218 throw new IllegalArgumentException("Too many exception table entries, max=0xffff");
219 }
220
221 getProperties();
222
223 byte[] newData = new byte[_data.length - 8 * _props.exceptionTableLength + 8 * e.length];
224 System.arraycopy(_data, 0, newData, 0, 8 + _props.codeLength);
225 Types.bytesFromShort((short)e.length, newData, 8 + _props.codeLength);
226
227 for(int i = 0; i < e.length; ++i) {
228 Types.bytesFromShort((short)(e[i].startPC & 0xffff), newData, 10 + _props.codeLength + i * 8);
229 Types.bytesFromShort((short)(e[i].endPC & 0xffff), newData, 12 + _props.codeLength + i * 8);
230 Types.bytesFromShort((short)(e[i].handlerPC & 0xffff), newData, 14 + _props.codeLength + i * 8);
231 Types.bytesFromShort((short)(e[i].catchType & 0xffff), newData, 16 + _props.codeLength + i * 8);
232 }
233
234 System.arraycopy(_data,
235 10 + _props.codeLength + 8 * _props.exceptionTableLength,
236 newData,
237 10 + _props.codeLength + 8 * e.length,
238 _data.length - 10 - _props.codeLength - 8 * _props.exceptionTableLength);
239 setData(newData);
240 }
241
242 /**
243 * Return a copy of the attributes.
244 *
245 * @return attributes
246 *
247 * @throws ClassFormatError
248 */
249 public AAttributeInfo[] getAttributes() throws ClassFormatError {
250 getProperties();
251
252 AAttributeInfo[] a = new AAttributeInfo[_props.attributesCount];
253 byte[] b = new byte[_data.length - 12 - _props.codeLength - 8 * _props.exceptionTableLength];
254 System.arraycopy(_data,
255 12 + _props.codeLength + 8 * _props.exceptionTableLength,
256 b,
257 0,
258 b.length);
259 try {
260 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(b));
261 for(int i = 0; i < _props.attributesCount; ++i) {
262 a[i] = AAttributeInfo.read(dis, _constantPool);
263 }
264 }
265 catch(IOException e) {
266 throw new ClassFormatError("Could not get code attributes");
267 }
268
269 return a;
270 }
271
272 /**
273 * Sets the attribute list.
274 *
275 * @param a attributes
276 *
277 * @throws ClassFormatError
278 * @throws IllegalArgumentException
279 */
280 public void setAttributes(AAttributeInfo[] a) throws ClassFormatError, IllegalArgumentException {
281 if (a.length > 0xffff) {
282 throw new IllegalArgumentException("Too many atttributes, max=0xffff");
283 }
284
285 getProperties();
286
287 byte[] newData = new byte[0];
288 try {
289 ByteArrayOutputStream baos = new ByteArrayOutputStream();
290 DataOutputStream dos = new DataOutputStream(baos);
291 for(AAttributeInfo attr : a) {
292 attr.write(dos);
293 }
294
295 byte[] attr = baos.toByteArray();
296 newData = new byte[12 + _props.codeLength + 8 * _props.exceptionTableLength + attr.length];
297 System.arraycopy(_data, 0, newData, 0, 10 + _props.codeLength + 8 * _props.exceptionTableLength);
298 Types.bytesFromShort((short)a.length, newData, 10 + _props.codeLength + 8 * _props.exceptionTableLength);
299 System.arraycopy(attr, 0, newData, newData.length - attr.length, attr.length);
300 }
301 catch(IOException e) {
302 throw new ClassFormatError("Cannot set code attributes");
303 }
304
305 setData(newData);
306 }
307
308 /**
309 * Execute a visitor on this attribute.
310 *
311 * @param visitor visitor
312 * @param param visitor-specific parameter
313 *
314 * @return visitor-specific return value
315 */
316 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) {
317 return visitor.codeCase(this, param);
318 }
319
320 /**
321 * Return a human-readable version of this attribute.
322 *
323 * @return string
324 */
325 public String toString() {
326 getProperties();
327 StringBuilder x = new StringBuilder();
328 x.append("Code <" + _data.length + " bytes, " + _props.maxStack + " max stack, " +
329 _props.maxLocals + " max locals, " + _props.codeLength + " code bytes, " +
330 _props.exceptionTableLength + " exception table entries { ");
331
332 for (ExceptionTableEntry e: getExceptionTableEntries()) {
333 x.append("(pc="+e.startPC+".."+e.endPC+" handler="+e.handlerPC+" type="+e.catchType+") ");
334 }
335
336 x.append("}, " + _props.attributesCount + " attributes = { ");
337
338 boolean first = true;
339 for(AAttributeInfo a : getAttributes()) {
340 if (first) {
341 first = false;
342 }
343 else {
344 x.append(", ");
345 }
346 x.append(a.toString());
347 }
348
349 x.append("} >");
350
351 return x.toString();
352 }
353
354 /**
355 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them.
356 *
357 * NOTE: This does not transform branch targets in the code.
358 *
359 * @param startPC program counter to start at
360 * @param deltaPC change in program counter values
361 */
362 public void adjustPC(int startPC, int deltaPC) {
363 // adjust PCs in exception table
364 ExceptionTableEntry[] exceptions = getExceptionTableEntries();
365 for(ExceptionTableEntry exc : exceptions) {
366 if (exc.startPC >= startPC) {
367 // Debug.out.println("Adjusted CodeAttribute.exceptions[].startPC: "+exc.startPC+" --> "+(exc.startPC+deltaPC));
368 exc.startPC += deltaPC;
369 }
370 if (exc.endPC >= startPC) {
371 // Debug.out.println("Adjusted CodeAttribute.exceptions[].endPC: "+exc.endPC+" --> "+(exc.endPC+deltaPC));
372 exc.endPC += deltaPC;
373 }
374 if (exc.handlerPC >= startPC) {
375 // Debug.out.println("Adjusted CodeAttribute.exceptions[].handlerPC: "+exc.handlerPC+" --> "+(exc.handlerPC+deltaPC));
376 exc.handlerPC += deltaPC;
377 }
378 }
379 setExceptionTableEntries(exceptions);
380
381 // recursively adjust PC in all contained attributes
382 AAttributeInfo[] attributes = getAttributes();
383 for(AAttributeInfo attr : attributes) {
384 attr.adjustPC(startPC, deltaPC);
385 }
386 setAttributes(attributes);
387 }
388
389 /**
390 * Translate the program counter values contained in this attribute from an old line number table to a new one.
391 *
392 * @param index critical point (insertion or deletion point)
393 * @param deltaIndex delta value to add to all old line numbers greater than the critical point
394 * @param oldLnt old line number table
395 * @param newLnt new line number table
396 */
397 public void translatePC(int index, int deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) {
398 // adjust PCs in exception table
399 ExceptionTableEntry[] exceptions = getExceptionTableEntries();
400 for(ExceptionTableEntry exc : exceptions) {
401 int oldLineNo = oldLnt.getLineNumber(exc.startPC);
402 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
403 exc.startPC = newLnt.getPC(oldLineNo);
404
405 oldLineNo = oldLnt.getLineNumber(exc.endPC);
406 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
407 exc.endPC = newLnt.getPC(oldLineNo);
408
409 oldLineNo = oldLnt.getLineNumber(exc.handlerPC);
410 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
411 exc.handlerPC = newLnt.getPC(oldLineNo);
412 }
413 setExceptionTableEntries(exceptions);
414
415 // recursively adjust PC in all contained attributes
416 AAttributeInfo[] attributes = getAttributes();
417 for(AAttributeInfo attr : attributes) {
418 attr.translatePC(index, deltaIndex, oldLnt, newLnt);
419 }
420 setAttributes(attributes);
421 }
422
423 /**
424 * Creates and returns a copy of this object.
425 */
426 public Object clone() throws CloneNotSupportedException {
427 return super.clone();
428 }
429
430 /**
431 * Returns the name of the attribute as it appears in the class file.
432 * @return name of the attribute.
433 */
434 public static String getAttributeName() {
435 return "Code";
436 }
437 }