/*
* This file is part of the OpenJML project.
* Author: David R. Cok
*/
package org.jmlspecs.openjml;
import java.util.Map;
import org.jmlspecs.annotation.Nullable;
import org.jmlspecs.openjml.JmlTree.*;
import com.sun.source.tree.*;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeCopier;
import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Log;
/** This class makes a copy of a tree, performing designated substitutions as
* it copies; new copies of the expressions provided for substitution are made
* for each use; in addition all local variables (let, forall, ...) have new
* declarations and new symbols declared and appropriately substituted in their
* bodies. As in JmlTreeCopier, some kinds of nodes are not copied.
*
* @author David Cok
*
*/
// DESIGN NOTE: This class follows the design of TreeCopier, namely to create
// copies by using the factory and explicitly filling in the nodes that the
// factory methods do not initialize. This is fragile against additions or
// removal of fields from AST nodes.
// Alternately we could have used clone methods, and let the AST classes themselves
// be responsible for copying themselves. JCTree does define clone().
// Using clone() would not have permitted using an alternate factory to create
// the copy of the tree (a piece of functionality we don't use), and it would be
// different from how TreeCopier is implemented.
// So we live with this design.
// FIXME - This class does not yet take care of the start and end position information;
// also links between declarations and symbols are not updated (e.g. the map from
// symbols to class declarations). It may be the case that the AST will have to be
// completely re-attributed and that it is not really possible to have copies
// of trees that really work
public class JmlTreeSubstitute extends JmlTreeCopier {
// inherits protected Context context;
// inherits protected JmlTree.Maker M;
protected Map<Object,JCExpression> replacements;
protected JmlTreeUtils treeutils;
/** Creates a new copier, whose new nodes are generated from the given factory*/
public JmlTreeSubstitute(Context context, JmlTree.Maker maker, Map<Object,JCExpression> replacements) {
super(context,maker);
this.replacements = replacements;
this.treeutils = JmlTreeUtils.instance(context);
}
/** A (overridable) factory method that returns a new (or derived) instance
* of a JmlCopier object.
*/
@Override
public JmlTreeSubstitute newCopier(JmlTree.Maker maker) {
return new JmlTreeSubstitute(context,maker,replacements); // FIXME - can we use the same replacements map with a new Maker object?
}
/** Static method to create a copy of the given AST with the given factory */
public static <T extends JCTree> T substitute(JmlTree.Maker maker, @Nullable T that,
Map<Object,JCExpression> replacements) {
return new JmlTreeSubstitute(maker.context,maker,replacements).copy(that,null);
}
// Only a few expression nodes need changing
@Override
public JCTree visitIdentifier(IdentifierTree node, Void p) {
JCIdent oldid = (JCIdent)node;
@Nullable JCExpression newexpr = replacements.get(oldid.sym);
if (newexpr == null) {
JCIdent id = (JCIdent)super.visitIdentifier(node,p).setType(((JCTree)node).type);
id.sym = ((JCIdent)node).sym;
return id;
} else {
return copy(newexpr);
}
}
@Override
public JCTree visitJmlSingleton(JmlSingleton that, Void p) {
// for substitution \result
if (that.token == JmlTokenKind.BSRESULT) {
@Nullable JCExpression newexpr = replacements.get(that.token);
if (newexpr != null) return copy(newexpr);
else return super.visitJmlSingleton(that, p);
} else {
return super.visitJmlSingleton(that, p);
}
}
@Override
public JCTree visitJmlQuantifiedExpr(JmlQuantifiedExpr that, Void p) {
try {
ListBuffer<JCVariableDecl> newdecls = new ListBuffer<JCVariableDecl>();
for (JCVariableDecl decl: that.decls) {
// Quantified expressions do not have initializers; if they did we would have to worry about
// the order of copying the initializers and adding items to the replacements map
JCVariableDecl newdecl = treeutils.makeVariableDecl(decl.name, decl.type, null, decl.pos);
JCIdent id = treeutils.makeIdent(that,newdecl.sym);
replacements.put(decl.sym, id);
newdecls.add(newdecl);
}
JmlQuantifiedExpr q = M.at(that.pos).JmlQuantifiedExpr(
that.op,
newdecls.toList(),
null,
null);
q.range = copy(that.range,p);
q.value = copy(that.value,p);
q.racexpr = copy(that.racexpr);
q.setType(that.type);
return q;
} finally {
for (JCVariableDecl decl: that.decls) {
replacements.remove(decl.sym);
}
}
}
@Override
public JCTree visitLetExpr(LetExpr that, Void p) {
try {
ListBuffer<JCVariableDecl> newdecls = new ListBuffer<JCVariableDecl>();
ListBuffer<JCExpression> newinits = new ListBuffer<JCExpression>();
// The initializers are evaluated in the external scope,
// even if there are multiple declarations
for (JCVariableDecl decl: that.defs) {
JCExpression newinit = copy(decl.init,p);
newinits.add(newinit);
}
for (JCVariableDecl decl: that.defs) {
JCExpression newinit = newinits.next();
JCVariableDecl newdecl = treeutils.makeVariableDecl(decl.name, decl.type, newinit, decl.pos);
JCIdent id = treeutils.makeIdent(that,newdecl.sym);
replacements.put(decl.sym, id);
newdecls.add(newdecl);
}
LetExpr q = M.LetExpr(newdecls.toList(), null);
q.expr = copy(that.expr, p);
q.setType(that.type);
q.pos = that.pos;
return q;
} finally {
for (JCVariableDecl decl: that.defs) {
replacements.remove(decl.sym);
}
}
}
@Override
public JCTree visitJmlSetComprehension(JmlSetComprehension that, Void p) {
JCVariableDecl decl = that.variable;
try {
JCVariableDecl newdecl = treeutils.makeVariableDecl(decl.name, decl.type, null, decl.pos);
JCIdent id = treeutils.makeIdent(that,newdecl.sym);
replacements.put(decl.sym,id);
JmlSetComprehension set =
M.JmlSetComprehension(
copy(that.newtype,p),
newdecl,
copy(that.predicate,p));
set.setType(that.type);
set.pos = that.pos;
return set;
} finally {
replacements.remove(decl.sym);
}
}
// TODO - Only implemented for Expressions, so far not for statements
// If we do whole programs, we also need to remap anything containing a
// declaration (including loops), as well as statement labels.
@Override
public JCTree visitJmlVariableDecl(JmlVariableDecl that, Void p) {
JmlVariableDecl copy = (JmlVariableDecl)super.visitVariable(that,p);
copy.sourcefile = that.sourcefile;
copy.specsDecl = that.specsDecl; // FIXME - repoint to new reference?
copy.fieldSpecs = (that.fieldSpecs);// FIXME - copy
copy.fieldSpecsCombined = (that.fieldSpecsCombined); // FIXME - need copy
copy.sym = that.sym;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlMethodInvocation(JmlMethodInvocation that, Void p) {
// Don't use visitMethodInvocation, since it is designed just to produce
// JCMethodInvocation nodes. Normal method calls are JCMethodInvocations;
// only special JML functions (e.g. \\nonnullelements) are JmlMethodInvocation
// nodes.
// CAUTION: if JCMethodInvocation adds fields, they have to be added here
JmlMethodInvocation copy = M.at(that.pos).JmlMethodInvocation(
that.token,
copy(that.args,p));
copy.startpos = that.startpos;
copy.label = that.label;
copy.type = that.type;
copy.meth = copy(that.meth,p);
copy.typeargs = copy(that.typeargs,p);
copy.varargsElement = that.varargsElement; // FIXME - copy?
return copy;
}
@Override
public JCTree visitJmlSpecificationCase(JmlSpecificationCase that, Void p) {
JmlSpecificationCase copy = M.at(that.pos).JmlSpecificationCase(
copy(that.modifiers,p),
that.code,
that.token,
that.also,
copy(that.clauses,p));
copy.block = copy(that.block,p);
copy.sourcefile = that.sourcefile;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlStatement(JmlStatement that, Void p) {
JmlStatement copy = M.at(that.pos).JmlStatement(
that.token,
copy(that.statement,p));
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlStatementDecls(JmlStatementDecls that, Void p) {
JmlStatementDecls copy = M.at(that.pos).JmlStatementDecls(
copy(that.list,p));
copy.token = that.token;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlStatementHavoc(JmlStatementHavoc that, Void p) {
JmlStatementHavoc copy = M.at(that.pos).JmlHavocStatement(
copy(that.storerefs,p));
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlStatementLoop(JmlStatementLoop that, Void p) {
JmlStatementLoop copy = M.at(that.pos).JmlStatementLoop(
that.token,
copy(that.expression,p));
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlStatementSpec(JmlStatementSpec that, Void p) {
return M.at(that.pos).JmlStatementSpec(
copy(that.statementSpecs,p)).setType(that.type);
}
@Override
public JCTree visitJmlTypeClauseConstraint(JmlTypeClauseConstraint that, Void p) {
JmlTypeClauseConstraint copy = M.at(that.pos).JmlTypeClauseConstraint(
copy(that.modifiers,p),
copy(that.expression,p),
copy(that.sigs,p));
copy.token = that.token;
copy.source = that.source;
copy.type = that.type;
copy.notlist = that.notlist;
return copy;
}
@Override
public JCTree visitJmlTypeClauseDecl(JmlTypeClauseDecl that, Void p) {
JmlTypeClauseDecl copy = M.at(that.pos).JmlTypeClauseDecl(
copy(that.decl,p));
copy.token = that.token;
copy.modifiers = copy(that.modifiers,p);
copy.source = that.source;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlTypeClauseIn(JmlTypeClauseIn that, Void p) {
JmlTypeClauseIn copy = M.at(that.pos).JmlTypeClauseIn(
copy(that.list,p));
copy.token = that.token;
copy.source = that.source;
copy.modifiers = copy(that.modifiers,p);
copy.parentVar = that.parentVar; // FIXME - does this need repointing to the new copy
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlTypeClauseMaps(JmlTypeClauseMaps that, Void p) {
JmlTypeClauseMaps copy = M.at(that.pos).JmlTypeClauseMaps(
copy(that.expression,p),
copy(that.list,p));
copy.token = that.token;
copy.modifiers = copy(that.modifiers,p);
copy.source = that.source;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlTypeClauseMonitorsFor(JmlTypeClauseMonitorsFor that, Void p) {
JmlTypeClauseMonitorsFor copy = M.at(that.pos).JmlTypeClauseMonitorsFor(
copy(that.modifiers,p),
copy(that.identifier,p),
copy(that.list,p));
copy.token = that.token;
copy.source = that.source;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlTypeClauseRepresents(JmlTypeClauseRepresents that, Void p) {
JmlTypeClauseRepresents copy = M.at(that.pos).JmlTypeClauseRepresents(
copy(that.modifiers,p),
copy(that.ident,p),
that.suchThat,
copy(that.expression,p));
copy.token = that.token;
copy.source = that.source;
copy.type = that.type;
return copy;
}
@Override
public JCTree visitJmlWhileLoop(JmlWhileLoop that, Void p) {
JmlWhileLoop r = M.at(that.pos).JmlWhileLoop(
(JmlWhileLoop)this.visitWhileLoop(that,p),
copy(that.loopSpecs,p));
// already done: r.type = that.type;
return r;
}
// We have to reimplement all the JCTree nodes because we want the type
// information copied
@Override
public JCTree visitAnnotation(AnnotationTree node, Void p) {
return super.visitAnnotation(node,p).setType(((JCAnnotation)node).type);
}
@Override
public JCTree visitAssert(AssertTree node, Void p) {
return super.visitAssert(node,p).setType(((JCAssert)node).type);
}
public JCTree visitDoWhileLoop(DoWhileLoopTree node, Void p) {
return super.visitDoWhileLoop(node,p).setType(((JCDoWhileLoop)node).type);
}
public JCTree visitEnhancedForLoop(EnhancedForLoopTree node, Void p) {
return super.visitEnhancedForLoop(node,p).setType(((JCEnhancedForLoop)node).type);
}
public JCTree visitForLoop(ForLoopTree node, Void p) {
return super.visitForLoop(node,p).setType(((JCForLoop)node).type);
}
public JCTree visitLabeledStatement(LabeledStatementTree node, Void p) {
return super.visitLabeledStatement(node,p).setType(((JCTree)node).type);
}
public JCTree visitLiteral(LiteralTree node, Void p) {
return super.visitLiteral(node,p).setType(((JCTree)node).type);
}
public JCTree visitMethod(MethodTree node, Void p) {
JCTree t = super.visitMethod(node,p).setType(((JCTree)node).type);
((JCMethodDecl)t).sym = ((JCMethodDecl)node).sym;
return t;
}
public JCTree visitMethodInvocation(MethodInvocationTree node, Void p) {
JCMethodInvocation copy = (JCMethodInvocation)super.visitMethodInvocation(node,p).setType(((JCTree)node).type);
copy.varargsElement = ((JCMethodInvocation)node).varargsElement; // FIXME - copy? - should be in super class
return copy;
}
public JCTree visitModifiers(ModifiersTree node, Void p) {
return super.visitModifiers(node,p).setType(((JCTree)node).type);
}
public JCTree visitNewArray(NewArrayTree node, Void p) {
return super.visitNewArray(node,p).setType(((JCTree)node).type);
}
public JCTree visitNewClass(NewClassTree node, Void p) {
return super.visitNewClass(node,p).setType(((JCTree)node).type);
// FIXME - does not copy constructor, varargsElement, constructorType
}
public JCTree visitParenthesized(ParenthesizedTree node, Void p) {
return super.visitParenthesized(node,p).setType(((JCTree)node).type);
}
public JCTree visitReturn(ReturnTree node, Void p) {
return super.visitReturn(node,p).setType(((JCTree)node).type);
}
public JCTree visitMemberSelect(MemberSelectTree node, Void p) {
JCTree t = super.visitMemberSelect(node,p).setType(((JCTree)node).type);
((JCFieldAccess)t).sym = ((JCFieldAccess)node).sym;
return t;
}
public JCTree visitEmptyStatement(EmptyStatementTree node, Void p) {
return super.visitEmptyStatement(node,p).setType(((JCTree)node).type);
}
public JCTree visitSwitch(SwitchTree node, Void p) {
return super.visitSwitch(node,p).setType(((JCTree)node).type);
}
public JCTree visitSynchronized(SynchronizedTree node, Void p) {
return super.visitSynchronized(node,p).setType(((JCTree)node).type);
}
public JCTree visitThrow(ThrowTree node, Void p) {
return super.visitThrow(node,p).setType(((JCTree)node).type);
}
public JCTree visitParameterizedType(ParameterizedTypeTree node, Void p) {
return super.visitParameterizedType(node,p).setType(((JCTree)node).type);
}
public JCTree visitArrayType(ArrayTypeTree node, Void p) {
return super.visitArrayType(node,p).setType(((JCTree)node).type);
}
public JCTree visitTypeCast(TypeCastTree node, Void p) {
return super.visitTypeCast(node,p).setType(((JCTree)node).type);
}
public JCTree visitPrimitiveType(PrimitiveTypeTree node, Void p) {
return super.visitPrimitiveType(node,p).setType(((JCTree)node).type);
}
public JCTree visitTypeParameter(TypeParameterTree node, Void p) {
return super.visitTypeParameter(node,p).setType(((JCTree)node).type);
}
public JCTree visitInstanceOf(InstanceOfTree node, Void p) {
return super.visitInstanceOf(node,p).setType(((JCTree)node).type);
}
public JCTree visitVariable(VariableTree node, Void p) {
JCTree t = super.visitVariable(node,p).setType(((JCTree)node).type);
((JCVariableDecl)t).sym = ((JCVariableDecl)node).sym;
return t;
}
public JCTree visitWhileLoop(WhileLoopTree node, Void p) {
return super.visitWhileLoop(node,p).setType(((JCTree)node).type);
}
public JCTree visitWildcard(WildcardTree node, Void p) {
return super.visitWildcard(node,p).setType(((JCTree)node).type);
}
public JCTree visitOther(Tree node, Void p) {
return super.visitOther(node,p).setType(((JCTree)node).type);
}
}