1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.bytecode;
17
18import java.io.DataInputStream;
19import java.io.DataOutputStream;
20import java.io.IOException;
21import java.util.List;
22import java.util.ArrayList;
23import java.util.Iterator;
24import java.util.Map;
25
26/**
27 * <code>Code_attribute</code>.
28 *
29 * <p>To browse the <code>code</code> field of
30 * a <code>Code_attribute</code> structure,
31 * use <code>CodeIterator</code>.
32 *
33 * @see CodeIterator
34 */
35public class CodeAttribute extends AttributeInfo implements Opcode {
36    /**
37     * The name of this attribute <code>"Code"</code>.
38     */
39    public static final String tag = "Code";
40
41    // code[] is stored in AttributeInfo.info.
42
43    private int maxStack;
44    private int maxLocals;
45    private ExceptionTable exceptions;
46    private ArrayList attributes;
47
48    /**
49     * Constructs a <code>Code_attribute</code>.
50     *
51     * @param cp        constant pool table
52     * @param stack     <code>max_stack</code>
53     * @param locals    <code>max_locals</code>
54     * @param code      <code>code[]</code>
55     * @param etable    <code>exception_table[]</code>
56     */
57    public CodeAttribute(ConstPool cp, int stack, int locals, byte[] code,
58                         ExceptionTable etable)
59    {
60        super(cp, tag);
61        maxStack = stack;
62        maxLocals = locals;
63        info = code;
64        exceptions = etable;
65        attributes = new ArrayList();
66    }
67
68    /**
69     * Constructs a copy of <code>Code_attribute</code>.
70     * Specified class names are replaced during the copy.
71     *
72     * @param cp                constant pool table.
73     * @param src               source Code attribute.
74     * @param classnames        pairs of replaced and substituted
75     *                          class names.
76     */
77    private CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames)
78        throws BadBytecode
79    {
80        super(cp, tag);
81
82        maxStack = src.getMaxStack();
83        maxLocals = src.getMaxLocals();
84        exceptions = src.getExceptionTable().copy(cp, classnames);
85        attributes = new ArrayList();
86        List src_attr = src.getAttributes();
87        int num = src_attr.size();
88        for (int i = 0; i < num; ++i) {
89            AttributeInfo ai = (AttributeInfo)src_attr.get(i);
90            attributes.add(ai.copy(cp, classnames));
91        }
92
93        info = src.copyCode(cp, classnames, exceptions, this);
94    }
95
96    CodeAttribute(ConstPool cp, int name_id, DataInputStream in)
97        throws IOException
98    {
99        super(cp, name_id, (byte[])null);
100        int attr_len = in.readInt();
101
102        maxStack = in.readUnsignedShort();
103        maxLocals = in.readUnsignedShort();
104
105        int code_len = in.readInt();
106        info = new byte[code_len];
107        in.readFully(info);
108
109        exceptions = new ExceptionTable(cp, in);
110
111        attributes = new ArrayList();
112        int num = in.readUnsignedShort();
113        for (int i = 0; i < num; ++i)
114            attributes.add(AttributeInfo.read(cp, in));
115    }
116
117    /**
118     * Makes a copy.  Class names are replaced according to the
119     * given <code>Map</code> object.
120     *
121     * @param newCp     the constant pool table used by the new copy.
122     * @param classnames        pairs of replaced and substituted
123     *                          class names.
124     * @exception RuntimeCopyException  if a <code>BadBytecode</code>
125     *                          exception is thrown, it is
126     *                          converted into
127     *                          <code>RuntimeCopyException</code>.
128     *
129     * @return <code>CodeAttribute</code> object.
130     */
131    public AttributeInfo copy(ConstPool newCp, Map classnames)
132        throws RuntimeCopyException
133    {
134        try {
135            return new CodeAttribute(newCp, this, classnames);
136        }
137        catch (BadBytecode e) {
138            throw new RuntimeCopyException("bad bytecode. fatal?");
139        }
140    }
141
142    /**
143     * An exception that may be thrown by <code>copy()</code>
144     * in <code>CodeAttribute</code>.
145     */
146    public static class RuntimeCopyException extends RuntimeException {
147        /**
148         * Constructs an exception.
149         */
150        public RuntimeCopyException(String s) {
151            super(s);
152        }
153    }
154
155    /**
156     * Returns the length of this <code>attribute_info</code>
157     * structure.
158     * The returned value is <code>attribute_length + 6</code>.
159     */
160    public int length() {
161        return 18 + info.length + exceptions.size() * 8
162               + AttributeInfo.getLength(attributes);
163    }
164
165    void write(DataOutputStream out) throws IOException {
166        out.writeShort(name);           // attribute_name_index
167        out.writeInt(length() - 6);     // attribute_length
168        out.writeShort(maxStack);       // max_stack
169        out.writeShort(maxLocals);      // max_locals
170        out.writeInt(info.length);      // code_length
171        out.write(info);                // code
172        exceptions.write(out);
173        out.writeShort(attributes.size());      // attributes_count
174        AttributeInfo.writeAll(attributes, out);        // attributes
175    }
176
177    /**
178     * This method is not available.
179     *
180     * @throws java.lang.UnsupportedOperationException  always thrown.
181     */
182    public byte[] get() {
183        throw new UnsupportedOperationException("CodeAttribute.get()");
184    }
185
186    /**
187     * This method is not available.
188     *
189     * @throws java.lang.UnsupportedOperationException  always thrown.
190     */
191    public void set(byte[] newinfo) {
192        throw new UnsupportedOperationException("CodeAttribute.set()");
193    }
194
195    void renameClass(String oldname, String newname) {
196        AttributeInfo.renameClass(attributes, oldname, newname);
197    }
198
199    void renameClass(Map classnames) {
200        AttributeInfo.renameClass(attributes, classnames);
201    }
202
203    void getRefClasses(Map classnames) {
204        AttributeInfo.getRefClasses(attributes, classnames);
205    }
206
207    /**
208     * Returns the name of the class declaring the method including
209     * this code attribute.
210     */
211    public String getDeclaringClass() {
212        ConstPool cp = getConstPool();
213        return cp.getClassName();
214    }
215
216    /**
217     * Returns <code>max_stack</code>.
218     */
219    public int getMaxStack() {
220        return maxStack;
221    }
222
223    /**
224     * Sets <code>max_stack</code>.
225     */
226    public void setMaxStack(int value) {
227        maxStack = value;
228    }
229
230    /**
231     * Computes the maximum stack size and sets <code>max_stack</code>
232     * to the computed size.
233     *
234     * @throws BadBytecode      if this method fails in computing.
235     * @return the newly computed value of <code>max_stack</code>
236     */
237    public int computeMaxStack() throws BadBytecode {
238        maxStack = new CodeAnalyzer(this).computeMaxStack();
239        return maxStack;
240    }
241
242    /**
243     * Returns <code>max_locals</code>.
244     */
245    public int getMaxLocals() {
246        return maxLocals;
247    }
248
249    /**
250     * Sets <code>max_locals</code>.
251     */
252    public void setMaxLocals(int value) {
253        maxLocals = value;
254    }
255
256    /**
257     * Returns <code>code_length</code>.
258     */
259    public int getCodeLength() {
260        return info.length;
261    }
262
263    /**
264     * Returns <code>code[]</code>.
265     */
266    public byte[] getCode() {
267        return info;
268    }
269
270    /**
271     * Sets <code>code[]</code>.
272     */
273    void setCode(byte[] newinfo) { super.set(newinfo); }
274
275    /**
276     * Makes a new iterator for reading this code attribute.
277     */
278    public CodeIterator iterator() {
279        return new CodeIterator(this);
280    }
281
282    /**
283     * Returns <code>exception_table[]</code>.
284     */
285    public ExceptionTable getExceptionTable() { return exceptions; }
286
287    /**
288     * Returns <code>attributes[]</code>.
289     * It returns a list of <code>AttributeInfo</code>.
290     * A new element can be added to the returned list
291     * and an existing element can be removed from the list.
292     *
293     * @see AttributeInfo
294     */
295    public List getAttributes() { return attributes; }
296
297    /**
298     * Returns the attribute with the specified name.
299     * If it is not found, this method returns null.
300     *
301     * @param name      attribute name
302     * @return          an <code>AttributeInfo</code> object or null.
303     */
304    public AttributeInfo getAttribute(String name) {
305        return AttributeInfo.lookup(attributes, name);
306    }
307
308    /**
309     * Adds a stack map table.  If another copy of stack map table
310     * is already contained, the old one is removed.
311     *
312     * @param smt       the stack map table added to this code attribute.
313     *                  If it is null, a new stack map is not added.
314     *                  Only the old stack map is removed.
315     */
316    public void setAttribute(StackMapTable smt) {
317        AttributeInfo.remove(attributes, StackMapTable.tag);
318        if (smt != null)
319            attributes.add(smt);
320    }
321
322    /**
323     * Adds a stack map table for J2ME (CLDC).  If another copy of stack map table
324     * is already contained, the old one is removed.
325     *
326     * @param sm        the stack map table added to this code attribute.
327     *                  If it is null, a new stack map is not added.
328     *                  Only the old stack map is removed.
329     * @since 3.12
330     */
331    public void setAttribute(StackMap sm) {
332        AttributeInfo.remove(attributes, StackMap.tag);
333        if (sm != null)
334            attributes.add(sm);
335    }
336
337    /**
338     * Copies code.
339     */
340    private byte[] copyCode(ConstPool destCp, Map classnames,
341                            ExceptionTable etable, CodeAttribute destCa)
342        throws BadBytecode
343    {
344        int len = getCodeLength();
345        byte[] newCode = new byte[len];
346        destCa.info = newCode;
347        LdcEntry ldc = copyCode(this.info, 0, len, this.getConstPool(),
348                                newCode, destCp, classnames);
349        return LdcEntry.doit(newCode, ldc, etable, destCa);
350    }
351
352    private static LdcEntry copyCode(byte[] code, int beginPos, int endPos,
353                                     ConstPool srcCp, byte[] newcode,
354                                     ConstPool destCp, Map classnameMap)
355        throws BadBytecode
356    {
357        int i2, index;
358        LdcEntry ldcEntry = null;
359
360        for (int i = beginPos; i < endPos; i = i2) {
361            i2 = CodeIterator.nextOpcode(code, i);
362            byte c = code[i];
363            newcode[i] = c;
364            switch (c & 0xff) {
365            case LDC_W :
366            case LDC2_W :
367            case GETSTATIC :
368            case PUTSTATIC :
369            case GETFIELD :
370            case PUTFIELD :
371            case INVOKEVIRTUAL :
372            case INVOKESPECIAL :
373            case INVOKESTATIC :
374            case NEW :
375            case ANEWARRAY :
376            case CHECKCAST :
377            case INSTANCEOF :
378                copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
379                                  classnameMap);
380                break;
381            case LDC :
382                index = code[i + 1] & 0xff;
383                index = srcCp.copy(index, destCp, classnameMap);
384                if (index < 0x100)
385                    newcode[i + 1] = (byte)index;
386                else {
387                    newcode[i] = NOP;
388                    newcode[i + 1] = NOP;
389                    LdcEntry ldc = new LdcEntry();
390                    ldc.where = i;
391                    ldc.index = index;
392                    ldc.next = ldcEntry;
393                    ldcEntry = ldc;
394                }
395                break;
396            case INVOKEINTERFACE :
397                copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
398                                  classnameMap);
399                newcode[i + 3] = code[i + 3];
400                newcode[i + 4] = code[i + 4];
401                break;
402            case MULTIANEWARRAY :
403                copyConstPoolInfo(i + 1, code, srcCp, newcode, destCp,
404                                  classnameMap);
405                newcode[i + 3] = code[i + 3];
406                break;
407            default :
408                while (++i < i2)
409                    newcode[i] = code[i];
410
411                break;
412            }
413        }
414
415        return ldcEntry;
416    }
417
418    private static void copyConstPoolInfo(int i, byte[] code, ConstPool srcCp,
419                                          byte[] newcode, ConstPool destCp,
420                                          Map classnameMap) {
421        int index = ((code[i] & 0xff) << 8) | (code[i + 1] & 0xff);
422        index = srcCp.copy(index, destCp, classnameMap);
423        newcode[i] = (byte)(index >> 8);
424        newcode[i + 1] = (byte)index;
425    }
426
427    static class LdcEntry {
428        LdcEntry next;
429        int where;
430        int index;
431
432        static byte[] doit(byte[] code, LdcEntry ldc, ExceptionTable etable,
433                           CodeAttribute ca)
434            throws BadBytecode
435        {
436            if (ldc != null)
437                code = CodeIterator.changeLdcToLdcW(code, etable, ca, ldc);
438
439            /* The original code was the following:
440
441               while (ldc != null) {
442                 int where = ldc.where;
443                 code = CodeIterator.insertGapCore0(code, where, 1, false, etable, ca);
444                 code[where] = (byte)Opcode.LDC_W;
445                 ByteArray.write16bit(ldc.index, code, where + 1);
446                 ldc = ldc.next;
447               }
448
449               But this code does not support a large method > 32KB.
450            */
451
452            return code;
453        }
454    }
455
456    /**
457     * Changes the index numbers of the local variables
458     * to append a new parameter.
459     * This method does not update <code>LocalVariableAttribute</code>,
460     * <code>StackMapTable</code>, or <code>StackMap</code>.
461     * These attributes must be explicitly updated.
462     *
463     * @param where         the index of the new parameter.
464     * @param size         the type size of the new parameter (1 or 2).
465     *
466     * @see LocalVariableAttribute#shiftIndex(int, int)
467     * @see StackMapTable#insertLocal(int, int, int)
468     * @see StackMap#insertLocal(int, int, int)
469     */
470    public void insertLocalVar(int where, int size) throws BadBytecode {
471        CodeIterator ci = iterator();
472        while (ci.hasNext())
473            shiftIndex(ci, where, size);
474
475        setMaxLocals(getMaxLocals() + size);
476    }
477
478    /**
479     * @param lessThan      If the index of the local variable is
480     *                      less than this value, it does not change.
481     *                      Otherwise, the index is increased.
482     * @param delta         the indexes of the local variables are
483     *                      increased by this value.
484     */
485    private static void shiftIndex(CodeIterator ci, int lessThan, int delta) throws BadBytecode {
486        int index = ci.next();
487        int opcode = ci.byteAt(index);
488        if (opcode < ILOAD)
489            return;
490        else if (opcode < IASTORE) {
491            if (opcode < ILOAD_0) {
492                // iload, lload, fload, dload, aload
493                shiftIndex8(ci, index, opcode, lessThan, delta);
494            }
495            else if (opcode < IALOAD) {
496                // iload_0, ..., aload_3
497                shiftIndex0(ci, index, opcode, lessThan, delta, ILOAD_0, ILOAD);
498            }
499            else if (opcode < ISTORE)
500                return;
501            else if (opcode < ISTORE_0) {
502                // istore, lstore, ...
503                shiftIndex8(ci, index, opcode, lessThan, delta);
504            }
505            else {
506                // istore_0, ..., astore_3
507                shiftIndex0(ci, index, opcode, lessThan, delta, ISTORE_0, ISTORE);
508            }
509        }
510        else if (opcode == IINC) {
511            int var = ci.byteAt(index + 1);
512            if (var < lessThan)
513                return;
514
515            var += delta;
516            if (var < 0x100)
517                ci.writeByte(var, index + 1);
518            else {
519                int plus = (byte)ci.byteAt(index + 2);
520                int pos = ci.insertExGap(3);
521                ci.writeByte(WIDE, pos - 3);
522                ci.writeByte(IINC, pos - 2);
523                ci.write16bit(var, pos - 1);
524                ci.write16bit(plus, pos + 1);
525            }
526        }
527        else if (opcode == RET)
528            shiftIndex8(ci, index, opcode, lessThan, delta);
529        else if (opcode == WIDE) {
530            int var = ci.u16bitAt(index + 2);
531            if (var < lessThan)
532                return;
533
534            var += delta;
535            ci.write16bit(var, index + 2);
536        }
537    }
538
539    private static void shiftIndex8(CodeIterator ci, int index, int opcode,
540                                    int lessThan, int delta)
541         throws BadBytecode
542    {
543        int var = ci.byteAt(index + 1);
544        if (var < lessThan)
545            return;
546
547        var += delta;
548        if (var < 0x100)
549            ci.writeByte(var, index + 1);
550        else {
551            int pos = ci.insertExGap(2);
552            ci.writeByte(WIDE, pos - 2);
553            ci.writeByte(opcode, pos - 1);
554            ci.write16bit(var, pos);
555        }
556    }
557
558    private static void shiftIndex0(CodeIterator ci, int index, int opcode,
559                                    int lessThan, int delta,
560                                    int opcode_i_0, int opcode_i)
561        throws BadBytecode
562    {
563        int var = (opcode - opcode_i_0) % 4;
564        if (var < lessThan)
565            return;
566
567        var += delta;
568        if (var < 4)
569            ci.writeByte(opcode + delta, index);
570        else {
571            opcode = (opcode - opcode_i_0) / 4 + opcode_i;
572            if (var < 0x100) {
573                int pos = ci.insertExGap(1);
574                ci.writeByte(opcode, pos - 1);
575                ci.writeByte(var, pos);
576            }
577            else {
578                int pos = ci.insertExGap(3);
579                ci.writeByte(WIDE, pos - 1);
580                ci.writeByte(opcode, pos);
581                ci.write16bit(var, pos + 1);
582            }
583        }
584    }
585}
586