/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