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