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.statement;
020
021import lucee.runtime.util.ForEachUtil;
022import lucee.transformer.bytecode.Body;
023import lucee.transformer.bytecode.BytecodeContext;
024import lucee.transformer.bytecode.BytecodeException;
025import lucee.transformer.bytecode.Position;
026import lucee.transformer.bytecode.expression.Expression;
027import lucee.transformer.bytecode.expression.var.Variable;
028import lucee.transformer.bytecode.expression.var.VariableRef;
029import lucee.transformer.bytecode.util.ExpressionUtil;
030import lucee.transformer.bytecode.util.Types;
031import lucee.transformer.bytecode.visitor.OnFinally;
032import lucee.transformer.bytecode.visitor.TryFinallyVisitor;
033
034import org.objectweb.asm.Label;
035import org.objectweb.asm.Opcodes;
036import org.objectweb.asm.Type;
037import org.objectweb.asm.commons.GeneratorAdapter;
038import org.objectweb.asm.commons.Method;
039
040public final class ForEach extends StatementBase implements FlowControlBreak,FlowControlContinue,HasBody {
041
042        
043        private Body body;
044        private VariableRef key;
045        private Expression value;
046
047        private final static Method HAS_NEXT =          new Method("hasNext",Types.BOOLEAN_VALUE,new Type[]{});
048        private final static Method NEXT =                      new Method("next",Types.OBJECT,new Type[]{});
049        private final static Method SET =                       new Method("set",Types.OBJECT,new Type[]{Types.PAGE_CONTEXT,Types.OBJECT});
050        public static final Method TO_ITERATOR = new Method("toIterator",Types.ITERATOR,new Type[]{Types.OBJECT});
051        private static final Type FOR_EACH_UTIL = Type.getType(ForEachUtil.class);
052        protected static final Method RESET = new Method("reset",Types.VOID,new Type[]{Types.ITERATOR});
053
054    //private static final Type COLLECTION_UTIL = Type.getType(CollectionUtil.class);
055
056        private Label begin = new Label();
057        private Label end = new Label();
058        private FlowControlFinal fcf;
059        private String label;
060
061        /**
062         * Constructor of the class
063         * @param key
064         * @param value
065         * @param body
066         * @param line
067         */
068        public ForEach(Variable key,Expression value,Body body,Position start, Position end,String label) {
069                super(start,end);
070                this.key=new VariableRef(key);
071                this.value=value;
072                this.body=body;
073                this.label=label;
074                body.setParent(this);
075                
076        }
077        
078        @Override
079        public void _writeOut(BytecodeContext bc) throws BytecodeException {
080                GeneratorAdapter adapter = bc.getAdapter();
081                final int it=adapter.newLocal(Types.ITERATOR);
082                final int item=adapter.newLocal(Types.REFERENCE);
083                
084                //Value
085                        // ForEachUtil.toIterator(value)
086                        value.writeOut(bc, Expression.MODE_REF);
087                        adapter.invokeStatic(FOR_EACH_UTIL, TO_ITERATOR);
088                        //adapter.invokeStatic(COLLECTION_UTIL, TO_ITERATOR);
089                        // Iterator it=...
090                        adapter.storeLocal(it);
091                        TryFinallyVisitor tfv=new TryFinallyVisitor(new OnFinally() {
092                                
093                                @Override
094                                public void _writeOut(BytecodeContext bc) throws BytecodeException {
095                                        GeneratorAdapter a = bc.getAdapter();
096                                        //if(fcf!=null && fcf.getAfterFinalGOTOLabel()!=null)ASMUtil.visitLabel(a,fcf.getFinalEntryLabel());
097                                        a.loadLocal(it);
098                                        a.invokeStatic(FOR_EACH_UTIL, RESET);
099                                        /*if(fcf!=null){
100                                                Label l=fcf.getAfterFinalGOTOLabel();
101                                                if(l!=null)a.visitJumpInsn(Opcodes.GOTO, l);
102                                        }*/
103                                }
104                        },getFlowControlFinal());
105                        tfv.visitTryBegin(bc);
106                        // Key
107                                // new VariableReference(...)
108                                key.writeOut(bc, Expression.MODE_REF);
109                                // VariableReference item=...
110                                adapter.storeLocal(item);
111                        
112                        // while
113                                ExpressionUtil.visitLine(bc, getStart());
114                                adapter.visitLabel(begin);
115                                
116                                // hasNext
117                                adapter.loadLocal(it);
118                                adapter.invokeInterface(Types.ITERATOR, HAS_NEXT);
119                                adapter.ifZCmp(Opcodes.IFEQ, end);
120                                
121                                // item.set(pc,it.next());
122                                adapter.loadLocal(item);
123                                adapter.loadArg(0);
124                                adapter.loadLocal(it);
125                                adapter.invokeInterface(Types.ITERATOR, NEXT);
126                                adapter.invokeInterface(Types.REFERENCE, SET);
127                                adapter.pop();
128                                
129                                // Body
130                                body.writeOut(bc);
131                                adapter.visitJumpInsn(Opcodes.GOTO, begin);
132                                adapter.visitLabel(end);
133                        tfv.visitTryEnd(bc);
134                
135        }
136
137        @Override
138        public Label getBreakLabel() {
139                return end;
140        }
141
142        @Override
143        public Label getContinueLabel() {
144                return begin;
145        }
146
147        @Override
148        public Body getBody() {
149                return body;
150        }
151
152        @Override
153        public FlowControlFinal getFlowControlFinal() {
154                if(fcf==null) fcf=new FlowControlFinalImpl();
155                return fcf;
156        }
157
158        @Override
159        public String getLabel() {
160                return label;
161        }
162}