• 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.stackmap;
17 
18 import javassist.ClassPool;
19 import javassist.bytecode.*;
20 
21 /**
22  * Stack map maker.
23  */
24 public class MapMaker extends Tracer {
25     /*
26     public static void main(String[] args) throws Exception {
27         boolean useMain2 = args[0].equals("0");
28         if (useMain2 && args.length > 1) {
29             main2(args);
30             return;
31         }
32 
33         for (int i = 0; i < args.length; i++)
34             main1(args[i]);
35     }
36 
37     public static void main1(String className) throws Exception {
38         ClassPool cp = ClassPool.getDefault();
39         //javassist.CtClass cc = cp.get(className);
40         javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(className));
41         System.out.println(className);
42         ClassFile cf = cc.getClassFile();
43         java.util.List minfos = cf.getMethods();
44         for (int i = 0; i < minfos.size(); i++) {
45             MethodInfo minfo = (MethodInfo)minfos.get(i);
46             CodeAttribute ca = minfo.getCodeAttribute();
47             if (ca != null)
48                 ca.setAttribute(make(cp, minfo));
49         }
50 
51         cc.writeFile("tmp");
52     }
53 
54     public static void main2(String[] args) throws Exception {
55         ClassPool cp = ClassPool.getDefault();
56         //javassist.CtClass cc = cp.get(args[1]);
57         javassist.CtClass cc = cp.makeClass(new java.io.FileInputStream(args[1]));
58         MethodInfo minfo;
59         if (args[2].equals("_init_"))
60             minfo = cc.getDeclaredConstructors()[0].getMethodInfo();
61             // minfo = cc.getClassInitializer().getMethodInfo();
62         else
63             minfo = cc.getDeclaredMethod(args[2]).getMethodInfo();
64 
65         CodeAttribute ca = minfo.getCodeAttribute();
66         if (ca == null) {
67             System.out.println("abstarct method");
68             return;
69         }
70 
71         TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, false);
72         MapMaker mm = new MapMaker(cp, minfo, ca);
73         mm.make(blocks, ca.getCode());
74         for (int i = 0; i < blocks.length; i++)
75             System.out.println(blocks[i]);
76     }
77     */
78 
79     /**
80      * Computes the stack map table of the given method and returns it.
81      * It returns null if the given method does not have to have a
82      * stack map table.
83      */
make(ClassPool classes, MethodInfo minfo)84     public static StackMapTable make(ClassPool classes, MethodInfo minfo)
85         throws BadBytecode
86     {
87         CodeAttribute ca = minfo.getCodeAttribute();
88         if (ca == null)
89             return null;
90 
91         TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
92         if (blocks == null)
93             return null;
94 
95         MapMaker mm = new MapMaker(classes, minfo, ca);
96         mm.make(blocks, ca.getCode());
97         return mm.toStackMap(blocks);
98     }
99 
100     /**
101      * Computes the stack map table for J2ME.
102      * It returns null if the given method does not have to have a
103      * stack map table.
104      */
make2(ClassPool classes, MethodInfo minfo)105     public static StackMap make2(ClassPool classes, MethodInfo minfo)
106         throws BadBytecode
107     {
108         CodeAttribute ca = minfo.getCodeAttribute();
109         if (ca == null)
110             return null;
111 
112         TypedBlock[] blocks = TypedBlock.makeBlocks(minfo, ca, true);
113         if (blocks == null)
114             return null;
115 
116         MapMaker mm = new MapMaker(classes, minfo, ca);
117         mm.make(blocks, ca.getCode());
118         return mm.toStackMap2(minfo.getConstPool(), blocks);
119     }
120 
MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca)121     public MapMaker(ClassPool classes, MethodInfo minfo, CodeAttribute ca) {
122         super(classes, minfo.getConstPool(),
123               ca.getMaxStack(), ca.getMaxLocals(),
124               TypedBlock.getRetType(minfo.getDescriptor()));
125     }
126 
MapMaker(MapMaker old, boolean copyStack)127     protected MapMaker(MapMaker old, boolean copyStack) {
128         super(old, copyStack);
129     }
130 
131     /**
132      * Runs an analyzer (Phase 1 and 2).
133      */
make(TypedBlock[] blocks, byte[] code)134     void make(TypedBlock[] blocks, byte[] code)
135         throws BadBytecode
136     {
137         TypedBlock first = blocks[0];
138         fixParamTypes(first);
139         TypeData[] srcTypes = first.localsTypes;
140         copyFrom(srcTypes.length, srcTypes, this.localsTypes);
141         make(code, first);
142 
143         int n = blocks.length;
144         for (int i = 0; i < n; i++)
145             evalExpected(blocks[i]);
146     }
147 
148     /*
149      * If a parameter type is String but it is used only as Object
150      * within the method body, this MapMaker class will report its type
151      * is Object.  To avoid this, fixParamTypes calls TypeData.setType()
152      * on each parameter type.
153      */
fixParamTypes(TypedBlock first)154     private void fixParamTypes(TypedBlock first) throws BadBytecode {
155         TypeData[] types = first.localsTypes;
156         int n = types.length;
157         for (int i = 0; i < n; i++) {
158             TypeData t = types[i];
159             if (t instanceof TypeData.ClassName) {
160                 /* Skip the following statement if t.isNullType() is true
161                  * although a parameter type is never null type.
162                  */
163                 TypeData.setType(t, t.getName(), classPool);
164             }
165         }
166     }
167 
168     // Phase 1
169 
make(byte[] code, TypedBlock tb)170     private void make(byte[] code, TypedBlock tb)
171         throws BadBytecode
172     {
173         BasicBlock.Catch handlers = tb.toCatch;
174         while (handlers != null) {
175             traceException(code, handlers);
176             handlers = handlers.next;
177         }
178 
179         int pos = tb.position;
180         int end = pos + tb.length;
181         while (pos < end)
182             pos += doOpcode(pos, code);
183 
184         if (tb.exit != null) {
185             for (int i = 0; i < tb.exit.length; i++) {
186                 TypedBlock e = (TypedBlock)tb.exit[i];
187                 if (e.alreadySet())
188                     mergeMap(e, true);
189                 else {
190                     recordStackMap(e);
191                     MapMaker maker = new MapMaker(this, true);
192                     maker.make(code, e);
193                 }
194             }
195         }
196     }
197 
traceException(byte[] code, TypedBlock.Catch handler)198     private void traceException(byte[] code, TypedBlock.Catch handler)
199         throws BadBytecode
200     {
201         TypedBlock tb = (TypedBlock)handler.body;
202         if (tb.alreadySet())
203             mergeMap(tb, false);
204         else {
205             recordStackMap(tb, handler.typeIndex);
206             MapMaker maker = new MapMaker(this, false);
207 
208             /* the following code is equivalent to maker.copyFrom(this)
209              * except stackTypes are not copied.
210              */
211             maker.stackTypes[0] = tb.stackTypes[0].getSelf();
212             maker.stackTop = 1;
213             maker.make(code, tb);
214         }
215     }
216 
mergeMap(TypedBlock dest, boolean mergeStack)217     private void mergeMap(TypedBlock dest, boolean mergeStack) {
218         boolean[] inputs = dest.inputs;
219         int n = inputs.length;
220         for (int i = 0; i < n; i++)
221             if (inputs[i])
222                 merge(localsTypes[i], dest.localsTypes[i]);
223 
224         if (mergeStack) {
225             n = stackTop;
226             for (int i = 0; i < n; i++)
227                 merge(stackTypes[i], dest.stackTypes[i]);
228         }
229     }
230 
merge(TypeData td, TypeData target)231     private void merge(TypeData td, TypeData target) {
232         boolean tdIsObj = false;
233         boolean targetIsObj = false;
234         // td or target is null if it is TOP.
235         if (td != TOP && td.isObjectType())
236             tdIsObj = true;
237 
238         if (target != TOP && target.isObjectType())
239             targetIsObj = true;
240 
241         if (tdIsObj && targetIsObj)
242             target.merge(td);
243     }
244 
recordStackMap(TypedBlock target)245     private void recordStackMap(TypedBlock target)
246         throws BadBytecode
247     {
248         TypeData[] tStackTypes = new TypeData[stackTypes.length];
249         int st = stackTop;
250         copyFrom(st, stackTypes, tStackTypes);
251         recordStackMap0(target, st, tStackTypes);
252     }
253 
recordStackMap(TypedBlock target, int exceptionType)254     private void recordStackMap(TypedBlock target, int exceptionType)
255         throws BadBytecode
256     {
257         String type;
258         if (exceptionType == 0)
259             type = "java.lang.Throwable";
260         else
261             type = cpool.getClassInfo(exceptionType);
262 
263         TypeData[] tStackTypes = new TypeData[stackTypes.length];
264         tStackTypes[0] = new TypeData.ClassName(type);
265 
266         recordStackMap0(target, 1, tStackTypes);
267     }
268 
recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes)269     private void recordStackMap0(TypedBlock target, int st, TypeData[] tStackTypes)
270         throws BadBytecode
271     {
272         int n = localsTypes.length;
273         TypeData[] tLocalsTypes = new TypeData[n];
274         int k = copyFrom(n, localsTypes, tLocalsTypes);
275 
276         boolean[] inputs = target.inputs;
277         for (int i = 0; i < n; i++)
278             if (!inputs[i])
279                 tLocalsTypes[i] = TOP;
280 
281         target.setStackMap(st, tStackTypes, k, tLocalsTypes);
282     }
283 
284     // Phase 2
285 
evalExpected(TypedBlock target)286     void evalExpected(TypedBlock target) throws BadBytecode {
287         ClassPool cp = classPool;
288         evalExpected(cp, target.stackTop, target.stackTypes);
289         TypeData[] types = target.localsTypes;
290         if (types != null)  // unless this block is dead code
291             evalExpected(cp, types.length, types);
292     }
293 
evalExpected(ClassPool cp, int n, TypeData[] types)294     private static void evalExpected(ClassPool cp, int n, TypeData[] types)
295         throws BadBytecode
296     {
297         for (int i = 0; i < n; i++) {
298             TypeData td = types[i];
299             if (td != null)
300                 td.evalExpectedType(cp);
301         }
302     }
303 
304     // Phase 3
305 
toStackMap(TypedBlock[] blocks)306     public StackMapTable toStackMap(TypedBlock[] blocks) {
307         StackMapTable.Writer writer = new StackMapTable.Writer(32);
308         int n = blocks.length;
309         TypedBlock prev = blocks[0];
310         int offsetDelta = prev.length;
311         if (prev.incoming > 0) {     // the first instruction is a branch target.
312             writer.sameFrame(0);
313             offsetDelta--;
314         }
315 
316         for (int i = 1; i < n; i++) {
317             TypedBlock bb = blocks[i];
318             if (isTarget(bb, blocks[i - 1])) {
319                 bb.resetNumLocals();
320                 int diffL = stackMapDiff(prev.numLocals, prev.localsTypes,
321                                          bb.numLocals, bb.localsTypes);
322                 toStackMapBody(writer, bb, diffL, offsetDelta, prev);
323                 offsetDelta = bb.length - 1;
324                 prev = bb;
325             }
326             else
327                 offsetDelta += bb.length;
328         }
329 
330         return writer.toStackMapTable(cpool);
331     }
332 
333     /**
334      * Returns true if cur is a branch target.
335      */
isTarget(TypedBlock cur, TypedBlock prev)336     private boolean isTarget(TypedBlock cur, TypedBlock prev) {
337         int in = cur.incoming;
338         if (in > 1)
339             return true;
340         else if (in < 1)
341             return false;
342 
343         return prev.stop;
344     }
345 
toStackMapBody(StackMapTable.Writer writer, TypedBlock bb, int diffL, int offsetDelta, TypedBlock prev)346     private void toStackMapBody(StackMapTable.Writer writer, TypedBlock bb,
347                                 int diffL, int offsetDelta, TypedBlock prev) {
348         // if diffL is -100, two TypeData arrays do not share
349         // any elements.
350 
351         int stackTop = bb.stackTop;
352         if (stackTop == 0) {
353             if (diffL == 0) {
354                 writer.sameFrame(offsetDelta);
355                 return;
356             }
357             else if (0 > diffL && diffL >= -3) {
358                 writer.chopFrame(offsetDelta, -diffL);
359                 return;
360             }
361             else if (0 < diffL && diffL <= 3) {
362                 int[] data = new int[diffL];
363                 int[] tags = fillStackMap(bb.numLocals - prev.numLocals,
364                                           prev.numLocals, data,
365                                           bb.localsTypes);
366                 writer.appendFrame(offsetDelta, tags, data);
367                 return;
368             }
369         }
370         else if (stackTop == 1 && diffL == 0) {
371             TypeData td = bb.stackTypes[0];
372             if (td == TOP)
373                 writer.sameLocals(offsetDelta, StackMapTable.TOP, 0);
374             else
375                 writer.sameLocals(offsetDelta, td.getTypeTag(),
376                                   td.getTypeData(cpool));
377             return;
378         }
379         else if (stackTop == 2 && diffL == 0) {
380             TypeData td = bb.stackTypes[0];
381             if (td != TOP && td.is2WordType()) {
382                 // bb.stackTypes[1] must be TOP.
383                 writer.sameLocals(offsetDelta, td.getTypeTag(),
384                                   td.getTypeData(cpool));
385                 return;
386             }
387         }
388 
389         int[] sdata = new int[stackTop];
390         int[] stags = fillStackMap(stackTop, 0, sdata, bb.stackTypes);
391         int[] ldata = new int[bb.numLocals];
392         int[] ltags = fillStackMap(bb.numLocals, 0, ldata, bb.localsTypes);
393         writer.fullFrame(offsetDelta, ltags, ldata, stags, sdata);
394     }
395 
fillStackMap(int num, int offset, int[] data, TypeData[] types)396     private int[] fillStackMap(int num, int offset, int[] data, TypeData[] types) {
397         int realNum = diffSize(types, offset, offset + num);
398         ConstPool cp = cpool;
399         int[] tags = new int[realNum];
400         int j = 0;
401         for (int i = 0; i < num; i++) {
402             TypeData td = types[offset + i];
403             if (td == TOP) {
404                 tags[j] = StackMapTable.TOP;
405                 data[j] = 0;
406             }
407             else {
408                 tags[j] = td.getTypeTag();
409                 data[j] = td.getTypeData(cp);
410                 if (td.is2WordType())
411                     i++;
412             }
413 
414             j++;
415         }
416 
417         return tags;
418     }
419 
stackMapDiff(int oldTdLen, TypeData[] oldTd, int newTdLen, TypeData[] newTd)420     private static int stackMapDiff(int oldTdLen, TypeData[] oldTd,
421                                     int newTdLen, TypeData[] newTd)
422     {
423         int diff = newTdLen - oldTdLen;
424         int len;
425         if (diff > 0)
426             len = oldTdLen;
427         else
428             len = newTdLen;
429 
430         if (stackMapEq(oldTd, newTd, len))
431             if (diff > 0)
432                 return diffSize(newTd, len, newTdLen);
433             else
434                 return -diffSize(oldTd, len, oldTdLen);
435         else
436             return -100;
437     }
438 
stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len)439     private static boolean stackMapEq(TypeData[] oldTd, TypeData[] newTd, int len) {
440         for (int i = 0; i < len; i++) {
441             TypeData td = oldTd[i];
442             if (td == TOP) {        // the next element to LONG/DOUBLE is TOP.
443                 if (newTd[i] != TOP)
444                     return false;
445             }
446             else
447                 if (!oldTd[i].equals(newTd[i]))
448                     return false;
449         }
450 
451         return true;
452     }
453 
diffSize(TypeData[] types, int offset, int len)454     private static int diffSize(TypeData[] types, int offset, int len) {
455         int num = 0;
456         while (offset < len) {
457             TypeData td = types[offset++];
458             num++;
459             if (td != TOP && td.is2WordType())
460                 offset++;
461         }
462 
463         return num;
464     }
465 
466     // Phase 3 for J2ME
467 
toStackMap2(ConstPool cp, TypedBlock[] blocks)468     public StackMap toStackMap2(ConstPool cp, TypedBlock[] blocks) {
469         StackMap.Writer writer = new StackMap.Writer();
470         int n = blocks.length;      // should be > 0
471         boolean[] effective = new boolean[n];
472         TypedBlock prev = blocks[0];
473 
474         // Is the first instruction a branch target?
475         effective[0] = prev.incoming > 0;
476 
477         int num = effective[0] ? 1 : 0;
478         for (int i = 1; i < n; i++) {
479             TypedBlock bb = blocks[i];
480             if (effective[i] = isTarget(bb, blocks[i - 1])) {
481                 bb.resetNumLocals();
482                 prev = bb;
483                 num++;
484             }
485         }
486 
487         if (num == 0)
488             return null;
489 
490         writer.write16bit(num);
491         for (int i = 0; i < n; i++)
492             if (effective[i])
493                 writeStackFrame(writer, cp, blocks[i].position, blocks[i]);
494 
495         return writer.toStackMap(cp);
496     }
497 
writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb)498     private void writeStackFrame(StackMap.Writer writer, ConstPool cp, int offset, TypedBlock tb) {
499         writer.write16bit(offset);
500         writeVerifyTypeInfo(writer, cp, tb.localsTypes, tb.numLocals);
501         writeVerifyTypeInfo(writer, cp, tb.stackTypes, tb.stackTop);
502     }
503 
writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num)504     private void writeVerifyTypeInfo(StackMap.Writer writer, ConstPool cp, TypeData[] types, int num) {
505         int numDWord = 0;
506         for (int i = 0; i < num; i++) {
507             TypeData td = types[i];
508             if (td != null && td.is2WordType()) {
509                 numDWord++;
510                 i++;
511             }
512         }
513 
514         writer.write16bit(num - numDWord);
515         for (int i = 0; i < num; i++) {
516             TypeData td = types[i];
517             if (td == TOP)
518                 writer.writeVerifyTypeInfo(StackMap.TOP, 0);
519             else {
520                 writer.writeVerifyTypeInfo(td.getTypeTag(), td.getTypeData(cp));
521                 if (td.is2WordType())
522                     i++;
523             }
524         }
525     }
526 }
527