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.tag;
020
021import java.util.ArrayList;
022import java.util.Iterator;
023import java.util.List;
024
025import lucee.transformer.bytecode.Body;
026import lucee.transformer.bytecode.BodyBase;
027import lucee.transformer.bytecode.BytecodeContext;
028import lucee.transformer.bytecode.BytecodeException;
029import lucee.transformer.bytecode.Position;
030import lucee.transformer.bytecode.Statement;
031import lucee.transformer.bytecode.expression.ExprString;
032import lucee.transformer.bytecode.expression.Expression;
033import lucee.transformer.bytecode.literal.LitString;
034import lucee.transformer.bytecode.statement.FlowControlFinal;
035import lucee.transformer.bytecode.statement.FlowControlFinalImpl;
036import lucee.transformer.bytecode.statement.FlowControlRetry;
037import lucee.transformer.bytecode.statement.TryCatchFinally;
038import lucee.transformer.bytecode.util.ExpressionUtil;
039import lucee.transformer.bytecode.util.Types;
040import lucee.transformer.bytecode.visitor.OnFinally;
041import lucee.transformer.bytecode.visitor.TryCatchFinallyVisitor;
042
043import org.objectweb.asm.Label;
044import org.objectweb.asm.Opcodes;
045import org.objectweb.asm.Type;
046import org.objectweb.asm.commons.GeneratorAdapter;
047import org.objectweb.asm.commons.Method;
048
049public final class TagTry extends TagBase implements FlowControlRetry {
050
051        private static final ExprString ANY=LitString.toExprString("any");
052
053        private static final Method GET_VARIABLE = new Method(
054                        "getVariable",
055                        Types.OBJECT,
056                        new Type[]{Types.STRING});
057        
058        private static final Method TO_PAGE_EXCEPTION = new Method(
059                        "toPageException",
060                        Types.PAGE_EXCEPTION,
061                        new Type[]{Types.THROWABLE});
062        
063        
064        public static final Method SET_CATCH_PE = new Method(
065                        "setCatch",
066                        Types.VOID,
067                        new Type[]{Types.PAGE_EXCEPTION});
068        
069        public static final Method SET_CATCH3 = new Method(
070                        "setCatch",
071                        Types.VOID,
072                        new Type[]{Types.PAGE_EXCEPTION,Types.BOOLEAN_VALUE,Types.BOOLEAN_VALUE});
073        
074        public static final Method GET_CATCH = new Method(
075                        "getCatch",
076                        Types.PAGE_EXCEPTION,
077                        new Type[]{});
078
079        //  public boolean typeEqual(String type);
080        private static final Method TYPE_EQUAL = new Method(
081                        "typeEqual",
082                        Types.BOOLEAN_VALUE,
083                        new Type[]{Types.STRING});
084
085        private FlowControlFinal fcf;
086
087        private boolean checked;
088        private Label begin = new Label();
089
090        
091        public TagTry(Position start,Position end) {
092                super(start,end);
093        }
094
095        /**
096         *
097         * @see lucee.transformer.bytecode.statement.tag.TagBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter)
098         */
099        public void _writeOut(BytecodeContext bc) throws BytecodeException {
100                GeneratorAdapter adapter = bc.getAdapter();
101                adapter.visitLabel(begin);
102                Body tryBody=new BodyBase();
103                List<Tag> catches=new ArrayList<Tag>();
104                Tag tmpFinal=null;
105
106                tryBody.setParent(getBody().getParent());
107                
108                List<Statement> statements = getBody().getStatements();
109                Statement stat;
110                Tag tag;
111                {
112                Iterator<Statement> it = statements.iterator();
113                while(it.hasNext()) {
114                        stat= it.next();
115                        if(stat instanceof Tag) {
116                                tag=(Tag) stat;
117                                if(tag.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Catch"))      {
118                                        catches.add(tag);
119                                        continue;
120                                }
121                                else if(tag.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Finally"))       {
122                                        tmpFinal=tag;
123                                        continue;
124                                }
125                        }
126                        tryBody.addStatement(stat);
127                };
128                }
129                final Tag _finally=tmpFinal;
130                
131                // has no try body, if there is no try body, no catches are executed, only finally 
132                if(!tryBody.hasStatements()) {
133                        
134                        if(_finally!=null && _finally.getBody()!=null){
135                                BodyBase.writeOut(bc, _finally.getBody());
136                                //ExpressionUtil.writeOut(_finally.getBody(), bc);
137                        }
138                        return;
139                }
140                TryCatchFinallyVisitor tcfv=new TryCatchFinallyVisitor(new OnFinally() {
141                        
142                        public void _writeOut(BytecodeContext bc) throws BytecodeException {
143                                if(_finally!=null) {
144                                        
145                                        ExpressionUtil.visitLine(bc, _finally.getStart());
146                                        BodyBase.writeOut(bc, _finally.getBody());
147                                        //ExpressionUtil.writeOut(_finally.getBody(), bc);
148                                }
149                        }
150                },getFlowControlFinal());
151                
152                
153                // Try
154                tcfv.visitTryBegin(bc);
155                        BodyBase.writeOut(bc, tryBody);
156                        //ExpressionUtil.writeOut(tryBody, bc);
157                int e=tcfv.visitTryEndCatchBeging(bc);
158                        // if(e instanceof lucee.runtime.exp.Abort) throw e;
159                        Label abortEnd=new Label();
160                        adapter.loadLocal(e);
161                        // Abort.isAbort(t);
162                        adapter.invokeStatic(Types.ABORT, TryCatchFinally.IS_ABORT);
163                        //adapter.instanceOf(Types.ABORT);
164                        
165                        
166                        
167                adapter.ifZCmp(Opcodes.IFEQ, abortEnd);
168                        adapter.loadLocal(e);
169                        adapter.throwException();
170                adapter.visitLabel(abortEnd);
171                    
172
173                // PageExceptionImpl old=pc.getCatch();
174                int old=adapter.newLocal(Types.PAGE_EXCEPTION);
175                adapter.loadArg(0);
176                adapter.invokeVirtual(Types.PAGE_CONTEXT, GET_CATCH);
177                        adapter.storeLocal(old);
178                        
179                // PageException pe=Caster.toPageEception(e);
180                int pe=adapter.newLocal(Types.PAGE_EXCEPTION);
181                adapter.loadLocal(e);
182                adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION);
183                        adapter.storeLocal(pe);
184                        
185                        Iterator<Tag> it = catches.iterator();
186                        Attribute attrType;
187                        Expression type;
188                        Label endAllIfs=new Label();
189                        Tag tagElse=null;
190                        while(it.hasNext()) {
191                                tag=it.next();
192                                Label endIf=new Label();
193                                attrType = tag.getAttribute("type");
194                                type=ANY;
195                                if(attrType!=null)type=attrType.getValue();
196
197                                if(type instanceof LitString && ((LitString)type).getString().equalsIgnoreCase("any")){
198                                        tagElse=tag;
199                                        continue;
200                                }
201                                
202                                ExpressionUtil.visitLine(bc, tag.getStart());
203                                
204                                // if(pe.typeEqual(@type)
205                                adapter.loadLocal(pe);
206                                type.writeOut(bc, Expression.MODE_REF);
207                                adapter.invokeVirtual(Types.PAGE_EXCEPTION, TYPE_EQUAL);
208                                
209                                adapter.ifZCmp(Opcodes.IFEQ, endIf);
210                                        catchBody(bc,adapter,tag,pe,true,true);
211                                        
212                    adapter.visitJumpInsn(Opcodes.GOTO, endAllIfs);
213                    
214                    adapter.visitLabel(endIf);
215                                
216                                
217                        }
218                        // else 
219                        if(tagElse!=null){
220                                catchBody(bc, adapter, tagElse, pe, true,true);
221                        }
222                        else{
223                                // pc.setCatch(pe,true);
224                                adapter.loadArg(0);
225                        adapter.loadLocal(pe);
226                        adapter.push(false);
227                        adapter.push(true);
228                        adapter.invokeVirtual(Types.PAGE_CONTEXT, SET_CATCH3);
229                                
230                                //throw pe;
231                                adapter.loadLocal(pe);
232                                adapter.throwException();
233                        }
234                        adapter.visitLabel(endAllIfs);
235                        
236                
237                // PageExceptionImpl old=pc.getCatch();
238        adapter.loadArg(0);
239        adapter.loadLocal(old);
240        adapter.invokeVirtual(Types.PAGE_CONTEXT, SET_CATCH_PE);
241                        
242                tcfv.visitCatchEnd(bc);
243        }
244        
245
246        private static void catchBody(BytecodeContext bc, GeneratorAdapter adapter,Tag tag, int pe,boolean caugth, boolean store) throws BytecodeException {
247                // pc.setCatch(pe,true);
248                adapter.loadArg(0);
249        adapter.loadLocal(pe);
250        adapter.push(caugth);
251        adapter.push(store);
252        adapter.invokeVirtual(Types.PAGE_CONTEXT, SET_CATCH3);
253        BodyBase.writeOut(bc, tag.getBody());
254                //ExpressionUtil.writeOut(tag.getBody(), bc);
255        
256        }
257        
258        private boolean hasFinally(){
259                List<Statement> statements = getBody().getStatements();
260                Statement stat;
261                Tag tag;
262                Iterator<Statement> it = statements.iterator();
263                while(it.hasNext()) {
264                        stat= it.next();
265                        if(stat instanceof Tag) {
266                                tag=(Tag) stat;
267                                if(tag.getTagLibTag().getTagClassName().equals("lucee.runtime.tag.Finally"))    {
268                                        return true;
269                                }
270                        }
271                }
272                return false;
273        }
274        
275
276        @Override
277        public FlowControlFinal getFlowControlFinal() {
278                if(!checked) {
279                        checked=true;
280                        if(!hasFinally()) return null;
281                        fcf=new FlowControlFinalImpl();
282                }
283                        
284                return fcf;
285        }
286
287        @Override
288        public Label getRetryLabel() {
289                return begin;
290        }
291
292        @Override
293        public String getLabel() {
294                return null;
295        }
296
297
298}