/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.quercus.program; import com.caucho.quercus.Location; import com.caucho.quercus.QuercusException; import com.caucho.quercus.env.Env; import com.caucho.quercus.env.EnvVar; import com.caucho.quercus.env.EnvVarImpl; import com.caucho.quercus.env.NullThisValue; import com.caucho.quercus.env.NullValue; import com.caucho.quercus.env.StringValue; import com.caucho.quercus.env.QuercusClass; import com.caucho.quercus.env.UnsetValue; import com.caucho.quercus.env.Value; import com.caucho.quercus.env.Var; import com.caucho.quercus.expr.Expr; import com.caucho.quercus.expr.ExprFactory; import com.caucho.quercus.expr.ParamRequiredExpr; import com.caucho.quercus.function.AbstractFunction; import com.caucho.quercus.statement.*; import com.caucho.util.L10N; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.logging.Logger; /** * Represents sequence of statements. */ public class Function extends AbstractFunction { private static final Logger log = Logger.getLogger(Function.class.getName()); private static final L10N L = new L10N(Function.class); protected final FunctionInfo _info; protected final boolean _isReturnsReference; protected final String _name; protected final Arg []_args; protected final Statement _statement; protected boolean _hasReturn; protected String _comment; protected Arg []_closureUseArgs; Function(Location location, String name, FunctionInfo info, Arg []args, Statement []statements) { super(location); _name = name.intern(); _info = info; _info.setFunction(this); _isReturnsReference = info.isReturnsReference(); _args = args; _statement = new BlockStatement(location, statements); setGlobal(info.isPageStatic()); setClosure(info.isClosure()); _isStatic = true; } public Function(ExprFactory exprFactory, Location location, String name, FunctionInfo info, Arg []args, Statement []statements) { super(location); _name = name.intern(); _info = info; _info.setFunction(this); _isReturnsReference = info.isReturnsReference(); _args = new Arg[args.length]; System.arraycopy(args, 0, _args, 0, args.length); _statement = exprFactory.createBlock(location, statements); setGlobal(info.isPageStatic()); setClosure(info.isClosure()); _isStatic = true; } /** * Returns the name. */ public String getName() { return _name; } /* * Returns the declaring class */ @Override public ClassDef getDeclaringClass() { return _info.getDeclaringClass(); } public FunctionInfo getInfo() { return _info; } protected boolean isMethod() { return getDeclaringClassName() != null; } /* * Returns the declaring class */ @Override public String getDeclaringClassName() { ClassDef declaringClass = _info.getDeclaringClass(); if (declaringClass != null) return declaringClass.getName(); else return null; } /** * Returns the args. */ public Arg []getArgs() { return _args; } /** * Returns the args. */ public Arg []getClosureUseArgs() { return _closureUseArgs; } /** * Returns the args. */ public void setClosureUseArgs(Arg []useArgs) { _closureUseArgs = useArgs; } public boolean isObjectMethod() { return false; } /** * True for a returns reference. */ public boolean isReturnsReference() { return _isReturnsReference; } /** * Sets the documentation for this function. */ public void setComment(String comment) { _comment = comment; } /** * Returns the documentation for this function. */ @Override public String getComment() { return _comment; } public Value execute(Env env) { return null; } /** * Evaluates a function's argument, handling ref vs non-ref */ @Override public Value []evalArguments(Env env, Expr fun, Expr []args) { Value []values = new Value[args.length]; for (int i = 0; i < args.length; i++) { Arg arg = null; if (i < _args.length) arg = _args[i]; if (arg == null) values[i] = args[i].eval(env).copy(); else if (arg.isReference()) values[i] = args[i].evalVar(env); else { // php/0d04 values[i] = args[i].eval(env); } } return values; } public Value call(Env env, Expr []args) { return callImpl(env, args, false); } public Value callCopy(Env env, Expr []args) { return callImpl(env, args, false); } public Value callRef(Env env, Expr []args) { return callImpl(env, args, true); } private Value callImpl(Env env, Expr []args, boolean isRef) { HashMap<StringValue,EnvVar> map = new HashMap<StringValue,EnvVar>(); Value []values = new Value[args.length]; for (int i = 0; i < args.length; i++) { Arg arg = null; if (i < _args.length) { arg = _args[i]; } if (arg == null) { values[i] = args[i].eval(env).copy(); } else if (arg.isReference()) { values[i] = args[i].evalVar(env); map.put(arg.getName(), new EnvVarImpl(values[i].toLocalVarDeclAsRef())); } else { // php/0d04 values[i] = args[i].eval(env); Var var = values[i].toVar(); map.put(arg.getName(), new EnvVarImpl(var)); values[i] = var.toValue(); } } for (int i = args.length; i < _args.length; i++) { Arg arg = _args[i]; Expr defaultExpr = arg.getDefault(); if (defaultExpr == null) return env.error("expected default expression"); else if (arg.isReference()) map.put(arg.getName(), new EnvVarImpl(defaultExpr.evalVar(env).toVar())); else { map.put(arg.getName(), new EnvVarImpl(defaultExpr.eval(env).copy().toVar())); } } Map<StringValue,EnvVar> oldMap = env.pushEnv(map); Value []oldArgs = env.setFunctionArgs(values); // php/0476 Value oldThis; if (isStatic()) { // php/0967 oldThis = env.setThis(env.getCallingClass()); } else oldThis = env.getThis(); try { Value value = _statement.execute(env); if (value != null) return value; else if (_info.isReturnsReference()) return new Var(); else return NullValue.NULL; /* else if (_isReturnsReference && isRef) return value; else return value.copyReturn(); */ } finally { env.restoreFunctionArgs(oldArgs); env.popEnv(oldMap); env.setThis(oldThis); } } @Override public Value call(Env env, Value []args) { return callImpl(env, args, false, null, null); } @Override public Value callCopy(Env env, Value []args) { return callImpl(env, args, false, null, null).copy(); } @Override public Value callRef(Env env, Value []args) { return callImpl(env, args, true, null, null); } public Value callImpl(Env env, Value []args, boolean isRef, Arg []useParams, Value []useArgs) { HashMap<StringValue,EnvVar> map = new HashMap<StringValue,EnvVar>(8); if (useParams != null) { for (int i = 0; i < useParams.length; i++) { map.put(useParams[i].getName(), new EnvVarImpl(useArgs[i].toVar())); } } for (int i = 0; i < args.length; i++) { Arg arg = null; if (i < _args.length) { arg = _args[i]; } if (arg == null) { } else if (arg.isReference()) { map.put(arg.getName(), new EnvVarImpl(args[i].toLocalVarDeclAsRef())); } else { // XXX: php/1708, toVar() may be doing another copy() Var var = args[i].toLocalVar(); if (arg.getExpectedClass() != null && arg.getDefault() instanceof ParamRequiredExpr) { env.checkTypeHint(var, arg.getExpectedClass(), arg.getName().toString(), getName()); } // quercus/0d04 map.put(arg.getName(), new EnvVarImpl(var)); } } for (int i = args.length; i < _args.length; i++) { Arg arg = _args[i]; Expr defaultExpr = arg.getDefault(); try { if (defaultExpr == null) return env.error("expected default expression"); else if (arg.isReference()) map.put(arg.getName(), new EnvVarImpl(defaultExpr.evalVar(env).toVar())); else { map.put(arg.getName(), new EnvVarImpl(defaultExpr.eval(env).toLocalVar())); } } catch (Exception e) { throw new QuercusException(getName() + ":arg(" + arg.getName() + ") " + e.getMessage(), e); } } Map<StringValue,EnvVar> oldMap = env.pushEnv(map); Value []oldArgs = env.setFunctionArgs(args); Value oldThis; if (_info.isMethod()) { oldThis = env.getThis(); } else { // php/0967, php/091i oldThis = env.setThis(NullThisValue.NULL); } try { Value value = _statement.execute(env); if (value == null) { if (_isReturnsReference) return new Var(); else return NullValue.NULL; } else if (_isReturnsReference) return value; else return value.toValue().copy(); } finally { env.restoreFunctionArgs(oldArgs); env.popEnv(oldMap); env.setThis(oldThis); } } // // method // @Override public Value callMethod(Env env, QuercusClass qClass, Value qThis, Value[] args) { if (isStatic()) qThis = qClass; Value oldThis = env.setThis(qThis); QuercusClass oldClass = env.setCallingClass(qClass); try { return callImpl(env, args, false, null, null); } finally { env.setThis(oldThis); env.setCallingClass(oldClass); } } @Override public Value callMethodRef(Env env, QuercusClass qClass, Value qThis, Value[] args) { Value oldThis = env.setThis(qThis); QuercusClass oldClass = env.setCallingClass(qClass); try { return callImpl(env, args, true, null, null); } finally { env.setThis(oldThis); env.setCallingClass(oldClass); } } private boolean isVariableArgs() { return _info.isVariableArgs() || _args.length > 5; } private boolean isVariableMap() { // return _info.isVariableVar(); // php/3254 return _info.isUsesSymbolTable() || _info.isVariableVar(); } public String toString() { return getClass().getSimpleName() + "[" + _name + "]"; } }