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.runtime.functions.system;
020
021import java.io.File;
022
023import lucee.commons.lang.ExceptionUtil;
024import lucee.runtime.Mapping;
025import lucee.runtime.Page;
026import lucee.runtime.PageContext;
027import lucee.runtime.PageSourceImpl;
028import lucee.runtime.config.ConfigWebImpl;
029import lucee.runtime.exp.ExpressionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.op.Caster;
032import lucee.runtime.op.Duplicator;
033import lucee.runtime.type.Collection;
034import lucee.runtime.type.FunctionValue;
035import lucee.runtime.type.KeyImpl;
036import lucee.runtime.type.Struct;
037import lucee.runtime.type.StructImpl;
038import lucee.runtime.type.UDF;
039import lucee.runtime.type.UDFImpl;
040import lucee.runtime.type.scope.Variables;
041import lucee.runtime.type.scope.VariablesImpl;
042import lucee.runtime.type.util.ArrayUtil;
043import lucee.runtime.type.util.KeyConstants;
044
045public class CFFunction {
046        
047        
048        private static final Variables VAR = new VariablesImpl();
049        //private static Map udfs=new ReferenceMap();
050        
051        public static Object call(PageContext pc , Object[] objArr) throws PageException {
052                if(objArr.length<3)
053                        throw new ExpressionException("invalid call of a CFML Based built in function");
054                
055                // translate arguments
056                String filename=Caster.toString((((FunctionValue) objArr[0]).getValue()));
057                Collection.Key name=KeyImpl.toKey((((FunctionValue) objArr[1]).getValue()));
058                boolean isweb=Caster.toBooleanValue((((FunctionValue) objArr[2]).getValue()));
059                
060                
061                UDF udf=loadUDF(pc, filename, name, isweb);
062                Struct meta = udf.getMetaData(pc);
063                boolean callerScopes=(meta==null)?false:Caster.toBooleanValue(meta.get("callerScopes",Boolean.FALSE),false);
064                boolean caller=meta==null?false:Caster.toBooleanValue(meta.get(KeyConstants._caller,Boolean.FALSE),false);
065                
066                Struct namedArguments=null,cs=null;
067                if(callerScopes) {
068                        
069                        cs=new StructImpl();
070                        if(pc.undefinedScope().getCheckArguments()) {
071                                cs.set(KeyConstants._local, pc.localScope().duplicate(false));
072                                cs.set(KeyConstants._arguments, pc.argumentsScope().duplicate(false));
073                        }
074                }
075                
076                Object[] arguments=null;
077                if(objArr.length<=3)arguments=ArrayUtil.OBJECT_EMPTY;
078                else if(objArr[3] instanceof FunctionValue){
079                        FunctionValue fv;
080                        namedArguments=new StructImpl();
081                        if(callerScopes)        namedArguments.setEL(KeyConstants._caller, cs);
082                        else if(caller)         namedArguments.setEL(KeyConstants._caller, Duplicator.duplicate(pc.undefinedScope(),false));
083                        for(int i=3;i<objArr.length;i++){
084                                fv=toFunctionValue(name,objArr[i]);
085                                namedArguments.set(fv.getName(), fv.getValue());
086                        }
087                }
088                else {
089                        int offset=(caller||callerScopes?2:3);
090                        arguments=new Object[objArr.length-offset];
091                        if(callerScopes) arguments[0]=cs;
092                        else if(caller)arguments[0]=Duplicator.duplicate(pc.undefinedScope(),false);
093                        for(int i=3;i<objArr.length;i++){
094                                arguments[i-offset]=toObject(name,objArr[i]);
095                        }
096                }
097                
098                // execute UDF
099                if(namedArguments==null){
100                        return ((UDFImpl)udf).call(pc,name, arguments, false);
101                }
102                
103                
104                return ((UDFImpl)udf).callWithNamedValues(pc,name, namedArguments, false);
105        }
106
107        public static synchronized UDF loadUDF(PageContext pc, String filename,Collection.Key name,boolean isweb) throws PageException {
108                ConfigWebImpl config = (ConfigWebImpl) pc.getConfig();
109                String key=isweb?name.getString()+config.getId():name.getString();
110        UDF udf=config.getFromFunctionCache(key);
111                if(udf!=null) return udf;
112                
113                Mapping mapping=isweb?config.getFunctionMapping():config.getServerFunctionMapping();
114        PageSourceImpl ps = (PageSourceImpl) mapping.getPageSource(filename);
115        Page p = ps.loadPage(pc);       
116                
117        
118        // execute page
119        Variables old = pc.variablesScope();
120        pc.setVariablesScope(VAR);
121        boolean wasSilent = pc.setSilent();
122        try {
123                        p.call(pc);
124                        Object o= pc.variablesScope().get(name,null);
125                        if(o instanceof UDF) {
126                                udf= (UDF) o;
127                                config.putToFunctionCache(key, udf);
128                                return udf;
129                        }
130                        throw new ExpressionException("there is no Function defined with name ["+name+"] in template ["+mapping.getStrPhysical()+File.separator+filename+"]");
131                } 
132        catch (Throwable t) {
133                ExceptionUtil.rethrowIfNecessary(t);
134                        throw Caster.toPageException(t);
135                }
136                finally{
137                        pc.setVariablesScope(old);
138                        if(!wasSilent)pc.unsetSilent();
139                }
140                
141                
142        }
143
144        private static FunctionValue toFunctionValue(Collection.Key name,Object obj) throws ExpressionException {
145                if(obj instanceof FunctionValue)
146                        return (FunctionValue) obj;
147                throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
148        }
149
150        private static Object toObject(Collection.Key name,Object obj) throws ExpressionException {
151                if(obj instanceof FunctionValue)
152                        throw new ExpressionException("invalid argument for function "+name+", you can not mix named and unnamed arguments");
153                return obj;
154        }
155}