1
0
mirror of https://github.com/gryf/tagbar.git synced 2025-12-17 19:40:27 +01:00
Files
tagbar/tests/java/JilBuilder.java
2013-04-06 00:59:14 +13:00

1934 lines
68 KiB
Java

// This file is part of the Java Compiler Kit (JKit)
//
// The Java Compiler Kit is free software; you can
// redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// The Java Compiler Kit is distributed in the hope
// that it will be useful, but WITHOUT ANY WARRANTY; without
// even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public
// License along with the Java Compiler Kit; if not,
// write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// (C) David James Pearce, 2009.
package jkit.java.stages;
import java.util.*;
import jkit.compiler.FieldNotFoundException;
import static jkit.compiler.SyntaxError.*;
import static jkit.jil.util.Exprs.*;
import static jkit.jil.util.Types.*;
import jkit.compiler.ClassLoader;
import jkit.compiler.Clazz;
import jkit.compiler.SyntacticAttribute;
import jkit.java.io.JavaFile;
import jkit.java.tree.*;
import jkit.jil.tree.*;
import jkit.jil.tree.Type;
import jkit.jil.util.Types;
import jkit.util.Pair;
import jkit.util.Triple;
/**
* <p>
* The aim of this stage is to generate Jil code representing this class.
* </p>
*
* @author djp
*
*/
public class JilBuilder {
private ClassLoader loader = null;
private TypeSystem types = null;
/**
* This class is used to annotate an invoke expression with the list of
* checked (and possibly unchecked) exceptions it has declared to throw, as
* well as the type of the method to be invoked. It's sole purpose is to
* prevent us from having to re-traverse the class heirarchy during code
* generation.
*
* @author djp
*
*/
public static class MethodInfo implements SyntacticAttribute {
public final ArrayList<Type.Clazz> exceptions;
public Type.Function type;
public MethodInfo(List<Type.Clazz> e, Type.Function type) {
exceptions = new ArrayList<Type.Clazz>(e);
this.type = type;
}
}
private static class Scope {}
private static class LabelScope extends Scope {
public String label;
public LabelScope(String lab) {
label = lab;
}
}
private static class SwitchScope extends Scope {
public String exitLab;
public SwitchScope(String el) {
exitLab = el;
}
}
private static class LoopScope extends SwitchScope {
public String continueLab;
public LoopScope(String cl, String el) {
super(el);
continueLab = cl;
}
}
private static class ClassScope extends Scope {
public Type.Clazz type;
public ClassScope(Type.Clazz type) {
this.type = type;
}
}
private final Stack<Scope> scopes = new Stack<Scope>();
public JilBuilder(ClassLoader loader, TypeSystem types) {
this.loader = loader;
this.types = types;
}
public void apply(JavaFile file) {
// Now, traverse the declarations
for(Decl d : file.declarations()) {
doDeclaration(d, null);
}
}
protected void doDeclaration(Decl d, JilClass parent) {
try {
if(d instanceof Decl.JavaInterface) {
doInterface((Decl.JavaInterface)d);
} else if(d instanceof Decl.JavaEnum) {
doEnum((Decl.JavaEnum)d);
} else if(d instanceof Decl.JavaClass) {
doClass((Decl.JavaClass)d);
} else if(d instanceof Decl.JavaMethod) {
doMethod((Decl.JavaMethod)d, parent);
} else if(d instanceof Decl.JavaField) {
doField((Decl.JavaField)d, parent);
} else if (d instanceof Decl.InitialiserBlock) {
doInitialiserBlock((Decl.InitialiserBlock)d , parent);
} else if (d instanceof Decl.StaticInitialiserBlock) {
doStaticInitialiserBlock((Decl.StaticInitialiserBlock) d, parent);
} else {
syntax_error("internal failure (unknown declaration \"" + d
+ "\" encountered)", d);
}
} catch(Exception ex) {
internal_error(d,ex);
}
}
protected void doInterface(Decl.JavaInterface d) throws ClassNotFoundException {
doClass(d);
}
protected void doEnum(Decl.JavaEnum en) throws ClassNotFoundException {
doClass(en);
}
protected void doClass(Decl.JavaClass c) throws ClassNotFoundException {
Type.Clazz type = c.attribute(Type.Clazz.class);
scopes.push(new ClassScope(type));
// We, need to update the skeleton so that any methods and fields
// discovered below this are attributed to this class!
JilClass skeleton = (JilClass) loader.loadClass(type);
// I do fields after everything else, so as to simplify the process
// of adding field initialisers to constructors. This is because it
// means I can be sure that the constructor has been otherwise
// completely generated, so all I need is to add initialisers at
// beginning, after super call (if there is one).
ArrayList<Decl> fields = new ArrayList<Decl>();
for(Decl d : c.declarations()) {
if (!(d instanceof Decl.JavaField)
&& !(d instanceof Decl.InitialiserBlock)
&& !(d instanceof Decl.StaticInitialiserBlock)) {
doDeclaration(d, skeleton);
} else {
fields.add(d);
}
}
// Note, I iterate the field declarations in reverse order to ensure
// that field initialisers are added to constructors in the right
// order.
for(int i=fields.size();i>0;--i) {
Decl d = fields.get(i-1);
doDeclaration(d, skeleton);
}
scopes.pop();
}
protected void doMethod(Decl.JavaMethod d, JilClass parent) {
Type.Function type = d.attribute(Type.Function.class);
List<JilStmt> stmts = doStatement(d.body());
// simple hack here, for case when no return statement is provided.
if (type.returnType() instanceof Type.Void
&& (stmts.size() == 0 || !(stmts.get(stmts.size() - 1) instanceof JilStmt.Return))) {
stmts.add(new JilStmt.Return(null));
}
// First, off. If this is a constructor, then check whether there is an
// explicit super constructor call or not. If not, then add one.
if (d instanceof Decl.JavaConstructor) {
if(findSuperCall(stmts) == -1) {
stmts.add(0, new JilExpr.SpecialInvoke(new JilExpr.Variable("super", parent
.superClass()), "super", new ArrayList<JilExpr>(),
new Type.Function(T_VOID), T_VOID));
}
}
// Now, add this statement list to the jil method representing this java
// method.
String name = d instanceof Decl.JavaConstructor ? parent.name() : d.name();
for (JilMethod m : parent.methods()) {
if (m.name().equals(name) && m.type().equals(type)) {
m.body().addAll(stmts);
}
}
}
protected void doField(Decl.JavaField d, JilClass parent) {
Pair<JilExpr,List<JilStmt>> tmp = doExpression(d.initialiser());
Type fieldT = d.type().attribute(Type.class);
boolean isStatic = d.isStatic();
if(tmp != null) {
if(d.isStatic()) {
if(!(d.isFinal() && d.isConstant())) {
// This is a static field with an non-constant initialiser.
// Therefore, we need to add it to the static initialiser.
JilMethod staticInit = createStaticInitialiser(parent);
JilExpr.Deref df = new JilExpr.Deref(new JilExpr.ClassVariable(
parent.type()), d.name(), isStatic, fieldT, d
.attributes());
JilStmt.Assign ae = new JilStmt.Assign(df, tmp.first(), d
.attributes());
// add them at the beginning to get the right ordering.
staticInit.body().add(0,ae);
staticInit.body().addAll(0,tmp.second());
}
} else {
// This is a non-static field with an initialiser. Therefore, we
// need to add it to the beginning of all constructors. One issue is
// that, if the first statement of a constructor is a super call
// (which is should normally be), then we need to put the statements
// after that.
for(JilMethod m : parent.methods()) {
if(m.name().equals(parent.name())) {
List<JilStmt> body = m.body();
JilExpr.Deref df = new JilExpr.Deref(new JilExpr.Variable("this",
parent.type()), d.name(), isStatic, fieldT, d
.attributes());
JilStmt.Assign ae = new JilStmt.Assign(df, tmp.first(), d
.attributes());
int sc = findSuperCall(body)+1;
body.add(sc,ae);
body.addAll(sc,tmp.second());
}
}
}
}
}
protected void doInitialiserBlock(Decl.InitialiserBlock d, JilClass parent) {
ArrayList<JilStmt> stmts = new ArrayList<JilStmt>();
for (Stmt s : d.statements()) {
stmts.addAll(doStatement(s));
}
// This is a non-static initialiser block. Therefore, we
// need to add it to the beginning of all constructors. One issue is
// that, if the first statement of a constructor is a super call
// (which is should normally be), then we need to put the statements
// after that.
for(JilMethod m : parent.methods()) {
if(m.name().equals(parent.name())) {
List<JilStmt> body = m.body();
body.addAll(findSuperCall(body)+1,stmts);
}
}
}
protected void doStaticInitialiserBlock(Decl.StaticInitialiserBlock d, JilClass parent) {
ArrayList<JilStmt> stmts = new ArrayList<JilStmt>();
for (Stmt s : d.statements()) {
stmts.addAll(doStatement(s));
}
JilMethod m = createStaticInitialiser(parent);
// Again, add at beginning to ensure the right order.
m.body().addAll(0,stmts);
}
protected List<JilStmt> doStatement(Stmt e) {
try {
if(e instanceof Stmt.SynchronisedBlock) {
return doSynchronisedBlock((Stmt.SynchronisedBlock)e);
} else if(e instanceof Stmt.TryCatchBlock) {
return doTryCatchBlock((Stmt.TryCatchBlock)e);
} else if(e instanceof Stmt.Block) {
return doBlock((Stmt.Block)e);
} else if(e instanceof Stmt.VarDef) {
return doVarDef((Stmt.VarDef) e);
} else if(e instanceof Stmt.AssignmentOp) {
return doAssignmentOp((Stmt.AssignmentOp) e).second();
} else if(e instanceof Stmt.Assignment) {
return doAssignment((Stmt.Assignment) e).second();
} else if(e instanceof Stmt.Return) {
return doReturn((Stmt.Return) e);
} else if(e instanceof Stmt.Throw) {
return doThrow((Stmt.Throw) e);
} else if(e instanceof Stmt.Assert) {
return doAssert((Stmt.Assert) e);
} else if(e instanceof Stmt.Break) {
return doBreak((Stmt.Break) e);
} else if(e instanceof Stmt.Continue) {
return doContinue((Stmt.Continue) e);
} else if(e instanceof Stmt.Label) {
return doLabel((Stmt.Label) e);
} else if(e instanceof Stmt.If) {
return doIf((Stmt.If) e);
} else if(e instanceof Stmt.For) {
return doFor((Stmt.For) e);
} else if(e instanceof Stmt.ForEach) {
return doForEach((Stmt.ForEach) e);
} else if(e instanceof Stmt.While) {
return doWhile((Stmt.While) e);
} else if(e instanceof Stmt.DoWhile) {
return doDoWhile((Stmt.DoWhile) e);
} else if(e instanceof Stmt.Switch) {
return doSwitch((Stmt.Switch) e);
} else if(e instanceof Expr.Invoke) {
Pair<JilExpr, List<JilStmt>> r = doInvoke((Expr.Invoke) e);
r.second().add((JilExpr.Invoke) r.first());
return r.second();
} else if(e instanceof Expr.New) {
Pair<JilExpr, List<JilStmt>> r = doNew((Expr.New) e);
r.second().add((JilExpr.New) r.first());
return r.second();
} else if(e instanceof Decl.JavaClass) {
doClass((Decl.JavaClass)e);
return new ArrayList<JilStmt>();
} else if(e instanceof Stmt.PrePostIncDec) {
Pair<JilExpr, List<JilStmt>> r = doExpression((Stmt.PrePostIncDec) e);
return r.second();
}
} catch(Exception ex) {
internal_error(e,ex);
}
if (e != null) {
syntax_error("Invalid statement encountered: " + e.getClass(), e);
}
return new ArrayList<JilStmt>();
}
protected List<JilStmt> doBlock(Stmt.Block block) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(block != null) {
// now process every statement in this block.
for(Stmt s : block.statements()) {
r.addAll(doStatement(s));
}
}
return r;
}
protected List<JilStmt> doCatchBlock(Stmt.CatchBlock block) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(block != null) {
// now process every statement in this block.
for(Stmt s : block.statements()) {
r.addAll(doStatement(s));
}
}
return r;
}
protected List<JilStmt> doSynchronisedBlock(Stmt.SynchronisedBlock block) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
r.addAll(doBlock(block));
doExpression(block.expr());
// need to add synch enter and leave here ?
return r;
}
protected int tryexit_label = 0;
protected int tryhandler_label = 0;
protected List<JilStmt> doTryCatchBlock(Stmt.TryCatchBlock block) {
String exitLab = "tryexit" + tryexit_label++;
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
r.addAll(doBlock(block));
int i = tryhandler_label;
for(Stmt.CatchBlock cb : block.handlers()) {
String handlerLab = "tryhandler" + i++;
for(int j=0;j!=r.size();++j) {
JilStmt s = r.get(j);
Type.Clazz ct = cb.type().attribute(Type.Clazz.class);
if(!(s instanceof JilStmt.Goto || s instanceof JilStmt.Label)) {
// This is very pedantic. Almost certainly, we could rule out
// certain kinds of exception branches. However, it's difficult
// to do this properly and, therefore, we remain quite
// conservative at this point. See the following paper for an
// excellent discussion of this:
//
// "Improving the precision and correctness of exception
// analysis in Soot", John Jorgensen, 2003.
r.set(j,s.addException(ct, handlerLab));
}
}
}
r.add(new JilStmt.Goto(exitLab,block.attributes()));
for(Stmt.CatchBlock cb : block.handlers()) {
String handlerLab = "tryhandler" + tryhandler_label++;
Type.Clazz ct = cb.type().attribute(Type.Clazz.class);
r.add(new JilStmt.Label(handlerLab,cb.attributes()));
r.add(new JilStmt.Assign(new JilExpr.Variable(cb.variable(), ct, cb
.attributes()), new JilExpr.Variable("$", ct,
cb.attributes())));
// At this point, we have a slightly interesting problem. We need to
// search
r.addAll(doCatchBlock(cb));
r.add(new JilStmt.Goto(exitLab,cb.attributes()));
}
r.add(new JilStmt.Label(exitLab,block.attributes()));
List<JilStmt> finallyBlock = doBlock(block.finaly());
if(!finallyBlock.isEmpty()) {
// Now, we add the finally block. This is done in a separate method
// because it's actually quite challenging.
addFinallyBlock(r,finallyBlock);
}
return r;
}
protected static int finallyex_label = 0;
protected void addFinallyBlock(List<JilStmt> block, List<JilStmt> finallyBlk) {
// So, to add the finally block properly, we need to iterate through the
// block and find any situations where we exit the block. This includes
// return statements, branches to labels which are not in the block,
// exceptional flow, and simply executing the last statement of the
// block.
// First, collect all local labels, so we can distinguish local from
// non-local branches.
HashSet<String> labels = new HashSet<String>();
for(JilStmt s : block) {
if(s instanceof JilStmt.Label) {
JilStmt.Label l = (JilStmt.Label) s;
labels.add(l.label());
}
}
String exceptionLabel = "finally" + finallyex_label++;
// Now, iterate the block looking for non-local branches.
boolean lastNonBranch = false;
for(int i=0;i!=block.size();++i) {
lastNonBranch = true;
JilStmt stmt = block.get(i);
if(stmt instanceof JilStmt.Return) {
block.addAll(i,copyBlock(finallyBlk));
i += finallyBlk.size();
lastNonBranch = false;
} else if(stmt instanceof JilStmt.Goto) {
JilStmt.Goto g = (JilStmt.Goto) stmt;
if(!labels.contains(g.label())) {
// this is a non-local branch statement.
block.addAll(i,copyBlock(finallyBlk));
i += finallyBlk.size();
}
lastNonBranch = false;
} else if(stmt instanceof JilStmt.IfGoto) {
JilStmt.IfGoto g = (JilStmt.IfGoto) stmt;
if(!labels.contains(g.label())) {
// houston, we have a problem. I'm not really sure how we
// can actually get here though.
throw new RuntimeException(
"An internal failure has occurred in JilBuilder. Please report it, along with the generating code!");
}
} else if(!(stmt instanceof JilStmt.Label)){
// Add the default exceptional edge for exceptional flow.
stmt = stmt.addException(Types.JAVA_LANG_THROWABLE, exceptionLabel);
block.set(i, stmt);
// just for the (unlikely) case when last statement is a throw.
if(stmt instanceof JilStmt.Throw) { lastNonBranch = false; }
}
}
String exitLabel = "finallyexit" + (finallyex_label-1);
if(lastNonBranch) {
block.addAll(finallyBlk);
block.add(new JilStmt.Goto(exitLabel));
}
// Now, process exceptional exit
block.add(new JilStmt.Label(exceptionLabel));
block.add(new JilStmt.Assign(new JilExpr.Variable(exceptionLabel + "$",
Types.JAVA_LANG_THROWABLE), new JilExpr.Variable("$",
Types.JAVA_LANG_THROWABLE)));
block.addAll(copyBlock(finallyBlk));
block.add(new JilStmt.Throw(new JilExpr.Variable(exceptionLabel + "$",
Types.JAVA_LANG_THROWABLE)));
if(lastNonBranch) {
block.add(new JilStmt.Label(exitLabel));
}
}
protected static int copy_label = 0;
protected List<JilStmt> copyBlock(List<JilStmt> block) {
// The purpose of this method is to create a copy of the block.
// In particular, labels within the block must be copied.
HashSet<String> labels = new HashSet<String>();
for(JilStmt stmt : block) {
if(stmt instanceof JilStmt.Label) {
JilStmt.Label lab = (JilStmt.Label) stmt;
labels.add(lab.label());
}
}
ArrayList<JilStmt> nblock = new ArrayList<JilStmt>();
for(JilStmt stmt : block) {
ArrayList<Pair<Type.Clazz, String>> nexceptions = new ArrayList();
for(Pair<Type.Clazz, String> p : stmt.exceptions()) {
String target = p.second();
if(labels.contains(target)) {
target = target + "$copy" + copy_label;
}
nexceptions.add(new Pair(p.first(),target));
}
if(stmt instanceof JilStmt.Goto) {
JilStmt.Goto gto = (JilStmt.Goto) stmt;
String target = gto.label();
if(labels.contains(target)) {
target = target + "$copy" + copy_label;
}
nblock.add(new JilStmt.Goto(target, nexceptions,
new ArrayList<SyntacticAttribute>(stmt.attributes())));
} else if(stmt instanceof JilStmt.IfGoto) {
JilStmt.IfGoto igto = (JilStmt.IfGoto) stmt;
String target = igto.label();
if(labels.contains(target)) {
target = target + "$copy" + copy_label;
}
nblock.add(new JilStmt.IfGoto(igto.condition(), target,
nexceptions,
new ArrayList<SyntacticAttribute>(stmt.attributes())));
} else if(stmt instanceof JilStmt.Switch) {
JilStmt.Switch swt = (JilStmt.Switch) stmt;
ArrayList<Pair<JilExpr.Number,String>> ncases = new ArrayList();
for(Pair<JilExpr.Number,String> c : swt.cases()) {
String target = c.second();
if(labels.contains(target)) {
target = target + "$copy" + copy_label;
}
ncases.add(new Pair(c.first(),target));
}
// And, don't forget the default label!
String deftarget = swt.defaultLabel();
if(labels.contains(deftarget)) {
deftarget = deftarget + "$copy" + copy_label;
}
nblock.add(new JilStmt.Switch(swt.condition(), ncases,
deftarget, new ArrayList<SyntacticAttribute>(swt.attributes())));
} else if(stmt instanceof JilStmt.Label) {
JilStmt.Label lab = (JilStmt.Label) stmt;
String target = lab.label();
if(labels.contains(target)) {
target = target + "$copy" + copy_label;
}
nblock.add(new JilStmt.Label(target,
new ArrayList<SyntacticAttribute>(stmt.attributes())));
} else {
// there is a bug relating to switch statements.
JilStmt nstmt = stmt.clearAddExceptions(nexceptions);
nblock.add(nstmt);
}
}
copy_label = copy_label + 1;
return nblock;
}
protected List<JilStmt> doVarDef(Stmt.VarDef def) {
Type type = def.type().attribute(Type.class);
List<Triple<String, Integer, Expr>> defs = def.definitions();
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
for(int i=0;i!=defs.size();++i) {
Triple<String, Integer, Expr> d = defs.get(i);
Type nt = type;
for(int j=0;j!=d.second();++j) {
nt = new Type.Array(nt);
}
if(d.third() != null) {
Pair<JilExpr,List<JilStmt>> e = doExpression(d.third());
r.addAll(e.second());
JilExpr lhs = new JilExpr.Variable(d.first(), nt, def
.attributes());
r.add(new JilStmt.Assign(lhs, e.first()));
}
}
return r;
}
protected Pair<JilExpr,List<JilStmt>> doAssignmentOp(Stmt.AssignmentOp def) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> lhs = doExpression(def.lhs());
Pair<JilExpr, List<JilStmt>> rhs = doExpression(new Expr.BinOp(
def.op(), def.lhs(), def.rhs(), def.attributes()));
Type lhs_t = lhs.first().type();
if (rhs.second().isEmpty() || lhs.first() instanceof JilExpr.Variable) {
JilExpr tmpVar = new JilExpr.Variable(getTempVar(), lhs_t, def
.attributes());
r.add(new JilStmt.Assign(tmpVar, lhs.first(), def.attributes()));
r.addAll(rhs.second());
r.add(new JilStmt.Assign(lhs.first(), rhs.first(), def
.attributes()));
return new Pair(lhs.first(), r);
} else if(lhs.first() instanceof JilExpr.Deref) {
JilExpr.Deref deref1 = (JilExpr.Deref) lhs.first();
if(deref1.target() instanceof JilExpr.ClassVariable) {
// Slightly awkward case, since this corresponds to a static
// class access and, hence, there are no possible side-effects.
// However, we cannot assign the target to a tmp variable, since
// it will not compile down to anything in practice.
r.addAll(rhs.second());
r.add(new JilStmt.Assign(lhs.first(), rhs.first(), def
.attributes()));
return new Pair(lhs.first(), r);
} else {
JilExpr tmpVar = new JilExpr.Variable(getTempVar(), deref1.target()
.type(), def.attributes());
r.add(new JilStmt.Assign(tmpVar, deref1.target(), def.attributes()));
r.addAll(rhs.second());
JilExpr.Deref deref2 = new JilExpr.Deref(tmpVar, deref1.name(),
deref1.isStatic(), deref1.type(), deref1.attributes());
r
.add(new JilStmt.Assign(deref2, rhs.first(), def
.attributes()));
return new Pair(deref2, r);
}
} else if(lhs.first() instanceof JilExpr.ArrayIndex) {
JilExpr.ArrayIndex aindex1 = (JilExpr.ArrayIndex) lhs.first();
JilExpr targetVar = new JilExpr.Variable(getTempVar(), aindex1
.target().type(), def.attributes());
JilExpr indexVar = new JilExpr.Variable(getTempVar(), aindex1
.index().type(), def.attributes());
r.add(new JilStmt.Assign(targetVar, aindex1.target(), def
.attributes()));
r.add(new JilStmt.Assign(indexVar, aindex1.index(), def
.attributes()));
r.addAll(rhs.second());
JilExpr.ArrayIndex aindex2 = new JilExpr.ArrayIndex(targetVar,
indexVar, aindex1.type(), aindex1.attributes());
r
.add(new JilStmt.Assign(aindex2, rhs.first(), def
.attributes()));
return new Pair(aindex2, r);
} else {
syntax_error(
"unknown l-value encountered on assignment with side-effects",
def);
return null; // unreachable.
}
}
protected Pair<JilExpr,List<JilStmt>> doAssignment(Stmt.Assignment def) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> lhs = doExpression(def.lhs());
Pair<JilExpr,List<JilStmt>> rhs = doExpression(def.rhs());
r.addAll(lhs.second());
if (rhs.second().isEmpty() || lhs.first() instanceof JilExpr.Variable) {
// easy case, no side-effects in rhs or trivial assignment.
r.addAll(rhs.second());
r.add(new JilStmt.Assign(lhs.first(), rhs.first(), def
.attributes()));
return new Pair(lhs.first(), r);
} else if(lhs.first() instanceof JilExpr.Deref) {
JilExpr.Deref deref1 = (JilExpr.Deref) lhs.first();
if(deref1.target() instanceof JilExpr.ClassVariable) {
// Slightly awkward case, since this corresponds to a static
// class access and, hence, there are no possible side-effects.
// However, we cannot assign the target to a tmp variable, since
// it will not compile down to anything in practice.
r.addAll(rhs.second());
r.add(new JilStmt.Assign(lhs.first(), rhs.first(), def
.attributes()));
return new Pair(lhs.first(), r);
} else {
JilExpr tmpVar = new JilExpr.Variable(getTempVar(), deref1.target()
.type(), def.attributes());
r.add(new JilStmt.Assign(tmpVar, deref1.target(), def.attributes()));
r.addAll(rhs.second());
JilExpr.Deref deref2 = new JilExpr.Deref(tmpVar, deref1.name(),
deref1.isStatic(), deref1.type(), deref1.attributes());
r.add(new JilStmt.Assign(deref2, rhs.first(), def
.attributes()));
return new Pair(deref2, r);
}
} else if(lhs.first() instanceof JilExpr.ArrayIndex) {
JilExpr.ArrayIndex aindex1 = (JilExpr.ArrayIndex) lhs.first();
JilExpr targetVar = new JilExpr.Variable(getTempVar(), aindex1
.target().type(), def.attributes());
JilExpr indexVar = new JilExpr.Variable(getTempVar(), aindex1
.index().type(), def.attributes());
r.add(new JilStmt.Assign(targetVar, aindex1.target(), def
.attributes()));
r.add(new JilStmt.Assign(indexVar, aindex1.index(), def
.attributes()));
r.addAll(rhs.second());
JilExpr.ArrayIndex aindex2 = new JilExpr.ArrayIndex(targetVar,
indexVar, aindex1.type(), aindex1.attributes());
r.add(new JilStmt.Assign(aindex2, rhs.first(), def.attributes()));
return new Pair(aindex2, r);
} else {
syntax_error(
"unknown l-value encountered on assignment with side-effects",
def);
return null; // unreachable.
}
}
protected List<JilStmt> doReturn(Stmt.Return ret) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(ret.expr() != null) {
Pair<JilExpr,List<JilStmt>> expr = doExpression(ret.expr());
r.addAll(expr.second());
r.add(new JilStmt.Return(expr.first(),ret.attributes()));
} else {
r.add(new JilStmt.Return(null,ret.attributes()));
}
return r;
}
protected List<JilStmt> doThrow(Stmt.Throw ret) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> expr = doExpression(ret.expr());
r.addAll(expr.second());
r.add(new JilStmt.Throw(expr.first(),ret.attributes()));
return r;
}
protected List<JilStmt> doAssert(Stmt.Assert ret) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> expr = doExpression(ret.expr());
// need to do some real code generation here.
return r;
}
protected List<JilStmt> doBreak(Stmt.Break brk) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(brk.label() == null) {
SwitchScope ls = (SwitchScope) findEnclosingScope(SwitchScope.class);
if(ls == null) {
syntax_error("break used outside of loop or switch",brk);
}
r.add(new JilStmt.Goto(ls.exitLab,brk.attributes()));
} else {
String label = brk.label();
LoopScope lastLoop = null;
for(int i=scopes.size()-1;i>=0;--i) {
Scope s = scopes.get(i);
if(s instanceof LoopScope) {
lastLoop = (LoopScope) s;
} else if(s instanceof LabelScope) {
LabelScope lab = (LabelScope) s;
if(lab.label.equals(label)) {
break;
}
}
}
if(lastLoop == null) {
syntax_error("no enclosing loop instance",brk);
}
r.add(new JilStmt.Goto(lastLoop.exitLab,brk.attributes()));
}
return r;
}
protected List<JilStmt> doContinue(Stmt.Continue brk) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(brk.label() == null) {
LoopScope ls = (LoopScope) findEnclosingScope(LoopScope.class);
if(ls == null) {
syntax_error("continue used outside loop",brk);
}
r.add(new JilStmt.Goto(ls.continueLab,brk.attributes()));
} else {
String label = brk.label();
LoopScope lastLoop = null;
for(int i=scopes.size()-1;i>=0;--i) {
Scope s = scopes.get(i);
if(s instanceof LoopScope) {
lastLoop = (LoopScope) s;
} else if(s instanceof LabelScope) {
LabelScope lab = (LabelScope) s;
if(lab.label.equals(label)) {
break;
}
}
}
if(lastLoop == null) {
syntax_error("no enclosing loop instance",brk);
}
r.add(new JilStmt.Goto(lastLoop.continueLab,brk.attributes()));
}
return r;
}
protected List<JilStmt> doLabel(Stmt.Label lab) {
scopes.push(new LabelScope(lab.label()));
List<JilStmt> r = doStatement(lab.statement());
scopes.pop();
r.add(0, new JilStmt.Label(lab.label(), lab.attributes()));
return r;
}
static protected int ifexit_label = 0;
static protected int iftrue_label = 0;
protected List<JilStmt> doIf(Stmt.If stmt) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> cond = doExpression(stmt.condition());
List<JilStmt> tbranch = doStatement(stmt.trueStatement());
List<JilStmt> fbranch = doStatement(stmt.falseStatement());
r.addAll(cond.second());
if(stmt.falseStatement() == null) {
r.add(new JilStmt.IfGoto(
new JilExpr.UnOp(cond.first(), JilExpr.UnOp.NOT, T_BOOL,
stmt.condition().attributes()), "ifexit" + ifexit_label, stmt.attributes()));
r.addAll(tbranch);
} else if(stmt.trueStatement() == null) {
r.add(new JilStmt.IfGoto(cond.first(),"ifexit" + ifexit_label,stmt.attributes()));
r.addAll(fbranch);
} else {
r.add(new JilStmt.IfGoto(cond.first(),"iftrue" + iftrue_label,stmt.attributes()));
r.addAll(fbranch);
r.add(new JilStmt.Goto("ifexit" + ifexit_label,stmt.attributes()));
r.add(new JilStmt.Label("iftrue" + iftrue_label++,stmt.attributes()));
r.addAll(tbranch);
}
r.add(new JilStmt.Label("ifexit" + ifexit_label++,stmt.attributes()));
return r;
}
static protected int whileheader_label = 0;
static protected int whileexit_label = 0;
protected List<JilStmt> doWhile(Stmt.While stmt) {
String headerLab = "whileheader" + whileheader_label++;
String exitLab = "whileexit" + whileexit_label++;
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
r.add(new JilStmt.Label(headerLab, stmt
.attributes()));
Pair<JilExpr, List<JilStmt>> cond = doExpression(stmt.condition());
r.addAll(cond.second());
r.add(new JilStmt.IfGoto(new JilExpr.UnOp(cond.first(), JilExpr.UnOp.NOT,
T_BOOL, stmt.condition().attributes()), exitLab, stmt
.attributes()));
scopes.push(new LoopScope(headerLab,exitLab));
r.addAll(doStatement(stmt.body()));
scopes.pop();
r.add(new JilStmt.Goto(headerLab, stmt
.attributes()));
r.add(new JilStmt.Label(exitLab, stmt
.attributes()));
return r;
}
static protected int dowhileheader_label = 0;
static protected int dowhileexit_label = 0;
protected List<JilStmt> doDoWhile(Stmt.DoWhile stmt) {
String headerLab = "dowhileheader" + dowhileheader_label++;
String exitLab = "dowhileexit" + dowhileexit_label++;
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
r.add(new JilStmt.Label(headerLab, stmt
.attributes()));
scopes.push(new LoopScope(headerLab,exitLab));
r.addAll(doStatement(stmt.body()));
scopes.pop();
Pair<JilExpr, List<JilStmt>> cond = doExpression(stmt.condition());
r.addAll(cond.second());
r.add(new JilStmt.IfGoto(cond.first(), headerLab, stmt.attributes()));
r.add(new JilStmt.Label(exitLab, stmt
.attributes()));
return r;
}
static protected int forheader_label = 0;
static protected int forinc_label = 0;
static protected int forexit_label = 0;
protected List<JilStmt> doFor(Stmt.For stmt) {
String headerLab = "forheader" + forheader_label++;
String exitLab = "forexit" + forexit_label++;
String incLab = "forinc" + forinc_label++;
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
if(stmt.initialiser() != null) {
r.addAll(doStatement(stmt.initialiser()));
}
r.add(new JilStmt.Label(headerLab, stmt
.attributes()));
if(stmt.condition() != null) {
Pair<JilExpr, List<JilStmt>> cond = doExpression(stmt.condition());
r.addAll(cond.second());
r.add(new JilStmt.IfGoto(new JilExpr.UnOp(cond.first(), JilExpr.UnOp.NOT,
T_BOOL, stmt.condition().attributes()), exitLab,
stmt.attributes()));
}
if(stmt.increment() != null) {
scopes.push(new LoopScope(incLab,exitLab));
} else {
// this is a minor optimisation in the case that no increment is
// provided.
scopes.push(new LoopScope(headerLab,exitLab));
}
r.addAll(doStatement(stmt.body()));
scopes.pop();
if(stmt.increment() != null) {
r.add(new JilStmt.Label(incLab));
r.addAll(doStatement(stmt.increment()));
}
r.add(new JilStmt.Goto(headerLab, stmt
.attributes()));
r.add(new JilStmt.Label(exitLab, stmt
.attributes()));
return r;
}
static protected int forallheader_label = 0;
static protected int forallexit_label = 0;
static protected int foralliter_label = 0;
static protected int forallinc_label = 0;
protected List<JilStmt> doForEach(Stmt.ForEach stmt) {
String headerLab = "forallheader" + forallheader_label++;
String exitLab = "forallexit" + forallexit_label++;
String iterLab = "foralliter" + foralliter_label++;
String incLab = "forallinc" + forallinc_label++;
ArrayList<JilStmt> stmts = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> src = doExpression(stmt.source());
JilExpr.Variable loopVar = new JilExpr.Variable(stmt.var(), stmt.type()
.attribute(Type.class), stmt.attributes());
Type srcType = src.first().type();
stmts.addAll(src.second());
JilExpr.Variable iter;
if (srcType instanceof Type.Array) {
iter = new JilExpr.Variable(iterLab, T_INT);
stmts
.add(new JilStmt.Assign(iter, new JilExpr.Int(0), stmt
.attributes()));
} else {
// the following needs to be expanded upon, so as to include generic
// information on the iterator. The easiest way to do this is to
// look up the iterator() method in the src class, and use it's
// return type.
iter = new JilExpr.Variable(iterLab, JAVA_UTIL_ITERATOR);
stmts.add(new JilStmt.Assign(iter, new JilExpr.Invoke(src.first(),
"iterator", new ArrayList<JilExpr>(), new Type.Function(
JAVA_UTIL_ITERATOR), JAVA_UTIL_ITERATOR), stmt
.attributes()));
}
stmts.add(new JilStmt.Label(headerLab, stmt
.attributes()));
// Second, do condition
if (srcType instanceof Type.Array) {
Type.Array arrType = (Type.Array) srcType;
JilExpr arrlength = new JilExpr.Deref(src.first(),"length",false,T_INT, stmt
.attributes());
JilExpr gecmp = new JilExpr.BinOp(iter,arrlength,JilExpr.BinOp.GTEQ,T_BOOL, stmt
.attributes());
stmts.add(new JilStmt.IfGoto(gecmp,exitLab, stmt
.attributes()));
stmts.add(new JilStmt.Assign(loopVar, implicitCast(new JilExpr.ArrayIndex(src.first(),
iter, arrType.element()),loopVar.type())));
} else {
JilExpr hasnext = new JilExpr.Invoke(iter, "hasNext",
new ArrayList<JilExpr>(), new Type.Function(T_BOOL),
T_BOOL, stmt.attributes());
stmts.add(new JilStmt.IfGoto(new JilExpr.UnOp(hasnext, JilExpr.UnOp.NOT,
T_BOOL), exitLab));
JilExpr cast;
if(loopVar.type() instanceof Type.Primitive) {
// In this case, we have to deal with casting and implicit
// conversion.
JilExpr next = new JilExpr.Invoke(iter, "next", new ArrayList<JilExpr>(),
new Type.Function(JAVA_LANG_OBJECT),
boxedType((Type.Primitive) loopVar.type()), stmt.attributes());
cast = implicitCast(next, loopVar.type());
} else {
JilExpr next = new JilExpr.Invoke(iter, "next", new ArrayList<JilExpr>(),
new Type.Function(JAVA_LANG_OBJECT),
loopVar.type(), stmt.attributes());
cast = new JilExpr.Cast(next, loopVar.type());
}
stmts.add(new JilStmt.Assign(loopVar, cast, stmt.attributes()));
}
// Third, do body
if(srcType instanceof Type.Array) {
scopes.push(new LoopScope(incLab,exitLab));
} else {
scopes.push(new LoopScope(headerLab,exitLab));
}
stmts.addAll(doStatement(stmt.body()));
scopes.pop();
// Fourth, do increment
if (srcType instanceof Type.Array) {
stmts.add(new JilStmt.Label(incLab));
forallinc_label++;
JilExpr.BinOp rhs = new JilExpr.BinOp(iter, new JilExpr.Int(1),
JilExpr.BinOp.ADD, T_INT, stmt.attributes());
stmts.add(new JilStmt.Assign(iter,rhs,stmt.attributes()));
}
stmts.add(new JilStmt.Goto(headerLab,stmt.attributes()));
stmts.add(new JilStmt.Label(exitLab, stmt
.attributes()));
return stmts;
}
protected int switchcase_label = 0;
protected int switchexit_label = 0;
protected List<JilStmt> doSwitch(Stmt.Switch sw) {
String switchExitLab = "switchexit" + switchexit_label++;
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Pair<JilExpr,List<JilStmt>> cond = doExpression(sw.condition());
ArrayList<Pair<JilExpr.Number,String>> cases = new ArrayList();
ArrayList<JilStmt> caseStmts = new ArrayList();
String defaultLab = null;
for(Stmt.Case c : sw.cases()) {
Pair<JilExpr,List<JilStmt>> ce = doExpression(c.condition());
String caseLab = "switchcase" + switchcase_label++;
caseStmts.add(new JilStmt.Label(caseLab));
scopes.push(new SwitchScope(switchExitLab));
for(Stmt s : c.statements()) {
caseStmts.addAll(doStatement(s));
}
scopes.pop();
if(c.condition() != null) {
JilExpr ce_first = ce.first();
if(ce_first instanceof JilExpr.Number) {
cases.add(new Pair(ce_first,caseLab));
} else {
syntax_error("constant expression required",c.condition());
}
} else {
defaultLab = caseLab;
}
}
if(defaultLab == null) { defaultLab = switchExitLab; }
r.addAll(cond.second());
r.add(new JilStmt.Switch(cond.first(),cases,defaultLab));
r.addAll(caseStmts);
r.add(new JilStmt.Label(switchExitLab));
return r;
}
protected Pair<JilExpr,List<JilStmt>> doExpression(Expr e) {
try {
if(e instanceof Value.Bool) {
return doBoolVal((Value.Bool)e);
} else if(e instanceof Value.Byte) {
return doByteVal((Value.Byte)e);
} else if(e instanceof Value.Char) {
return doCharVal((Value.Char)e);
} else if(e instanceof Value.Short) {
return doShortVal((Value.Short)e);
} else if(e instanceof Value.Int) {
return doIntVal((Value.Int)e);
} else if(e instanceof Value.Long) {
return doLongVal((Value.Long)e);
} else if(e instanceof Value.Float) {
return doFloatVal((Value.Float)e);
} else if(e instanceof Value.Double) {
return doDoubleVal((Value.Double)e);
} else if(e instanceof Value.String) {
return doStringVal((Value.String)e);
} else if(e instanceof Value.Null) {
return doNullVal((Value.Null)e);
} else if(e instanceof Value.TypedArray) {
return doTypedArrayVal((Value.TypedArray)e);
} else if(e instanceof Value.Array) {
return doArrayVal((Value.Array)e);
} else if(e instanceof Value.Class) {
return doClassVal((Value.Class) e);
} else if(e instanceof Expr.LocalVariable) {
return doLocalVariable((Expr.LocalVariable)e);
} else if(e instanceof Expr.NonLocalVariable) {
return doNonLocalVariable((Expr.NonLocalVariable)e);
} else if(e instanceof Expr.ClassVariable) {
return doClassVariable((Expr.ClassVariable)e);
} else if(e instanceof Expr.UnOp) {
return doUnOp((Expr.UnOp)e);
} else if(e instanceof Expr.BinOp) {
return doBinOp((Expr.BinOp)e);
} else if(e instanceof Expr.TernOp) {
return doTernOp((Expr.TernOp)e);
} else if(e instanceof Expr.Cast) {
return doCast((Expr.Cast)e);
} else if(e instanceof Expr.Convert) {
return doConvert((Expr.Convert)e);
} else if(e instanceof Expr.InstanceOf) {
return doInstanceOf((Expr.InstanceOf)e);
} else if(e instanceof Expr.Invoke) {
return doInvoke((Expr.Invoke) e);
} else if(e instanceof Expr.New) {
return doNew((Expr.New) e);
} else if(e instanceof Expr.ArrayIndex) {
return doArrayIndex((Expr.ArrayIndex) e);
} else if(e instanceof Expr.Deref) {
return doDeref((Expr.Deref) e);
} else if(e instanceof Stmt.AssignmentOp) {
// force brackets
return doAssignmentOp((Stmt.AssignmentOp) e);
} else if(e instanceof Stmt.Assignment) {
// force brackets
return doAssignment((Stmt.Assignment) e);
}
} catch(Exception ex) {
internal_error(e,ex);
}
if(e != null) {
syntax_error("Invalid expression encountered: "
+ e.getClass(),e);
}
return null;
}
protected Pair<JilExpr, List<JilStmt>> doDeref(Expr.Deref e)
throws ClassNotFoundException, FieldNotFoundException {
Pair<JilExpr,List<JilStmt>> target = doExpression(e.target());
Type type = e.attribute(Type.class);
Type.Reference _targetT = e.target().attribute(Type.Reference.class);
if(_targetT instanceof Type.Clazz) {
Type.Clazz targetT = (Type.Clazz) _targetT;
if(e.name().equals("this")) {
// This is a special case, where we're trying to look up a field
// called "this". No such field can exist! What this means is that
// we're inside an inner class, and we're trying to access the this
// pointer of an enclosing class.
ClassScope cs = (ClassScope) findEnclosingScope(ClassScope.class);
int level = cs.type.components().size() - targetT.components().size();
JilExpr r = new JilExpr.Variable("this",cs.type);
Type.Clazz t = cs.type;
while(level > 0) {
t = parentType(t);
r = new JilExpr.Deref(r,"this$0",false,t);
level = level - 1;
}
return new Pair(r, new ArrayList<JilStmt>());
} else {
Triple<Clazz, Clazz.Field, Type> r = types
.resolveField(targetT, e.name(), loader);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Deref(target.first(), e
.name(),r.second().isStatic(), type, e.attributes()),
target.second());
}
} else if(_targetT instanceof Type.Array && e.name().equals("length")) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Deref(target.first(), e
.name(), false, type, e.attributes()),
target.second());
} else {
syntax_error("cannot dereference type " + _targetT,e);
}
return null; // dead code
}
protected Pair<JilExpr,List<JilStmt>> doArrayIndex(Expr.ArrayIndex e) {
Pair<JilExpr,List<JilStmt>> target = doExpression(e.target());
Pair<JilExpr,List<JilStmt>> index = doExpression(e.index());
Type type = e.attribute(Type.class);
List<JilStmt> r = target.second();
if(index.second().isEmpty()) {
// easy case when no side-effects in index expression.
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.ArrayIndex(target.first(),
index.first(), type, e.attributes()), r);
} else {
// harder case, there are side-effects in the index expression.
JilExpr.Variable tmpVar = new JilExpr.Variable(getTempVar(),
target.first().type(), e.attributes());
r.add(new JilStmt.Assign(tmpVar,target.first(),e.attributes()));
r.addAll(index.second());
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.ArrayIndex(tmpVar,
index.first(), type, e.attributes()), r);
}
}
protected Pair<JilExpr,List<JilStmt>> doNew(Expr.New e) {
// Second, recurse through any parameters supplied ...
ArrayList<JilStmt> r = new ArrayList();
Type.Reference type = e.type().attribute(Type.Reference.class);
MethodInfo mi = e.attribute(MethodInfo.class);
Pair<JilExpr,List<JilStmt>> context = doExpression(e.context());
Pair<List<JilExpr>,List<JilStmt>> params = doExpressionList(e.parameters());
if(context != null) {
r.addAll(context.second());
}
r.addAll(params.second());
if(mi != null) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.New(type, params
.first(), mi.type, e.attributes()), r);
} else if(type instanceof Type.Array){
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.New(type,
params.first(), new Type.Function(Types.T_VOID), e
.attributes()), r);
} else {
syntax_error("internal failure --- unable to find method information",e);
return null;
}
}
protected Pair<JilExpr,List<JilStmt>> doInvoke(Expr.Invoke e) {
ArrayList<JilStmt> r = new ArrayList();
Type type = e.attribute(Type.class);
MethodInfo mi = e.attribute(MethodInfo.class);
Pair<JilExpr,List<JilStmt>> target = doExpression(e.target());
r.addAll(target.second());
Pair<List<JilExpr>,List<JilStmt>> params = doExpressionList(e.parameters());
r.addAll(params.second());
JilExpr rec = target.first();
if (rec instanceof JilExpr.ClassVariable) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Invoke(target.first(), e
.name(), params.first(), mi.type, type, e
.attributes()), r);
} else if (rec instanceof JilExpr.Variable
&& ((JilExpr.Variable) rec).value().equals("super")) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.SpecialInvoke(target
.first(), e.name(), params.first(), mi.type, type, e
.attributes()), r);
} else if (rec.type() instanceof Type.Array && e.name().equals("clone")) {
// this is a special case for array cloning. It should really be in
// TypePropagation, but the problem is that implicit cast needs to
// be on the returned expression, which type propagation does not
// support.
JilExpr ie = new JilExpr.Invoke(target.first(), e.name(), params
.first(), mi.type, type, e.attributes());
ie = new JilExpr.Cast(ie, rec.type());
return new Pair<JilExpr, List<JilStmt>>(ie, r);
} else {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Invoke(target.first(), e
.name(), params.first(), mi.type, type, e
.attributes()), r);
}
}
protected Pair<JilExpr,List<JilStmt>> doInstanceOf(Expr.InstanceOf e) {
Pair<JilExpr,List<JilStmt>> lhs = doExpression(e.lhs());
Type type = e.attribute(Type.class);
Type.Reference rhs = e.rhs().attribute(Type.Reference.class);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.InstanceOf(lhs.first(), rhs,
type, e.attributes()), lhs.second());
}
protected Pair<JilExpr,List<JilStmt>> doCast(Expr.Cast e) {
Pair<JilExpr,List<JilStmt>> expr = doExpression(e.expr());
Type type = e.attribute(Type.class);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Cast(expr.first(),
type, e.attributes()), expr.second());
}
protected Pair<JilExpr,List<JilStmt>> doConvert(Expr.Convert e) {
Pair<JilExpr,List<JilStmt>> expr = doExpression(e.expr());
Type.Primitive type = e.attribute(Type.Primitive.class);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Convert(type, expr.first(),
e.attributes()), expr.second());
}
protected Pair<JilExpr,List<JilStmt>> doBoolVal(Value.Bool e) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Bool(e.value()),
new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doCharVal(Value.Char e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Char(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doByteVal(Value.Byte e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Byte(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doShortVal(Value.Short e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Short(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doIntVal(Value.Int e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Int(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doLongVal(Value.Long e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Long(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doFloatVal(Value.Float e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Float(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doDoubleVal(Value.Double e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Double(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doStringVal(Value.String e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.StringVal(e.value()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doNullVal(Value.Null e) {
return new Pair<JilExpr,List<JilStmt>>(new JilExpr.Null(), new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doTypedArrayVal(Value.TypedArray e) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Type.Array type = e.attribute(Type.Array.class);
Pair<List<JilExpr>,List<JilStmt>> exprs = doExpressionList(e.values());
r.addAll(exprs.second());
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Array(
exprs.first(), type, e.attributes()), r);
}
protected Pair<JilExpr,List<JilStmt>> doArrayVal(Value.Array e) {
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
Type.Array type = e.attribute(Type.Array.class);
Pair<List<JilExpr>,List<JilStmt>> exprs = doExpressionList(e.values());
r.addAll(exprs.second());
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Array(
exprs.first(), type, e.attributes()), r);
}
protected Pair<JilExpr,List<JilStmt>> doClassVal(Value.Class e) {
Type classType = e.value().attribute(Type.class);
Type.Clazz type = e.attribute(Type.Clazz.class);
if(type instanceof Type.Clazz) {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Class(
classType, type, e.attributes()),
new ArrayList<JilStmt>());
} else {
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Class(
classType, JAVA_LANG_OBJECT, e.attributes()),
new ArrayList<JilStmt>());
}
}
protected Pair<JilExpr, List<JilStmt>> doLocalVariable(
Expr.LocalVariable e) {
Type type = e.attribute(Type.class);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.Variable(e.value(), type, e
.attributes()), new ArrayList<JilStmt>());
}
protected Pair<JilExpr, List<JilStmt>> doNonLocalVariable(
Expr.NonLocalVariable e) {
syntax_error(
"internal failure (support for non-local variables not implemented!)",
e);
return null;
}
protected Pair<JilExpr,List<JilStmt>> doClassVariable(Expr.ClassVariable e) {
Type.Clazz type = e.attribute(Type.Clazz.class);
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.ClassVariable(type, e.attributes()),
new ArrayList<JilStmt>());
}
protected Pair<JilExpr,List<JilStmt>> doUnOp(Expr.UnOp e) {
Pair<JilExpr, List<JilStmt>> r = doExpression(e.expr());
Type.Primitive type = e.attribute(Type.Primitive.class);
List<JilStmt> stmts = r.second();
switch (e.op()) {
case Expr.UnOp.PREDEC:
{
JilExpr lhs = r.first();
JilExpr rhs = new JilExpr.BinOp(lhs, constant(1,lhs.type()), JilExpr.BinOp.SUB,
type, e.attributes());
stmts.add(new JilStmt.Assign(lhs,rhs,e.attributes()));
return new Pair<JilExpr, List<JilStmt>>(r.first(),stmts);
}
case Expr.UnOp.PREINC:
{
JilExpr lhs = r.first();
JilExpr rhs = new JilExpr.BinOp(lhs, constant(1,lhs.type()), JilExpr.BinOp.ADD,
type, e.attributes());
stmts.add(new JilStmt.Assign(lhs,rhs,e.attributes()));
return new Pair<JilExpr, List<JilStmt>>(lhs,stmts);
}
case Expr.UnOp.POSTINC:
{
JilExpr lhs = r.first();
JilExpr.Variable tmp = new JilExpr.Variable(getTempVar(),type,new ArrayList(lhs.attributes()));
stmts.add(new JilStmt.Assign(tmp,lhs,e.attributes()));
JilExpr rhs = new JilExpr.BinOp(lhs, constant(1,lhs.type()), JilExpr.BinOp.ADD,
type, e.attributes());
stmts.add(new JilStmt.Assign(lhs,rhs,e.attributes()));
return new Pair<JilExpr, List<JilStmt>>(tmp,stmts);
}
case Expr.UnOp.POSTDEC:
{
JilExpr lhs = r.first();
JilExpr.Variable tmp = new JilExpr.Variable(getTempVar(),type,new ArrayList(lhs.attributes()));
stmts.add(new JilStmt.Assign(tmp,lhs,e.attributes()));
JilExpr rhs = new JilExpr.BinOp(lhs, constant(1,lhs.type()), JilExpr.BinOp.SUB,
type, e.attributes());
stmts.add(new JilStmt.Assign(lhs,rhs,e.attributes()));
return new Pair<JilExpr, List<JilStmt>>(tmp,stmts);
}
default:
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.UnOp(r.first(), e.op(),
type, e.attributes()), r.second());
}
}
protected Pair<JilExpr,List<JilStmt>> doBinOp(Expr.BinOp e) {
// First, check for special string concatenation operator.
if(e.op() == Expr.BinOp.CONCAT) {
return doStringConcat(e);
}
Pair<JilExpr,List<JilStmt>> lhs = doExpression(e.lhs());
Pair<JilExpr,List<JilStmt>> rhs = doExpression(e.rhs());
Type type = e.attribute(Type.class);
if(type instanceof Type.Primitive) {
Type.Primitive ptype = (Type.Primitive) type;
List<JilStmt> r = lhs.second();
if(rhs.second().isEmpty()) {
// This is the easy case: there are no side-effects in the rhs.
r.addAll(rhs.second());
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.BinOp(lhs.first(), rhs
.first(), e.op(), ptype, e.attributes()), r);
} else {
// This is the harder case: we have side-effects in the rhs.
// Now, to deal with this i'm going to be a little conservative
// and use a temporary variable. In some cases, it may be
// possible to avoid the temporary variable, but for simplicity
// I don't do this yet. [NOTE, IT's HARD TO AVOID THE TMP]
JilExpr.Variable tmpVar = new JilExpr.Variable(getTempVar(),
lhs.first().type(), e.attributes());
r.add(new JilStmt.Assign(tmpVar,lhs.first(),e.attributes()));
r.addAll(rhs.second());
return new Pair<JilExpr, List<JilStmt>>(new JilExpr.BinOp(tmpVar, rhs
.first(), e.op(), ptype, e.attributes()), r);
}
} else {
syntax_error(
"internal failure --- problem processing binary operator ("
+ lhs.first().type() + ")", e);
return null;
}
}
protected static int stringbuilder_label = 0;
protected Pair<JilExpr,List<JilStmt>> doStringConcat(Expr.BinOp bop){
// This method is evidence as to why Java sucks as a programming
// language. It should be easy to construct datatypes, as I'm doing
// here, but lack of good notation makes it awkward in Java. Sure, there
// are some hacks to can do to improve the situation but basically it's
// screwed.
String builderLab = "$builder" + stringbuilder_label++;
Pair<JilExpr,List<JilStmt>> lhs = doExpression(bop.lhs());
Pair<JilExpr,List<JilStmt>> rhs = doExpression(bop.rhs());
List<JilStmt> stmts = lhs.second();
stmts.addAll(rhs.second());
Type.Clazz builder = new Type.Clazz("java.lang",
"StringBuilder");
stmts.add(new JilStmt.Assign(new JilExpr.Variable(builderLab, builder),
new JilExpr.New(builder, new ArrayList<JilExpr>(),
new Type.Function(T_VOID), bop.attributes()), bop
.attributes()));
Type lhs_t = lhs.first().type();
if (lhs_t instanceof Type.Primitive || isString(lhs_t)) {
ArrayList<JilExpr> params = new ArrayList<JilExpr>();
params.add(lhs.first());
stmts.add(new JilExpr.Invoke(new JilExpr.Variable(builderLab,
builder), "append", params, new Type.Function(
new Type.Clazz("java.lang", "StringBuilder"), lhs.first()
.type()), new Type.Clazz("java.lang",
"StringBuilder"), bop.attributes()));
} else {
ArrayList<JilExpr> params = new ArrayList<JilExpr>();
params.add(lhs.first());
stmts.add(new JilExpr.Invoke(new JilExpr.Variable(builderLab,
builder), "append", params, new Type.Function(
new Type.Clazz("java.lang", "StringBuilder"),
JAVA_LANG_OBJECT), new Type.Clazz("java.lang",
"StringBuilder"), bop.attributes()));
}
// Now, do the right hand side
JilExpr r;
Type rhs_t = rhs.first().type();
if(rhs_t instanceof Type.Primitive || isString(rhs_t)) {
ArrayList<JilExpr> params = new ArrayList<JilExpr>();
params.add(rhs.first());
r = new JilExpr.Invoke(new JilExpr.Variable(builderLab, builder),
"append", params, new Type.Function(new Type.Clazz(
"java.lang", "StringBuilder"), rhs_t),
new Type.Clazz("java.lang", "StringBuilder"), bop
.attributes());
} else {
ArrayList<JilExpr> params = new ArrayList<JilExpr>();
params.add(rhs.first());
r = new JilExpr.Invoke(new JilExpr.Variable(builderLab, builder),
"append", params, new Type.Function(new Type.Clazz(
"java.lang", "StringBuilder"), JAVA_LANG_OBJECT),
new Type.Clazz("java.lang", "StringBuilder"), bop
.attributes());
}
r = new JilExpr.Invoke(r, "toString", new ArrayList<JilExpr>(),
new Type.Function(JAVA_LANG_STRING), JAVA_LANG_STRING, bop
.attributes());
return new Pair<JilExpr,List<JilStmt>>(r,stmts);
}
protected int ternop_label = 0;
protected Pair<JilExpr,List<JilStmt>> doTernOp(Expr.TernOp e) {
String trueLab = "$ternoptrue" + ternop_label;
String exitLab = "$ternopexit" + ternop_label++;
Type r_t = e.attribute(Type.class);
Pair<JilExpr,List<JilStmt>> cond = doExpression(e.condition());
Pair<JilExpr,List<JilStmt>> tbranch = doExpression(e.trueBranch());
Pair<JilExpr,List<JilStmt>> fbranch = doExpression(e.falseBranch());
ArrayList<JilStmt> r = new ArrayList<JilStmt>();
JilExpr.Variable tmp = new JilExpr.Variable(getTempVar(),r_t,e.attributes());
r.addAll(cond.second());
r.add(new JilStmt.IfGoto(cond.first(),trueLab,e.attributes()));
r.addAll(fbranch.second());
r.add(new JilStmt.Assign(tmp,fbranch.first(),e.attributes()));
r.add(new JilStmt.Goto(exitLab,e.attributes()));
r.add(new JilStmt.Label(trueLab,e.attributes()));
r.addAll(tbranch.second());
r.add(new JilStmt.Assign(tmp,tbranch.first(),e.attributes()));
r.add(new JilStmt.Label(exitLab,e.attributes()));
return new Pair(tmp,r);
}
/**
* The purpose of this method is to simplify the processing of an expression
* list. This is particular complex in the case of side-effecting
* statements.
*/
protected Pair<List<JilExpr>,List<JilStmt>> doExpressionList(List<Expr> exprs) {
ArrayList<JilExpr> nexprs = new ArrayList();
ArrayList<JilStmt> nstmts = new ArrayList();
boolean hasSideEffects = false;
for(int i=exprs.size()-1;i>=0;--i) {
Expr p = exprs.get(i);
Pair<JilExpr,List<JilStmt>> tmp = doExpression(p);
if(hasSideEffects) {
JilExpr.Variable var = new JilExpr.Variable(getTempVar(), tmp
.first().type(), p.attributes());
nstmts.add(0,new JilStmt.Assign(var,tmp.first(),p.attributes()));
nexprs.add(0,var);
} else {
nexprs.add(0,tmp.first());
}
nstmts.addAll(0,tmp.second());
if(!tmp.second().isEmpty()) {
hasSideEffects=true;
}
}
return new Pair<List<JilExpr>,List<JilStmt>>(nexprs,nstmts);
}
/**
* This method determines whether or not it is possible for a statement to
* throw a particular exception. Observe that this determination is from a
* static point of view; thus, it may be possible with more complex
* reasoning to determine that a statement cannot throw an exception.
* However, we need to be somewhat conservative here to ensure that the
* bytecode produced will in fact pass the JVM bytecode verifier.
*
* @param stmt
* @param exception
* @return
*/
protected boolean canThrowException(JilStmt stmt, Type.Clazz exception)
throws ClassNotFoundException {
if (types.subtype(JAVA_LANG_VIRTUALMACHINEERROR,
exception, loader)) {
// must treat these exceptions very conservatively, spec JVM spec
// dictates they can occur at any point during exceptions.
return true;
}
// now, try to eliminate all statements which definitely cannot
// throw an exception.
if (stmt instanceof JilStmt.Goto || stmt instanceof JilStmt.Label
|| stmt instanceof JilStmt.Nop) {
return false;
}
// Now, tackle the easier ones.
if (stmt instanceof JilStmt.Lock || stmt instanceof JilStmt.Unlock) {
// these statements can only throw null pointer exceptions.
// Actually, can also throw IllegaMonitorStateException
return types.subtype(exception, JAVA_LANG_NULLPOINTEREXCEPTION,
loader);
}
if (stmt instanceof JilStmt.Throw) {
// this can throw null pointer exception if the argument is null.
// can also throw IllegalMonitorStateException (see JVM spec athrow)
JilStmt.Throw tr = (JilStmt.Throw) stmt;
return types.subtype(exception, tr.expr().type(), loader)
|| types.subtype(exception, JAVA_LANG_NULLPOINTEREXCEPTION,
loader);
}
ArrayList<JilExpr> exprs = new ArrayList();
if (stmt instanceof JilExpr.Invoke) {
// Some possible issue with respect to throwing Errors if
// this causes class loading. See JVM Section 2.17.5.
//
// Also, can throw IncompatibleClassChangeError, IllegalAccessError,
// AbstractMethodError, UnsatisfiedLinkError.
if (types.subtype(exception, JAVA_LANG_RUNTIMEEXCEPTION, loader)
|| types.subtype(JAVA_LANG_RUNTIMEEXCEPTION, exception,
loader)) {
return true;
}
// check declared exceptions
MethodInfo mi = stmt.attribute(MethodInfo.class);
for(Type.Clazz ex : mi.exceptions) {
if (types.subtype(exception, ex, loader)) {
return true;
}
}
JilExpr.Invoke ivk = (JilExpr.Invoke) stmt;
exprs.addAll(ivk.parameters());
} else if (stmt instanceof JilExpr.New) {
if (types.subtype(exception, JAVA_LANG_RUNTIMEEXCEPTION, loader)
|| types.subtype(JAVA_LANG_RUNTIMEEXCEPTION, exception, loader)) {
return true;
}
JilExpr.New ivk = (JilExpr.New) stmt;
if (ivk.type() instanceof Type.Array
&& types.subtype(exception, new Type.Clazz("java.lang",
"NegativeArraySizeException"), loader)) {
// In some cases, we can certain figure out that this cannot
// happen.
return true;
} else if(!(ivk.type() instanceof Type.Array)) {
// check declared exceptions
MethodInfo mi = ivk.attribute(MethodInfo.class);
for(Type.Clazz ex : mi.exceptions) {
if (types.subtype(exception, ex, loader)) {
return true;
}
}
}
// Need to do something about checked exceptions. Also, if static
// method then cannot throw NullPointException
exprs.addAll(ivk.parameters());
} else if(stmt instanceof JilStmt.Return) {
JilStmt.Return r = (JilStmt.Return) stmt;
// can also throw IllegalMonitorStateException (see JVM spec areturn)
if(r.expr() == null) {
return false;
} else {
exprs.add(r.expr());
}
} else if(stmt instanceof JilStmt.Assign) {
JilStmt.Assign r = (JilStmt.Assign) stmt;
if (r.lhs() instanceof JilExpr.ArrayIndex
&& types.subtype(exception, new Type.Clazz("java.lang",
"ArrayStoreException"), loader)) {
return true;
}
exprs.add(r.lhs());
exprs.add(r.rhs());
} else if(stmt instanceof JilStmt.IfGoto) {
JilStmt.IfGoto r = (JilStmt.IfGoto) stmt;
exprs.add(r.condition());
} else if(stmt instanceof JilStmt.Switch) {
JilStmt.Switch r = (JilStmt.Switch) stmt;
exprs.add(r.condition());
}
// Right, at this point, we have a bunch of expressions and we need to
// check whether or not any of these could throw the exception in
// question.
for(JilExpr e : exprs) {
if(canThrowException(e,exception)) {
return true;
}
}
return false;
}
protected boolean canThrowException(JilExpr expr, Type.Clazz exception)
throws ClassNotFoundException {
// reuse code above if possible
if(expr instanceof JilExpr.Invoke || expr instanceof JilExpr.New) {
return canThrowException((JilStmt)expr,exception);
}
// Again, build up an expression list to check
ArrayList<JilExpr> exprs = new ArrayList();
if(expr instanceof JilExpr.Cast) {
JilExpr.Cast ec = (JilExpr.Cast) expr;
if ((ec.type() instanceof Type.Reference || ec.type() instanceof Type.Null)
&& types.subtype(exception, new Type.Clazz("java.lang",
"ClassCastException"), loader)) {
return true;
}
exprs.add(ec.expr());
} else if(expr instanceof JilExpr.BinOp) {
JilExpr.BinOp bop = (JilExpr.BinOp) expr;
if (!(bop.type() instanceof Type.Float || bop.type() instanceof Type.Double)
&& (bop.op() == JilExpr.BinOp.DIV || bop.op() == JilExpr.BinOp.MOD)
&& types.subtype(exception, JAVA_LANG_ARITHMETICEXCEPTION,
loader)) {
// Curiously, divide-by-zero is only a problem for integer types.
return true;
}
exprs.add(bop.lhs());
exprs.add(bop.rhs());
} else if(expr instanceof JilExpr.UnOp) {
JilExpr.UnOp bop = (JilExpr.UnOp) expr;
exprs.add(bop.expr());
} else if(expr instanceof JilExpr.Deref) {
// Some possible issue with respect to throwing Errors if this
// instruction causes class loading. See JVM Section 2.17.5.
JilExpr.Deref def = (JilExpr.Deref) expr;
if (types.subtype(exception, JAVA_LANG_NULLPOINTEREXCEPTION, loader)) {
return true;
}
exprs.add(def.target());
} else if(expr instanceof JilExpr.Array) {
JilExpr.Array arr = (JilExpr.Array) expr;
exprs.addAll(arr.values());
} else if(expr instanceof JilExpr.ArrayIndex) {
JilExpr.ArrayIndex ai = (JilExpr.ArrayIndex) expr;
if (types.subtype(exception, JAVA_LANG_NULLPOINTEREXCEPTION, loader)
|| types.subtype(exception, new Type.Clazz("java.lang",
"ArrayIndexOutOfBoundsException"), loader)) {
return true;
}
exprs.add(ai.target());
exprs.add(ai.index());
} else if(expr instanceof JilExpr.InstanceOf) {
JilExpr.InstanceOf iof = (JilExpr.InstanceOf) expr;
exprs.add(iof.lhs());
}
// Right, at this point, we have a bunch of expressions and we need to
// check whether or not any of these could throw the exception in
// question.
for (JilExpr e : exprs) {
if (canThrowException(e, exception)) {
return true;
}
}
return false;
}
protected Scope findEnclosingScope(Class c) {
for(int i=scopes.size()-1;i>=0;--i) {
Scope s = scopes.get(i);
if(c.isInstance(s)) {
return s;
}
}
return null;
}
protected int findSuperCall(List<JilStmt> stmts) {
int r = 0;
for(JilStmt stmt : stmts) {
if(stmt instanceof JilExpr.Invoke) {
JilExpr.Invoke sc = (JilExpr.Invoke) stmt;
if (sc.name().equals("super") || sc.name().equals("this")) {
return r;
}
}
r=r+1;
}
return -1;
}
public JilMethod createStaticInitialiser(JilClass parent) {
List<JilMethod> si = parent.methods("<clinit>");
if(si.size() == 0) {
ArrayList<Modifier> mods = new ArrayList<Modifier>();
mods.add(Modifier.ACC_STATIC);
JilMethod r = new JilMethod("<clinit>", new Type.Function(
Types.T_VOID), new ArrayList(), mods,
new ArrayList<Type.Clazz>());
r.body().add(new JilStmt.Return(null));
parent.methods().add(r);
return r;
} else {
// It should be impossible to have more than one.
return si.get(0);
}
}
protected JilExpr constant(int constant, Type t) {
if(t instanceof Type.Byte) {
return new JilExpr.Byte((byte)constant);
} else if(t instanceof Type.Char) {
return new JilExpr.Char((char)constant);
} else if(t instanceof Type.Short) {
return new JilExpr.Short((short)constant);
} else if(t instanceof Type.Int) {
return new JilExpr.Int(constant);
} else if(t instanceof Type.Long) {
return new JilExpr.Long((long)constant);
} else if(t instanceof Type.Float) {
return new JilExpr.Float((float)constant);
} else {
return new JilExpr.Double((double)constant);
}
}
protected int tmp_label = 0;
protected String getTempVar() {
return "$tmp" + tmp_label++;
}
/**
* Check wither a given type is a reference to java.lang.String or not.
*
* @param t
* @return
*/
protected static boolean isString(Type t) {
if(t instanceof Type.Clazz) {
Type.Clazz c = (Type.Clazz) t;
return c.pkg().equals("java.lang") && c.components().size() == 1
&& c.components().get(0).first().equals("String");
}
return false;
}
}