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