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