/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- 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,
 * or the Apache License Version 2.0.
 *
 * 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.CannotCompileException;
import javassist.CtBehavior;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMember;
import javassist.CtMethod;
import javassist.CtPrimitiveType;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.BadBytecode;
import javassist.bytecode.Bytecode;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.Opcode;
import javassist.compiler.ast.ASTList;
import javassist.compiler.ast.ASTree;
import javassist.compiler.ast.CallExpr;
import javassist.compiler.ast.Declarator;
import javassist.compiler.ast.Expr;
import javassist.compiler.ast.FieldDecl;
import javassist.compiler.ast.Member;
import javassist.compiler.ast.MethodDecl;
import javassist.compiler.ast.Stmnt;
import javassist.compiler.ast.Symbol;

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.
Params:
  • thisClass – the class that a compiled method/field belongs to.
/** * 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.
Params:
  • thisClass – the class that a compiled method/field belongs to.
/** * Constructs a compiler. * The produced bytecode is stored in the <code>Bytecode</code> object * specified by <code>b</code>. * * @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.
/** * 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.

See Also:
Returns: a CtMethod, CtConstructor, or CtField object.
/** * Compiles a method, constructor, or field declaration * to a class. * A field declaration can declare only one field. * * <p>In a method or constructor body, $0, $1, ... and $_ * are not available. * * @return a <code>CtMethod</code>, <code>CtConstructor</code>, * or <code>CtField</code> 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); CtBehavior cb = compileMethod(p, (MethodDecl)mem); CtClass decl = cb.getDeclaringClass(); cb.getMethodInfo2() .rebuildStackMapIf6(decl.getClassPool(), decl.getClassFile2()); return cb; } catch (BadBytecode bb) { throw new CompileError(bb.getMessage()); } 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; } @Override protected ASTree getInitAST() { return init; } } private CtField compileField(FieldDecl fd) throws CompileError, CannotCompileException { CtFieldWithInit f; Declarator d = fd.getDeclarator(); f = new CtFieldWithInit(gen.resolver.lookupClass(d), d.getVariable().get(), gen.getThisClass()); f.setModifiers(MemberResolver.getModifiers(fd.getModifiers())); if (fd.getInit() != null) f.setInit(fd.getInit()); return f; } private CtBehavior compileMethod(Parser p, MethodDecl md) throws CompileError { int mod = MemberResolver.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; } Declarator r = md.getReturn(); CtClass rtype = gen.resolver.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.
Params:
  • src – a single statement or a block. If null, this method produces a body returning zero or null.
/** * Compiles a method (or constructor) body. * * @param src a single statement or a block. * If null, this method produces a body returning zero or null. */
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; if (src == null) makeDefaultBody(bytecode, rtype); else { Parser p = new Parser(new Lex(src)); SymbolTable stb = new SymbolTable(stable); Stmnt s = p.parseStatement(stb); if (p.hasMore()) throw new CompileError( "the method/constructor body must be surrounded by {}"); boolean callSuper = false; if (method instanceof CtConstructor) callSuper = !((CtConstructor)method).isClassInitializer(); gen.atMethodBody(s, callSuper, isVoid); } return bytecode; } catch (NotFoundException e) { throw new CompileError(e.toString()); } } private static void makeDefaultBody(Bytecode b, CtClass type) { int op; int value; if (type instanceof CtPrimitiveType) { CtPrimitiveType pt = (CtPrimitiveType)type; op = pt.getReturnOp(); if (op == Opcode.DRETURN) value = Opcode.DCONST_0; else if (op == Opcode.FRETURN) value = Opcode.FCONST_0; else if (op == Opcode.LRETURN) value = Opcode.LCONST_0; else if (op == Opcode.RETURN) value = Opcode.NOP; else value = Opcode.ICONST_0; } else { op = Opcode.ARETURN; value = Opcode.ACONST_NULL; } if (value != Opcode.NOP) b.addOpcode(value); b.addOpcode(op); }
Records local variables available at the specified program counter. If the LocalVariableAttribute is not available, this method does not record any local variable. It only returns false.
Params:
  • pc – program counter (>= 0)
