8c6d77deef
Instead of having the body of the closure be in the ##__invoke()## on the ##Closure## class, instead we make an anonymous function on the real class and put the body there. The signature for this function is: function methodForClosure$1234($arg1, $arg2, ..., $use1, $use2, ...) and then ##__invoke## now just takes all the params that were passed to it, puts them as the first args to the anonymous function, then takes all the use variables it had saved up and passed them in as the next params. I tried to not have an ##__invoke## at all, but I ended up basically doing the same parameter and use var repacking in iopFCall (and would have had to do it in x86 code too). I opted for doing the rejiggering in bytecode. If I did it in raw PHP I think it would have been much slower with many ##func_get_args()## and array operations.
2640 linhas
90 KiB
C++
2640 linhas
90 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include <compiler/analysis/function_scope.h>
|
|
#include <compiler/analysis/analysis_result.h>
|
|
#include <compiler/expression/constant_expression.h>
|
|
#include <compiler/expression/modifier_expression.h>
|
|
#include <compiler/expression/expression_list.h>
|
|
#include <compiler/expression/function_call.h>
|
|
#include <compiler/analysis/code_error.h>
|
|
#include <compiler/statement/statement_list.h>
|
|
#include <compiler/analysis/file_scope.h>
|
|
#include <compiler/analysis/variable_table.h>
|
|
#include <compiler/parser/parser.h>
|
|
#include <util/logger.h>
|
|
#include <compiler/option.h>
|
|
#include <compiler/statement/method_statement.h>
|
|
#include <compiler/statement/exp_statement.h>
|
|
#include <compiler/expression/parameter_expression.h>
|
|
#include <compiler/analysis/class_scope.h>
|
|
#include <util/atomic.h>
|
|
#include <util/util.h>
|
|
#include <runtime/base/class_info.h>
|
|
#include <runtime/base/type_conversions.h>
|
|
#include <runtime/base/builtin_functions.h>
|
|
#include <util/parser/hphp.tab.hpp>
|
|
#include <runtime/base/variable_serializer.h>
|
|
#include <runtime/base/zend/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_needsRefTemp(false),
|
|
m_needsObjTemp(false), m_needsCheckMem(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) {
|
|
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();
|
|
}
|
|
}
|
|
|
|
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_needsRefTemp(orig->m_needsRefTemp),
|
|
m_needsObjTemp(orig->m_needsObjTemp),
|
|
m_needsCheckMem(orig->m_needsCheckMem),
|
|
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) {
|
|
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_needsRefTemp(false), m_needsObjTemp(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::ignoreRedefinition() const {
|
|
return m_attribute & FileScope::IgnoreRedefinition;
|
|
}
|
|
|
|
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::isClosure() const {
|
|
return ParserBase::IsClosureName(name());
|
|
}
|
|
|
|
bool FunctionScope::isGenerator() const {
|
|
assert(!getOrigGenStmt() ||
|
|
(ParserBase::IsContinuationName(name()) &&
|
|
m_paramNames.size() == 1 &&
|
|
m_paramNames[0] == CONTINUATION_OBJECT_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::setIgnoreRedefinition() {
|
|
m_attribute |= FileScope::IgnoreRedefinition;
|
|
}
|
|
|
|
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();
|
|
}
|
|
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::needsTypeCheckWrapper() const {
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (isRefParam(i)) continue;
|
|
if (TypePtr spec = m_paramTypeSpecs[i]) {
|
|
if (Type::SameType(spec, m_paramTypes[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool FunctionScope::needsClassParam() {
|
|
if (!isStatic()) return false;
|
|
ClassScopeRawPtr cls = getContainingClass();
|
|
if (!ClassScope::NeedStaticArray(cls, FunctionScopeRawPtr(this))) {
|
|
return false;
|
|
}
|
|
return getVariables()->hasStatic();
|
|
}
|
|
|
|
const char *FunctionScope::getPrefix(AnalysisResultPtr ar, ExpressionListPtr params) {
|
|
bool isMethod = getContainingClass();
|
|
bool callInner = false;
|
|
if (Option::HardTypeHints && !Option::SystemGen &&
|
|
!m_system && !m_sep && params) {
|
|
int count = params->getCount();
|
|
if (count >= m_minParam) {
|
|
for (int i = 0; i < count; i++) {
|
|
if (i == m_maxParam) break;
|
|
if (isRefParam(i)) continue;
|
|
if (TypePtr spec = m_paramTypeSpecs[i]) {
|
|
if (Type::SameType(spec, m_paramTypes[i])) {
|
|
ExpressionPtr p = (*params)[i];
|
|
TypePtr at = p->getActualType();
|
|
if (!Type::SubType(ar, at, spec)) {
|
|
callInner = false;
|
|
break;
|
|
}
|
|
callInner = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (callInner) {
|
|
return isMethod ? Option::TypedMethodPrefix : Option::TypedFunctionPrefix;
|
|
}
|
|
|
|
return isMethod ? Option::MethodPrefix : Option::FunctionPrefix;
|
|
}
|
|
|
|
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());
|
|
StringData* sd = new StringData(value, len, AttachLiteral);
|
|
sd->setStatic();
|
|
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::outputCPPDef(CodeGenerator &cg) {
|
|
if (isVolatile()) {
|
|
string name = CodeGenerator::FormatLabel(m_name);
|
|
if (isRedeclaring()) {
|
|
cg_printf("extern const %sCallInfo %s%s;\n",
|
|
isRedeclaring() ? "Redeclared" : "",
|
|
Option::CallInfoPrefix, getId().c_str());
|
|
cg_printf("g->GCI(%s) = &%s%s;\n",
|
|
name.c_str(),
|
|
Option::CallInfoPrefix,
|
|
getId().c_str());
|
|
}
|
|
cg_printf("g->FVF(%s) = true;\n", name.c_str());
|
|
}
|
|
}
|
|
|
|
void FunctionScope::outputCPP(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
ExpressionListPtr params =
|
|
dynamic_pointer_cast<MethodStatement>(getStmt())->getParams();
|
|
|
|
int inTypedWrapper = Option::HardTypeHints ?
|
|
cg.getContext() == CodeGenerator::CppTypedParamsWrapperImpl : -1;
|
|
|
|
string funcName = CodeGenerator::EscapeLabel(getOriginalName());
|
|
if (ClassScopePtr cls = getContainingClass()) {
|
|
funcName = CodeGenerator::EscapeLabel(cls->getOriginalName()) + "::" +
|
|
funcName;
|
|
}
|
|
funcName += "()";
|
|
|
|
/* Typecheck parameters */
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (inTypedWrapper <= 0 && isRefParam(i)) {
|
|
const string &name = getParamName(i);
|
|
string vname = Option::VariablePrefix + CodeGenerator::FormatLabel(name);
|
|
cg_printf("Variant &%s ATTRIBUTE_UNUSED = r%s;\n",
|
|
vname.c_str(), vname.c_str());
|
|
}
|
|
TypePtr specType = m_paramTypeSpecs[i];
|
|
if (!specType) continue;
|
|
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
|
|
ConstantExpressionPtr constPtr;
|
|
bool isDefaultNull =
|
|
param->defaultValue() &&
|
|
(constPtr = dynamic_pointer_cast<ConstantExpression>(
|
|
param->defaultValue())) &&
|
|
constPtr->isNull();
|
|
TypePtr infType = m_paramTypes[i];
|
|
|
|
if (inTypedWrapper >= 0) {
|
|
bool typed = !isRefParam(i) &&
|
|
(Type::SameType(specType, infType) ||
|
|
(infType && infType->isStandardObject() && isDefaultNull));
|
|
// if the infType is a standard object +
|
|
// we have a default null parameter, then we need to
|
|
// do some special runtime checks
|
|
if (!typed == inTypedWrapper) continue;
|
|
}
|
|
|
|
/* Insert runtime checks. */
|
|
if (infType && infType->isStandardObject() && isDefaultNull) {
|
|
cg_indentBegin("if(!%s%s.isNull() && !%s%s.isObject()) {\n",
|
|
Option::VariablePrefix, param->getName().c_str(),
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
cg_printf("throw_unexpected_argument_type(%d,\"%s\",\"%s\",%s%s);\n",
|
|
i + 1, funcName.c_str(), specType->getName().c_str(),
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
if (Option::HardTypeHints) {
|
|
cg_printf("return%s;\n", getReturnType() ? " null" : "");
|
|
}
|
|
cg_indentEnd("}\n");
|
|
} else if (specType->is(Type::KindOfObject)) {
|
|
cg_printf("if(");
|
|
if (!m_paramDefaults[i].empty()) {
|
|
cg_printf("!f_is_null(%s%s) && ",
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
}
|
|
cg_printf("!%s%s.instanceof(", Option::VariablePrefix,
|
|
param->getName().c_str());
|
|
cg_printString(specType->getName(), ar, shared_from_this());
|
|
cg_indentBegin(")) {\n");
|
|
cg_printf("throw_unexpected_argument_type(%d,\"%s\",\"%s\",%s%s);\n",
|
|
i + 1, funcName.c_str(), specType->getName().c_str(),
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
if (Option::HardTypeHints) {
|
|
cg_printf("return%s;\n", getReturnType() ? " null" : "");
|
|
}
|
|
cg_indentEnd("}\n");
|
|
} else {
|
|
cg_printf("if(");
|
|
if (!m_paramDefaults[i].empty() &&
|
|
(isRefParam(i) || isDefaultNull)) {
|
|
cg_printf("!f_is_null(%s%s) && ",
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
}
|
|
const char *p = 0;
|
|
switch (specType->getKindOf()) {
|
|
case Type::KindOfArray: p = "Array"; break;
|
|
case Type::KindOfBoolean: p = "Boolean"; break;
|
|
case Type::KindOfInt64: p = "Integer"; break;
|
|
case Type::KindOfDouble: p = "Double"; break;
|
|
case Type::KindOfString: p = "String"; break;
|
|
default: not_reached();
|
|
}
|
|
cg_indentBegin("!%s%s.is%s()) {\n",
|
|
Option::VariablePrefix, param->getName().c_str(), p);
|
|
cg_printf("throw_unexpected_argument_type"
|
|
"(%d,\"%s\",\"%s\",%s%s);\n",
|
|
i + 1, funcName.c_str(), p,
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
if (Option::HardTypeHints) {
|
|
cg_printf("return%s;\n", getReturnType() ? " null" : "");
|
|
}
|
|
cg_indentEnd("}\n");
|
|
}
|
|
}
|
|
|
|
if (inTypedWrapper <= 0) {
|
|
BlockScope::outputCPP(cg, ar);
|
|
|
|
if (isVariableArgument()) {
|
|
VariableTablePtr variables = getVariables();
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
const string &name = getParamName(i);
|
|
Symbol *sym = variables->getSymbol(name);
|
|
bool ref = isRefParam(i);
|
|
if (ref ? sym->isReseated() : sym->isLvalParam()) {
|
|
sym->setStashedVal();
|
|
TypePtr paramType = getParamType(i);
|
|
paramType->outputCPPDecl(cg, ar, shared_from_this());
|
|
string vname = Option::VariablePrefix +
|
|
CodeGenerator::FormatLabel(name);
|
|
if (!ref || paramType->isExactType()) {
|
|
cg_printf(" v%s = %s;\n", vname.c_str(), vname.c_str());
|
|
} else {
|
|
cg_printf(" v%s = strongBind(%s);\n",
|
|
vname.c_str(), vname.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ParameterExpressionPtrVec useVars;
|
|
if (needsAnonClosureClass(useVars)) {
|
|
if (!m_closureGenerator) {
|
|
VariableTablePtr variables = getVariables();
|
|
BOOST_FOREACH(ParameterExpressionPtr param, useVars) {
|
|
const string &name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
assert(sym->isUsed());
|
|
TypePtr t(sym->getFinalType());
|
|
assert(!param->isRef() || t->is(Type::KindOfVariant));
|
|
if (t->is(Type::KindOfVariant)) {
|
|
const char *s = param->isRef() ? "Ref" : "Val";
|
|
cg_printf("%s%s.assign%s(closure->%s%s);\n",
|
|
Option::VariablePrefix, name.c_str(), s,
|
|
Option::VariablePrefix, name.c_str());
|
|
} else {
|
|
cg_printf("%s%s = closure->%s%s;\n",
|
|
Option::VariablePrefix, name.c_str(),
|
|
Option::VariablePrefix, name.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FunctionScope::outputCPPParamsDecl(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
ExpressionListPtr params,
|
|
bool showDefault) {
|
|
if (isVariableArgument()) {
|
|
if (m_name[0] == '0' && !m_method) {
|
|
cg_printf("void *extra, ");
|
|
}
|
|
cg_printf("int num_args, ");
|
|
if (params) {
|
|
cg.setInExpression(true);
|
|
params->outputCPP(cg, ar);
|
|
cg.setInExpression(false);
|
|
cg_printf(", ");
|
|
}
|
|
if (showDefault) {
|
|
cg_printf("Array args = Array()");
|
|
} else {
|
|
cg_printf("Array args /* = Array() */");
|
|
}
|
|
} else if (m_pseudoMain) {
|
|
cg_printf("bool incOnce, LVariableTable* variables, Globals *globals");
|
|
} else if (params) {
|
|
if (m_name[0] == '0' && !m_method) {
|
|
cg_printf("void *extra, ");
|
|
}
|
|
cg.setInExpression(true);
|
|
params->outputCPP(cg, ar);
|
|
cg.setInExpression(false);
|
|
} else if (m_name[0] == '0' && !m_method) {
|
|
cg_printf("void *extra");
|
|
}
|
|
}
|
|
|
|
|
|
void FunctionScope::outputCPPParamsImpl(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
int paramcount = getMaxParamCount();
|
|
if (isUserFunction()) {
|
|
MethodStatementPtr m = dynamic_pointer_cast<MethodStatement>(getStmt());
|
|
outputCPPParamsDecl(cg, ar, m->getParams(), false);
|
|
return;
|
|
}
|
|
bool first = true;
|
|
if (isVariableArgument()) {
|
|
cg_printf("int num_args");
|
|
first = false;
|
|
}
|
|
|
|
for (int i = 0; i < paramcount; i++) {
|
|
if (first) {
|
|
first = false;
|
|
} else {
|
|
cg_printf(", ");
|
|
}
|
|
if (isRefParam(i)) {
|
|
cg_printf("VRefParam");
|
|
} else {
|
|
TypePtr type = getParamType(i);
|
|
type->outputCPPDecl(cg, ar, BlockScopeRawPtr(this));
|
|
}
|
|
cg_printf(" a%d", i);
|
|
}
|
|
|
|
if (isVariableArgument()) {
|
|
cg_printf(", Array args /* = Array() */");
|
|
}
|
|
}
|
|
|
|
bool FunctionScope::outputCPPArrayCreate(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
int m_maxParam) {
|
|
assert(m_maxParam >= 0);
|
|
assert(m_attribute & FileScope::VariableArgument);
|
|
if (!Option::GenArrayCreate ||
|
|
cg.getOutput() == CodeGenerator::SystemCPP ||
|
|
m_maxParam == 0) {
|
|
return false;
|
|
}
|
|
if (isUserFunction()) {
|
|
MethodStatementPtr stmt;
|
|
stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
if (stmt->hasRefParam()) return false;
|
|
cg_printf("Array(");
|
|
stmt->outputParamArrayCreate(cg, false);
|
|
} else if (hasRefParam(m_maxParam)) {
|
|
assert(false);
|
|
return false;
|
|
} else {
|
|
assert(false);
|
|
cg_printf("Array(");
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
cg_printf("%d, a%d", i, i);
|
|
if (i < m_maxParam - 1) {
|
|
cg_printf(", ");
|
|
} else {
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
}
|
|
cg_printf(")");
|
|
cg_printf(isVariableArgument() ? ",args" : ",Array()");
|
|
return true;
|
|
}
|
|
|
|
void FunctionScope::outputCPPParamsCall(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
bool aggregateParams) {
|
|
if (isVariableArgument()) {
|
|
cg_printf("num_args, ");
|
|
} else if (aggregateParams) {
|
|
cg_printf("%d, ", m_maxParam);
|
|
}
|
|
if (aggregateParams && outputCPPArrayCreate(cg, ar, m_maxParam)) {
|
|
return;
|
|
}
|
|
bool userFunc = isUserFunction();
|
|
CodeGenerator::Context context = cg.getContext();
|
|
bool isWrapper = !aggregateParams &&
|
|
(context == CodeGenerator::CppTypedParamsWrapperImpl ||
|
|
context == CodeGenerator::CppTypedParamsWrapperDecl ||
|
|
context == CodeGenerator::CppFunctionWrapperImpl ||
|
|
context == CodeGenerator::CppFunctionWrapperDecl);
|
|
|
|
MethodStatementPtr stmt;
|
|
ExpressionListPtr params;
|
|
if (userFunc) {
|
|
stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
|
|
params = stmt->getParams();
|
|
}
|
|
|
|
VariableTablePtr variables = getVariables();
|
|
if (aggregateParams) {
|
|
cg_printf("Array(");
|
|
if (m_maxParam) {
|
|
// param arrays are always vectors
|
|
cg_printf("ArrayInit(%d, ArrayInit::vectorInit).", m_maxParam);
|
|
}
|
|
}
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (i > 0) cg_printf(aggregateParams ? "." : ", ");
|
|
bool isRef;
|
|
if (userFunc) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
isRef = !isWrapper && param->isRef();
|
|
if (aggregateParams) {
|
|
cg_printf("set(%s%s%s",
|
|
variables->getSymbol(param->getName())->isStashedVal() ?
|
|
"v" : "",
|
|
Option::VariablePrefix, param->getName().c_str());
|
|
} else {
|
|
cg_printf("%s%s%s%s",
|
|
isRef ? "ref(" : "",
|
|
Option::VariablePrefix, param->getName().c_str(),
|
|
isRef ? ")" : "");
|
|
}
|
|
} else {
|
|
isRef = !isWrapper && isRefParam(i);
|
|
if (aggregateParams) {
|
|
assert(false);
|
|
cg_printf("set(a%d", i);
|
|
} else {
|
|
cg_printf("%sa%d%s",
|
|
isRef ? "ref(" : "", i, isRef ? ")" : "");
|
|
}
|
|
}
|
|
if (aggregateParams) cg_printf(")");
|
|
}
|
|
if (aggregateParams) {
|
|
if (m_maxParam) cg_printf(".create()");
|
|
cg_printf(")");
|
|
}
|
|
if (isVariableArgument()) {
|
|
if (aggregateParams || m_maxParam > 0) cg_printf(",");
|
|
cg_printf("args");
|
|
} else if (aggregateParams) {
|
|
cg_printf(", Array()");
|
|
}
|
|
}
|
|
|
|
void FunctionScope::OutputCPPArguments(ExpressionListPtr params,
|
|
FunctionScopePtr func,
|
|
CodeGenerator &cg,
|
|
AnalysisResultPtr ar, int extraArg,
|
|
bool variableArgument,
|
|
int extraArgArrayId /* = -1 */,
|
|
int extraArgArrayHash /* = -1 */,
|
|
int extraArgArrayIndex /* = -1 */,
|
|
bool ignoreFuncParamTypes /* = false */)
|
|
{
|
|
int paramCount = params ? params->getOutputCount() : 0;
|
|
assert(extraArg <= paramCount);
|
|
int iMax = paramCount - extraArg;
|
|
bool extra = false;
|
|
bool callUserFuncFewArgs = false;
|
|
|
|
if (variableArgument) {
|
|
callUserFuncFewArgs =
|
|
Option::UseCallUserFuncFewArgs &&
|
|
func->getName() == "call_user_func" &&
|
|
(paramCount <= CALL_USER_FUNC_FEW_ARGS_COUNT + 1) &&
|
|
extraArgArrayId == -1;
|
|
|
|
if (!callUserFuncFewArgs) {
|
|
if (paramCount == 0) {
|
|
cg_printf("0");
|
|
} else {
|
|
cg_printf("%d, ", paramCount);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < paramCount; i++) {
|
|
ExpressionPtr param = (*params)[i];
|
|
if (i > 0) cg_printf(extra && !callUserFuncFewArgs ? "." : ", ");
|
|
if (!extra && (i == iMax || extraArg < 0)) {
|
|
if (extraArgArrayId != -1) {
|
|
always_assert(extraArgArrayHash != -1 && extraArgArrayIndex != -1);
|
|
ar->outputCPPScalarArrayId(cg, extraArgArrayId, extraArgArrayHash,
|
|
extraArgArrayIndex);
|
|
break;
|
|
}
|
|
extra = true;
|
|
if (!callUserFuncFewArgs) {
|
|
// Parameter arrays are always vectors.
|
|
if (Option::GenArrayCreate &&
|
|
cg.getOutput() != CodeGenerator::SystemCPP) {
|
|
if (!params->hasNonArrayCreateValue(false, i)) {
|
|
always_assert(!callUserFuncFewArgs);
|
|
if (ar->m_arrayIntegerKeyMaxSize < paramCount - i) {
|
|
ar->m_arrayIntegerKeyMaxSize = paramCount - i;
|
|
}
|
|
cg_printf("Array(");
|
|
params->outputCPPUniqLitKeyArrayInit(cg, ar, false,
|
|
paramCount - i, false, i);
|
|
cg_printf(")");
|
|
return;
|
|
}
|
|
}
|
|
cg_printf("Array(ArrayInit(%d, ArrayInit::vectorInit).",
|
|
paramCount - i);
|
|
}
|
|
}
|
|
if (extra) {
|
|
bool needRef = param->hasContext(Expression::RefValue) &&
|
|
!param->hasContext(Expression::NoRefWrapper) &&
|
|
param->isRefable();
|
|
if (!callUserFuncFewArgs) {
|
|
cg_printf("set%s(", needRef ? "Ref" : "");
|
|
}
|
|
if (needRef) {
|
|
// The parameter itself shouldn't be wrapped with ref() any more.
|
|
param->setContext(Expression::NoRefWrapper);
|
|
}
|
|
param->outputCPP(cg, ar);
|
|
if (!callUserFuncFewArgs) cg_printf(")");
|
|
} else {
|
|
// If the implemented type is ref-counted and expected type is variant,
|
|
// use VarNR to avoid unnecessary ref-counting because we know
|
|
// the actual argument will always has a ref in the callee.
|
|
bool wrap = false, wrapv = false;
|
|
bool scalar = param->isScalar();
|
|
bool isValidFuncIdx = func && i < func->getMaxParamCount();
|
|
TypePtr expType(
|
|
isValidFuncIdx && !ignoreFuncParamTypes ?
|
|
// the common case, where we read the param type from the function
|
|
// scope
|
|
func->getParamType(i) :
|
|
|
|
// the uncommon case
|
|
!func && !ignoreFuncParamTypes ?
|
|
|
|
// if we don't have a function, then we can assume its param type
|
|
// is Variant. however, we only want to do this if we aren't
|
|
// ignoring function param types
|
|
Type::Variant :
|
|
|
|
// in this case, just use the expected type from the parameter
|
|
// ast node (that's the default behavior of CheckVarNR)
|
|
TypePtr());
|
|
if (Expression::CheckVarNR(param, expType)) {
|
|
if (scalar) {
|
|
assert(!cg.hasScalarVariant());
|
|
cg.setScalarVariant();
|
|
}
|
|
if (!scalar ||
|
|
(!Option::UseScalarVariant && !param->isLiteralNull())) {
|
|
wrap = true;
|
|
}
|
|
if (isValidFuncIdx) {
|
|
VariableTablePtr variables = func->getVariables();
|
|
if (variables->isLvalParam(func->getParamName(i))) {
|
|
// Callee expects a Variant instead of CVarRef
|
|
wrap = false;
|
|
if (scalar) cg.clearScalarVariant();
|
|
}
|
|
}
|
|
} else if (isValidFuncIdx &&
|
|
!param->hasCPPTemp() &&
|
|
func->getVariables()->isLvalParam(func->getParamName(i)) &&
|
|
!param->hasAnyContext(Expression::RefValue|
|
|
Expression::RefParameter|
|
|
Expression::InvokeArgument)) {
|
|
FunctionCallPtr f = dynamic_pointer_cast<FunctionCall>(param);
|
|
if (f) {
|
|
if (f->getFuncScope()) {
|
|
wrapv = f->getFuncScope()->isRefReturn();
|
|
} else if (!f->getName().empty()) {
|
|
FunctionInfoPtr info = GetFunctionInfo(f->getName());
|
|
wrapv = info && info->getMaybeRefReturn();
|
|
} else {
|
|
wrapv = true;
|
|
}
|
|
if (wrapv) cg_printf("wrap_variant(");
|
|
}
|
|
}
|
|
if (wrap) cg_printf("VarNR(");
|
|
param->outputCPP(cg, ar);
|
|
if (scalar) cg.clearScalarVariant();
|
|
assert(!cg.hasScalarVariant());
|
|
if (wrap || wrapv) {
|
|
cg_printf(")");
|
|
}
|
|
}
|
|
}
|
|
if (extra) {
|
|
if (!callUserFuncFewArgs) cg_printf(".create())");
|
|
}
|
|
}
|
|
|
|
void FunctionScope::OutputCPPEffectiveArguments(ExpressionListPtr params,
|
|
CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
int paramCount = params ? params->getCount() : 0;
|
|
for (int i = 0; i < paramCount; i++) {
|
|
ExpressionPtr param = (*params)[i];
|
|
if (param->hasEffect()) {
|
|
param->outputCPP(cg, ar);
|
|
cg_printf(", ");
|
|
}
|
|
}
|
|
}
|
|
|
|
void FunctionScope::OutputCPPDynamicInvokeCount(CodeGenerator &cg) {
|
|
cg_printf("int count ATTRIBUTE_UNUSED = params.size();\n");
|
|
}
|
|
|
|
int FunctionScope::outputCPPInvokeArgCountCheck(
|
|
CodeGenerator &cg, AnalysisResultPtr ar,
|
|
bool ret, bool constructor, int maxCount) {
|
|
bool variable = isVariableArgument();
|
|
// system function has different handling of argument counts
|
|
bool system = (m_system || m_sep ||
|
|
cg.getOutput() == CodeGenerator::SystemCPP);
|
|
if (!system) {
|
|
ClassScopePtr scope = getContainingClass();
|
|
if (scope) system = (!scope->isUserClass() || scope->isExtensionClass() ||
|
|
scope->isSepExtension());
|
|
}
|
|
|
|
bool checkMissing = (m_minParam > 0);
|
|
bool checkTooMany = (!variable && (system ||
|
|
RuntimeOption::ThrowTooManyArguments));
|
|
const char *sysret = (system && ret) ? "return " : "";
|
|
const char *level = (system ? (constructor ? ", 2" : ", 1") : "");
|
|
int guarded = system && (ret || constructor) ? m_minParam : 0;
|
|
string fullname = CodeGenerator::EscapeLabel(getOriginalFullName());
|
|
if (checkMissing) {
|
|
bool fullGuard = ret && (system || Option::HardTypeHints);
|
|
for (int i = 0; i < m_minParam; i++) {
|
|
if (TypePtr t = m_paramTypeSpecs[i]) {
|
|
if (m_paramDefaults[i].empty()) {
|
|
if (i < maxCount) cg_printf("if (UNLIKELY(count < %d)) ", i + 1);
|
|
cg_printf("%sthrow_missing_typed_argument(\"%s\", ",
|
|
fullGuard ? "return " : "", fullname.c_str());
|
|
cg_printf(t->is(Type::KindOfArray) ?
|
|
"0" : "\"%s\"",
|
|
CodeGenerator::EscapeLabel(t->getName()).c_str());
|
|
cg_printf(", %d);\n", i + 1);
|
|
if (fullGuard) {
|
|
if (i >= maxCount) return m_minParam;
|
|
if (guarded <= i) guarded = i + 1;
|
|
if (i + 1 == m_minParam) {
|
|
checkMissing = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (checkMissing && checkTooMany) {
|
|
if (!variable && m_minParam == m_maxParam) {
|
|
if (maxCount >= m_minParam) {
|
|
cg_printf("if (UNLIKELY(count != %d))", m_minParam);
|
|
}
|
|
cg_printf(" %sthrow_wrong_arguments(\"%s\", count, %d, %d%s);\n",
|
|
sysret, fullname.c_str(), m_minParam, m_maxParam, level);
|
|
} else {
|
|
if (maxCount >= m_minParam) {
|
|
if (maxCount <= m_maxParam) {
|
|
cg_printf("if (UNLIKELY(count < %d))", m_minParam);
|
|
} else {
|
|
cg_printf("if (UNLIKELY(count < %d || count > %d))",
|
|
m_minParam, m_maxParam);
|
|
}
|
|
}
|
|
cg_printf(" %sthrow_wrong_arguments(\"%s\", count, %d, %d%s);\n",
|
|
sysret, fullname.c_str(),
|
|
m_minParam, variable ? -1 : m_maxParam, level);
|
|
}
|
|
} else if (checkMissing) {
|
|
if (maxCount >= m_minParam) {
|
|
cg_printf("if (UNLIKELY(count < %d))", m_minParam);
|
|
}
|
|
cg_printf(" %sthrow_missing_arguments(\"%s\", count+1%s);\n",
|
|
sysret, fullname.c_str(), level);
|
|
} else if (checkTooMany && maxCount > m_maxParam) {
|
|
cg_printf("if (UNLIKELY(count > %d))"
|
|
" %sthrow_toomany_arguments(\"%s\", %d%s);\n",
|
|
m_maxParam, sysret, fullname.c_str(), m_maxParam, level);
|
|
}
|
|
return guarded;
|
|
}
|
|
|
|
void FunctionScope::outputCPPDynamicInvoke(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
const char *funcPrefix,
|
|
const char *name,
|
|
bool voidWrapperOff /* = false */,
|
|
bool fewArgs /* = false */,
|
|
bool ret /* = true */,
|
|
const char *extraArg /* = NULL */,
|
|
bool constructor /* = false */,
|
|
const char *instance /* = NULL */,
|
|
const char *class_name /* = "" */) {
|
|
const char *voidWrapper = (m_returnType || voidWrapperOff) ? "" : ", null";
|
|
const char *retrn = ret ? "return " : "";
|
|
bool variable = isVariableArgument();
|
|
bool refVariable = isReferenceVariableArgument();
|
|
int maxCount = fewArgs ? Option::InvokeFewArgsCount : INT_MAX;
|
|
bool useDefaults = !m_stmt || ar->isSystem();
|
|
|
|
assert(m_minParam >= 0);
|
|
|
|
int guarded = outputCPPInvokeArgCountCheck(cg, ar, ret,
|
|
constructor, maxCount);
|
|
if (guarded > maxCount) return;
|
|
|
|
bool do_while = !ret && !fewArgs && m_maxParam > m_minParam && useDefaults;
|
|
cg_printf("%s", class_name);
|
|
if (!fewArgs && m_maxParam) {
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
if (isRefParam(i)) {
|
|
cg_printf("const_cast<Array&>(params).escalate(true);\n");
|
|
break;
|
|
}
|
|
}
|
|
if (do_while) cg_printf("do ");
|
|
cg_indentBegin("{\n");
|
|
cg_printf("ArrayData *ad(params.get());\n");
|
|
cg_printf("ssize_t pos = ad ? ad->iter_begin() : "
|
|
"ArrayData::invalid_index;\n");
|
|
}
|
|
|
|
CodeGenerator::Context context = cg.getContext();
|
|
std::vector<Variant> defs;
|
|
|
|
int i = !m_minParam ? -1 : 0;
|
|
while (true) {
|
|
if (i >= 0 && i < m_maxParam) {
|
|
bool ref = isRefParam(i);
|
|
int defIndex = -1;
|
|
bool wantNullVariantNotNull = fewArgs ? i < maxCount : ref;
|
|
bool dftNull = false;
|
|
bool isCVarRef = false;
|
|
if (!m_paramDefaults[i].empty() && !useDefaults) {
|
|
Variant tmp;
|
|
MethodStatementPtr m(dynamic_pointer_cast<MethodStatement>(m_stmt));
|
|
ExpressionListPtr params = m->getParams();
|
|
always_assert(params && params->getCount() > i);
|
|
ParameterExpressionPtr p(
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]));
|
|
ExpressionPtr defVal = p->defaultValue();
|
|
bool isScalar = defVal->isScalar();
|
|
if (Option::UseScalarVariant && isScalar && !ref && fewArgs) isCVarRef = true;
|
|
dftNull = isScalar && defVal->getScalarValue(tmp) && tmp.isNull();
|
|
if (!isCVarRef && wantNullVariantNotNull && !dftNull && !ref) {
|
|
cg_printf("TypedValue def%d;\n", i);
|
|
defIndex = i;
|
|
}
|
|
}
|
|
cg_printf("%s arg%d(", ref ? "VRefParam" : "CVarRef", i);
|
|
if (m_paramDefaults[i].empty()) {
|
|
if (i >= guarded) {
|
|
if (i < maxCount) cg_printf("UNLIKELY(count <= %d) ? ", i);
|
|
cg_printf(ref ? "(VRefParamValue())" :
|
|
(wantNullVariantNotNull ? "null_variant" : "null"));
|
|
if (i < maxCount) cg_printf(" : ");
|
|
}
|
|
} else if (!useDefaults) {
|
|
bool close = false;
|
|
if (i < maxCount) {
|
|
cg_printf("count <= %d ? ", i);
|
|
} else if (defIndex < 0) {
|
|
cg_printf("(");
|
|
close = true;
|
|
}
|
|
if (dftNull) {
|
|
cg_printf(ref ? "(VRefParamValue())" :
|
|
(wantNullVariantNotNull ? "null_variant" : "null"));
|
|
} else {
|
|
MethodStatementPtr m(dynamic_pointer_cast<MethodStatement>(m_stmt));
|
|
ExpressionListPtr params = m->getParams();
|
|
always_assert(params && params->getCount() > i);
|
|
ParameterExpressionPtr p(
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]));
|
|
|
|
if (defIndex >= 0) {
|
|
if (!isCVarRef) cg_printf("*new (&def%d) Variant(", defIndex);
|
|
} else {
|
|
ExpressionPtr defVal = p->defaultValue();
|
|
bool isScalar = defVal->isScalar();
|
|
if (Option::UseScalarVariant && isScalar && !ref && fewArgs) isCVarRef = true;
|
|
TypePtr type = p->defaultValue()->getCPPType();
|
|
bool isVariant = type && Type::IsMappedToVariant(type);
|
|
cg_printf("%s(", ref ? "VRefParamValue" :
|
|
(!fewArgs || i >= maxCount) && !isVariant && !isCVarRef ?
|
|
"Variant" : "");
|
|
}
|
|
cg.setContext(CodeGenerator::CppParameterDefaultValueDecl);
|
|
ExpressionPtr defVal = p->defaultValue();
|
|
if (isCVarRef) {
|
|
assert(!cg.hasScalarVariant());
|
|
cg.setScalarVariant();
|
|
}
|
|
|
|
TypePtr exp = defVal->getExpectedType();
|
|
defVal->setExpectedType(TypePtr());
|
|
defVal->outputCPP(cg, ar);
|
|
defVal->setExpectedType(exp);
|
|
|
|
if (isCVarRef) cg.clearScalarVariant();
|
|
assert(!cg.hasScalarVariant());
|
|
cg.setContext(context);
|
|
cg_printf(")");
|
|
}
|
|
if (close) cg_printf(")");
|
|
if (i < maxCount) cg_printf(" : ");
|
|
}
|
|
if (fewArgs) {
|
|
if (i < maxCount) cg_printf(ref ? "vref(a%d)" : "a%d", i);
|
|
} else {
|
|
cg_printf("%s(ad->getValue%s(pos%s))",
|
|
ref ? "vref" : "",
|
|
ref ? "Ref" : "",
|
|
i ? " = ad->iter_advance(pos)" : "");
|
|
}
|
|
cg_printf(");\n");
|
|
}
|
|
if (++i < m_minParam) continue;
|
|
const char *extra = "";
|
|
bool last = i >= maxCount || i == m_maxParam;
|
|
if (!useDefaults && !last) continue;
|
|
|
|
if (variable && last) {
|
|
if (fewArgs) {
|
|
if (i < Option::InvokeFewArgsCount) {
|
|
cg_printf("Array p;\n");
|
|
for (int j = i; j < Option::InvokeFewArgsCount; j++) {
|
|
if (refVariable) {
|
|
cg_printf("if (count >= %d) p.append(ref(a%d));\n", j + 1, j);
|
|
} else {
|
|
cg_printf("if (count >= %d) p.append(a%d);\n", j + 1, j);
|
|
}
|
|
}
|
|
extra = ", p";
|
|
}
|
|
} else {
|
|
if (m_maxParam) {
|
|
cg_printf("const Array &p(count > %d ? "
|
|
"params.slice(%d, count - %d, false) : Array());\n",
|
|
m_maxParam, m_maxParam, m_maxParam);
|
|
} else {
|
|
// in this case, avoid the un-necessary call to params.slice(),
|
|
// since we want the entire params array anyways
|
|
assert(m_maxParam == 0);
|
|
cg_printf("const Array &p(count > 0 ? "
|
|
"ArrayUtil::EnsureIntKeys(params) : Array());\n");
|
|
}
|
|
extra = ", p";
|
|
}
|
|
}
|
|
|
|
if (!last) {
|
|
cg_printf("if (count <= %d) ", i);
|
|
if (do_while) {
|
|
cg_indentBegin("{\n");
|
|
}
|
|
}
|
|
cg_printf("%s%s%s", retrn, (m_refReturn ? "strongBind(" : "("),
|
|
instance ? instance : "");
|
|
if (m_perfectVirtual) {
|
|
ClassScopePtr cls = getContainingClass();
|
|
cg_printf("%s%s::", Option::ClassPrefix, cls->getId().c_str());
|
|
}
|
|
cg_printf("%s%s(", funcPrefix, name);
|
|
|
|
bool preArgs = false;
|
|
if (name[0] == '0' && !m_method) {
|
|
cg_printf("extra"); // closure
|
|
preArgs = true;
|
|
}
|
|
if (extraArg) {
|
|
if (preArgs) cg_printf(", ");
|
|
cg_printf("%s", extraArg);
|
|
preArgs = true;
|
|
}
|
|
if (variable) {
|
|
if (preArgs) cg_printf(", ");
|
|
cg_printf("count");
|
|
preArgs = true;
|
|
}
|
|
for (int j = 0; j < i; j++) {
|
|
if (j >= m_minParam && j >= maxCount) break;
|
|
if (preArgs || j > 0) cg_printf(", ");
|
|
cg_printf("arg%d", j);
|
|
}
|
|
cg_printf("%s)%s);\n", extra, voidWrapper);
|
|
if (last) break;
|
|
if (do_while) {
|
|
cg_printf("break;\n");
|
|
cg_indentEnd("}\n");
|
|
}
|
|
}
|
|
|
|
if (!fewArgs && m_maxParam) {
|
|
if (do_while) {
|
|
cg_indentEnd("} while (false);\n");
|
|
} else {
|
|
cg_indentEnd("}\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
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::outputCPPCreateDecl(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
ClassScopePtr scope = getContainingClass();
|
|
CodeGenerator::Context context = cg.getContext();
|
|
bool setWrapper = Option::HardTypeHints && needsTypeCheckWrapper();
|
|
|
|
cg_printf("public: %s%s *create(",
|
|
Option::ClassPrefix, scope->getId().c_str());
|
|
cg.setContext(setWrapper ?
|
|
CodeGenerator::CppTypedParamsWrapperDecl :
|
|
CodeGenerator::CppFunctionWrapperDecl);
|
|
outputCPPParamsDecl(cg, ar,
|
|
dynamic_pointer_cast<MethodStatement>(getStmt())
|
|
->getParams(), true);
|
|
cg.setContext(context);
|
|
cg_printf(");\n");
|
|
}
|
|
|
|
void FunctionScope::outputCPPCreateImpl(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
ClassScopePtr scope = getContainingClass();
|
|
string clsNameStr = scope->getId();
|
|
const char *clsName = clsNameStr.c_str();
|
|
const string &funcNameStr = this->getName();
|
|
const char *consName = funcNameStr.c_str();
|
|
CodeGenerator::Context context = cg.getContext();
|
|
bool setWrapper = Option::HardTypeHints && needsTypeCheckWrapper();
|
|
|
|
cg_printf("%s%s *%s%s::create(",
|
|
Option::ClassPrefix, clsName, Option::ClassPrefix, clsName);
|
|
|
|
cg.setContext(setWrapper ?
|
|
CodeGenerator::CppTypedParamsWrapperImpl :
|
|
CodeGenerator::CppFunctionWrapperImpl);
|
|
outputCPPParamsImpl(cg, ar);
|
|
cg_indentBegin(") {\n");
|
|
cg_printf("CountableHelper h(this);\n");
|
|
cg_printf("init();\n");
|
|
cg_printf("%s%s(", Option::MethodPrefix, consName);
|
|
outputCPPParamsCall(cg, ar, false);
|
|
cg.setContext(context);
|
|
cg_printf(");\n");
|
|
if (scope->derivesFromRedeclaring() ||
|
|
scope->hasAttribute(ClassScope::HasDestructor, ar)) {
|
|
cg_printf("clearNoDestruct();\n");
|
|
}
|
|
cg_printf("return this;\n");
|
|
cg_indentEnd("}\n");
|
|
}
|
|
|
|
void FunctionScope::outputCPPClassMap(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
if (m_method && ParserBase::IsAnonFunctionName(m_name)) {
|
|
return;
|
|
}
|
|
int attribute = ClassInfo::IsNothing;
|
|
if (!isUserFunction()) attribute |= ClassInfo::IsSystem;
|
|
if (!m_method && isSepExtension()) attribute |= ClassInfo::IsPrivate;
|
|
if (isVolatile() && !isRedeclaring()) attribute |= ClassInfo::IsVolatile;
|
|
if (isRefReturn()) attribute |= ClassInfo::IsReference;
|
|
|
|
if (isProtected()) {
|
|
attribute |= ClassInfo::IsProtected;
|
|
} else if (isPrivate()) {
|
|
attribute |= ClassInfo::IsPrivate;
|
|
} else {
|
|
attribute |= ClassInfo::IsPublic;
|
|
}
|
|
if (isAbstract()) attribute |= ClassInfo::IsAbstract;
|
|
if (isStatic()) {
|
|
attribute |= ClassInfo::IsStatic;
|
|
}
|
|
if (isFinal()) attribute |= ClassInfo::IsFinal;
|
|
|
|
if (isVariableArgument()) {
|
|
attribute |= ClassInfo::VariableArguments;
|
|
}
|
|
if (isReferenceVariableArgument()) {
|
|
attribute |= ClassInfo::RefVariableArguments;
|
|
}
|
|
if (isMixedVariableArgument()) {
|
|
attribute |= ClassInfo::MixedVariableArguments;
|
|
}
|
|
if (hasGeneratorAsBody()) {
|
|
attribute |= ClassInfo::HasGeneratorAsBody;
|
|
}
|
|
if (isClosure()) {
|
|
attribute |= ClassInfo::IsClosure;
|
|
}
|
|
|
|
attribute |= m_attributeClassInfo;
|
|
if (!m_docComment.empty() && Option::GenerateDocComments) {
|
|
attribute |= ClassInfo::HasDocComment;
|
|
} else {
|
|
attribute &= ~ClassInfo::HasDocComment;
|
|
}
|
|
|
|
if (m_method && isConstructor(getContainingClass())) {
|
|
attribute |= ClassInfo::IsConstructor;
|
|
}
|
|
if (needsActRec()) {
|
|
attribute |= ClassInfo::NeedsActRec;
|
|
}
|
|
|
|
// Use the original cased name, for reflection to work correctly.
|
|
cg_printf("(const char *)0x%04X, \"%s\", \"%s\", (const char *)%d, "
|
|
"(const char *)%d,\n", attribute,
|
|
CodeGenerator::EscapeLabel(getOriginalName()).c_str(),
|
|
m_stmt ? m_stmt->getLocation()->file : "",
|
|
m_stmt ? m_stmt->getLocation()->line0 : 0,
|
|
m_stmt ? m_stmt->getLocation()->line1 : 0);
|
|
|
|
|
|
if (attribute & ClassInfo::IsVolatile) {
|
|
cg_printf("(const char*)offsetof(GlobalVariables, FVF(%s)),\n",
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
}
|
|
|
|
if (!m_docComment.empty() && Option::GenerateDocComments) {
|
|
std::string dc = string_cplus_escape(m_docComment.c_str(), m_docComment.size());
|
|
cg_printf("\"%s\",\n", dc.c_str());
|
|
}
|
|
|
|
if (attribute & ClassInfo::IsSystem) {
|
|
if (m_returnType) {
|
|
cg_printf("(const char *)0x%x, ", m_returnType->getDataType());
|
|
} else {
|
|
cg_printf("(const char *)-1, ");
|
|
}
|
|
}
|
|
|
|
Variant defArg;
|
|
for (int i = 0; i < m_maxParam; i++) {
|
|
int attr = ClassInfo::IsNothing;
|
|
if (isRefParam(i)) attr |= ClassInfo::IsReference;
|
|
|
|
cg_printf("(const char *)0x%04X, \"%s\", ",
|
|
attr, m_paramNames[i].c_str());
|
|
|
|
MethodStatementPtr m =
|
|
dynamic_pointer_cast<MethodStatement>(getStmt());
|
|
if (m) {
|
|
ExpressionListPtr params = m->getParams();
|
|
always_assert(i < params->getCount());
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
always_assert(param);
|
|
cg_printf("\"%s\", ", param->hasTypeHint() ?
|
|
param->getOriginalTypeHint().c_str() : "");
|
|
if (attribute & ClassInfo::IsSystem) {
|
|
cg_printf("(const char *)-1, ");
|
|
}
|
|
ExpressionPtr def = param->defaultValue();
|
|
if (def) {
|
|
std::string text, value;
|
|
if (!def->isScalar() || !def->getScalarValue(defArg)) {
|
|
/**
|
|
* Special value runtime/ext/ext_reflection.cpp can check and throw.
|
|
* If we want to avoid seeing this so to make getDefaultValue()
|
|
* work better for reflections, we will have to implement
|
|
* getScalarValue() to greater extent under compiler/expressions.
|
|
*/
|
|
value = "\x01";
|
|
text = def->getText();
|
|
} else {
|
|
String str = f_serialize(defArg);
|
|
value = string_cplus_escape(str.data(), str.size());
|
|
text = VariableSerializer(VariableSerializer::PHPOutput).serialize(
|
|
defArg, true).data();
|
|
}
|
|
cg_printf("\"%s\", \"%s\",\n", value.c_str(),
|
|
string_cplus_escape(text.data(), text.size()).c_str());
|
|
} else {
|
|
cg_printf("\"\", \"\",\n");
|
|
}
|
|
// user attributes
|
|
ExpressionListPtr paramUserAttrs =
|
|
dynamic_pointer_cast<ExpressionList>(param->userAttributeList());
|
|
if (paramUserAttrs) {
|
|
for (int j = 0; j < paramUserAttrs->getCount(); ++j) {
|
|
UserAttributePtr a = dynamic_pointer_cast<UserAttribute>(
|
|
(*paramUserAttrs)[j]);
|
|
ExpressionPtr expr = a->getExp();
|
|
Variant v;
|
|
bool isScalar UNUSED = expr->getScalarValue(v);
|
|
assert(isScalar);
|
|
int valueLen = 0;
|
|
string valueText = SymbolTable::getEscapedText(v, valueLen);
|
|
cg_printf("\"%s\", (const char *)%d, \"%s\",\n",
|
|
CodeGenerator::EscapeLabel(a->getName()).c_str(),
|
|
valueLen, valueText.c_str());
|
|
}
|
|
}
|
|
cg_printf("NULL,\n");
|
|
} else {
|
|
cg_printf("\"\", ");
|
|
if (attribute & ClassInfo::IsSystem) {
|
|
cg_printf("(const char *)0x%x, ", m_paramTypes[i]->getDataType());
|
|
}
|
|
std::string def = string_cplus_escape(m_paramDefaults[i].data(),
|
|
m_paramDefaults[i].size());
|
|
std::string defText = string_cplus_escape(m_paramDefaultTexts[i].data(),
|
|
m_paramDefaultTexts[i].size());
|
|
cg_printf("\"%s\", \"%s\", NULL,\n", def.c_str(), defText.c_str());
|
|
}
|
|
}
|
|
cg_printf("NULL,\n");
|
|
|
|
m_variables->outputCPPStaticVariables(cg, ar);
|
|
|
|
// user attributes
|
|
UserAttributeMap::const_iterator it = m_userAttributes.begin();
|
|
for (; it != m_userAttributes.end(); ++it) {
|
|
ExpressionPtr expr = it->second;
|
|
Variant v;
|
|
bool isScalar UNUSED = expr->getScalarValue(v);
|
|
assert(isScalar);
|
|
int valueLen = 0;
|
|
string valueText = SymbolTable::getEscapedText(v, valueLen);
|
|
cg_printf("\"%s\", (const char *)%d, \"%s\",\n",
|
|
CodeGenerator::EscapeLabel(it->first).c_str(),
|
|
valueLen, valueText.c_str());
|
|
}
|
|
cg_printf("NULL,\n");
|
|
}
|
|
|
|
void FunctionScope::outputCPPHelperClassAlloc(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
|
|
const string &funcName = CodeGenerator::FormatLabel(m_name);
|
|
ParameterExpressionPtrVec useVars;
|
|
if (needsAnonClosureClass(useVars)) {
|
|
cg_printf("IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(%sClosure$%s)\n",
|
|
Option::ClassPrefix, funcName.c_str());
|
|
}
|
|
if (isGenerator()) {
|
|
cg_printf(
|
|
"IMPLEMENT_OBJECT_ALLOCATION_NO_DEFAULT_SWEEP(%sContinuation$%s)\n",
|
|
Option::ClassPrefix, funcName.c_str());
|
|
}
|
|
}
|
|
|
|
void FunctionScope::outputCPPCallInfo(CodeGenerator &cg,
|
|
AnalysisResultPtr ar) {
|
|
if (isAbstract()) return;
|
|
string id;
|
|
if (m_method) {
|
|
id = CodeGenerator::FormatLabel(m_name);
|
|
} else {
|
|
id = getId();
|
|
}
|
|
uint64 refflags = 0;
|
|
for (int i = 0; i < m_maxParam; ++i) {
|
|
if (isRefParam(i)) {
|
|
refflags |= 1 << i;
|
|
}
|
|
}
|
|
int flags = 0;
|
|
if (isMixedVariableArgument()) {
|
|
flags |= CallInfo::MixedVarArgs;
|
|
} else if (isReferenceVariableArgument()) {
|
|
flags |= CallInfo::RefVarArgs;
|
|
} else if (isVariableArgument()) {
|
|
flags |= CallInfo::VarArgs;
|
|
}
|
|
if (m_method) {
|
|
flags |= CallInfo::Method;
|
|
if (isProtected()) {
|
|
flags |= CallInfo::Protected;
|
|
} else if (isPrivate()) {
|
|
flags |= CallInfo::Private;
|
|
}
|
|
if (isStatic()) {
|
|
flags |= CallInfo::StaticMethod;
|
|
}
|
|
ClassScopePtr scope = getContainingClass();
|
|
string clsName = scope->getId();
|
|
|
|
cg.printf("extern const CallInfo %s%s%s%s = { (void*)&%s%s::%s%s, ",
|
|
Option::CallInfoPrefix, clsName.c_str(),
|
|
Option::IdPrefix.c_str(), id.c_str(),
|
|
Option::ClassPrefix, clsName.c_str(), Option::InvokePrefix,
|
|
id.c_str());
|
|
cg.printf("(void*)&%s%s::%s%s", Option::ClassPrefix, clsName.c_str(),
|
|
Option::InvokeFewArgsPrefix, id.c_str());
|
|
if (m_name == "__invoke" &&
|
|
strcasecmp(clsName.c_str(), "closure")) {
|
|
cg.printf(", %d, %d, 0x%.16llXLL};\n",
|
|
m_maxParam, flags, refflags);
|
|
|
|
// need to generate an extra call info for an extra wrapper
|
|
cg.printf("const CallInfo %s%s%s%s = { (void*)&%s%s::%s%s, ",
|
|
Option::CallInfoWrapperPrefix, clsName.c_str(),
|
|
Option::IdPrefix.c_str(), id.c_str(),
|
|
Option::ClassPrefix, clsName.c_str(),
|
|
Option::InvokeWrapperPrefix, id.c_str());
|
|
cg.printf("(void*)&%s%s::%s%s", Option::ClassPrefix, clsName.c_str(),
|
|
Option::InvokeWrapperFewArgsPrefix, id.c_str());
|
|
}
|
|
} else {
|
|
cg.printf("extern const %sCallInfo %s%s = {",
|
|
isRedeclaring() ? "Redeclared" : "",
|
|
Option::CallInfoPrefix, id.c_str());
|
|
if (isRedeclaring()) cg_printf("{");
|
|
cg_printf("(void*)&%s%s, (void*)&%s%s",
|
|
Option::InvokePrefix, id.c_str(),
|
|
Option::InvokeFewArgsPrefix, id.c_str());
|
|
}
|
|
cg.printf(", %d, %d, 0x%.16llXL", m_maxParam, flags, refflags);
|
|
if (isRedeclaring()) {
|
|
cg_printf("}, %d", getRedeclaringId());
|
|
}
|
|
cg_printf("};\n");
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
void FunctionScope::outputCPPSubClassParam(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
ParameterExpressionPtr param) {
|
|
VariableTablePtr variables = getVariables();
|
|
const string &name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
assert(sym);
|
|
TypePtr t(sym->getFinalType());
|
|
if (!param->isRef()) {
|
|
if (t->is(Type::KindOfVariant) || t->is(Type::KindOfSome)) {
|
|
cg_printf("CVarRef");
|
|
} else if (t->is(Type::KindOfArray)) cg_printf("CArrRef");
|
|
else if (t->is(Type::KindOfString)) cg_printf("CStrRef");
|
|
else if (t->isStandardObject()) cg_printf("CObjRef");
|
|
else t->outputCPPDecl(cg, ar, shared_from_this());
|
|
} else {
|
|
t->outputCPPDecl(cg, ar, shared_from_this());
|
|
}
|
|
cg_printf(" %s%s",
|
|
Option::TempVariablePrefix,
|
|
CodeGenerator::FormatLabel(name).c_str());
|
|
}
|
|
|
|
void FunctionScope::outputCPPPreface(CodeGenerator &cg, AnalysisResultPtr ar) {
|
|
ClassScopeRawPtr cls = getContainingClass();
|
|
if (!cls) {
|
|
if (!inPseudoMain()) {
|
|
// spit out extern CallInfo decl
|
|
cg_printf("extern const %sCallInfo %s%s;\n",
|
|
isRedeclaring() ? "Redeclared" : "",
|
|
Option::CallInfoPrefix, getId().c_str());
|
|
}
|
|
} else {
|
|
if (isGenerator()) {
|
|
cg_printf("extern const CallInfo %s%s%s%s;\n",
|
|
Option::CallInfoPrefix, getContainingClass()->getId().c_str(),
|
|
Option::IdPrefix.c_str(), getId().c_str());
|
|
}
|
|
}
|
|
|
|
const string &funcName = CodeGenerator::FormatLabel(m_name);
|
|
ParameterExpressionPtrVec useVars;
|
|
if (needsAnonClosureClass(useVars) &&
|
|
cg.insertDeclaredClosure(this)) {
|
|
cg_printf("FORWARD_DECLARE_CLASS(Closure$%s);\n", funcName.c_str());
|
|
cg_indentBegin("class %sClosure$%s : public %sClosure {\n",
|
|
Option::ClassPrefix, funcName.c_str(),
|
|
Option::ClassPrefix);
|
|
|
|
cg_printf("public:\n");
|
|
|
|
cg_printf("DECLARE_OBJECT_ALLOCATION_NO_SWEEP(%sClosure$%s)\n",
|
|
Option::ClassPrefix, funcName.c_str());
|
|
|
|
VariableTablePtr variables = getVariables();
|
|
if (!useVars.empty()) {
|
|
BOOST_FOREACH(ParameterExpressionPtr param, useVars) {
|
|
const string &name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
TypePtr t(sym->getFinalType());
|
|
t->outputCPPDecl(cg, ar, shared_from_this());
|
|
const string &varName = variables->getVariableName(ar, sym);
|
|
cg_printf(" %s;\n", varName.c_str());
|
|
}
|
|
cg_printf("\n");
|
|
}
|
|
|
|
if (variables->hasStaticLocals()) {
|
|
variables->outputCPPStaticLocals(cg, ar, false);
|
|
cg_printf("\n");
|
|
}
|
|
|
|
cg_printf("%sClosure$%s(const CallInfo *func, const char *name",
|
|
Option::ClassPrefix, funcName.c_str());
|
|
|
|
if (!useVars.empty()) cg_printf(", ");
|
|
bool hasEmit = false;
|
|
BOOST_FOREACH(ParameterExpressionPtr param, useVars) {
|
|
if (!hasEmit) hasEmit = true;
|
|
else cg_printf(", ");
|
|
outputCPPSubClassParam(cg, ar, param);
|
|
}
|
|
|
|
// TODO: non ref variants can be directly assigned to the member
|
|
// variable in the initialization list, giving an ever-so-slight
|
|
// gain in performance
|
|
cg_printf(") : %sClosure(func, name)", Option::ClassPrefix);
|
|
if (variables->hasStaticLocals()) {
|
|
cg_printf(", ");
|
|
variables->outputCPPStaticLocals(cg, ar, true);
|
|
}
|
|
cg_indentBegin(" {\n");
|
|
BOOST_FOREACH(ParameterExpressionPtr param, useVars) {
|
|
const string &name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
assert(sym);
|
|
TypePtr t(sym->getFinalType());
|
|
assert(!param->isRef() || t->is(Type::KindOfVariant));
|
|
const string &varName = variables->getVariableName(ar, sym);
|
|
const string &tmpName =
|
|
string(Option::TempVariablePrefix) + CodeGenerator::FormatLabel(name);
|
|
if (t->is(Type::KindOfVariant)) {
|
|
const char *s = param->isRef() ? "Ref" : "Val";
|
|
cg_printf("%s.assign%s(%s);\n", varName.c_str(), s, tmpName.c_str());
|
|
} else {
|
|
assert(!param->isRef());
|
|
cg_printf("%s = %s;\n", varName.c_str(), tmpName.c_str());
|
|
}
|
|
}
|
|
cg_indentEnd("}\n");
|
|
cg_indentEnd("};\n");
|
|
}
|
|
|
|
if (isGenerator()) {
|
|
cg_printf("FORWARD_DECLARE_CLASS(Continuation$%s);\n", funcName.c_str());
|
|
cg_indentBegin("class %sContinuation$%s : public %sContinuation {\n",
|
|
Option::ClassPrefix, funcName.c_str(), Option::ClassPrefix);
|
|
|
|
if (cls && cls->derivedByDynamic()) {
|
|
/*
|
|
* The m_obj property is potentially a redeclared-parent. We
|
|
* need to keep a reference to the root object too, to ensure
|
|
* that the root object doesnt get destroyed before m_obj
|
|
*/
|
|
cg_printf("Object o_rootObj;\n");
|
|
}
|
|
cg_printf("public:\n");
|
|
|
|
cg_printf("DECLARE_OBJECT_ALLOCATION_NO_SWEEP(%sContinuation$%s)\n",
|
|
Option::ClassPrefix, funcName.c_str());
|
|
|
|
VariableTablePtr variables = getVariables();
|
|
vector<Symbol*> symbols;
|
|
variables->getSymbols(symbols, true);
|
|
|
|
if (!symbols.empty()) {
|
|
BOOST_FOREACH(Symbol *sym, symbols) {
|
|
const string &varName = variables->getVariableName(ar, sym);
|
|
TypePtr type = sym->getFinalType();
|
|
type->outputCPPDecl(cg, ar, shared_from_this());
|
|
cg_printf(" %s;\n", varName.c_str());
|
|
}
|
|
cg_printf("\n");
|
|
}
|
|
|
|
if (variables->hasStaticLocals()) {
|
|
variables->outputCPPStaticLocals(cg, ar, false);
|
|
cg_printf("\n");
|
|
|
|
// no arg ctor w/ init list of static locals
|
|
cg_printf("%sContinuation$%s() : ",
|
|
Option::ClassPrefix,
|
|
funcName.c_str());
|
|
variables->outputCPPStaticLocals(cg, ar, true);
|
|
cg_printf(" {}\n");
|
|
}
|
|
|
|
// constructor is bootstrapped with all the parameters
|
|
// of the original generator function + use vars for any
|
|
// surrounding closure generator
|
|
cg_printf("static %sContinuation$%s Build("
|
|
"int64 func, int64 extra, bool isMethod, CStrRef origFuncName, ",
|
|
Option::SmartPtrPrefix, funcName.c_str());
|
|
|
|
MethodStatementRawPtr orig = getOrigGenStmt();
|
|
assert(orig);
|
|
ExpressionListPtr params = orig->getParams();
|
|
|
|
vector<ParameterExpressionPtr> ctorParams;
|
|
if (params) {
|
|
for (int i = 0; i < params->getCount(); i++) {
|
|
ParameterExpressionPtr param =
|
|
dynamic_pointer_cast<ParameterExpression>((*params)[i]);
|
|
ctorParams.push_back(param);
|
|
}
|
|
}
|
|
useVars.clear();
|
|
bool needsClosureCls =
|
|
orig->getFunctionScope()->needsAnonClosureClass(useVars);
|
|
if (needsClosureCls) {
|
|
assert(useVars.size() > 0);
|
|
ctorParams.insert(ctorParams.end(), useVars.begin(), useVars.end());
|
|
}
|
|
|
|
bool hasEmit = false;
|
|
BOOST_FOREACH(ParameterExpressionPtr param, ctorParams) {
|
|
const string& name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
if (sym) {
|
|
if (!hasEmit) hasEmit = true;
|
|
else cg_printf(", ");
|
|
outputCPPSubClassParam(cg, ar, param);
|
|
}
|
|
}
|
|
|
|
if (hasEmit) cg_printf(", ");
|
|
if (cls && cls->derivedByDynamic()) {
|
|
cg_printf("CObjRef rootObj, ");
|
|
}
|
|
cg_indentBegin("CVarRef obj, CArrRef args) {\n");
|
|
cg_printf("%sContinuation$%s cont = "
|
|
"%sContinuation$%s(NEWOBJ(%sContinuation$%s)());\n",
|
|
Option::SmartPtrPrefix, funcName.c_str(),
|
|
Option::SmartPtrPrefix, funcName.c_str(),
|
|
Option::ClassPrefix, funcName.c_str());
|
|
cg_printf("cont->%s__construct"
|
|
"(func, extra, isMethod, origFuncName, obj, args);\n",
|
|
Option::MethodPrefix);
|
|
if (cls && cls->derivedByDynamic()) {
|
|
cg_printf("cont->o_rootObj = rootObj;\n");
|
|
}
|
|
|
|
BOOST_FOREACH(ParameterExpressionPtr param, ctorParams) {
|
|
const string& name = param->getName();
|
|
Symbol *sym = variables->getSymbol(name);
|
|
if (sym) {
|
|
const string &varName = variables->getVariableName(ar, sym);
|
|
const string &tmpName =
|
|
string(Option::TempVariablePrefix) +
|
|
CodeGenerator::FormatLabel(name);
|
|
TypePtr t(sym->getFinalType());
|
|
if (t->is(Type::KindOfVariant)) {
|
|
const char *s = param->isRef() ? "Ref" : "Val";
|
|
cg_printf("cont->%s.assign%s(%s);\n",
|
|
varName.c_str(), s, tmpName.c_str());
|
|
} else {
|
|
assert(!param->isRef());
|
|
cg_printf("cont->%s = %s;\n", varName.c_str(), tmpName.c_str());
|
|
}
|
|
}
|
|
}
|
|
if (usesLSB()) {
|
|
cg_printf("cont->setCalledClass(f_get_called_class());\n");
|
|
}
|
|
cg_printf("return cont;\n");
|
|
cg_indentEnd("}\n");
|
|
cg_indentEnd("};\n");
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void FunctionScope::outputMethodWrapper(CodeGenerator &cg,
|
|
AnalysisResultPtr ar,
|
|
const char *clsToConstruct) {
|
|
cg_printf("\n");
|
|
if (m_stmt) {
|
|
m_stmt->printSource(cg);
|
|
}
|
|
cg.printDocComment(m_docComment);
|
|
|
|
int minCount = getMinParamCount();
|
|
int maxCount = getMaxParamCount();
|
|
|
|
for (int count = minCount; count <= maxCount; count++) {
|
|
TypePtr type = getReturnType();
|
|
|
|
if (clsToConstruct) {
|
|
cg_printf("static %s%s Create(", Option::SmartPtrPrefix, clsToConstruct);
|
|
} else {
|
|
if (isStatic()) cg_printf("static ");
|
|
if (type) {
|
|
type->outputCPPDecl(cg, ar, BlockScopeRawPtr(this));
|
|
} else {
|
|
cg_printf("void");
|
|
}
|
|
cg_printf(" %s%s(", Option::MethodWrapperPrefix, getId().c_str());
|
|
}
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
if (i > 0) cg_printf(", ");
|
|
getParamType(i)->outputCPPDecl(cg, ar, BlockScopeRawPtr(this));
|
|
cg_printf(" %s%s", Option::VariablePrefix, getParamName(i).c_str());
|
|
}
|
|
if (isVariableArgument()) {
|
|
if (count) cg_printf(", ");
|
|
cg_printf("Array args = Array()");
|
|
}
|
|
cg_indentBegin(") {\n");
|
|
|
|
if (clsToConstruct) {
|
|
cg_printf("%s%s ret(NEWOBJ(%s%s)());\n",
|
|
Option::SmartPtrPrefix, clsToConstruct,
|
|
Option::ClassPrefix, clsToConstruct);
|
|
}
|
|
|
|
if ((isStatic() || isPerfectVirtual() || !isVirtual() || clsToConstruct) &&
|
|
!isRedeclaring()) {
|
|
|
|
if (clsToConstruct) {
|
|
cg_printf("ret->");
|
|
} else if (type) {
|
|
cg_printf("return ");
|
|
}
|
|
|
|
cg_printf("%s%s(", Option::MethodPrefix,
|
|
CodeGenerator::FormatLabel(m_name).c_str());
|
|
if (isVariableArgument()) {
|
|
cg_printf("args.size() + %d, ", count);
|
|
}
|
|
for (int i = 0; i < count; i++) {
|
|
if (i > 0) cg_printf(", ");
|
|
bool isRef = isRefParam(i);
|
|
if (isRef) cg_printf("ref(");
|
|
cg_printf("%s%s", Option::VariablePrefix, getParamName(i).c_str());
|
|
if (isRef) cg_printf(")");
|
|
}
|
|
if (isVariableArgument()) {
|
|
if (count) cg_printf(", ");
|
|
cg_printf("args");
|
|
}
|
|
cg_printf(");\n");
|
|
|
|
} else {
|
|
cg_printf("Array params;\n");
|
|
for (int i = 0; i < count; i++) {
|
|
cg_printf("params.append(");
|
|
|
|
bool isRef = isRefParam(i);
|
|
if (isRef) cg_printf("ref(");
|
|
cg_printf("%s%s", Option::VariablePrefix, getParamName(i).c_str());
|
|
if (isRef) cg_printf(")");
|
|
|
|
cg_printf(");\n");
|
|
}
|
|
if (isVariableArgument()) {
|
|
cg_printf("params.merge(args);\n");
|
|
}
|
|
|
|
if (clsToConstruct) {
|
|
cg_printf("ret->");
|
|
} else if (type) {
|
|
cg_printf("return ");
|
|
}
|
|
|
|
always_assert(!isStatic());
|
|
cg_printf("%sinvoke(\"%s\", params, -1);\n",
|
|
Option::ObjectPrefix, m_name.c_str());
|
|
}
|
|
|
|
if (clsToConstruct) {
|
|
cg_printf("return ret;\n");
|
|
}
|
|
cg_indentEnd("}\n");
|
|
}
|
|
}
|