• 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.ByteArrayOutputStream;
19 import java.io.DataInputStream;
20 import java.io.IOException;
21 import java.util.Map;
22 
23 import javassist.CannotCompileException;
24 import javassist.bytecode.StackMapTable.InsertLocal;
25 import javassist.bytecode.StackMapTable.NewRemover;
26 import javassist.bytecode.StackMapTable.Shifter;
27 
28 /**
29  * Another <code>stack_map</code> attribute defined in CLDC 1.1 for J2ME.
30  *
31  * <p>This is an entry in the attributes table of a Code attribute.
32  * It was introduced by J2ME CLDC 1.1 (JSR 139) for pre-verification.
33  *
34  * <p>According to the CLDC specification, the sizes of some fields are not 16bit
35  * but 32bit if the code size is more than 64K or the number of the local variables
36  * is more than 64K.  However, for the J2ME CLDC technology, they are always 16bit.
37  * The implementation of the StackMap class assumes they are 16bit.
38  *
39  * @see MethodInfo#doPreverify
40  * @see StackMapTable
41  * @since 3.12
42  */
43 public class StackMap extends AttributeInfo {
44     /**
45      * The name of this attribute <code>"StackMap"</code>.
46      */
47     public static final String tag = "StackMap";
48 
49 
50     /**
51      * Constructs a <code>stack_map</code> attribute.
52      */
StackMap(ConstPool cp, byte[] newInfo)53     StackMap(ConstPool cp, byte[] newInfo) {
54         super(cp, tag, newInfo);
55     }
56 
StackMap(ConstPool cp, int name_id, DataInputStream in)57     StackMap(ConstPool cp, int name_id, DataInputStream in)
58         throws IOException
59     {
60         super(cp, name_id, in);
61     }
62 
63     /**
64      * Returns <code>number_of_entries</code>.
65      */
numOfEntries()66     public int numOfEntries() {
67     	return ByteArray.readU16bit(info, 0);
68     }
69 
70     /**
71      * <code>Top_variable_info.tag</code>.
72      */
73     public static final int TOP = 0;
74 
75     /**
76      * <code>Integer_variable_info.tag</code>.
77      */
78     public static final int INTEGER = 1;
79 
80     /**
81      * <code>Float_variable_info.tag</code>.
82      */
83     public static final int FLOAT = 2;
84 
85     /**
86      * <code>Double_variable_info.tag</code>.
87      */
88     public static final int DOUBLE = 3;
89 
90     /**
91      * <code>Long_variable_info.tag</code>.
92      */
93     public static final int LONG = 4;
94 
95     /**
96      * <code>Null_variable_info.tag</code>.
97      */
98     public static final int NULL = 5;
99 
100     /**
101      * <code>UninitializedThis_variable_info.tag</code>.
102      */
103     public static final int THIS = 6;
104 
105     /**
106      * <code>Object_variable_info.tag</code>.
107      */
108     public static final int OBJECT = 7;
109 
110     /**
111      * <code>Uninitialized_variable_info.tag</code>.
112      */
113     public static final int UNINIT = 8;
114 
115     /**
116      * Makes a copy.
117      */
copy(ConstPool newCp, Map classnames)118     public AttributeInfo copy(ConstPool newCp, Map classnames) {
119         Copier copier = new Copier(this, newCp, classnames);
120         copier.visit();
121         return copier.getStackMap();
122     }
123 
124     /**
125      * A code walker for a StackMap attribute.
126      */
127     public static class Walker {
128         byte[] info;
129 
130         /**
131          * Constructs a walker.
132          */
Walker(StackMap sm)133         public Walker(StackMap sm) {
134             info = sm.get();
135         }
136 
137         /**
138          * Visits each entry of the stack map frames.
139          */
visit()140         public void visit() {
141             int num = ByteArray.readU16bit(info, 0);
142             int pos = 2;
143             for (int i = 0; i < num; i++) {
144                 int offset = ByteArray.readU16bit(info, pos);
145                 int numLoc = ByteArray.readU16bit(info, pos + 2);
146                 pos = locals(pos + 4, offset, numLoc);
147                 int numStack = ByteArray.readU16bit(info, pos);
148                 pos = stack(pos + 2, offset, numStack);
149             }
150         }
151 
152         /**
153          * Invoked when <code>locals</code> of <code>stack_map_frame</code>
154          * is visited.
155          */
locals(int pos, int offset, int num)156         public int locals(int pos, int offset, int num) {
157             return typeInfoArray(pos, offset, num, true);
158         }
159 
160         /**
161          * Invoked when <code>stack</code> of <code>stack_map_frame</code>
162          * is visited.
163          */
stack(int pos, int offset, int num)164         public int stack(int pos, int offset, int num) {
165             return typeInfoArray(pos, offset, num, false);
166         }
167 
168         /**
169          * Invoked when an array of <code>verification_type_info</code> is
170          * visited.
171          *
172          * @param num       the number of elements.
173          * @param isLocals  true if this array is for <code>locals</code>.
174          *                  false if it is for <code>stack</code>.
175          */
typeInfoArray(int pos, int offset, int num, boolean isLocals)176         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
177             for (int k = 0; k < num; k++)
178                 pos = typeInfoArray2(k, pos);
179 
180             return pos;
181         }
182 
typeInfoArray2(int k, int pos)183         int typeInfoArray2(int k, int pos) {
184             byte tag = info[pos];
185             if (tag == OBJECT) {
186                 int clazz = ByteArray.readU16bit(info, pos + 1);
187                 objectVariable(pos, clazz);
188                 pos += 3;
189             }
190             else if (tag == UNINIT) {
191                 int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
192                 uninitialized(pos, offsetOfNew);
193                 pos += 3;
194             }
195             else {
196                 typeInfo(pos, tag);
197                 pos++;
198             }
199 
200             return pos;
201         }
202 
203         /**
204          * Invoked when an element of <code>verification_type_info</code>
205          * (except <code>Object_variable_info</code> and
206          * <code>Uninitialized_variable_info</code>) is visited.
207          */
typeInfo(int pos, byte tag)208         public void typeInfo(int pos, byte tag) {}
209 
210         /**
211          * Invoked when an element of type <code>Object_variable_info</code>
212          * is visited.
213          */
objectVariable(int pos, int clazz)214         public void objectVariable(int pos, int clazz) {}
215 
216         /**
217          * Invoked when an element of type <code>Uninitialized_variable_info</code>
218          * is visited.
219          */
uninitialized(int pos, int offset)220         public void uninitialized(int pos, int offset) {}
221     }
222 
223     static class Copier extends Walker {
224         byte[] dest;
225         ConstPool srcCp, destCp;
226         Map classnames;
227 
Copier(StackMap map, ConstPool newCp, Map classnames)228         Copier(StackMap map, ConstPool newCp, Map classnames) {
229             super(map);
230             srcCp = map.getConstPool();
231             dest = new byte[info.length];
232             destCp = newCp;
233             this.classnames = classnames;
234         }
235 
visit()236         public void visit() {
237             int num = ByteArray.readU16bit(info, 0);
238             ByteArray.write16bit(num, dest, 0);
239             super.visit();
240         }
241 
locals(int pos, int offset, int num)242         public int locals(int pos, int offset, int num) {
243             ByteArray.write16bit(offset, dest, pos - 4);
244             return super.locals(pos, offset, num);
245         }
246 
typeInfoArray(int pos, int offset, int num, boolean isLocals)247         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
248             ByteArray.write16bit(num, dest, pos - 2);
249             return super.typeInfoArray(pos, offset, num, isLocals);
250         }
251 
typeInfo(int pos, byte tag)252         public void typeInfo(int pos, byte tag) {
253             dest[pos] = tag;
254         }
255 
objectVariable(int pos, int clazz)256         public void objectVariable(int pos, int clazz) {
257             dest[pos] = OBJECT;
258             int newClazz = srcCp.copy(clazz, destCp, classnames);
259             ByteArray.write16bit(newClazz, dest, pos + 1);
260         }
261 
uninitialized(int pos, int offset)262         public void uninitialized(int pos, int offset) {
263             dest[pos] = UNINIT;
264             ByteArray.write16bit(offset, dest, pos + 1);
265         }
266 
getStackMap()267         public StackMap getStackMap() {
268             return new StackMap(destCp, dest);
269         }
270     }
271 
272     /**
273      * Updates this stack map table when a new local variable is inserted
274      * for a new parameter.
275      *
276      * @param index          the index of the added local variable.
277      * @param tag            the type tag of that local variable.
278      *                       It is available by <code>StackMapTable.typeTagOf(char)</code>.
279      * @param classInfo      the index of the <code>CONSTANT_Class_info</code> structure
280      *                       in a constant pool table.  This should be zero unless the tag
281      *                       is <code>ITEM_Object</code>.
282      *
283      * @see javassist.CtBehavior#addParameter(javassist.CtClass)
284      * @see StackMapTable#typeTagOf(char)
285      * @see ConstPool
286      */
insertLocal(int index, int tag, int classInfo)287     public void insertLocal(int index, int tag, int classInfo)
288         throws BadBytecode
289     {
290         byte[] data = new InsertLocal(this, index, tag, classInfo).doit();
291         this.set(data);
292     }
293 
294     static class SimpleCopy extends Walker {
295         Writer writer;
296 
SimpleCopy(StackMap map)297         SimpleCopy(StackMap map) {
298             super(map);
299             writer = new Writer();
300         }
301 
doit()302         byte[] doit() {
303             visit();
304             return writer.toByteArray();
305         }
306 
visit()307         public void visit() {
308             int num = ByteArray.readU16bit(info, 0);
309             writer.write16bit(num);
310             super.visit();
311         }
312 
locals(int pos, int offset, int num)313         public int locals(int pos, int offset, int num) {
314             writer.write16bit(offset);
315             return super.locals(pos, offset, num);
316         }
317 
typeInfoArray(int pos, int offset, int num, boolean isLocals)318         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
319             writer.write16bit(num);
320             return super.typeInfoArray(pos, offset, num, isLocals);
321         }
322 
typeInfo(int pos, byte tag)323         public void typeInfo(int pos, byte tag) {
324             writer.writeVerifyTypeInfo(tag, 0);
325         }
326 
objectVariable(int pos, int clazz)327         public void objectVariable(int pos, int clazz) {
328             writer.writeVerifyTypeInfo(OBJECT, clazz);
329         }
330 
uninitialized(int pos, int offset)331         public void uninitialized(int pos, int offset) {
332             writer.writeVerifyTypeInfo(UNINIT, offset);
333         }
334     }
335 
336     static class InsertLocal extends SimpleCopy {
337         private int varIndex;
338         private int varTag, varData;
339 
InsertLocal(StackMap map, int varIndex, int varTag, int varData)340         InsertLocal(StackMap map, int varIndex, int varTag, int varData) {
341             super(map);
342             this.varIndex = varIndex;
343             this.varTag = varTag;
344             this.varData = varData;
345         }
346 
typeInfoArray(int pos, int offset, int num, boolean isLocals)347         public int typeInfoArray(int pos, int offset, int num, boolean isLocals) {
348             if (!isLocals || num < varIndex)
349                 return super.typeInfoArray(pos, offset, num, isLocals);
350 
351             writer.write16bit(num + 1);
352             for (int k = 0; k < num; k++) {
353                 if (k == varIndex)
354                     writeVarTypeInfo();
355 
356                 pos = typeInfoArray2(k, pos);
357             }
358 
359             if (num == varIndex)
360                 writeVarTypeInfo();
361 
362             return pos;
363         }
364 
writeVarTypeInfo()365         private void writeVarTypeInfo() {
366             if (varTag == OBJECT)
367                 writer.writeVerifyTypeInfo(OBJECT, varData);
368             else if (varTag == UNINIT)
369                 writer.writeVerifyTypeInfo(UNINIT, varData);
370             else
371                 writer.writeVerifyTypeInfo(varTag, 0);
372         }
373     }
374 
shiftPc(int where, int gapSize, boolean exclusive)375     void shiftPc(int where, int gapSize, boolean exclusive)
376         throws BadBytecode
377     {
378         new Shifter(this, where, gapSize, exclusive).visit();
379     }
380 
381     static class Shifter extends Walker {
382         private int where, gap;
383         private boolean exclusive;
384 
Shifter(StackMap smt, int where, int gap, boolean exclusive)385         public Shifter(StackMap smt, int where, int gap, boolean exclusive) {
386             super(smt);
387             this.where = where;
388             this.gap = gap;
389             this.exclusive = exclusive;
390         }
391 
locals(int pos, int offset, int num)392         public int locals(int pos, int offset, int num) {
393             if (exclusive ? where <= offset : where < offset)
394                 ByteArray.write16bit(offset + gap, info, pos - 4);
395 
396             return super.locals(pos, offset, num);
397         }
398     }
399 
400     /**
401      * Undocumented method.  Do not use; internal-use only.
402      *
403      * <p>This method is for javassist.convert.TransformNew.
404      * It is called to update the stack map when
405      * the NEW opcode (and the following DUP) is removed.
406      *
407      * @param where     the position of the removed NEW opcode.
408      */
removeNew(int where)409      public void removeNew(int where) throws CannotCompileException {
410          byte[] data = new NewRemover(this, where).doit();
411          this.set(data);
412     }
413 
414     static class NewRemover extends SimpleCopy {
415         int posOfNew;
416 
NewRemover(StackMap map, int where)417         NewRemover(StackMap map, int where) {
418             super(map);
419             posOfNew = where;
420         }
421 
stack(int pos, int offset, int num)422         public int stack(int pos, int offset, int num) {
423             return stackTypeInfoArray(pos, offset, num);
424         }
425 
stackTypeInfoArray(int pos, int offset, int num)426         private int stackTypeInfoArray(int pos, int offset, int num) {
427             int p = pos;
428             int count = 0;
429             for (int k = 0; k < num; k++) {
430                 byte tag = info[p];
431                 if (tag == OBJECT)
432                     p += 3;
433                 else if (tag == UNINIT) {
434                     int offsetOfNew = ByteArray.readU16bit(info, p + 1);
435                     if (offsetOfNew == posOfNew)
436                         count++;
437 
438                     p += 3;
439                 }
440                 else
441                     p++;
442             }
443 
444             writer.write16bit(num - count);
445             for (int k = 0; k < num; k++) {
446                 byte tag = info[pos];
447                 if (tag == OBJECT) {
448                     int clazz = ByteArray.readU16bit(info, pos + 1);
449                     objectVariable(pos, clazz);
450                     pos += 3;
451                 }
452                 else if (tag == UNINIT) {
453                     int offsetOfNew = ByteArray.readU16bit(info, pos + 1);
454                     if (offsetOfNew != posOfNew)
455                         uninitialized(pos, offsetOfNew);
456 
457                     pos += 3;
458                 }
459                 else {
460                     typeInfo(pos, tag);
461                     pos++;
462                 }
463             }
464 
465             return pos;
466         }
467     }
468 
469     /**
470      * Prints this stack map.
471      */
print(java.io.PrintWriter out)472     public void print(java.io.PrintWriter out) {
473         new Printer(this, out).print();
474     }
475 
476     static class Printer extends Walker {
477         private java.io.PrintWriter writer;
478 
Printer(StackMap map, java.io.PrintWriter out)479         public Printer(StackMap map, java.io.PrintWriter out) {
480             super(map);
481             writer = out;
482         }
483 
print()484         public void print() {
485             int num = ByteArray.readU16bit(info, 0);
486             writer.println(num + " entries");
487             visit();
488         }
489 
locals(int pos, int offset, int num)490         public int locals(int pos, int offset, int num) {
491             writer.println("  * offset " + offset);
492             return super.locals(pos, offset, num);
493         }
494     }
495 
496     /**
497      * Internal use only.
498      */
499     public static class Writer {
500         // see javassist.bytecode.stackmap.MapMaker
501 
502         private ByteArrayOutputStream output;
503 
504         /**
505          * Constructs a writer.
506          */
Writer()507         public Writer() {
508             output = new ByteArrayOutputStream();
509         }
510 
511         /**
512          * Converts the written data into a byte array.
513          */
toByteArray()514         public byte[] toByteArray() {
515             return output.toByteArray();
516         }
517 
518         /**
519          * Converts to a <code>StackMap</code> attribute.
520          */
toStackMap(ConstPool cp)521         public StackMap toStackMap(ConstPool cp) {
522             return new StackMap(cp, output.toByteArray());
523         }
524 
525         /**
526          * Writes a <code>union verification_type_info</code> value.
527          *
528          * @param data      <code>cpool_index</code> or <code>offset</code>.
529          */
writeVerifyTypeInfo(int tag, int data)530         public void writeVerifyTypeInfo(int tag, int data) {
531             output.write(tag);
532             if (tag == StackMap.OBJECT || tag == StackMap.UNINIT)
533                 write16bit(data);
534         }
535 
536         /**
537          * Writes a 16bit value.
538          */
write16bit(int value)539         public void write16bit(int value) {
540             output.write((value >>> 8) & 0xff);
541             output.write(value & 0xff);
542         }
543     }
544 }
545