/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.transformer.bytecode; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import lucee.transformer.Factory; import lucee.transformer.Position; import lucee.transformer.TransformerException; import lucee.transformer.bytecode.statement.PrintOut; import lucee.transformer.bytecode.statement.StatementBaseNoFinal; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.bytecode.util.ExpressionUtil; import lucee.transformer.bytecode.util.Types; import lucee.transformer.expression.Expression; import lucee.transformer.expression.literal.LitString; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; /** * 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(Factory f) { super(f,null,null); } /** * * @see lucee.transformer.bytecode.Body#addStatement(lucee.transformer.bytecode.Statement) */ @Override 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; } @Override public void addFirst(Statement statement) { statement.setParent(this); this.statements.add(0,statement); } @Override public void remove(Statement statement) { statement.setParent(null); this.statements.remove(statement); } /** * * @see lucee.transformer.bytecode.Body#getStatements() */ @Override public List<Statement> getStatements() { return statements; } @Override public boolean hasStatements() { return !statements.isEmpty(); } /** * * @see lucee.transformer.bytecode.Body#moveStatmentsTo(lucee.transformer.bytecode.Body) */ @Override 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(); } @Override public void addPrintOut(Factory f,String str, Position start,Position end) { if(concatPrintouts(str)) return; last=new PrintOut(f.createLitString(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(lit.getFactory().createLitString(lit.getString().concat(str),lit.getStart(),lit.getEnd())); return true; } } } return false; } @Override public void _writeOut(BytecodeContext bc) throws TransformerException { writeOut(bc,this); } public static void writeOut(final BytecodeContext bc, Body body) throws TransformerException { writeOut(bc,body.getStatements()); } public static void writeOut(final BytecodeContext bc, List<Statement> statements) throws TransformerException { GeneratorAdapter adapter = bc.getAdapter(); boolean isOutsideMethod; GeneratorAdapter a=null; Method m; BytecodeContext _bc=bc; Iterator<Statement> it = statements.iterator(); boolean split = bc.getPage().getSplitIfNecessary(); //int lastLine=-1; while(it.hasNext()) { isOutsideMethod=bc.getMethod().getReturnType().equals(Types.VOID); Statement s = it.next(); if(split && _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(bc.getConstructor(),bc.getKeys(),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, "(Llucee/runtime/PageContext;)V"); } if(_bc!=bc && s.hasFlowController()) { if(a!=null){ a.returnValue(); a.endMethod(); } _bc=bc; a=null; } ExpressionUtil.writeOut(s, _bc); } if(a!=null){ a.returnValue(); a.endMethod(); } } public static void writeOutNew(final BytecodeContext bc, List<Statement> statements) throws TransformerException { if(statements==null || statements.size()==0) return; Statement s; Iterator<Statement> it = statements.iterator(); boolean isVoidMethod=bc.getMethod().getReturnType().equals(Types.VOID); boolean split = bc.getPage().getSplitIfNecessary(); // split if(split && isVoidMethod && statements.size()>1 && bc.doSubFunctions()) { int collectionSize=statements.size()/10; if(collectionSize<1) collectionSize=1; List<Statement> _statements=new ArrayList<Statement>(); while(it.hasNext()){ s=it.next(); if(s.hasFlowController()) { // add existing statements to sub method if(_statements.size()>0) { addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); _statements.clear(); } ExpressionUtil.writeOut(s, bc); } else { _statements.add(s); if(_statements.size()>=collectionSize) { if(_statements.size()<=10 && ASMUtil.count(_statements,true)<=20) { Iterator<Statement> _it = _statements.iterator(); while(_it.hasNext()) ExpressionUtil.writeOut(_it.next(), bc); } else addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); _statements.clear(); } } } if(_statements.size()>0) addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); } // no split else { while(it.hasNext()){ ExpressionUtil.writeOut(it.next(), bc); } } } private static void addToSubMethod(BytecodeContext bc, Statement... statements) throws TransformerException { if(statements==null || statements.length==0) return; GeneratorAdapter adapter = bc.getAdapter(); String method= ASMUtil.createOverfowMethod(bc.getMethod().getName(),bc.getPage().getMethodCount()); for(int i=0;i<statements.length;i++){ if(statements[i].getStart()!=null) { ExpressionUtil.visitLine(bc, statements[i].getStart()); break; } } //ExpressionUtil.lastLine(bc); Method m = new Method(method,Types.VOID,new Type[]{Types.PAGE_CONTEXT}); GeneratorAdapter a = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , m, null, new Type[]{Types.THROWABLE}, bc.getClassWriter()); BytecodeContext _bc = new BytecodeContext(bc.getConstructor(),bc.getKeys(),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, "(Llucee/runtime/PageContext;)V"); for(int i=0;i<statements.length;i++){ ExpressionUtil.writeOut(statements[i], _bc); } a.returnValue(); a.endMethod(); } public static synchronized String id() { counter++; if(counter<0) counter=1; return Long.toString(counter, Character.MAX_RADIX); } /** * * @see lucee.transformer.bytecode.Body#isEmpty() */ @Override public boolean isEmpty() { return statements.isEmpty(); } }