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}