KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javassist > compiler > Javac


1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2005 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.CtClass;
19 import javassist.CtPrimitiveType;
20 import javassist.CtMember;
21 import javassist.CtField;
22 import javassist.CtBehavior;
23 import javassist.CtMethod;
24 import javassist.CtConstructor;
25 import javassist.CannotCompileException;
26 import javassist.Modifier;
27 import javassist.bytecode.Bytecode;
28 import javassist.bytecode.CodeAttribute;
29 import javassist.bytecode.LocalVariableAttribute;
30 import javassist.bytecode.Opcode;
31 import javassist.NotFoundException;
32
33 import javassist.compiler.ast.*;
34
35 public class Javac {
36     JvstCodeGen gen;
37     SymbolTable stable;
38     private Bytecode bytecode;
39
40     public static final String JavaDoc param0Name = "$0";
41     public static final String JavaDoc resultVarName = "$_";
42     public static final String JavaDoc proceedName = "$proceed";
43
44     /**
45      * Constructs a compiler.
46      *
47      * @param thisClass the class that a compiled method/field
48      * belongs to.
49      */

50     public Javac(CtClass thisClass) {
51         this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0),
52              thisClass);
53     }
54
55     /**
56      * Constructs a compiler.
57      * The produced bytecode is stored in the <code>Bytecode</code> object
58      * specified by <code>b</code>.
59      *
60      * @param thisClass the class that a compiled method/field
61      * belongs to.
62      */

63     public Javac(Bytecode b, CtClass thisClass) {
64         gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool());
65         stable = new SymbolTable();
66         bytecode = b;
67     }
68
69     /**
70      * Returns the produced bytecode.
71      */

72     public Bytecode getBytecode() { return bytecode; }
73
74     /**
75      * Compiles a method, constructor, or field declaration
76      * to a class.
77      * A field declaration can declare only one field.
78      *
79      * <p>In a method or constructor body, $0, $1, ... and $_
80      * are not available.
81      *
82      * @return a <code>CtMethod</code>, <code>CtConstructor</code>,
83      * or <code>CtField</code> object.
84      * @see #recordProceed(String,String)
85      */

86     public CtMember compile(String JavaDoc src) throws CompileError {
87         Parser p = new Parser(new Lex(src));
88         ASTList mem = p.parseMember1(stable);
89         try {
90             if (mem instanceof FieldDecl)
91                 return compileField((FieldDecl)mem);
92             else
93                 return compileMethod(p, (MethodDecl)mem);
94         }
95         catch (CannotCompileException e) {
96             throw new CompileError(e.getMessage());
97         }
98     }
99
100     public static class CtFieldWithInit extends CtField {
101         private ASTree init;
102
103         CtFieldWithInit(CtClass type, String JavaDoc name, CtClass declaring)
104             throws CannotCompileException
105         {
106             super(type, name, declaring);
107             init = null;
108         }
109
110         protected void setInit(ASTree i) { init = i; }
111
112         protected ASTree getInitAST() {
113             return init;
114         }
115     }
116
117     private CtField compileField(FieldDecl fd)
118         throws CompileError, CannotCompileException
119     {
120         CtFieldWithInit f;
121         Declarator d = fd.getDeclarator();
122         f = new CtFieldWithInit(gen.resolver.lookupClass(d),
123                                 d.getVariable().get(), gen.getThisClass());
124         f.setModifiers(MemberResolver.getModifiers(fd.getModifiers()));
125         if (fd.getInit() != null)
126             f.setInit(fd.getInit());
127
128         return f;
129     }
130
131     private CtMember compileMethod(Parser p, MethodDecl md)
132         throws CompileError
133     {
134         int mod = MemberResolver.getModifiers(md.getModifiers());
135         CtClass[] plist = gen.makeParamList(md);
136         CtClass[] tlist = gen.makeThrowsList(md);
137         recordParams(plist, Modifier.isStatic(mod));
138         md = p.parseMethod2(stable, md);
139         try {
140             if (md.isConstructor()) {
141                 CtConstructor cons = new CtConstructor(plist,
142                                                    gen.getThisClass());
143                 cons.setModifiers(mod);
144                 md.accept(gen);
145                 cons.getMethodInfo().setCodeAttribute(
146                                         bytecode.toCodeAttribute());
147                 cons.setExceptionTypes(tlist);
148                 return cons;
149             }
150             else {
151                 Declarator r = md.getReturn();
152                 CtClass rtype = gen.resolver.lookupClass(r);
153                 recordReturnType(rtype, false);
154                 CtMethod method = new CtMethod(rtype, r.getVariable().get(),
155                                            plist, gen.getThisClass());
156                 method.setModifiers(mod);
157                 gen.setThisMethod(method);
158                 md.accept(gen);
159                 if (md.getBody() != null)
160                     method.getMethodInfo().setCodeAttribute(
161                                         bytecode.toCodeAttribute());
162                 else
163                     method.setModifiers(mod | Modifier.ABSTRACT);
164
165                 method.setExceptionTypes(tlist);
166                 return method;
167             }
168         }
169         catch (NotFoundException e) {
170             throw new CompileError(e.toString());
171         }
172     }
173
174     /**
175      * Compiles a method (or constructor) body.
176      *
177      * @src a single statement or a block.
178      * If null, this method produces a body returning zero or null.
179      */

