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 }