/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2003 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.compiler; import javassist.CtClass; import javassist.CtMember; import javassist.CtField; import javassist.CtBehavior; import javassist.CtMethod; import javassist.CtConstructor; import javassist.CannotCompileException; import javassist.ClassPool; import javassist.Modifier; import javassist.bytecode.Bytecode; import javassist.NotFoundException; import javassist.compiler.ast.*; public class Javac { JvstCodeGen gen; SymbolTable stable; private Bytecode bytecode; public static final String param0Name = "$0"; public static final String resultVarName = "$_"; public static final String proceedName = "$proceed"; /** * Constructs a compiler. * * @param thisClass the class that a compiled method/field * belongs to. */ public Javac(CtClass thisClass) { this(new Bytecode(thisClass.getClassFile2().getConstPool(), 0, 0), thisClass); } /** * Constructs a compiler. * The produced bytecode is stored in the Bytecode object * specified by b. * * @param thisClass the class that a compiled method/field * belongs to. */ public Javac(Bytecode b, CtClass thisClass) { gen = new JvstCodeGen(b, thisClass, thisClass.getClassPool()); stable = new SymbolTable(); bytecode = b; } /** * Returns the produced bytecode. */ public Bytecode getBytecode() { return bytecode; } /** * Compiles a method, constructor, or field declaration * to a class. * A field declaration can declare only one field. * *

In a method or constructor body, $0, $1, ... and $_ * are not available. * * @return a CtMethod, CtConstructor, * or CtField object. * @see #recordProceed(String,String) */ public CtMember compile(String src) throws CompileError { Parser p = new Parser(new Lex(src)); ASTList mem = p.parseMember1(stable); try { if (mem instanceof FieldDecl) return compileField((FieldDecl)mem); else return compileMethod(p, (MethodDecl)mem); } catch (CannotCompileException e) { throw new CompileError(e.getMessage()); } } public static class CtFieldWithInit extends CtField { private ASTree init; CtFieldWithInit(CtClass type, String name, CtClass declaring) throws CannotCompileException { super(type, name, declaring); init = null; } protected void setInit(ASTree i) { init = i; } protected ASTree getInitAST() { return init; } } private CtField compileField(FieldDecl fd) throws CompileError, CannotCompileException { CtFieldWithInit f; Declarator d = fd.getDeclarator(); f = new CtFieldWithInit(gen.lookupClass(d), d.getVariable().get(), gen.getThisClass()); f.setModifiers(gen.getModifiers(fd.getModifiers())); if (fd.getInit() != null) f.setInit(fd.getInit()); return f; } private CtMember compileMethod(Parser p, MethodDecl md) throws CompileError { int mod = gen.getModifiers(md.getModifiers()); CtClass[] plist = gen.makeParamList(md); CtClass[] tlist = gen.makeThrowsList(md); recordParams(plist, Modifier.isStatic(mod)); md = p.parseMethod2(stable, md); try { if (md.isConstructor()) { CtConstructor cons = new CtConstructor(plist, gen.getThisClass()); cons.setModifiers(mod); md.accept(gen); cons.getMethodInfo().setCodeAttribute( bytecode.toCodeAttribute()); cons.setExceptionTypes(tlist); return cons; } else { Declarator r = md.getReturn(); CtClass rtype = gen.lookupClass(r); recordReturnType(rtype, false); CtMethod method = new CtMethod(rtype, r.getVariable().get(), plist, gen.getThisClass()); method.setModifiers(mod); gen.setThisMethod(method); md.accept(gen); if (md.getBody() != null) method.getMethodInfo().setCodeAttribute( bytecode.toCodeAttribute()); else method.setModifiers(mod | Modifier.ABSTRACT); method.setExceptionTypes(tlist); return method; } } catch (NotFoundException e) { throw new CompileError(e.toString()); } } /** * Compiles a method (or constructor) body. */ public Bytecode compileBody(CtBehavior method, String src) throws CompileError { try { int mod = method.getModifiers(); recordParams(method.getParameterTypes(), Modifier.isStatic(mod)); CtClass rtype; if (method instanceof CtMethod) { gen.setThisMethod((CtMethod)method); rtype = ((CtMethod)method).getReturnType(); } else rtype = CtClass.voidType; recordReturnType(rtype, false); boolean isVoid = rtype == CtClass.voidType; Parser p = new Parser(new Lex(src)); SymbolTable stb = new SymbolTable(stable); Stmnt s = p.parseStatement(stb); gen.atMethodBody(s, method instanceof CtConstructor, isVoid); return bytecode; } catch (NotFoundException e) { throw new CompileError(e.toString()); } } /** * Makes variables $0 (this), $1, $2, ..., and $args represent method * parameters. $args represents an array of all the parameters. * It also makes $$ available as a parameter list of method call. * *

This must be called before calling compileStmnt() and * compileExpr(). The correct value of * isStatic must be recorded before compilation. */ public void recordParams(CtClass[] params, boolean isStatic) throws CompileError { gen.recordParams(params, isStatic, "$", "$args", "$$", stable); } /** * Makes variables $0, $1, $2, ..., and $args represent method * parameters. $args represents an array of all the parameters. * It also makes $$ available as a parameter list of method call. * $0 can represent a local variable other than THIS (variable 0). * *

This must be called before calling compileStmnt() and * compileExpr(). The correct value of * isStatic must be recorded before compilation. * * @paaram use0 true if $0 is used. * @param varNo the register number of $0 (use0 is true) * or $1 (otherwise). * @param target the type of $0 (it can be null if use0 is false). * @param isStatic true if the method in which the compiled bytecode * is embedded is static. */ public void recordParams(String target, CtClass[] params, boolean use0, int varNo, boolean isStatic) throws CompileError { gen.recordParams(params, isStatic, "$", "$args", "$$", use0, varNo, target, stable); } /** * Prepares to use cast $r, $w, $_, and $type. * It also enables to write a return statement with a return value * for void method. * *

If the return type is void, ($r) does nothing. * The type of $_ is java.lang.Object. * * @param useResultVar true if $_ is used. * @return -1 or the variable index assigned to $_. */ public int recordReturnType(CtClass type, boolean useResultVar) throws CompileError { gen.recordType(type); return gen.recordReturnType(type, "$r", (useResultVar ? resultVarName : null), stable); } /** * Prepares to use $type. Note that recordReturnType() overwrites * the value of $type. */ public void recordType(CtClass t) { gen.recordType(t); } /** * Makes the given variable available. * * @param type variable type * @param name variable name */ public int recordVariable(CtClass type, String name) throws CompileError { return gen.recordVariable(type, name, stable); } /** * Prepares to use $proceed(). * If the return type of $proceed() is void, null is pushed on the * stack. * * @param target an expression specifying the target object. * if null, "this" is the target. * @param method the method name. */ public void recordProceed(String target, String method) throws CompileError { Parser p = new Parser(new Lex(target)); final ASTree texpr = p.parseExpression(stable); final String m = method; ProceedHandler h = new ProceedHandler() { public void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError { ASTree expr = new Member(m); if (texpr != null) expr = Expr.make('.', texpr, expr); expr = Expr.make(TokenId.CALL, expr, args); expr.accept(gen); gen.addNullIfVoid(); } }; gen.setProceedHandler(h, proceedName); } /** * Prepares to use $proceed(). */ public void recordProceed(ProceedHandler h) { gen.setProceedHandler(h, proceedName); } /** * Compiles a statement (or a block). * recordParams() must be called before invoking * this method. * *

Local variables that are not declared * in the compiled source text are not accessible within that * source text. Fields and method parameters * ($0, $1, ..) are available. */ public void compileStmnt(String src) throws CompileError { Parser p = new Parser(new Lex(src)); SymbolTable stb = new SymbolTable(stable); // while (p.hasMore()) { Stmnt s = p.parseStatement(stb); if (s != null) s.accept(gen); // } } /** * Compiles an exression. recordParams() must be * called before invoking this method. * *

Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if recordParams() * have been invoked. */ public void compileExpr(String src) throws CompileError { Parser p = new Parser(new Lex(src)); ASTree e = p.parseExpression(stable); compileExpr(e); } /** * Compiles an exression. recordParams() must be * called before invoking this method. * *

Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if recordParams() * have been invoked. */ public void compileExpr(ASTree e) throws CompileError { if (e != null) e.accept(gen); } }