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