Returns:false if the CodeAttribute does not include a LocalVariableAttribute.
/** * Records local variables available at the specified program counter. * If the LocalVariableAttribute is not available, this method does not * record any local variable. It only returns false. * * @param pc program counter (&gt;= 0) * @return false if the CodeAttribute does not include a * LocalVariableAttribute. */
public boolean recordLocalVariables(CodeAttribute ca, int pc) throws CompileError { LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va == null) return false; int n = va.tableLength(); for (int i = 0; i < n; ++i) { int start = va.startPc(i); int len = va.codeLength(i); if (start <= pc && pc < start + len) gen.recordVariable(va.descriptor(i), va.variableName(i), va.index(i), stable); } return true; }
Records parameter names if the LocalVariableAttribute is available. It returns false unless the LocalVariableAttribute is available.
Params:
  • numOfLocalVars – the number of local variables used for storing the parameters.
Returns:false if the CodeAttribute does not include a LocalVariableAttribute.
/** * Records parameter names if the LocalVariableAttribute is available. * It returns false unless the LocalVariableAttribute is available. * * @param numOfLocalVars the number of local variables used * for storing the parameters. * @return false if the CodeAttribute does not include a * LocalVariableAttribute. */
public boolean recordParamNames(CodeAttribute ca, int numOfLocalVars) throws CompileError { LocalVariableAttribute va = (LocalVariableAttribute) ca.getAttribute(LocalVariableAttribute.tag); if (va == null) return false; int n = va.tableLength(); for (int i = 0; i < n; ++i) { int index = va.index(i); if (index < numOfLocalVars) gen.recordVariable(va.descriptor(i), va.variableName(i), index, stable); } return true; }
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. maxLocals is updated to include $0,...

/** * 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. * * <p>This must be called before calling <code>compileStmnt()</code> and * <code>compileExpr()</code>. The correct value of * <code>isStatic</code> must be recorded before compilation. * <code>maxLocals</code> is updated to include $0,... */
public int recordParams(CtClass[] params, boolean isStatic) throws CompileError { return 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). $class is also made available.

This must be called before calling compileStmnt() and compileExpr(). The correct value of isStatic must be recorded before compilation. maxLocals is updated to include $0,...

Params:
  • use0 – true if $0 is used.
  • varNo – the register number of $0 (use0 is true) or $1 (otherwise).
  • target – the type of $0 (it can be null if use0 is false). It is used as the name of the type represented by $class.
  • isStatic – true if the method in which the compiled bytecode is embedded is static.
/** * 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). * $class is also made available. * * <p>This must be called before calling <code>compileStmnt()</code> and * <code>compileExpr()</code>. The correct value of * <code>isStatic</code> must be recorded before compilation. * <code>maxLocals</code> is updated to include $0,... * * @param 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). * It is used as the name of the type represented * by $class. * @param isStatic true if the method in which the compiled bytecode * is embedded is static. */
public int recordParams(String target, CtClass[] params, boolean use0, int varNo, boolean isStatic) throws CompileError { return gen.recordParams(params, isStatic, "$", "$args", "$$", use0, varNo, target, stable); }
Sets maxLocals to max. This method tells the compiler the local variables that have been allocated for the rest of the code. When the compiler needs new local variables, the local variables at the index max, max + 1, ... are assigned.

This method is indirectly called by recordParams.

/** * Sets <code>maxLocals</code> to <code>max</code>. * This method tells the compiler the local variables that have been * allocated for the rest of the code. When the compiler needs * new local variables, the local variables at the index <code>max</code>, * <code>max + 1</code>, ... are assigned. * * <p>This method is indirectly called by <code>recordParams</code>. */
public void setMaxLocals(int max) { gen.setMaxLocals(max); }
Prepares to use cast $r, $w, $_, and $type. $type is made to represent the specified return 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.

Params:
  • type – the return type.
  • useResultVar – true if $_ is used.
See Also:
Returns: -1 or the variable index assigned to $_.
/** * Prepares to use cast $r, $w, $_, and $type. * $type is made to represent the specified return type. * It also enables to write a return statement with a return value * for void method. * * <p>If the return type is void, ($r) does nothing. * The type of $_ is java.lang.Object. * * @param type the return type. * @param useResultVar true if $_ is used. * @return -1 or the variable index assigned to $_. * @see #recordType(CtClass) */
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.
Params:
  • t – the type represented by $type.
