001 package railo.transformer.bytecode.expression.var; 002 003 import java.util.ArrayList; 004 import java.util.Iterator; 005 import java.util.List; 006 007 import org.objectweb.asm.Opcodes; 008 import org.objectweb.asm.Type; 009 import org.objectweb.asm.commons.GeneratorAdapter; 010 import org.objectweb.asm.commons.Method; 011 012 import railo.commons.lang.StringUtil; 013 import railo.runtime.exp.TemplateException; 014 import railo.runtime.type.Scope; 015 import railo.runtime.type.scope.ScopeSupport; 016 import railo.runtime.type.util.ArrayUtil; 017 import railo.runtime.type.util.KeyConstants; 018 import railo.runtime.util.VariableUtilImpl; 019 import railo.transformer.bytecode.BytecodeContext; 020 import railo.transformer.bytecode.BytecodeException; 021 import railo.transformer.bytecode.cast.Cast; 022 import railo.transformer.bytecode.expression.ExprString; 023 import railo.transformer.bytecode.expression.Expression; 024 import railo.transformer.bytecode.expression.ExpressionBase; 025 import railo.transformer.bytecode.expression.Invoker; 026 import railo.transformer.bytecode.literal.LitBoolean; 027 import railo.transformer.bytecode.literal.LitDouble; 028 import railo.transformer.bytecode.literal.LitString; 029 import railo.transformer.bytecode.util.ASMConstants; 030 import railo.transformer.bytecode.util.ASMUtil; 031 import railo.transformer.bytecode.util.ExpressionUtil; 032 import railo.transformer.bytecode.util.TypeScope; 033 import railo.transformer.bytecode.util.Types; 034 import railo.transformer.cfml.expression.CFMLExprTransformer; 035 import railo.transformer.library.function.FunctionLibFunction; 036 import railo.transformer.library.function.FunctionLibFunctionArg; 037 038 public class Variable extends ExpressionBase implements Invoker { 039 040 041 private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class); 042 043 // java.lang.Object get(java.lang.String) 044 final static Method METHOD_SCOPE_GET_KEY = new Method("get", 045 Types.OBJECT, 046 new Type[]{Types.COLLECTION_KEY}); 047 // Object getCollection(java.lang.String) 048 final static Method METHOD_SCOPE_GET_COLLECTION_KEY= new Method("getCollection", 049 Types.OBJECT, 050 new Type[]{Types.COLLECTION_KEY}); 051 052 // java.lang.Object get(java.lang.String) 053 final static Method METHOD_SCOPE_GET = new Method("get", 054 Types.OBJECT, 055 new Type[]{Types.STRING}); 056 // Object getCollection(java.lang.String) 057 final static Method METHOD_SCOPE_GET_COLLECTION= new Method("getCollection", 058 Types.OBJECT, 059 new Type[]{Types.STRING}); 060 061 final static Method[] METHODS_SCOPE_GET = new Method[6]; 062 static { 063 METHODS_SCOPE_GET[0] = METHOD_SCOPE_GET; 064 METHODS_SCOPE_GET[1] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING}); 065 METHODS_SCOPE_GET[2] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING}); 066 METHODS_SCOPE_GET[3] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING,Types.STRING}); 067 METHODS_SCOPE_GET[4] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING,Types.STRING,Types.STRING}); 068 METHODS_SCOPE_GET[5] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING,Types.STRING,Types.STRING,Types.STRING}); 069 } 070 071 // Object getCollection (Object,String) 072 private final static Method GET_COLLECTION = new Method("getCollection", 073 Types.OBJECT, 074 new Type[]{Types.OBJECT,Types.STRING}); 075 // Object get (Object,String) 076 private final static Method GET = new Method("get", 077 Types.OBJECT, 078 new Type[]{Types.OBJECT,Types.STRING}); 079 080 081 // Object getCollection (Object,String) 082 private final static Method GET_COLLECTION_KEY = new Method("getCollection", 083 Types.OBJECT, 084 new Type[]{Types.OBJECT,Types.COLLECTION_KEY}); 085 // Object get (Object,String) 086 private final static Method GET_KEY = new Method("get", 087 Types.OBJECT, 088 new Type[]{Types.OBJECT,Types.COLLECTION_KEY}); 089 090 091 092 // Object getFunction (Object,String,Object[]) 093 private final static Method GET_FUNCTION = new Method("getFunction", 094 Types.OBJECT, 095 new Type[]{Types.OBJECT,Types.STRING,Types.OBJECT_ARRAY}); 096 // Object getFunctionWithNamedValues (Object,String,Object[]) 097 private final static Method GET_FUNCTION_WITH_NAMED_ARGS = new Method("getFunctionWithNamedValues", 098 Types.OBJECT, 099 new Type[]{Types.OBJECT,Types.STRING,Types.OBJECT_ARRAY}); 100 101 102 // Object getFunction (Object,String,Object[]) 103 private final static Method GET_FUNCTION_KEY = new Method("getFunction", 104 Types.OBJECT, 105 new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); 106 // Object getFunctionWithNamedValues (Object,String,Object[]) 107 private final static Method GET_FUNCTION_WITH_NAMED_ARGS_KEY = new Method("getFunctionWithNamedValues", 108 Types.OBJECT, 109 new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); 110 111 private static final Type VARIABLE_UTIL_IMPL = Type.getType(VariableUtilImpl.class); 112 113 private static final Method RECORDCOUNT = new Method("recordcount", 114 Types.OBJECT, 115 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 116 private static final Method CURRENTROW = new Method("currentrow", 117 Types.OBJECT, 118 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 119 private static final Method COLUMNLIST = new Method("columnlist", 120 Types.OBJECT, 121 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 122 123 124 int scope=Scope.SCOPE_UNDEFINED; 125 List members=new ArrayList(); 126 int countDM=0; 127 int countFM=0; 128 private boolean ignoredFirstMember; 129 130 public Variable(int line) { 131 super(line); 132 } 133 134 public Variable(int scope,int line) { 135 super(line); 136 this.scope=scope; 137 } 138 139 /** 140 * @return the scope 141 */ 142 public int getScope() { 143 return scope; 144 } 145 146 /** 147 * @param scope the scope to set 148 */ 149 public void setScope(int scope) { 150 this.scope = scope; 151 } 152 153 public void addMember(Member member) { 154 if(member instanceof DataMember)countDM++; 155 else countFM++; 156 members.add(member); 157 } 158 159 public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException { 160 161 162 GeneratorAdapter adapter = bc.getAdapter(); 163 int count=countFM+countDM; 164 165 // count 0 166 if(count==0) return _writeOutEmpty(bc); 167 168 boolean doOnlyScope=scope==Scope.SCOPE_LOCAL; 169 170 Type rtn=Types.OBJECT; 171 //boolean last; 172 for(int i=doOnlyScope?0:1;i<count;i++) { 173 adapter.loadArg(0); 174 } 175 176 rtn=_writeOutFirst(bc, ((Member)members.get(0)),mode,count==1,doOnlyScope); 177 178 // pc.get( 179 for(int i=doOnlyScope?0:1;i<count;i++) { 180 Member member=((Member)members.get(i)); 181 boolean last=(i+1)==count; 182 183 // Data Member 184 if(member instanceof DataMember) { 185 ExprString name = ((DataMember)member).getName(); 186 187 if(last && ASMUtil.isDotKey(name)){ 188 LitString ls = (LitString)name; 189 if(ls.getString().equalsIgnoreCase("RECORDCOUNT")){ 190 adapter.invokeStatic(VARIABLE_UTIL_IMPL, RECORDCOUNT); 191 } 192 else if(ls.getString().equalsIgnoreCase("CURRENTROW")){ 193 adapter.invokeStatic(VARIABLE_UTIL_IMPL, CURRENTROW); 194 } 195 else if(ls.getString().equalsIgnoreCase("COLUMNLIST")){ 196 adapter.invokeStatic(VARIABLE_UTIL_IMPL, COLUMNLIST); 197 } 198 else { 199 if(registerKey(bc,name))adapter.invokeVirtual(Types.PAGE_CONTEXT,last?GET_KEY:GET_COLLECTION_KEY); 200 else adapter.invokeVirtual(Types.PAGE_CONTEXT,last?GET:GET_COLLECTION); 201 } 202 } 203 else{ 204 if(registerKey(bc,name))adapter.invokeVirtual(Types.PAGE_CONTEXT,last?GET_KEY:GET_COLLECTION_KEY); 205 else adapter.invokeVirtual(Types.PAGE_CONTEXT,last?GET:GET_COLLECTION); 206 } 207 rtn=Types.OBJECT; 208 } 209 210 // UDF 211 else if(member instanceof UDF) { 212 UDF udf=(UDF) member; 213 boolean isKey=registerKey(bc,udf.getName()); 214 //udf.getName().writeOut(bc, MODE_REF); 215 ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, udf.getArguments()); 216 217 if(isKey) adapter.invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS_KEY:GET_FUNCTION_KEY); 218 else adapter.invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS:GET_FUNCTION); 219 rtn=Types.OBJECT; 220 } 221 } 222 return rtn; 223 } 224 225 public static boolean registerKey(BytecodeContext bc,Expression name) throws BytecodeException { 226 return registerKey(bc, name, false); 227 } 228 229 public static boolean registerKey(BytecodeContext bc,Expression name,boolean doUpperCase) throws BytecodeException { 230 231 if(name instanceof LitString) { 232 LitString lit = (LitString)name; 233 if(doUpperCase){ 234 lit=lit.duplicate(); 235 lit.upperCase(); 236 } 237 String key=KeyConstants.getFieldName(lit.getString()); 238 if(key!=null){ 239 bc.getAdapter().getStatic(KEY_CONSTANTS, key, Types.COLLECTION_KEY); 240 return true; 241 } 242 int index=bc.registerKey(lit); 243 bc.getAdapter().visitFieldInsn(Opcodes.GETSTATIC, 244 bc.getClassName(), "keys", Types.COLLECTION_KEY_ARRAY.toString()); 245 bc.getAdapter().push(index); 246 bc.getAdapter().visitInsn(Opcodes.AALOAD); 247 248 //bc.getAdapter().visitFieldInsn(Opcodes.GETSTATIC, bc.getClassName(), key, "Lrailo/runtime/type/Collection$Key;"); 249 return true; 250 } 251 name.writeOut(bc, MODE_REF); 252 return false; 253 } 254 255 public static boolean canRegisterKey(Expression name) throws BytecodeException { 256 return name instanceof LitString; 257 } 258 259 260 /** 261 * outputs a empty Variable, only scope 262 * Example: pc.formScope(); 263 * @param adapter 264 * @throws TemplateException 265 */ 266 private Type _writeOutEmpty(BytecodeContext bc) throws BytecodeException { 267 if(ignoredFirstMember && (scope==Scope.SCOPE_LOCAL || scope==ScopeSupport.SCOPE_VAR)) 268 return Types.VOID; 269 270 271 GeneratorAdapter adapter = bc.getAdapter(); 272 adapter.loadArg(0); 273 Method m; 274 Type t=Types.PAGE_CONTEXT; 275 if(scope==Scope.SCOPE_ARGUMENTS) { 276 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 277 m = TypeScope.METHOD_ARGUMENT_BIND; 278 } 279 else if(scope==Scope.SCOPE_LOCAL) { 280 adapter.checkCast(Types.PAGE_CONTEXT_IMPL);// FUTURE remove when function localScope(boolean) is part of class PageContext 281 t=Types.PAGE_CONTEXT_IMPL; 282 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 283 m = TypeScope.METHOD_LOCAL_BIND; 284 } 285 else if(scope==ScopeSupport.SCOPE_VAR) { 286 adapter.checkCast(Types.PAGE_CONTEXT_IMPL);// FUTURE remove when function localScope(boolean) is part of class PageContext 287 t=Types.PAGE_CONTEXT_IMPL; 288 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 289 m = TypeScope.METHOD_VAR_BIND; 290 } 291 else m = TypeScope.METHODS[scope]; 292 293 TypeScope.invokeScope(adapter,m,t); 294 295 296 return m.getReturnType(); 297 } 298 299 300 301 private Type _writeOutFirst(BytecodeContext bc, Member member, int mode, boolean last, boolean doOnlyScope) throws BytecodeException { 302 if(member instanceof DataMember) 303 return _writeOutFirstDataMember(bc,(DataMember)member, scope,last , doOnlyScope); 304 else if(member instanceof UDF) 305 return _writeOutFirstUDF(bc,(UDF)member,scope,doOnlyScope); 306 else 307 return _writeOutFirstBIF(bc,(BIF)member,mode,last,getLine()); 308 } 309 310 static Type _writeOutFirstBIF(BytecodeContext bc, BIF bif, int mode,boolean last,int line) throws BytecodeException { 311 GeneratorAdapter adapter = bc.getAdapter(); 312 adapter.loadArg(0); 313 // class 314 Type bifClass = Types.toType(bif.getClassName()); 315 316 // arguments 317 Argument[] args = bif.getArguments(); 318 Type[] argTypes; 319 // Arg Type FIX 320 if(bif.getArgType()==FunctionLibFunction.ARG_FIX) { 321 322 if(isNamed(bif.getName(),args)) { 323 NamedArgument[] nargs=toNamedArguments(args); 324 325 String[] names=new String[nargs.length]; 326 // get all names 327 for(int i=0;i<nargs.length;i++){ 328 names[i] = getName(nargs[i].getName()); 329 } 330 331 332 ArrayList<FunctionLibFunctionArg> list = bif.getFlf().getArg(); 333 Iterator<FunctionLibFunctionArg> it = list.iterator(); 334 335 argTypes=new Type[list.size()+1]; 336 argTypes[0]=Types.PAGE_CONTEXT; 337 338 FunctionLibFunctionArg flfa; 339 int index=0; 340 VT vt; 341 while(it.hasNext()) { 342 flfa =it.next(); 343 vt = getMatchingValueAndType(flfa,nargs,names,line); 344 if(vt.index!=-1) 345 names[vt.index]=null; 346 argTypes[++index]=Types.toType(vt.type); 347 if(vt.value==null)ASMConstants.NULL(bc.getAdapter()); 348 else vt.value.writeOut(bc, Types.isPrimitiveType(argTypes[index])?MODE_VALUE:MODE_REF); 349 } 350 351 for(int y=0;y<names.length;y++){ 352 if(names[y]!=null) { 353 BytecodeException bce = new BytecodeException("argument ["+names[y]+"] is not allowed for function ["+bif.getFlf().getName()+"]", args[y].getLine()); 354 CFMLExprTransformer.addFunctionDoc(bce, bif.getFlf()); 355 throw bce; 356 } 357 } 358 359 } 360 else{ 361 argTypes=new Type[args.length+1]; 362 argTypes[0]=Types.PAGE_CONTEXT; 363 364 365 for(int y=0;y<args.length;y++) { 366 argTypes[y+1]=Types.toType(args[y].getStringType()); 367 args[y].writeOutValue(bc, Types.isPrimitiveType(argTypes[y+1])?MODE_VALUE:MODE_REF); 368 } 369 } 370 371 } 372 // Arg Type DYN 373 else { 374 375 argTypes=new Type[2]; 376 argTypes[0]=Types.PAGE_CONTEXT; 377 argTypes[1]=Types.OBJECT_ARRAY; 378 ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, args); 379 } 380 381 // return type 382 Type rtnType=Types.toType(bif.getReturnType()); 383 if(rtnType==Types.VOID)rtnType=Types.STRING; 384 adapter. invokeStatic(bifClass,new Method("call",rtnType,argTypes)); 385 386 387 if(mode==MODE_REF || !last) { 388 if(Types.isPrimitiveType(rtnType)) { 389 adapter.invokeStatic(Types.CASTER,new Method("toRef",Types.toRefType(rtnType),new Type[]{rtnType})); 390 rtnType=Types.toRefType(rtnType); 391 } 392 } 393 return rtnType; 394 } 395 396 397 398 399 400 401 402 static Type _writeOutFirstUDF(BytecodeContext bc, UDF udf, int scope, boolean doOnlyScope) throws BytecodeException { 403 404 GeneratorAdapter adapter = bc.getAdapter(); 405 // pc.getFunction (Object,String,Object[]) 406 // pc.getFunctionWithNamedValues (Object,String,Object[]) 407 adapter.loadArg(0); 408 if(!doOnlyScope)adapter.loadArg(0); 409 Type rtn = TypeScope.invokeScope(adapter, scope); 410 if(doOnlyScope) return rtn; 411 412 boolean isKey=registerKey(bc,udf.getName()); 413 ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, udf.getArguments()); 414 if(isKey) adapter.invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS_KEY:GET_FUNCTION_KEY); 415 else adapter.invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS:GET_FUNCTION); 416 return Types.OBJECT; 417 418 } 419 420 static Type _writeOutFirstDataMember(BytecodeContext bc, DataMember member, int scope, boolean last, boolean doOnlyScope) throws BytecodeException { 421 422 423 GeneratorAdapter adapter = bc.getAdapter(); 424 adapter.loadArg(0); 425 Type rtn = TypeScope.invokeScope(adapter, scope); 426 if(doOnlyScope) return rtn; 427 428 if(registerKey(bc,member.getName())) 429 adapter.invokeInterface(TypeScope.SCOPES[scope],!last && scope==Scope.SCOPE_UNDEFINED?METHOD_SCOPE_GET_COLLECTION_KEY:METHOD_SCOPE_GET_KEY); 430 else 431 adapter.invokeInterface(TypeScope.SCOPES[scope],!last && scope==Scope.SCOPE_UNDEFINED?METHOD_SCOPE_GET_COLLECTION:METHOD_SCOPE_GET); 432 return Types.OBJECT; 433 } 434 435 436 437 /** 438 * @return the members 439 */ 440 public List getMembers() { 441 return members; 442 } 443 444 /** 445 * @return the first member or null if there no member 446 */ 447 public Member getFirstMember() { 448 if(members.isEmpty()) return null; 449 return (Member) members.get(0); 450 } 451 452 /** 453 * @return the first member or null if there no member 454 */ 455 public Member getLastMember() { 456 if(members.isEmpty()) return null; 457 return (Member) members.get(members.size()-1); 458 } 459 460 public void ignoredFirstMember(boolean b) { 461 this.ignoredFirstMember=b; 462 } 463 public boolean ignoredFirstMember() { 464 return ignoredFirstMember; 465 } 466 467 468 469 470 private static VT getMatchingValueAndType(FunctionLibFunctionArg flfa, NamedArgument[] nargs,String[] names, int line) throws BytecodeException { 471 String flfan=flfa.getName(); 472 473 // first search if a argument match 474 for(int i=0;i<nargs.length;i++){ 475 if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) { 476 nargs[i].setValue(nargs[i].getRawValue(),flfa.getType()); 477 return new VT(nargs[i].getValue(),flfa.getType(),i); 478 } 479 } 480 481 // then check if a alias match 482 String alias=flfa.getAlias(); 483 if(!StringUtil.isEmpty(alias)) { 484 //String[] arrAlias = railo.runtime.type.List.toStringArray(railo.runtime.type.List.trimItems(railo.runtime.type.List.listToArrayRemoveEmpty(alias, ','))); 485 for(int i=0;i<nargs.length;i++){ 486 if(names[i]!=null && railo.runtime.type.List.listFindNoCase(alias, names[i])!=-1){ 487 nargs[i].setValue(nargs[i].getRawValue(),flfa.getType()); 488 return new VT(nargs[i].getValue(),flfa.getType(),i); 489 } 490 } 491 } 492 493 // if not required return the default value 494 if(!flfa.getRequired()) { 495 String defaultValue = flfa.getDefaultValue(); 496 String type=flfa.getTypeAsString().toLowerCase(); 497 498 if(defaultValue==null) { 499 if(type.equals("boolean") || type.equals("bool")) 500 return new VT(LitBoolean.FALSE,type,-1); 501 if(type.equals("number") || type.equals("numeric") || type.equals("double")) 502 return new VT(LitDouble.ZERO,type,-1); 503 return new VT(null,type,-1); 504 } 505 else { 506 return new VT(Cast.toExpression(LitString.toExprString(defaultValue), type),type,-1); 507 } 508 509 } 510 BytecodeException be = new BytecodeException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]",line); 511 CFMLExprTransformer.addFunctionDoc(be, flfa.getFunction()); 512 throw be; 513 } 514 515 516 517 518 private static String getName(Expression expr) throws BytecodeException { 519 String name = ASMUtil.toString(expr); 520 if(name==null) throw new BytecodeException("cannot extract a string from a object of type ["+expr.getClass().getName()+"]",-1); 521 return name; 522 } 523 524 /** 525 * translate a array of arguments to a araay of NamedArguments, attention no check if the elements are really named arguments 526 * @param args 527 * @return 528 */ 529 private static NamedArgument[] toNamedArguments(Argument[] args) { 530 NamedArgument[] nargs=new NamedArgument[args.length]; 531 for(int i=0;i<args.length;i++){ 532 nargs[i]=(NamedArgument) args[i]; 533 } 534 535 return nargs; 536 } 537 538 539 540 /** 541 * check if the arguments are named arguments or regular arguments, throws a exception when mixed 542 * @param funcName 543 * @param args 544 * @param line 545 * @return 546 * @throws BytecodeException 547 */ 548 private static boolean isNamed(String funcName,Argument[] args) throws BytecodeException { 549 if(ArrayUtil.isEmpty(args)) return false; 550 boolean named=false; 551 for(int i=0;i<args.length;i++){ 552 if(args[i] instanceof NamedArgument)named=true; 553 else if(named) 554 throw new BytecodeException("invalid argument for function "+funcName+", you can not mix named and unnamed arguments", args[i].getLine()); 555 } 556 557 558 return named; 559 } 560 561 } 562 563 class VT{ 564 Expression value; 565 String type; 566 int index; 567 568 public VT(Expression value, String type, int index) { 569 this.value=value; 570 this.type=type; 571 this.index=index; 572 } 573 }