180     public Bytecode compileBody(CtBehavior method, String JavaDoc src)
181         throws CompileError
182     {
183         try {
184             int mod = method.getModifiers();
185             recordParams(method.getParameterTypes(), Modifier.isStatic(mod));
186
187             CtClass rtype;
188             if (method instanceof CtMethod) {
189                 gen.setThisMethod((CtMethod)method);
190                 rtype = ((CtMethod)method).getReturnType();
191             }
192             else
193                 rtype = CtClass.voidType;
194
195             recordReturnType(rtype, false);
196             boolean isVoid = rtype == CtClass.voidType;
197
198             if (src == null)
199                 makeDefaultBody(bytecode, rtype);
200             else {
201                 Parser p = new Parser(new Lex(src));
202                 SymbolTable stb = new SymbolTable(stable);
203                 Stmnt s = p.parseStatement(stb);
204                 boolean callSuper = false;
205                 if (method instanceof CtConstructor)
206                     callSuper = !((CtConstructor)method).isClassInitializer();
207
208                 gen.atMethodBody(s, callSuper, isVoid);
209             }
210
211             return bytecode;
212         }
213         catch (NotFoundException e) {
214             throw new CompileError(e.toString());
215         }
216     }
217
218     private static void makeDefaultBody(Bytecode b, CtClass type) {
219         int op;
220         int value;
221         if (type instanceof CtPrimitiveType) {
222             CtPrimitiveType pt = (CtPrimitiveType)type;
223             op = pt.getReturnOp();
224             if (op == Opcode.DRETURN)
225                 value = Opcode.DCONST_0;
226             else if (op == Opcode.FRETURN)
227                 value = Opcode.FCONST_0;
228             else if (op == Opcode.LRETURN)
229                 value = Opcode.LCONST_0;
230             else if (op == Opcode.RETURN)
231                 value = Opcode.NOP;
232             else
233                 value = Opcode.ICONST_0;
234         }
235         else {
236             op = Opcode.ARETURN;
237             value = Opcode.ACONST_NULL;
238         }
239
240         if (value != Opcode.NOP)
241             b.addOpcode(value);
242
243         b.addOpcode(op);
244     }
245
246     /**
247      * Records local variables available at the specified program counter.
248      * If the LocalVariableAttribute is not available, this method does not
249      * record any local variable. It only returns false.
250      *
251      * @param pc program counter (&gt;= 0)
252      * @return false if the CodeAttribute does not include a
253      * LocalVariableAttribute.
254      */

255     public boolean recordLocalVariables(CodeAttribute ca, int pc)
256         throws CompileError
257     {
258         LocalVariableAttribute va
259             = (LocalVariableAttribute)
260               ca.getAttribute(LocalVariableAttribute.tag);
261         if (va == null)
262             return false;
263
264         int n = va.tableLength();
265         for (int i = 0; i < n; ++i) {
266             int start = va.startPc(i);
267             int len = va.codeLength(i);
268             if (start <= pc && pc < start + len)
269                 gen.recordVariable(va.descriptor(i), va.variableName(i),
270                                    va.index(i), stable);
271         }
272
273         return true;
274     }
275
276     /**
277      * Records parameter names if the LocalVariableAttribute is available.
278      * It returns false unless the LocalVariableAttribute is available.
279      *
280      * @param numOfLocalVars the number of local variables used
281      * for storing the parameters.
282      * @return false if the CodeAttribute does not include a
283      * LocalVariableAttribute.
284      */

