123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999-2006 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.CtPrimitiveType;
- import javassist.CtMember;
- import javassist.CtField;
- import javassist.CtBehavior;
- import javassist.CtMethod;
- import javassist.CtConstructor;
- import javassist.CannotCompileException;
- import javassist.Modifier;
- import javassist.bytecode.Bytecode;
- import javassist.bytecode.CodeAttribute;
- import javassist.bytecode.LocalVariableAttribute;
- import javassist.bytecode.Opcode;
- 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 <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.
- */
- public Bytecode getBytecode() { return bytecode; }
-
- /**
- * 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);
- 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.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 CtMember 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;
- }
- else {
- 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.
- *
- * @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.
- *
- * @param pc program counter (>= 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.
- *
- * @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.
- *
- * <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.
- *
- * <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,...
- *
- * @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).
- * 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 <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.
- *
- * <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.
- *
- * @param t the type represented by $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 = CallExpr.makeCall(expr, args);
- gen.compileExpr(expr);
- gen.addNullIfVoid();
- }
-
- 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.
- *
- * @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() {
- 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();
- }
-
- 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.
- *
- * @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, String classname,
- String methodname, String descriptor)
- throws CompileError
- {
- Parser p = new Parser(new Lex(target));
- final ASTree texpr = p.parseExpression(stable);
- final String cname = classname;
- final String method = methodname;
- final String desc = descriptor;
-
- ProceedHandler h = new ProceedHandler() {
- public void doit(JvstCodeGen gen, Bytecode b, ASTList args)
- throws CompileError
- {
- gen.compileInvokeSpecial(texpr, cname, method, desc, args);
- }
-
- public void setReturnType(JvstTypeChecker c, ASTList args)
- throws CompileError
- {
- c.compileInvokeSpecial(texpr, cname, method, desc, args);
- }
-
- };
-
- gen.setProceedHandler(h, proceedName);
- }
-
- /**
- * Prepares to use $proceed().
- */
- public void recordProceed(ProceedHandler h) {
- gen.setProceedHandler(h, proceedName);
- }
-
- /**
- * 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. <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.
- */
- public static ASTree parseExpr(String src, SymbolTable st)
- throws CompileError
- {
- Parser p = new Parser(new Lex(src));
- return p.parseExpression(st);
- }
-
- /**
- * 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);
- }
- }
|