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.statement.udf;
020
021import java.util.ArrayList;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import lucee.commons.lang.CFTypes;
027import lucee.commons.lang.StringUtil;
028import lucee.runtime.Component;
029import lucee.runtime.exp.TemplateException;
030import lucee.runtime.listener.AppListenerUtil;
031import lucee.runtime.type.FunctionArgument;
032import lucee.runtime.type.FunctionArgumentImpl;
033import lucee.runtime.type.FunctionArgumentLight;
034import lucee.runtime.type.util.ComponentUtil;
035import lucee.transformer.bytecode.Body;
036import lucee.transformer.bytecode.BytecodeContext;
037import lucee.transformer.bytecode.BytecodeException;
038import lucee.transformer.bytecode.Literal;
039import lucee.transformer.bytecode.Page;
040import lucee.transformer.bytecode.Position;
041import lucee.transformer.bytecode.cast.CastBoolean;
042import lucee.transformer.bytecode.cast.CastInt;
043import lucee.transformer.bytecode.cast.CastString;
044import lucee.transformer.bytecode.expression.ExprBoolean;
045import lucee.transformer.bytecode.expression.ExprInt;
046import lucee.transformer.bytecode.expression.ExprString;
047import lucee.transformer.bytecode.expression.Expression;
048import lucee.transformer.bytecode.expression.var.Variable;
049import lucee.transformer.bytecode.literal.LitBoolean;
050import lucee.transformer.bytecode.literal.LitInteger;
051import lucee.transformer.bytecode.literal.LitString;
052import lucee.transformer.bytecode.statement.Argument;
053import lucee.transformer.bytecode.statement.HasBody;
054import lucee.transformer.bytecode.statement.IFunction;
055import lucee.transformer.bytecode.statement.StatementBaseNoFinal;
056import lucee.transformer.bytecode.statement.tag.Attribute;
057import lucee.transformer.bytecode.util.ASMConstants;
058import lucee.transformer.bytecode.util.ASMUtil;
059import lucee.transformer.bytecode.util.ExpressionUtil;
060import lucee.transformer.bytecode.util.Types;
061import lucee.transformer.cfml.evaluator.EvaluatorException;
062
063import org.objectweb.asm.Opcodes;
064import org.objectweb.asm.Type;
065import org.objectweb.asm.commons.GeneratorAdapter;
066import org.objectweb.asm.commons.Method;
067
068public abstract class Function extends StatementBaseNoFinal implements Opcodes, IFunction,HasBody {
069
070        
071
072        // Scope variablesScope()
073        static final Method VARIABLE_SCOPE = new Method(
074                        "variablesScope",
075                        Types.VARIABLES,
076                        new Type[]{}
077                );
078        // Scope variablesScope()
079        static final Method GET_PAGESOURCE = new Method(
080                        "getPageSource",
081                        Types.PAGE_SOURCE,
082                        new Type[]{}
083                );
084
085
086        // Object set(String,Object)
087        static final Method SET_STR = new Method(
088                        "set",
089                        Types.OBJECT,
090                        new Type[]{Types.STRING,Types.OBJECT}
091                );
092        
093        static final Method SET_KEY = new Method(
094                        "set",
095                        Types.OBJECT,
096                        new Type[]{Types.COLLECTION_KEY,Types.OBJECT}
097                );
098        
099        /*static final Method REG_UDF_STR = new Method(
100                        "registerUDF",
101                        Types.VOID,
102                        new Type[]{Types.STRING,Types.UDF_PROPERTIES}
103                );*/
104
105        
106        static final Method REG_UDF_KEY = new Method(
107                        "registerUDF",
108                        Types.VOID,
109                        new Type[]{Types.COLLECTION_KEY,Types.UDF_PROPERTIES}
110                );
111
112        
113        
114        private static final ExprString ANY = LitString.toExprString("any");
115        
116        // <init>(Page,FunctionArgument[],int String,String,boolean);
117        private static final Type FUNCTION_ARGUMENT = Type.getType(FunctionArgument.class);
118        private static final Type FUNCTION_ARGUMENT_IMPL = Type.getType(FunctionArgumentImpl.class);
119        private static final Type FUNCTION_ARGUMENT_LIGHT = Type.getType(FunctionArgumentLight.class);
120        private static final Type FUNCTION_ARGUMENT_ARRAY = Type.getType(FunctionArgument[].class);
121        
122        protected static final Method INIT_UDF_IMPL_PROP = new Method(
123                        "<init>",
124                        Types.VOID,
125                        new Type[]{
126                                        Types.UDF_PROPERTIES
127                                }
128                );
129        
130        private static final Method INIT_UDF_PROPERTIES_STRTYPE = new Method(
131                        "<init>",
132                        Types.VOID,
133                        new Type[]{
134                                        Types.PAGE_SOURCE,
135                                        FUNCTION_ARGUMENT_ARRAY,
136                                        Types.INT_VALUE,
137                                        Types.STRING,
138                                        Types.STRING,
139                                        Types.STRING,
140                                        Types.BOOLEAN_VALUE,
141                                        Types.INT_VALUE,
142                                        Types.BOOLEAN,
143                                        Types.STRING,
144                                        Types.STRING,
145                                        Types.STRING,
146                                        Types.BOOLEAN,
147                                        Types.BOOLEAN,
148                                        Types.OBJECT,
149                                        Types.INTEGER,
150                                        Types.STRUCT_IMPL
151                                }
152                );
153        private static final Method INIT_UDF_PROPERTIES_SHORTTYPE = new Method(
154                        "<init>",
155                        Types.VOID,
156                        new Type[]{
157                                        Types.PAGE_SOURCE,
158                                        FUNCTION_ARGUMENT_ARRAY,
159                                        Types.INT_VALUE,
160                                        Types.STRING,
161                                        Types.SHORT_VALUE,
162                                        Types.STRING,
163                                        Types.BOOLEAN_VALUE,
164                                        Types.INT_VALUE,
165                                        Types.BOOLEAN,
166                                        Types.STRING,
167                                        Types.STRING,
168                                        Types.STRING,
169                                        Types.BOOLEAN,
170                                        Types.BOOLEAN,
171                                        Types.OBJECT,
172                                        Types.INTEGER,
173                                        Types.STRUCT_IMPL
174                                }
175                );
176        private static final Method INIT_UDF_PROPERTIES_SHORTTYPE_LIGHT = new Method(
177                        "<init>",
178                        Types.VOID,
179                        new Type[]{
180                                        Types.PAGE_SOURCE,
181                                        FUNCTION_ARGUMENT_ARRAY,
182                                        Types.INT_VALUE,
183                                        Types.STRING,
184                                        Types.SHORT_VALUE,
185                                        Types.STRING,
186                                        Types.BOOLEAN_VALUE,
187                                        Types.INT_VALUE
188                                }
189                );
190
191
192        // FunctionArgumentImpl(String name,String type,boolean required,int defaultType,String dspName,String hint,StructImpl meta)
193        private static final Method INIT_FAI_KEY1 = new Method(
194                        "<init>",
195                        Types.VOID,
196                        new Type[]{
197                                        Types.COLLECTION_KEY}
198                );      
199        private static final Method INIT_FAI_KEY3 = new Method(
200                        "<init>",
201                        Types.VOID,
202                        new Type[]{
203                                        Types.COLLECTION_KEY,
204                                        Types.STRING,
205                                        Types.SHORT_VALUE}
206                );
207        private static final Method INIT_FAI_KEY4 = new Method(
208                        "<init>",
209                        Types.VOID,
210                        new Type[]{
211                                        Types.COLLECTION_KEY,
212                                        Types.STRING,
213                                        Types.SHORT_VALUE,
214                                        Types.BOOLEAN_VALUE}
215                );      
216        private static final Method INIT_FAI_KEY5 = new Method(
217                        "<init>",
218                        Types.VOID,
219                        new Type[]{
220                                        Types.COLLECTION_KEY,
221                                        Types.STRING,
222                                        Types.SHORT_VALUE,
223                                        Types.BOOLEAN_VALUE,
224                                        Types.INT_VALUE}
225                );
226        private static final Method INIT_FAI_KEY6 = new Method(
227                        "<init>",
228                        Types.VOID,
229                        new Type[]{
230                                        Types.COLLECTION_KEY,
231                                        Types.STRING,
232                                        Types.SHORT_VALUE,
233                                        Types.BOOLEAN_VALUE,
234                                        Types.INT_VALUE,
235                                        Types.BOOLEAN_VALUE}
236                );
237        private static final Method INIT_FAI_KEY7 = new Method(
238                        "<init>",
239                        Types.VOID,
240                        new Type[]{
241                                        Types.COLLECTION_KEY,
242                                        Types.STRING,
243                                        Types.SHORT_VALUE,
244                                        Types.BOOLEAN_VALUE,
245                                        Types.INT_VALUE,
246                                        Types.BOOLEAN_VALUE,
247                                        Types.STRING}
248                );
249        private static final Method INIT_FAI_KEY8 = new Method(
250                        "<init>",
251                        Types.VOID,
252                        new Type[]{
253                                        Types.COLLECTION_KEY,
254                                        Types.STRING,
255                                        Types.SHORT_VALUE,
256                                        Types.BOOLEAN_VALUE,
257                                        Types.INT_VALUE,
258                                        Types.BOOLEAN_VALUE,
259                                        Types.STRING,
260                                        Types.STRING}
261                );
262        private static final Method INIT_FAI_KEY9 = new Method(
263                        "<init>",
264                        Types.VOID,
265                        new Type[]{
266                                        Types.COLLECTION_KEY,
267                                        Types.STRING,
268                                        Types.SHORT_VALUE,
269                                        Types.BOOLEAN_VALUE,
270                                        Types.INT_VALUE,
271                                        Types.BOOLEAN_VALUE,
272                                        Types.STRING,
273                                        Types.STRING,
274                                        Types.STRUCT_IMPL}
275                );      
276        private static final Method[] INIT_FAI_KEY=new Method[]{
277                INIT_FAI_KEY1,INIT_FAI_KEY3,INIT_FAI_KEY4,INIT_FAI_KEY5,INIT_FAI_KEY6,INIT_FAI_KEY7,INIT_FAI_KEY8,INIT_FAI_KEY9
278        };
279        private static final Method[] INIT_FAI_KEY_LIGHT=new Method[]{
280                INIT_FAI_KEY1,INIT_FAI_KEY3
281        };
282        
283        
284        ExprString name;
285        ExprString returnType=ANY;
286        ExprBoolean output=LitBoolean.TRUE;
287        ExprBoolean bufferOutput;
288        //ExprBoolean abstry=LitBoolean.FALSE;
289        int access=Component.ACCESS_PUBLIC;
290        ExprString displayName=LitString.EMPTY;
291        ExprString hint=LitString.EMPTY;
292        Body body;
293        List<Argument> arguments=new ArrayList<Argument>();
294        Map<String,Attribute> metadata;
295        ExprString returnFormat;
296        ExprString description;
297        ExprBoolean secureJson;
298        ExprBoolean verifyClient;
299        ExprInt localMode;
300        protected int valueIndex;
301        protected int arrayIndex;
302        private Literal cachedWithin;
303        private boolean _abstract;
304        private boolean _final;
305        
306
307        public Function(Page page,String name,int access,String returnType,Body body,Position start, Position end) {
308                super(start,end);
309                this.name=LitString.toExprString(name);
310                this.access=access;
311                if(!StringUtil.isEmpty(returnType))this.returnType=LitString.toExprString(returnType);
312                this.body=body;
313                body.setParent(this);
314                int[] indexes = page.addFunction(this);
315                valueIndex=indexes[VALUE_INDEX];
316                arrayIndex=indexes[ARRAY_INDEX];
317        }
318        
319        public Function(Page page,Expression name,Expression returnType,Expression returnFormat,Expression output,Expression bufferOutput,
320                        int access,Expression displayName,Expression description,Expression hint,Expression secureJson,
321                        Expression verifyClient,Expression localMode,Literal cachedWithin, boolean _abstract, boolean _final,Body body,Position start, Position end) {
322                super(start,end);
323                
324                this.name=CastString.toExprString(name);
325                this.returnType=CastString.toExprString(returnType);
326                this.returnFormat=returnFormat!=null?CastString.toExprString(returnFormat):null;
327                this.output=CastBoolean.toExprBoolean(output);
328                this.bufferOutput=bufferOutput==null?null:CastBoolean.toExprBoolean(bufferOutput);
329                this.access=access;
330                this.description=description!=null?CastString.toExprString(description):null;
331                this.displayName=CastString.toExprString(displayName);
332                this.hint=CastString.toExprString(hint);
333                this.secureJson=secureJson!=null?CastBoolean.toExprBoolean(secureJson):null;
334                this.verifyClient=verifyClient!=null?CastBoolean.toExprBoolean(verifyClient):null;
335                this.cachedWithin=cachedWithin;
336                this._abstract=_abstract;
337                this._final=_final;
338                this.localMode=toLocalMode(localMode, null);
339                
340                this.body=body;
341                body.setParent(this);
342                int[] indexes=page.addFunction(this);
343                valueIndex=indexes[VALUE_INDEX];
344                arrayIndex=indexes[ARRAY_INDEX];
345                
346        }
347
348
349        public static ExprInt toLocalMode(Expression expr, ExprInt defaultValue) {
350                int mode=-1;
351                if(expr instanceof Literal) {
352                        String str = ((Literal)expr).getString();
353                        str=str.trim().toLowerCase();
354                        mode = AppListenerUtil.toLocalMode(str,-1);
355                }
356                if(mode==-1) return defaultValue;
357                return LitInteger.toExpr(mode);
358        }
359        
360
361
362        /*private static void checkNameConflict(ExprString expr) {
363                if(expr instanceof LitString){
364                        String name=((LitString)expr).getString();
365                        if()
366                }
367        }*/
368
369        /**
370         * @see lucee.transformer.bytecode.statement.IFunction#writeOut(lucee.transformer.bytecode.BytecodeContext, int)
371         */
372        public final void writeOut(BytecodeContext bc, int type) throws BytecodeException {
373        ExpressionUtil.visitLine(bc, getStart());
374        _writeOut(bc,type);
375        ExpressionUtil.visitLine(bc, getEnd());
376        }
377        
378        /**
379         * @see lucee.transformer.bytecode.statement.StatementBase#_writeOut(lucee.transformer.bytecode.BytecodeContext)
380         */
381        public final void _writeOut(BytecodeContext bc) throws BytecodeException {
382                _writeOut(bc,PAGE_TYPE_REGULAR);
383        }
384        
385        public abstract void _writeOut(BytecodeContext bc, int pageType) throws BytecodeException;
386        
387
388        public final void loadUDFProperties(BytecodeContext bc, int valueIndex,int arrayIndex, boolean closure) throws BytecodeException {
389                BytecodeContext constr = bc.getConstructor();
390                GeneratorAdapter cga = constr.getAdapter();
391                GeneratorAdapter ga = bc.getAdapter();
392                
393                // store
394                cga.visitVarInsn(ALOAD, 0);
395                cga.visitFieldInsn(GETFIELD, bc.getClassName(), "udfs", Types.UDF_PROPERTIES_ARRAY.toString());
396                cga.push(arrayIndex);
397                createUDFProperties(constr,valueIndex,closure);
398                //cga.visitInsn(DUP_X2);
399                cga.visitInsn(AASTORE);
400                
401                // get
402                ga.visitVarInsn(ALOAD, 0);
403                ga.visitFieldInsn(GETFIELD, bc.getClassName(), "udfs", Types.UDF_PROPERTIES_ARRAY.toString());
404                ga.push(arrayIndex);
405                ga.visitInsn(AALOAD);
406        }
407        
408        
409        
410        public final void createUDFProperties(BytecodeContext bc, int index, boolean closure) throws BytecodeException {
411                GeneratorAdapter adapter=bc.getAdapter();
412                adapter.newInstance(Types.UDF_PROPERTIES_IMPL);
413                adapter.dup();
414                if(closure){
415                        adapter.loadThis();
416                        adapter.invokeVirtual(Types.PAGE, GET_PAGESOURCE);
417                }
418                else adapter.visitVarInsn(ALOAD, 1);
419                // page
420                //adapter.loadLocal(0);
421                //adapter.loadThis();
422                
423                // arguments
424                createArguments(bc);
425                // index
426                adapter.push(index);
427                // name
428                ExpressionUtil.writeOutSilent(name,bc, Expression.MODE_REF);
429                // return type
430                short type=ExpressionUtil.toShortType(returnType,false,CFTypes.TYPE_UNKNOW);
431                if(type==CFTypes.TYPE_UNKNOW) ExpressionUtil.writeOutSilent(returnType,bc, Expression.MODE_REF);
432                else adapter.push(type);
433                
434                // return format
435                if(returnFormat!=null)ExpressionUtil.writeOutSilent(returnFormat,bc, Expression.MODE_REF);
436                else ASMConstants.NULL(adapter);
437                
438                // output
439                ExpressionUtil.writeOutSilent(output,bc, Expression.MODE_VALUE);
440                
441                
442                // access
443                writeOutAccess(bc, access);
444                
445                boolean light=type!=-1;
446                if(light && !LitString.EMPTY.equals(displayName))light=false;
447                if(light && description!=null && !LitString.EMPTY.equals(description))light=false;
448                if(light && !LitString.EMPTY.equals(hint))light=false;
449                if(light && secureJson!=null)light=false;
450                if(light && verifyClient!=null)light=false;
451                if(light && cachedWithin!=null)light=false;
452                if(light && bufferOutput!=null)light=false;
453                if(light && localMode!=null)light=false;
454                if(light && Page.hasMetaDataStruct(metadata, null))light=false;
455                if(light){
456                        adapter.invokeConstructor(Types.UDF_PROPERTIES_IMPL, INIT_UDF_PROPERTIES_SHORTTYPE_LIGHT);
457                        return;
458                }
459                
460
461                // buffer output
462                if(bufferOutput!=null)ExpressionUtil.writeOutSilent(bufferOutput,bc, Expression.MODE_REF);
463                else ASMConstants.NULL(adapter);
464                
465                // displayName
466                ExpressionUtil.writeOutSilent(displayName,bc, Expression.MODE_REF);// displayName;
467                
468                // description
469                if(description!=null)ExpressionUtil.writeOutSilent(description,bc, Expression.MODE_REF);// displayName;
470                else adapter.push("");
471                
472                // hint
473                ExpressionUtil.writeOutSilent(hint,bc, Expression.MODE_REF);// hint;
474                
475                // secureJson
476                if(secureJson!=null)ExpressionUtil.writeOutSilent(secureJson,bc, Expression.MODE_REF);
477                else ASMConstants.NULL(adapter);
478                
479                // verify client
480                if(verifyClient!=null)ExpressionUtil.writeOutSilent(verifyClient,bc, Expression.MODE_REF);
481                else ASMConstants.NULL(adapter);
482                
483                // cachedwithin
484                if(cachedWithin!=null) {
485                        cachedWithin.writeOut(bc, Expression.MODE_REF);
486                }
487                else ASMConstants.NULL(adapter);
488                //adapter.push(cachedWithin<0?0:cachedWithin);
489                
490                // localMode
491                if(localMode!=null)ExpressionUtil.writeOutSilent(localMode,bc, Expression.MODE_REF);
492                else ASMConstants.NULL(adapter);
493                
494                // meta
495                Page.createMetaDataStruct(bc,metadata,null);
496                
497                adapter.invokeConstructor(Types.UDF_PROPERTIES_IMPL, type==-1?INIT_UDF_PROPERTIES_STRTYPE:INIT_UDF_PROPERTIES_SHORTTYPE);
498                
499        }
500        
501        /*public final void loadUDF(BytecodeContext bc, int index) throws BytecodeException {
502                // new UDF(...)
503                GeneratorAdapter adapter=bc.getAdapter();
504                adapter.newInstance(Types.UDF_IMPL);
505                adapter.dup();
506                
507                loadUDFProperties(bc, index,false);
508                
509                adapter.invokeConstructor(Types.UDF_IMPL, INIT_UDF_IMPL_PROP);
510        }*/
511        
512        public final void createUDF(BytecodeContext bc, int index, boolean closure) throws BytecodeException {
513                // new UDF(...)
514                GeneratorAdapter adapter=bc.getAdapter();
515                adapter.newInstance(closure?Types.CLOSURE:Types.UDF_IMPL);
516                adapter.dup();
517                
518                createUDFProperties(bc, index,closure);
519                //loadUDFProperties(bc, index,closure);
520                
521                adapter.invokeConstructor(closure?Types.CLOSURE:Types.UDF_IMPL, INIT_UDF_IMPL_PROP);
522        }
523        
524
525        
526        private final void createArguments(BytecodeContext bc) throws BytecodeException {
527                GeneratorAdapter ga = bc.getAdapter();
528                ga.push(arguments.size());
529                ga.newArray(FUNCTION_ARGUMENT);
530                Argument arg;
531        for (int i = 0; i < arguments.size(); i++) {
532                arg= arguments.get(i);
533            
534                boolean canHaveKey = Variable.canRegisterKey(arg.getName());
535                
536        // CHECK if default values
537                // type
538                ExprString _strType = arg.getType();
539                short _type=CFTypes.TYPE_UNKNOW;
540                if(_strType instanceof LitString){
541                        _type=CFTypes.toShortStrict(((LitString)_strType).getString(),CFTypes.TYPE_UNKNOW);
542                }
543                boolean useType=!canHaveKey || _type!=CFTypes.TYPE_ANY;
544                //boolean useStrType=useType && (_type==CFTypes.TYPE_UNDEFINED || _type==CFTypes.TYPE_UNKNOW || CFTypes.toString(_type, null)==null);
545                
546                // required
547                ExprBoolean _req = arg.getRequired();
548                boolean useReq=!canHaveKey || toBoolean(_req,null)!=Boolean.FALSE;
549                
550                // default-type
551                Expression _def = arg.getDefaultValueType();
552                boolean useDef=!canHaveKey || toInt(_def,-1)!=FunctionArgument.DEFAULT_TYPE_NULL;
553                
554                // pass by reference
555                ExprBoolean _pass = arg.isPassByReference();
556                boolean usePass=!canHaveKey || toBoolean(_pass,null)!=Boolean.TRUE;
557                
558                // display-hint
559                ExprString _dsp = arg.getDisplayName();
560                boolean useDsp=!canHaveKey || !isLiteralEmptyString(_dsp);
561                
562                // hint
563                ExprString _hint = arg.getHint();
564                boolean useHint=!canHaveKey || !isLiteralEmptyString(_hint);
565                
566                // meta
567                Map _meta = arg.getMetaData();
568                boolean useMeta=!canHaveKey || (_meta!=null && !_meta.isEmpty());
569                int functionIndex=7;
570                if(!useMeta) {
571                        functionIndex--;
572                        if(!useHint) {
573                                functionIndex--;
574                                if(!useDsp){
575                                        functionIndex--;
576                                        if(!usePass) {
577                                                functionIndex--;
578                                                if(!useDef) {
579                                                        functionIndex--;
580                                                        if(!useReq) {
581                                                                functionIndex--;
582                                                                if(!useType){
583                                                                        functionIndex--;
584                                                                }
585                                                        }
586                                                }
587                                        }
588                                }
589                        }
590                }
591        // write out arguments  
592                ga.dup();
593            ga.push(i);
594                
595            // new FunctionArgument(...)
596            ga.newInstance(canHaveKey && functionIndex<INIT_FAI_KEY_LIGHT.length?FUNCTION_ARGUMENT_LIGHT:FUNCTION_ARGUMENT_IMPL);
597                ga.dup();
598                Variable.registerKey(bc,arg.getName(),false);
599                
600                // type
601                if(functionIndex>=INIT_FAI_KEY.length-7) {
602                        _strType.writeOut(bc, Expression.MODE_REF);
603                        bc.getAdapter().push(_type);
604                }
605                // required
606                if(functionIndex>=INIT_FAI_KEY.length-6)_req.writeOut(bc, Expression.MODE_VALUE);
607                // default value
608                if(functionIndex>=INIT_FAI_KEY.length-5)_def.writeOut(bc, Expression.MODE_VALUE);
609                // pass by reference
610                if(functionIndex>=INIT_FAI_KEY.length-4)_pass.writeOut(bc, Expression.MODE_VALUE);
611                // display-name
612                if(functionIndex>=INIT_FAI_KEY.length-3)_dsp.writeOut(bc, Expression.MODE_REF);
613                // hint
614                if(functionIndex>=INIT_FAI_KEY.length-2)_hint.writeOut(bc, Expression.MODE_REF);
615                //meta
616                if(functionIndex==INIT_FAI_KEY.length-1)Page.createMetaDataStruct(bc,_meta,null);
617                
618                if(functionIndex<INIT_FAI_KEY_LIGHT.length)
619                        ga.invokeConstructor(FUNCTION_ARGUMENT_LIGHT, INIT_FAI_KEY[functionIndex]);
620                else 
621                        ga.invokeConstructor(FUNCTION_ARGUMENT_IMPL, INIT_FAI_KEY[functionIndex]);
622
623            ga.visitInsn(Opcodes.AASTORE);
624        }
625        }
626
627        private final int toInt(Expression expr, int defaultValue) {
628                if(expr instanceof LitInteger) {
629                        return ((LitInteger)expr).getInteger().intValue();
630                }
631                return defaultValue;
632        }
633
634        private final Boolean toBoolean(ExprBoolean expr, Boolean defaultValue) {
635                if(expr instanceof LitBoolean) {
636                        return ((LitBoolean)expr).getBooleanValue()?Boolean.TRUE:Boolean.FALSE;
637                }
638                return defaultValue;
639        }
640
641        private final boolean isLiteralEmptyString(ExprString expr) {
642                if(expr instanceof LitString) {
643                        return StringUtil.isEmpty(((LitString)expr).getString());
644                }
645                return false;
646        }
647
648        private final void writeOutAccess(BytecodeContext bc,ExprString expr) {
649                
650                // write short type
651                if(expr instanceof LitString){
652                        int access=ComponentUtil.toIntAccess(((LitString)expr).getString(),Component.ACCESS_PUBLIC);
653                        bc.getAdapter().push(access);
654                }
655                else bc.getAdapter().push(Component.ACCESS_PUBLIC);
656        }
657        private final void writeOutAccess(BytecodeContext bc,int access) {
658                bc.getAdapter().push(access);
659        }
660
661        public final void addArgument(String name, String type, boolean required, Expression defaultValue) {
662                addArgument(
663                                LitString.toExprString(name), 
664                                LitString.toExprString(type), 
665                                LitBoolean.toExprBoolean(required),
666                                defaultValue, 
667                                LitBoolean.TRUE,
668                                LitString.EMPTY,
669                                LitString.EMPTY,null);
670        }
671
672        public final void addArgument(Expression name, Expression type, Expression required, Expression defaultValue,ExprBoolean passByReference, 
673                        Expression displayName, Expression hint,Map meta) {
674                arguments.add(new Argument(name,type,required,defaultValue,passByReference,displayName,hint,meta));
675        }
676
677        /**
678         * @return the arguments
679         */
680        public final List<Argument> getArguments() {
681                return arguments;
682        }
683
684        /**
685         * @return the body
686         */
687        public final Body getBody() {
688                return body;
689        }
690
691        public final void setMetaData(Map<String,Attribute> metadata) {
692                this.metadata=metadata;
693        }
694        
695        public final void setHint(String hint){
696                this.hint=LitString.toExprString(hint);
697        }
698
699        public final void addAttribute(Attribute attr) throws TemplateException {
700                String name=attr.getName().toLowerCase();
701                // name
702                if("name".equals(name)) {
703                        throw new BytecodeException("name cannot be defined twice",getStart());
704                        //this.name=CastString.toExprString(attr.getValue());
705                }
706                else if("returntype".equals(name))      {
707                        this.returnType=toLitString(name,attr.getValue());
708                }
709                else if("access".equals(name))  {
710                        
711                        LitString ls = toLitString(name,attr.getValue());
712                        String strAccess = ls.getString();
713                        int acc = ComponentUtil.toIntAccess(strAccess,-1);
714                        if(acc==-1)
715                                throw new BytecodeException("invalid access type ["+strAccess+"], access types are remote, public, package, private",getStart());
716                        access=acc;
717                        
718                }
719                
720                else if("output".equals(name))          this.output=toLitBoolean(name,attr.getValue());
721                else if("bufferoutput".equals(name))this.bufferOutput=toLitBoolean(name,attr.getValue());
722                else if("displayname".equals(name))     this.displayName=toLitString(name,attr.getValue());
723                else if("hint".equals(name))            this.hint=toLitString(name,attr.getValue());
724                else if("description".equals(name))     this.description=toLitString(name,attr.getValue());
725                else if("returnformat".equals(name))this.returnFormat=toLitString(name,attr.getValue());
726                else if("securejson".equals(name))      this.secureJson=toLitBoolean(name,attr.getValue());
727                else if("verifyclient".equals(name))    this.verifyClient=toLitBoolean(name,attr.getValue());
728                else if("localmode".equals(name))       {
729                        Expression v = attr.getValue();
730                        if(v!=null) {
731                                String str = ASMUtil.toString(v,null);
732                                if(!StringUtil.isEmpty(str)){
733                                        int mode = AppListenerUtil.toLocalMode(str, -1);
734                                        if(mode!=-1) this.localMode=LitInteger.toExpr(mode);
735                                        else throw new BytecodeException("Attribute localMode of the Tag Function, must be a literal value (modern, classic, true or false)",getStart());
736                                }
737                        }
738                }
739                else if("cachedwithin".equals(name))    {
740                        try {
741                                this.cachedWithin=ASMUtil.cachedWithinValue(attr.getValue());//ASMUtil.timeSpanToLong(attr.getValue());
742                        } catch (EvaluatorException e) {
743                                throw new TemplateException(e.getMessage());
744                        }
745                }
746                else if("modifier".equals(name))        {
747                        Expression val = attr.getValue();
748                        if(val instanceof Literal) {
749                                Literal l=(Literal) val;
750                                String str = StringUtil.emptyIfNull(l.getString()).trim();
751                                if("abstract".equalsIgnoreCase(str))_abstract=true;
752                                else if("final".equalsIgnoreCase(str))_final=true;
753                        }
754                }
755                
756                
757                
758                else {
759                        toLitString(name,attr.getValue());// needed for testing
760                        if(metadata==null)metadata=new HashMap<String,Attribute>();
761                        metadata.put(attr.getName(), attr);
762                }
763        }
764
765        private final LitString toLitString(String name, Expression value) throws BytecodeException {
766                ExprString es = CastString.toExprString(value);
767                if(!(es instanceof LitString))
768                        throw new BytecodeException("value of attribute ["+name+"] must have a literal/constant value",getStart());
769                return (LitString) es;
770        }
771        
772        private final LitBoolean toLitBoolean(String name, Expression value) throws BytecodeException {
773                 ExprBoolean eb = CastBoolean.toExprBoolean(value);
774                if(!(eb instanceof LitBoolean))
775                        throw new BytecodeException("value of attribute ["+name+"] must have a literal/constant value",getStart());
776                return (LitBoolean) eb;
777        }
778        
779        private final ExprInt toLitInt(String name, Expression value) throws BytecodeException {
780                ExprInt eb = CastInt.toExprInt(value);
781                if(!(eb instanceof Literal))
782                        throw new BytecodeException("value of attribute ["+name+"] must have a literal/constant value",getStart());
783                return eb;
784        }
785
786}