Arquivos
hhvm/hphp/compiler/expression/simple_function_call.cpp
T
2013-02-13 06:44:30 -08:00

2578 linhas
88 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/simple_function_call.h>
#include <compiler/analysis/file_scope.h>
#include <compiler/analysis/function_scope.h>
#include <compiler/analysis/class_scope.h>
#include <compiler/analysis/code_error.h>
#include <compiler/expression/expression_list.h>
#include <compiler/expression/scalar_expression.h>
#include <compiler/expression/constant_expression.h>
#include <compiler/expression/assignment_expression.h>
#include <compiler/expression/array_pair_expression.h>
#include <compiler/expression/array_element_expression.h>
#include <compiler/expression/unary_op_expression.h>
#include <compiler/expression/parameter_expression.h>
#include <compiler/statement/method_statement.h>
#include <compiler/analysis/constant_table.h>
#include <compiler/analysis/variable_table.h>
#include <util/util.h>
#include <compiler/option.h>
#include <compiler/expression/simple_variable.h>
#include <compiler/parser/parser.h>
#include <runtime/base/complex_types.h>
#include <runtime/base/externals.h>
#include <runtime/base/execution_context.h>
#include <runtime/base/array/array_init.h>
#include <runtime/base/string_util.h>
#include <runtime/ext/ext_variable.h>
using namespace HPHP;
///////////////////////////////////////////////////////////////////////////////
// statics
std::map<std::string, int> SimpleFunctionCall::FunctionTypeMap;
void SimpleFunctionCall::InitFunctionTypeMap() {
if (FunctionTypeMap.empty()) {
FunctionTypeMap["define"] = DefineFunction;
FunctionTypeMap["create_function"] = CreateFunction;
FunctionTypeMap["func_get_arg"] = VariableArgumentFunction;
FunctionTypeMap["func_get_args"] = VariableArgumentFunction;
FunctionTypeMap["func_num_args"] = VariableArgumentFunction;
FunctionTypeMap["extract"] = ExtractFunction;
FunctionTypeMap["compact"] = CompactFunction;
FunctionTypeMap["shell_exec"] = ShellExecFunction;
FunctionTypeMap["exec"] = ShellExecFunction;
FunctionTypeMap["passthru"] = ShellExecFunction;
FunctionTypeMap["system"] = ShellExecFunction;
FunctionTypeMap["defined"] = DefinedFunction;
FunctionTypeMap["function_exists"] = FunctionExistsFunction;
FunctionTypeMap["class_exists"] = ClassExistsFunction;
FunctionTypeMap["interface_exists"] = InterfaceExistsFunction;
FunctionTypeMap["constant"] = ConstantFunction;
FunctionTypeMap["unserialize"] = UnserializeFunction;
FunctionTypeMap["apc_fetch"] = UnserializeFunction;
FunctionTypeMap["get_defined_vars"] = GetDefinedVarsFunction;
FunctionTypeMap["fb_call_user_func_safe"] = FBCallUserFuncSafeFunction;
FunctionTypeMap["fb_call_user_func_array_safe"] =
FBCallUserFuncSafeFunction;
FunctionTypeMap["fb_call_user_func_safe_return"] =
FBCallUserFuncSafeFunction;
}
}
static class FunctionTypeMapInitializer {
public:
FunctionTypeMapInitializer() {
SimpleFunctionCall::InitFunctionTypeMap();
}
} s_function_type_map_initializer;
///////////////////////////////////////////////////////////////////////////////
// constructors/destructors
SimpleFunctionCall::SimpleFunctionCall
(EXPRESSION_CONSTRUCTOR_PARAMETERS,
const std::string &name, ExpressionListPtr params, ExpressionPtr cls)
: FunctionCall(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(SimpleFunctionCall),
ExpressionPtr(), name, params, cls),
m_type(UnknownType), m_dynamicConstant(false),
m_builtinFunction(false), m_noPrefix(false), m_fromCompiler(false),
m_dynamicInvoke(false), m_transformed(false), m_no_volatile_check(false),
m_safe(0), m_extra(NULL) {
if (!m_class && m_className.empty()) {
m_dynamicInvoke = Option::DynamicInvokeFunctions.find(m_name) !=
Option::DynamicInvokeFunctions.end();
std::map<string, int>::const_iterator iter =
FunctionTypeMap.find(m_name);
if (iter != FunctionTypeMap.end()) {
m_type = iter->second;
}
}
}
ExpressionPtr SimpleFunctionCall::clone() {
SimpleFunctionCallPtr exp(new SimpleFunctionCall(*this));
deepCopy(exp);
return exp;
}
void SimpleFunctionCall::deepCopy(SimpleFunctionCallPtr exp) {
FunctionCall::deepCopy(exp);
exp->m_safeDef = Clone(m_safeDef);
}
///////////////////////////////////////////////////////////////////////////////
// parser functions
void SimpleFunctionCall::onParse(AnalysisResultConstPtr ar, FileScopePtr fs) {
StaticClassName::onParse(ar, fs);
ConstructPtr self = shared_from_this();
switch (m_type) {
case DefineFunction:
if (Option::ParseTimeOpts && m_params &&
unsigned(m_params->getCount() - 2) <= 1u) {
// need to register the constant before AnalyzeAll, so that
// DefinedFunction can mark this volatile
ExpressionPtr ename = (*m_params)[0];
if (ConstantExpressionPtr cname =
dynamic_pointer_cast<ConstantExpression>(ename)) {
/*
Hack: If the name of the constant being defined is itself
a constant expression, assume that its not yet defined.
So define(FOO, 'bar') is equivalent to define('FOO', 'bar').
*/
ename = makeScalarExpression(ar, cname->getName());
m_params->removeElement(0);
m_params->insertElement(ename);
}
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>(ename);
if (name) {
string varName = name->getIdentifier();
if (varName.empty()) break;
AnalysisResult::Locker lock(ar);
fs->declareConstant(lock.get(), varName);
// handling define("CONSTANT", ...);
ExpressionPtr value = (*m_params)[1];
BlockScopePtr block = lock->findConstantDeclarer(varName);
ConstantTablePtr constants = block->getConstants();
if (constants != ar->getConstants()) {
constants->add(varName, Type::Some, value, ar, self);
}
}
}
break;
case CreateFunction:
if (Option::ParseTimeOpts &&
m_params->getCount() == 2 &&
(*m_params)[0]->isLiteralString() &&
(*m_params)[1]->isLiteralString()) {
string params = (*m_params)[0]->getLiteralString();
string body = (*m_params)[1]->getLiteralString();
m_lambda = CodeGenerator::GetNewLambda();
string code = "function " + m_lambda + "(" + params + ") "
"{" + body + "}";
m_lambda = "1_" + m_lambda;
ar->appendExtraCode(fs->getName(), code);
}
break;
case VariableArgumentFunction:
/*
Note:
At this point, we dont have a function scope, so we set
the flags on the FileScope.
The FileScope maintains a stack of attributes, so that
it correctly handles each function.
But note that later phases should set/get the attribute
directly on the FunctionScope, rather than on the FileScope
*/
fs->setAttribute(FileScope::VariableArgument);
break;
case ExtractFunction:
fs->setAttribute(FileScope::ContainsLDynamicVariable);
fs->setAttribute(FileScope::ContainsExtract);
break;
case CompactFunction: {
// If all the parameters in the compact() call are statically known,
// there is no need to create a variable table.
vector<ExpressionPtr> literals;
if (!Option::OutputHHBC && m_params->flattenLiteralStrings(literals)) {
m_type = StaticCompactFunction;
m_params->clearElements();
for (unsigned i = 0; i < literals.size(); i++) {
m_params->addElement(literals[i]);
}
} else {
fs->setAttribute(FileScope::ContainsDynamicVariable);
}
fs->setAttribute(FileScope::ContainsCompact);
break;
}
case GetDefinedVarsFunction:
fs->setAttribute(FileScope::ContainsDynamicVariable);
fs->setAttribute(FileScope::ContainsGetDefinedVars);
fs->setAttribute(FileScope::ContainsCompact);
break;
case UnknownType:
if (!m_class && m_className.empty()) {
ar->parseOnDemandByFunction(m_name);
}
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////
// static analysis functions
void SimpleFunctionCall::addDependencies(AnalysisResultPtr ar) {
if (!m_class) {
if (m_className.empty()) {
addUserFunction(ar, m_name);
} else if ((!isParent() && !isSelf()) ||
getOriginalScope() != getScope()) {
addUserClass(ar, m_className);
}
}
}
void SimpleFunctionCall::setupScopes(AnalysisResultConstPtr ar) {
FunctionScopePtr func;
if (!m_class && m_className.empty()) {
if (!m_dynamicInvoke) {
bool namespaced = (m_name[0] == '\\');
if (namespaced) {
m_name = m_name.substr(1);
}
func = ar->findFunction(m_name);
if (!func && namespaced) {
int pos = m_name.rfind('\\');
m_name = m_name.substr(pos + 1);
func = ar->findFunction(m_name);
}
}
} else {
ClassScopePtr cls = resolveClass();
if (cls) {
m_classScope = cls;
if (m_name == "__construct") {
func = cls->findConstructor(ar, true);
} else {
func = cls->findFunction(ar, m_name, true, true);
}
}
}
if (func && !func->isRedeclaring()) {
if (m_funcScope != func) {
m_funcScope = func;
assert(ar->getPhase() != AnalysisResult::FirstInference);
Construct::recomputeEffects();
m_funcScope->addCaller(getScope());
}
}
}
void SimpleFunctionCall::addLateDependencies(AnalysisResultConstPtr ar) {
m_funcScope.reset();
m_classScope.reset();
setupScopes(ar);
}
ConstructPtr SimpleFunctionCall::getNthKid(int n) const {
if (n == 1) return m_safeDef;
return FunctionCall::getNthKid(n);
}
void SimpleFunctionCall::setNthKid(int n, ConstructPtr cp) {
if (n == 1) {
m_safeDef = boost::dynamic_pointer_cast<Expression>(cp);
} else {
FunctionCall::setNthKid(n, cp);
}
}
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
FunctionCall::analyzeProgram(ar);
if (m_class) {
if (!Option::AllDynamic) {
setDynamicByIdentifier(ar, m_name);
}
} else if (ar->getPhase() >= AnalysisResult::AnalyzeAll) {
addDependencies(ar);
}
if (m_safeDef) m_safeDef->analyzeProgram(ar);
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
ConstructPtr self = shared_from_this();
// Look up the corresponding FunctionScope and ClassScope
// for this function call
m_funcScope.reset();
m_classScope.reset();
setupScopes(ar);
if (m_funcScope && m_funcScope->getOptFunction()) {
SimpleFunctionCallPtr self(
static_pointer_cast<SimpleFunctionCall>(shared_from_this()));
(m_funcScope->getOptFunction())(0, ar, self, 1);
}
if (!m_class && !m_className.empty()) {
if (Option::DynamicInvokeFunctions.find(
Util::toLower(m_className + "::" + m_name)) !=
Option::DynamicInvokeFunctions.end()) {
setNoInline();
}
}
// check for dynamic constant and volatile function/class
if (!m_class && m_className.empty() &&
(m_type == DefinedFunction ||
m_type == FunctionExistsFunction ||
m_type == FBCallUserFuncSafeFunction ||
m_type == ClassExistsFunction ||
m_type == InterfaceExistsFunction) &&
m_params && m_params->getCount() >= 1) {
ExpressionPtr value = (*m_params)[0];
if (value->isScalar()) {
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>(value);
if (name && name->isLiteralString()) {
string symbol = name->getLiteralString();
switch (m_type) {
case DefinedFunction: {
ConstantTablePtr constants = ar->getConstants();
if (!constants->isPresent(symbol)) {
// user constant
BlockScopePtr block = ar->findConstantDeclarer(symbol);
if (block) { // found the constant
constants = block->getConstants();
// set to be dynamic
if (m_type == DefinedFunction) {
constants->setDynamic(ar, symbol, true);
}
}
}
break;
}
case FBCallUserFuncSafeFunction:
case FunctionExistsFunction:
if (!m_no_volatile_check) {
FunctionScopePtr func = ar->findFunction(Util::toLower(symbol));
if (func && func->isUserFunction()) {
func->setVolatile();
}
break;
}
case InterfaceExistsFunction:
case ClassExistsFunction:
if (!m_no_volatile_check) {
ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
if (cls && cls->isUserClass()) {
cls->setVolatile();
}
break;
}
default:
assert(false);
}
}
} else if ((m_type == InterfaceExistsFunction ||
m_type == ClassExistsFunction) &&
value->is(KindOfSimpleVariable)) {
SimpleVariablePtr name = dynamic_pointer_cast<SimpleVariable>(value);
if (name && name->getSymbol()) {
// name is checked as class name
name->getSymbol()->setClassName();
}
}
}
if (m_type == StaticCompactFunction) {
FunctionScopePtr fs = getFunctionScope();
VariableTablePtr vt = fs->getVariables();
if (vt->isPseudoMainTable() ||
vt->getAttribute(VariableTable::ContainsDynamicVariable)) {
// When there is a variable table already, we will keep the ordinary
// compact() call.
m_type = CompactFunction;
} else {
// compact('a', 'b', 'c') becomes compact('a', $a, 'b', $b, 'c', $c)
vector<ExpressionPtr> new_params;
vector<string> strs;
for (int i = 0; i < m_params->getCount(); i++) {
ExpressionPtr e = (*m_params)[i];
always_assert(e->isLiteralString());
string name = e->getLiteralString();
// no need to record duplicate names
bool found = false;
for (unsigned j = 0; j < strs.size(); j++) {
if (strcasecmp(name.data(), strs[j].data()) == 0) {
found = true;
break;
}
}
if (found) continue;
strs.push_back(name);
SimpleVariablePtr var(new SimpleVariable(
e->getScope(), e->getLocation(), name));
var->copyContext(e);
var->updateSymbol(SimpleVariablePtr());
new_params.push_back(e);
new_params.push_back(var);
}
m_params->clearElements();
for (unsigned i = 0; i < new_params.size(); i++) {
m_params->addElement(new_params[i]);
}
}
}
if (m_type == UnserializeFunction) {
ar->forceClassVariants(getOriginalClass(), false);
}
if (m_params) {
markRefParams(m_funcScope, m_name, canInvokeFewArgs());
}
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
if (!m_fromCompiler && !m_noPrefix && m_type == UnknownType &&
!m_class && !m_redeclared && !m_dynamicInvoke && !m_funcScope &&
(m_className.empty() ||
(m_classScope &&
!m_classScope->isTrait() &&
!m_classScope->derivesFromRedeclaring() &&
!m_classScope->getAttribute(
ClassScope::HasUnknownStaticMethodHandler) &&
!m_classScope->getAttribute(
ClassScope::InheritsUnknownStaticMethodHandler)))) {
bool ok = false;
if (m_classScope && getOriginalClass()) {
FunctionScopeRawPtr fs = getOriginalFunction();
if (fs && !fs->isStatic() &&
(m_classScope->getAttribute(
ClassScope::HasUnknownMethodHandler) ||
m_classScope->getAttribute(
ClassScope::InheritsUnknownMethodHandler))) {
ok = true;
}
}
if (!ok) {
Compiler::Error(Compiler::UnknownFunction, shared_from_this());
}
}
}
}
bool SimpleFunctionCall::readsLocals() const {
return m_type == GetDefinedVarsFunction ||
m_type == CompactFunction;
}
bool SimpleFunctionCall::writesLocals() const {
return m_type == ExtractFunction;
}
void SimpleFunctionCall::updateVtFlags() {
FunctionScopeRawPtr f = getFunctionScope();
if (f) {
if (m_funcScope) {
if (m_funcScope->getContextSensitive()) {
f->setInlineSameContext(true);
}
if ((m_classScope && (isSelf() || isParent()) &&
m_funcScope->usesLSB()) ||
isStatic() ||
m_type == FBCallUserFuncSafeFunction ||
m_name == "call_user_func" ||
m_name == "call_user_func_array" ||
m_name == "forward_static_call" ||
m_name == "forward_static_call_array" ||
m_name == "hphp_create_continuation" ||
m_name == "get_called_class") {
f->setNextLSB(true);
}
}
}
if (m_type != UnknownType) {
VariableTablePtr vt = getScope()->getVariables();
switch (m_type) {
case ExtractFunction:
vt->setAttribute(VariableTable::ContainsLDynamicVariable);
vt->setAttribute(VariableTable::ContainsExtract);
break;
case CompactFunction:
vt->setAttribute(VariableTable::ContainsDynamicVariable);
case StaticCompactFunction:
vt->setAttribute(VariableTable::ContainsCompact);
break;
case GetDefinedVarsFunction:
vt->setAttribute(VariableTable::ContainsDynamicVariable);
vt->setAttribute(VariableTable::ContainsGetDefinedVars);
vt->setAttribute(VariableTable::ContainsCompact);
break;
}
}
}
bool SimpleFunctionCall::isCallToFunction(const char *name) const {
return !strcasecmp(getName().c_str(), name) &&
!getClass() && getClassName().empty();
}
bool SimpleFunctionCall::isCompilerCallToFunction(const char *name) const {
return m_fromCompiler && isCallToFunction(name);
}
bool SimpleFunctionCall::isSimpleDefine(StringData **outName,
TypedValue *outValue) const {
if (!isCallToFunction("define")) return false;
if (!m_params || m_params->getCount() != 2) return false;
Variant v;
if (!(*m_params)[0]->getScalarValue(v) || !v.isString()) return false;
if (outName) {
*outName = StringData::GetStaticString(v.toCStrRef().get());
}
if (!(*m_params)[1]->getScalarValue(v) || v.isArray()) return false;
if (outValue) {
if (v.isString()) {
v = StringData::GetStaticString(v.toCStrRef().get());
}
*outValue = *v.asTypedValue();
}
return true;
}
bool SimpleFunctionCall::isDefineWithoutImpl(AnalysisResultConstPtr ar) {
if (Option::OutputHHBC) return false;
if (m_class || !m_className.empty()) return false;
if (m_type == DefineFunction && m_params &&
unsigned(m_params->getCount() - 2) <= 1u) {
if (m_dynamicConstant) return false;
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
if (!name) return false;
string varName = name->getIdentifier();
if (varName.empty()) return false;
if (ar->isSystemConstant(varName)) {
always_assert(!m_extra);
return true;
}
ExpressionPtr value = (*m_params)[1];
if (ar->isConstantRedeclared(varName)) {
return false;
}
Variant scalarValue;
return (value->isScalar() &&
value->getScalarValue(scalarValue) &&
scalarValue.isAllowedAsConstantValue());
} else {
return false;
}
}
ExpressionPtr SimpleFunctionCall::optimize(AnalysisResultConstPtr ar) {
if (m_class || !m_funcScope ||
(!m_className.empty() && (!m_classScope || !isPresent()))) {
return ExpressionPtr();
}
if (!m_funcScope->isUserFunction()) {
if (m_type == ExtractFunction && m_params && m_params->getCount() >= 1) {
ExpressionPtr vars = (*m_params)[0];
while (vars) {
if (vars->is(KindOfUnaryOpExpression) &&
static_pointer_cast<UnaryOpExpression>(vars)->getOp() == T_ARRAY) {
break;
}
if (vars->is(KindOfExpressionList)) {
vars = static_pointer_cast<ExpressionList>(vars)->listValue();
} else {
vars = vars->getCanonPtr();
}
}
if (vars) {
bool svar = vars->isScalar();
if (!svar && getScope()->getUpdated()) {
/*
* kind of a hack. If the extract param is non-scalar,
* and we've made changes already, dont try to optimize yet.
* this gives us a better chance of getting a scalar result.
* Later, we should add more array optimizations, which would
* allow us to optimize the generated code once the scalar
* expressions are resolved
*/
return ExpressionPtr();
}
int n = m_params->getCount();
String prefix;
int mode = EXTR_OVERWRITE;
if (n >= 2) {
Variant v;
ExpressionPtr m = (*m_params)[1];
if (m->isScalar() && m->getScalarValue(v)) {
mode = v.toInt64();
} else {
mode = -1;
}
if (n >= 3) {
ExpressionPtr p = (*m_params)[2];
if (p->isScalar() && p->getScalarValue(v)) {
prefix = v.toString();
} else {
mode = -1;
}
}
}
bool ref = mode & EXTR_REFS;
mode &= ~EXTR_REFS;
switch (mode) {
case EXTR_PREFIX_ALL:
case EXTR_PREFIX_INVALID:
case EXTR_OVERWRITE: {
ExpressionListPtr arr(
static_pointer_cast<ExpressionList>(
static_pointer_cast<UnaryOpExpression>(vars)->getExpression()));
ExpressionListPtr rep(
new ExpressionList(getScope(), getLocation(),
ExpressionList::ListKindWrapped));
string root_name;
int n = arr ? arr->getCount() : 0;
int i, j, k;
for (i = j = k = 0; i < n; i++) {
ArrayPairExpressionPtr ap(
dynamic_pointer_cast<ArrayPairExpression>((*arr)[i]));
always_assert(ap);
String name;
Variant voff;
if (!ap->getName()) {
voff = j++;
} else {
if (!ap->getName()->isScalar() ||
!ap->getName()->getScalarValue(voff)) {
return ExpressionPtr();
}
}
name = voff.toString();
if (mode == EXTR_PREFIX_ALL ||
(mode == EXTR_PREFIX_INVALID &&
!name.isValidVariableName())) {
name = prefix + "_" + name;
}
if (!name.isValidVariableName()) continue;
SimpleVariablePtr var(
new SimpleVariable(getScope(), getLocation(), name.data()));
var->updateSymbol(SimpleVariablePtr());
ExpressionPtr val(ap->getValue());
if (!val->isScalar()) {
if (root_name.empty()) {
root_name = "t" + lexical_cast<string>(
getFunctionScope()->nextInlineIndex());
SimpleVariablePtr rv(
new SimpleVariable(getScope(), getLocation(), root_name));
rv->updateSymbol(SimpleVariablePtr());
rv->getSymbol()->setHidden();
ExpressionPtr root(
new AssignmentExpression(getScope(), getLocation(),
rv, (*m_params)[0], false));
rep->insertElement(root);
}
SimpleVariablePtr rv(
new SimpleVariable(getScope(), getLocation(), root_name));
rv->updateSymbol(SimpleVariablePtr());
rv->getSymbol()->setHidden();
ExpressionPtr offset(makeScalarExpression(ar, voff));
val = ExpressionPtr(
new ArrayElementExpression(getScope(), getLocation(),
rv, offset));
}
ExpressionPtr a(
new AssignmentExpression(getScope(), getLocation(),
var, val, ref));
rep->addElement(a);
k++;
}
if (root_name.empty()) {
if ((*m_params)[0]->hasEffect()) {
rep->insertElement((*m_params)[0]);
}
} else {
ExpressionListPtr unset_list
(new ExpressionList(getScope(), getLocation()));
SimpleVariablePtr rv(
new SimpleVariable(getScope(), getLocation(), root_name));
rv->updateSymbol(SimpleVariablePtr());
unset_list->addElement(rv);
ExpressionPtr unset(
new UnaryOpExpression(getScope(), getLocation(),
unset_list, T_UNSET, true));
rep->addElement(unset);
}
rep->addElement(makeScalarExpression(ar, k));
return replaceValue(rep);
}
default: break;
}
}
}
}
if (!m_classScope && !m_funcScope->isUserFunction()) {
if (m_type == UnknownType && m_funcScope->isFoldable()) {
Array arr;
if (m_params) {
if (!m_params->isScalar()) return ExpressionPtr();
for (int i = 0, n = m_params->getCount(); i < n; ++i) {
Variant v;
if (!(*m_params)[i]->getScalarValue(v)) return ExpressionPtr();
arr.set(i, v);
}
if (m_arrayParams) {
arr = arr[0];
}
}
try {
g_context->setThrowAllErrors(true);
Variant v = invoke_builtin(m_funcScope->getName().c_str(),
arr, -1, true);
g_context->setThrowAllErrors(false);
return makeScalarExpression(ar, v);
} catch (...) {
g_context->setThrowAllErrors(false);
}
return ExpressionPtr();
}
if (m_funcScope->getOptFunction()) {
SimpleFunctionCallPtr self(
static_pointer_cast<SimpleFunctionCall>(shared_from_this()));
ExpressionPtr e = (m_funcScope->getOptFunction())(0, ar, self, 0);
if (e) return e;
}
}
if (m_type != UnknownType || m_safe) {
return ExpressionPtr();
}
return inliner(ar, ExpressionPtr(), m_localThis);
}
ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultConstPtr ar) {
if (!Option::ParseTimeOpts) return ExpressionPtr();
if (m_class) updateClassName();
if (ar->getPhase() < AnalysisResult::FirstPreOptimize) {
return ExpressionPtr();
}
if (ExpressionPtr rep = optimize(ar)) {
return rep;
}
if (!m_class && m_className.empty() &&
(m_type == DefineFunction ||
m_type == DefinedFunction ||
m_type == FBCallUserFuncSafeFunction ||
m_type == FunctionExistsFunction ||
m_type == ClassExistsFunction ||
m_type == InterfaceExistsFunction) &&
m_params &&
(m_type == DefineFunction ?
unsigned(m_params->getCount() - 2) <= 1u :
m_type == FBCallUserFuncSafeFunction ? m_params->getCount() >= 1 :
m_params->getCount() == 1)) {
ExpressionPtr value = (*m_params)[0];
if (value->isScalar()) {
ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value);
if (name && name->isLiteralString()) {
string symbol = name->getLiteralString();
switch (m_type) {
case DefineFunction: {
ConstantTableConstPtr constants = ar->getConstants();
// system constant
if (constants->isPresent(symbol)) {
break;
}
// user constant
BlockScopeConstPtr block = ar->findConstantDeclarer(symbol);
// not found (i.e., undefined)
if (!block) break;
constants = block->getConstants();
const Symbol *sym = constants->getSymbol(symbol);
always_assert(sym);
m_extra = (void *)sym;
Lock lock(BlockScope::s_constMutex);
if (!sym->isDynamic()) {
if (sym->getValue() != (*m_params)[1]) {
if (sym->getDeclaration() != shared_from_this()) {
// redeclared
const_cast<Symbol*>(sym)->setDynamic();
}
const_cast<Symbol*>(sym)->setValue((*m_params)[1]);
getScope()->addUpdates(BlockScope::UseKindConstRef);
}
Variant v;
ExpressionPtr value =
static_pointer_cast<Expression>(sym->getValue());
if (value->getScalarValue(v)) {
if (!v.isAllowedAsConstantValue()) {
const_cast<Symbol*>(sym)->setDynamic();
}
}
}
break;
}
case DefinedFunction: {
if (symbol == "false" ||
symbol == "true" ||
symbol == "null") {
return CONSTANT("true");
}
ConstantTableConstPtr constants = ar->getConstants();
// system constant
if (constants->isPresent(symbol) && !constants->isDynamic(symbol)) {
return CONSTANT("true");
}
// user constant
BlockScopeConstPtr block = ar->findConstantDeclarer(symbol);
// not found (i.e., undefined)
if (!block) {
if (symbol.find("::") == std::string::npos &&
Option::WholeProgram) {
return CONSTANT("false");
} else {
// e.g., defined("self::ZERO")
break;
}
}
constants = block->getConstants();
// already set to be dynamic
if (constants->isDynamic(symbol)) return ExpressionPtr();
Lock lock(BlockScope::s_constMutex);
ConstructPtr decl = constants->getValue(symbol);
ExpressionPtr constValue = dynamic_pointer_cast<Expression>(decl);
if (constValue->isScalar()) {
return CONSTANT("true");
}
break;
}
case FBCallUserFuncSafeFunction:
case FunctionExistsFunction: {
const std::string &lname = Util::toLower(symbol);
if (Option::DynamicInvokeFunctions.find(lname) ==
Option::DynamicInvokeFunctions.end()) {
FunctionScopePtr func = ar->findFunction(lname);
if (!func) {
if (m_type == FunctionExistsFunction &&
Option::WholeProgram) {
return CONSTANT("false");
}
break;
}
if (!m_no_volatile_check && func->isUserFunction()) {
func->setVolatile();
}
if (!func->isVolatile() && m_type == FunctionExistsFunction) {
return CONSTANT("true");
}
}
break;
}
case InterfaceExistsFunction: {
ClassScopePtrVec classes = ar->findClasses(Util::toLower(symbol));
bool interfaceFound = false;
for (ClassScopePtrVec::const_iterator it = classes.begin();
it != classes.end(); ++it) {
ClassScopePtr cls = *it;
if (!m_no_volatile_check && cls->isUserClass()) {
cls->setVolatile();
}
if (cls->isInterface()) {
interfaceFound = true;
}
}
if (!interfaceFound) {
if (Option::WholeProgram) {
return CONSTANT("false");
}
break;
}
if (classes.size() == 1 && !classes.back()->isVolatile()) {
return CONSTANT("true");
}
break;
}
case ClassExistsFunction: {
ClassScopePtrVec classes = ar->findClasses(Util::toLower(symbol));
bool classFound = false;
for (ClassScopePtrVec::const_iterator it = classes.begin();
it != classes.end(); ++it) {
ClassScopePtr cls = *it;
if (!m_no_volatile_check && cls->isUserClass()) {
cls->setVolatile();
}
if (!cls->isInterface() && !cls->isTrait()) {
classFound = true;
}
}
if (!classFound) {
if (Option::WholeProgram) {
return CONSTANT("false");
}
break;
}
if (classes.size() == 1 && !classes.back()->isVolatile()) {
return CONSTANT("true");
}
break;
}
default:
assert(false);
}
}
}
}
return ExpressionPtr();
}
ExpressionPtr SimpleFunctionCall::postOptimize(AnalysisResultConstPtr ar) {
if (!Option::KeepStatementsWithNoEffect && isDefineWithoutImpl(ar)) {
Construct::recomputeEffects();
if (m_extra) {
Symbol *sym = (Symbol *)m_extra;
Lock lock(BlockScope::s_constMutex);
sym->setReplaced();
}
return m_extra ? CONSTANT("true") : CONSTANT("false");
}
if (m_type == StaticCompactFunction) {
for (int i = 0; i < m_params->getCount(); i += 2) {
ExpressionPtr e = (*m_params)[i + 1];
if (e->is(KindOfUnaryOpExpression) &&
static_pointer_cast<UnaryOpExpression>(e)->getOp() == T_UNSET_CAST) {
m_params->removeElement(i);
m_params->removeElement(i);
i -= 2;
m_extraArg -= 2;
if (m_extraArg < 0) m_extraArg = 0;
}
}
if (!m_params->getCount()) {
ExpressionPtr rep(new UnaryOpExpression(getScope(), getLocation(),
ExpressionPtr(), T_ARRAY, true));
return replaceValue(rep);
}
m_params->resetOutputCount();
}
/*
Dont do this for now. Need to take account of newly created
variables etc (which would normally be handled by inferTypes).
if (ExpressionPtr rep = optimize(ar)) {
return rep;
}
*/
return FunctionCall::postOptimize(ar);
}
int SimpleFunctionCall::getLocalEffects() const {
if (m_class) return UnknownEffect;
if (m_funcScope && !m_funcScope->hasEffect()) {
return 0;
}
return UnknownEffect;
}
TypePtr SimpleFunctionCall::inferTypes(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
assert(false);
return TypePtr();
}
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
bool coerce) {
assert(type);
IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());
resetTypes();
reset();
if (m_class) {
m_class->inferAndCheck(ar, Type::Any, false);
}
if (m_safeDef) {
m_safeDef->inferAndCheck(ar, Type::Any, false);
}
if (m_safe) {
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
}
ConstructPtr self = shared_from_this();
// handling define("CONSTANT", ...);
if (!m_class && m_className.empty()) {
if (m_type == DefineFunction && m_params &&
unsigned(m_params->getCount() - 2) <= 1u) {
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
if (name) {
string varName = name->getIdentifier();
if (!varName.empty()) {
ExpressionPtr value = (*m_params)[1];
TypePtr varType = value->inferAndCheck(ar, Type::Some, false);
BlockScopePtr block;
bool newlyDeclared = false;
{
Lock lock(ar->getMutex());
block = ar->findConstantDeclarer(varName);
if (!block) {
FileScopeRawPtr fs(getFileScope());
GET_LOCK(fs); // file scope cannot depend on a function scope
fs->declareConstant(ar, varName);
block = ar->findConstantDeclarer(varName);
newlyDeclared = true;
}
}
assert(block);
ConstantTablePtr constants = block->getConstants();
if (constants != ar->getConstants()) {
TRY_LOCK(block);
if (value && !value->isScalar()) {
constants->setDynamic(ar, varName, true);
varType = Type::Variant;
}
if (constants->isDynamic(varName)) {
m_dynamicConstant = true;
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
} else {
if (newlyDeclared) {
const Symbol *sym = constants->getSymbol(varName);
assert(!sym || !sym->declarationSet());
constants->add(varName, varType, value, ar, self);
sym = constants->getSymbol(varName);
always_assert(sym);
m_extra = (void *)sym;
} else {
constants->setType(ar, varName, varType, true);
}
}
// in case the old 'value' has been optimized
constants->setValue(ar, varName, value);
} else {
always_assert(!newlyDeclared);
}
m_valid = true;
return checkTypesImpl(ar, type, Type::Boolean, coerce);
}
}
if (getScope()->isFirstPass()) {
Compiler::Error(Compiler::BadDefine, self);
}
} else if (m_type == ExtractFunction || m_type == GetDefinedVarsFunction) {
getScope()->getVariables()->forceVariants(ar, VariableTable::AnyVars);
}
}
FunctionScopePtr func;
if (!m_class && m_className.empty()) {
if (!m_dynamicInvoke) {
func = ar->findFunction(m_name);
}
} else {
ClassScopePtr cls = resolveClassWithChecks();
if (!cls) {
if (m_params) {
m_params->inferAndCheck(ar, Type::Some, false);
markRefParams(FunctionScopePtr(), m_name, canInvokeFewArgs());
}
return checkTypesImpl(ar, type, Type::Variant, coerce);
}
m_classScope = cls;
if (m_name == "__construct") {
// if the class is known, php will try to identify class-name ctor
func = cls->findConstructor(ar, true);
} else {
func = cls->findFunction(ar, m_name, true, true);
}
if (func && !func->isStatic()) {
ClassScopePtr clsThis = getOriginalClass();
FunctionScopePtr funcThis = getOriginalFunction();
if (!Option::AllDynamic &&
(!clsThis ||
(clsThis != m_classScope &&
!clsThis->derivesFrom(ar, m_className, true, false)) ||
funcThis->isStatic())) {
func->setDynamic();
}
}
}
if (!func || func->isRedeclaring() || func->isAbstract()) {
if (m_funcScope) {
m_funcScope.reset();
Construct::recomputeEffects();
}
if (func && func->isRedeclaring()) {
m_redeclared = true;
getScope()->getVariables()->
setAttribute(VariableTable::NeedGlobalPointer);
}
if (m_params) {
if (func && func->isRedeclaring()) {
FunctionScope::FunctionInfoPtr info =
FunctionScope::GetFunctionInfo(m_name);
always_assert(info);
for (int i = m_params->getCount(); i--; ) {
if (!Option::WholeProgram || info->isRefParam(i)) {
m_params->markParam(i, canInvokeFewArgs());
}
}
}
if (m_arrayParams) {
(*m_params)[0]->inferAndCheck(ar, Type::Array, false);
} else {
m_params->inferAndCheck(ar, Type::Some, false);
}
}
return checkTypesImpl(ar, type, Type::Variant, coerce);
} else if (func != m_funcScope) {
assert(!m_funcScope ||
!func->hasUser(getScope(), BlockScope::UseKindCaller));
m_funcScope = func;
m_funcScope->addCaller(getScope(), !type->is(Type::KindOfAny));
Construct::recomputeEffects();
}
m_builtinFunction = (!func->isUserFunction() || func->isSepExtension());
beforeCheck(ar);
m_valid = true;
TypePtr rtype = checkParamsAndReturn(ar, type, coerce,
func, m_arrayParams);
// this is ok un-guarded b/c this value never gets un-set (once its
// true its always true) and the value itself doesn't get read
// until outputCPP time
if (m_arrayParams && func && !m_builtinFunction) func->setDirectInvoke();
if (m_safe) {
TypePtr atype = getActualType();
if (m_safe > 0 && !m_safeDef) {
atype = Type::Array;
} else if (!m_safeDef) {
atype = Type::Variant;
} else {
TypePtr t = m_safeDef->getActualType();
if (!t || !atype || !Type::SameType(t, atype)) {
atype = Type::Variant;
}
}
rtype = checkTypesImpl(ar, type, atype, coerce);
m_voidReturn = m_voidWrapper = false;
}
if (m_valid && !m_className.empty() &&
(!m_funcScope || !m_funcScope->isStatic())) {
int objCall = checkObjCall(ar);
if (objCall <= 0 || !m_localThis.empty()) {
m_implementedType = Type::Variant;
}
}
assert(rtype);
return rtype;
}
///////////////////////////////////////////////////////////////////////////////
// code generation functions
void SimpleFunctionCall::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
outputLineMap(cg, ar);
if (m_class || !m_className.empty()) {
StaticClassName::outputPHP(cg, ar);
cg_printf("::%s(", m_origName.c_str());
} else {
if (cg.getOutput() == CodeGenerator::InlinedPHP ||
cg.getOutput() == CodeGenerator::TrimmedPHP) {
if (cg.getOutput() == CodeGenerator::TrimmedPHP &&
cg.usingStream(CodeGenerator::PrimaryStream) &&
Option::DynamicFunctionCalls.find(m_name) !=
Option::DynamicFunctionCalls.end()) {
int funcNamePos = Option::DynamicFunctionCalls[m_name];
if (m_params && m_params->getCount() &&
m_params->getCount() >= funcNamePos + 1) {
if (funcNamePos == -1) funcNamePos = m_params->getCount() - 1;
ExpressionPtr funcName = (*m_params)[funcNamePos];
if (!funcName->is(Expression::KindOfScalarExpression)) {
cg_printf("%s(", m_name.c_str());
for (int i = 0; i < m_params->getCount(); i++) {
if (i > 0) cg_printf(", ");
if (i == funcNamePos) {
cg_printf("%sdynamic_load(", Option::IdPrefix.c_str());
funcName->outputPHP(cg, ar);
cg_printf(")");
} else {
ExpressionPtr param = (*m_params)[i];
if (param) param->outputPHP(cg, ar);
}
}
cg_printf(")");
return;
}
}
}
cg_printf("%s(", m_origName.c_str());
} else {
cg_printf("%s(", m_origName.c_str());
}
}
if (m_params) m_params->outputPHP(cg, ar);
cg_printf(")");
}
/*
* returns: 1 - if the call is dynamic
* -1 - if the call may be dynamic, depending on redeclared derivation
* 0 - if the call is static (ie with an "empty" this).
*/
static int isObjCall(AnalysisResultPtr ar,
ClassScopeRawPtr thisCls, FunctionScopeRawPtr thisFunc,
ClassScopeRawPtr methCls, const std::string &methClsName) {
if (!thisCls || !thisFunc || thisFunc->isStatic()) return 0;
if (thisCls == methCls) return 1;
if (thisCls->derivesFrom(ar, methClsName, true, false)) return 1;
if (thisCls->derivesFromRedeclaring() &&
thisCls->derivesFrom(ar, methClsName, true, true)) {
return -1;
}
return 0;
}
FunctionScopePtr
SimpleFunctionCall::getFuncScopeFromParams(AnalysisResultPtr ar,
BlockScopeRawPtr scope,
ExpressionPtr clsName,
ExpressionPtr funcName,
ClassScopePtr &clsScope) {
clsScope.reset();
ScalarExpressionPtr clsName0(
dynamic_pointer_cast<ScalarExpression>(clsName));
ScalarExpressionPtr funcName0(
dynamic_pointer_cast<ScalarExpression>(funcName));
if (clsName0 && funcName0) {
string cname = clsName0->getLiteralString();
string fname = funcName0->getLiteralString();
if (!fname.empty()) {
if (!cname.empty()) {
ClassScopePtr cscope(ar->findClass(cname));
if (cscope && cscope->isRedeclaring()) {
cscope = scope->findExactClass(cscope);
}
if (cscope) {
FunctionScopePtr fscope(cscope->findFunction(ar, fname, true));
if (fscope) {
clsScope = cscope;
}
return fscope;
}
} else {
FunctionScopePtr fscope(ar->findFunction(fname));
return fscope;
}
}
}
return FunctionScopePtr();
}
int SimpleFunctionCall::checkObjCall(AnalysisResultPtr ar) {
ClassScopeRawPtr orig = getOriginalClass();
int objCall = isObjCall(ar, orig, getOriginalFunction(),
m_classScope, m_className);
if (objCall > 0 && m_localThis.empty() &&
(getClassScope() != orig || getFunctionScope()->isStatic())) {
int o = isObjCall(ar, getClassScope(), getFunctionScope(),
orig, orig->getName());
if (o <= 0) objCall = o;
}
return objCall;
}
bool SimpleFunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
int state) {
if (m_type == ThrowFatalFunction) return false;
if (m_type == StaticCompactFunction) {
if (!cg.inExpression()) return true;
cg.wrapExpressionBegin();
m_ciTemp = cg.createNewLocalId(shared_from_this());
cg_printf("ArrayInit compact%d(%d);\n",
m_ciTemp, m_params->getCount() / 2);
for (int i = 0; i < m_params->getCount(); i += 2) {
always_assert((*m_params)[i]->isLiteralString());
string p = (*m_params)[i]->getLiteralString();
ExpressionPtr e = (*m_params)[i + 1];
if (e->is(KindOfSimpleVariable)
&& Type::SameType(e->getCPPType(), Type::Variant)) {
SimpleVariablePtr sv = dynamic_pointer_cast<SimpleVariable>(e);
const string &cppName = sv->getAssignableCPPVariable(ar);
assert(!cppName.empty());
cg_printf("if (%s.isInitialized()) ", cppName.c_str());
}
e->preOutputCPP(cg, ar, 0);
cg_printf("compact%d.add(", m_ciTemp);
cg_printString(p, ar, shared_from_this());
cg_printf(", ");
e->outputCPP(cg, ar);
cg_printf(");\n");
}
return true;
}
int objCall = 0;
if (!m_className.empty() && (!m_funcScope || !m_funcScope->isStatic())) {
objCall = checkObjCall(ar);
if (objCall < 0) {
/*
We have X::foo (which is non-static) inside a non-static
method of Y, where Y may or may not be derived from X, depending
on redeclared classes.
Revert to dynamic dispatch for this case.
*/
m_valid = false;
} else if (objCall > 0 && !m_localThis.empty()) {
m_valid = false;
}
}
if (m_valid) {
bool ret = false;
if (m_classScope &&
(m_arrayParams || !m_funcScope->isStatic())) {
ret = true;
if (cg.inExpression()) {
if (m_funcScope->isStatic()) {
cg.wrapExpressionBegin();
m_ciTemp = cg.createNewLocalId(shared_from_this());
cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
// mcp.isObj is by default false
cg_printf("mcp%d.rootCls = %s%s::s_class_name.get();\n",
m_ciTemp,
Option::ClassPrefix, m_classScope->getId().c_str());
} else {
if (!objCall || m_arrayParams) {
cg.wrapExpressionBegin();
m_ciTemp = cg.createNewLocalId(shared_from_this());
cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
}
if (objCall && m_arrayParams) {
cg_printf("mcp%d.obj = %s;\n",
m_ciTemp, getThisString(false).c_str());
}
}
}
} else if (m_arrayParams && !m_funcScope->isUserFunction()) {
ret = true;
if (cg.inExpression()) {
cg.wrapExpressionBegin();
cg_printf("extern Variant %s%s(void*,CArrRef);\n",
Option::InvokePrefix, m_funcScope->getId().c_str());
}
}
return FunctionCall::preOutputCPP(cg, ar, state) || ret;
}
// Short circuit out if inExpression() returns false
if (!cg.inExpression()) return true;
cg.wrapExpressionBegin();
m_ciTemp = cg.createNewLocalId(shared_from_this());
bool needHash = true;
string escapedName(CodeGenerator::EscapeLabel(m_origName));
string escapedClass;
ClassScopePtr cls = m_classScope;
if (!m_className.empty()) {
if (!m_safe && !isPresent()) {
ClassScope::OutputVolatileCheck(
cg, ar, getScope(), m_origClassName, false);
cg_printf(";\n");
}
escapedClass = CodeGenerator::EscapeLabel(m_className);
}
cg_printf("const CallInfo *cit%d = NULL;\n", m_ciTemp);
if (!m_class && m_className.empty()) {
cg_printf("void *vt%d = NULL;\n", m_ciTemp);
} else {
cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
}
bool safeCheck = false;
if (m_safe) {
if (!m_className.empty()) {
if (!isPresent()) {
cg_indentBegin("if (");
ClassScope::OutputVolatileCheck(cg, ar, getScope(),
m_origClassName, true);
safeCheck = true;
}
} else if (!m_funcScope || m_funcScope->isVolatile()) {
cg_indentBegin("if (");
cg_printf("checkFunctionExistsNoThrow(");
cg_printString(m_origName, ar, shared_from_this());
cg_printf(", &%s->FVF(%s))",
cg.getGlobals(ar), CodeGenerator::FormatLabel(m_name).c_str());
safeCheck = true;
}
}
if (safeCheck) {
cg_printf(") {\n");
}
if (!m_class && m_className.empty()) {
if (m_redeclared && !m_dynamicInvoke) {
needHash = false;
cg_printf("RedeclaredCallInfoConst **rcit%d = &%s->GCI(%s);\n",
m_ciTemp, cg.getGlobals(ar),
CodeGenerator::FormatLabel(m_name).c_str());
cg_printf("cit%d = (CallInfo*)*rcit%d;\n", m_ciTemp, m_ciTemp);
if (!safeCheck) {
// No need to do this again if safeCheck is set
cg_printf("if (!cit%d) cit%d = invoke_check(", m_ciTemp, m_ciTemp);
cg_printString(m_name, ar, shared_from_this());
cg_printf(", (const CallInfo**)rcit%d, %s);\n",
m_ciTemp, m_safe ? "true" : "false");
}
} else {
cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp, m_ciTemp);
cg_printString(m_name, ar, shared_from_this());
cg_printf(");\n");
needHash = false;
}
} else {
if (safeCheck) {
cg_printf("mcp%d.noFatal();\n", m_ciTemp);
}
ClassScopePtr cscope = getOriginalClass();
// The call was like parent::
string className;
if (m_classScope) {
if (isRedeclared()) {
className = CodeGenerator::FormatLabel(m_className);
} else {
className = m_classScope->getId();
}
} else {
className = CodeGenerator::FormatLabel(m_className);
if (!m_className.empty() && m_cppTemp.empty() &&
!isSelf() && !isParent() && !isStatic()) {
// Create a temporary to hold the class name, in case it is not a
// StaticString.
m_clsNameTemp = cg.createNewLocalId(shared_from_this());
cg_printf("CStrRef clsName%d(", m_clsNameTemp);
cg_printString(m_origClassName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("id(clsName%d);\n", m_clsNameTemp);
}
}
if (objCall > 0 && !isUnknown() && !m_classScope) {
// class must be redeclaring, and cant be the originalClass
// (because then m_classScope would not be null).
// so we can start the search by following the redeclared parent.
cg_printf("mcp%d.methodCallEx(%s, ",
m_ciTemp, getThisString(false).c_str());
cg_printString(escapedName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("%sparent->%sget_call_info(mcp%d",
getThisString(true).c_str(),
Option::ObjectPrefix,
m_ciTemp);
} else if (objCall < 0) {
// Dont know if going to get an object or not
cg_printf("mcp%d.dynamicNamedCall(", m_ciTemp);
cg_printString(escapedClass, ar, shared_from_this());
cg_printf(", ");
cg_printString(escapedName, ar, shared_from_this());
} else if (isRedeclared()) {
cg_printf("mcp%d.staticMethodCall(", m_ciTemp);
cg_printString(escapedClass, ar, shared_from_this());
cg_printf(", ");
cg_printString(escapedName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("%s->%s%s->%sget_call_info(mcp%d",
cg.getGlobals(ar),
Option::ClassStaticsCallbackPrefix, className.c_str(),
Option::ObjectStaticPrefix,
m_ciTemp);
} else if (m_classScope) {
// In an object, calling a superclass's method
if (objCall > 0) {
cg_printf("mcp%d.methodCallEx(%s, ",
m_ciTemp, getThisString(false).c_str());
cg_printString(escapedName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("mcp%d.obj = mcp%d.rootObj;\n", m_ciTemp, m_ciTemp);
cg_printf("%s%s.%sget_call_info(mcp%d",
Option::ClassStaticsCallbackPrefix, className.c_str(),
Option::ObjectStaticPrefix,
m_ciTemp);
} else {
cg_printf("mcp%d.staticMethodCall(", m_ciTemp);
cg_printString(escapedClass, ar, shared_from_this());
cg_printf(", ");
cg_printString(escapedName, ar, shared_from_this());
cg_printf(");\n");
cg_printf("%s%s.%sget_call_info(mcp%d",
Option::ClassStaticsCallbackPrefix, className.c_str(),
Option::ObjectStaticPrefix,
m_ciTemp);
}
} else {
if (m_class) {
needHash = false;
bool lsb = false;
if (m_class->is(KindOfScalarExpression)) {
cg_printf("mcp%d.staticMethodCall(", m_ciTemp);
assert(strcasecmp(dynamic_pointer_cast<ScalarExpression>(m_class)->
getString().c_str(), "static") == 0);
cg_printString("static", ar, shared_from_this());
lsb = true;
} else {
m_class->preOutputCPP(cg, ar, 0);
m_class->preOutputStash(cg, ar, StashAll);
cg_printf("mcp%d.dynamicNamedCall(", m_ciTemp);
m_class->outputCPP(cg, ar);
}
cg_printf(", ");
cg_printString(escapedName, ar, shared_from_this());
cg_printf(");\n");
if (lsb) {
cg_printf("mcp%d.lateStaticBind(fi.getThreadInfo());\n", m_ciTemp);
}
} else {
// Nonexistent method
cg_printf("mcp%d.dynamicNamedCall(", m_ciTemp);
cg_printString(escapedClass, ar, shared_from_this());
cg_printf(", ");
cg_printString(escapedName, ar, shared_from_this());
}
}
}
if (needHash) {
cg_printf(", " STRHASH_FMT ");\n",
hash_string_i(m_name.data(), m_name.size()));
}
if (m_class || !m_className.empty()) {
cg_printf("cit%d = mcp%d.ci;\n", m_ciTemp, m_ciTemp);
}
if (safeCheck) {
cg_indentEnd("}\n");
}
int s = m_safeDef && m_params &&
m_safeDef->hasEffect() && m_params->hasEffect() ? FixOrder | StashVars : 0;
if (m_safeDef) m_safeDef->preOutputCPP(cg, ar, s);
if (m_params && m_params->getCount() > 0) {
cg.pushCallInfo(m_ciTemp);
m_params->preOutputCPP(cg, ar, s & ~StashVars);
cg.popCallInfo();
}
if (state & FixOrder) {
if (cg.inExpression()) {
preOutputStash(cg, ar, state);
}
}
return true;
}
string SimpleFunctionCall::getThisString(bool withArrow) {
if (m_localThis.empty() || getOriginalClass() == getClassScope()) {
return withArrow ? "" : "this";
}
always_assert(!m_localThis.empty());
if (withArrow) return m_localThis + "->";
return m_localThis;
}
void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (!m_class && m_className.empty()) {
switch (m_type) {
case ExtractFunction:
cg_printf("extract(variables, ");
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar, 0,
false);
cg_printf(")");
return;
case CompactFunction:
cg_printf("compact(variables, ");
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar, -1,
true);
cg_printf(")");
return;
case StaticCompactFunction:
cg_printf("Array(compact%d.create())", m_ciTemp);
return;
default:
break;
}
}
bool volatileCheck = false;
ClassScopePtr cls = m_classScope;
TypePtr safeCast;
if (m_safe) {
if (!m_className.empty()) {
if (!isPresent()) {
if (m_valid) {
cg_printf("(");
ClassScope::OutputVolatileCheck(cg, ar, getScope(),
m_origClassName, true);
}
volatileCheck = true;
}
} else if (!m_funcScope || m_funcScope->isVolatile()) {
if (m_valid) {
cg_printf("(checkFunctionExistsNoThrow(");
cg_printString(m_origName, ar, shared_from_this());
cg_printf(", &%s->FVF(%s))",
cg.getGlobals(ar),
CodeGenerator::FormatLabel(m_name).c_str());
}
volatileCheck = true;
}
if (volatileCheck) {
if (!m_valid) {
// ci will be null if fn/cl not defined. Set in preoutput
cg_printf("(cit%d", m_ciTemp);
}
if (isUnused()) {
cg_printf(" && (");
} else {
cg_printf(" ? ");
}
}
if (!isUnused()) {
if (m_safe > 0 && !m_safeDef) {
cg_printf("Array(ArrayInit(2).set(0, true).set(1, ");
Array ret(Array::Create(0, false));
ret.set(1, Variant());
} else if (m_funcScope && m_funcScope->getReturnType() &&
!Type::SameType(m_funcScope->getReturnType(),
getActualType())) {
safeCast = getActualType();
safeCast->outputCPPCast(cg, ar, getScope());
cg_printf("(");
}
if (m_funcScope && !m_funcScope->getReturnType()) {
cg_printf("(");
}
}
} else if (!m_className.empty()) {
if (!isPresent()) {
volatileCheck = true;
ClassScope::OutputVolatileCheckBegin(cg, ar, getScope(), m_origClassName);
}
}
if (m_valid && m_ciTemp < 0 && !m_arrayParams) {
if (!m_className.empty()) {
always_assert(cls);
if (!m_funcScope->isStatic()) {
if (m_localThis.empty() && getOriginalClass()->isRedeclaring() &&
getOriginalClass() != getClassScope()) {
cg_printf("((%s%s*)parent.get())->",
Option::ClassPrefix, getOriginalClass()->getId().c_str());
}
if (m_classScope->isRedeclaring() &&
m_classScope != getOriginalClass()) {
cg_printf("((%s%s*)%sparent.get())->",
Option::ClassPrefix, cls->getId().c_str(),
getThisString(true).c_str());
} else if (getOriginalClass() != getClassScope()) {
cg_printf("%s", getThisString(true).c_str());
}
}
cg_printf("%s%s::%s%s(", Option::ClassPrefix, cls->getId().c_str(),
m_funcScope->getPrefix(ar, m_params),
CodeGenerator::FormatLabel(m_funcScope->getName()).c_str());
} else {
int paramCount = m_params ? m_params->getCount() : 0;
if (m_name == "get_class" && getOriginalClass() && paramCount == 0) {
cg_printf("(");
cg_printString(getOriginalClass()->getOriginalName(), ar,
shared_from_this());
} else if (m_name == "get_parent_class" && getOriginalClass() &&
paramCount == 0) {
const std::string &parentClass =
getOriginalClass()->getOriginalParent();
cg_printf("(");
if (!parentClass.empty()) {
cg_printString(parentClass, ar, shared_from_this());
} else {
cg_printf("false");
}
} else {
if (m_noPrefix) {
cg_printf("%s(", CodeGenerator::FormatLabel(m_name).c_str());
} else {
bool callUserFuncFewArgs =
Option::UseCallUserFuncFewArgs &&
m_name == "call_user_func" &&
(m_params->getCount() <= CALL_USER_FUNC_FEW_ARGS_COUNT + 1) &&
m_argArrayId == -1;
if (callUserFuncFewArgs) {
cg_printf("%s%d(", m_name.c_str(), m_params->getCount() - 1);
} else {
cg_printf("%s%s(",
m_builtinFunction ? Option::BuiltinFunctionPrefix :
m_funcScope->getPrefix(ar, m_params),
m_funcScope->getId().c_str());
}
}
}
}
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar,
m_extraArg, m_variableArgument,
m_argArrayId,
m_argArrayHash, m_argArrayIndex);
} else {
int pcount = m_params ? m_params->getCount() : 0;
bool outputExtraArgs = true;
if (!m_class && m_className.empty()) {
if (m_valid) {
always_assert(m_arrayParams && m_ciTemp < 0);
if (getClassScope() && m_funcScope->isUserFunction()) {
cg_printf("HPHP::");
}
cg_printf("%s%s(NULL, ", Option::InvokePrefix,
m_funcScope->getId().c_str());
} else if (canInvokeFewArgs() && !m_arrayParams) {
if (Option::InvokeWithSpecificArgs) {
cg_printf("(cit%d->getFunc%dArgs())(vt%d, ",
m_ciTemp, pcount, m_ciTemp);
outputExtraArgs = false;
} else {
cg_printf("(cit%d->getFuncFewArgs())(vt%d, ", m_ciTemp, m_ciTemp);
}
} else {
cg_printf("(cit%d->getFunc())(vt%d, ", m_ciTemp, m_ciTemp);
}
} else {
if (m_valid) {
const char *prefix = m_arrayParams || !canInvokeFewArgs() ?
Option::InvokePrefix : Option::InvokeFewArgsPrefix;
cg_printf("%s%s%s::%s%s(mcp%d, ",
getThisString(true).c_str(),
Option::ClassPrefix, cls->getId().c_str(),
prefix,
CodeGenerator::FormatLabel(m_funcScope->getName()).c_str(),
m_ciTemp);
} else if (canInvokeFewArgs() && !m_arrayParams) {
if (Option::InvokeWithSpecificArgs) {
cg_printf("(cit%d->getMeth%dArgs())(mcp%d, ",
m_ciTemp, pcount, m_ciTemp);
outputExtraArgs = false;
} else {
cg_printf("(cit%d->getMethFewArgs())(mcp%d, ", m_ciTemp, m_ciTemp);
}
} else {
cg_printf("(cit%d->getMeth())(mcp%d, ", m_ciTemp, m_ciTemp);
}
}
if (canInvokeFewArgs() && !m_arrayParams) {
if (pcount) {
cg_printf("%d, ", pcount);
cg.pushCallInfo(m_ciTemp);
for (int i = 0; i < pcount; i++) {
(*m_params)[i]->setContext(NoRefWrapper);
}
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar, 0,
false);
cg.popCallInfo();
} else {
cg_printf("0");
}
if (outputExtraArgs) {
for (int i = pcount; i < Option::InvokeFewArgsCount; i++) {
cg_printf(", null_variant");
}
}
} else {
if (!pcount) {
cg_printf("Array()");
} else {
cg.pushCallInfo(m_ciTemp);
FunctionScope::OutputCPPArguments(m_params, m_funcScope, cg, ar,
m_arrayParams ? 0 : -1, false,
-1, -1, -1, m_arrayParams);
cg.popCallInfo();
}
}
}
cg_printf(")");
if (m_safe) {
if (isUnused()) {
} else {
if (m_funcScope && !m_funcScope->getReturnType()) {
cg_printf(", null)");
}
if (safeCast) {
cg_printf(")");
}
if (m_safe > 0 && !m_safeDef) {
cg_printf(").create())");
}
}
}
if (volatileCheck) {
if (m_safe) {
if (isUnused()) {
cg_printf(", false)");
} else {
cg_printf(" : ");
if (m_safeDef && m_safeDef->getActualType() &&
!Type::SameType(m_safeDef->getActualType(), getActualType())) {
safeCast = getActualType();
safeCast->outputCPPCast(cg, ar, getScope());
cg_printf("(");
} else {
safeCast.reset();
}
if (m_safeDef) {
m_safeDef->outputCPP(cg, ar);
} else {
if (m_safe < 0) {
cg_printf("null");
} else {
Array ret(Array::Create(0, false));
ret.set(1, Variant());
ExpressionPtr t = makeScalarExpression(ar, ret);
t->outputCPP(cg, ar);
}
}
if (safeCast) {
cg_printf(")");
}
}
cg_printf(")");
} else {
ClassScope::OutputVolatileCheckEnd(cg);
}
}
}
void SimpleFunctionCall::outputCPPImpl(CodeGenerator &cg,
AnalysisResultPtr ar) {
if (m_type == ThrowFatalFunction) {
cg_printf("%s(", m_name.c_str());
Variant t;
if (!(*m_params)[0]->getScalarValue(t) || !t.isString()) {
not_reached();
}
cg_printString(t.toString().data(), ar, shared_from_this());
cg_printf(")");
return;
}
if (!m_lambda.empty()) {
cg_printf("\"%s\"", m_lambda.c_str());
return;
}
if (!m_class && m_className.empty()) {
if (m_type == DefineFunction && m_params &&
unsigned(m_params->getCount() - 2) <= 1u) {
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
string varName;
ExpressionPtr value = (*m_params)[1];
if (name) {
varName = name->getIdentifier();
if (varName.empty()) {
cg_printf("throw_fatal(\"bad define\")");
} else if (m_dynamicConstant) {
cg_printf("g->declareConstant(");
cg_printString(varName, ar, shared_from_this());
cg_printf(", g->%s%s, ", Option::ConstantPrefix,
CodeGenerator::FormatLabel(varName).c_str());
value->outputCPP(cg, ar);
cg_printf(")");
} else {
bool needAssignment = true;
bool isSystem = ar->isSystemConstant(varName);
if (isSystem ||
((!ar->isConstantRedeclared(varName)) && value->isScalar())) {
needAssignment = false;
}
Variant scalarValue;
if (value->isScalar() &&
value->getScalarValue(scalarValue)) {
// If this isn't true, m_dynamicConstant should have been true
always_assert(scalarValue.isAllowedAsConstantValue());
}
if (needAssignment) {
cg_printf("%s%s = ", Option::ConstantPrefix, varName.c_str());
value->outputCPP(cg, ar);
}
}
} else {
bool close = false;
if (value->hasEffect()) {
cg_printf("(id(");
value->outputCPP(cg, ar);
cg_printf("),");
close = true;
}
cg_printf("throw_fatal(\"bad define\")");
if (close) cg_printf(")");
}
return;
}
if (m_name == "func_num_args") {
FunctionScopePtr func = getFunctionScope();
if (func && func->isGenerator()) {
cg_printf("%s%s->%snum_args()",
Option::VariablePrefix, CONTINUATION_OBJECT_NAME,
Option::MethodPrefix);
return;
}
if (!func || func->isVariableArgument()) {
cg_printf("num_args");
} else {
cg_printf("%d", func->getMaxParamCount());
}
return;
}
if (m_name == "hphp_get_call_info" &&
m_params && m_params->getCount() == 2) {
ClassScopePtr cscope;
FunctionScopePtr fscope(
getFuncScopeFromParams(
ar, getScope(), (*m_params)[0], (*m_params)[1], cscope));
if (fscope) {
if (cscope) {
cg_printf("(int64)&%s%s%s%s",
Option::CallInfoPrefix,
cscope->getId().c_str(),
Option::IdPrefix.c_str(),
fscope->getId().c_str());
} else {
cg_printf("(int64)&%s%s",
Option::CallInfoPrefix,
fscope->getId().c_str());
}
return;
}
}
if (m_name == "hphp_create_continuation" &&
m_params &&
(m_params->getCount() == 3 || m_params->getCount() == 4)) {
ClassScopePtr cscope;
FunctionScopePtr fscope(
getFuncScopeFromParams(
ar, getScope(), (*m_params)[0], (*m_params)[1], cscope));
if (fscope) {
cg_printf("%sContinuation$%s::Build(",
Option::ClassPrefix,
fscope->getId().c_str());
// func
if (cscope) {
cg_printf("(int64)&%s%s%s%s, ",
Option::CallInfoPrefix,
cscope->getId().c_str(),
Option::IdPrefix.c_str(),
fscope->getId().c_str());
} else {
cg_printf("(int64)&%s%s, ",
Option::CallInfoPrefix,
fscope->getId().c_str());
}
// extra
cg_printf("int64_t(0), ");
// isMethod
cg_printf("%s, ", cscope ? "true" : "false");
// origFuncName
((*m_params)[2])->outputCPP(cg, ar);
cg_printf(", ");
// function params
// vtable for the transformed *generator* function
VariableTablePtr variables = fscope->getVariables();
// method statement for the *original* function
MethodStatementPtr m =
dynamic_pointer_cast<MethodStatement>(getFunctionScope()->getStmt());
assert(m);
ExpressionListPtr params = m->getParams();
if (params) {
for (int i = 0; i < params->getCount(); i++) {
ParameterExpressionPtr param =
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
const string& name = param->getName();
Symbol *sym = variables->getSymbol(name);
if (sym) {
if (param->isRef()) {
assert(sym->getFinalType()->is(Type::KindOfVariant));
cg_printf("strongBind(%s%s), ",
variables->getVariablePrefix(sym),
CodeGenerator::FormatLabel(name).c_str());
} else {
cg_printf("%s%s, ",
variables->getVariablePrefix(sym),
CodeGenerator::FormatLabel(name).c_str());
}
}
}
}
// closure use vars
if (getFunctionScope()->isClosureGenerator()) {
ParameterExpressionPtrVec useVars;
if (getFunctionScope()->needsAnonClosureClass(useVars)) {
BOOST_FOREACH(ParameterExpressionPtr param, useVars) {
const string &name = param->getName();
Symbol *sym = variables->getSymbol(name);
if (sym) {
TypePtr t(sym->getFinalType());
// read the variables off the closure
if (param->isRef()) {
assert(sym->getFinalType()->is(Type::KindOfVariant));
cg_printf("strongBind(closure->%s%s), ",
variables->getVariablePrefix(sym),
CodeGenerator::FormatLabel(name).c_str());
} else {
cg_printf("closure->%s%s, ",
variables->getVariablePrefix(sym),
CodeGenerator::FormatLabel(name).c_str());
}
}
}
}
}
// TODO: avoid generating obj/args for specialized continuations
// if possible
// obj
if (!cscope) {
cg_printf("null_object, ");
} else {
if (cscope->derivedByDynamic()) {
cg_printf("Object(GET_THIS()), Object(this->o_id ? this : NULL), ");
} else {
cg_printf("GET_THIS_TYPED(%s), ", cscope->getId().c_str());
}
}
// args
if (m_params->getCount() == 4) {
((*m_params)[3])->outputCPP(cg, ar);
} else {
cg_printf("null_array");
}
cg_printf(")");
return;
}
}
if (m_name == "hphp_pack_continuation" &&
m_params && m_params->getCount() == 3) {
cg_printf("%s%s->%supdate(",
Option::VariablePrefix,
CONTINUATION_OBJECT_NAME,
Option::MethodPrefix);
m_params->removeElement(0); // remove cont obj
// fetch the function scope for Continuation::update()
ClassScopePtr cls = ar->findClass("continuation");
assert(cls && !cls->isRedeclaring());
FunctionScopePtr func = cls->findFunction(ar, "update", false);
assert(func);
FunctionScope::OutputCPPArguments(m_params, func,
cg, ar, 0, false);
cg_printf(")");
return;
}
if (m_name == "hphp_unpack_continuation" &&
m_params && m_params->getCount() == 1) {
cg_printf("0");
return;
}
switch (m_type) {
case VariableArgumentFunction:
{
FunctionScopePtr func = getFunctionScope();
if (func) {
bool isGetArgs = m_name == "func_get_args";
if (func->isGenerator()) {
cg_printf("%s%s->%s%s(",
Option::VariablePrefix, CONTINUATION_OBJECT_NAME,
Option::MethodPrefix,
isGetArgs ? "get_args" : "get_arg");
if (m_params) {
for (int i = 0; i < m_params->getCount(); i++) {
if (i) cg_printf(",");
(*m_params)[i]->outputCPP(cg, ar);
}
}
cg_printf(")");
return;
}
if (isGetArgs &&
func->getMaxParamCount() == 0 &&
(!m_params || m_params->getCount() == 0)) {
// in the special case of calling func_get_args() for
// a function with no explicit params, bypass the call
// to func_get_args() and simply use the passed in args
// array or the empty array
if (func->isVariableArgument()) {
cg_printf("(args.isNull() ? Array::Create() : args)");
} else {
cg_printf("Array::Create()");
}
return;
} else if (m_name == "func_get_arg" &&
m_params &&
m_params->getCount() == 1) {
Variant v;
ExpressionPtr p((*m_params)[0]);
if (p->getScalarValue(v) && v.isInteger()) {
// if func_get_arg is called with a scalar int, then
// optimize the call to func_get_arg away
int64 idx = v.toInt64();
if (idx < 0) {
cg_printf("Variant(false)");
return;
}
if (idx >= func->getMaxParamCount()) {
if (func->isVariableArgument()) {
int64 idx0 = idx - func->getMaxParamCount();
cg_printf("(%" PRId64 "L < num_args ? "
"args.rvalAt(int64_t(%" PRId64 ")) : Variant(false))",
idx, idx0);
} else {
cg_printf("Variant(false)");
}
return;
}
const string& funcName = func->getParamName(idx);
bool needsCast =
!func->getParamType(idx)->is(Type::KindOfVariant) &&
!func->getParamType(idx)->is(Type::KindOfSome);
bool isStashed =
func->getVariables()->getSymbol(funcName)->isStashedVal();
if (func->isVariableArgument()) {
cg_printf("(%" PRId64 "L < num_args ? ", idx);
}
if (needsCast) cg_printf("Variant(");
cg_printf("%s%s%s",
isStashed ? "v" : "",
Option::VariablePrefix,
funcName.c_str());
if (needsCast) cg_printf(")");
if (func->isVariableArgument()) {
cg_printf(" : Variant(false))");
}
return;
}
}
cg_printf("%s(", m_name.c_str());
func->outputCPPParamsCall(cg, ar, true);
if (m_params) {
cg_printf(", ");
m_params->outputCPP(cg, ar);
}
cg_printf(")");
return;
}
}
break;
case FunctionExistsFunction:
case ClassExistsFunction:
case InterfaceExistsFunction:
{
bool literalString = false;
string symbol;
if (m_params && m_params->getCount() == 1) {
ExpressionPtr value = (*m_params)[0];
if (value->isScalar()) {
ScalarExpressionPtr name =
dynamic_pointer_cast<ScalarExpression>(value);
if (name && name->isLiteralString()) {
literalString = true;
symbol = name->getLiteralString();
}
}
}
if (literalString) {
const std::string &lname = Util::toLower(symbol);
switch (m_type) {
case FunctionExistsFunction:
{
bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) !=
Option::DynamicInvokeFunctions.end();
if (!dynInvoke) {
FunctionScopePtr func = ar->findFunction(lname);
if (func) {
if (func->isVolatile()) {
cg_printf("checkFunctionExistsNoThrow(");
cg_printString(symbol, ar, shared_from_this());
cg_printf(", &%s->FVF(%s))",
cg.getGlobals(ar),
CodeGenerator::FormatLabel(lname).c_str());
break;
}
cg_printf("true");
break;
} else {
cg_printf("false");
break;
}
}
cg_printf("f_function_exists(");
cg_printString(symbol, ar, shared_from_this());
cg_printf(")");
}
break;
case ClassExistsFunction:
case InterfaceExistsFunction:
{
ClassScopePtrVec classes = ar->findClasses(Util::toLower(symbol));
int found = 0;
bool foundOther = false;
for (ClassScopePtrVec::const_iterator it = classes.begin();
it != classes.end(); ++it) {
ClassScopePtr cls = *it;
if (cls->isInterface() == (m_type == InterfaceExistsFunction)) {
found += cls->isVolatile() ? 2 : 1;
} else {
foundOther = true;
}
}
if (!found) {
cg_printf("false");
} else if (!foundOther) {
if (found == 1) {
cg_printf("true");
} else {
if (m_type == ClassExistsFunction) {
cg_printf("checkClassExistsNoThrow(");
} else {
cg_printf("checkInterfaceExistsNoThrow(");
}
cg_printString(symbol, ar, shared_from_this());
cg_printf(", &%s->CDEC(%s))",
cg.getGlobals(ar),
CodeGenerator::FormatLabel(lname).c_str());
}
} else {
if (m_type == ClassExistsFunction) {
cg_printf("f_class_exists(");
} else {
cg_printf("f_interface_exists(");
}
cg_printString(symbol, ar, shared_from_this());
cg_printf(")");
}
}
break;
default:
break;
}
return;
}
}
break;
case GetDefinedVarsFunction:
cg_printf("get_defined_vars(variables)");
return;
default:
break;
}
}
outputCPPParamOrderControlled(cg, ar);
}
SimpleFunctionCallPtr SimpleFunctionCall::GetFunctionCallForCallUserFunc(
AnalysisResultConstPtr ar, SimpleFunctionCallPtr call, int testOnly,
int firstParam, bool &error) {
error = false;
ExpressionListPtr params = call->getParams();
if (params && params->getCount() >= firstParam) {
ExpressionPtr p0 = (*params)[0];
Variant v;
if (p0->isScalar() && p0->getScalarValue(v)) {
if (v.isString()) {
Variant t = StringUtil::Explode(v.toString(), "::", 3);
if (!t.isArray() || t.toArray().size() != 2) {
std::string name = Util::toLower(v.toString().data());
FunctionScopePtr func = ar->findFunction(name);
if (!func || func->isDynamicInvoke()) {
error = !func;
return SimpleFunctionCallPtr();
}
if (func->isUserFunction()) func->setVolatile();
if (testOnly < 0) return SimpleFunctionCallPtr();
ExpressionListPtr p2;
if (testOnly) {
p2 = ExpressionListPtr(
new ExpressionList(call->getScope(), call->getLocation()));
p2->addElement(call->makeScalarExpression(ar, v));
name = "function_exists";
} else {
p2 = static_pointer_cast<ExpressionList>(params->clone());
while (firstParam--) {
p2->removeElement(0);
}
}
SimpleFunctionCallPtr rep(
NewSimpleFunctionCall(call->getScope(), call->getLocation(),
name, p2, ExpressionPtr()));
return rep;
}
v = t;
}
if (v.isArray()) {
Array arr = v.toArray();
if (arr.size() != 2 || !arr.exists(0) || !arr.exists(1)) {
error = true;
return SimpleFunctionCallPtr();
}
Variant classname = arr.rvalAt(int64_t(0));
Variant methodname = arr.rvalAt(int64_t(1));
if (!methodname.isString()) {
error = true;
return SimpleFunctionCallPtr();
}
if (!classname.isString()) {
return SimpleFunctionCallPtr();
}
std::string sclass = classname.toString().data();
std::string smethod = Util::toLower(methodname.toString().data());
ClassScopePtr cls;
if (sclass == "self") {
cls = call->getClassScope();
}
if (!cls) {
if (sclass == "parent") {
cls = call->getClassScope();
if (cls && !cls->getOriginalParent().empty()) {
sclass = cls->getOriginalParent();
}
}
cls = ar->findClass(sclass);
if (!cls) {
error = true;
return SimpleFunctionCallPtr();
}
if (cls->isRedeclaring()) {
cls = call->getScope()->findExactClass(cls);
} else if (!cls->isVolatile() && cls->isUserClass() &&
!ar->checkClassPresent(call, sclass)) {
cls->setVolatile();
}
if (!cls) {
return SimpleFunctionCallPtr();
}
}
if (testOnly < 0) return SimpleFunctionCallPtr();
size_t c = smethod.find("::");
if (c != 0 && c != string::npos && c+2 < smethod.size()) {
string name = smethod.substr(0, c);
if (cls->getName() != name) {
if (!cls->derivesFrom(ar, name, true, false)) {
error = cls->derivesFromRedeclaring() == ClassScope::FromNormal;
return SimpleFunctionCallPtr();
}
}
smethod = smethod.substr(c+2);
}
FunctionScopePtr func = cls->findFunction(ar, smethod, true);
if (!func) {
error = true;
return SimpleFunctionCallPtr();
}
if (func->isPrivate() ?
(cls != call->getOriginalClass() ||
!cls->findFunction(ar, smethod, false)) :
(func->isProtected() &&
(!call->getOriginalClass() ||
!call->getOriginalClass()->derivesFrom(ar, sclass,
true, false)))) {
error = true;
return SimpleFunctionCallPtr();
}
ExpressionPtr cl(call->makeScalarExpression(ar, classname));
ExpressionListPtr p2;
if (testOnly) {
p2 = ExpressionListPtr(
new ExpressionList(call->getScope(), call->getLocation()));
p2->addElement(cl);
cl.reset();
smethod = "class_exists";
} else {
p2 = static_pointer_cast<ExpressionList>(params->clone());
while (firstParam--) {
p2->removeElement(0);
}
}
SimpleFunctionCallPtr rep(
NewSimpleFunctionCall(call->getScope(), call->getLocation(),
smethod, p2, cl));
return rep;
}
}
}
return SimpleFunctionCallPtr();
}
namespace HPHP {
ExpressionPtr hphp_opt_call_user_func(CodeGenerator *cg,
AnalysisResultConstPtr ar,
SimpleFunctionCallPtr call, int mode) {
bool error = false;
if (!cg && mode <= 1 && !ar->isSystem()) {
const std::string &name = call->getName();
bool isArray = name == "call_user_func_array";
if (name == "call_user_func" || isArray) {
SimpleFunctionCallPtr rep(
SimpleFunctionCall::GetFunctionCallForCallUserFunc(ar, call,
mode ? -1 : 0,
1, error));
if (!mode) {
if (error) {
rep.reset();
} else if (rep) {
if (!Option::OutputHHBC || !isArray) {
rep->setSafeCall(-1);
rep->addLateDependencies(ar);
if (isArray) rep->setArrayParams();
} else {
rep.reset();
}
}
return rep;
}
}
}
return ExpressionPtr();
}
ExpressionPtr hphp_opt_fb_call_user_func(CodeGenerator *cg,
AnalysisResultConstPtr ar,
SimpleFunctionCallPtr call, int mode) {
bool error = false;
if (!cg && mode <= 1 && !ar->isSystem()) {
const std::string &name = call->getName();
bool isArray = name == "fb_call_user_func_array_safe";
bool safe_ret = name == "fb_call_user_func_safe_return";
if (isArray || safe_ret || name == "fb_call_user_func_safe") {
SimpleFunctionCallPtr rep(
SimpleFunctionCall::GetFunctionCallForCallUserFunc(
ar, call, mode ? -1 : 0, safe_ret ? 2 : 1, error));
if (!mode) {
if (error) {
if (!Option::WholeProgram) {
rep.reset();
} else if (safe_ret) {
return (*call->getParams())[1];
} else {
Array ret(Array::Create(0, false));
ret.set(1, Variant());
return call->makeScalarExpression(ar, ret);
}
}
if (rep && !Option::OutputHHBC) {
if (isArray) rep->setArrayParams();
rep->addLateDependencies(ar);
rep->setSafeCall(1);
if (safe_ret) {
ExpressionPtr def = (*call->getParams())[1];
rep->setSafeDefault(def);
}
return rep;
}
}
}
}
return ExpressionPtr();
}
ExpressionPtr hphp_opt_is_callable(CodeGenerator *cg,
AnalysisResultConstPtr ar,
SimpleFunctionCallPtr call, int mode) {
if (!cg && mode <= 1 && !ar->isSystem()) {
bool error = false;
SimpleFunctionCallPtr rep(
SimpleFunctionCall::GetFunctionCallForCallUserFunc(
ar, call, mode ? 1 : -1, 1, error));
if (error && !mode) {
if (!Option::WholeProgram) {
rep.reset();
} else {
return call->makeConstant(ar, "false");
}
}
return rep;
}
return ExpressionPtr();
}
}