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    }