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    }