• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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.BadBytecode;
31 import javassist.bytecode.Opcode;
32 import javassist.NotFoundException;
33 
34 import javassist.compiler.ast.*;
35 
36 public class Javac {
37     JvstCodeGen gen;
38     SymbolTable stable;
39     private Bytecode bytecode;
40 
41     public static final String param0Name = "$0";
42     public static final String resultVarName = "$_";
43     public static final String proceedName = "$proceed";
44 
45     /**
46      * Constructs a compiler.
47      *
48      * @param thisClass         the class that a compiled method/field
49      *                          belongs to.
50      */
Javac(CtClass thisClass)51     public Javac(CtClass thisClass) {
52         this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0),
53              thisClass);
54     }
55 
56     /**
57      * Constructs a compiler.
58      * The produced bytecode is stored in the <code>Bytecode</code> object
59      * specified by <code>b</code>.
60      *
61      * @param thisClass         the class that a compiled method/field
62      *                          belongs to.
63      */
Javac(Bytecode b, CtClass thisClass)64     public Javac(Bytecode b, CtClass thisClass) {
65         gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool());
66         stable = new SymbolTable();
67         bytecode = b;
68     }
69 
70     /**
71      * Returns the produced bytecode.
72      */
getBytecode()73     public Bytecode getBytecode() { return bytecode; }
74 
75     /**
76      * Compiles a method, constructor, or field declaration
77      * to a class.
78      * A field declaration can declare only one field.
79      *
80      * <p>In a method or constructor body, $0, $1, ... and $_
81      * are not available.
82      *
83      * @return          a <code>CtMethod</code>, <code>CtConstructor</code>,
84      *                  or <code>CtField</code> object.
85      * @see #recordProceed(String,String)
86      */
compile(String src)87     public CtMember compile(String src) throws CompileError {
88         Parser p = new Parser(new Lex(src));
89         ASTList mem = p.parseMember1(stable);
90         try {
91             if (mem instanceof FieldDecl)
92                 return compileField((FieldDecl)mem);
93             else {
94                 CtBehavior cb = compileMethod(p, (MethodDecl)mem);
95                 CtClass decl = cb.getDeclaringClass();
96                 cb.getMethodInfo2()
97                   .rebuildStackMapIf6(decl.getClassPool(),
98                                       decl.getClassFile2());
99                 return cb;
100             }
101         }
102         catch (BadBytecode bb) {
103             throw new CompileError(bb.getMessage());
104         }
105         catch (CannotCompileException e) {
106             throw new CompileError(e.getMessage());
107         }
108     }
109 
110     public static class CtFieldWithInit extends CtField {
111         private ASTree init;
112 
CtFieldWithInit(CtClass type, String name, CtClass declaring)113         CtFieldWithInit(CtClass type, String name, CtClass declaring)
114             throws CannotCompileException
115         {
116             super(type, name, declaring);
117             init = null;
118         }
119 
setInit(ASTree i)120         protected void setInit(ASTree i) { init = i; }
121 
getInitAST()122         protected ASTree getInitAST() {
123             return init;
124         }
125     }
126 
compileField(FieldDecl fd)127     private CtField compileField(FieldDecl fd)
128         throws CompileError, CannotCompileException
129     {
130         CtFieldWithInit f;
131         Declarator d = fd.getDeclarator();
132         f = new CtFieldWithInit(gen.resolver.lookupClass(d),
133                                 d.getVariable().get(), gen.getThisClass());
134         f.setModifiers(MemberResolver.getModifiers(fd.getModifiers()));
135         if (fd.getInit() != null)
136             f.setInit(fd.getInit());
137 
138         return f;
139     }
140 
compileMethod(Parser p, MethodDecl md)141     private CtBehavior compileMethod(Parser p, MethodDecl md)
142         throws CompileError
143     {
144         int mod = MemberResolver.getModifiers(md.getModifiers());
145         CtClass[] plist = gen.makeParamList(md);
146         CtClass[] tlist = gen.makeThrowsList(md);
147         recordParams(plist, Modifier.isStatic(mod));
148         md = p.parseMethod2(stable, md);
149         try {
150             if (md.isConstructor()) {
151                 CtConstructor cons = new CtConstructor(plist,
152                                                    gen.getThisClass());
153                 cons.setModifiers(mod);
154                 md.accept(gen);
155                 cons.getMethodInfo().setCodeAttribute(
156                                         bytecode.toCodeAttribute());
157                 cons.setExceptionTypes(tlist);
158                 return cons;
159             }
160             else {
161                 Declarator r = md.getReturn();
162                 CtClass rtype = gen.resolver.lookupClass(r);
163                 recordReturnType(rtype, false);
164                 CtMethod method = new CtMethod(rtype, r.getVariable().get(),
165                                            plist, gen.getThisClass());
166                 method.setModifiers(mod);
167                 gen.setThisMethod(method);
168                 md.accept(gen);
169                 if (md.getBody() != null)
170                     method.getMethodInfo().setCodeAttribute(
171                                         bytecode.toCodeAttribute());
172                 else
173                     method.setModifiers(mod | Modifier.ABSTRACT);
174 
175                 method.setExceptionTypes(tlist);
176                 return method;
177             }
178         }
179         catch (NotFoundException e) {
180             throw new CompileError(e.toString());
181         }
182     }
183 
184     /**
185      * Compiles a method (or constructor) body.
186      *
187      * @src	a single statement or a block.
188      *          If null, this method produces a body returning zero or null.
189      */
compileBody(CtBehavior method, String src)190     public Bytecode compileBody(CtBehavior method, String src)
191         throws CompileError
192     {
193         try {
194             int mod = method.getModifiers();
195             recordParams(method.getParameterTypes(), Modifier.isStatic(mod));
196 
197             CtClass rtype;
198             if (method instanceof CtMethod) {
199                 gen.setThisMethod((CtMethod)method);
200                 rtype = ((CtMethod)method).getReturnType();
201             }
202             else
203                 rtype = CtClass.voidType;
204 
205             recordReturnType(rtype, false);
206             boolean isVoid = rtype == CtClass.voidType;
207 
208             if (src == null)
209                 makeDefaultBody(bytecode, rtype);
210             else {
211                 Parser p = new Parser(new Lex(src));
212                 SymbolTable stb = new SymbolTable(stable);
213                 Stmnt s = p.parseStatement(stb);
214                 if (p.hasMore())
215                     throw new CompileError(
216                         "the method/constructor body must be surrounded by {}");
217 
218                 boolean callSuper = false;
219                 if (method instanceof CtConstructor)
220                     callSuper = !((CtConstructor)method).isClassInitializer();
221 
222                 gen.atMethodBody(s, callSuper, isVoid);
223             }
224 
225             return bytecode;
226         }
227         catch (NotFoundException e) {
228             throw new CompileError(e.toString());
229         }
230     }
231 
makeDefaultBody(Bytecode b, CtClass type)232     private static void makeDefaultBody(Bytecode b, CtClass type) {
233         int op;
234         int value;
235         if (type instanceof CtPrimitiveType) {
236             CtPrimitiveType pt = (CtPrimitiveType)type;
237             op = pt.getReturnOp();
238             if (op == Opcode.DRETURN)
239                 value = Opcode.DCONST_0;
240             else if (op == Opcode.FRETURN)
241                 value = Opcode.FCONST_0;
242             else if (op == Opcode.LRETURN)
243                 value = Opcode.LCONST_0;
244             else if (op == Opcode.RETURN)
245                 value = Opcode.NOP;
246             else
247                 value = Opcode.ICONST_0;
248         }
249         else {
250             op = Opcode.ARETURN;
251             value = Opcode.ACONST_NULL;
252         }
253 
254         if (value != Opcode.NOP)
255             b.addOpcode(value);
256 
257         b.addOpcode(op);
258     }
259 
260     /**
261      * Records local variables available at the specified program counter.
262      * If the LocalVariableAttribute is not available, this method does not
263      * record any local variable.  It only returns false.
264      *
265      * @param pc    program counter (&gt;= 0)
266      * @return false if the CodeAttribute does not include a
267      *              LocalVariableAttribute.
268      */
recordLocalVariables(CodeAttribute ca, int pc)269     public boolean recordLocalVariables(CodeAttribute ca, int pc)
270         throws CompileError
271     {
272         LocalVariableAttribute va
273             = (LocalVariableAttribute)
274               ca.getAttribute(LocalVariableAttribute.tag);
275         if (va == null)
276             return false;
277 
278         int n = va.tableLength();
279         for (int i = 0; i < n; ++i) {
280             int start = va.startPc(i);
281             int len = va.codeLength(i);
282             if (start <= pc && pc < start + len)
283                 gen.recordVariable(va.descriptor(i), va.variableName(i),
284                                    va.index(i), stable);
285         }
286 
287         return true;
288     }
289 
290     /**
291      * Records parameter names if the LocalVariableAttribute is available.
292      * It returns false unless the LocalVariableAttribute is available.
293      *
294      * @param numOfLocalVars    the number of local variables used
295      *                          for storing the parameters.
296      * @return false if the CodeAttribute does not include a
297      *              LocalVariableAttribute.
298      */
recordParamNames(CodeAttribute ca, int numOfLocalVars)299     public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars)
300         throws CompileError
301     {
302         LocalVariableAttribute va
303             = (LocalVariableAttribute)
304               ca.getAttribute(LocalVariableAttribute.tag);
305         if (va == null)
306             return false;
307 
308         int n = va.tableLength();
309         for (int i = 0; i < n; ++i) {
310             int index = va.index(i);
311             if (index < numOfLocalVars)
312                 gen.recordVariable(va.descriptor(i), va.variableName(i),
313                                    index, stable);
314         }
315 
316         return true;
317     }
318 
319 
320     /**
321      * Makes variables $0 (this), $1, $2, ..., and $args represent method
322      * parameters.  $args represents an array of all the parameters.
323      * It also makes $$ available as a parameter list of method call.
324      *
325      * <p>This must be called before calling <code>compileStmnt()</code> and
326      * <code>compileExpr()</code>.  The correct value of
327      * <code>isStatic</code> must be recorded before compilation.
328      * <code>maxLocals</code> is updated to include $0,...
329      */
recordParams(CtClass[] params, boolean isStatic)330     public int recordParams(CtClass[] params, boolean isStatic)
331         throws CompileError
332     {
333         return gen.recordParams(params, isStatic, "$", "$args", "$$", stable);
334     }
335 
336     /**
337      * Makes variables $0, $1, $2, ..., and $args represent method
338      * parameters.  $args represents an array of all the parameters.
339      * It also makes $$ available as a parameter list of method call.
340      * $0 can represent a local variable other than THIS (variable 0).
341      * $class is also made available.
342      *
343      * <p>This must be called before calling <code>compileStmnt()</code> and
344      * <code>compileExpr()</code>.  The correct value of
345      * <code>isStatic</code> must be recorded before compilation.
346      * <code>maxLocals</code> is updated to include $0,...
347      *
348      * @paaram use0     true if $0 is used.
349      * @param varNo     the register number of $0 (use0 is true)
350      *                          or $1 (otherwise).
351      * @param target    the type of $0 (it can be null if use0 is false).
352      *                  It is used as the name of the type represented
353      *                  by $class.
354      * @param isStatic  true if the method in which the compiled bytecode
355      *                  is embedded is static.
356      */
recordParams(String target, CtClass[] params, boolean use0, int varNo, boolean isStatic)357     public int recordParams(String target, CtClass[] params,
358                              boolean use0, int varNo, boolean isStatic)
359         throws CompileError
360     {
361         return gen.recordParams(params, isStatic, "$", "$args", "$$",
362                                 use0, varNo, target, stable);
363     }
364 
365     /**
366      * Sets <code>maxLocals</code> to <code>max</code>.
367      * This method tells the compiler the local variables that have been
368      * allocated for the rest of the code.  When the compiler needs
369      * new local variables, the local variables at the index <code>max</code>,
370      * <code>max + 1</code>, ... are assigned.
371      *
372      * <p>This method is indirectly called by <code>recordParams</code>.
373      */
setMaxLocals(int max)374     public void setMaxLocals(int max) {
375         gen.setMaxLocals(max);
376     }
377 
378     /**
379      * Prepares to use cast $r, $w, $_, and $type.
380      * $type is made to represent the specified return type.
381      * It also enables to write a return statement with a return value
382      * for void method.
383      *
384      * <p>If the return type is void, ($r) does nothing.
385      * The type of $_ is java.lang.Object.
386      *
387      * @param type              the return type.
388      * @param useResultVar      true if $_ is used.
389      * @return          -1 or the variable index assigned to $_.
390      * @see #recordType(CtClass)
391      */
recordReturnType(CtClass type, boolean useResultVar)392     public int recordReturnType(CtClass type, boolean useResultVar)
393         throws CompileError
394     {
395         gen.recordType(type);
396         return gen.recordReturnType(type, "$r",
397                         (useResultVar ? resultVarName : null), stable);
398     }
399 
400     /**
401      * Prepares to use $type.  Note that recordReturnType() overwrites
402      * the value of $type.
403      *
404      * @param t     the type represented by $type.
405      */
recordType(CtClass t)406     public void recordType(CtClass t) {
407         gen.recordType(t);
408     }
409 
410     /**
411      * Makes the given variable available.
412      *
413      * @param type      variable type
414      * @param name      variable name
415      */
recordVariable(CtClass type, String name)416     public int recordVariable(CtClass type, String name)
417         throws CompileError
418     {
419         return gen.recordVariable(type, name, stable);
420     }
421 
422     /**
423      * Prepares to use $proceed().
424      * If the return type of $proceed() is void, null is pushed on the
425      * stack.
426      *
427      * @param target    an expression specifying the target object.
428      *                          if null, "this" is the target.
429      * @param method    the method name.
430      */
recordProceed(String target, String method)431     public void recordProceed(String target, String method)
432         throws CompileError
433     {
434         Parser p = new Parser(new Lex(target));
435         final ASTree texpr = p.parseExpression(stable);
436         final String m = method;
437 
438         ProceedHandler h = new ProceedHandler() {
439                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
440                     throws CompileError
441                 {
442                     ASTree expr = new Member(m);
443                     if (texpr != null)
444                         expr = Expr.make('.', texpr, expr);
445 
446                     expr = CallExpr.makeCall(expr, args);
447                     gen.compileExpr(expr);
448                     gen.addNullIfVoid();
449                 }
450 
451                 public void setReturnType(JvstTypeChecker check, ASTList args)
452                     throws CompileError
453                 {
454                     ASTree expr = new Member(m);
455                     if (texpr != null)
456                         expr = Expr.make('.', texpr, expr);
457 
458                     expr = CallExpr.makeCall(expr, args);
459                     expr.accept(check);
460                     check.addNullIfVoid();
461                 }
462             };
463 
464         gen.setProceedHandler(h, proceedName);
465     }
466 
467     /**
468      * Prepares to use $proceed() representing a static method.
469      * If the return type of $proceed() is void, null is pushed on the
470      * stack.
471      *
472      * @param targetClass    the fully-qualified dot-separated name
473      *				of the class declaring the method.
474      * @param method         the method name.
475      */
recordStaticProceed(String targetClass, String method)476     public void recordStaticProceed(String targetClass, String method)
477         throws CompileError
478     {
479         final String c = targetClass;
480         final String m = method;
481 
482         ProceedHandler h = new ProceedHandler() {
483                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
484                     throws CompileError
485                 {
486                     Expr expr = Expr.make(TokenId.MEMBER,
487                                           new Symbol(c), new Member(m));
488                     expr = CallExpr.makeCall(expr, args);
489                     gen.compileExpr(expr);
490                     gen.addNullIfVoid();
491                 }
492 
493                 public void setReturnType(JvstTypeChecker check, ASTList args)
494                     throws CompileError
495                 {
496                     Expr expr = Expr.make(TokenId.MEMBER,
497                                           new Symbol(c), new Member(m));
498                     expr = CallExpr.makeCall(expr, args);
499                     expr.accept(check);
500                     check.addNullIfVoid();
501                 }
502             };
503 
504         gen.setProceedHandler(h, proceedName);
505     }
506 
507     /**
508      * Prepares to use $proceed() representing a private/super's method.
509      * If the return type of $proceed() is void, null is pushed on the
510      * stack.  This method is for methods invoked by INVOKESPECIAL.
511      *
512      * @param target    an expression specifying the target object.
513      *                          if null, "this" is the target.
514      * @param classname	    the class name declaring the method.
515      * @param methodname    the method name.
516      * @param descriptor    the method descriptor.
517      */
recordSpecialProceed(String target, String classname, String methodname, String descriptor)518     public void recordSpecialProceed(String target, String classname,
519                                      String methodname, String descriptor)
520         throws CompileError
521     {
522         Parser p = new Parser(new Lex(target));
523         final ASTree texpr = p.parseExpression(stable);
524         final String cname = classname;
525         final String method = methodname;
526         final String desc = descriptor;
527 
528         ProceedHandler h = new ProceedHandler() {
529                 public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
530                     throws CompileError
531                 {
532                     gen.compileInvokeSpecial(texpr, cname, method, desc, args);
533                 }
534 
535                 public void setReturnType(JvstTypeChecker c, ASTList args)
536                     throws CompileError
537                 {
538                     c.compileInvokeSpecial(texpr, cname, method, desc, args);
539                 }
540 
541             };
542 
543         gen.setProceedHandler(h, proceedName);
544     }
545 
546     /**
547      * Prepares to use $proceed().
548      */
recordProceed(ProceedHandler h)549     public void recordProceed(ProceedHandler h) {
550         gen.setProceedHandler(h, proceedName);
551     }
552 
553     /**
554      * Compiles a statement (or a block).
555      * <code>recordParams()</code> must be called before invoking
556      * this method.
557      *
558      * <p>Local variables that are not declared
559      * in the compiled source text might not be accessible within that
560      * source text.  Fields and method parameters ($0, $1, ..) are available.
561      */
compileStmnt(String src)562     public void compileStmnt(String src) throws CompileError {
563         Parser p = new Parser(new Lex(src));
564         SymbolTable stb = new SymbolTable(stable);
565         while (p.hasMore()) {
566             Stmnt s = p.parseStatement(stb);
567             if (s != null)
568                 s.accept(gen);
569         }
570     }
571 
572     /**
573      * Compiles an exression.  <code>recordParams()</code> must be
574      * called before invoking this method.
575      *
576      * <p>Local variables are not accessible
577      * within the compiled source text.  Fields and method parameters
578      * ($0, $1, ..) are available if <code>recordParams()</code>
579      * have been invoked.
580      */
compileExpr(String src)581     public void compileExpr(String src) throws CompileError {
582         ASTree e = parseExpr(src, stable);
583         compileExpr(e);
584     }
585 
586     /**
587      * Parsers an expression.
588      */
parseExpr(String src, SymbolTable st)589     public static ASTree parseExpr(String src, SymbolTable st)
590         throws CompileError
591     {
592         Parser p = new Parser(new Lex(src));
593         return p.parseExpression(st);
594     }
595 
596     /**
597      * Compiles an exression.  <code>recordParams()</code> must be
598      * called before invoking this method.
599      *
600      * <p>Local variables are not accessible
601      * within the compiled source text.  Fields and method parameters
602      * ($0, $1, ..) are available if <code>recordParams()</code>
603      * have been invoked.
604      */
compileExpr(ASTree e)605     public void compileExpr(ASTree e) throws CompileError {
606         if (e != null)
607             gen.compileExpr(e);
608     }
609 }
610