package railo.transformer.bytecode;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
import railo.transformer.bytecode.expression.Expression;
import railo.transformer.bytecode.literal.LitString;
import railo.transformer.bytecode.statement.PrintOut;
import railo.transformer.bytecode.statement.StatementBaseNoFinal;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.ExpressionUtil;
import railo.transformer.bytecode.util.Types;
/**
* Base Body implementation
*/
public class BodyBase extends StatementBaseNoFinal implements Body {
private static long counter=0;
private LinkedList<Statement> statements=new LinkedList<Statement>();
private Statement last=null;
//private int count=-1;
private final static int MAX_STATEMENTS=206;
/**
* Constructor of the class
*/
public BodyBase() {
super(null,null);
}
/**
*
* @see railo.transformer.bytecode.Body#addStatement(railo.transformer.bytecode.Statement)
*/
public void addStatement(Statement statement) {
if(statement instanceof PrintOut) {
Expression expr = ((PrintOut)statement).getExpr();
if(expr instanceof LitString && concatPrintouts(((LitString)expr).getString())) return;
}
statement.setParent(this);
this.statements.add(statement);
last=statement;
}
public void addFirst(Statement statement) {
statement.setParent(this);
this.statements.add(0,statement);
}
public void remove(Statement statement) {
statement.setParent(null);
this.statements.remove(statement);
}
/**
*
* @see railo.transformer.bytecode.Body#getStatements()
*/
public List<Statement> getStatements() {
return statements;
}
public boolean hasStatements() {
return !statements.isEmpty();
}
/**
*
* @see railo.transformer.bytecode.Body#moveStatmentsTo(railo.transformer.bytecode.Body)
*/
public void moveStatmentsTo(Body trg) {
Iterator<Statement> it = statements.iterator();
while(it.hasNext()) {
Statement stat=it.next();
stat.setParent(trg);
trg.getStatements().add(stat);
}
statements.clear();
}
public void addPrintOut(String str, Position start,Position end) {
if(concatPrintouts(str)) return;
last=new PrintOut(new LitString(str,start,end),start,end);
last.setParent(this);
this.statements.add(last);
}
private boolean concatPrintouts(String str) {
if(last instanceof PrintOut) {
PrintOut po=(PrintOut) last;
Expression expr = po.getExpr();
if(expr instanceof LitString) {
LitString lit=(LitString)expr;
if(lit.getString().length()<1024) {
po.setExpr(LitString.toExprString(lit.getString().concat(str),lit.getStart(),lit.getEnd()));
return true;
}
}
}
return false;
}
public void _writeOut(BytecodeContext bc) throws BytecodeException {
writeOut(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),statements, bc);
}
public static void writeOut2(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys,List<Statement> statements,BytecodeContext bc) throws BytecodeException {
Iterator<Statement> it = statements.iterator();
while(it.hasNext()) {
Statement s = it.next();
s.writeOut(bc);
}
}
public static void writeOut(BytecodeContext statConstr,BytecodeContext constr,List<LitString> keys,List<Statement> statements,final BytecodeContext bc) throws BytecodeException {
GeneratorAdapter adapter = bc.getAdapter();
boolean isOutsideMethod;
GeneratorAdapter a=null;
Method m;
BytecodeContext _bc=bc;
Iterator<Statement> it = statements.iterator();
//int lastLine=-1;
while(it.hasNext()) {
isOutsideMethod=bc.getMethod().getReturnType().equals(Types.VOID);
Statement s = it.next();
if(_bc.incCount()>MAX_STATEMENTS && bc.doSubFunctions() &&
(isOutsideMethod || !s.hasFlowController()) && s.getStart()!=null) {
if(a!=null){
a.returnValue();
a.endMethod();
}
//ExpressionUtil.visitLine(bc, s.getLine());
String method= ASMUtil.createOverfowMethod(bc.getMethod().getName(),bc.getPage().getMethodCount());
ExpressionUtil.visitLine(bc, s.getStart());
//ExpressionUtil.lastLine(bc);
m= new Method(method,Types.VOID,new Type[]{Types.PAGE_CONTEXT});
a = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , m, null, new Type[]{Types.THROWABLE}, bc.getClassWriter());
_bc=new BytecodeContext(statConstr,constr,keys,bc,a,m);
if(bc.getRoot()!=null)_bc.setRoot(bc.getRoot());
else _bc.setRoot(bc);
adapter.visitVarInsn(Opcodes.ALOAD, 0);
adapter.visitVarInsn(Opcodes.ALOAD, 1);
adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, bc.getClassName(), method, "(Lrailo/runtime/PageContext;)V");
}
if(_bc!=bc && s.hasFlowController()) {
if(a!=null){
a.returnValue();
a.endMethod();
}
_bc=bc;
a=null;
}
if(ExpressionUtil.doLog(bc)) {
String id=id();
ExpressionUtil.callStartLog(bc, s,id);
s.writeOut(_bc);
ExpressionUtil.callEndLog(bc, s,id);
}
else s.writeOut(_bc);
}
if(a!=null){
a.returnValue();
a.endMethod();
}
}
private static synchronized String id() {
counter++;
if(counter<0) counter=1;
return Long.toString(counter, Character.MAX_RADIX);
}
/**
*
* @see railo.transformer.bytecode.Body#isEmpty()
*/
public boolean isEmpty() {
return statements.isEmpty();
}
}