/* +----------------------------------------------------------------------+ | 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace HPHP; /////////////////////////////////////////////////////////////////////////////// // statics std::map 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::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(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(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 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(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(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(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(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 new_params; vector 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_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((*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(vars)->getOp() == T_ARRAY) { break; } if (vars->is(KindOfExpressionList)) { vars = static_pointer_cast(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( static_pointer_cast(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((*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( 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(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(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(sym)->setDynamic(); } const_cast(sym)->setValue((*m_params)[1]); getScope()->addUpdates(BlockScope::UseKindConstRef); } Variant v; ExpressionPtr value = static_pointer_cast(sym->getValue()); if (value->getScalarValue(v)) { if (!v.isAllowedAsConstantValue()) { const_cast(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(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(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((*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(clsName)); ScalarExpressionPtr funcName0( dynamic_pointer_cast(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(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(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((*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("0LL, "); // 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(getFunctionScope()->getStmt()); assert(m); ExpressionListPtr params = m->getParams(); if (params) { for (int i = 0; i < params->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast((*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("(%lldLL < num_args ? " "args.rvalAt(%lldLL) : 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("(%lldLL < 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(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(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(0LL); Variant methodname = arr.rvalAt(1LL); 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(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(); } }