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}