285     public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars)
286         throws CompileError
287     {
288         LocalVariableAttribute va
289             = (LocalVariableAttribute)
290               ca.getAttribute(LocalVariableAttribute.tag);
291         if (va == null)
292             return false;
293
294         int n = va.tableLength();
295         for (int i = 0; i < n; ++i) {
296             int index = va.index(i);
297             if (index < numOfLocalVars)
298                 gen.recordVariable(va.descriptor(i), va.variableName(i),
299                                    index, stable);
300         }
301
302         return true;
303     }
304
305
306     /**
307      * Makes variables $0 (this), $1, $2, ..., and $args represent method
308      * parameters. $args represents an array of all the parameters.
309      * It also makes $$ available as a parameter list of method call.
310      *
311      * <p>This must be called before calling <code>compileStmnt()</code> and
312      * <code>compileExpr()</code>. The correct value of
313      * <code>isStatic</code> must be recorded before compilation.
314      */

315     public int recordParams(CtClass[] params, boolean isStatic)
316         throws CompileError
317     {
318         return gen.recordParams(params, isStatic, "$", "$args", "$$", stable);
319     }
320
321     /**
322      * Makes variables $0, $1, $2, ..., and $args represent method
323      * parameters. $args represents an array of all the parameters.
324      * It also makes $$ available as a parameter list of method call.
325      * $0 can represent a local variable other than THIS (variable 0).
326      * $class is also made available.
327      *
328      * <p>This must be called before calling <code>compileStmnt()</code> and
329      * <code>compileExpr()</code>. The correct value of
330      * <code>isStatic</code> must be recorded before compilation.
331      *
332      * @paaram use0 true if $0 is used.
333      * @param varNo the register number of $0 (use0 is true)
334      * or $1 (otherwise).
335      * @param target the type of $0 (it can be null if use0 is false).
336      * It is used as the name of the type represented
337      * by $class.
338      * @param isStatic true if the method in which the compiled bytecode
339      * is embedded is static.
340      */

341     public int recordParams(String JavaDoc target, CtClass[] params,
342                              boolean use0, int varNo, boolean isStatic)
343         throws CompileError
344     {
345         return gen.recordParams(params, isStatic, "$", "$args", "$$",
346                                 use0, varNo, target, stable);
347     }
348
349     /**
350      * Prepares to use cast $r, $w, $_, and $type.
351      * $type is made to represent the specified return type.
352      * It also enables to write a return statement with a return value
353      * for void method.
354      *
355      * <p>If the return type is void, ($r) does nothing.
356      * The type of $_ is java.lang.Object.
357      *
358      * @param type the return type.
359      * @param useResultVar true if $_ is used.
360      * @return -1 or the variable index assigned to $_.
361      * @see #recordType(CtClass)
362      */

363     public int recordReturnType(CtClass type, boolean useResultVar)
364         throws CompileError
365     {
366         gen.recordType(type);
367         return gen.recordReturnType(type, "$r",
368                         (useResultVar ? resultVarName : null), stable);
369     }
370
371     /**
372      * Prepares to use $type. Note that recordReturnType() overwrites
373      * the value of $type.
374      *
375      * @param t the type represented by $type.
376      */

377     public void recordType(CtClass t) {
378         gen.recordType(t);
379     }
380
381     /**
382      * Makes the given variable available.
383      *
384      * @param type variable type
385      * @param name variable name
386      */

387     public int recordVariable(CtClass type, String JavaDoc name)
388         throws CompileError
389     {
390         return gen.recordVariable(type, name, stable);
391     }
392
393     /**
394      * Prepares to use $proceed().
395      * If the return type of $proceed() is void, null is pushed on the
396      * stack.
397      *
398      * @param target an expression specifying the target object.
399      * if null, "this" is the target.
400      * @param method the method name.
401      */

402     public void recordProceed(String JavaDoc target, String JavaDoc method)
403         throws CompileError
404     {
405         Parser p = new Parser(new Lex(target));
406         final ASTree texpr = p.parseExpression(stable);
407         final String JavaDoc m = method;
408
409         ProceedHandler h = new ProceedHandler() {
410                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
411                     throws CompileError
412                 {
413                     ASTree expr = new Member(m);
414                     if (texpr != null)
415                         expr = Expr.make('.', texpr, expr);
416
417                     expr = CallExpr.makeCall(expr, args);
418                     gen.compileExpr(expr);
419                     gen.addNullIfVoid();
420                 }
421
422                 public void setReturnType(JvstTypeChecker check, ASTList args)
423                     throws CompileError
424                 {
425                     ASTree expr = new Member(m);
426                     if (texpr != null)
427                         expr = Expr.make('.', texpr, expr);
428
429                     expr = CallExpr.makeCall(expr, args);
430                     expr.accept(check);
431                     check.addNullIfVoid();
432                 }
433             };
434
435         gen.setProceedHandler(h, proceedName);
436     }
437
438     /**
439      * Prepares to use $proceed() representing a static method.
440      * If the return type of $proceed() is void, null is pushed on the
441      * stack.
442      *
443      * @param targetClass the fully-qualified dot-separated name
444      * of the class declaring the method.
445      * @param method the method name.
446      */

