001    package railo.transformer.bytecode.statement.tag;
002    
003    import java.util.Iterator;
004    import java.util.Map;
005    import java.util.Map.Entry;
006    
007    import javax.servlet.jsp.tagext.BodyTag;
008    import javax.servlet.jsp.tagext.IterationTag;
009    
010    import org.objectweb.asm.Label;
011    import org.objectweb.asm.Opcodes;
012    import org.objectweb.asm.Type;
013    import org.objectweb.asm.commons.GeneratorAdapter;
014    import org.objectweb.asm.commons.Method;
015    
016    import railo.commons.lang.ClassException;
017    import railo.runtime.exp.Abort;
018    import railo.runtime.tag.MissingAttribute;
019    import railo.transformer.bytecode.BytecodeContext;
020    import railo.transformer.bytecode.BytecodeException;
021    import railo.transformer.bytecode.cast.CastOther;
022    import railo.transformer.bytecode.expression.Expression;
023    import railo.transformer.bytecode.expression.var.Variable;
024    import railo.transformer.bytecode.literal.LitString;
025    import railo.transformer.bytecode.statement.FlowControlFinal;
026    import railo.transformer.bytecode.util.ASMConstants;
027    import railo.transformer.bytecode.util.ExpressionUtil;
028    import railo.transformer.bytecode.util.Types;
029    import railo.transformer.bytecode.visitor.ArrayVisitor;
030    import railo.transformer.bytecode.visitor.OnFinally;
031    import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor;
032    import railo.transformer.bytecode.visitor.TryFinallyVisitor;
033    import railo.transformer.library.tag.TagLibTag;
034    
035    public final class TagHelper {
036            private static final Type MISSING_ATTRIBUTE = Type.getType(MissingAttribute.class);
037            private static final Type MISSING_ATTRIBUTE_ARRAY = Type.getType(MissingAttribute[].class);
038            private static final Type TAG=Type.getType(javax.servlet.jsp.tagext.Tag.class);
039            private static final Type TAG_UTIL=Type.getType(railo.runtime.tag.TagUtil.class);
040            
041            // TagUtil.setAttributeCollection(Tag, Struct)
042            private static final Method SET_ATTRIBUTE_COLLECTION = new Method(
043                            "setAttributeCollection",Types.VOID,new Type[]{Types.PAGE_CONTEXT,TAG,MISSING_ATTRIBUTE_ARRAY,Types.STRUCT,Types.INT_VALUE});
044            
045            // Tag use(String)
046            private static final Method USE= new Method("use",TAG,new Type[]{Types.STRING});
047            
048            // void setAppendix(String appendix)
049            private static final Method SET_APPENDIX = new Method("setAppendix",Type.VOID_TYPE,new Type[]{Types.STRING});
050            
051            // void setDynamicAttribute(String uri, String name, Object value)
052            private static final Method SET_DYNAMIC_ATTRIBUTE = new Method(
053                            "setDynamicAttribute",
054                            Type.VOID_TYPE,
055                            new Type[]{Types.STRING,Types.COLLECTION_KEY,Types.OBJECT});
056            
057            private static final Method SET_META_DATA = new Method(
058                            "setMetaData",
059                            Type.VOID_TYPE,
060                            new Type[]{Types.STRING,Types.OBJECT});
061    
062            // void hasBody(boolean hasBody)
063            private static final Method HAS_BODY = new Method(
064                            "hasBody",
065                            Type.VOID_TYPE,
066                            new Type[]{Types.BOOLEAN_VALUE});
067    
068            // int doStartTag()
069            private static final Method DO_START_TAG = new Method(
070                            "doStartTag",
071                            Types.INT_VALUE,
072                            new Type[]{});
073    
074            // int doEndTag()
075            private static final Method DO_END_TAG =  new Method(
076                            "doEndTag",
077                            Types.INT_VALUE,
078                            new Type[]{});
079    
080            private static final Type ABORT = Type.getType(Abort.class);
081            //private static final Type EXPRESSION_EXCEPTION = Type.getType(ExpressionException.class);
082            private static final Type BODY_TAG = Type.getType(BodyTag.class);
083    
084            // ExpressionException newInstance(int)
085            private static final Method NEW_INSTANCE =  new Method(
086                            "newInstance",
087                            ABORT,
088                            new Type[]{Types.INT_VALUE});
089            private static final Method NEW_INSTANCE_MAX =  new Method(
090                            "newInstance",
091                            MISSING_ATTRIBUTE,
092                            new Type[]{Types.COLLECTION_KEY,Types.STRING});
093            
094            
095            
096    
097            // void initBody(BodyTag bodyTag, int state)
098            private static final Method INIT_BODY = new Method(
099                            "initBody",
100                            Types.VOID,
101                            new Type[]{BODY_TAG,Types.INT_VALUE});
102    
103            // int doAfterBody()
104            private static final Method DO_AFTER_BODY = new Method(
105                            "doAfterBody",
106                            Types.INT_VALUE,
107                            new Type[]{});
108    
109            // void doCatch(Throwable t)
110            private static final Method DO_CATCH = new Method(
111                            "doCatch",
112                            Types.VOID,
113                            new Type[]{Types.THROWABLE});
114    
115            // void doFinally()
116            private static final Method DO_FINALLY = new Method(
117                            "doFinally",
118                            Types.VOID,
119                            new Type[]{});
120    
121            // JspWriter popBody()
122            private static final Method POP_BODY = new Method(
123                            "popBody",
124                            Types.JSP_WRITER,
125                            new Type[]{});
126    
127            // void reuse(Tag tag)
128            private static final Method RE_USE = new Method(
129                            "reuse",
130                            Types.VOID,
131                            new Type[]{Types.TAG});
132            
133            /**
134             * writes out the tag
135             * @param tag
136             * @param bc
137             * @param doReuse
138             * @throws BytecodeException
139             */
140            public static void writeOut(Tag tag, BytecodeContext bc, boolean doReuse, final FlowControlFinal fcf) throws BytecodeException {
141                    final GeneratorAdapter adapter = bc.getAdapter();
142                    final TagLibTag tlt = tag.getTagLibTag();
143                    final Type currType=getTagType(tag);
144                    
145                    final int currLocal=adapter.newLocal(currType);
146                    Label tagBegin=new Label();
147                    Label tagEnd=new Label();
148                    ExpressionUtil.visitLine(bc, tag.getStart());
149                    // TODO adapter.visitLocalVariable("tag", "L"+currType.getInternalName()+";", null, tagBegin, tagEnd, currLocal);
150    
151                    adapter.visitLabel(tagBegin);
152                    
153            // tag=pc.use(str);
154                    adapter.loadArg(0);
155                    adapter.push(tlt.getTagClassName());
156                    adapter.invokeVirtual(Types.PAGE_CONTEXT, USE);
157                    adapter.checkCast(currType);
158                    adapter.storeLocal(currLocal);
159            
160            TryFinallyVisitor outerTcfv=new TryFinallyVisitor(new OnFinally() {
161                    public void writeOut(BytecodeContext bc) {
162    
163                            adapter.loadArg(0);
164                            adapter.loadLocal(currLocal);
165                            adapter.invokeVirtual(Types.PAGE_CONTEXT, RE_USE);
166                    }
167            },null);
168            if(doReuse)outerTcfv.visitTryBegin(bc);
169                    
170            // appendix
171                    if(tlt.hasAppendix()) {
172                            adapter.loadLocal(currLocal);
173                            adapter.push(tag.getAppendix());
174                            adapter.invokeVirtual(currType, SET_APPENDIX);
175                    }
176            
177            // hasBody
178                    boolean hasBody=tag.getBody()!=null;
179                    if(tlt.isBodyFree() && tlt.hasBodyMethodExists()) {
180                            adapter.loadLocal(currLocal);
181                            adapter.push(hasBody);
182                            adapter.invokeVirtual(currType, HAS_BODY);
183                    }
184                    
185            // attributes
186                    Attribute attr;
187                    
188                    // attributeCollection
189                    attr=tag.getAttribute("attributecollection");
190                    if(attr!=null){
191                            int attrType = tag.getTagLibTag().getAttributeType();
192                            if(TagLibTag.ATTRIBUTE_TYPE_NONAME!=attrType) {
193                                    tag.removeAttribute("attributecollection");
194                                    // TagUtil.setAttributeCollection(Tag, Struct)
195                                    adapter.loadArg(0);
196                                    adapter.loadLocal(currLocal);
197                                    adapter.cast(currType, TAG);
198                                    
199                                    ///
200                                    Map missings = tag.getMissingAttributes();
201                                    if(missings.size()>0) {
202                                            ArrayVisitor av=new ArrayVisitor();
203                                av.visitBegin(adapter,MISSING_ATTRIBUTE,missings.size());
204                                Map.Entry entry;
205                                int count=0;
206                                Iterator it = missings.entrySet().iterator();
207                                while(it.hasNext()){
208                                    entry=(Entry) it.next();
209                                            av.visitBeginItem(adapter, count++);
210                                                    Variable.registerKey(bc, LitString.toExprString((String)entry.getKey()));
211                                                    adapter.push((String)entry.getValue());
212                                                    adapter.invokeStatic(MISSING_ATTRIBUTE, NEW_INSTANCE_MAX);
213                                            av.visitEndItem(bc.getAdapter());
214                                }
215                                av.visitEnd();
216                                    }
217                                    else {
218                                            ASMConstants.NULL(adapter);
219                                    }
220                                    ///
221                                    attr.getValue().writeOut(bc, Expression.MODE_REF);
222                                    
223                                    adapter.push(attrType);
224                                    adapter.invokeStatic(TAG_UTIL, SET_ATTRIBUTE_COLLECTION);
225                            }
226                    }
227    
228    
229                    // metadata
230                    Map<String, Attribute> metadata = tag.getMetaData();
231                    if(metadata!=null){
232                            Iterator<Attribute> it = metadata.values().iterator();
233                            while(it.hasNext()) {
234                                    attr=it.next();
235                                            adapter.loadLocal(currLocal);
236                                            adapter.push(attr.getName());
237                                            attr.getValue().writeOut(bc, Expression.MODE_REF);
238                                            adapter.invokeVirtual(currType, SET_META_DATA);
239                            }
240                    }
241                    
242                    
243                    
244                    String methodName;
245                    Map attributes = tag.getAttributes();
246    
247                    // static attributes
248                    Iterator it = attributes.values().iterator();
249                    while(it.hasNext()) {
250                            attr=(Attribute) it.next();
251                            if(!attr.isDynamicType()){
252                                    Type type = CastOther.getType(attr.getType());
253                                    methodName=tag.getTagLibTag().getSetter(attr,type);
254                                    adapter.loadLocal(currLocal);
255                                    attr.getValue().writeOut(bc, Types.isPrimitiveType(type)?Expression.MODE_VALUE:Expression.MODE_REF);
256                                    adapter.invokeVirtual(currType, new Method(methodName,Type.VOID_TYPE,new Type[]{type}));
257                            }
258                    }
259                    
260                    // dynamic attributes
261                    it = attributes.values().iterator();
262                    while(it.hasNext()) {
263                            attr=(Attribute) it.next();
264                            if(attr.isDynamicType()){
265                                    adapter.loadLocal(currLocal);
266                                    adapter.visitInsn(Opcodes.ACONST_NULL);
267                                    //adapter.push(attr.getName());
268                                    Variable.registerKey(bc, LitString.toExprString(attr.getName()));
269                                    attr.getValue().writeOut(bc, Expression.MODE_REF);
270                                    adapter.invokeVirtual(currType, SET_DYNAMIC_ATTRIBUTE);
271                            }
272                    }
273                    
274                    
275            // Body
276                    if(hasBody){
277                            final int state=adapter.newLocal(Types.INT_VALUE);
278                            
279                            // int state=tag.doStartTag();
280                            adapter.loadLocal(currLocal);
281                            adapter.invokeVirtual(currType, DO_START_TAG);
282                            adapter.storeLocal(state);
283                            
284                            // if (state!=Tag.SKIP_BODY)
285                            Label endBody=new Label();
286                            adapter.loadLocal(state);
287                            adapter.push(javax.servlet.jsp.tagext.Tag.SKIP_BODY);
288                            adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, endBody);
289                                    // pc.initBody(tag, state);
290                                    adapter.loadArg(0);
291                                    adapter.loadLocal(currLocal);
292                                    adapter.loadLocal(state);
293                                    adapter.invokeVirtual(Types.PAGE_CONTEXT, INIT_BODY);
294                                    
295                                    
296                                    OnFinally onFinally = new OnFinally() {
297                                            
298                                            public void writeOut(BytecodeContext bc) {
299                                                    Label endIf = new Label();
300                                                    /*if(tlt.handleException() && fcf!=null && fcf.getAfterFinalGOTOLabel()!=null){
301                                                            ASMUtil.visitLabel(adapter, fcf.getFinalEntryLabel());
302                                                    }*/
303                                                    adapter.loadLocal(state);
304                                                    adapter.push(javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE);
305                                                    adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, endIf);
306                                                            // ... pc.popBody();
307                                                            adapter.loadArg(0);
308                                                            adapter.invokeVirtual(Types.PAGE_CONTEXT, POP_BODY);
309                                                            adapter.pop();
310                                                    adapter.visitLabel(endIf);
311                                            
312                                                    // tag.doFinally();
313                                                    if(tlt.handleException()) {
314                                                            adapter.loadLocal(currLocal);
315                                                            adapter.invokeVirtual(currType, DO_FINALLY);
316                                                    }
317                                                    // GOTO after execution body, used when a continue/break was called before
318                                                    /*if(fcf!=null) {
319                                                            Label l = fcf.getAfterFinalGOTOLabel();
320                                                            if(l!=null)adapter.visitJumpInsn(Opcodes.GOTO, l);
321                                                    }*/
322                                                    
323                                            }
324                                    };
325                                    
326                                    
327                                    if(tlt.handleException()) {
328                                            TryCatchFinallyVisitor tcfv=new TryCatchFinallyVisitor(onFinally,fcf);
329                                            tcfv.visitTryBegin(bc);
330                                                    doTry(bc,adapter,tag,currLocal,currType);
331                                            int t=tcfv.visitTryEndCatchBeging(bc);
332                                                    // tag.doCatch(t);
333                                                    adapter.loadLocal(currLocal);
334                                                    adapter.loadLocal(t);
335                                                    //adapter.visitVarInsn(Opcodes.ALOAD,t);
336                                                    adapter.invokeVirtual(currType, DO_CATCH);
337                                            tcfv.visitCatchEnd(bc);
338                                    }
339                                    else {
340                                            TryFinallyVisitor tfv=new TryFinallyVisitor(onFinally,fcf);
341                                            tfv.visitTryBegin(bc);
342                                                    doTry(bc,adapter,tag,currLocal,currType);
343                                            tfv.visitTryEnd(bc);
344                                    }
345                                    
346    
347                            adapter.visitLabel(endBody);
348                                    
349                    }
350                    else {
351                            //tag.doStartTag();
352                            adapter.loadLocal(currLocal);
353                            adapter.invokeVirtual(currType, DO_START_TAG);
354                            adapter.pop();
355                    }
356                    
357                    // if (tag.doEndTag()==Tag.SKIP_PAGE) throw new Abort(0<!-- SCOPE_PAGE -->);
358                    Label endDoEndTag=new Label();
359                    adapter.loadLocal(currLocal);
360                    adapter.invokeVirtual(currType, DO_END_TAG);
361                    adapter.push(javax.servlet.jsp.tagext.Tag.SKIP_PAGE);
362                    adapter.visitJumpInsn(Opcodes.IF_ICMPNE, endDoEndTag);
363                            adapter.push(Abort.SCOPE_PAGE);
364                            adapter.invokeStatic(ABORT, NEW_INSTANCE);
365                            adapter.throwException();
366                    adapter.visitLabel(endDoEndTag);
367                    
368                    
369                    if(doReuse) {
370                            // } finally{pc.reuse(tag);}
371                            outerTcfv.visitTryEnd(bc);
372                    }
373                    
374    
375                    adapter.visitLabel(tagEnd);
376                    ExpressionUtil.visitLine(bc, tag.getEnd());
377            }
378    
379            private static void doTry(BytecodeContext bc, GeneratorAdapter adapter, Tag tag, int currLocal, Type currType) throws BytecodeException {
380                    Label beginDoWhile=new Label();
381                    adapter.visitLabel(beginDoWhile);
382                            bc.setCurrentTag(currLocal);
383                            tag.getBody().writeOut(bc);
384                            
385                    // while (tag.doAfterBody()==BodyTag.EVAL_BODY_AGAIN);
386                            adapter.loadLocal(currLocal);
387                            adapter.invokeVirtual(currType, DO_AFTER_BODY);
388                            adapter.push(IterationTag.EVAL_BODY_AGAIN);
389                    adapter.visitJumpInsn(Opcodes.IF_ICMPEQ, beginDoWhile);
390            }
391    
392            private static Type getTagType(Tag tag) throws BytecodeException {
393                    TagLibTag tlt = tag.getTagLibTag();
394                    try {
395                            return tlt.getTagType();
396                    } catch (ClassException e) {
397                            throw new BytecodeException(e,tag.getStart());
398                    }
399            }
400    }