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}