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
021
022import java.util.ArrayList;
023import java.util.Iterator;
024import java.util.List;
025
026import lucee.runtime.type.scope.Scope;
027import lucee.transformer.bytecode.Body;
028import lucee.transformer.bytecode.BytecodeContext;
029import lucee.transformer.bytecode.BytecodeException;
030import lucee.transformer.bytecode.Position;
031import lucee.transformer.bytecode.expression.ExprString;
032import lucee.transformer.bytecode.expression.Expression;
033import lucee.transformer.bytecode.expression.var.DataMember;
034import lucee.transformer.bytecode.expression.var.Variable;
035import lucee.transformer.bytecode.expression.var.VariableRef;
036import lucee.transformer.bytecode.expression.var.VariableString;
037import lucee.transformer.bytecode.literal.LitBoolean;
038import lucee.transformer.bytecode.literal.LitString;
039import lucee.transformer.bytecode.statement.tag.TagTry;
040import lucee.transformer.bytecode.util.ExpressionUtil;
041import lucee.transformer.bytecode.util.Types;
042import lucee.transformer.bytecode.visitor.OnFinally;
043import lucee.transformer.bytecode.visitor.TryCatchFinallyVisitor;
044
045import org.objectweb.asm.Label;
046import org.objectweb.asm.Opcodes;
047import org.objectweb.asm.Type;
048import org.objectweb.asm.commons.GeneratorAdapter;
049import org.objectweb.asm.commons.Method;
050
051/**
052 * produce  try-catch-finally
053 */
054public final class TryCatchFinally extends StatementBase implements Opcodes,HasBodies,FlowControlRetry {
055
056        //private static LitString ANY=LitString.toExprString("any", -1);
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        
065        //  public boolean typeEqual(String type);
066        private static final Method TYPE_EQUAL = new Method(
067                        "typeEqual",
068                        Types.BOOLEAN_VALUE,
069                        new Type[]{Types.STRING});
070        
071        
072        // Struct getCatchBlock(PageContext pc);
073        private static final Method GET_CATCH_BLOCK = new Method(
074                        "getCatchBlock",
075                        Types.STRUCT,
076                        new Type[]{Types.PAGE_CONTEXT});
077
078
079        // void isAbort(e)
080        public static final Method IS_ABORT = new Method(
081                        "isAbort",
082                        Types.BOOLEAN_VALUE,
083                        new Type[]{Types.THROWABLE});
084
085        
086
087        private final static Method SET = new Method("set",Types.OBJECT,new Type[]{Types.PAGE_CONTEXT,Types.OBJECT});
088
089        private static final Method REMOVE_EL = new Method("removeEL",Types.OBJECT,new Type[]{Types.PAGE_CONTEXT});
090        
091        
092        
093        
094        private Body tryBody;
095        private Body finallyBody;
096        private List<Catch> catches=new ArrayList<Catch>();
097        private Position finallyLine;
098
099
100        private Label begin = new Label();
101
102        private FlowControlFinal fcf;
103
104
105        /**
106         * Constructor of the class
107         * @param body
108         * @param line
109         */
110        public TryCatchFinally(Body body,Position start, Position end) {
111                super(start,end);
112                this.tryBody=body;
113                body.setParent(this);
114        }
115
116        /**
117         * sets finally body
118         * @param body
119         */
120        public void setFinally(Body body, Position finallyLine) {
121                body.setParent(this);
122                this.finallyBody=body;
123                this.finallyLine=finallyLine;
124        }
125
126        /**
127         * data for a single catch block
128         */
129        private class Catch {
130
131                private ExprString type;
132                private Body body;
133                private VariableRef name;
134                private Position line;
135
136                public Catch(ExprString type, VariableRef name, Body body, Position line) {
137                        this.type=type;
138                        this.name=name;
139                        this.body=body;
140                        this.line=line;
141                }
142                
143        }
144        
145        
146        
147        /**
148         *
149         * @see lucee.transformer.bytecode.statement.StatementBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter)
150         */
151        public void _writeOut(BytecodeContext bc) throws BytecodeException {
152                GeneratorAdapter adapter = bc.getAdapter();
153                
154                adapter.visitLabel(begin);
155                
156                // Reference ref=null;
157                final int lRef=adapter.newLocal(Types.REFERENCE);
158                adapter.visitInsn(Opcodes.ACONST_NULL);
159                adapter.storeLocal(lRef);
160                
161                // has no try body, if there is no try body, no catches are executed, only finally 
162                if(!tryBody.hasStatements()) {
163                        if(finallyBody!=null)finallyBody.writeOut(bc);
164                        return;
165                }
166                
167                
168                TryCatchFinallyVisitor tcfv=new TryCatchFinallyVisitor(new OnFinally() {
169                        
170                        public void _writeOut(BytecodeContext bc) throws BytecodeException {
171                                _writeOutFinally(bc,lRef);
172                        }
173                },getFlowControlFinal());
174
175                // try
176                tcfv.visitTryBegin(bc);
177                        tryBody.writeOut(bc);
178                int lThrow = tcfv.visitTryEndCatchBeging(bc);
179                        _writeOutCatch(bc, lRef, lThrow);
180                tcfv.visitCatchEnd(bc);
181                
182        }
183        
184        
185        
186        
187        
188        
189        
190        
191        private void _writeOutFinally(BytecodeContext bc, int lRef) throws BytecodeException {
192                // ref.remove(pc);
193                //Reference r=null;
194                GeneratorAdapter adapter = bc.getAdapter();
195                
196                //if(fcf!=null && fcf.getAfterFinalGOTOLabel()!=null)ASMUtil.visitLabel(adapter,fcf.getFinalEntryLabel());
197                ExpressionUtil.visitLine(bc, finallyLine);
198                
199                
200                
201                //if (reference != null)
202                //    reference.removeEL(pagecontext);
203                Label removeEnd=new Label();
204                adapter.loadLocal(lRef);
205                adapter.ifNull(removeEnd);
206                        adapter.loadLocal(lRef);
207                        adapter.loadArg(0);
208                        adapter.invokeInterface(Types.REFERENCE, REMOVE_EL);
209                        adapter.pop();
210                adapter.visitLabel(removeEnd);
211                
212                if(finallyBody!=null)finallyBody.writeOut(bc); // finally
213                /*if(fcf!=null){
214                        Label l = fcf.getAfterFinalGOTOLabel();
215                        if(l!=null)adapter.visitJumpInsn(Opcodes.GOTO, l);
216                }*/
217        }
218        
219        private void _writeOutCatch(BytecodeContext bc, int lRef,int lThrow) throws BytecodeException {
220                GeneratorAdapter adapter = bc.getAdapter();
221                int pe=adapter.newLocal(Types.PAGE_EXCEPTION);
222                
223                
224                // instance of Abort
225                        Label abortEnd=new Label();
226                        adapter.loadLocal(lThrow);
227                        adapter.invokeStatic(Types.ABORT, TryCatchFinally.IS_ABORT);
228                        //adapter.instanceOf(Types.ABORT);
229                adapter.ifZCmp(Opcodes.IFEQ, abortEnd);
230                adapter.loadLocal(lThrow);
231                adapter.throwException();
232                adapter.visitLabel(abortEnd);
233
234
235                // PageExceptionImpl old=pc.getCatch();
236                int old=adapter.newLocal(Types.PAGE_EXCEPTION);
237                adapter.loadArg(0);
238                adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.GET_CATCH);
239                        adapter.storeLocal(old);
240                
241                
242                        // cast to PageException  Caster.toPagException(t);
243                adapter.loadLocal(lThrow);
244                adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION);
245                
246            // PageException pe=...
247                adapter.storeLocal(pe);
248
249            // catch loop
250                        Label endAllIf = new Label();
251                Iterator<Catch> it = catches.iterator();
252                Catch ctElse=null;
253                        while(it.hasNext()) {
254                                Catch ct=it.next();
255                                // store any for else
256                                if(ct.type!=null && ct.type instanceof LitString && ((LitString)ct.type).getString().equalsIgnoreCase("any")){
257                                        ctElse=ct;
258                                        continue;
259                                }
260                                
261                                ExpressionUtil.visitLine(bc, ct.line);
262                                
263                                // pe.typeEqual(type)
264                                if(ct.type==null){
265                                        LitBoolean.TRUE.writeOut(bc, Expression.MODE_VALUE);
266                                }
267                                else {
268                                        adapter.loadLocal(pe);
269                                        ct.type.writeOut(bc, Expression.MODE_REF);
270                                        adapter.invokeVirtual(Types.PAGE_EXCEPTION, TYPE_EQUAL);
271                                }
272                                
273                                
274                                
275
276                                Label endIf = new Label();
277                    adapter.ifZCmp(Opcodes.IFEQ, endIf);
278                    
279                    catchBody(bc,adapter,ct,pe,lRef,true,true);
280                    
281                    adapter.visitJumpInsn(Opcodes.GOTO, endAllIf);
282                    adapter.visitLabel(endIf);
283                    
284
285                                
286                        }
287                        
288                        if(ctElse!=null){
289                                catchBody(bc,adapter,ctElse,pe,lRef,true,true);
290                        }
291                        else{
292                        // pc.setCatch(pe,true);
293                                adapter.loadArg(0);
294                        adapter.loadLocal(pe);
295                        adapter.push(false);
296                        adapter.push(false);
297                        adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH3);
298                    
299                                
300                                adapter.loadLocal(pe);
301                                adapter.throwException();
302                        }
303                        adapter.visitLabel(endAllIf);
304                        
305                // PageExceptionImpl old=pc.setCatch(old);
306            adapter.loadArg(0);
307            adapter.loadLocal(old);
308            adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH_PE);
309                        
310        }
311
312        private static void catchBody(BytecodeContext bc, GeneratorAdapter adapter, Catch ct, int pe, int lRef,boolean caugth,boolean store) throws BytecodeException {
313                // pc.setCatch(pe,true);
314                adapter.loadArg(0);
315        adapter.loadLocal(pe);
316        adapter.push(caugth);
317        adapter.push(store);
318        adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH3);
319        
320        
321        // ref=
322        ct.name.writeOut(bc, Expression.MODE_REF);
323        adapter.storeLocal(lRef);
324        
325                adapter.loadLocal(lRef);
326                adapter.loadArg(0);
327                adapter.loadLocal(pe);// (...,pe.getCatchBlock(pc))
328                adapter.loadArg(0);
329        adapter.invokeVirtual(Types.PAGE_EXCEPTION, GET_CATCH_BLOCK);
330                adapter.invokeInterface(Types.REFERENCE, SET);
331                adapter.pop();
332        
333        ct.body.writeOut(bc);
334        }
335
336        /**
337         * @param type
338         * @param name
339         * @param body
340         * @param line
341         */
342        public void addCatch(ExprString type, VariableRef name, Body body, Position line) {
343                body.setParent(this);
344                catches.add(new Catch(type,name,body,line));
345        }
346
347        /**
348         * @param type
349         * @param name
350         * @param b
351         * @param line
352         * @throws BytecodeException
353         */
354        public void addCatch(Expression type, Expression name, Body b, Position line) throws BytecodeException {
355                
356                // type
357                if(type==null || type instanceof ExprString) ;
358                else if(type instanceof Variable) {
359                        type=VariableString.toExprString(type);
360                }
361                else throw new BytecodeException("type from catch statement is invalid",type.getStart());
362                
363                // name
364                if(name instanceof LitString){
365                        Variable v = new Variable(Scope.SCOPE_UNDEFINED,name.getStart(),name.getEnd());
366                        v.addMember(new DataMember(name));
367                        name=new VariableRef(v);
368                }
369                else if(name instanceof Variable) name=new VariableRef((Variable) name);
370                else throw new BytecodeException("name from catch statement is invalid",name.getStart());
371                
372                addCatch((ExprString)type, (VariableRef)name, b, line);
373        }       
374        
375
376        /**
377         * @see lucee.transformer.bytecode.statement.HasBodies#getBodies()
378         */
379        public Body[] getBodies() {
380                
381                int len=catches.size(),count=0;
382                if(tryBody!=null)len++;
383                if(finallyBody!=null)len++;
384                Body[] bodies=new Body[len];
385                Catch c;
386                Iterator<Catch> it = catches.iterator();
387                while(it.hasNext()) {
388                        c=it.next();
389                        bodies[count++]=c.body;
390                }
391                if(tryBody!=null)bodies[count++]=tryBody;
392                if(finallyBody!=null)bodies[count++]=finallyBody;
393                
394                return bodies;
395        }
396
397        @Override
398        public FlowControlFinal getFlowControlFinal() {
399                if(fcf==null) fcf=new FlowControlFinalImpl();
400                return fcf;
401        }
402
403        @Override
404        public Label getRetryLabel() {
405                return begin;
406        }
407
408        @Override
409        public String getLabel() {
410                return null;
411        }
412}