/*
* 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 + "]";
}
}