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 }