Arquivos
hhvm/hphp/compiler/analysis/function_scope.cpp
T
mwilliams e300bc8cce Fix nemo warnings
Setup for parameters was skipped by some cleanup of the use of
the WholeProgram flag. Re-enable it.
2013-02-13 06:42:47 -08:00

2600 linhas
89 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;
}
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");
}
}