/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.transformer.bytecode.statement; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import lucee.runtime.type.scope.Scope; import lucee.transformer.Factory; import lucee.transformer.Position; import lucee.transformer.TransformerException; import lucee.transformer.bytecode.Body; import lucee.transformer.bytecode.BytecodeContext; import lucee.transformer.bytecode.expression.var.VariableRef; import lucee.transformer.bytecode.expression.var.VariableString; import lucee.transformer.bytecode.statement.tag.TagTry; import lucee.transformer.bytecode.util.ExpressionUtil; import lucee.transformer.bytecode.util.Types; import lucee.transformer.bytecode.visitor.OnFinally; import lucee.transformer.bytecode.visitor.TryCatchFinallyVisitor; import lucee.transformer.expression.ExprString; import lucee.transformer.expression.Expression; import lucee.transformer.expression.literal.LitString; import lucee.transformer.expression.var.Variable; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import org.objectweb.asm.commons.Method; /** * produce try-catch-finally */ public final class TryCatchFinally extends StatementBase implements Opcodes,HasBodies,FlowControlRetry { //private static LitString ANY=LitString.toExprString("any", -1); private static final Method TO_PAGE_EXCEPTION = new Method( "toPageException", Types.PAGE_EXCEPTION, new Type[]{Types.THROWABLE}); // public boolean typeEqual(String type); private static final Method TYPE_EQUAL = new Method( "typeEqual", Types.BOOLEAN_VALUE, new Type[]{Types.STRING}); // Struct getCatchBlock(PageContext pc); private static final Method GET_CATCH_BLOCK = new Method( "getCatchBlock", Types.STRUCT, new Type[]{Types.PAGE_CONTEXT}); // void isAbort(e) public static final Method IS_ABORT = new Method( "isAbort", Types.BOOLEAN_VALUE, new Type[]{Types.THROWABLE}); private final static Method SET = new Method("set",Types.OBJECT,new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); private static final Method REMOVE_EL = new Method("removeEL",Types.OBJECT,new Type[]{Types.PAGE_CONTEXT}); private Body tryBody; private Body finallyBody; private List<Catch> catches=new ArrayList<Catch>(); private Position finallyLine; private Label begin = new Label(); private FlowControlFinal fcf; /** * Constructor of the class * @param body * @param line */ public TryCatchFinally(Factory factory,Body body,Position start, Position end) { super(factory,start,end); this.tryBody=body; body.setParent(this); } /** * sets finally body * @param body */ public void setFinally(Body body, Position finallyLine) { body.setParent(this); this.finallyBody=body; this.finallyLine=finallyLine; } /** * data for a single catch block */ private class Catch { private ExprString type; private Body body; private VariableRef name; private Position line; public Catch(ExprString type, VariableRef name, Body body, Position line) { this.type=type; this.name=name; this.body=body; this.line=line; } } /** * * @see lucee.transformer.bytecode.statement.StatementBase#_writeOut(org.objectweb.asm.commons.GeneratorAdapter) */ @Override public void _writeOut(BytecodeContext bc) throws TransformerException { final GeneratorAdapter adapter = bc.getAdapter(); adapter.visitLabel(begin); // Reference ref=null; final int lRef=adapter.newLocal(Types.REFERENCE); adapter.visitInsn(Opcodes.ACONST_NULL); adapter.storeLocal(lRef); // has no try body, if there is no try body, no catches are executed, only finally if(!tryBody.hasStatements()) { if(finallyBody!=null)finallyBody.writeOut(bc); return; } // PageExceptionImpl old=pc.getCatch(); final int old=adapter.newLocal(Types.PAGE_EXCEPTION); adapter.loadArg(0); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.GET_CATCH); adapter.storeLocal(old); TryCatchFinallyVisitor tcfv=new TryCatchFinallyVisitor(new OnFinally() { @Override public void _writeOut(BytecodeContext bc) throws TransformerException { adapter.loadArg(0); adapter.loadLocal(old); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH_PE); _writeOutFinally(bc,lRef); } },getFlowControlFinal()); // try tcfv.visitTryBegin(bc); tryBody.writeOut(bc); int lThrow = tcfv.visitTryEndCatchBeging(bc); _writeOutCatch(bc, lRef, lThrow,old); tcfv.visitCatchEnd(bc); } private void _writeOutFinally(BytecodeContext bc, int lRef) throws TransformerException { // ref.remove(pc); //Reference r=null; GeneratorAdapter adapter = bc.getAdapter(); //if(fcf!=null && fcf.getAfterFinalGOTOLabel()!=null)ASMUtil.visitLabel(adapter,fcf.getFinalEntryLabel()); ExpressionUtil.visitLine(bc, finallyLine); //if (reference != null) // reference.removeEL(pagecontext); Label removeEnd=new Label(); adapter.loadLocal(lRef); adapter.ifNull(removeEnd); adapter.loadLocal(lRef); adapter.loadArg(0); adapter.invokeInterface(Types.REFERENCE, REMOVE_EL); adapter.pop(); adapter.visitLabel(removeEnd); if(finallyBody!=null)finallyBody.writeOut(bc); // finally /*if(fcf!=null){ Label l = fcf.getAfterFinalGOTOLabel(); if(l!=null)adapter.visitJumpInsn(Opcodes.GOTO, l); }*/ } private void _writeOutCatch(BytecodeContext bc, int lRef,int lThrow, int old) throws TransformerException { GeneratorAdapter adapter = bc.getAdapter(); int pe=adapter.newLocal(Types.PAGE_EXCEPTION); // instance of Abort Label abortEnd=new Label(); adapter.loadLocal(lThrow); adapter.invokeStatic(Types.ABORT, TryCatchFinally.IS_ABORT); //adapter.instanceOf(Types.ABORT); adapter.ifZCmp(Opcodes.IFEQ, abortEnd); adapter.loadLocal(lThrow); adapter.throwException(); adapter.visitLabel(abortEnd); /* // PageExceptionImpl old=pc.getCatch(); int old=adapter.newLocal(Types.PAGE_EXCEPTION); adapter.loadArg(0); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.GET_CATCH); adapter.storeLocal(old); */ // cast to PageException Caster.toPagException(t); adapter.loadLocal(lThrow); adapter.invokeStatic(Types.CASTER, TO_PAGE_EXCEPTION); // PageException pe=... adapter.storeLocal(pe); // catch loop Label endAllIf = new Label(); Iterator<Catch> it = catches.iterator(); Catch ctElse=null; while(it.hasNext()) { Catch ct=it.next(); // store any for else if(ct.type!=null && ct.type instanceof LitString && ((LitString)ct.type).getString().equalsIgnoreCase("any")){ ctElse=ct; continue; } ExpressionUtil.visitLine(bc, ct.line); // pe.typeEqual(type) if(ct.type==null){ getFactory().TRUE().writeOut(bc, Expression.MODE_VALUE); } else { adapter.loadLocal(pe); ct.type.writeOut(bc, Expression.MODE_REF); adapter.invokeVirtual(Types.PAGE_EXCEPTION, TYPE_EQUAL); } Label endIf = new Label(); adapter.ifZCmp(Opcodes.IFEQ, endIf); catchBody(bc,adapter,ct,pe,lRef,true,true); adapter.visitJumpInsn(Opcodes.GOTO, endAllIf); adapter.visitLabel(endIf); } if(ctElse!=null){ catchBody(bc,adapter,ctElse,pe,lRef,true,true); } else{ // pc.setCatch(pe,true); adapter.loadArg(0); adapter.loadLocal(pe); adapter.push(false); adapter.push(false); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH3); adapter.loadLocal(pe); adapter.throwException(); } adapter.visitLabel(endAllIf); /*adapter.loadArg(0); adapter.loadLocal(old); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH_PE);*/ } private static void catchBody(BytecodeContext bc, GeneratorAdapter adapter, Catch ct, int pe, int lRef,boolean caugth,boolean store) throws TransformerException { // pc.setCatch(pe,true); adapter.loadArg(0); adapter.loadLocal(pe); adapter.push(caugth); adapter.push(store); adapter.invokeVirtual(Types.PAGE_CONTEXT, TagTry.SET_CATCH3); // ref= ct.name.writeOut(bc, Expression.MODE_REF); adapter.storeLocal(lRef); adapter.loadLocal(lRef); adapter.loadArg(0); adapter.loadLocal(pe);// (...,pe.getCatchBlock(pc)) adapter.loadArg(0); adapter.invokeVirtual(Types.PAGE_EXCEPTION, GET_CATCH_BLOCK); adapter.invokeInterface(Types.REFERENCE, SET); adapter.pop(); ct.body.writeOut(bc); } /** * @param type * @param name * @param body * @param line */ public void addCatch(ExprString type, VariableRef name, Body body, Position line) { body.setParent(this); catches.add(new Catch(type,name,body,line)); } /** * @param type * @param name * @param b * @param line * @throws TransformerException */ public void addCatch(Expression type, Expression name, Body b, Position line) throws TransformerException { // MUSTMUST // type if(type==null || type instanceof ExprString) ; else if(type instanceof Variable) { type=VariableString.toExprString(type); } else throw new TransformerException("type from catch statement is invalid",type.getStart()); // name if(name instanceof LitString){ Variable v = getFactory().createVariable(Scope.SCOPE_UNDEFINED,name.getStart(),name.getEnd()); v.addMember(getFactory().createDataMember(getFactory().toExprString(name))); name=new VariableRef(v,true); } else if(name instanceof Variable) name=new VariableRef((Variable) name,true); else throw new TransformerException("name from catch statement is invalid",name.getStart()); addCatch((ExprString)type, (VariableRef)name, b, line); } /** * @see lucee.transformer.bytecode.statement.HasBodies#getBodies() */ @Override public Body[] getBodies() { int len=catches.size(),count=0; if(tryBody!=null)len++; if(finallyBody!=null)len++; Body[] bodies=new Body[len]; Catch c; Iterator<Catch> it = catches.iterator(); while(it.hasNext()) { c=it.next(); bodies[count++]=c.body; } if(tryBody!=null)bodies[count++]=tryBody; if(finallyBody!=null)bodies[count++]=finallyBody; return bodies; } @Override public FlowControlFinal getFlowControlFinal() { if(fcf==null) fcf=new FlowControlFinalImpl(); return fcf; } @Override public Label getRetryLabel() { return begin; } @Override public String getLabel() { return null; } }