/** * Prepares to use $type. Note that recordReturnType() overwrites * the value of $type. * * @param t the type represented by $type. */
public void recordType(CtClass t) { gen.recordType(t); }
Makes the given variable available.
Params:
  • type – variable type
  • name – variable name
/** * 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.
Params:
  • target – an expression specifying the target object. if null, "this" is the target.
  • method – the method name.
/** * 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() { @Override 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 = CallExpr.makeCall(expr, args); gen.compileExpr(expr); gen.addNullIfVoid(); } @Override public void setReturnType(JvstTypeChecker check, ASTList args) throws CompileError { ASTree expr = new Member(m); if (texpr != null) expr = Expr.make('.', texpr, expr); expr = CallExpr.makeCall(expr, args); expr.accept(check); check.addNullIfVoid(); } }; gen.setProceedHandler(h, proceedName); }
Prepares to use $proceed() representing a static method. If the return type of $proceed() is void, null is pushed on the stack.
Params:
  • targetClass – the fully-qualified dot-separated name of the class declaring the method.
  • method – the method name.
/** * Prepares to use $proceed() representing a static method. * If the return type of $proceed() is void, null is pushed on the * stack. * * @param targetClass the fully-qualified dot-separated name * of the class declaring the method. * @param method the method name. */
public void recordStaticProceed(String targetClass, String method) throws CompileError { final String c = targetClass; final String m = method; ProceedHandler h = new ProceedHandler() { @Override public void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError { Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c), new Member(m)); expr = CallExpr.makeCall(expr, args); gen.compileExpr(expr); gen.addNullIfVoid(); } @Override public void setReturnType(JvstTypeChecker check, ASTList args) throws CompileError { Expr expr = Expr.make(TokenId.MEMBER, new Symbol(c), new Member(m)); expr = CallExpr.makeCall(expr, args); expr.accept(check); check.addNullIfVoid(); } }; gen.setProceedHandler(h, proceedName); }
Prepares to use $proceed() representing a private/super's method. If the return type of $proceed() is void, null is pushed on the stack. This method is for methods invoked by INVOKESPECIAL.
Params:
  • target – an expression specifying the target object. if null, "this" is the target.
  • classname – the class name declaring the method.
  • methodname – the method name.
  • descriptor – the method descriptor.
/** * Prepares to use $proceed() representing a private/super's method. * If the return type of $proceed() is void, null is pushed on the * stack. This method is for methods invoked by INVOKESPECIAL. * * @param target an expression specifying the target object. * if null, "this" is the target. * @param classname the class name declaring the method. * @param methodname the method name. * @param descriptor the method descriptor. */
public void recordSpecialProceed(String target, final String classname, final String methodname, final String descriptor, final int methodIndex) throws CompileError { Parser p = new Parser(new Lex(target)); final ASTree texpr = p.parseExpression(stable); ProceedHandler h = new ProceedHandler() { @Override public void doit(JvstCodeGen gen, Bytecode b, ASTList args) throws CompileError { gen.compileInvokeSpecial(texpr, methodIndex, descriptor, args); } @Override public void setReturnType(JvstTypeChecker c, ASTList args) throws CompileError { c.compileInvokeSpecial(texpr, classname, methodname, descriptor, args); } }; gen.setProceedHandler(h, proceedName); }
Prepares to use $proceed().
/** * 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 might not be accessible within that source text. Fields and method parameters ($0, $1, ..) are available.

/** * Compiles a statement (or a block). * <code>recordParams()</code> must be called before invoking * this method. * * <p>Local variables that are not declared * in the compiled source text might not be 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.

/** * Compiles an exression. <code>recordParams()</code> must be * called before invoking this method. * * <p>Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if <code>recordParams()</code> * have been invoked. */
public void compileExpr(String src) throws CompileError { ASTree e = parseExpr(src, stable); compileExpr(e); }
Parsers an expression.
/** * Parsers an expression. */
public static ASTree parseExpr(String src, SymbolTable st) throws CompileError { Parser p = new Parser(new Lex(src)); return p.parseExpression(st); }
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.

/** * Compiles an exression. <code>recordParams()</code> must be * called before invoking this method. * * <p>Local variables are not accessible * within the compiled source text. Fields and method parameters * ($0, $1, ..) are available if <code>recordParams()</code> * have been invoked. */
public void compileExpr(ASTree e) throws CompileError { if (e != null) gen.compileExpr(e); } }