Arquivos
hhvm/hphp/compiler/expression/function_call.cpp
T
Owen Yamauchi a57ae0488a Generate the parser at build time, part one
Update a number of things to make optionally generating the parser at
build time possible.  @sgolemon will add the OSS pieces of this in a
separate commit.
2013-04-30 09:57:57 -07:00

586 linhas
19 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include <compiler/expression/function_call.h>
#include <util/util.h>
#include <util/logger.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/analysis/code_error.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/variable_table.h>
#include <compiler/statement/statement.h>
#include <compiler/statement/method_statement.h>
#include <compiler/statement/exp_statement.h>
#include <compiler/statement/return_statement.h>
#include <compiler/statement/statement_list.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/array_pair_expression.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/expression/simple_function_call.h>
#include <compiler/expression/parameter_expression.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include "hphp/util/parser/hphp.tab.hpp"
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
FunctionCall::FunctionCall
(EXPRESSION_CONSTRUCTOR_BASE_PARAMETERS,
ExpressionPtr nameExp, const std::string &name, ExpressionListPtr params,
ExpressionPtr classExp)
: Expression(EXPRESSION_CONSTRUCTOR_BASE_PARAMETER_VALUES),
StaticClassName(classExp), m_nameExp(nameExp),
m_ciTemp(-1), m_params(params), m_valid(false),
m_extraArg(0), m_variableArgument(false), m_voidReturn(false),
m_voidWrapper(false), m_redeclared(false),
m_noStatic(false), m_noInline(false), m_invokeFewArgsDecision(true),
m_arrayParams(false),
m_argArrayId(-1), m_argArrayHash(-1), m_argArrayIndex(-1) {
if (m_nameExp &&
m_nameExp->getKindOf() == Expression::KindOfScalarExpression) {
assert(m_name.empty());
ScalarExpressionPtr c = dynamic_pointer_cast<ScalarExpression>(m_nameExp);
m_origName = c->getOriginalLiteralString();
c->toLower(true /* func call*/);
m_name = c->getLiteralString();
} else {
m_origName = name;
m_name = Util::toLower(name);
}
m_clsNameTemp = -1;
}
void FunctionCall::reset() {
m_valid = false;
m_extraArg = 0;
m_variableArgument = false;
m_voidWrapper = false;
}
bool FunctionCall::isTemporary() const {
return m_funcScope && !m_funcScope->isRefReturn();
}
void FunctionCall::deepCopy(FunctionCallPtr exp) {
Expression::deepCopy(exp);
exp->m_class = Clone(m_class);
exp->m_params = Clone(m_params);
exp->m_nameExp = Clone(m_nameExp);
}
bool FunctionCall::canInvokeFewArgs() {
// We can always change out minds about saying yes, but once we say
// no, it sticks.
if (m_invokeFewArgsDecision &&
((m_params && m_params->getCount() > Option::InvokeFewArgsCount) ||
m_arrayParams)) {
m_invokeFewArgsDecision = false;
}
return m_invokeFewArgsDecision;
}
ConstructPtr FunctionCall::getNthKid(int n) const {
switch (n) {
case 0:
return m_class;
case 1:
return m_nameExp;
case 2:
return m_params;
default:
assert(false);
break;
}
return ConstructPtr();
}
int FunctionCall::getKidCount() const {
return 3;
}
void FunctionCall::setNthKid(int n, ConstructPtr cp) {
switch (n) {
case 0:
m_class = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 1:
m_nameExp = boost::dynamic_pointer_cast<Expression>(cp);
break;
case 2:
m_params = boost::dynamic_pointer_cast<ExpressionList>(cp);
break;
default:
assert(false);
break;
}
}
void FunctionCall::markRefParams(FunctionScopePtr func,
const std::string &name,
bool canInvokeFewArgs) {
ExpressionList &params = *m_params;
if (func) {
int mpc = func->getMaxParamCount();
for (int i = params.getCount(); i--; ) {
ExpressionPtr p = params[i];
if (i < mpc ? func->isRefParam(i) :
func->isReferenceVariableArgument()) {
p->setContext(Expression::RefValue);
} else if (i < mpc && p->hasContext(RefParameter)) {
Symbol *sym = func->getVariables()->addSymbol(func->getParamName(i));
sym->setLvalParam();
sym->setCallTimeRef();
}
}
} else if (Option::WholeProgram && !m_name.empty()) {
FunctionScope::FunctionInfoPtr info =
FunctionScope::GetFunctionInfo(m_name);
if (info) {
for (int i = params.getCount(); i--; ) {
if (info->isRefParam(i)) {
m_params->markParam(i, canInvokeFewArgs);
}
}
}
// If we cannot find information of the so-named function, it might not
// exist, or it might go through __call(), either of which cannot have
// reference parameters.
} else {
for (int i = params.getCount(); i--; ) {
m_params->markParam(i, canInvokeFewArgs);
}
}
}
void FunctionCall::analyzeProgram(AnalysisResultPtr ar) {
if (m_class) m_class->analyzeProgram(ar);
if (m_nameExp) m_nameExp->analyzeProgram(ar);
if (m_params) m_params->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (m_funcScope && !m_arrayParams) {
for (int i = 0, n = m_funcScope->getMaxParamCount(); i < n; ++i) {
if (TypePtr specType = m_funcScope->getParamTypeSpec(i)) {
const char *fmt = 0;
string ptype;
if (!m_params || m_params->getCount() <= i) {
if (i >= m_funcScope->getMinParamCount()) break;
fmt = "parameter %d of %s() requires %s, none given";
} else {
ExpressionPtr param = (*m_params)[i];
if (!Type::Inferred(ar, param->getType(), specType)) {
fmt = "parameter %d of %s() requires %s, called with %s";
}
ptype = param->getType()->toString();
}
if (fmt) {
string msg;
Util::string_printf
(msg, fmt,
i + 1,
Util::escapeStringForCPP(m_funcScope->getOriginalName()).c_str(),
specType->toString().c_str(), ptype.c_str());
Compiler::Error(Compiler::BadArgumentType,
shared_from_this(), msg);
}
}
}
}
}
}
struct InlineCloneInfo {
explicit InlineCloneInfo(FunctionScopePtr fs)
: func(fs)
, callWithThis(false)
{}
FunctionScopePtr func;
StringToExpressionPtrMap sepm;
ExpressionListPtr elist;
bool callWithThis;
string localThis;
string staticClass;
};
//typedef std::map<std::string, ExpressionPtr> StringToExpressionPtrMap;
static ExpressionPtr cloneForInlineRecur(InlineCloneInfo &info,
ExpressionPtr exp,
const std::string &prefix,
AnalysisResultConstPtr ar,
FunctionScopePtr scope) {
exp->getOriginalScope(); // make sure to cache the original scope
exp->setBlockScope(scope);
for (int i = 0, n = exp->getKidCount(); i < n; i++) {
if (ExpressionPtr k = exp->getNthExpr(i)) {
exp->setNthKid(i, cloneForInlineRecur(info, k, prefix, ar, scope));
}
}
StaticClassName *scn = dynamic_cast<StaticClassName*>(exp.get());
if (scn && scn->isStatic() && !info.staticClass.empty()) {
scn->resolveStatic(info.staticClass);
}
switch (exp->getKindOf()) {
case Expression::KindOfSimpleVariable:
{
SimpleVariablePtr sv(dynamic_pointer_cast<SimpleVariable>(exp));
if (sv->isSuperGlobal()) break;
string name;
if (sv->isThis()) {
if (!info.callWithThis) {
if (!sv->hasContext(Expression::ObjectContext)) {
exp = sv->makeConstant(ar, "null");
} else {
// This will produce the wrong error
// we really want a "throw_fatal" ast node.
exp = sv->makeConstant(ar, "null");
}
break;
}
if (info.localThis.empty()) break;
name = info.localThis;
} else {
name = prefix + sv->getName();
}
SimpleVariablePtr rep(new SimpleVariable(
exp->getScope(), exp->getLocation(), name));
rep->copyContext(sv);
rep->updateSymbol(SimpleVariablePtr());
rep->getSymbol()->setHidden();
// Conservatively set flags to prevent
// the alias manager from getting confused.
// On the next pass, it will correct the values,
// and optimize appropriately.
rep->getSymbol()->setUsed();
rep->getSymbol()->setReferenced();
if (exp->getContext() & (Expression::LValue|
Expression::RefValue|
Expression::RefParameter)) {
info.sepm[name] = rep;
}
exp = rep;
}
break;
case Expression::KindOfObjectMethodExpression:
{
FunctionCallPtr call(
static_pointer_cast<FunctionCall>(exp));
if (call->getFuncScope() == info.func) {
call->setNoInline();
}
break;
}
case Expression::KindOfSimpleFunctionCall:
{
SimpleFunctionCallPtr call(static_pointer_cast<SimpleFunctionCall>(exp));
call->addLateDependencies(ar);
call->setLocalThis(info.localThis);
if (call->getFuncScope() == info.func) {
call->setNoInline();
}
}
default:
break;
}
return exp;
}
static ExpressionPtr cloneForInline(InlineCloneInfo &info,
ExpressionPtr exp,
const std::string &prefix,
AnalysisResultConstPtr ar,
FunctionScopePtr scope) {
return cloneForInlineRecur(info, exp->clone(), prefix, ar, scope);
}
static int cloneStmtsForInline(InlineCloneInfo &info, StatementPtr s,
const std::string &prefix,
AnalysisResultConstPtr ar,
FunctionScopePtr scope) {
switch (s->getKindOf()) {
case Statement::KindOfStatementList:
{
for (int i = 0, n = s->getKidCount(); i < n; ++i) {
if (int ret = cloneStmtsForInline(info, s->getNthStmt(i),
prefix, ar, scope)) {
return ret;
}
}
return 0;
}
case Statement::KindOfExpStatement:
info.elist->addElement(cloneForInline(
info, dynamic_pointer_cast<ExpStatement>(s)->
getExpression(), prefix, ar, scope));
return 0;
case Statement::KindOfReturnStatement:
{
ExpressionPtr exp =
dynamic_pointer_cast<ReturnStatement>(s)->getRetExp();
if (exp) {
exp = cloneForInline(info, exp, prefix, ar, scope);
if (exp->hasContext(Expression::RefValue)) {
exp->clearContext(Expression::RefValue);
if (exp->isRefable()) exp->setContext(Expression::LValue);
}
info.elist->addElement(exp);
return 1;
}
return -1;
}
default:
not_reached();
}
return 1;
}
ExpressionPtr FunctionCall::inliner(AnalysisResultConstPtr ar,
ExpressionPtr obj, std::string localThis) {
FunctionScopePtr fs = getFunctionScope();
if (m_noInline || !fs || fs == m_funcScope || !m_funcScope->getStmt()) {
return ExpressionPtr();
}
BlockScope::s_jobStateMutex.lock();
if (m_funcScope->getMark() == BlockScope::MarkProcessing) {
fs->setForceRerun(true);
BlockScope::s_jobStateMutex.unlock();
return ExpressionPtr();
}
ReadLock lock(m_funcScope->getInlineMutex());
BlockScope::s_jobStateMutex.unlock();
if (!m_funcScope->getInlineAsExpr()) {
return ExpressionPtr();
}
if (m_funcScope->getInlineSameContext() &&
m_funcScope->getContainingClass() &&
m_funcScope->getContainingClass() != getClassScope()) {
/*
The function contains a context sensitive construct such as
call_user_func (context sensitive because it could call
array('parent', 'foo')) so its not safe to inline it
into a different context.
*/
return ExpressionPtr();
}
MethodStatementPtr m
(dynamic_pointer_cast<MethodStatement>(m_funcScope->getStmt()));
VariableTablePtr vt = fs->getVariables();
int nAct = m_params ? m_params->getCount() : 0;
int nMax = m_funcScope->getMaxParamCount();
if (nAct < m_funcScope->getMinParamCount() || !m->getStmts()) {
return ExpressionPtr();
}
InlineCloneInfo info(m_funcScope);
info.elist = ExpressionListPtr(new ExpressionList(
getScope(), getLocation(),
ExpressionList::ListKindWrapped));
std::ostringstream oss;
oss << fs->nextInlineIndex() << "_" << m_name << "_";
std::string prefix = oss.str();
if (obj) {
info.callWithThis = true;
if (!obj->isThis()) {
SimpleVariablePtr var
(new SimpleVariable(getScope(),
obj->getLocation(),
prefix + "this"));
var->updateSymbol(SimpleVariablePtr());
var->getSymbol()->setHidden();
var->getSymbol()->setUsed();
var->getSymbol()->setReferenced();
AssignmentExpressionPtr ae
(new AssignmentExpression(getScope(),
obj->getLocation(),
var, obj, false));
info.elist->addElement(ae);
info.sepm[var->getName()] = var;
info.localThis = var->getName();
}
} else {
if (m_classScope) {
if (!m_funcScope->isStatic()) {
ClassScopeRawPtr oCls = getOriginalClass();
FunctionScopeRawPtr oFunc = getOriginalFunction();
if (oCls && !oFunc->isStatic() &&
(oCls == m_classScope ||
oCls->derivesFrom(ar, m_className, true, false))) {
info.callWithThis = true;
info.localThis = localThis;
}
}
if (!isSelf() && !isParent() && !isStatic()) {
info.staticClass = m_className;
}
}
}
ExpressionListPtr plist = m->getParams();
int i;
for (i = 0; i < nMax || i < nAct; i++) {
ParameterExpressionPtr param
(i < nMax ?
dynamic_pointer_cast<ParameterExpression>((*plist)[i]) :
ParameterExpressionPtr());
ExpressionPtr arg = i < nAct ? (*m_params)[i] :
Clone(param->defaultValue(), getScope());
SimpleVariablePtr var
(new SimpleVariable(getScope(),
(i < nAct ? arg.get() : this)->getLocation(),
prefix + (param ?
param->getName() :
lexical_cast<string>(i))));
var->updateSymbol(SimpleVariablePtr());
var->getSymbol()->setHidden();
var->getSymbol()->setUsed();
var->getSymbol()->setReferenced();
bool ref =
(i < nMax && m_funcScope->isRefParam(i)) ||
arg->hasContext(RefParameter);
arg->clearContext(RefParameter);
AssignmentExpressionPtr ae
(new AssignmentExpression(getScope(),
arg->getLocation(),
var, arg, ref));
info.elist->addElement(ae);
if (i < nAct && (ref || !arg->isScalar())) {
info.sepm[var->getName()] = var;
}
}
if (cloneStmtsForInline(info, m->getStmts(), prefix, ar,
getFunctionScope()) <= 0) {
info.elist->addElement(makeConstant(ar, "null"));
}
if (info.sepm.size()) {
ExpressionListPtr unset_list
(new ExpressionList(getScope(), getLocation()));
for (StringToExpressionPtrMap::iterator it = info.sepm.begin(),
end = info.sepm.end(); it != end; ++it) {
ExpressionPtr var = it->second->clone();
var->clearContext((Context)(unsigned)-1);
unset_list->addElement(var);
}
ExpressionPtr unset(
new UnaryOpExpression(getScope(), getLocation(),
unset_list, T_UNSET, true));
i = info.elist->getCount();
ExpressionPtr ret = (*info.elist)[--i];
if (ret->isScalar()) {
info.elist->insertElement(unset, i);
} else {
ExpressionListPtr result_list
(new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindLeft));
if (ret->hasContext(LValue)) {
result_list->setContext(LValue);
result_list->setContext(ReturnContext);
}
result_list->addElement(ret);
result_list->addElement(unset);
(*info.elist)[i] = result_list;
}
}
recomputeEffects();
return replaceValue(info.elist);
}
ExpressionPtr FunctionCall::preOptimize(AnalysisResultConstPtr ar) {
if (m_class) updateClassName();
return ExpressionPtr();
}
ExpressionPtr FunctionCall::postOptimize(AnalysisResultConstPtr ar) {
if (m_class) updateClassName();
return ExpressionPtr();
}
///////////////////////////////////////////////////////////////////////////////
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar,
TypePtr type, bool coerce,
FunctionScopePtr func,
bool arrayParams) {
#ifdef HPHP_DETAILED_TYPE_INF_ASSERT
assert(func->hasUser(getScope(), BlockScope::UseKindCaller));
#endif /* HPHP_DETAILED_TYPE_INF_ASSERT */
ConstructPtr self = shared_from_this();
TypePtr frt;
{
TRY_LOCK(func);
func->getInferTypesMutex().assertOwnedBySelf();
assert(!func->inVisitScopes() || getScope() == func);
frt = func->getReturnType();
}
if (!frt) {
m_voidReturn = true;
setActualType(TypePtr());
if (!isUnused() && !type->is(Type::KindOfAny)) {
if (!hasContext(ReturnContext) &&
!func->isFirstPass() && !func->isAbstract()) {
if (Option::WholeProgram || !func->getContainingClass() ||
func->isStatic() || func->isFinal() || func->isPrivate()) {
Compiler::Error(Compiler::UseVoidReturn, self);
}
}
if (!Type::IsMappedToVariant(type)) {
setExpectedType(type);
}
m_voidWrapper = true;
}
} else {
m_voidReturn = false;
m_voidWrapper = false;
type = checkTypesImpl(ar, type, frt, coerce);
assert(m_actualType);
}
if (arrayParams) {
m_extraArg = 0;
(*m_params)[0]->inferAndCheck(ar, Type::Array, false);
} else {
m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid);
}
m_variableArgument = func->isVariableArgument();
if (m_valid) {
m_implementedType.reset();
} else {
m_implementedType = Type::Variant;
}
assert(type);
return type;
}