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