90b7170582
No emitted bytecode relies on Continuation being stored in local 0 anymore. Stop using local 0 for this purpose and compute offset to the Continuation at JIT time. 16 bytes of memory freed. At this point all locals of Continuation construction wrapper share the same indices with their respective locals of Continuation body, which should allow further optimizations.
1126 linhas
38 KiB
C++
1126 linhas
38 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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 "hphp/compiler/analysis/function_scope.h"
|
|
#include "hphp/compiler/analysis/analysis_result.h"
|
|
#include "hphp/compiler/expression/constant_expression.h"
|
|
#include "hphp/compiler/expression/modifier_expression.h"
|
|
#include "hphp/compiler/expression/expression_list.h"
|
|
#include "hphp/compiler/expression/function_call.h"
|
|
#include "hphp/compiler/analysis/code_error.h"
|
|
#include "hphp/compiler/statement/statement_list.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/compiler/parser/parser.h"
|
|
#include "hphp/util/logger.h"
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/statement/method_statement.h"
|
|
#include "hphp/compiler/statement/exp_statement.h"
|
|
#include "hphp/compiler/expression/parameter_expression.h"
|
|
#include "hphp/compiler/analysis/class_scope.h"
|
|
#include "hphp/util/atomic.h"
|
|
#include "hphp/util/util.h"
|
|
#include "hphp/runtime/base/class_info.h"
|
|
#include "hphp/runtime/base/type_conversions.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/util/parser/hphp.tab.hpp"
|
|
#include "hphp/runtime/base/variable_serializer.h"
|
|
#include "hphp/runtime/base/zend_string.h"
|
|
|
|
using namespace HPHP;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
FunctionScope::FunctionScope(AnalysisResultConstPtr ar, bool method,
|
|
const std::string &name, StatementPtr stmt,
|
|
bool reference, int minParam, int maxParam,
|
|
ModifierExpressionPtr modifiers,
|
|
int attribute, const std::string &docComment,
|
|
FileScopePtr file,
|
|
const std::vector<UserAttributePtr> &attrs,
|
|
bool inPseudoMain /* = false */)
|
|
: BlockScope(name, docComment, stmt, BlockScope::FunctionScope),
|
|
m_minParam(minParam), m_maxParam(maxParam), m_attribute(attribute),
|
|
m_modifiers(modifiers), m_hasVoid(false),
|
|
m_method(method), m_refReturn(reference), m_virtual(false),
|
|
m_hasOverride(false), m_perfectVirtual(false), m_overriding(false),
|
|
m_volatile(false), m_persistent(false), m_pseudoMain(inPseudoMain),
|
|
m_magicMethod(false), m_system(false), m_inlineable(false), m_sep(false),
|
|
m_containsThis(false), m_containsBareThis(0), m_nrvoFix(true),
|
|
m_inlineAsExpr(false), m_inlineSameContext(false),
|
|
m_contextSensitive(false),
|
|
m_directInvoke(false),
|
|
m_closureGenerator(false), m_noLSB(false), m_nextLSB(false),
|
|
m_hasTry(false), m_hasGoto(false), m_localRedeclaring(false),
|
|
m_redeclaring(-1), m_inlineIndex(0), m_optFunction(0), m_nextID(0),
|
|
m_yieldLabelCount(0) {
|
|
init(ar);
|
|
for (unsigned i = 0; i < attrs.size(); ++i) {
|
|
if (m_userAttributes.find(attrs[i]->getName()) != m_userAttributes.end()) {
|
|
attrs[i]->parseTimeFatal(Compiler::DeclaredAttributeTwice,
|
|
"Redeclared attribute %s",
|
|
attrs[i]->getName().c_str());
|
|
}
|
|
m_userAttributes[attrs[i]->getName()] = attrs[i]->getExp();
|
|
}
|
|
|
|
// Support for systemlib functions implemented in PHP
|
|
if (!m_method &&
|
|
m_userAttributes.find("__Overridable") != m_userAttributes.end()) {
|
|
setAllowOverride();
|
|
}
|
|
}
|
|
|
|
FunctionScope::FunctionScope(FunctionScopePtr orig,
|
|
AnalysisResultConstPtr ar,
|
|
const string &name,
|
|
const string &originalName,
|
|
StatementPtr stmt,
|
|
ModifierExpressionPtr modifiers)
|
|
: BlockScope(name, orig->m_docComment, stmt,
|
|
BlockScope::FunctionScope),
|
|
m_minParam(orig->m_minParam), m_maxParam(orig->m_maxParam),
|
|
m_attribute(orig->m_attribute), m_modifiers(modifiers),
|
|
m_userAttributes(orig->m_userAttributes), m_hasVoid(orig->m_hasVoid),
|
|
m_method(orig->m_method), m_refReturn(orig->m_refReturn),
|
|
m_virtual(orig->m_virtual), m_hasOverride(orig->m_hasOverride),
|
|
m_perfectVirtual(orig->m_perfectVirtual),
|
|
m_overriding(orig->m_overriding), m_volatile(orig->m_volatile),
|
|
m_persistent(orig->m_persistent),
|
|
m_pseudoMain(orig->m_pseudoMain), m_magicMethod(orig->m_magicMethod),
|
|
m_system(orig->m_system), m_inlineable(orig->m_inlineable),
|
|
m_sep(orig->m_sep), m_containsThis(orig->m_containsThis),
|
|
m_containsBareThis(orig->m_containsBareThis), m_nrvoFix(orig->m_nrvoFix),
|
|
m_inlineAsExpr(orig->m_inlineAsExpr),
|
|
m_inlineSameContext(orig->m_inlineSameContext),
|
|
m_contextSensitive(orig->m_contextSensitive),
|
|
m_directInvoke(orig->m_directInvoke),
|
|
m_closureGenerator(orig->m_closureGenerator), m_noLSB(orig->m_noLSB),
|
|
m_nextLSB(orig->m_nextLSB), m_hasTry(orig->m_hasTry),
|
|
m_hasGoto(orig->m_hasGoto), m_localRedeclaring(orig->m_localRedeclaring),
|
|
m_redeclaring(orig->m_redeclaring),
|
|
m_inlineIndex(orig->m_inlineIndex), m_optFunction(orig->m_optFunction),
|
|
m_nextID(0), m_yieldLabelCount(orig->m_yieldLabelCount) {
|
|
init(ar);
|
|
m_originalName = originalName;
|
|
setParamCounts(ar, m_minParam, m_maxParam);
|
|
}
|
|
|
|
void FunctionScope::init(AnalysisResultConstPtr ar) {
|
|
m_dynamicInvoke = false;
|
|
bool canInline = true;
|
|
if (m_pseudoMain) {
|
|
canInline = false;
|
|
m_variables->forceVariants(ar, VariableTable::AnyVars);
|
|
setReturnType(ar, Type::Variant);
|
|
}
|
|
|
|
if (m_refReturn) {
|
|
m_returnType = Type::Variant;
|
|
}
|
|
|
|
if (!strcasecmp(m_name.c_str(), "__autoload")) {
|
|
setVolatile();
|
|
}
|
|
|
|
// FileScope's flags are from parser, but VariableTable has more flags
|
|
// coming from type inference phase. So we are tranferring these flags
|
|
// just for better modularization between FileScope and VariableTable.
|
|
if (m_attribute & FileScope::ContainsDynamicVariable) {
|
|
m_variables->setAttribute(VariableTable::ContainsDynamicVariable);
|
|
}
|
|
if (m_attribute & FileScope::ContainsLDynamicVariable) {
|
|
m_variables->setAttribute(VariableTable::ContainsLDynamicVariable);
|
|
}
|
|
if (m_attribute & FileScope::ContainsExtract) {
|
|
m_variables->setAttribute(VariableTable::ContainsExtract);
|
|
}
|
|
if (m_attribute & FileScope::ContainsCompact) {
|
|
m_variables->setAttribute(VariableTable::ContainsCompact);
|
|
}
|
|
if (m_attribute & FileScope::ContainsUnset) {
|
|
m_variables->setAttribute(VariableTable::ContainsUnset);
|
|
}
|
|
if (m_attribute & FileScope::ContainsGetDefinedVars) {
|
|
m_variables->setAttribute(VariableTable::ContainsGetDefinedVars);
|
|
}
|
|
|
|
if (m_stmt && Option::AllVolatile && !m_pseudoMain && !m_method) {
|
|
m_volatile = true;
|
|
}
|
|
|
|
m_dynamic = Option::IsDynamicFunction(m_method, m_name) ||
|
|
Option::EnableEval == Option::FullEval || Option::AllDynamic;
|
|
if (!m_method && Option::DynamicInvokeFunctions.find(m_name) !=
|
|
Option::DynamicInvokeFunctions.end()) {
|
|
setDynamicInvoke();
|
|
}
|
|
if (m_modifiers) {
|
|
m_virtual = m_modifiers->isAbstract();
|
|
}
|
|
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
StatementListPtr stmts = stmt->getStmts();
|
|
if (stmts) {
|
|
if (stmts->getRecursiveCount() > Option::InlineFunctionThreshold)
|
|
canInline = false;
|
|
for (int i = 0; i < stmts->getCount(); i++) {
|
|
StatementPtr stmt = (*stmts)[i];
|
|
stmt->setFileLevel();
|
|
if (stmt->is(Statement::KindOfExpStatement)) {
|
|
ExpStatementPtr expStmt = dynamic_pointer_cast<ExpStatement>(stmt);
|
|
ExpressionPtr exp = expStmt->getExpression();
|
|
exp->setTopLevel();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
canInline = false;
|
|
}
|
|
m_inlineable = canInline;
|
|
}
|
|
|
|
FunctionScope::FunctionScope(bool method, const std::string &name,
|
|
bool reference)
|
|
: BlockScope(name, "", StatementPtr(), BlockScope::FunctionScope),
|
|
m_minParam(0), m_maxParam(0), m_attribute(0),
|
|
m_modifiers(ModifierExpressionPtr()), m_hasVoid(false),
|
|
m_method(method), m_refReturn(reference), m_virtual(false),
|
|
m_hasOverride(false), m_perfectVirtual(false), m_overriding(false),
|
|
m_volatile(false), m_persistent(false), m_pseudoMain(false),
|
|
m_magicMethod(false), m_system(true), m_inlineable(false), m_sep(false),
|
|
m_containsThis(false), m_containsBareThis(0), m_nrvoFix(true),
|
|
m_inlineAsExpr(false), m_inlineSameContext(false),
|
|
m_contextSensitive(false),
|
|
m_directInvoke(false),
|
|
m_closureGenerator(false), m_noLSB(false), m_nextLSB(false),
|
|
m_hasTry(false), m_hasGoto(false), m_localRedeclaring(false),
|
|
m_redeclaring(-1), m_inlineIndex(0),
|
|
m_optFunction(0) {
|
|
m_dynamic = Option::IsDynamicFunction(method, m_name) ||
|
|
Option::EnableEval == Option::FullEval || Option::AllDynamic;
|
|
m_dynamicInvoke = false;
|
|
if (!method && Option::DynamicInvokeFunctions.find(m_name) !=
|
|
Option::DynamicInvokeFunctions.end()) {
|
|
setDynamicInvoke();
|
|
}
|
|
}
|
|
|
|
void FunctionScope::setDynamicInvoke() {
|
|
m_returnType = Type::Variant;
|
|
m_dynamicInvoke = true;
|
|
}
|
|
|
|
void FunctionScope::setParamCounts(AnalysisResultConstPtr ar, int minParam,
|
|
int maxParam) {
|
|
if (minParam >= 0) {
|
|
m_minParam = minParam;
|
|
m_maxParam = maxParam;
|
|
} else {
|
|
assert(maxParam == minParam);
|
|
}
|
|
assert(m_minParam >= 0 && m_maxParam >= m_minParam);
|
|
if (m_maxParam > 0) {
|
|
m_paramNames.resize(m_maxParam);
|
|
m_paramTypes.resize(m_maxParam);
|
|
m_paramTypeSpecs.resize(m_maxParam);
|
|
m_paramDefaults.resize(m_maxParam);
|
|
m_paramDefaultTexts.resize(m_maxParam);
|
|
m_refs.resize(m_maxParam);
|
|
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
ExpressionListPtr params = stmt->getParams();
|
|
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (stmt->isRef(i)) m_refs[i] = true;
|
|
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
m_paramNames[i] = param->getName();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FunctionScope::setParamSpecs(AnalysisResultPtr ar) {
|
|
if (m_maxParam > 0 && m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
ExpressionListPtr params = stmt->getParams();
|
|
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
TypePtr specType = param->getTypeSpec(ar, false);
|
|
if (specType &&
|
|
!specType->is(Type::KindOfSome) &&
|
|
!specType->is(Type::KindOfVariant)) {
|
|
m_paramTypeSpecs[i] = specType;
|
|
}
|
|
ExpressionPtr exp = param->defaultValue();
|
|
if (exp) {
|
|
m_paramDefaults[i] = exp->getText(false, false, ar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FunctionScope::isPublic() const {
|
|
return m_modifiers && m_modifiers->isPublic();
|
|
}
|
|
|
|
bool FunctionScope::isProtected() const {
|
|
return m_modifiers && m_modifiers->isProtected();
|
|
}
|
|
|
|
bool FunctionScope::isPrivate() const {
|
|
return m_modifiers && m_modifiers->isPrivate();
|
|
}
|
|
|
|
bool FunctionScope::isStatic() const {
|
|
return m_modifiers && m_modifiers->isStatic();
|
|
}
|
|
|
|
bool FunctionScope::isAbstract() const {
|
|
return m_modifiers && m_modifiers->isAbstract();
|
|
}
|
|
|
|
bool FunctionScope::isFinal() const {
|
|
return m_modifiers && m_modifiers->isFinal();
|
|
}
|
|
|
|
bool FunctionScope::isVariableArgument() const {
|
|
bool res = (m_attribute & FileScope::VariableArgument) && !m_overriding;
|
|
return res;
|
|
}
|
|
|
|
bool FunctionScope::allowOverride() const {
|
|
return m_attribute & FileScope::AllowOverride;
|
|
}
|
|
|
|
bool FunctionScope::isReferenceVariableArgument() const {
|
|
bool res = (m_attribute & FileScope::ReferenceVariableArgument) &&
|
|
!m_overriding;
|
|
// If this method returns true, then isVariableArgument() must also
|
|
// return true.
|
|
assert(!res || isVariableArgument());
|
|
return res;
|
|
}
|
|
|
|
bool FunctionScope::isMixedVariableArgument() const {
|
|
bool res = (m_attribute & FileScope::MixedVariableArgument) && !m_overriding;
|
|
// If this method returns true, then isReferenceVariableArgument()
|
|
// must also return true.
|
|
assert(!res || isReferenceVariableArgument());
|
|
return res;
|
|
}
|
|
|
|
bool FunctionScope::needsActRec() const {
|
|
bool res = (m_attribute & FileScope::NeedsActRec);
|
|
return res;
|
|
}
|
|
|
|
bool FunctionScope::mayContainThis() {
|
|
return inPseudoMain() || getContainingClass() ||
|
|
(isClosure() && !m_modifiers->isStatic());
|
|
}
|
|
|
|
bool FunctionScope::isClosure() const {
|
|
return ParserBase::IsClosureName(name());
|
|
}
|
|
|
|
bool FunctionScope::isGenerator() const {
|
|
assert(!getOrigGenStmt() || ParserBase::IsContinuationName(name()));
|
|
return !!getOrigGenStmt();
|
|
}
|
|
|
|
bool FunctionScope::hasGeneratorAsBody() const {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(getStmt());
|
|
return stmt ? !!stmt->getGeneratorFunc() : false;
|
|
}
|
|
|
|
bool FunctionScope::isGeneratorFromClosure() const {
|
|
bool res = isGenerator() && getOrigGenFS()->isClosure();
|
|
assert(!res || getOrigGenFS()->isClosureGenerator());
|
|
return res;
|
|
}
|
|
|
|
MethodStatementRawPtr FunctionScope::getOrigGenStmt() const {
|
|
if (!getStmt()) return MethodStatementRawPtr();
|
|
MethodStatementPtr m =
|
|
dynamic_pointer_cast<MethodStatement>(getStmt());
|
|
return m ? m->getOrigGeneratorFunc() : MethodStatementRawPtr();
|
|
}
|
|
|
|
FunctionScopeRawPtr FunctionScope::getOrigGenFS() const {
|
|
MethodStatementRawPtr origStmt = getOrigGenStmt();
|
|
return origStmt ? origStmt->getFunctionScope() : FunctionScopeRawPtr();
|
|
}
|
|
|
|
void FunctionScope::setVariableArgument(int reference) {
|
|
m_attribute |= FileScope::VariableArgument;
|
|
if (reference) {
|
|
m_attribute |= FileScope::ReferenceVariableArgument;
|
|
if (reference < 0) {
|
|
m_attribute |= FileScope::MixedVariableArgument;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FunctionScope::setAllowOverride() {
|
|
m_attribute |= FileScope::AllowOverride;
|
|
}
|
|
|
|
bool FunctionScope::hasEffect() const {
|
|
return (m_attribute & FileScope::NoEffect) == 0;
|
|
}
|
|
|
|
void FunctionScope::setNoEffect() {
|
|
if (!(m_attribute & FileScope::NoEffect)) {
|
|
m_attribute |= FileScope::NoEffect;
|
|
}
|
|
}
|
|
|
|
bool FunctionScope::isFoldable() const {
|
|
return m_attribute & FileScope::IsFoldable;
|
|
}
|
|
|
|
void FunctionScope::setIsFoldable() {
|
|
m_attribute |= FileScope::IsFoldable;
|
|
}
|
|
|
|
void FunctionScope::setNeedsActRec() {
|
|
m_attribute |= FileScope::NeedsActRec;
|
|
}
|
|
|
|
void FunctionScope::setHelperFunction() {
|
|
m_attribute |= FileScope::HelperFunction;
|
|
}
|
|
|
|
bool FunctionScope::containsReference() const {
|
|
return m_attribute & FileScope::ContainsReference;
|
|
}
|
|
|
|
void FunctionScope::setContainsThis(bool f /* = true */) {
|
|
m_containsThis = f;
|
|
|
|
BlockScopePtr bs(this->getOuterScope());
|
|
while (bs && bs->is(BlockScope::FunctionScope)) {
|
|
FunctionScopePtr fs = static_pointer_cast<FunctionScope>(bs);
|
|
if (!fs->isClosure()) {
|
|
break;
|
|
}
|
|
fs->setContainsThis(f);
|
|
bs = bs->getOuterScope();
|
|
}
|
|
|
|
for (auto it = m_clonedTraitOuterScope.begin(); it != m_clonedTraitOuterScope.end(); it++) {
|
|
(*it)->setContainsThis(f);
|
|
}
|
|
}
|
|
|
|
void FunctionScope::setContainsBareThis(bool f, bool ref /* = false */) {
|
|
if (f) {
|
|
m_containsBareThis |= ref ? 2 : 1;
|
|
} else {
|
|
m_containsBareThis = 0;
|
|
}
|
|
|
|
BlockScopePtr bs(this->getOuterScope());
|
|
while (bs && bs->is(BlockScope::FunctionScope)) {
|
|
FunctionScopePtr fs = static_pointer_cast<FunctionScope>(bs);
|
|
if (!fs->isClosure()) {
|
|
break;
|
|
}
|
|
fs->setContainsBareThis(f, ref);
|
|
bs = bs->getOuterScope();
|
|
}
|
|
|
|
for (auto it = m_clonedTraitOuterScope.begin(); it != m_clonedTraitOuterScope.end(); it++) {
|
|
(*it)->setContainsBareThis(f, ref);
|
|
}
|
|
}
|
|
|
|
bool FunctionScope::hasImpl() const {
|
|
if (!isUserFunction()) {
|
|
return !isAbstract();
|
|
}
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
return stmt->getStmts() != nullptr;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FunctionScope::isConstructor(ClassScopePtr cls) const {
|
|
return m_stmt && cls
|
|
&& (getName() == "__construct"
|
|
|| (cls->classNameCtor() && getName() == cls->getName()));
|
|
}
|
|
|
|
bool FunctionScope::isMagic() const {
|
|
return m_name.size() >= 2 && m_name[0] == '_' && m_name[1] == '_';
|
|
}
|
|
|
|
bool FunctionScope::needsLocalThis() const {
|
|
return containsBareThis() &&
|
|
(inPseudoMain() ||
|
|
containsRefThis() ||
|
|
isStatic() ||
|
|
getVariables()->getAttribute(
|
|
VariableTable::ContainsDynamicVariable));
|
|
}
|
|
|
|
static std::string s_empty;
|
|
const string &FunctionScope::getOriginalName() const {
|
|
if (m_pseudoMain) return s_empty;
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
return stmt->getOriginalName();
|
|
}
|
|
return m_originalName;
|
|
}
|
|
|
|
string FunctionScope::getFullName() const {
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
return stmt->getFullName();
|
|
}
|
|
return m_name;
|
|
}
|
|
|
|
string FunctionScope::getOriginalFullName() const {
|
|
if (m_stmt) {
|
|
MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
return stmt->getOriginalFullName();
|
|
}
|
|
return m_name;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FunctionScope::addCaller(BlockScopePtr caller,
|
|
bool careAboutReturn /* = true */) {
|
|
addUse(caller, UseKindCaller);
|
|
}
|
|
|
|
void FunctionScope::addNewObjCaller(BlockScopePtr caller) {
|
|
addUse(caller, UseKindCaller & ~UseKindCallerReturn);
|
|
}
|
|
|
|
bool FunctionScope::mayUseVV() const {
|
|
VariableTableConstPtr variables = getVariables();
|
|
return (inPseudoMain() ||
|
|
isVariableArgument() ||
|
|
isGenerator() ||
|
|
variables->getAttribute(VariableTable::ContainsDynamicVariable) ||
|
|
variables->getAttribute(VariableTable::ContainsExtract) ||
|
|
variables->getAttribute(VariableTable::ContainsCompact) ||
|
|
variables->getAttribute(VariableTable::ContainsGetDefinedVars) ||
|
|
variables->getAttribute(VariableTable::ContainsDynamicFunctionCall));
|
|
}
|
|
|
|
bool FunctionScope::matchParams(FunctionScopePtr func) {
|
|
// leaving them alone for now
|
|
if (m_overriding || func->m_overriding) return false;
|
|
if (isStatic() || func->isStatic()) return false;
|
|
|
|
// conservative here, as we could normalize them into same counts.
|
|
if (m_minParam != func->m_minParam || m_maxParam != func->m_maxParam) {
|
|
return false;
|
|
}
|
|
if (isVariableArgument() != func->isVariableArgument() ||
|
|
isReferenceVariableArgument() != func->isReferenceVariableArgument() ||
|
|
isMixedVariableArgument() != func->isMixedVariableArgument()) {
|
|
return false;
|
|
}
|
|
|
|
// needs perfect match for ref, hint and defaults
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (m_refs[i] != func->m_refs[i]) return false;
|
|
|
|
TypePtr type1 = m_paramTypeSpecs[i];
|
|
TypePtr type2 = func->m_paramTypeSpecs[i];
|
|
if ((type1 && !type2) || (!type1 && type2) ||
|
|
(type1 && type2 && !Type::SameType(type1, type2))) return false;
|
|
|
|
if (m_paramDefaults[i] != func->m_paramDefaults[i]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FunctionScope::setPerfectVirtual() {
|
|
m_virtual = true;
|
|
m_perfectVirtual = true;
|
|
|
|
// conservative here, as we could still try to infer types THEN only
|
|
// force variants on non-matching parameters
|
|
m_returnType = Type::Variant;
|
|
for (unsigned int i = 0; i < m_paramTypes.size(); i++) {
|
|
m_paramTypes[i] = Type::Variant;
|
|
m_variables->addLvalParam(m_paramNames[i]);
|
|
}
|
|
}
|
|
|
|
bool FunctionScope::needsClassParam() {
|
|
if (!isStatic()) return false;
|
|
ClassScopeRawPtr cls = getContainingClass();
|
|
if (!ClassScope::NeedStaticArray(cls, FunctionScopeRawPtr(this))) {
|
|
return false;
|
|
}
|
|
return getVariables()->hasStatic();
|
|
}
|
|
|
|
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
|
|
ExpressionListPtr params, bool &valid) {
|
|
if (!params) {
|
|
if (m_minParam > 0) {
|
|
if (exp->getScope()->isFirstPass()) {
|
|
Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
|
|
}
|
|
valid = false;
|
|
if (!Option::AllDynamic) setDynamic();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int ret = 0;
|
|
if (params->getCount() < m_minParam) {
|
|
if (exp->getScope()->isFirstPass()) {
|
|
Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
|
|
}
|
|
valid = false;
|
|
if (!Option::AllDynamic) setDynamic();
|
|
}
|
|
if (params->getCount() > m_maxParam) {
|
|
if (isVariableArgument()) {
|
|
ret = params->getCount() - m_maxParam;
|
|
} else {
|
|
if (exp->getScope()->isFirstPass()) {
|
|
Compiler::Error(Compiler::TooManyArgument, exp, m_stmt);
|
|
}
|
|
valid = false;
|
|
if (!Option::AllDynamic) setDynamic();
|
|
}
|
|
}
|
|
|
|
bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual;
|
|
for (int i = 0; i < params->getCount(); i++) {
|
|
ExpressionPtr param = (*params)[i];
|
|
if (i < m_maxParam && param->hasContext(Expression::RefParameter)) {
|
|
/**
|
|
* This should be very un-likely, since call time pass by ref is a
|
|
* deprecated, not very widely used (at least in FB codebase) feature.
|
|
*/
|
|
TRY_LOCK_THIS();
|
|
Symbol *sym = getVariables()->addSymbol(m_paramNames[i]);
|
|
sym->setLvalParam();
|
|
sym->setCallTimeRef();
|
|
}
|
|
if (valid && param->hasContext(Expression::InvokeArgument)) {
|
|
param->clearContext(Expression::InvokeArgument);
|
|
param->clearContext(Expression::RefValue);
|
|
param->clearContext(Expression::NoRefWrapper);
|
|
}
|
|
bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument());
|
|
if ((i < m_maxParam && isRefParam(i)) || isRefVararg) {
|
|
param->setContext(Expression::LValue);
|
|
param->setContext(Expression::RefValue);
|
|
param->inferAndCheck(ar, Type::Variant, true);
|
|
} else if (!(param->getContext() & Expression::RefParameter)) {
|
|
param->clearContext(Expression::LValue);
|
|
param->clearContext(Expression::RefValue);
|
|
param->clearContext(Expression::InvokeArgument);
|
|
param->clearContext(Expression::NoRefWrapper);
|
|
}
|
|
TypePtr expType;
|
|
/**
|
|
* Duplicate the logic of getParamType(i), w/o the mutation
|
|
*/
|
|
TypePtr paramType(i < m_maxParam ? m_paramTypes[i] : TypePtr());
|
|
if (!paramType) paramType = Type::Some;
|
|
if (valid && !canSetParamType && i < m_maxParam &&
|
|
(!Option::HardTypeHints || !m_paramTypeSpecs[i])) {
|
|
/**
|
|
* What is this magic, you might ask?
|
|
*
|
|
* Here, we take advantage of implicit conversion from every type to
|
|
* Variant. Essentially, we don't really care what type comes out of this
|
|
* expression since it'll just get converted anyways. Doing it this way
|
|
* allows us to generate less temporaries along the way.
|
|
*/
|
|
TypePtr optParamType(
|
|
paramType->is(Type::KindOfVariant) ? Type::Some : paramType);
|
|
expType = param->inferAndCheck(ar, optParamType, false);
|
|
} else {
|
|
expType = param->inferAndCheck(ar, Type::Some, false);
|
|
}
|
|
if (i < m_maxParam) {
|
|
if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) {
|
|
if (canSetParamType) {
|
|
if (!Type::SameType(paramType, expType) &&
|
|
!paramType->is(Type::KindOfVariant)) {
|
|
TRY_LOCK_THIS();
|
|
paramType = setParamType(ar, i, expType);
|
|
} else {
|
|
// do nothing - how is this safe? well, if we ever observe
|
|
// paramType == expType, then this means at some point in the past,
|
|
// somebody called setParamType() with expType. thus, by calling
|
|
// setParamType() again with expType, we contribute no "new"
|
|
// information. this argument also still applies in the face of
|
|
// concurrency
|
|
}
|
|
}
|
|
// See note above. If we have an implemented type, however, we
|
|
// should set the paramType to the implemented type to avoid an
|
|
// un-necessary cast
|
|
if (paramType->is(Type::KindOfVariant)) {
|
|
TypePtr it(param->getImplementedType());
|
|
paramType = it ? it : expType;
|
|
}
|
|
if (valid) {
|
|
if (!Type::IsLegalCast(ar, expType, paramType) &&
|
|
paramType->isNonConvertibleType()) {
|
|
param->inferAndCheck(ar, paramType, true);
|
|
}
|
|
param->setExpectedType(paramType);
|
|
}
|
|
}
|
|
}
|
|
// we do a best-effort check for bad pass-by-reference and do not report
|
|
// error for some vararg case (e.g., array_multisort can have either ref
|
|
// or value for the same vararg).
|
|
if (!isRefVararg || !isMixedVariableArgument()) {
|
|
Expression::CheckPassByReference(ar, param);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
TypePtr FunctionScope::setParamType(AnalysisResultConstPtr ar, int index,
|
|
TypePtr type) {
|
|
assert(index >= 0 && index < (int)m_paramTypes.size());
|
|
TypePtr paramType = m_paramTypes[index];
|
|
|
|
if (!paramType) paramType = Type::Some;
|
|
type = Type::Coerce(ar, paramType, type);
|
|
if (type && !Type::SameType(paramType, type)) {
|
|
addUpdates(UseKindCallerParam);
|
|
if (!isFirstPass()) {
|
|
Logger::Verbose("Corrected type of parameter %d of %s: %s -> %s",
|
|
index, m_name.c_str(),
|
|
paramType->toString().c_str(), type->toString().c_str());
|
|
}
|
|
}
|
|
m_paramTypes[index] = type;
|
|
return type;
|
|
}
|
|
|
|
TypePtr FunctionScope::getParamType(int index) {
|
|
assert(index >= 0 && index < (int)m_paramTypes.size());
|
|
TypePtr paramType = m_paramTypes[index];
|
|
if (!paramType) {
|
|
paramType = Type::Some;
|
|
m_paramTypes[index] = paramType;
|
|
}
|
|
return paramType;
|
|
}
|
|
|
|
bool FunctionScope::isRefParam(int index) const {
|
|
assert(index >= 0 && index < (int)m_refs.size());
|
|
return m_refs[index];
|
|
}
|
|
|
|
void FunctionScope::setRefParam(int index) {
|
|
assert(index >= 0 && index < (int)m_refs.size());
|
|
m_refs[index] = true;
|
|
}
|
|
|
|
bool FunctionScope::hasRefParam(int max) const {
|
|
assert(max >= 0 && max < (int)m_refs.size());
|
|
for (int i = 0; i < max; i++) {
|
|
if (m_refs[i]) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const std::string &FunctionScope::getParamName(int index) const {
|
|
assert(index >= 0 && index < (int)m_paramNames.size());
|
|
return m_paramNames[index];
|
|
}
|
|
|
|
void FunctionScope::setParamName(int index, const std::string &name) {
|
|
assert(index >= 0 && index < (int)m_paramNames.size());
|
|
m_paramNames[index] = name;
|
|
}
|
|
|
|
void FunctionScope::setParamDefault(int index, const char* value, int64_t len,
|
|
const std::string &text) {
|
|
assert(index >= 0 && index < (int)m_paramNames.size());
|
|
auto sd = StringData::GetStaticString(value, len);
|
|
m_paramDefaults[index] = String(sd);
|
|
m_paramDefaultTexts[index] = text;
|
|
}
|
|
|
|
CStrRef FunctionScope::getParamDefault(int index) {
|
|
return m_paramDefaults[index];
|
|
}
|
|
|
|
void FunctionScope::addModifier(int mod) {
|
|
if (!m_modifiers) {
|
|
m_modifiers =
|
|
ModifierExpressionPtr(new ModifierExpression(
|
|
shared_from_this(), LocationPtr()));
|
|
}
|
|
m_modifiers->add(mod);
|
|
}
|
|
|
|
void FunctionScope::setReturnType(AnalysisResultConstPtr ar, TypePtr type) {
|
|
if (inTypeInference()) {
|
|
getInferTypesMutex().assertOwnedBySelf();
|
|
}
|
|
// no change can be made to virtual function's prototype
|
|
if (m_overriding || m_dynamicInvoke) return;
|
|
|
|
if (!type) {
|
|
m_hasVoid = true;
|
|
if (!m_returnType) return;
|
|
}
|
|
|
|
if (m_hasVoid) {
|
|
type = Type::Variant;
|
|
} else if (m_returnType) {
|
|
type = Type::Coerce(ar, m_returnType, type);
|
|
}
|
|
m_returnType = type;
|
|
assert(m_returnType);
|
|
}
|
|
|
|
void FunctionScope::pushReturnType() {
|
|
getInferTypesMutex().assertOwnedBySelf();
|
|
m_prevReturn = m_returnType;
|
|
m_hasVoid = false;
|
|
if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
|
|
return;
|
|
}
|
|
m_returnType.reset();
|
|
}
|
|
|
|
bool FunctionScope::popReturnType() {
|
|
getInferTypesMutex().assertOwnedBySelf();
|
|
if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
|
|
return false;
|
|
}
|
|
|
|
if (m_returnType) {
|
|
if (m_prevReturn) {
|
|
if (Type::SameType(m_returnType, m_prevReturn)) {
|
|
m_prevReturn.reset();
|
|
return false;
|
|
}
|
|
if (!isFirstPass()) {
|
|
Logger::Verbose("Corrected function return type %s -> %s",
|
|
m_prevReturn->toString().c_str(),
|
|
m_returnType->toString().c_str());
|
|
}
|
|
}
|
|
} else if (!m_prevReturn) {
|
|
return false;
|
|
}
|
|
|
|
m_prevReturn.reset();
|
|
addUpdates(UseKindCallerReturn);
|
|
#ifdef HPHP_INSTRUMENT_TYPE_INF
|
|
atomic_inc(RescheduleException::s_NumRetTypesChanged);
|
|
#endif /* HPHP_INSTRUMENT_TYPE_INF */
|
|
return true;
|
|
}
|
|
|
|
void FunctionScope::resetReturnType() {
|
|
getInferTypesMutex().assertOwnedBySelf();
|
|
if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
|
|
return;
|
|
}
|
|
m_returnType = m_prevReturn;
|
|
m_prevReturn.reset();
|
|
}
|
|
|
|
void FunctionScope::addRetExprToFix(ExpressionPtr e) {
|
|
m_retExprsToFix.push_back(e);
|
|
}
|
|
|
|
void FunctionScope::clearRetExprs() {
|
|
m_retExprsToFix.clear();
|
|
}
|
|
|
|
void FunctionScope::fixRetExprs() {
|
|
for (ExpressionPtrVec::iterator it = m_retExprsToFix.begin(),
|
|
end = m_retExprsToFix.end(); it != end; ++it) {
|
|
(*it)->setExpectedType(m_returnType);
|
|
}
|
|
m_retExprsToFix.clear();
|
|
}
|
|
|
|
void FunctionScope::setOverriding(TypePtr returnType,
|
|
TypePtr param1 /* = TypePtr() */,
|
|
TypePtr param2 /* = TypePtr() */) {
|
|
m_returnType = returnType;
|
|
m_overriding = true;
|
|
|
|
if (param1 && m_paramTypes.size() >= 1) m_paramTypes[0] = param1;
|
|
if (param2 && m_paramTypes.size() >= 2) m_paramTypes[1] = param2;
|
|
|
|
// TODO: remove this block and replace with stronger typing
|
|
// Right now, we have to avoid a situation where a parameter is assigned
|
|
// with different values, making them a Variant.
|
|
for (unsigned int i = 0; i < m_paramTypes.size(); i++) {
|
|
m_paramTypes[i] = Type::Variant;
|
|
}
|
|
}
|
|
|
|
std::string FunctionScope::getId() const {
|
|
string name = CodeGenerator::FormatLabel(getOriginalName());
|
|
if (m_redeclaring < 0) {
|
|
return name;
|
|
}
|
|
return name + Option::IdPrefix +
|
|
boost::lexical_cast<std::string>(m_redeclaring);
|
|
}
|
|
|
|
std::string FunctionScope::getDocName() const {
|
|
string name = getOriginalName();
|
|
if (m_redeclaring < 0) {
|
|
return name;
|
|
}
|
|
return name + Option::IdPrefix +
|
|
boost::lexical_cast<std::string>(m_redeclaring);
|
|
}
|
|
|
|
std::string FunctionScope::getDocFullName() const {
|
|
FunctionScope *self = const_cast<FunctionScope*>(this);
|
|
const string &docName = getDocName();
|
|
if (ClassScopeRawPtr cls = self->getContainingClass()) {
|
|
return cls->getDocName() + string("::") + docName;
|
|
}
|
|
return docName;
|
|
}
|
|
|
|
std::string FunctionScope::getInjectionId() const {
|
|
string injectionName = CodeGenerator::FormatLabel(getOriginalName());
|
|
MethodStatementPtr stmt =
|
|
dynamic_pointer_cast<MethodStatement>(getStmt());
|
|
assert(stmt);
|
|
if (stmt->getGeneratorFunc()) {
|
|
injectionName = isClosureGenerator() ?
|
|
injectionName :
|
|
injectionName + "{continuation}";
|
|
} else if (stmt->getOrigGeneratorFunc() &&
|
|
!getOrigGenFS()->isClosure()) {
|
|
injectionName = CodeGenerator::FormatLabel(
|
|
stmt->getOrigGeneratorFunc()->getOriginalName());
|
|
}
|
|
if (m_redeclaring < 0) {
|
|
return injectionName;
|
|
}
|
|
const string &redecSuffix = string(Option::IdPrefix) +
|
|
boost::lexical_cast<std::string>(m_redeclaring);
|
|
return injectionName + redecSuffix;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void FunctionScope::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
if (Option::GenerateInferredTypes && m_returnType) {
|
|
cg_printf("// @return %s\n", m_returnType->toString().c_str());
|
|
}
|
|
|
|
BlockScope::outputPHP(cg, ar);
|
|
}
|
|
|
|
void FunctionScope::serialize(JSON::CodeError::OutputStream &out) const {
|
|
JSON::CodeError::MapStream ms(out);
|
|
int vis = 0;
|
|
if (isPublic()) vis = ClassScope::Public;
|
|
else if (isProtected()) vis = ClassScope::Protected;
|
|
else if (isPrivate()) vis = ClassScope::Protected;
|
|
|
|
int mod = 0;
|
|
if (isAbstract()) mod = ClassScope::Abstract;
|
|
else if (isFinal()) mod = ClassScope::Final;
|
|
|
|
if (!m_returnType) {
|
|
ms.add("retTp", -1);
|
|
} else if (m_returnType->isSpecificObject()) {
|
|
ms.add("retTp", m_returnType->getName());
|
|
} else {
|
|
ms.add("retTp", m_returnType->getKindOf());
|
|
}
|
|
ms.add("minArgs", m_minParam)
|
|
.add("maxArgs", m_maxParam)
|
|
.add("varArgs", isVariableArgument())
|
|
.add("static", isStatic())
|
|
.add("modifier", mod)
|
|
.add("visibility", vis)
|
|
.add("argIsRef", m_refs)
|
|
.done();
|
|
}
|
|
|
|
void FunctionScope::serialize(JSON::DocTarget::OutputStream &out) const {
|
|
JSON::DocTarget::MapStream ms(out);
|
|
|
|
ms.add("name", getDocName());
|
|
ms.add("line", getStmt() ? getStmt()->getLocation()->line0 : 0);
|
|
ms.add("docs", m_docComment);
|
|
|
|
int mods = 0;
|
|
if (isPublic()) mods |= ClassInfo::IsPublic;
|
|
if (isProtected()) mods |= ClassInfo::IsProtected;
|
|
if (isPrivate()) mods |= ClassInfo::IsPrivate;
|
|
if (isStatic()) mods |= ClassInfo::IsStatic;
|
|
if (isFinal()) mods |= ClassInfo::IsFinal;
|
|
if (isAbstract()) mods |= ClassInfo::IsAbstract;
|
|
ms.add("modifiers", mods);
|
|
|
|
ms.add("refreturn", isRefReturn());
|
|
ms.add("return", getReturnType());
|
|
|
|
vector<SymParamWrapper> paramSymbols;
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
const string &name = getParamName(i);
|
|
const Symbol *sym = getVariables()->getSymbol(name);
|
|
assert(sym && sym->isParameter());
|
|
paramSymbols.push_back(SymParamWrapper(sym));
|
|
}
|
|
ms.add("parameters", paramSymbols);
|
|
|
|
// scopes that call this scope (callers)
|
|
vector<string> callers;
|
|
const BlockScopeRawPtrFlagsPtrVec &deps = getDeps();
|
|
for (BlockScopeRawPtrFlagsPtrVec::const_iterator it = deps.begin();
|
|
it != deps.end(); ++it) {
|
|
const BlockScopeRawPtrFlagsPtrPair &p(*it);
|
|
if ((*p.second & BlockScope::UseKindCaller) &&
|
|
p.first->is(BlockScope::FunctionScope)) {
|
|
FunctionScopeRawPtr f(
|
|
static_pointer_cast<FunctionScope>(p.first));
|
|
callers.push_back(f->getDocFullName());
|
|
}
|
|
}
|
|
ms.add("callers", callers);
|
|
|
|
// scopes that this scope calls (callees)
|
|
// TODO(stephentu): this list only contains *user* functions,
|
|
// we should also include builtins
|
|
vector<string> callees;
|
|
const BlockScopeRawPtrFlagsVec &users = getOrderedUsers();
|
|
for (BlockScopeRawPtrFlagsVec::const_iterator uit = users.begin();
|
|
uit != users.end(); ++uit) {
|
|
BlockScopeRawPtrFlagsVec::value_type pf = *uit;
|
|
if ((pf->second & BlockScope::UseKindCaller) &&
|
|
pf->first->is(BlockScope::FunctionScope)) {
|
|
FunctionScopeRawPtr f(
|
|
static_pointer_cast<FunctionScope>(pf->first));
|
|
callees.push_back(f->getDocFullName());
|
|
}
|
|
}
|
|
ms.add("callees", callees);
|
|
|
|
ms.done();
|
|
}
|
|
|
|
void FunctionScope::getClosureUseVars(
|
|
ParameterExpressionPtrIdxPairVec &useVars,
|
|
bool filterUsed /* = true */) {
|
|
useVars.clear();
|
|
if (!m_closureVars) return;
|
|
assert(isClosure());
|
|
VariableTablePtr variables = getVariables();
|
|
for (int i = 0; i < m_closureVars->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_closureVars)[i]);
|
|
const string &name = param->getName();
|
|
if (!filterUsed || variables->isUsed(name)) {
|
|
useVars.push_back(ParameterExpressionPtrIdxPair(param, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class U, class V>
|
|
static U pair_first_elem(std::pair<U, V> p) { return p.first; }
|
|
|
|
bool FunctionScope::needsAnonClosureClass(ParameterExpressionPtrVec &useVars) {
|
|
useVars.clear();
|
|
if (!isClosure()) return false;
|
|
ParameterExpressionPtrIdxPairVec useVars0;
|
|
getClosureUseVars(useVars0, !m_closureGenerator);
|
|
useVars.resize(useVars0.size());
|
|
// C++ seems to be unable to infer the type here on pair_first_elem
|
|
transform(useVars0.begin(),
|
|
useVars0.end(),
|
|
useVars.begin(),
|
|
pair_first_elem<ParameterExpressionPtr, int>);
|
|
return useVars.size() > 0 || getVariables()->hasStaticLocals();
|
|
}
|
|
|
|
bool FunctionScope::needsAnonClosureClass(
|
|
ParameterExpressionPtrIdxPairVec &useVars) {
|
|
useVars.clear();
|
|
if (!isClosure()) return false;
|
|
getClosureUseVars(useVars, !m_closureGenerator);
|
|
return useVars.size() > 0 || getVariables()->hasStaticLocals();
|
|
}
|
|
|
|
FunctionScope::StringToFunctionInfoPtrMap FunctionScope::s_refParamInfo;
|
|
static Mutex s_refParamInfoLock;
|
|
|
|
void FunctionScope::RecordFunctionInfo(string fname, FunctionScopePtr func) {
|
|
VariableTablePtr variables = func->getVariables();
|
|
if (Option::WholeProgram) {
|
|
Lock lock(s_refParamInfoLock);
|
|
FunctionInfoPtr &info = s_refParamInfo[fname];
|
|
if (!info) {
|
|
info = FunctionInfoPtr(new FunctionInfo());
|
|
}
|
|
if (func->isStatic()) {
|
|
info->setMaybeStatic();
|
|
}
|
|
if (func->isRefReturn()) {
|
|
info->setMaybeRefReturn();
|
|
}
|
|
if (func->isReferenceVariableArgument()) {
|
|
info->setRefVarArg(func->getMaxParamCount());
|
|
}
|
|
for (int i = 0; i < func->getMaxParamCount(); i++) {
|
|
if (func->isRefParam(i)) info->setRefParam(i);
|
|
}
|
|
}
|
|
for (int i = 0; i < func->getMaxParamCount(); i++) {
|
|
variables->addParam(func->getParamName(i),
|
|
TypePtr(), AnalysisResultPtr(), ConstructPtr());
|
|
}
|
|
}
|
|
|
|
FunctionScope::FunctionInfoPtr FunctionScope::GetFunctionInfo(string fname) {
|
|
assert(Option::WholeProgram);
|
|
StringToFunctionInfoPtrMap::iterator it = s_refParamInfo.find(fname);
|
|
if (it == s_refParamInfo.end()) {
|
|
return FunctionInfoPtr();
|
|
}
|
|
return it->second;
|
|
}
|