447     public void recordStaticProceed(String JavaDoc targetClass, String JavaDoc method)
448         throws CompileError
449     {
450         final String JavaDoc c = targetClass;
451         final String JavaDoc m = method;
452
453         ProceedHandler h = new ProceedHandler() {
454                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
455                     throws CompileError
456                 {
457                     Expr expr = Expr.make(TokenId.MEMBER,
458                                           new Symbol(c), new Member(m));
459                     expr = CallExpr.makeCall(expr, args);
460                     gen.compileExpr(expr);
461                     gen.addNullIfVoid();
462                 }
463
464                 public void setReturnType(JvstTypeChecker check, ASTList args)
465                     throws CompileError
466                 {
467                     Expr expr = Expr.make(TokenId.MEMBER,
468                                           new Symbol(c), new Member(m));
469                     expr = CallExpr.makeCall(expr, args);
470                     expr.accept(check);
471                     check.addNullIfVoid();
472                 }
473             };
474
475         gen.setProceedHandler(h, proceedName);
476     }
477
478     /**
479      * Prepares to use $proceed() representing a private/super's method.
480      * If the return type of $proceed() is void, null is pushed on the
481      * stack. This method is for methods invoked by INVOKESPECIAL.
482      *
483      * @param target an expression specifying the target object.
484      * if null, "this" is the target.
485      * @param classname the class name declaring the method.
486      * @param methodname the method name.
487      * @param descriptor the method descriptor.
488      */

489     public void recordSpecialProceed(String JavaDoc target, String JavaDoc classname,
490                                      String JavaDoc methodname, String JavaDoc descriptor)
491         throws CompileError
492     {
493         Parser p = new Parser(new Lex(target));
494         final ASTree texpr = p.parseExpression(stable);
495         final String JavaDoc cname = classname;
496         final String JavaDoc method = methodname;
497         final String JavaDoc desc = descriptor;
498
499         ProceedHandler h = new ProceedHandler() {
500                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
501                     throws CompileError
502                 {
503                     gen.compileInvokeSpecial(texpr, cname, method, desc, args);
504                 }
505
506                 public void setReturnType(JvstTypeChecker c, ASTList args)
507                     throws CompileError
508                 {
509                     c.compileInvokeSpecial(texpr, cname, method, desc, args);
510                 }
511
512             };
513
514         gen.setProceedHandler(h, proceedName);
515     }
516
517     /**
518      * Prepares to use $proceed().
519      */

520     public void recordProceed(ProceedHandler h) {
521         gen.setProceedHandler(h, proceedName);
522     }
523
524     /**
525      * Compiles a statement (or a block).
526      * <code>recordParams()</code> must be called before invoking
527      * this method.
528      *
529      * <p>Local variables that are not declared
530      * in the compiled source text might not be accessible within that
531      * source text. Fields and method parameters ($0, $1, ..) are available.
532      */

533     public void compileStmnt(String JavaDoc src) throws CompileError {
534         Parser p = new Parser(new Lex(src));
535         SymbolTable stb = new SymbolTable(stable);
536         while (p.hasMore()) {
537             Stmnt s = p.parseStatement(stb);
538             if (s != null)
539                 s.accept(gen);
540         }
541     }
542
543     /**
544      * Compiles an exression. <code>recordParams()</code> must be
545      * called before invoking this method.
546      *
547      * <p>Local variables are not accessible
548      * within the compiled source text. Fields and method parameters
549      * ($0, $1, ..) are available if <code>recordParams()</code>
550      * have been invoked.
551      */

552     public void compileExpr(String JavaDoc src) throws CompileError {
553         Parser p = new Parser(new Lex(src));
554         ASTree e = p.parseExpression(stable);
555         compileExpr(e);
556     }
557
558     /**
559      * Compiles an exression. <code>recordParams()</code> must be
560      * called before invoking this method.
561      *
562      * <p>Local variables are not accessible
563      * within the compiled source text. Fields and method parameters
564      * ($0, $1, ..) are available if <code>recordParams()</code>
565      * have been invoked.
566      */

567     public void compileExpr(ASTree e) throws CompileError {
568         if (e != null)
569             e.accept(gen);
570     }
571 }
572
Popular Tags