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