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 }