/Users/lyon/j4p/src/javassist/compiler/Javac.java
|
1 /*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2003 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.compiler;
17
18 import javassist.*;
19 import javassist.bytecode.Bytecode;
20 import javassist.bytecode.Opcode;
21 import javassist.compiler.ast.*;
22
23 public class Javac {
24 JvstCodeGen gen;
25 SymbolTable stable;
26 private Bytecode bytecode;
27
28 public static final String param0Name = "$0";
29 public static final String resultVarName = "$_";
30 public static final String proceedName = "$proceed";
31
32 /**
33 * Constructs a compiler.
34 *
35 * @param thisClass the class that a compiled method/field
36 * belongs to.
37 */
38 public Javac(CtClass thisClass) {
39 this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0),
40 thisClass);
41 }
42
43 /**
44 * Constructs a compiler.
45 * The produced bytecode is stored in the <code>Bytecode</code> object
46 * specified by <code>b</code>.
47 *
48 * @param thisClass the class that a compiled method/field
49 * belongs to.
50 */
51 public Javac(Bytecode b, CtClass thisClass) {
52 gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool());
53 stable = new SymbolTable();
54 bytecode = b;
55 }
56
57 /**
58 * Returns the produced bytecode.
59 */
60 public Bytecode getBytecode() {
61 return bytecode;
62 }
63
64 /**
65 * Compiles a method, constructor, or field declaration
66 * to a class.
67 * A field declaration can declare only one field.
68 *
69 * <p>In a method or constructor body, $0, $1, ... and $_
70 * are not available.
71 *
72 * @return a <code>CtMethod</code>, <code>CtConstructor</code>,
73 * or <code>CtField</code> object.
74 * @see #recordProceed(String,String)
75 */
76 public CtMember compile(String src) throws CompileError {
77 Parser p = new Parser(new Lex(src));
78 ASTList mem = p.parseMember1(stable);
79 try {
80 if (mem instanceof FieldDecl)
81 return compileField((FieldDecl) mem);
82 else
83 return compileMethod(p, (MethodDecl) mem);
84 } catch (CannotCompileException e) {
85 throw new CompileError(e.getMessage());
86 }
87 }
88
89 public static class CtFieldWithInit extends CtField {
90 private ASTree init;
91
92 CtFieldWithInit(CtClass type, String name, CtClass declaring)
93 throws CannotCompileException {
94 super(type, name, declaring);
95 init = null;
96 }
97
98 protected void setInit(ASTree i) {
99 init = i;
100 }
101
102 protected ASTree getInitAST() {
103 return init;
104 }
105 }
106
107 private CtField compileField(FieldDecl fd)
108 throws CompileError, CannotCompileException {
109 CtFieldWithInit f;
110 Declarator d = fd.getDeclarator();
111 f = new CtFieldWithInit(gen.lookupClass(d), d.getVariable().get(),
112 gen.getThisClass());
113 f.setModifiers(gen.getModifiers(fd.getModifiers()));
114 if (fd.getInit() != null)
115 f.setInit(fd.getInit());
116
117 return f;
118 }
119
120 private CtMember compileMethod(Parser p, MethodDecl md)
121 throws CompileError {
122 int mod = gen.getModifiers(md.getModifiers());
123 CtClass[] plist = gen.makeParamList(md);
124 CtClass[] tlist = gen.makeThrowsList(md);
125 recordParams(plist, Modifier.isStatic(mod));
126 md = p.parseMethod2(stable, md);
127 try {
128 if (md.isConstructor()) {
129 CtConstructor cons = new CtConstructor(plist,
130 gen.getThisClass());
131 cons.setModifiers(mod);
132 md.accept(gen);
133 cons.getMethodInfo().setCodeAttribute(
134 bytecode.toCodeAttribute());
135 cons.setExceptionTypes(tlist);
136 return cons;
137 } else {
138 Declarator r = md.getReturn();
139 CtClass rtype = gen.lookupClass(r);
140 recordReturnType(rtype, false);
141 CtMethod method = new CtMethod(rtype, r.getVariable().get(),
142 plist, gen.getThisClass());
143 method.setModifiers(mod);
144 gen.setThisMethod(method);
145 md.accept(gen);
146 if (md.getBody() != null)
147 method.getMethodInfo().setCodeAttribute(
148 bytecode.toCodeAttribute());
149 else
150 method.setModifiers(mod | Modifier.ABSTRACT);
151
152 method.setExceptionTypes(tlist);
153 return method;
154 }
155 } catch (NotFoundException e) {
156 throw new CompileError(e.toString());
157 }
158 }
159
160 /**
161 * Compiles a method (or constructor) body.
162 *
163 * @src a single statement or a block.
164 * If null, this method produces a body returning zero or null.
165 */
166 public Bytecode compileBody(CtBehavior method, String src)
167 throws CompileError {
168 try {
169 int mod = method.getModifiers();
170 recordParams(method.getParameterTypes(), Modifier.isStatic(mod));
171
172 CtClass rtype;
173 if (method instanceof CtMethod) {
174 gen.setThisMethod((CtMethod) method);
175 rtype = ((CtMethod) method).getReturnType();
176 } else
177 rtype = CtClass.voidType;
178
179 recordReturnType(rtype, false);
180 boolean isVoid = rtype == CtClass.voidType;
181
182 if (src == null)
183 makeDefaultBody(bytecode, rtype);
184 else {
185 Parser p = new Parser(new Lex(src));
186 SymbolTable stb = new SymbolTable(stable);
187 Stmnt s = p.parseStatement(stb);
188 boolean callSuper = false;
189 if (method instanceof CtConstructor)
190 callSuper = !((CtConstructor) method).isClassInitializer();
191
192 gen.atMethodBody(s, callSuper, isVoid);
193 }
194
195 return bytecode;
196 } catch (NotFoundException e) {
197 throw new CompileError(e.toString());
198 }
199 }
200
201 private static void makeDefaultBody(Bytecode b, CtClass type) {
202 int op;
203 int value;
204 if (type instanceof CtPrimitiveType) {
205 CtPrimitiveType pt = (CtPrimitiveType) type;
206 op = pt.getReturnOp();
207 if (op == Opcode.DRETURN)
208 value = Opcode.DCONST_0;
209 else if (op == Opcode.FRETURN)
210 value = Opcode.FCONST_0;
211 else if (op == Opcode.LRETURN)
212 value = Opcode.LCONST_0;
213 else if (op == Opcode.RETURN)
214 value = Opcode.NOP;
215 else
216 value = Opcode.ICONST_0;
217 } else {
218 op = Opcode.ARETURN;
219 value = Opcode.ACONST_NULL;
220 }
221
222 if (value != Opcode.NOP)
223 b.addOpcode(value);
224
225 b.addOpcode(op);
226 }
227
228 /**
229 * Makes variables $0 (this), $1, $2, ..., and $args represent method
230 * parameters. $args represents an array of all the parameters.
231 * It also makes $$ available as a parameter list of method call.
232 *
233 * <p>This must be called before calling <code>compileStmnt()</code> and
234 * <code>compileExpr()</code>. The correct value of
235 * <code>isStatic</code> must be recorded before compilation.
236 */
237 public void recordParams(CtClass[] params, boolean isStatic)
238 throws CompileError {
239 gen.recordParams(params, isStatic, "$", "$args", "$$", stable);
240 }
241
242 /**
243 * Makes variables $0, $1, $2, ..., and $args represent method
244 * parameters. $args represents an array of all the parameters.
245 * It also makes $$ available as a parameter list of method call.
246 * $0 can represent a local variable other than THIS (variable 0).
247 *
248 * <p>This must be called before calling <code>compileStmnt()</code> and
249 * <code>compileExpr()</code>. The correct value of
250 * <code>isStatic</code> must be recorded before compilation.
251 *
252 * @paaram use0 true if $0 is used.
253 * @param varNo the register number of $0 (use0 is true)
254 * or $1 (otherwise).
255 * @param target the type of $0 (it can be null if use0 is false).
256 * @param isStatic true if the method in which the compiled bytecode
257 * is embedded is static.
258 */
259 public void recordParams(String target, CtClass[] params,
260 boolean use0, int varNo, boolean isStatic)
261 throws CompileError {
262 gen.recordParams(params, isStatic, "$", "$args", "$$",
263 use0, varNo, target, stable);
264 }
265
266 /**
267 * Prepares to use cast $r, $w, $_, and $type.
268 * It also enables to write a return statement with a return value
269 * for void method.
270 *
271 * <p>If the return type is void, ($r) does nothing.
272 * The type of $_ is java.lang.Object.
273 *
274 * @param useResultVar true if $_ is used.
275 * @return -1 or the variable index assigned to $_.
276 */
277 public int recordReturnType(CtClass type, boolean useResultVar)
278 throws CompileError {
279 gen.recordType(type);
280 return gen.recordReturnType(type, "$r",
281 (useResultVar ? resultVarName : null), stable);
282 }
283
284 /**
285 * Prepares to use $type. Note that recordReturnType() overwrites
286 * the value of $type.
287 */
288 public void recordType(CtClass t) {
289 gen.recordType(t);
290 }
291
292 /**
293 * Makes the given variable available.
294 *
295 * @param type variable type
296 * @param name variable name
297 */
298 public int recordVariable(CtClass type, String name)
299 throws CompileError {
300 return gen.recordVariable(type, name, stable);
301 }
302
303 /**
304 * Prepares to use $proceed().
305 * If the return type of $proceed() is void, null is pushed on the
306 * stack.
307 *
308 * @param target an expression specifying the target object.
309 * if null, "this" is the target.
310 * @param method the method name.
311 */
312 public void recordProceed(String target, String method)
313 throws CompileError {
314 Parser p = new Parser(new Lex(target));
315 final ASTree texpr = p.parseExpression(stable);
316 final String m = method;
317
318 ProceedHandler h = new ProceedHandler() {
319 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
320 throws CompileError {
321 ASTree expr = new Member(m);
322 if (texpr != null)
323 expr = Expr.make('.', texpr, expr);
324
325 expr = Expr.make(TokenId.CALL, expr, args);
326 expr.accept(gen);
327 gen.addNullIfVoid();
328 }
329 };
330
331 gen.setProceedHandler(h, proceedName);
332 }
333
334 /**
335 * Prepares to use $proceed().
336 */
337 public void recordProceed(ProceedHandler h) {
338 gen.setProceedHandler(h, proceedName);
339 }
340
341 /**
342 * Compiles a statement (or a block).
343 * <code>recordParams()</code> must be called before invoking
344 * this method.
345 *
346 * <p>Local variables that are not declared
347 * in the compiled source text are not accessible within that
348 * source text. Fields and method parameters
349 * ($0, $1, ..) are available.
350 */
351 public void compileStmnt(String src) throws CompileError {
352 Parser p = new Parser(new Lex(src));
353 SymbolTable stb = new SymbolTable(stable);
354 // while (p.hasMore()) {
355 Stmnt s = p.parseStatement(stb);
356 if (s != null)
357 s.accept(gen);
358 // }
359 }
360
361 /**
362 * Compiles an exression. <code>recordParams()</code> must be
363 * called before invoking this method.
364 *
365 * <p>Local variables are not accessible
366 * within the compiled source text. Fields and method parameters
367 * ($0, $1, ..) are available if <code>recordParams()</code>
368 * have been invoked.
369 */
370 public void compileExpr(String src) throws CompileError {
371 Parser p = new Parser(new Lex(src));
372 ASTree e = p.parseExpression(stable);
373 compileExpr(e);
374 }
375
376 /**
377 * Compiles an exression. <code>recordParams()</code> must be
378 * called before invoking this method.
379 *
380 * <p>Local variables are not accessible
381 * within the compiled source text. Fields and method parameters
382 * ($0, $1, ..) are available if <code>recordParams()</code>
383 * have been invoked.
384 */
385 public void compileExpr(ASTree e) throws CompileError {
386 if (e != null)
387 e.accept(gen);
388 }
389 }
390