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}