001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.transformer.bytecode.expression.var; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023import java.util.List; 024 025import lucee.commons.lang.StringUtil; 026import lucee.commons.lang.types.RefInteger; 027import lucee.commons.lang.types.RefIntegerImpl; 028import lucee.runtime.exp.TemplateException; 029import lucee.runtime.op.Constants; 030import lucee.runtime.type.scope.Scope; 031import lucee.runtime.type.scope.ScopeSupport; 032import lucee.runtime.type.util.ArrayUtil; 033import lucee.runtime.type.util.KeyConstants; 034import lucee.runtime.type.util.UDFUtil; 035import lucee.runtime.util.CallerUtil; 036import lucee.runtime.util.VariableUtilImpl; 037import lucee.transformer.bytecode.BytecodeContext; 038import lucee.transformer.bytecode.BytecodeException; 039import lucee.transformer.bytecode.Literal; 040import lucee.transformer.bytecode.Page; 041import lucee.transformer.bytecode.Position; 042import lucee.transformer.bytecode.cast.CastOther; 043import lucee.transformer.bytecode.expression.ExprString; 044import lucee.transformer.bytecode.expression.Expression; 045import lucee.transformer.bytecode.expression.ExpressionBase; 046import lucee.transformer.bytecode.expression.Invoker; 047import lucee.transformer.bytecode.literal.LitBoolean; 048import lucee.transformer.bytecode.literal.LitDouble; 049import lucee.transformer.bytecode.literal.LitString; 050import lucee.transformer.bytecode.util.ASMConstants; 051import lucee.transformer.bytecode.util.ASMUtil; 052import lucee.transformer.bytecode.util.ExpressionUtil; 053import lucee.transformer.bytecode.util.TypeScope; 054import lucee.transformer.bytecode.util.Types; 055import lucee.transformer.bytecode.visitor.ArrayVisitor; 056import lucee.transformer.library.function.FunctionLibFunction; 057import lucee.transformer.library.function.FunctionLibFunctionArg; 058 059import org.objectweb.asm.Opcodes; 060import org.objectweb.asm.Type; 061import org.objectweb.asm.commons.GeneratorAdapter; 062import org.objectweb.asm.commons.Method; 063 064public class Variable extends ExpressionBase implements Invoker { 065 066 067 private static final Type KEY_CONSTANTS = Type.getType(KeyConstants.class); 068 private static final Type CALLER_UTIL = Type.getType(CallerUtil.class); 069 070 // java.lang.Object get(java.lang.String) 071 final static Method METHOD_SCOPE_GET_KEY = new Method("get", 072 Types.OBJECT, 073 new Type[]{Types.COLLECTION_KEY}); 074 // Object getCollection(java.lang.String) 075 final static Method METHOD_SCOPE_GET_COLLECTION_KEY= new Method("getCollection", 076 Types.OBJECT, 077 new Type[]{Types.COLLECTION_KEY}); 078 079 // java.lang.Object get(java.lang.String) 080 final static Method METHOD_SCOPE_GET = new Method("get", 081 Types.OBJECT, 082 new Type[]{Types.STRING}); 083 // Object getCollection(java.lang.String) 084 /*final static Method METHOD_SCOPE_GET_COLLECTION= new Method("getCollection", 085 Types.OBJECT, 086 new Type[]{Types.STRING});*/ 087 088 final static Method INIT= new Method("init", 089 Types.COLLECTION_KEY, 090 new Type[]{Types.STRING}); 091 final static Method TO_KEY= new Method("toKey", 092 Types.COLLECTION_KEY, 093 new Type[]{Types.OBJECT}); 094 095 final static Method[] METHODS_SCOPE_GET = new Method[6]; 096 static { 097 METHODS_SCOPE_GET[0] = METHOD_SCOPE_GET; 098 METHODS_SCOPE_GET[1] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING}); 099 METHODS_SCOPE_GET[2] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING}); 100 METHODS_SCOPE_GET[3] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING,Types.STRING}); 101 METHODS_SCOPE_GET[4] = new Method("get",Types.OBJECT,new Type[]{Types.SCOPE,Types.STRING,Types.STRING,Types.STRING,Types.STRING,Types.STRING}); 102 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}); 103 } 104 105 // Object getCollection (Object,String) 106 /*private final static Method GET_COLLECTION = new Method("getCollection", 107 Types.OBJECT, 108 new Type[]{Types.OBJECT,Types.STRING});*/ 109 // Object get (Object,String) 110 /*private final static Method GET = new Method("get", 111 Types.OBJECT, 112 new Type[]{Types.OBJECT,Types.STRING});*/ 113 114 //public Object get(PageContext pc,Object coll, Key[] keys, Object defaultValue) { 115 private final static Method CALLER_UTIL_GET = new Method("get", 116 Types.OBJECT, 117 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT,Types.COLLECTION_KEY_ARRAY,Types.OBJECT}); 118 119 120 // Object getCollection (Object,String) 121 private final static Method GET_COLLECTION_KEY = new Method("getCollection", 122 Types.OBJECT, 123 new Type[]{Types.OBJECT,Types.COLLECTION_KEY}); 124 // Object get (Object,String) 125 private final static Method GET_KEY = new Method("get", 126 Types.OBJECT, 127 new Type[]{Types.OBJECT,Types.COLLECTION_KEY}); 128 129 130 131 private final static Method GET_FUNCTION_KEY = new Method("getFunction", 132 Types.OBJECT, 133 new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); 134 135 136 // Object getFunctionWithNamedValues (Object,String,Object[]) 137 private final static Method GET_FUNCTION_WITH_NAMED_ARGS_KEY = new Method("getFunctionWithNamedValues", 138 Types.OBJECT, 139 new Type[]{Types.OBJECT,Types.COLLECTION_KEY,Types.OBJECT_ARRAY}); 140 141 private static final Type VARIABLE_UTIL_IMPL = Type.getType(VariableUtilImpl.class); 142 143 private static final Method RECORDCOUNT = new Method("recordcount", 144 Types.OBJECT, 145 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 146 private static final Method CURRENTROW = new Method("currentrow", 147 Types.OBJECT, 148 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 149 private static final Method COLUMNLIST = new Method("columnlist", 150 Types.OBJECT, 151 new Type[]{Types.PAGE_CONTEXT,Types.OBJECT}); 152 153 private static final Method THIS_GET = new Method("thisGet", 154 Types.OBJECT, 155 new Type[]{}); 156 private static final Method THIS_TOUCH = new Method("thisTouch", 157 Types.OBJECT, 158 new Type[]{}); 159 160 private static final Method THIS_GET_EL = new Method("thisGet", 161 Types.OBJECT, 162 new Type[]{Types.OBJECT}); 163 private static final Method THIS_TOUCH_EL = new Method("thisTouch", 164 Types.OBJECT, 165 new Type[]{Types.OBJECT}); 166 167 private static final Type CONSTANTS = Type.getType(Constants.class); 168 169 170 int scope=Scope.SCOPE_UNDEFINED; 171 List<Member> members=new ArrayList<Member>(); 172 int countDM=0; 173 int countFM=0; 174 private boolean ignoredFirstMember; 175 176 private boolean fromHash=false; 177 private Expression defaultValue; 178 private Boolean asCollection; 179 180 public Variable(Position start,Position end) { 181 super(start,end); 182 } 183 184 public Variable(int scope,Position start,Position end) { 185 super(start,end); 186 this.scope=scope; 187 } 188 189 190 191 public Expression getDefaultValue() { 192 return defaultValue; 193 } 194 195 public void setDefaultValue(Expression defaultValue) { 196 this.defaultValue = defaultValue; 197 } 198 199 public Boolean getAsCollection() { 200 return asCollection; 201 } 202 203 public void setAsCollection(Boolean asCollection) { 204 this.asCollection = asCollection; 205 } 206 207 /** 208 * @return the scope 209 */ 210 public int getScope() { 211 return scope; 212 } 213 214 /** 215 * @param scope the scope to set 216 */ 217 public void setScope(int scope) { 218 this.scope = scope; 219 } 220 221 public void addMember(Member member) { 222 if(member instanceof DataMember)countDM++; 223 else countFM++; 224 members.add(member); 225 } 226 227 public Member removeMember(int index) { 228 Member rtn = members.remove(index); 229 if(rtn instanceof DataMember)countDM--; 230 else countFM--; 231 return rtn; 232 } 233 234 public final Type writeOutCollection(BytecodeContext bc, int mode) throws BytecodeException { 235 ExpressionUtil.visitLine(bc, getStart()); 236 Type type = _writeOut(bc,mode, Boolean.TRUE); 237 ExpressionUtil.visitLine(bc, getEnd()); 238 return type; 239 } 240 241 public Type _writeOut(BytecodeContext bc, int mode) throws BytecodeException { 242 if(defaultValue!=null && countFM==0 && countDM!=0) 243 return _writeOutCallerUtil(bc, mode); 244 return _writeOut(bc, mode, asCollection); 245 } 246 private Type _writeOut(BytecodeContext bc, int mode,Boolean asCollection) throws BytecodeException { 247 248 249 GeneratorAdapter adapter = bc.getAdapter(); 250 final int count=countFM+countDM; 251 252 // count 0 253 if(count==0) return _writeOutEmpty(bc); 254 255 boolean doOnlyScope=scope==Scope.SCOPE_LOCAL; 256 257 258 //boolean last; 259 for(int i=doOnlyScope?0:1;i<count;i++) { 260 adapter.loadArg(0); 261 } 262 263 Type rtn=_writeOutFirst(bc, (members.get(0)),mode,count==1,doOnlyScope,null,null); 264 265 // pc.get( 266 for(int i=doOnlyScope?0:1;i<count;i++) { 267 Member member=(members.get(i)); 268 boolean last=(i+1)==count; 269 270 // Data Member 271 if(member instanceof DataMember) { 272 ExprString name = ((DataMember)member).getName(); 273 if(last && ASMUtil.isDotKey(name)){ 274 LitString ls = (LitString)name; 275 if(ls.getString().equalsIgnoreCase("RECORDCOUNT")){ 276 adapter.invokeStatic(VARIABLE_UTIL_IMPL, RECORDCOUNT); 277 } 278 else if(ls.getString().equalsIgnoreCase("CURRENTROW")){ 279 adapter.invokeStatic(VARIABLE_UTIL_IMPL, CURRENTROW); 280 } 281 else if(ls.getString().equalsIgnoreCase("COLUMNLIST")){ 282 adapter.invokeStatic(VARIABLE_UTIL_IMPL, COLUMNLIST); 283 } 284 else { 285 registerKey(bc,name); 286 adapter.invokeVirtual(Types.PAGE_CONTEXT,asCollection(asCollection, last)?GET_COLLECTION_KEY:GET_KEY); 287 } 288 } 289 else{ 290 registerKey(bc,name); 291 adapter.invokeVirtual(Types.PAGE_CONTEXT,asCollection(asCollection, last)?GET_COLLECTION_KEY:GET_KEY); 292 } 293 rtn=Types.OBJECT; 294 } 295 296 // UDF 297 else if(member instanceof UDF) { 298 rtn= _writeOutUDF(bc,(UDF) member); 299 } 300 } 301 return rtn; 302 } 303 304 private Type _writeOutCallerUtil(BytecodeContext bc, int mode) throws BytecodeException { 305 306 307 GeneratorAdapter adapter = bc.getAdapter(); 308 final int count=countFM+countDM; 309 310 // count 0 311 if(count==0) return _writeOutEmpty(bc); 312 313 314 //boolean last; 315 /*for(int i=doOnlyScope?0:1;i<count;i++) { 316 adapter.loadArg(0); 317 }*/ 318 319 // pc 320 //adapter.loadArg(0); 321 adapter.loadArg(0); 322 323 // collection 324 RefInteger startIndex=new RefIntegerImpl(); 325 _writeOutFirst(bc, (members.get(0)),mode,count==1,true,defaultValue,startIndex); 326 327 // keys 328 Iterator<Member> it = members.iterator(); 329 ArrayVisitor av=new ArrayVisitor(); 330 av.visitBegin(adapter,Types.COLLECTION_KEY,countDM-startIndex.toInt()); 331 int index=0, i=0; 332 while(it.hasNext()) { 333 DataMember member=(DataMember) it.next(); 334 if(i++<startIndex.toInt()) continue; 335 av.visitBeginItem(adapter, index++); 336 registerKey(bc,member.getName()); 337 av.visitEndItem(bc.getAdapter()); 338 339 } 340 av.visitEnd(); 341 342 // defaultValue 343 defaultValue.writeOut(bc, MODE_REF); 344 345 bc.getAdapter().invokeStatic(CALLER_UTIL, CALLER_UTIL_GET); 346 347 return Types.OBJECT; 348 } 349 350 private boolean asCollection(Boolean asCollection, boolean last) { 351 if(!last) return true; 352 return asCollection!=null && asCollection.booleanValue(); 353 } 354 355 public static void registerKey(BytecodeContext bc,Expression name) throws BytecodeException { 356 registerKey(bc, name, false); 357 } 358 359 public static void registerKey(BytecodeContext bc,Expression name,boolean doUpperCase) throws BytecodeException { 360 361 if(name instanceof Literal) { 362 Literal l=(Literal) name; 363 364 LitString ls = name instanceof LitString?(LitString)l:LitString.toLitString(l.getString()); 365 if(doUpperCase){ 366 ls=ls.duplicate(); 367 ls.upperCase(); 368 } 369 String key=KeyConstants.getFieldName(ls.getString()); 370 if(key!=null){ 371 bc.getAdapter().getStatic(KEY_CONSTANTS, key, Types.COLLECTION_KEY); 372 return; 373 } 374 int index=bc.registerKey(ls); 375 bc.getAdapter().visitVarInsn(Opcodes.ALOAD, 0); 376 bc.getAdapter().visitFieldInsn(Opcodes.GETFIELD, bc.getClassName(), "keys", Types.COLLECTION_KEY_ARRAY.toString()); 377 bc.getAdapter().push(index); 378 bc.getAdapter().visitInsn(Opcodes.AALOAD); 379 380 return; 381 } 382 name.writeOut(bc, MODE_REF); 383 bc.getAdapter().invokeStatic(Page.KEY_IMPL, INIT); 384 //bc.getAdapter().invokeStatic(Types.CASTER, TO_KEY); 385 return; 386 } 387 388 public static boolean canRegisterKey(Expression name) { 389 return name instanceof LitString; 390 } 391 392 393 /** 394 * outputs a empty Variable, only scope 395 * Example: pc.formScope(); 396 * @param adapter 397 * @throws TemplateException 398 */ 399 private Type _writeOutEmpty(BytecodeContext bc) throws BytecodeException { 400 if(ignoredFirstMember && (scope==Scope.SCOPE_LOCAL || scope==ScopeSupport.SCOPE_VAR)) 401 return Types.VOID; 402 403 404 GeneratorAdapter adapter = bc.getAdapter(); 405 adapter.loadArg(0); 406 Method m; 407 Type t=Types.PAGE_CONTEXT; 408 if(scope==Scope.SCOPE_ARGUMENTS) { 409 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 410 m = TypeScope.METHOD_ARGUMENT_BIND; 411 } 412 else if(scope==Scope.SCOPE_LOCAL) { 413 t=Types.PAGE_CONTEXT; 414 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 415 m = TypeScope.METHOD_LOCAL_BIND; 416 } 417 else if(scope==ScopeSupport.SCOPE_VAR) { 418 t=Types.PAGE_CONTEXT; 419 LitBoolean.TRUE.writeOut(bc, MODE_VALUE); 420 m = TypeScope.METHOD_VAR_BIND; 421 } 422 else m = TypeScope.METHODS[scope]; 423 424 TypeScope.invokeScope(adapter,m,t); 425 426 427 return m.getReturnType(); 428 } 429 430 431 432 private Type _writeOutFirst(BytecodeContext bc, Member member, int mode, boolean last, boolean doOnlyScope, Expression defaultValue, RefInteger startIndex) throws BytecodeException { 433 if(member instanceof DataMember) 434 return _writeOutFirstDataMember(bc,(DataMember)member, scope,last , doOnlyScope,defaultValue,startIndex); 435 else if(member instanceof UDF) 436 return _writeOutFirstUDF(bc,(UDF)member,scope,doOnlyScope); 437 else { 438 BIF bif = ((BIF)member); 439 440 // patch: add name to queryExecute 441 /*if(bif.getName() instanceof LitString && ((LitString)bif.getName()).getString().equalsIgnoreCase("queryExecute")) { 442 443 Argument[] args = bif.getArguments(); 444 if(args!=null && args.length>=4){// should always be the case 445 446 // named arguments 447 if(args[args.length-1] instanceof NamedArgument){ 448 bif.addArgument( 449 new NamedArgument( 450 LitString.toExprString("queryName"), 451 LitString.toExprString("aaaa"), 452 "string", false)); 453 454 } 455 else { 456 457 // because the right position is importend we have to make sue we have params and options 458 if(args.length==4) bif.addArgument(new Argument(new EmptyStruct(),"struct")); // params 459 if(args.length<=5) bif.addArgument(new Argument(new EmptyStruct(),"struct")); // options 460 bif.addArgument(new Argument(LitString.toExprString("aaaa"),"string")); 461 } 462 } 463 //print.e("type:"+((NamedArgument)bif.getArguments()[0]).getStringType()); 464 465 466 }*/ 467 468 return _writeOutFirstBIF(bc,bif,mode,last,getStart()); 469 470 } 471 } 472 473 static Type _writeOutFirstBIF(BytecodeContext bc, BIF bif, int mode,boolean last,Position line) throws BytecodeException { 474 GeneratorAdapter adapter = bc.getAdapter(); 475 adapter.loadArg(0); 476 // class 477 Class bifClass = bif.getClazz(); 478 Type bifType = Type.getType(bifClass);//Types.toType(bif.getClassName()); 479 Type rtnType=Types.toType(bif.getReturnType()); 480 if(rtnType==Types.VOID)rtnType=Types.STRING; 481 482 // arguments 483 Argument[] args = bif.getArguments(); 484 Type[] argTypes; 485 // Arg Type FIX 486 if(bif.getArgType()==FunctionLibFunction.ARG_FIX) { 487 488 if(isNamed(bif.getName(),args)) { 489 NamedArgument[] nargs=toNamedArguments(args); 490 491 String[] names=new String[nargs.length]; 492 // get all names 493 for(int i=0;i<nargs.length;i++){ 494 names[i] = getName(nargs[i].getName()); 495 } 496 497 498 ArrayList<FunctionLibFunctionArg> list = bif.getFlf().getArg(); 499 Iterator<FunctionLibFunctionArg> it = list.iterator(); 500 501 argTypes=new Type[list.size()+1]; 502 argTypes[0]=Types.PAGE_CONTEXT; 503 504 FunctionLibFunctionArg flfa; 505 int index=0; 506 VT vt; 507 while(it.hasNext()) { 508 flfa =it.next(); 509 vt = getMatchingValueAndType(flfa,nargs,names,line); 510 if(vt.index!=-1) 511 names[vt.index]=null; 512 argTypes[++index]=Types.toType(vt.type); 513 if(vt.value==null)ASMConstants.NULL(bc.getAdapter()); 514 else vt.value.writeOut(bc, Types.isPrimitiveType(argTypes[index])?MODE_VALUE:MODE_REF); 515 } 516 517 for(int y=0;y<names.length;y++){ 518 if(names[y]!=null) { 519 BytecodeException bce = new BytecodeException("argument ["+names[y]+"] is not allowed for function ["+bif.getFlf().getName()+"]", args[y].getStart()); 520 UDFUtil.addFunctionDoc(bce, bif.getFlf()); 521 throw bce; 522 } 523 } 524 525 } 526 else{ 527 argTypes=new Type[args.length+1]; 528 argTypes[0]=Types.PAGE_CONTEXT; 529 530 531 for(int y=0;y<args.length;y++) { 532 argTypes[y+1]=Types.toType(args[y].getStringType()); 533 args[y].writeOutValue(bc, Types.isPrimitiveType(argTypes[y+1])?MODE_VALUE:MODE_REF); 534 } 535 // if no method exists for the exact match of arguments, call the method with all arguments (when exists) 536 if(methodExists(bifClass,"call",argTypes,rtnType)==Boolean.FALSE) { 537 ArrayList<FunctionLibFunctionArg> _args = bif.getFlf().getArg(); 538 539 Type[] tmp = new Type[_args.size()+1]; 540 541 // fill the existing 542 for(int i=0;i<argTypes.length;i++){ 543 tmp[i]=argTypes[i]; 544 } 545 546 // get the rest with default values 547 FunctionLibFunctionArg flfa; 548 for(int i=argTypes.length;i<tmp.length;i++){ 549 flfa = _args.get(i-1); 550 tmp[i]=Types.toType(flfa.getTypeAsString()); 551 getDefaultValue(flfa).value.writeOut( 552 bc, 553 Types.isPrimitiveType(tmp[i])?MODE_VALUE:MODE_REF); 554 } 555 argTypes=tmp; 556 } 557 558 } 559 560 } 561 // Arg Type DYN 562 else { 563 564 argTypes=new Type[2]; 565 argTypes[0]=Types.PAGE_CONTEXT; 566 argTypes[1]=Types.OBJECT_ARRAY; 567 ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, args); 568 } 569 adapter.invokeStatic(bifType,new Method("call",rtnType,argTypes)); 570 if(mode==MODE_REF || !last) { 571 if(Types.isPrimitiveType(rtnType)) { 572 adapter.invokeStatic(Types.CASTER,new Method("toRef",Types.toRefType(rtnType),new Type[]{rtnType})); 573 rtnType=Types.toRefType(rtnType); 574 } 575 } 576 return rtnType; 577 } 578 579 580 581 582 583 584 /** 585 * checks if a method exists 586 * @param clazz 587 * @param methodName 588 * @param args 589 * @param returnType 590 * @return returns null when checking fi 591 */ 592 593 private static Boolean methodExists(Class clazz, String methodName, Type[] args, Type returnType) { 594 try { 595 //Class _clazz=Types.toClass(clazz); 596 Class[] _args=new Class[args.length]; 597 for(int i=0;i<_args.length;i++){ 598 _args[i]=Types.toClass(args[i]); 599 } 600 Class rtn = Types.toClass(returnType); 601 602 try { 603 java.lang.reflect.Method m = clazz.getMethod(methodName, _args); 604 return m.getReturnType()==rtn; 605 } 606 catch (Exception e) { 607 return false; 608 } 609 610 } 611 catch (Exception e) {e.printStackTrace(); 612 return null; 613 } 614 } 615 616 static Type _writeOutFirstUDF(BytecodeContext bc, UDF udf, int scope, boolean doOnlyScope) throws BytecodeException { 617 618 GeneratorAdapter adapter = bc.getAdapter(); 619 // pc.getFunction (Object,String,Object[]) 620 // pc.getFunctionWithNamedValues (Object,String,Object[]) 621 adapter.loadArg(0); 622 623 if(!doOnlyScope)adapter.loadArg(0); 624 Type rtn = TypeScope.invokeScope(adapter, scope); 625 if(doOnlyScope) return rtn; 626 627 628 return _writeOutUDF(bc,udf); 629 } 630 631 private static Type _writeOutUDF(BytecodeContext bc, UDF udf) throws BytecodeException { 632 registerKey(bc,udf.getName()); 633 Argument[] args = udf.getArguments(); 634 635 // no arguments 636 if(args.length==0) { 637 bc.getAdapter().getStatic(CONSTANTS, "EMPTY_OBJECT_ARRAY", Types.OBJECT_ARRAY); 638 } 639 else ExpressionUtil.writeOutExpressionArray(bc, Types.OBJECT, args); 640 bc.getAdapter().invokeVirtual(Types.PAGE_CONTEXT,udf.hasNamedArgs()?GET_FUNCTION_WITH_NAMED_ARGS_KEY:GET_FUNCTION_KEY); 641 return Types.OBJECT; 642 } 643 644 Type _writeOutFirstDataMember(BytecodeContext bc, DataMember member, int scope, boolean last, boolean doOnlyScope, Expression defaultValue, RefInteger startIndex) throws BytecodeException { 645 GeneratorAdapter adapter = bc.getAdapter(); 646 if(startIndex!=null)startIndex.setValue(doOnlyScope?0:1); 647 648 // this 649 if(scope==Scope.SCOPE_UNDEFINED) { 650 ExprString name = member.getName(); 651 if(ASMUtil.isDotKey(name)){ 652 LitString ls = (LitString)name; 653 if(ls.getString().equalsIgnoreCase("THIS")){ 654 if(startIndex!=null)startIndex.setValue(1); 655 adapter.loadArg(0); 656 adapter.checkCast(Types.PAGE_CONTEXT_IMPL); 657 if(defaultValue!=null) { 658 defaultValue.writeOut(bc, MODE_REF); 659 adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL,(countFM+countDM)==1?THIS_GET_EL:THIS_TOUCH_EL); 660 } 661 else adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL,(countFM+countDM)==1?THIS_GET:THIS_TOUCH); 662 return Types.OBJECT; 663 } 664 } 665 } 666 // local 667 Type rtn; 668 if(scope==Scope.SCOPE_LOCAL && defaultValue!=null) { 669 adapter.loadArg(0); 670 adapter.checkCast(Types.PAGE_CONTEXT_IMPL); 671 LitBoolean.FALSE.writeOut(bc, MODE_VALUE); 672 defaultValue.writeOut(bc, MODE_VALUE); 673 adapter.invokeVirtual(Types.PAGE_CONTEXT_IMPL, TypeScope.METHOD_LOCAL_EL); 674 rtn= Types.OBJECT; 675 } 676 else { 677 adapter.loadArg(0); 678 rtn = TypeScope.invokeScope(adapter, scope); 679 } 680 if(doOnlyScope) return rtn; 681 registerKey(bc,member.getName()); 682 adapter.invokeInterface(TypeScope.SCOPES[scope],!last && scope==Scope.SCOPE_UNDEFINED?METHOD_SCOPE_GET_COLLECTION_KEY:METHOD_SCOPE_GET_KEY); 683 return Types.OBJECT; 684 } 685 686 687 688 /** 689 * @return the members 690 */ 691 public List<Member> getMembers() { 692 return members; 693 } 694 695 /** 696 * @return the first member or null if there no member 697 */ 698 public Member getFirstMember() { 699 if(members.isEmpty()) return null; 700 return members.get(0); 701 } 702 703 /** 704 * @return the first member or null if there no member 705 */ 706 public Member getLastMember() { 707 if(members.isEmpty()) return null; 708 return members.get(members.size()-1); 709 } 710 711 public void ignoredFirstMember(boolean b) { 712 this.ignoredFirstMember=b; 713 } 714 public boolean ignoredFirstMember() { 715 return ignoredFirstMember; 716 } 717 718 719 720 721 private static VT getMatchingValueAndType(FunctionLibFunctionArg flfa, NamedArgument[] nargs,String[] names, Position line) throws BytecodeException { 722 String flfan=flfa.getName(); 723 724 // first search if a argument match 725 for(int i=0;i<nargs.length;i++){ 726 if(names[i]!=null && names[i].equalsIgnoreCase(flfan)) { 727 nargs[i].setValue(nargs[i].getRawValue(),flfa.getTypeAsString()); 728 return new VT(nargs[i].getValue(),flfa.getTypeAsString(),i); 729 } 730 } 731 732 // then check if a alias match 733 String alias=flfa.getAlias(); 734 if(!StringUtil.isEmpty(alias)) { 735 //String[] arrAlias = lucee.runtime.type.List.toStringArray(lucee.runtime.type.List.trimItems(lucee.runtime.type.List.listToArrayRemoveEmpty(alias, ','))); 736 for(int i=0;i<nargs.length;i++){ 737 if(names[i]!=null && lucee.runtime.type.util.ListUtil.listFindNoCase(alias, names[i], ",")!=-1){ 738 nargs[i].setValue(nargs[i].getRawValue(),flfa.getTypeAsString()); 739 return new VT(nargs[i].getValue(),flfa.getTypeAsString(),i); 740 } 741 } 742 } 743 744 // if not required return the default value 745 if(!flfa.getRequired()) { 746 return getDefaultValue(flfa); 747 } 748 BytecodeException be = new BytecodeException("missing required argument ["+flfan+"] for function ["+flfa.getFunction().getName()+"]",line); 749 UDFUtil.addFunctionDoc(be, flfa.getFunction()); 750 throw be; 751 } 752 753 private static VT getDefaultValue(FunctionLibFunctionArg flfa) { 754 String defaultValue = flfa.getDefaultValue(); 755 String type = flfa.getTypeAsString(); 756 if(defaultValue==null) { 757 if(type.equals("boolean") || type.equals("bool")) 758 return new VT(LitBoolean.FALSE,type,-1); 759 if(type.equals("number") || type.equals("numeric") || type.equals("double")) 760 return new VT(LitDouble.ZERO,type,-1); 761 return new VT(null,type,-1); 762 } 763 return new VT(CastOther.toExpression(LitString.toExprString(defaultValue), type),type,-1); 764 } 765 766 private static String getName(Expression expr) throws BytecodeException { 767 String name = ASMUtil.toString(expr); 768 if(name==null) throw new BytecodeException("cannot extract a string from a object of type ["+expr.getClass().getName()+"]",null); 769 return name; 770 } 771 772 /** 773 * translate a array of arguments to a araay of NamedArguments, attention no check if the elements are really named arguments 774 * @param args 775 * @return 776 */ 777 private static NamedArgument[] toNamedArguments(Argument[] args) { 778 NamedArgument[] nargs=new NamedArgument[args.length]; 779 for(int i=0;i<args.length;i++){ 780 nargs[i]=(NamedArgument) args[i]; 781 } 782 783 return nargs; 784 } 785 786 787 788 /** 789 * check if the arguments are named arguments or regular arguments, throws a exception when mixed 790 * @param funcName 791 * @param args 792 * @param line 793 * @return 794 * @throws BytecodeException 795 */ 796 private static boolean isNamed(Object funcName,Argument[] args) throws BytecodeException { 797 if(ArrayUtil.isEmpty(args)) return false; 798 boolean named=false; 799 for(int i=0;i<args.length;i++){ 800 if(args[i] instanceof NamedArgument)named=true; 801 else if(named) 802 throw new BytecodeException("invalid argument for function "+funcName+", you can not mix named and unnamed arguments", args[i].getStart()); 803 } 804 805 806 return named; 807 } 808 809 public void setFromHash(boolean fromHash) { 810 this.fromHash=fromHash; 811 } 812 813 public boolean fromHash() { 814 return fromHash; 815 } 816 817} 818 819class VT{ 820 Expression value; 821 String type; 822 int index; 823 824 public VT(Expression value, String type, int index) { 825 this.value=value; 826 this.type=type; 827 this.index=index; 828 } 829}