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