001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.transformer.bytecode; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.LinkedList; 024import java.util.List; 025 026import lucee.transformer.bytecode.expression.Expression; 027import lucee.transformer.bytecode.literal.LitString; 028import lucee.transformer.bytecode.statement.PrintOut; 029import lucee.transformer.bytecode.statement.StatementBaseNoFinal; 030import lucee.transformer.bytecode.util.ASMUtil; 031import lucee.transformer.bytecode.util.ExpressionUtil; 032import lucee.transformer.bytecode.util.Types; 033 034import org.objectweb.asm.Opcodes; 035import org.objectweb.asm.Type; 036import org.objectweb.asm.commons.GeneratorAdapter; 037import org.objectweb.asm.commons.Method; 038 039/** 040 * Base Body implementation 041 */ 042public class BodyBase extends StatementBaseNoFinal implements Body { 043 044 private static long counter=0; 045 private LinkedList<Statement> statements=new LinkedList<Statement>(); 046 private Statement last=null; 047 //private int count=-1; 048 private final static int MAX_STATEMENTS=206; 049 050 /** 051 * Constructor of the class 052 */ 053 public BodyBase() { 054 super(null,null); 055 } 056 057 058 /** 059 * 060 * @see lucee.transformer.bytecode.Body#addStatement(lucee.transformer.bytecode.Statement) 061 */ 062 public void addStatement(Statement statement) { 063 064 if(statement instanceof PrintOut) { 065 Expression expr = ((PrintOut)statement).getExpr(); 066 if(expr instanceof LitString && concatPrintouts(((LitString)expr).getString())) return; 067 } 068 statement.setParent(this); 069 this.statements.add(statement); 070 last=statement; 071 } 072 073 public void addFirst(Statement statement) { 074 statement.setParent(this); 075 this.statements.add(0,statement); 076 } 077 078 public void remove(Statement statement) { 079 statement.setParent(null); 080 this.statements.remove(statement); 081 } 082 083 /** 084 * 085 * @see lucee.transformer.bytecode.Body#getStatements() 086 */ 087 public List<Statement> getStatements() { 088 return statements; 089 } 090 091 public boolean hasStatements() { 092 return !statements.isEmpty(); 093 } 094 095 /** 096 * 097 * @see lucee.transformer.bytecode.Body#moveStatmentsTo(lucee.transformer.bytecode.Body) 098 */ 099 public void moveStatmentsTo(Body trg) { 100 Iterator<Statement> it = statements.iterator(); 101 while(it.hasNext()) { 102 Statement stat=it.next(); 103 stat.setParent(trg); 104 trg.getStatements().add(stat); 105 } 106 statements.clear(); 107 } 108 109 public void addPrintOut(String str, Position start,Position end) { 110 if(concatPrintouts(str)) return; 111 112 last=new PrintOut(LitString.toExprString(str,start,end),start,end); 113 last.setParent(this); 114 this.statements.add(last); 115 } 116 117 private boolean concatPrintouts(String str) { 118 if(last instanceof PrintOut) { 119 PrintOut po=(PrintOut) last; 120 Expression expr = po.getExpr(); 121 if(expr instanceof LitString) { 122 LitString lit=(LitString)expr; 123 if(lit.getString().length()<1024) { 124 po.setExpr(LitString.toExprString(lit.getString().concat(str),lit.getStart(),lit.getEnd())); 125 return true; 126 } 127 } 128 } 129 return false; 130 } 131 132 public void _writeOut(BytecodeContext bc) throws BytecodeException { 133 writeOut(bc,this); 134 } 135 136 137 138 139 public static void writeOut(final BytecodeContext bc, Body body) throws BytecodeException { 140 writeOut(bc,body.getStatements()); 141 } 142 143 144 145 public static void writeOut(final BytecodeContext bc, List<Statement> statements) throws BytecodeException { 146 GeneratorAdapter adapter = bc.getAdapter(); 147 boolean isOutsideMethod; 148 GeneratorAdapter a=null; 149 Method m; 150 BytecodeContext _bc=bc; 151 Iterator<Statement> it = statements.iterator(); 152 boolean split = bc.getPage().getSplitIfNecessary(); 153 154 155 //int lastLine=-1; 156 while(it.hasNext()) { 157 isOutsideMethod=bc.getMethod().getReturnType().equals(Types.VOID); 158 Statement s = it.next(); 159 if(split && _bc.incCount()>MAX_STATEMENTS && bc.doSubFunctions() && 160 (isOutsideMethod || !s.hasFlowController()) && s.getStart()!=null) { 161 if(a!=null){ 162 a.returnValue(); 163 a.endMethod(); 164 } 165 //ExpressionUtil.visitLine(bc, s.getLine()); 166 String method= ASMUtil.createOverfowMethod(bc.getMethod().getName(),bc.getPage().getMethodCount()); 167 ExpressionUtil.visitLine(bc, s.getStart()); 168 //ExpressionUtil.lastLine(bc); 169 m= new Method(method,Types.VOID,new Type[]{Types.PAGE_CONTEXT}); 170 a = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , m, null, new Type[]{Types.THROWABLE}, bc.getClassWriter()); 171 172 _bc=new BytecodeContext(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),bc,a,m); 173 if(bc.getRoot()!=null)_bc.setRoot(bc.getRoot()); 174 else _bc.setRoot(bc); 175 176 adapter.visitVarInsn(Opcodes.ALOAD, 0); 177 adapter.visitVarInsn(Opcodes.ALOAD, 1); 178 adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, bc.getClassName(), method, "(Llucee/runtime/PageContext;)V"); 179 } 180 if(_bc!=bc && s.hasFlowController()) { 181 if(a!=null){ 182 a.returnValue(); 183 a.endMethod(); 184 } 185 _bc=bc; 186 a=null; 187 } 188 ExpressionUtil.writeOut(s, _bc); 189 } 190 if(a!=null){ 191 a.returnValue(); 192 a.endMethod(); 193 } 194 } 195 196 197 public static void writeOutNew(final BytecodeContext bc, List<Statement> statements) throws BytecodeException { 198 199 if(statements==null || statements.size()==0) return; 200 201 Statement s; 202 Iterator<Statement> it = statements.iterator(); 203 boolean isVoidMethod=bc.getMethod().getReturnType().equals(Types.VOID); 204 boolean split = bc.getPage().getSplitIfNecessary(); 205 206 207 208 // split 209 if(split && isVoidMethod && statements.size()>1 && bc.doSubFunctions()) { 210 int collectionSize=statements.size()/10; 211 if(collectionSize<1) collectionSize=1; 212 List<Statement> _statements=new ArrayList<Statement>(); 213 while(it.hasNext()){ 214 s=it.next(); 215 216 if(s.hasFlowController()) { 217 // add existing statements to sub method 218 if(_statements.size()>0) { 219 addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); 220 _statements.clear(); 221 } 222 ExpressionUtil.writeOut(s, bc); 223 } 224 else { 225 _statements.add(s); 226 if(_statements.size()>=collectionSize) { 227 if(_statements.size()<=10 && ASMUtil.count(_statements,true)<=20) { 228 Iterator<Statement> _it = _statements.iterator(); 229 while(_it.hasNext()) 230 ExpressionUtil.writeOut(_it.next(), bc); 231 } 232 else 233 addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); 234 _statements.clear(); 235 } 236 } 237 } 238 239 if(_statements.size()>0) 240 addToSubMethod(bc,_statements.toArray(new Statement[_statements.size()])); 241 } 242 // no split 243 else { 244 while(it.hasNext()){ 245 ExpressionUtil.writeOut(it.next(), bc); 246 } 247 } 248 } 249 250 private static void addToSubMethod(BytecodeContext bc, Statement... statements) throws BytecodeException { 251 if(statements==null || statements.length==0) return; 252 253 GeneratorAdapter adapter = bc.getAdapter(); 254 String method= ASMUtil.createOverfowMethod(bc.getMethod().getName(),bc.getPage().getMethodCount()); 255 256 for(int i=0;i<statements.length;i++){ 257 if(statements[i].getStart()!=null) { 258 ExpressionUtil.visitLine(bc, statements[i].getStart()); 259 break; 260 } 261 } 262 263 //ExpressionUtil.lastLine(bc); 264 Method m = new Method(method,Types.VOID,new Type[]{Types.PAGE_CONTEXT}); 265 GeneratorAdapter a = new GeneratorAdapter(Opcodes.ACC_PRIVATE+Opcodes.ACC_FINAL , m, null, new Type[]{Types.THROWABLE}, bc.getClassWriter()); 266 267 BytecodeContext _bc = new BytecodeContext(bc.getStaticConstructor(),bc.getConstructor(),bc.getKeys(),bc,a,m); 268 if(bc.getRoot()!=null)_bc.setRoot(bc.getRoot()); 269 else _bc.setRoot(bc); 270 271 adapter.visitVarInsn(Opcodes.ALOAD, 0); 272 adapter.visitVarInsn(Opcodes.ALOAD, 1); 273 adapter.visitMethodInsn(Opcodes.INVOKEVIRTUAL, bc.getClassName(), method, "(Llucee/runtime/PageContext;)V"); 274 275 for(int i=0;i<statements.length;i++){ 276 ExpressionUtil.writeOut(statements[i], _bc); 277 } 278 279 a.returnValue(); 280 a.endMethod(); 281 } 282 283 284 public static synchronized String id() { 285 counter++; 286 if(counter<0) counter=1; 287 return Long.toString(counter, Character.MAX_RADIX); 288 } 289 290 /** 291 * 292 * @see lucee.transformer.bytecode.Body#isEmpty() 293 */ 294 public boolean isEmpty() { 295 return statements.isEmpty(); 296 } 297}