/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.util.synthesizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import polyglot.ast.Block;
import polyglot.ast.Expr;
import polyglot.ast.Local;
import polyglot.ast.LocalDecl;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.ast.Receiver;
import polyglot.ast.Stmt;
import polyglot.types.ClassDef;
import polyglot.types.ClassType;
import polyglot.types.Flags;
import polyglot.types.Name;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.util.Position;
import polyglot.util.CollectionUtil; import x10.util.CollectionFactory;
import polyglot.visit.NodeVisitor;
import x10.ast.AnnotationNode;
import polyglot.types.Context;
/**
* Synthesizer to construct a code block
*
* User could add statement one by one or add statement in batch.
*
* Every local decl added into will be recorded as one local variables
* And then user could query local variables in the current block by name.
* If no local variables found, it will query its container if its container
* is a code block synth.
*
* And it provides query accessible field APIs to return a accessible field
* The mechanism is it will locate the class container and locate the class's
* field as a expr
*
*
*/
public class CodeBlockSynth extends AbstractStateSynth implements IStmtSynth{
/**
* @author Haichuan
* Inner class for wrapping a statement as a IStmtSynth
*/
class SimpleStmtSynth implements IStmtSynth{
Stmt stmt;
SimpleStmtSynth(Stmt stmt){
this.stmt = stmt;
}
public Stmt genStmt(Position pos) throws SemanticException {
return (Stmt) stmt.position(pos);
}
public Stmt genStmt() throws SemanticException {
return stmt;
}
}
protected boolean reachable = true;
protected Block block; //the result
protected AbstractStateSynth containerSynth; //The current block's parent
protected List<IStmtSynth> stmtSythns; //all synthesizers for generate the code block
protected Map<String, Local> localVarMap; //name to local var map
protected List<NodeVisitor> codeProcessingJobs;
/**
* Create a code block synth and specify its container
* @param xnf
* @param xct
* @param containerSynth
* @param pos
*/
public CodeBlockSynth(NodeFactory xnf, Context xct, AbstractStateSynth containerSynth, Position pos) {
super(xnf, xct, pos);
this.containerSynth = containerSynth;
stmtSythns = new ArrayList<IStmtSynth>();
localVarMap = CollectionFactory.newHashMap();
}
/**
* Create a stand alone codeblock synth
* @param xnf
* @param xct
* @param pos
*/
public CodeBlockSynth(NodeFactory xnf, Context xct, Position pos) {
this(xnf, xct, null, pos);
}
/**
* Only be used in MethodSynth/Constructor Synth
* @param containerSynth
*/
protected void setContainerSynth(AbstractStateSynth containerSynth) {
this.containerSynth = containerSynth;
}
/**
* Add locals to the code block map.
* It's should be only invoked by this class and method/constructor synthesizer (add formal)
* @param local
*/
protected void addLocal(Local local){
localVarMap.put(local.name().id().toString(), local);
}
public Local getLocal(String name){
AbstractStateSynth synth = this;
while(synth != null && synth instanceof CodeBlockSynth){
CodeBlockSynth codeBlockSynth = (CodeBlockSynth)synth;
if(codeBlockSynth.localVarMap.containsKey(name)){
return codeBlockSynth.localVarMap.get(name);
}
//not found, to its parent
synth = codeBlockSynth.containerSynth;
}
return null;
}
/**
* Add one statement at the end of the code block
* @param stmt
*/
public void addStmt(Stmt stmt) {
try {
checkClose();
stmtSythns.add(new SimpleStmtSynth(stmt));
if(stmt instanceof LocalDecl){
//add this local
addLocal(synth.createLocal(compilerPos, (LocalDecl)stmt));
}
} catch (StateSynthClosedException e) {
e.printStackTrace();
}
}
/**
* Add one statement (statement synth) at the end of the code block
* @param iss
*/
public void addStmt(IStmtSynth iss) {
try {
checkClose();
stmtSythns.add(iss);
if(iss instanceof NewLocalVarSynth){
//add this local
addLocal(((NewLocalVarSynth)iss).getLocal());
}
} catch (StateSynthClosedException e) {
e.printStackTrace();
}
}
/**
* Add statements at the end of the code block
* @param stmts
*/
public void addStmts(List<Stmt> stmts) {
for(Stmt stmt : stmts){
addStmt(stmt);
}
}
/**
* Add one statement in the front of the code block.
* @param stmt
*/
public void addStmtInFront(Stmt stmt) {
stmtSythns.add(0, new SimpleStmtSynth(stmt));
}
/**
* Add statements in the front of the code block
* @param stmts
*/
public void addStmtsInFront(List<Stmt> stmts){
for(int i = stmts.size() - 1; i >=0; i--){
addStmtInFront(stmts.get(i));
}
}
public InstanceCallSynth createInstanceCallStmt(Position pos, Receiver insRef, String methodName) {
InstanceCallSynth synth = new InstanceCallSynth(xnf, xct, pos, insRef, methodName);
addStmt(synth);
return synth;
}
public NewInstanceSynth createNewInstaceStmt(Position pos, ClassType classType) {
NewInstanceSynth synth = new NewInstanceSynth(xnf, xct, pos, classType);
addStmt(synth);
return synth;
}
public SwitchSynth createSwitchStmt(Position pos, Expr switchCond) {
SwitchSynth synth = new SwitchSynth(xnf, xct, pos, switchCond);
addStmt(synth);
return synth;
}
public NewLocalVarSynth createLocalVar(Position pos, Name name, Flags flags,
Expr initializer,List<AnnotationNode> annotations){
NewLocalVarSynth synth = new NewLocalVarSynth(xnf, xct, pos, name, flags,
initializer, initializer.type(), annotations);
addStmt(synth);
return synth;
}
public NewLocalVarSynth createLocalVar(Position pos, Expr initializer) {
NewLocalVarSynth synth = new NewLocalVarSynth(xnf, xct, pos, Flags.NONE, initializer);
addStmt(synth);
return synth;
}
public NewLocalVarSynth createLocalVar(Position pos, Flags flags, Expr initializer) {
NewLocalVarSynth synth = new NewLocalVarSynth(xnf, xct, pos, flags, initializer);
addStmt(synth);
return synth;
}
public SuperCallSynth createSuperCall(Position pos, ClassDef classDef){
SuperCallSynth synth = new SuperCallSynth(xnf, xct, pos, classDef);
addStmt(synth);
return synth;
}
public CodeBlockSynth createCodeBlock(Position pos){
CodeBlockSynth synth = new CodeBlockSynth(xnf, xct, this, pos);
addStmt(synth);
return synth;
}
public void setReachable(boolean reachable) {
this.reachable = reachable;
}
public Block close() throws SemanticException {
if(closed){
return block; //the result;
}
closed = true;
ArrayList<Stmt> stmts = new ArrayList<Stmt>();
for(IStmtSynth iss : stmtSythns){
stmts.add(iss.genStmt());
}
block = (Block) xnf.Block(pos, stmts).reachable(reachable);
//now process the final jobs
if(codeProcessingJobs != null){
for(NodeVisitor visitor : codeProcessingJobs){
block = (Block) block.visit(visitor);
}
}
return block;
}
public Stmt genStmt() throws SemanticException {
return close();
}
/**
* Add some code visiting jobs.
* During code gen, the code visitor will running.
* @param visitor
*/
public void addCodeProcessingJob(NodeVisitor visitor){
if(codeProcessingJobs == null){
codeProcessingJobs = new ArrayList<NodeVisitor>();
}
codeProcessingJobs.add(visitor);
}
}