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.compiler;
17
18import javassist.*;
19import javassist.compiler.ast.*;
20
21/* Type checker accepting extended Java syntax for Javassist.
22 */
23
24public class JvstTypeChecker extends TypeChecker {
25    private JvstCodeGen codeGen;
26
27    public JvstTypeChecker(CtClass cc, ClassPool cp, JvstCodeGen gen) {
28        super(cc, cp);
29        codeGen = gen;
30    }
31
32    /* If the type of the expression compiled last is void,
33     * add ACONST_NULL and change exprType, arrayDim, className.
34     */
35    public void addNullIfVoid() {
36        if (exprType == VOID) {
37            exprType = CLASS;
38            arrayDim = 0;
39            className = jvmJavaLangObject;
40        }
41    }
42
43    /* To support $args, $sig, and $type.
44     * $args is an array of parameter list.
45     */
46    public void atMember(Member mem) throws CompileError {
47        String name = mem.get();
48        if (name.equals(codeGen.paramArrayName)) {
49            exprType = CLASS;
50            arrayDim = 1;
51            className = jvmJavaLangObject;
52        }
53        else if (name.equals(JvstCodeGen.sigName)) {
54            exprType = CLASS;
55            arrayDim = 1;
56            className = "java/lang/Class";
57        }
58        else if (name.equals(JvstCodeGen.dollarTypeName)
59                 || name.equals(JvstCodeGen.clazzName)) {
60            exprType = CLASS;
61            arrayDim = 0;
62            className = "java/lang/Class";
63        }
64        else
65            super.atMember(mem);
66    }
67
68    protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right)
69        throws CompileError
70    {
71        if (left instanceof Member
72            && ((Member)left).get().equals(codeGen.paramArrayName)) {
73            right.accept(this);
74            CtClass[] params = codeGen.paramTypeList;
75            if (params == null)
76                return;
77
78            int n = params.length;
79            for (int i = 0; i < n; ++i)
80                compileUnwrapValue(params[i]);
81        }
82        else
83            super.atFieldAssign(expr, op, left, right);
84    }
85
86    public void atCastExpr(CastExpr expr) throws CompileError {
87        ASTList classname = expr.getClassName();
88        if (classname != null && expr.getArrayDim() == 0) {
89            ASTree p = classname.head();
90            if (p instanceof Symbol && classname.tail() == null) {
91                String typename = ((Symbol)p).get();
92                if (typename.equals(codeGen.returnCastName)) {
93                    atCastToRtype(expr);
94                    return;
95                }
96                else if (typename.equals(JvstCodeGen.wrapperCastName)) {
97                    atCastToWrapper(expr);
98                    return;
99                }
100            }
101        }
102
103        super.atCastExpr(expr);
104    }
105
106    /**
107     * Inserts a cast operator to the return type.
108     * If the return type is void, this does nothing.
109     */
110    protected void atCastToRtype(CastExpr expr) throws CompileError {
111        CtClass returnType = codeGen.returnType;
112        expr.getOprand().accept(this);
113        if (exprType == VOID || CodeGen.isRefType(exprType) || arrayDim > 0)
114            compileUnwrapValue(returnType);
115        else if (returnType instanceof CtPrimitiveType) {
116            CtPrimitiveType pt = (CtPrimitiveType)returnType;
117            int destType = MemberResolver.descToType(pt.getDescriptor());
118            exprType = destType;
119            arrayDim = 0;
120            className = null;
121        }
122    }
123
124    protected void atCastToWrapper(CastExpr expr) throws CompileError {
125        expr.getOprand().accept(this);
126        if (CodeGen.isRefType(exprType) || arrayDim > 0)
127            return;     // Object type.  do nothing.
128
129        CtClass clazz = resolver.lookupClass(exprType, arrayDim, className);
130        if (clazz instanceof CtPrimitiveType) {
131            exprType = CLASS;
132            arrayDim = 0;
133            className = jvmJavaLangObject;
134        }
135    }
136
137    /* Delegates to a ProcHandler object if the method call is
138     * $proceed().  It may process $cflow().
139     */
140    public void atCallExpr(CallExpr expr) throws CompileError {
141        ASTree method = expr.oprand1();
142        if (method instanceof Member) {
143            String name = ((Member)method).get();
144            if (codeGen.procHandler != null
145                && name.equals(codeGen.proceedName)) {
146                codeGen.procHandler.setReturnType(this,
147                                                  (ASTList)expr.oprand2());
148                return;
149            }
150            else if (name.equals(JvstCodeGen.cflowName)) {
151                atCflow((ASTList)expr.oprand2());
152                return;
153            }
154        }
155
156        super.atCallExpr(expr);
157    }
158
159    /* To support $cflow().
160     */
161    protected void atCflow(ASTList cname) throws CompileError {
162        exprType = INT;
163        arrayDim = 0;
164        className = null;
165    }
166
167    /* To support $$.  ($$) is equivalent to ($1, ..., $n).
168     * It can be used only as a parameter list of method call.
169     */
170    public boolean isParamListName(ASTList args) {
171        if (codeGen.paramTypeList != null
172            && args != null && args.tail() == null) {
173            ASTree left = args.head();
174            return (left instanceof Member
175                    && ((Member)left).get().equals(codeGen.paramListName));
176        }
177        else
178            return false;
179    }
180
181    public int getMethodArgsLength(ASTList args) {
182        String pname = codeGen.paramListName;
183        int n = 0;
184        while (args != null) {
185            ASTree a = args.head();
186            if (a instanceof Member && ((Member)a).get().equals(pname)) {
187                if (codeGen.paramTypeList != null)
188                    n += codeGen.paramTypeList.length;
189            }
190            else
191                ++n;
192
193            args = args.tail();
194        }
195
196        return n;
197    }
198
199    public void atMethodArgs(ASTList args, int[] types, int[] dims,
200                                String[] cnames) throws CompileError {
201        CtClass[] params = codeGen.paramTypeList;
202        String pname = codeGen.paramListName;
203        int i = 0;
204        while (args != null) {
205            ASTree a = args.head();
206            if (a instanceof Member && ((Member)a).get().equals(pname)) {
207                if (params != null) {
208                    int n = params.length;
209                    for (int k = 0; k < n; ++k) {
210                        CtClass p = params[k];
211                        setType(p);
212                        types[i] = exprType;
213                        dims[i] = arrayDim;
214                        cnames[i] = className;
215                        ++i;
216                    }
217                }
218            }
219            else {
220                a.accept(this);
221                types[i] = exprType;
222                dims[i] = arrayDim;
223                cnames[i] = className;
224                ++i;
225            }
226
227            args = args.tail();
228        }
229    }
230
231    /* called by Javac#recordSpecialProceed().
232     */
233    void compileInvokeSpecial(ASTree target, String classname,
234                              String methodname, String descriptor,
235                              ASTList args)
236        throws CompileError
237    {
238        target.accept(this);
239        int nargs = getMethodArgsLength(args);
240        atMethodArgs(args, new int[nargs], new int[nargs],
241                     new String[nargs]);
242        setReturnType(descriptor);
243        addNullIfVoid();
244    }
245
246    protected void compileUnwrapValue(CtClass type) throws CompileError
247    {
248        if (type == CtClass.voidType)
249            addNullIfVoid();
250        else
251            setType(type);
252    }
253
254    /* Sets exprType, arrayDim, and className;
255     * If type is void, then this method does nothing.
256     */
257    public void setType(CtClass type) throws CompileError {
258        setType(type, 0);
259    }
260
261    private void setType(CtClass type, int dim) throws CompileError {
262        if (type.isPrimitive()) {
263            CtPrimitiveType pt = (CtPrimitiveType)type;
264            exprType = MemberResolver.descToType(pt.getDescriptor());
265            arrayDim = dim;
266            className = null;
267        }
268        else if (type.isArray())
269            try {
270                setType(type.getComponentType(), dim + 1);
271            }
272            catch (NotFoundException e) {
273                throw new CompileError("undefined type: " + type.getName());
274            }
275        else {
276            exprType = CLASS;
277            arrayDim = dim;
278            className = MemberResolver.javaToJvmName(type.getName());
279        }
280    }
281}
282