package railo.transformer.bytecode.statement.tag; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import javax.servlet.jsp.tagext.BodyTag; import javax.servlet.jsp.tagext.IterationTag; 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; import railo.commons.lang.ClassException; import railo.runtime.exp.Abort; import railo.runtime.tag.MissingAttribute; import railo.transformer.bytecode.BytecodeContext; import railo.transformer.bytecode.BytecodeException; import railo.transformer.bytecode.cast.CastOther; import railo.transformer.bytecode.expression.Expression; import railo.transformer.bytecode.expression.var.Variable; import railo.transformer.bytecode.literal.LitString; import railo.transformer.bytecode.statement.FlowControlFinal; import railo.transformer.bytecode.util.ASMConstants; import railo.transformer.bytecode.util.ExpressionUtil; import railo.transformer.bytecode.util.Types; import railo.transformer.bytecode.visitor.ArrayVisitor; import railo.transformer.bytecode.visitor.OnFinally; import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor; import railo.transformer.bytecode.visitor.TryFinallyVisitor; import railo.transformer.library.tag.TagLibTag; public final class TagHelper { private static final Type MISSING_ATTRIBUTE = Type.getType(MissingAttribute.class); private static final Type MISSING_ATTRIBUTE_ARRAY = Type.getType(MissingAttribute[].class); private static final Type TAG=Type.getType(javax.servlet.jsp.tagext.Tag.class); private static final Type TAG_UTIL=Type.getType(railo.runtime.tag.TagUtil.class); // TagUtil.setAttributeCollection(Tag, Struct) private static final Method SET_ATTRIBUTE_COLLECTION = new Method( "setAttributeCollection",Types.VOID,new Type[]{Types.PAGE_CONTEXT,TAG,MISSING_ATTRIBUTE_ARRAY,Types.STRUCT,Types.INT_VALUE}); // Tag use(String) private static final Method USE= new Method("use",TAG,new Type[]{Types.STRING}); // void setAppendix(String appendix) private static final Method SET_APPENDIX = new Method("setAppendix",Type.VOID_TYPE,new Type[]{Types.STRING}); // void setDynamicAttribute(String uri, String name, Object value) private static final Method SET_DYNAMIC_ATTRIBUTE = new Method( "setDynamicAttribute", Type.VOID_TYPE, new Type[]{Types.STRING,Types.COLLECTION_KEY,Types.OBJECT}); private static final Method SET_META_DATA = new Method( "setMetaData", Type.VOID_TYPE, new Type[]{Types.STRING,Types.OBJECT}); // void hasBody(boolean hasBody) private static final Method HAS_BODY = new Method( "hasBody", Type.VOID_TYPE, new Type[]{Types.BOOLEAN_VALUE}); // int doStartTag() private static final Method DO_START_TAG = new Method( "doStartTag", Types.INT_VALUE, new Type[]{}); // int doEndTag() private static final Method DO_END_TAG = new Method( "doEndTag", Types.INT_VALUE, new Type[]{}); private static final Type ABORT = Type.getType(Abort.class); //private static final Type EXPRESSION_EXCEPTION = Type.getType(ExpressionException.class); private static final Type BODY_TAG = Type.getType(BodyTag.class); // ExpressionException newInstance(int) private static final Method NEW_INSTANCE = new Method( "newInstance", ABORT, new Type[]{Types.INT_VALUE}); private static final Method NEW_INSTANCE_MAX = new Method( "newInstance", MISSING_ATTRIBUTE, new Type[]{Types.COLLECTION_KEY,Types.STRING}); // void initBody(BodyTag bodyTag, int state) private static final Method INIT_BODY = new Method( "initBody", Types.VOID, new Type[]{BODY_TAG,Types.INT_VALUE}); // int doAfterBody() private static final Method DO_AFTER_BODY = new Method( "doAfterBody", Types.INT_VALUE, new Type[]{}); // void doCatch(Throwable t) private static final Method DO_CATCH = new Method( "doCatch", Types.VOID, new Type[]{Types.THROWABLE}); // void doFinally() private static final Method DO_FINALLY = new Method( "doFinally", Types.VOID, new Type[]{}); // JspWriter popBody() private static final Method POP_BODY = new Method( "popBody", Types.JSP_WRITER, new Type[]{}); // void reuse(Tag tag) private static final Method RE_USE = new Method( "reuse", Types.VOID, new Type[]{Types.TAG}); /** * writes out the tag * @param tag * @param bc * @param doReuse * @throws BytecodeException */ public static void writeOut(Tag tag, BytecodeContext bc, boolean doReuse, final FlowControlFinal fcf) throws BytecodeException { final GeneratorAdapter adapter = bc.getAdapter(); final TagLibTag tlt = tag.getTagLibTag(); final Type currType=getTagType(tag); final int currLocal=adapter.newLocal(currType); Label tagBegin=new Label(); Label tagEnd=new Label(); ExpressionUtil.visitLine(bc, tag.getStart()); // TODO adapter.visitLocalVariable("tag", "L"+currType.getInternalName()+";", null, tagBegin, tagEnd, currLocal); adapter.visitLabel(tagBegin); // tag=pc.use(str); adapter.loadArg(0); adapter.push(tlt.getTagClassName()); adapter.invokeVirtual(Types.PAGE_CONTEXT, USE); adapter.checkCast(currType); adapter.storeLocal(currLocal); TryFinallyVisitor outerTcfv=new TryFinallyVisitor(new OnFinally() { public void writeOut(BytecodeContext bc) { adapter.loadArg(0); adapter.loadLocal(currLocal); adapter.invokeVirtual(Types.PAGE_CONTEXT, RE_USE); } },null); if(doReuse)outerTcfv.visitTryBegin(bc); // appendix if(tlt.hasAppendix()) { adapter.loadLocal(currLocal); adapter.push(tag.getAppendix()); adapter.invokeVirtual(currType, SET_APPENDIX); } // hasBody boolean hasBody=tag.getBody()!=null; if(tlt.isBodyFree() && tlt.hasBodyMethodExists()) { adapter.loadLocal(currLocal); adapter.push(hasBody); adapter.invokeVirtual(currType, HAS_BODY); } // attributes Attribute attr; // attributeCollection attr=tag.getAttribute("attributecollection"); if(attr!=null){ int attrType = tag.getTagLibTag().getAttributeType(); if(TagLibTag.ATTRIBUTE_TYPE_NONAME!=attrType) { tag.removeAttribute("attributecollection"); // TagUtil.setAttributeCollection(Tag, Struct) adapter.loadArg(0); adapter.loadLocal(currLocal); adapter.cast(currType, TAG); /// Map missings = tag.getMissingAttributes(); if(missings.size()>0) { ArrayVisitor av=new ArrayVisitor(); av.visitBegin(adapter,MISSING_ATTRIBUTE,missings.size()); Map.Entry entry; int count=0; Iterator it = missings.entrySet().iterator(); while(it.hasNext()){ entry=(Entry) it.next(); av.visitBeginItem(adapter, count++); Variable.registerKey(bc, LitString.toExprString((String)entry.getKey())); adapter.push((String)entry.getValue()); adapter.invokeStatic(MISSING_ATTRIBUTE, NEW_INSTANCE_MAX); av.visitEndItem(bc.getAdapter()); } av.visitEnd(); } else { ASMConstants.NULL(adapter); } /// attr.getValue().writeOut(bc, Expression.MODE_REF); adapter.push(attrType); adapter.invokeStatic(TAG_UTIL, SET_ATTRIBUTE_COLLECTION); } } // metadata Map<String, Attribute> metadata = tag.getMetaData(); if(metadata!=null){ Iterator<Attribute> it = metadata.values().iterator(); while(it.hasNext()) { attr=it.next(); adapter.loadLocal(currLocal); adapter.push(attr.getName()); attr.getValue().writeOut(bc, Expression.MODE_REF); adapter.invokeVirtual(currType, SET_META_DATA); } } String methodName; Map attributes = tag.getAttributes(); // static attributes Iterator it = attributes.values().iterator(); while(it.hasNext()) { attr=(Attribute) it.next(); if(!attr.isDynamicType()){ Type type = CastOther.getType(attr.getType()); methodName=tag.getTagLibTag().getSetter(attr,type); adapter.loadLocal(currLocal); attr.getValue().writeOut(bc, Types.isPrimitiveType(type)?Expression.MODE_VALUE:Expression.MODE_REF); adapter.invokeVirtual(currType, new Method(methodName,Type.VOID_TYPE,new Type[]{type})); } } // dynamic attributes it = attributes.values().iterator(); while(it.hasNext()) { attr=(Attribute) it.next(); if(attr.isDynamicType()){ adapter.loadLocal(currLocal); adapter.visitInsn(Opcodes.ACONST_NULL); //adapter.push(attr.getName()); Variable.registerKey(bc, LitString.toExprString(attr.getName())); attr.getValue().writeOut(bc, Expression.MODE_REF); adapter.invokeVirtual(currType, SET_DYNAMIC_ATTRIBUTE); } } // Body if(hasBody){ final int state=adapter.newLocal(Types.INT_VALUE); // int state=tag.doStartTag(); adapter.loadLocal(currLocal); adapter.invokeVirtual(currType, DO_START_TAG); adapter.storeLocal(state); // if (state!=Tag.SKIP_BODY) Label endBody=new Label(); adapter.loadLocal(state); adapter.push(javax.servlet.jsp.tagext.Tag.SKIP_BODY); adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, endBody); // pc.initBody(tag, state); adapter.loadArg(0); adapter.loadLocal(currLocal); adapter.loadLocal(state); adapter.invokeVirtual(Types.PAGE_CONTEXT, INIT_BODY); OnFinally onFinally = new OnFinally() { public void writeOut(BytecodeContext bc) { Label endIf = new Label(); /*if(tlt.handleException() && fcf!=null && fcf.getAfterFinalGOTOLabel()!=null){ ASMUtil.visitLabel(adapter, fcf.getFinalEntryLabel()); }*/ adapter.loadLocal(state); adapter.push(javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE); adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, endIf); // ... pc.popBody(); adapter.loadArg(0); adapter.invokeVirtual(Types.PAGE_CONTEXT, POP_BODY); adapter.pop(); adapter.visitLabel(endIf); // tag.doFinally(); if(tlt.handleException()) { adapter.loadLocal(currLocal); adapter.invokeVirtual(currType, DO_FINALLY); } // GOTO after execution body, used when a continue/break was called before /*if(fcf!=null) { Label l = fcf.getAfterFinalGOTOLabel(); if(l!=null)adapter.visitJumpInsn(Opcodes.GOTO, l); }*/ } }; if(tlt.handleException()) { TryCatchFinallyVisitor tcfv=new TryCatchFinallyVisitor(onFinally,fcf); tcfv.visitTryBegin(bc); doTry(bc,adapter,tag,currLocal,currType); int t=tcfv.visitTryEndCatchBeging(bc); // tag.doCatch(t); adapter.loadLocal(currLocal); adapter.loadLocal(t); //adapter.visitVarInsn(Opcodes.ALOAD,t); adapter.invokeVirtual(currType, DO_CATCH); tcfv.visitCatchEnd(bc); } else { TryFinallyVisitor tfv=new TryFinallyVisitor(onFinally,fcf); tfv.visitTryBegin(bc); doTry(bc,adapter,tag,currLocal,currType); tfv.visitTryEnd(bc); } adapter.visitLabel(endBody); } else { //tag.doStartTag(); adapter.loadLocal(currLocal); adapter.invokeVirtual(currType, DO_START_TAG); adapter.pop(); } // if (tag.doEndTag()==Tag.SKIP_PAGE) throw new Abort(0<!-- SCOPE_PAGE -->); Label endDoEndTag=new Label(); adapter.loadLocal(currLocal); adapter.invokeVirtual(currType, DO_END_TAG); adapter.push(javax.servlet.jsp.tagext.Tag.SKIP_PAGE); adapter.visitJumpInsn(Opcodes.IF_ICMPNE, endDoEndTag); adapter.push(Abort.SCOPE_PAGE); adapter.invokeStatic(ABORT, NEW_INSTANCE); adapter.throwException(); adapter.visitLabel(endDoEndTag); if(doReuse) { // } finally{pc.reuse(tag);} outerTcfv.visitTryEnd(bc); } adapter.visitLabel(tagEnd); ExpressionUtil.visitLine(bc, tag.getEnd()); } private static void doTry(BytecodeContext bc, GeneratorAdapter adapter, Tag tag, int currLocal, Type currType) throws BytecodeException { Label beginDoWhile=new Label(); adapter.visitLabel(beginDoWhile); bc.setCurrentTag(currLocal); tag.getBody().writeOut(bc); // while (tag.doAfterBody()==BodyTag.EVAL_BODY_AGAIN); adapter.loadLocal(currLocal); adapter.invokeVirtual(currType, DO_AFTER_BODY); adapter.push(IterationTag.EVAL_BODY_AGAIN); adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, beginDoWhile); } private static Type getTagType(Tag tag) throws BytecodeException { TagLibTag tlt = tag.getTagLibTag(); try { return tlt.getTagType(); } catch (ClassException e) { throw new BytecodeException(e,tag.getStart()); } } }