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
16package javassist.bytecode.stackmap;
17
18import javassist.bytecode.*;
19
20public class TypedBlock extends BasicBlock {
21    public int stackTop, numLocals;
22    public TypeData[] stackTypes, localsTypes;
23
24    // set by a Liveness object.
25    // inputs[i] is true if the i-th variable is used within this block.
26    public boolean[] inputs;
27
28    // working area for Liveness class.
29    public boolean updating;
30    public int status;
31    public byte[] localsUsage;
32
33    /**
34     * Divides the method body into basic blocks.
35     * The type information of the first block is initialized.
36     *
37     * @param optmize       if it is true and the method does not include
38     *                      branches, this method returns null.
39     */
40    public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca,
41                                          boolean optimize)
42        throws BadBytecode
43    {
44        TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo);
45        if (optimize && blocks.length < 2)
46            if (blocks.length == 0 || blocks[0].incoming == 0)
47                return null;
48
49        ConstPool pool = minfo.getConstPool();
50        boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0;
51        blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(),
52                                 pool.getClassName(), minfo.getDescriptor(),
53                                 isStatic, minfo.isConstructor());
54        new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(),
55                               blocks[0].localsTypes);
56        return blocks;
57    }
58
59    protected TypedBlock(int pos) {
60        super(pos);
61        localsTypes = null;
62        inputs = null;
63        updating = false;
64    }
65
66    protected void toString2(StringBuffer sbuf) {
67        super.toString2(sbuf);
68        sbuf.append(",\n stack={");
69        printTypes(sbuf, stackTop, stackTypes);
70        sbuf.append("}, locals={");
71        printTypes(sbuf, numLocals, localsTypes);
72        sbuf.append("}, inputs={");
73        if (inputs != null)
74            for (int i = 0; i < inputs.length; i++)
75                sbuf.append(inputs[i] ? "1, " : "0, ");
76
77        sbuf.append('}');
78    }
79
80    private void printTypes(StringBuffer sbuf, int size,
81                            TypeData[] types) {
82        if (types == null)
83            return;
84
85        for (int i = 0; i < size; i++) {
86            if (i > 0)
87                sbuf.append(", ");
88
89            TypeData td = types[i];
90            sbuf.append(td == null ? "<>" : td.toString());
91        }
92    }
93
94    public boolean alreadySet() {
95        return localsTypes != null;
96    }
97
98    public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals)
99        throws BadBytecode
100    {
101        stackTop = st;
102        stackTypes = stack;
103        numLocals = nl;
104        localsTypes = locals;
105    }
106
107    /*
108     * Computes the correct value of numLocals.
109     */
110    public void resetNumLocals() {
111        if (localsTypes != null) {
112            int nl = localsTypes.length;
113            while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) {
114                if (nl > 1) {
115                    TypeData td = localsTypes[nl - 2];
116                    if (td == TypeTag.LONG || td == TypeTag.DOUBLE)
117                        break;
118                }
119
120                --nl;
121            }
122
123            numLocals = nl;
124        }
125    }
126
127    public static class Maker extends BasicBlock.Maker {
128        protected BasicBlock makeBlock(int pos) {
129            return new TypedBlock(pos);
130        }
131
132        protected BasicBlock[] makeArray(int size) {
133            return new TypedBlock[size];
134        }
135    }
136
137    /**
138     * Initializes the first block by the given method descriptor.
139     *
140     * @param block             the first basic block that this method initializes.
141     * @param className         a dot-separated fully qualified class name.
142     *                          For example, <code>javassist.bytecode.stackmap.BasicBlock</code>.
143     * @param methodDesc        method descriptor.
144     * @param isStatic          true if the method is a static method.
145     * @param isConstructor     true if the method is a constructor.
146     */
147    void initFirstBlock(int maxStack, int maxLocals, String className,
148                        String methodDesc, boolean isStatic, boolean isConstructor)
149        throws BadBytecode
150    {
151        if (methodDesc.charAt(0) != '(')
152            throw new BadBytecode("no method descriptor: " + methodDesc);
153
154        stackTop = 0;
155        stackTypes = new TypeData[maxStack];
156        TypeData[] locals = new TypeData[maxLocals];
157        if (isConstructor)
158            locals[0] = new TypeData.UninitThis(className);
159        else if (!isStatic)
160            locals[0] = new TypeData.ClassName(className);
161
162        int n = isStatic ? -1 : 0;
163        int i = 1;
164        try {
165            while ((i = descToTag(methodDesc, i, ++n, locals)) > 0)
166                if (locals[n].is2WordType())
167                    locals[++n] = TypeTag.TOP;
168        }
169        catch (StringIndexOutOfBoundsException e) {
170            throw new BadBytecode("bad method descriptor: "
171                                  + methodDesc);
172        }
173
174        numLocals = n;
175        localsTypes = locals;
176    }
177
178    private static int descToTag(String desc, int i,
179                                 int n, TypeData[] types)
180        throws BadBytecode
181    {
182        int i0 = i;
183        int arrayDim = 0;
184        char c = desc.charAt(i);
185        if (c == ')')
186            return 0;
187
188        while (c == '[') {
189            ++arrayDim;
190            c = desc.charAt(++i);
191        }
192
193        if (c == 'L') {
194            int i2 = desc.indexOf(';', ++i);
195            if (arrayDim > 0)
196                types[n] = new TypeData.ClassName(desc.substring(i0, ++i2));
197            else
198                types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1)
199                                                      .replace('/', '.'));
200            return i2;
201        }
202        else if (arrayDim > 0) {
203            types[n] = new TypeData.ClassName(desc.substring(i0, ++i));
204            return i;
205        }
206        else {
207            TypeData t = toPrimitiveTag(c);
208            if (t == null)
209                throw new BadBytecode("bad method descriptor: " + desc);
210
211            types[n] = t;
212            return i + 1;
213        }
214    }
215
216    private static TypeData toPrimitiveTag(char c) {
217        switch (c) {
218        case 'Z' :
219        case 'C' :
220        case 'B' :
221        case 'S' :
222        case 'I' :
223            return TypeTag.INTEGER;
224        case 'J' :
225            return TypeTag.LONG;
226        case 'F' :
227            return TypeTag.FLOAT;
228        case 'D' :
229            return TypeTag.DOUBLE;
230        case 'V' :
231        default :
232            return null;
233        }
234    }
235
236    public static String getRetType(String desc) {
237        int i = desc.indexOf(')');
238        if (i < 0)
239            return "java.lang.Object";
240
241        char c = desc.charAt(i + 1);
242        if (c == '[')
243            return desc.substring(i + 1);
244        else if (c == 'L')
245            return desc.substring(i + 2, desc.length() - 1).replace('/', '.');
246        else
247            return "java.lang.Object";
248    }
249}
250