001 package sysModel.classFile.attributes;
002
003 import sysModel.classFile.attributes.visitors.IAttributeVisitor;
004 import sysModel.classFile.code.instructions.LineNumberTable;
005 import sysModel.classFile.constantPool.AUTFPoolInfo;
006 import sysModel.classFile.constantPool.ConstantPool;
007 import sysModel.classFile.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 short maxStack;
022 public short maxLocals;
023 public int codeLength;
024 public short exceptionTableLength;
025 public short attributesCount;
026
027 public CodeProperties(short maxStack,
028 short maxLocals,
029 int codeLength,
030 short exceptionTableLength,
031 short 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 short startPC;
042 public short endPC;
043 public short handlerPC;
044 public short catchType;
045
046 public ExceptionTableEntry(short startPC, short endPC, short handlerPC, short 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, short maxStack, short 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 (null == _props) {
123 short maxStack = Types.ushortFromBytes(_data, 0);
124 short maxLocals = Types.ushortFromBytes(_data, 2);
125 int codeLength = Types.uintFromBytes(_data, 4);
126 short exceptionTableLength = Types.ushortFromBytes(_data, 8 + codeLength);
127 short 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(short maxStack, short maxLocals) {
144 getProperties();
145
146 Types.bytesFromShort(_props.maxStack = maxStack, _data, 0);
147 Types.bytesFromShort(_props.maxLocals = maxLocals, _data, 2);
148 }
149
150 /**
151 * Return a copy of the code bytes.
152 *
153 * @return code bytes
154 */
155 public byte[] getCode() {
156 getProperties();
157
158 byte[] b = new byte[_props.codeLength];
159 System.arraycopy(_data, 8, b, 0, _props.codeLength);
160
161 return b;
162 }
163
164 /**
165 * Set the code bytes.
166 *
167 * @param code new code bytes
168 */
169 public void setCode(byte[] code) throws IllegalArgumentException {
170 if (0 == code.length) {
171 throw new IllegalArgumentException("Code block cannot be empty");
172 }
173
174 getProperties();
175
176 byte[] newData = new byte[_data.length - _props.codeLength + code.length];
177 System.arraycopy(_data, 0, newData, 0, 4);
178 Types.bytesFromInt(code.length, newData, 4);
179 System.arraycopy(code, 0, newData, 8, code.length);
180 System.arraycopy(_data, 8 + _props.codeLength, newData, 8 + code.length, _data.length - 8 - _props.codeLength);
181 setData(newData);
182 }
183
184 /**
185 * Return a copy of the exception table entries.
186 *
187 * @return entries
188 */
189 public ExceptionTableEntry[] getExceptionTableEntries() {
190 getProperties();
191
192 ExceptionTableEntry[] e = new ExceptionTableEntry[_props.exceptionTableLength];
193 for(int i = 0, index = 10 + _props.codeLength; i < _props.exceptionTableLength; ++i, index += 8) {
194 short startPC = Types.ushortFromBytes(_data, index);
195 short endPC = Types.ushortFromBytes(_data, index + 2);
196 short handlerPC = Types.ushortFromBytes(_data, index + 4);
197 short catchType = Types.ushortFromBytes(_data, index + 6);
198 e[i] = new ExceptionTableEntry(startPC, endPC, handlerPC, catchType);
199 }
200
201 return e;
202 }
203
204 /**
205 * Set the exception table entries.
206 *
207 * @param e entries
208 *
209 * @throws IllegalArgumentException
210 */
211 public void setExceptionTableEntries(ExceptionTableEntry[] e) throws IllegalArgumentException {
212 if (0xffff < e.length) {
213 throw new IllegalArgumentException("Too many exception table entries, max=0xffff");
214 }
215
216 getProperties();
217
218 byte[] newData = new byte[_data.length - 8 * _props.exceptionTableLength + 8 * e.length];
219 System.arraycopy(_data, 0, newData, 0, 8 + _props.codeLength);
220 Types.bytesFromShort((short)e.length, newData, 8 + _props.codeLength);
221
222 for(int i = 0; i < e.length; ++i) {
223 Types.bytesFromShort(e[i].startPC, newData, 10 + _props.codeLength + i * 8);
224 Types.bytesFromShort(e[i].endPC, newData, 12 + _props.codeLength + i * 8);
225 Types.bytesFromShort(e[i].handlerPC, newData, 14 + _props.codeLength + i * 8);
226 Types.bytesFromShort(e[i].catchType, newData, 16 + _props.codeLength + i * 8);
227 }
228
229 System.arraycopy(_data,
230 10 + _props.codeLength + 8 * _props.exceptionTableLength,
231 newData,
232 10 + _props.codeLength + 8 * e.length,
233 _data.length - 10 - _props.codeLength - 8 * _props.exceptionTableLength);
234 setData(newData);
235 }
236
237 /**
238 * Return a copy of the attributes.
239 *
240 * @return attributes
241 *
242 * @throws ClassFormatError
243 */
244 public AAttributeInfo[] getAttributes() throws ClassFormatError {
245 getProperties();
246
247 AAttributeInfo[] a = new AAttributeInfo[_props.attributesCount];
248 byte[] b = new byte[_data.length - 12 - _props.codeLength - 8 * _props.exceptionTableLength];
249 System.arraycopy(_data,
250 12 + _props.codeLength + 8 * _props.exceptionTableLength,
251 b,
252 0,
253 b.length);
254 try {
255 DataInputStream dis = new DataInputStream(new ByteArrayInputStream(b));
256 for(int i = 0; i < _props.attributesCount; ++i) {
257 a[i] = AAttributeInfo.read(dis, _constantPool);
258 }
259 }
260 catch(IOException e) {
261 throw new ClassFormatError("Could not get code attributes: "+e);
262 }
263
264 return a;
265 }
266
267 /**
268 * Sets the attribute list.
269 *
270 * @param a attributes
271 *
272 * @throws ClassFormatError
273 * @throws IllegalArgumentException
274 */
275 public void setAttributes(AAttributeInfo[] a) throws ClassFormatError, IllegalArgumentException {
276 if (0xffff < a.length) {
277 throw new IllegalArgumentException("Too many atttributes, max=0xffff");
278 }
279
280 getProperties();
281
282 byte[] newData = new byte[0];
283 try {
284 ByteArrayOutputStream baos = new ByteArrayOutputStream();
285 DataOutputStream dos = new DataOutputStream(baos);
286 for(AAttributeInfo attr : a) {
287 attr.write(dos);
288 }
289
290 byte[] attr = baos.toByteArray();
291 newData = new byte[12 + _props.codeLength + 8 * _props.exceptionTableLength + attr.length];
292 System.arraycopy(_data, 0, newData, 0, 10 + _props.codeLength + 8 * _props.exceptionTableLength);
293 Types.bytesFromShort((short)a.length, newData, 10 + _props.codeLength + 8 * _props.exceptionTableLength);
294 System.arraycopy(attr, 0, newData, newData.length - attr.length, attr.length);
295 }
296 catch(IOException e) {
297 throw new ClassFormatError("Cannot set code attributes: "+e);
298 }
299
300 setData(newData);
301 }
302
303 /**
304 * Execute a visitor on this attribute.
305 *
306 * @param visitor visitor
307 * @param param visitor-specific parameter
308 *
309 * @return visitor-specific return value
310 */
311 public <R, D> R execute(IAttributeVisitor<R, D> visitor, D param) {
312 return visitor.codeCase(this, param);
313 }
314
315 /**
316 * Return a human-readable version of this attribute.
317 *
318 * @return string
319 */
320 public String toString() {
321 getProperties();
322 StringBuffer x = new StringBuffer();
323 x.append("Code <" + _data.length + " bytes, " + _props.maxStack + " max stack, " +
324 _props.maxLocals + " max locals, " + _props.codeLength + " code bytes, " +
325 _props.exceptionTableLength + " exception table entries { ");
326
327 for (ExceptionTableEntry e: getExceptionTableEntries()) {
328 x.append("(pc="+e.startPC+".."+e.endPC+" handler="+e.handlerPC+" type="+e.catchType+") ");
329 }
330
331 x.append("}, " + _props.attributesCount + " attributes = { ");
332
333 boolean first = true;
334 for(AAttributeInfo a : getAttributes()) {
335 if (first) {
336 first = false;
337 }
338 else {
339 x.append(", ");
340 }
341 x.append(a.toString());
342 }
343
344 x.append("} >");
345
346 return x.toString();
347 }
348
349 /**
350 * Adjust program counter values contained in this attribute, starting at startPC, by adding deltaPC to them.
351 *
352 * NOTE: This does not transform branch targets in the code.
353 *
354 * @param startPC program counter to start at
355 * @param deltaPC change in program counter values
356 */
357 public void adjustPC(short startPC, short deltaPC) {
358 // adjust PCs in exception table
359 ExceptionTableEntry[] exceptions = getExceptionTableEntries();
360 for(ExceptionTableEntry exc : exceptions) {
361 if (exc.startPC >= startPC) {
362 // Debug.out.println("Adjusted CodeAttribute.exceptions[].startPC: "+exc.startPC+" --> "+(exc.startPC+deltaPC));
363 exc.startPC += deltaPC;
364 }
365 if (exc.endPC >= startPC) {
366 // Debug.out.println("Adjusted CodeAttribute.exceptions[].endPC: "+exc.endPC+" --> "+(exc.endPC+deltaPC));
367 exc.endPC += deltaPC;
368 }
369 if (exc.handlerPC >= startPC) {
370 // Debug.out.println("Adjusted CodeAttribute.exceptions[].handlerPC: "+exc.handlerPC+" --> "+(exc.handlerPC+deltaPC));
371 exc.handlerPC += deltaPC;
372 }
373 }
374 setExceptionTableEntries(exceptions);
375
376 // recursively adjust PC in all contained attributes
377 AAttributeInfo[] attributes = getAttributes();
378 for(AAttributeInfo attr : attributes) {
379 attr.adjustPC(startPC, deltaPC);
380 }
381 setAttributes(attributes);
382 }
383
384 /**
385 * Translate the program counter values contained in this attribute from an old line number table to a new one.
386 *
387 * @param index critical point (insertion or deletion point)
388 * @param deltaIndex delta value to add to all old line numbers greater than the critical point
389 * @param oldLnt old line number table
390 * @param newLnt new line number table
391 */
392 public void translatePC(short index, short deltaIndex, LineNumberTable oldLnt, LineNumberTable newLnt) {
393 // adjust PCs in exception table
394 ExceptionTableEntry[] exceptions = getExceptionTableEntries();
395 for(ExceptionTableEntry exc : exceptions) {
396 int oldLineNo = oldLnt.getLineNumber(exc.startPC);
397 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
398 exc.startPC = (short)newLnt.getPC(oldLineNo);
399
400 oldLineNo = oldLnt.getLineNumber(exc.endPC);
401 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
402 exc.endPC = (short)newLnt.getPC(oldLineNo);
403
404 oldLineNo = oldLnt.getLineNumber(exc.handlerPC);
405 oldLineNo += (oldLineNo > index) ? deltaIndex : 0;
406 exc.handlerPC = (short)newLnt.getPC(oldLineNo);
407 }
408 setExceptionTableEntries(exceptions);
409
410 // recursively adjust PC in all contained attributes
411 AAttributeInfo[] attributes = getAttributes();
412 for(AAttributeInfo attr : attributes) {
413 attr.translatePC(index, deltaIndex, oldLnt, newLnt);
414 }
415 setAttributes(attributes);
416 }
417
418 /**
419 * Creates and returns a copy of this object.
420 */
421 public Object clone() throws CloneNotSupportedException {
422 return super.clone();
423 }
424
425 /**
426 * Returns the name of the attribute as it appears in the class file.
427 * @return name of the attribute.
428 */
429 public static String getAttributeName() {
430 return "Code";
431 }
432 }