• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 package javassist.bytecode;
17 
18 import java.io.DataInputStream;
19 import java.io.DataOutputStream;
20 import java.io.IOException;
21 import java.util.List;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import 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  */
35 public 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      */
CodeAttribute(ConstPool cp, int stack, int locals, byte[] code, ExceptionTable etable)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      */
CodeAttribute(ConstPool cp, CodeAttribute src, Map classnames)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 
CodeAttribute(ConstPool cp, int name_id, DataInputStream in)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      */
copy(ConstPool newCp, Map classnames)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          */
RuntimeCopyException(String s)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      */
length()160     public int length() {
161         return 18 + info.length + exceptions.size() * 8
162                + AttributeInfo.getLength(attributes);
163     }
164 
write(DataOutputStream out)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      */
get()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      */
set(byte[] newinfo)191     public void set(byte[] newinfo) {
192         throw new UnsupportedOperationException("CodeAttribute.set()");
193     }
194 
renameClass(String oldname, String newname)195     void renameClass(String oldname, String newname) {
196         AttributeInfo.renameClass(attributes, oldname, newname);
197     }
198 
renameClass(Map classnames)199     void renameClass(Map classnames) {
200         AttributeInfo.renameClass(attributes, classnames);
201     }
202 
getRefClasses(Map classnames)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      */
getDeclaringClass()211     public String getDeclaringClass() {
212         ConstPool cp = getConstPool();
213         return cp.getClassName();
214     }
215 
216     /**
217      * Returns <code>max_stack</code>.
218      */
getMaxStack()219     public int getMaxStack() {
220         return maxStack;
221     }
222 
223     /**
224      * Sets <code>max_stack</code>.
225      */
setMaxStack(int value)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      */
computeMaxStack()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      */
getMaxLocals()245     public int getMaxLocals() {
246         return maxLocals;
247     }
248 
249     /**
250      * Sets <code>max_locals</code>.
251      */
setMaxLocals(int value)252     public void setMaxLocals(int value) {
253         maxLocals = value;
254     }
255 
256     /**
257      * Returns <code>code_length</code>.
258      */
getCodeLength()259     public int getCodeLength() {
260         return info.length;
261     }
262 
263     /**
264      * Returns <code>code[]</code>.
265      */
getCode()266     public byte[] getCode() {
267         return info;
268     }
269 
270     /**
271      * Sets <code>code[]</code>.
272      */
setCode(byte[] newinfo)273     void setCode(byte[] newinfo) { super.set(newinfo); }
274 
275     /**
276      * Makes a new iterator for reading this code attribute.
277      */
iterator()278     public CodeIterator iterator() {
279         return new CodeIterator(this);
280     }
281 
282     /**
283      * Returns <code>exception_table[]</code>.
284      */
getExceptionTable()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      */
getAttributes()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      */
getAttribute(String name)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      */
setAttribute(StackMapTable smt)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      */
setAttribute(StackMap sm)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      */
copyCode(ConstPool destCp, Map classnames, ExceptionTable etable, CodeAttribute destCa)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 
copyCode(byte[] code, int beginPos, int endPos, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map classnameMap)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 
copyConstPoolInfo(int i, byte[] code, ConstPool srcCp, byte[] newcode, ConstPool destCp, Map classnameMap)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 
doit(byte[] code, LdcEntry ldc, ExceptionTable etable, CodeAttribute ca)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      */
insertLocalVar(int where, int size)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      */
shiftIndex(CodeIterator ci, int lessThan, int delta)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 
shiftIndex8(CodeIterator ci, int index, int opcode, int lessThan, int delta)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 
shiftIndex0(CodeIterator ci, int index, int opcode, int lessThan, int delta, int opcode_i_0, int opcode_i)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