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