59f2c1749f
If the original function has a variable that the generator does not, we create a varenv. Its not clear that that should ever happen, but it *was* happening because: - closures get a magic variable called 0Closure, but generators from closures did not. - unused parameters didnt get put into the variable table, so generators with unused parameters got a varenv
637 linhas
22 KiB
C++
637 linhas
22 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "hphp/compiler/statement/method_statement.h"
|
|
#include "hphp/compiler/statement/return_statement.h"
|
|
#include "hphp/compiler/statement/statement_list.h"
|
|
#include "hphp/compiler/statement/try_statement.h"
|
|
#include "hphp/compiler/statement/label_statement.h"
|
|
#include "hphp/compiler/statement/goto_statement.h"
|
|
#include "hphp/compiler/statement/exp_statement.h"
|
|
#include "hphp/compiler/statement/switch_statement.h"
|
|
#include "hphp/compiler/statement/case_statement.h"
|
|
#include "hphp/compiler/statement/catch_statement.h"
|
|
|
|
#include "hphp/compiler/expression/modifier_expression.h"
|
|
#include "hphp/compiler/expression/expression_list.h"
|
|
#include "hphp/compiler/expression/constant_expression.h"
|
|
#include "hphp/compiler/expression/parameter_expression.h"
|
|
#include "hphp/compiler/expression/assignment_expression.h"
|
|
#include "hphp/compiler/expression/simple_variable.h"
|
|
#include "hphp/compiler/expression/closure_expression.h"
|
|
|
|
#include "hphp/compiler/analysis/ast_walker.h"
|
|
#include "hphp/compiler/analysis/analysis_result.h"
|
|
#include "hphp/compiler/analysis/code_error.h"
|
|
#include "hphp/compiler/analysis/file_scope.h"
|
|
#include "hphp/compiler/analysis/variable_table.h"
|
|
#include "hphp/compiler/analysis/class_scope.h"
|
|
#include "hphp/compiler/analysis/function_scope.h"
|
|
|
|
#include "hphp/compiler/option.h"
|
|
#include "hphp/compiler/builtin_symbols.h"
|
|
#include "hphp/compiler/analysis/alias_manager.h"
|
|
|
|
#include "hphp/runtime/base/complex_types.h"
|
|
|
|
#include "hphp/util/parser/parser.h"
|
|
#include "hphp/util/util.h"
|
|
|
|
using namespace HPHP;
|
|
using std::map;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// constructors/destructors
|
|
|
|
MethodStatement::MethodStatement
|
|
(STATEMENT_CONSTRUCTOR_BASE_PARAMETERS,
|
|
ModifierExpressionPtr modifiers, bool ref, const string &name,
|
|
ExpressionListPtr params, const std::string &retTypeConstraint,
|
|
StatementListPtr stmt, int attr, const string &docComment,
|
|
ExpressionListPtr attrList, bool method /* = true */)
|
|
: Statement(STATEMENT_CONSTRUCTOR_BASE_PARAMETER_VALUES),
|
|
m_method(method), m_ref(ref), m_attribute(attr),
|
|
m_cppLength(-1), m_modifiers(modifiers),
|
|
m_originalName(name), m_params(params),
|
|
m_retTypeConstraint(retTypeConstraint), m_stmt(stmt),
|
|
m_docComment(docComment), m_attrList(attrList) {
|
|
m_name = Util::toLower(name);
|
|
}
|
|
|
|
MethodStatement::MethodStatement
|
|
(STATEMENT_CONSTRUCTOR_PARAMETERS,
|
|
ModifierExpressionPtr modifiers, bool ref, const string &name,
|
|
ExpressionListPtr params, const std::string &retTypeConstraint,
|
|
StatementListPtr stmt,
|
|
int attr, const string &docComment, ExpressionListPtr attrList,
|
|
bool method /* = true */)
|
|
: Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(MethodStatement)),
|
|
m_method(method), m_ref(ref), m_attribute(attr), m_cppLength(-1),
|
|
m_modifiers(modifiers), m_originalName(name),
|
|
m_params(params), m_retTypeConstraint(retTypeConstraint),
|
|
m_stmt(stmt), m_docComment(docComment), m_attrList(attrList) {
|
|
m_name = Util::toLower(name);
|
|
}
|
|
|
|
StatementPtr MethodStatement::clone() {
|
|
MethodStatementPtr stmt(new MethodStatement(*this));
|
|
stmt->m_stmt = Clone(m_stmt);
|
|
stmt->m_params = Clone(m_params);
|
|
stmt->m_modifiers = Clone(m_modifiers);
|
|
return stmt;
|
|
}
|
|
|
|
string MethodStatement::getFullName() const {
|
|
if (m_className.empty()) return m_name;
|
|
return m_className + "::" + m_name;
|
|
}
|
|
|
|
string MethodStatement::getOriginalFullName() const {
|
|
if (m_originalClassName.empty()) return m_originalName;
|
|
return m_originalClassName + "::" + m_originalName;
|
|
}
|
|
|
|
string MethodStatement::getOriginalFullNameForInjection() const {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
string injectionName;
|
|
if (getGeneratorFunc()) {
|
|
injectionName = funcScope->isClosureGenerator() ?
|
|
m_originalName :
|
|
m_originalName + "{continuation}";
|
|
} else if (getOrigGeneratorFunc()) {
|
|
bool needsOrig = !funcScope->getOrigGenFS()->isClosure();
|
|
injectionName = needsOrig ?
|
|
getOrigGeneratorFunc()->getOriginalName() :
|
|
m_originalName;
|
|
} else {
|
|
injectionName = m_originalName;
|
|
}
|
|
return m_originalClassName.empty() ?
|
|
injectionName :
|
|
m_originalClassName + "::" + injectionName;
|
|
}
|
|
|
|
bool MethodStatement::isRef(int index /* = -1 */) const {
|
|
if (index == -1) return m_ref;
|
|
assert(index >= 0 && index < m_params->getCount());
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[index]);
|
|
return param->isRef();
|
|
}
|
|
|
|
int MethodStatement::getRecursiveCount() const {
|
|
return m_stmt ? m_stmt->getRecursiveCount() : 0;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// parser functions
|
|
|
|
FunctionScopePtr MethodStatement::onInitialParse(AnalysisResultConstPtr ar,
|
|
FileScopePtr fs) {
|
|
int minParam, maxParam;
|
|
ConstructPtr self = shared_from_this();
|
|
minParam = maxParam = 0;
|
|
bool hasRef = false;
|
|
if (m_params) {
|
|
std::set<string> names, allDeclNames;
|
|
int i = 0;
|
|
maxParam = m_params->getCount();
|
|
for (i = maxParam; i--; ) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (param->isRef()) hasRef = true;
|
|
if (!param->isOptional()) {
|
|
if (!minParam) minParam = i + 1;
|
|
} else if (minParam && !param->hasTypeHint()) {
|
|
Compiler::Error(Compiler::RequiredAfterOptionalParam, param);
|
|
}
|
|
allDeclNames.insert(param->getName());
|
|
}
|
|
|
|
for (i = maxParam-1; i >= 0; i--) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (names.find(param->getName()) != names.end()) {
|
|
Compiler::Error(Compiler::RedundantParameter, param);
|
|
for (int j = 0; j < 1000; j++) {
|
|
string name = param->getName() + lexical_cast<string>(j);
|
|
if (names.find(name) == names.end() &&
|
|
allDeclNames.find(name) == allDeclNames.end()) {
|
|
param->rename(name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
names.insert(param->getName());
|
|
}
|
|
}
|
|
|
|
if (hasRef || m_ref) {
|
|
m_attribute |= FileScope::ContainsReference;
|
|
}
|
|
|
|
vector<UserAttributePtr> attrs;
|
|
if (m_attrList) {
|
|
for (int i = 0; i < m_attrList->getCount(); ++i) {
|
|
UserAttributePtr a =
|
|
dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]);
|
|
attrs.push_back(a);
|
|
}
|
|
}
|
|
|
|
StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this());
|
|
FunctionScopePtr funcScope
|
|
(new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam,
|
|
m_modifiers, m_attribute, m_docComment, fs, attrs));
|
|
if (!m_stmt) {
|
|
funcScope->setVirtual();
|
|
}
|
|
setBlockScope(funcScope);
|
|
|
|
funcScope->setParamCounts(ar, -1, -1);
|
|
return funcScope;
|
|
}
|
|
|
|
void MethodStatement::onParseRecur(AnalysisResultConstPtr ar,
|
|
ClassScopePtr classScope) {
|
|
|
|
if (m_modifiers) {
|
|
if (classScope->isInterface()) {
|
|
if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
|
|
m_modifiers->isAbstract() || m_modifiers->isFinal()) {
|
|
m_modifiers->parseTimeFatal(
|
|
Compiler::InvalidAttribute,
|
|
"Access type for interface method %s::%s() must be omitted",
|
|
classScope->getOriginalName().c_str(), getOriginalName().c_str());
|
|
}
|
|
}
|
|
if (m_modifiers->isAbstract()) {
|
|
if (m_modifiers->isPrivate() || m_modifiers->isFinal()) {
|
|
m_modifiers->parseTimeFatal(
|
|
Compiler::InvalidAttribute,
|
|
"Cannot declare abstract method %s::%s() %s",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str(),
|
|
m_modifiers->isPrivate() ? "private" : "final");
|
|
}
|
|
if (!classScope->isInterface() && !classScope->isAbstract()) {
|
|
/* note that classScope->isAbstract() returns true for traits */
|
|
m_modifiers->parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Class %s contains abstract method %s and "
|
|
"must therefore be declared abstract",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
if (getStmts()) {
|
|
parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Abstract method %s::%s() cannot contain body",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
}
|
|
}
|
|
if ((!m_modifiers || !m_modifiers->isAbstract()) &&
|
|
!getStmts() && !classScope->isInterface()) {
|
|
parseTimeFatal(Compiler::InvalidAttribute,
|
|
"Non-abstract method %s::%s() must contain body",
|
|
classScope->getOriginalName().c_str(),
|
|
getOriginalName().c_str());
|
|
}
|
|
|
|
FunctionScopeRawPtr fs = getFunctionScope();
|
|
|
|
classScope->addFunction(ar, fs);
|
|
|
|
m_className = classScope->getName();
|
|
m_originalClassName = classScope->getOriginalName();
|
|
|
|
setSpecialMethod(classScope);
|
|
|
|
if (Option::DynamicInvokeFunctions.find(getFullName()) !=
|
|
Option::DynamicInvokeFunctions.end()) {
|
|
fs->setDynamicInvoke();
|
|
}
|
|
if (m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
param->parseHandler(classScope);
|
|
}
|
|
}
|
|
FunctionScope::RecordFunctionInfo(m_name, fs);
|
|
}
|
|
|
|
void MethodStatement::fixupSelfAndParentTypehints(ClassScopePtr scope) {
|
|
if (m_params) {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
param->fixupSelfAndParentTypehints(scope);
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::setSpecialMethod(ClassScopePtr classScope) {
|
|
if (m_name.size() < 2 || m_name.substr(0,2) != "__") {
|
|
return;
|
|
}
|
|
int numArgs = -1;
|
|
bool isStatic = false;
|
|
if (m_name == "__construct") {
|
|
classScope->setAttribute(ClassScope::HasConstructor);
|
|
} else if (m_name == "__destruct") {
|
|
classScope->setAttribute(ClassScope::HasDestructor);
|
|
} else if (m_name == "__call") {
|
|
classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
|
|
numArgs = 2;
|
|
} else if (m_name == "__get") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropGetter);
|
|
numArgs = 1;
|
|
} else if (m_name == "__set") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropSetter);
|
|
numArgs = 2;
|
|
} else if (m_name == "__isset") {
|
|
classScope->setAttribute(ClassScope::HasUnknownPropTester);
|
|
numArgs = 1;
|
|
} else if (m_name == "__unset") {
|
|
classScope->setAttribute(ClassScope::HasPropUnsetter);
|
|
numArgs = 1;
|
|
} else if (m_name == "__call") {
|
|
classScope->setAttribute(ClassScope::HasUnknownMethodHandler);
|
|
numArgs = 2;
|
|
} else if (m_name == "__callstatic") {
|
|
classScope->setAttribute(ClassScope::HasUnknownStaticMethodHandler);
|
|
numArgs = 2;
|
|
isStatic = true;
|
|
} else if (m_name == "__invoke") {
|
|
classScope->setAttribute(ClassScope::HasInvokeMethod);
|
|
} else if (m_name == "__tostring") {
|
|
numArgs = 0;
|
|
}
|
|
if (numArgs >= 0) {
|
|
// Fatal if the number of arguments is wrong
|
|
int n = m_params ? m_params->getCount() : 0;
|
|
if (numArgs != n) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() must take exactly %d argument%s",
|
|
m_originalClassName.c_str(), m_originalName.c_str(),
|
|
numArgs, (numArgs == 1) ? "" : "s");
|
|
}
|
|
// Fatal if any arguments are pass by reference
|
|
if (m_params && hasRefParam()) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() cannot take arguments by reference",
|
|
m_originalClassName.c_str(), m_originalName.c_str());
|
|
}
|
|
// Fatal if protected/private or if the staticness is wrong
|
|
if (m_modifiers->isProtected() || m_modifiers->isPrivate() ||
|
|
m_modifiers->isStatic() != isStatic) {
|
|
parseTimeFatal(Compiler::InvalidMagicMethod,
|
|
"Method %s::%s() must have public visibility and %sbe static",
|
|
m_originalClassName.c_str(), m_originalName.c_str(),
|
|
isStatic ? "" : "cannot ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void MethodStatement::addTraitMethodToScope(AnalysisResultConstPtr ar,
|
|
ClassScopePtr classScope) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
classScope->addFunction(ar, funcScope);
|
|
setSpecialMethod(classScope);
|
|
FunctionScope::RecordFunctionInfo(m_name, funcScope);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// static analysis functions
|
|
|
|
int MethodStatement::getLocalEffects() const {
|
|
if (m_method) return NoEffect;
|
|
FunctionScopeRawPtr scope = getFunctionScope();
|
|
return scope->isVolatile() ? OtherEffect | CanThrow : NoEffect;
|
|
}
|
|
|
|
void MethodStatement::analyzeProgram(AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
|
|
if (m_params) {
|
|
m_params->analyzeProgram(ar);
|
|
}
|
|
if (m_stmt) m_stmt->analyzeProgram(ar);
|
|
|
|
if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
|
|
funcScope->setParamSpecs(ar);
|
|
if (funcScope->isGenerator()) {
|
|
MethodStatementRawPtr orig = getOrigGeneratorFunc();
|
|
VariableTablePtr variables = funcScope->getVariables();
|
|
|
|
Symbol *cont = variables->getSymbol(CONTINUATION_OBJECT_NAME);
|
|
cont->setHidden();
|
|
|
|
orig->getFunctionScope()->addUse(funcScope, BlockScope::UseKindClosure);
|
|
orig->getFunctionScope()->setContainsBareThis(
|
|
funcScope->containsBareThis(), funcScope->containsRefThis());
|
|
orig->getFunctionScope()->setContainsThis(funcScope->containsThis());
|
|
|
|
if (ExpressionListPtr params = orig->getParams()) {
|
|
for (int i = 0; i < params->getCount(); ++i) {
|
|
auto param = dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
Symbol *gp = variables->addDeclaredSymbol(param->getName(), param);
|
|
gp->setGeneratorParameter();
|
|
if (param->isRef()) {
|
|
gp->setRefGeneratorParameter();
|
|
gp->setReferenced();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ClosureExpressionRawPtr closure = orig->getContainingClosure()) {
|
|
if (ExpressionListPtr cvars = closure->getClosureVariables()) {
|
|
for (int i = 0; i < cvars->getCount(); ++i) {
|
|
auto param = dynamic_pointer_cast<ParameterExpression>((*cvars)[i]);
|
|
Symbol *gp = variables->addDeclaredSymbol(
|
|
param->getName(), ConstructPtr());
|
|
gp->setGeneratorParameter();
|
|
if (param->isRef()) {
|
|
gp->setRefGeneratorParameter();
|
|
gp->setReferenced();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (funcScope->isSepExtension() ||
|
|
Option::IsDynamicFunction(m_method, m_name) || Option::AllDynamic) {
|
|
funcScope->setDynamic();
|
|
}
|
|
// TODO: this may have to expand to a concept of "virtual" functions...
|
|
if (m_method) {
|
|
funcScope->disableInline();
|
|
if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
|
|
bool magic = true;
|
|
int paramCount = 0;
|
|
if (m_name == "__destruct") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__call") {
|
|
funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
|
|
paramCount = 2;
|
|
} else if (m_name == "__set") {
|
|
funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
|
|
paramCount = 2;
|
|
} else if (m_name == "__get") {
|
|
funcScope->setOverriding(Type::Variant, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__isset") {
|
|
funcScope->setOverriding(Type::Boolean, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__unset") {
|
|
funcScope->setOverriding(Type::Variant, Type::String);
|
|
paramCount = 1;
|
|
} else if (m_name == "__sleep") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__wakeup") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else if (m_name == "__set_state") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
paramCount = 1;
|
|
} else if (m_name == "__tostring") {
|
|
funcScope->setOverriding(Type::String);
|
|
} else if (m_name == "__clone") {
|
|
funcScope->setOverriding(Type::Variant);
|
|
} else {
|
|
paramCount = -1;
|
|
if (m_name != "__construct") {
|
|
magic = false;
|
|
}
|
|
}
|
|
if (paramCount >= 0 && paramCount != funcScope->getMaxParamCount()) {
|
|
Compiler::Error(Compiler::InvalidMagicMethod, shared_from_this());
|
|
magic = false;
|
|
}
|
|
if (magic) funcScope->setMagicMethod();
|
|
}
|
|
// ArrayAccess methods
|
|
else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
|
|
if (m_name == "offsetexists") {
|
|
funcScope->setOverriding(Type::Boolean, Type::Variant);
|
|
} else if (m_name == "offsetget") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
} else if (m_name == "offsetset") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
|
|
} else if (m_name == "offsetunset") {
|
|
funcScope->setOverriding(Type::Variant, Type::Variant);
|
|
}
|
|
}
|
|
}
|
|
} else if (ar->getPhase() == AnalysisResult::AnalyzeFinal) {
|
|
TypePtr ret = funcScope->getReturnType();
|
|
if (ret && ret->isSpecificObject()) {
|
|
FileScopePtr fs = getFileScope();
|
|
if (fs) fs->addClassDependency(ar, ret->getName());
|
|
}
|
|
if (!getFunctionScope()->usesLSB()) {
|
|
if (StatementPtr orig = getOrigGeneratorFunc()) {
|
|
orig->getFunctionScope()->clearUsesLSB();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ConstructPtr MethodStatement::getNthKid(int n) const {
|
|
switch (n) {
|
|
case 0:
|
|
return m_modifiers;
|
|
case 1:
|
|
return m_params;
|
|
case 2:
|
|
return m_stmt;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
return ConstructPtr();
|
|
}
|
|
|
|
int MethodStatement::getKidCount() const {
|
|
return 3;
|
|
}
|
|
|
|
void MethodStatement::setNthKid(int n, ConstructPtr cp) {
|
|
switch (n) {
|
|
case 0:
|
|
m_modifiers = boost::dynamic_pointer_cast<ModifierExpression>(cp);
|
|
break;
|
|
case 1:
|
|
m_params = boost::dynamic_pointer_cast<ExpressionList>(cp);
|
|
break;
|
|
case 2:
|
|
m_stmt = boost::dynamic_pointer_cast<StatementList>(cp);
|
|
break;
|
|
default:
|
|
assert(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MethodStatement::inferTypes(AnalysisResultPtr ar) {
|
|
}
|
|
|
|
void MethodStatement::inferFunctionTypes(AnalysisResultPtr ar) {
|
|
IMPLEMENT_INFER_AND_CHECK_ASSERT(getFunctionScope());
|
|
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
bool pseudoMain = funcScope->inPseudoMain();
|
|
|
|
if (m_stmt && funcScope->isFirstPass()) {
|
|
if (pseudoMain ||
|
|
funcScope->getReturnType() ||
|
|
m_stmt->hasRetExp()) {
|
|
bool lastIsReturn = false;
|
|
if (m_stmt->getCount()) {
|
|
StatementPtr lastStmt = (*m_stmt)[m_stmt->getCount()-1];
|
|
if (lastStmt->is(Statement::KindOfReturnStatement)) {
|
|
lastIsReturn = true;
|
|
}
|
|
}
|
|
if (!lastIsReturn) {
|
|
ExpressionPtr constant =
|
|
makeScalarExpression(ar, funcScope->inPseudoMain() ?
|
|
Variant(1) : Variant(Variant::nullInit));
|
|
ReturnStatementPtr returnStmt =
|
|
ReturnStatementPtr(
|
|
new ReturnStatement(getScope(), getLocation(), constant));
|
|
m_stmt->addElement(returnStmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_params) {
|
|
m_params->inferAndCheck(ar, Type::Any, false);
|
|
}
|
|
|
|
// must also include params and use vars if this is a generator. note: we are
|
|
// OK reading the params from the AST nodes of the original generator
|
|
// function, since we have the dependency links set up
|
|
if (funcScope->isGenerator()) {
|
|
// orig function params
|
|
MethodStatementRawPtr m = getOrigGeneratorFunc();
|
|
assert(m);
|
|
|
|
VariableTablePtr variables = funcScope->getVariables();
|
|
ExpressionListPtr params = m->getParams();
|
|
if (params) {
|
|
for (int i = 0; i < params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
const string &name = param->getName();
|
|
assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
|
|
variables->addParamLike(name, param->getType(), ar, param,
|
|
funcScope->isFirstPass());
|
|
}
|
|
}
|
|
|
|
// use vars
|
|
ExpressionListPtr useVars = m->getFunctionScope()->getClosureVars();
|
|
if (useVars) {
|
|
for (int i = 0; i < useVars->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*useVars)[i]);
|
|
const string &name = param->getName();
|
|
assert(!param->isRef() || param->getType()->is(Type::KindOfVariant));
|
|
variables->addParamLike(name, param->getType(), ar, param,
|
|
funcScope->isFirstPass());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_stmt) {
|
|
m_stmt->inferTypes(ar);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// code generation functions
|
|
|
|
void MethodStatement::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
FunctionScopeRawPtr funcScope = getFunctionScope();
|
|
|
|
m_modifiers->outputPHP(cg, ar);
|
|
cg_printf(" function ");
|
|
if (m_ref) cg_printf("&");
|
|
if (!ParserBase::IsClosureOrContinuationName(m_name)) {
|
|
cg_printf("%s", m_originalName.c_str());
|
|
}
|
|
cg_printf("(");
|
|
if (m_params) m_params->outputPHP(cg, ar);
|
|
if (m_stmt) {
|
|
cg_indentBegin(") {\n");
|
|
funcScope->outputPHP(cg, ar);
|
|
m_stmt->outputPHP(cg, ar);
|
|
cg_indentEnd("}\n");
|
|
} else {
|
|
cg_printf(");\n");
|
|
}
|
|
}
|
|
|
|
bool MethodStatement::hasRefParam() {
|
|
for (int i = 0; i < m_params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
|
|
if (param->isRef()) return true;
|
|
}
|
|
return false;
|
|
}
|