2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/compiler/analysis/function_scope.h"
18 #include "hphp/compiler/analysis/analysis_result.h"
19 #include "hphp/compiler/expression/constant_expression.h"
20 #include "hphp/compiler/expression/modifier_expression.h"
21 #include "hphp/compiler/expression/expression_list.h"
22 #include "hphp/compiler/expression/function_call.h"
23 #include "hphp/compiler/analysis/code_error.h"
24 #include "hphp/compiler/statement/statement_list.h"
25 #include "hphp/compiler/analysis/file_scope.h"
26 #include "hphp/compiler/analysis/variable_table.h"
27 #include "hphp/compiler/parser/parser.h"
28 #include "hphp/util/logger.h"
29 #include "hphp/compiler/option.h"
30 #include "hphp/compiler/statement/method_statement.h"
31 #include "hphp/compiler/statement/exp_statement.h"
32 #include "hphp/compiler/expression/parameter_expression.h"
33 #include "hphp/compiler/analysis/class_scope.h"
34 #include "hphp/util/atomic.h"
35 #include "hphp/util/util.h"
36 #include "hphp/runtime/base/class_info.h"
37 #include "hphp/runtime/base/type_conversions.h"
38 #include "hphp/runtime/base/builtin_functions.h"
39 #include "hphp/util/parser/hphp.tab.hpp"
40 #include "hphp/runtime/base/variable_serializer.h"
41 #include "hphp/runtime/base/zend/zend_string.h"
45 ///////////////////////////////////////////////////////////////////////////////
47 FunctionScope::FunctionScope(AnalysisResultConstPtr ar
, bool method
,
48 const std::string
&name
, StatementPtr stmt
,
49 bool reference
, int minParam
, int maxParam
,
50 ModifierExpressionPtr modifiers
,
51 int attribute
, const std::string
&docComment
,
53 const std::vector
<UserAttributePtr
> &attrs
,
54 bool inPseudoMain
/* = false */)
55 : BlockScope(name
, docComment
, stmt
, BlockScope::FunctionScope
),
56 m_minParam(minParam
), m_maxParam(maxParam
), m_attribute(attribute
),
57 m_modifiers(modifiers
), m_hasVoid(false),
58 m_method(method
), m_refReturn(reference
), m_virtual(false),
59 m_hasOverride(false), m_perfectVirtual(false), m_overriding(false),
60 m_volatile(false), m_persistent(false), m_pseudoMain(inPseudoMain
),
61 m_magicMethod(false), m_system(false), m_inlineable(false), m_sep(false),
62 m_containsThis(false), m_containsBareThis(0), m_nrvoFix(true),
63 m_inlineAsExpr(false), m_inlineSameContext(false),
64 m_contextSensitive(false),
65 m_directInvoke(false),
66 m_closureGenerator(false), m_noLSB(false), m_nextLSB(false),
67 m_hasTry(false), m_hasGoto(false), m_localRedeclaring(false),
68 m_redeclaring(-1), m_inlineIndex(0), m_optFunction(0), m_nextID(0),
69 m_yieldLabelCount(0) {
71 for (unsigned i
= 0; i
< attrs
.size(); ++i
) {
72 if (m_userAttributes
.find(attrs
[i
]->getName()) != m_userAttributes
.end()) {
73 attrs
[i
]->parseTimeFatal(Compiler::DeclaredAttributeTwice
,
74 "Redeclared attribute %s",
75 attrs
[i
]->getName().c_str());
77 m_userAttributes
[attrs
[i
]->getName()] = attrs
[i
]->getExp();
81 FunctionScope::FunctionScope(FunctionScopePtr orig
,
82 AnalysisResultConstPtr ar
,
84 const string
&originalName
,
86 ModifierExpressionPtr modifiers
)
87 : BlockScope(name
, orig
->m_docComment
, stmt
,
88 BlockScope::FunctionScope
),
89 m_minParam(orig
->m_minParam
), m_maxParam(orig
->m_maxParam
),
90 m_attribute(orig
->m_attribute
), m_modifiers(modifiers
),
91 m_userAttributes(orig
->m_userAttributes
), m_hasVoid(orig
->m_hasVoid
),
92 m_method(orig
->m_method
), m_refReturn(orig
->m_refReturn
),
93 m_virtual(orig
->m_virtual
), m_hasOverride(orig
->m_hasOverride
),
94 m_perfectVirtual(orig
->m_perfectVirtual
),
95 m_overriding(orig
->m_overriding
), m_volatile(orig
->m_volatile
),
96 m_persistent(orig
->m_persistent
),
97 m_pseudoMain(orig
->m_pseudoMain
), m_magicMethod(orig
->m_magicMethod
),
98 m_system(orig
->m_system
), m_inlineable(orig
->m_inlineable
),
99 m_sep(orig
->m_sep
), m_containsThis(orig
->m_containsThis
),
100 m_containsBareThis(orig
->m_containsBareThis
), m_nrvoFix(orig
->m_nrvoFix
),
101 m_inlineAsExpr(orig
->m_inlineAsExpr
),
102 m_inlineSameContext(orig
->m_inlineSameContext
),
103 m_contextSensitive(orig
->m_contextSensitive
),
104 m_directInvoke(orig
->m_directInvoke
),
105 m_closureGenerator(orig
->m_closureGenerator
), m_noLSB(orig
->m_noLSB
),
106 m_nextLSB(orig
->m_nextLSB
), m_hasTry(orig
->m_hasTry
),
107 m_hasGoto(orig
->m_hasGoto
), m_localRedeclaring(orig
->m_localRedeclaring
),
108 m_redeclaring(orig
->m_redeclaring
),
109 m_inlineIndex(orig
->m_inlineIndex
), m_optFunction(orig
->m_optFunction
),
110 m_nextID(0), m_yieldLabelCount(orig
->m_yieldLabelCount
) {
112 m_originalName
= originalName
;
113 setParamCounts(ar
, m_minParam
, m_maxParam
);
116 void FunctionScope::init(AnalysisResultConstPtr ar
) {
117 m_dynamicInvoke
= false;
118 bool canInline
= true;
121 m_variables
->forceVariants(ar
, VariableTable::AnyVars
);
122 setReturnType(ar
, Type::Variant
);
126 m_returnType
= Type::Variant
;
129 if (!strcasecmp(m_name
.c_str(), "__autoload")) {
133 // FileScope's flags are from parser, but VariableTable has more flags
134 // coming from type inference phase. So we are tranferring these flags
135 // just for better modularization between FileScope and VariableTable.
136 if (m_attribute
& FileScope::ContainsDynamicVariable
) {
137 m_variables
->setAttribute(VariableTable::ContainsDynamicVariable
);
139 if (m_attribute
& FileScope::ContainsLDynamicVariable
) {
140 m_variables
->setAttribute(VariableTable::ContainsLDynamicVariable
);
142 if (m_attribute
& FileScope::ContainsExtract
) {
143 m_variables
->setAttribute(VariableTable::ContainsExtract
);
145 if (m_attribute
& FileScope::ContainsCompact
) {
146 m_variables
->setAttribute(VariableTable::ContainsCompact
);
148 if (m_attribute
& FileScope::ContainsUnset
) {
149 m_variables
->setAttribute(VariableTable::ContainsUnset
);
151 if (m_attribute
& FileScope::ContainsGetDefinedVars
) {
152 m_variables
->setAttribute(VariableTable::ContainsGetDefinedVars
);
155 if (m_stmt
&& Option::AllVolatile
&& !m_pseudoMain
&& !m_method
) {
159 m_dynamic
= Option::IsDynamicFunction(m_method
, m_name
) ||
160 Option::EnableEval
== Option::FullEval
|| Option::AllDynamic
;
161 if (!m_method
&& Option::DynamicInvokeFunctions
.find(m_name
) !=
162 Option::DynamicInvokeFunctions
.end()) {
166 m_virtual
= m_modifiers
->isAbstract();
170 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
171 StatementListPtr stmts
= stmt
->getStmts();
173 if (stmts
->getRecursiveCount() > Option::InlineFunctionThreshold
)
175 for (int i
= 0; i
< stmts
->getCount(); i
++) {
176 StatementPtr stmt
= (*stmts
)[i
];
177 stmt
->setFileLevel();
178 if (stmt
->is(Statement::KindOfExpStatement
)) {
179 ExpStatementPtr expStmt
= dynamic_pointer_cast
<ExpStatement
>(stmt
);
180 ExpressionPtr exp
= expStmt
->getExpression();
188 m_inlineable
= canInline
;
191 FunctionScope::FunctionScope(bool method
, const std::string
&name
,
193 : BlockScope(name
, "", StatementPtr(), BlockScope::FunctionScope
),
194 m_minParam(0), m_maxParam(0), m_attribute(0),
195 m_modifiers(ModifierExpressionPtr()), m_hasVoid(false),
196 m_method(method
), m_refReturn(reference
), m_virtual(false),
197 m_hasOverride(false), m_perfectVirtual(false), m_overriding(false),
198 m_volatile(false), m_persistent(false), m_pseudoMain(false),
199 m_magicMethod(false), m_system(true), m_inlineable(false), m_sep(false),
200 m_containsThis(false), m_containsBareThis(0), m_nrvoFix(true),
201 m_inlineAsExpr(false), m_inlineSameContext(false),
202 m_contextSensitive(false),
203 m_directInvoke(false),
204 m_closureGenerator(false), m_noLSB(false), m_nextLSB(false),
205 m_hasTry(false), m_hasGoto(false), m_localRedeclaring(false),
206 m_redeclaring(-1), m_inlineIndex(0),
208 m_dynamic
= Option::IsDynamicFunction(method
, m_name
) ||
209 Option::EnableEval
== Option::FullEval
|| Option::AllDynamic
;
210 m_dynamicInvoke
= false;
211 if (!method
&& Option::DynamicInvokeFunctions
.find(m_name
) !=
212 Option::DynamicInvokeFunctions
.end()) {
217 void FunctionScope::setDynamicInvoke() {
218 m_returnType
= Type::Variant
;
219 m_dynamicInvoke
= true;
222 void FunctionScope::setParamCounts(AnalysisResultConstPtr ar
, int minParam
,
225 m_minParam
= minParam
;
226 m_maxParam
= maxParam
;
228 assert(maxParam
== minParam
);
230 assert(m_minParam
>= 0 && m_maxParam
>= m_minParam
);
231 if (m_maxParam
> 0) {
232 m_paramNames
.resize(m_maxParam
);
233 m_paramTypes
.resize(m_maxParam
);
234 m_paramTypeSpecs
.resize(m_maxParam
);
235 m_paramDefaults
.resize(m_maxParam
);
236 m_paramDefaultTexts
.resize(m_maxParam
);
237 m_refs
.resize(m_maxParam
);
240 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
241 ExpressionListPtr params
= stmt
->getParams();
243 for (int i
= 0; i
< m_maxParam
; i
++) {
244 if (stmt
->isRef(i
)) m_refs
[i
] = true;
246 ParameterExpressionPtr param
=
247 dynamic_pointer_cast
<ParameterExpression
>((*params
)[i
]);
248 m_paramNames
[i
] = param
->getName();
254 void FunctionScope::setParamSpecs(AnalysisResultPtr ar
) {
255 if (m_maxParam
> 0 && m_stmt
) {
256 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
257 ExpressionListPtr params
= stmt
->getParams();
259 for (int i
= 0; i
< m_maxParam
; i
++) {
260 ParameterExpressionPtr param
=
261 dynamic_pointer_cast
<ParameterExpression
>((*params
)[i
]);
262 TypePtr specType
= param
->getTypeSpec(ar
, false);
264 !specType
->is(Type::KindOfSome
) &&
265 !specType
->is(Type::KindOfVariant
)) {
266 m_paramTypeSpecs
[i
] = specType
;
268 ExpressionPtr exp
= param
->defaultValue();
270 m_paramDefaults
[i
] = exp
->getText(false, false, ar
);
276 bool FunctionScope::isPublic() const {
277 return m_modifiers
&& m_modifiers
->isPublic();
280 bool FunctionScope::isProtected() const {
281 return m_modifiers
&& m_modifiers
->isProtected();
284 bool FunctionScope::isPrivate() const {
285 return m_modifiers
&& m_modifiers
->isPrivate();
288 bool FunctionScope::isStatic() const {
289 return m_modifiers
&& m_modifiers
->isStatic();
292 bool FunctionScope::isAbstract() const {
293 return m_modifiers
&& m_modifiers
->isAbstract();
296 bool FunctionScope::isFinal() const {
297 return m_modifiers
&& m_modifiers
->isFinal();
300 bool FunctionScope::isVariableArgument() const {
301 bool res
= (m_attribute
& FileScope::VariableArgument
) && !m_overriding
;
305 bool FunctionScope::allowOverride() const {
306 return m_attribute
& FileScope::AllowOverride
;
309 bool FunctionScope::isReferenceVariableArgument() const {
310 bool res
= (m_attribute
& FileScope::ReferenceVariableArgument
) &&
312 // If this method returns true, then isVariableArgument() must also
314 assert(!res
|| isVariableArgument());
318 bool FunctionScope::isMixedVariableArgument() const {
319 bool res
= (m_attribute
& FileScope::MixedVariableArgument
) && !m_overriding
;
320 // If this method returns true, then isReferenceVariableArgument()
321 // must also return true.
322 assert(!res
|| isReferenceVariableArgument());
326 bool FunctionScope::needsActRec() const {
327 bool res
= (m_attribute
& FileScope::NeedsActRec
);
331 bool FunctionScope::mayContainThis() {
332 return inPseudoMain() || getContainingClass() ||
333 (isClosure() && !m_modifiers
->isStatic());
336 bool FunctionScope::isClosure() const {
337 return ParserBase::IsClosureName(name());
340 bool FunctionScope::isGenerator() const {
341 assert(!getOrigGenStmt() ||
342 (ParserBase::IsContinuationName(name()) &&
343 m_paramNames
.size() == 1 &&
344 m_paramNames
[0] == CONTINUATION_OBJECT_NAME
));
345 return !!getOrigGenStmt();
348 bool FunctionScope::hasGeneratorAsBody() const {
349 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(getStmt());
350 return stmt
? !!stmt
->getGeneratorFunc() : false;
353 bool FunctionScope::isGeneratorFromClosure() const {
354 bool res
= isGenerator() && getOrigGenFS()->isClosure();
355 assert(!res
|| getOrigGenFS()->isClosureGenerator());
359 MethodStatementRawPtr
FunctionScope::getOrigGenStmt() const {
360 if (!getStmt()) return MethodStatementRawPtr();
361 MethodStatementPtr m
=
362 dynamic_pointer_cast
<MethodStatement
>(getStmt());
363 return m
? m
->getOrigGeneratorFunc() : MethodStatementRawPtr();
366 FunctionScopeRawPtr
FunctionScope::getOrigGenFS() const {
367 MethodStatementRawPtr origStmt
= getOrigGenStmt();
368 return origStmt
? origStmt
->getFunctionScope() : FunctionScopeRawPtr();
371 void FunctionScope::setVariableArgument(int reference
) {
372 m_attribute
|= FileScope::VariableArgument
;
374 m_attribute
|= FileScope::ReferenceVariableArgument
;
376 m_attribute
|= FileScope::MixedVariableArgument
;
381 void FunctionScope::setAllowOverride() {
382 m_attribute
|= FileScope::AllowOverride
;
385 bool FunctionScope::hasEffect() const {
386 return (m_attribute
& FileScope::NoEffect
) == 0;
389 void FunctionScope::setNoEffect() {
390 if (!(m_attribute
& FileScope::NoEffect
)) {
391 m_attribute
|= FileScope::NoEffect
;
395 bool FunctionScope::isFoldable() const {
396 return m_attribute
& FileScope::IsFoldable
;
399 void FunctionScope::setIsFoldable() {
400 m_attribute
|= FileScope::IsFoldable
;
403 void FunctionScope::setNeedsActRec() {
404 m_attribute
|= FileScope::NeedsActRec
;
407 void FunctionScope::setHelperFunction() {
408 m_attribute
|= FileScope::HelperFunction
;
411 bool FunctionScope::containsReference() const {
412 return m_attribute
& FileScope::ContainsReference
;
415 void FunctionScope::setContainsThis(bool f
/* = true */) {
418 BlockScopePtr
bs(this->getOuterScope());
419 while (bs
&& bs
->is(BlockScope::FunctionScope
)) {
420 FunctionScopePtr fs
= static_pointer_cast
<FunctionScope
>(bs
);
421 if (!fs
->isClosure()) {
424 fs
->setContainsThis(f
);
425 bs
= bs
->getOuterScope();
428 for (auto it
= m_clonedTraitOuterScope
.begin(); it
!= m_clonedTraitOuterScope
.end(); it
++) {
429 (*it
)->setContainsThis(f
);
433 void FunctionScope::setContainsBareThis(bool f
, bool ref
/* = false */) {
435 m_containsBareThis
|= ref
? 2 : 1;
437 m_containsBareThis
= 0;
440 BlockScopePtr
bs(this->getOuterScope());
441 while (bs
&& bs
->is(BlockScope::FunctionScope
)) {
442 FunctionScopePtr fs
= static_pointer_cast
<FunctionScope
>(bs
);
443 if (!fs
->isClosure()) {
446 fs
->setContainsBareThis(f
, ref
);
447 bs
= bs
->getOuterScope();
450 for (auto it
= m_clonedTraitOuterScope
.begin(); it
!= m_clonedTraitOuterScope
.end(); it
++) {
451 (*it
)->setContainsBareThis(f
, ref
);
455 bool FunctionScope::hasImpl() const {
456 if (!isUserFunction()) {
457 return !isAbstract();
460 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
461 return stmt
->getStmts();
466 bool FunctionScope::isConstructor(ClassScopePtr cls
) const {
468 && (getName() == "__construct"
469 || (cls
->classNameCtor() && getName() == cls
->getName()));
472 bool FunctionScope::isMagic() const {
473 return m_name
.size() >= 2 && m_name
[0] == '_' && m_name
[1] == '_';
476 bool FunctionScope::needsLocalThis() const {
477 return containsBareThis() &&
481 getVariables()->getAttribute(
482 VariableTable::ContainsDynamicVariable
));
485 static std::string s_empty
;
486 const string
&FunctionScope::getOriginalName() const {
487 if (m_pseudoMain
) return s_empty
;
489 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
490 return stmt
->getOriginalName();
492 return m_originalName
;
495 string
FunctionScope::getFullName() const {
497 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
498 return stmt
->getFullName();
503 string
FunctionScope::getOriginalFullName() const {
505 MethodStatementPtr stmt
= dynamic_pointer_cast
<MethodStatement
>(m_stmt
);
506 return stmt
->getOriginalFullName();
511 ///////////////////////////////////////////////////////////////////////////////
513 void FunctionScope::addCaller(BlockScopePtr caller
,
514 bool careAboutReturn
/* = true */) {
515 addUse(caller
, UseKindCaller
);
518 void FunctionScope::addNewObjCaller(BlockScopePtr caller
) {
519 addUse(caller
, UseKindCaller
& ~UseKindCallerReturn
);
522 bool FunctionScope::mayUseVV() const {
523 VariableTableConstPtr variables
= getVariables();
524 return (inPseudoMain() ||
525 isVariableArgument() ||
527 variables
->getAttribute(VariableTable::ContainsDynamicVariable
) ||
528 variables
->getAttribute(VariableTable::ContainsExtract
) ||
529 variables
->getAttribute(VariableTable::ContainsCompact
) ||
530 variables
->getAttribute(VariableTable::ContainsGetDefinedVars
) ||
531 variables
->getAttribute(VariableTable::ContainsDynamicFunctionCall
));
534 bool FunctionScope::matchParams(FunctionScopePtr func
) {
535 // leaving them alone for now
536 if (m_overriding
|| func
->m_overriding
) return false;
537 if (isStatic() || func
->isStatic()) return false;
539 // conservative here, as we could normalize them into same counts.
540 if (m_minParam
!= func
->m_minParam
|| m_maxParam
!= func
->m_maxParam
) {
543 if (isVariableArgument() != func
->isVariableArgument() ||
544 isReferenceVariableArgument() != func
->isReferenceVariableArgument() ||
545 isMixedVariableArgument() != func
->isMixedVariableArgument()) {
549 // needs perfect match for ref, hint and defaults
550 for (int i
= 0; i
< m_maxParam
; i
++) {
551 if (m_refs
[i
] != func
->m_refs
[i
]) return false;
553 TypePtr type1
= m_paramTypeSpecs
[i
];
554 TypePtr type2
= func
->m_paramTypeSpecs
[i
];
555 if ((type1
&& !type2
) || (!type1
&& type2
) ||
556 (type1
&& type2
&& !Type::SameType(type1
, type2
))) return false;
558 if (m_paramDefaults
[i
] != func
->m_paramDefaults
[i
]) return false;
564 void FunctionScope::setPerfectVirtual() {
566 m_perfectVirtual
= true;
568 // conservative here, as we could still try to infer types THEN only
569 // force variants on non-matching parameters
570 m_returnType
= Type::Variant
;
571 for (unsigned int i
= 0; i
< m_paramTypes
.size(); i
++) {
572 m_paramTypes
[i
] = Type::Variant
;
573 m_variables
->addLvalParam(m_paramNames
[i
]);
577 bool FunctionScope::needsTypeCheckWrapper() const {
578 for (int i
= 0; i
< m_maxParam
; i
++) {
579 if (isRefParam(i
)) continue;
580 if (TypePtr spec
= m_paramTypeSpecs
[i
]) {
581 if (Type::SameType(spec
, m_paramTypes
[i
])) {
589 bool FunctionScope::needsClassParam() {
590 if (!isStatic()) return false;
591 ClassScopeRawPtr cls
= getContainingClass();
592 if (!ClassScope::NeedStaticArray(cls
, FunctionScopeRawPtr(this))) {
595 return getVariables()->hasStatic();
598 int FunctionScope::inferParamTypes(AnalysisResultPtr ar
, ConstructPtr exp
,
599 ExpressionListPtr params
, bool &valid
) {
601 if (m_minParam
> 0) {
602 if (exp
->getScope()->isFirstPass()) {
603 Compiler::Error(Compiler::TooFewArgument
, exp
, m_stmt
);
606 if (!Option::AllDynamic
) setDynamic();
612 if (params
->getCount() < m_minParam
) {
613 if (exp
->getScope()->isFirstPass()) {
614 Compiler::Error(Compiler::TooFewArgument
, exp
, m_stmt
);
617 if (!Option::AllDynamic
) setDynamic();
619 if (params
->getCount() > m_maxParam
) {
620 if (isVariableArgument()) {
621 ret
= params
->getCount() - m_maxParam
;
623 if (exp
->getScope()->isFirstPass()) {
624 Compiler::Error(Compiler::TooManyArgument
, exp
, m_stmt
);
627 if (!Option::AllDynamic
) setDynamic();
631 bool canSetParamType
= isUserFunction() && !m_overriding
&& !m_perfectVirtual
;
632 for (int i
= 0; i
< params
->getCount(); i
++) {
633 ExpressionPtr param
= (*params
)[i
];
634 if (i
< m_maxParam
&& param
->hasContext(Expression::RefParameter
)) {
636 * This should be very un-likely, since call time pass by ref is a
637 * deprecated, not very widely used (at least in FB codebase) feature.
640 Symbol
*sym
= getVariables()->addSymbol(m_paramNames
[i
]);
642 sym
->setCallTimeRef();
644 if (valid
&& param
->hasContext(Expression::InvokeArgument
)) {
645 param
->clearContext(Expression::InvokeArgument
);
646 param
->clearContext(Expression::RefValue
);
647 param
->clearContext(Expression::NoRefWrapper
);
649 bool isRefVararg
= (i
>= m_maxParam
&& isReferenceVariableArgument());
650 if ((i
< m_maxParam
&& isRefParam(i
)) || isRefVararg
) {
651 param
->setContext(Expression::LValue
);
652 param
->setContext(Expression::RefValue
);
653 param
->inferAndCheck(ar
, Type::Variant
, true);
654 } else if (!(param
->getContext() & Expression::RefParameter
)) {
655 param
->clearContext(Expression::LValue
);
656 param
->clearContext(Expression::RefValue
);
657 param
->clearContext(Expression::InvokeArgument
);
658 param
->clearContext(Expression::NoRefWrapper
);
662 * Duplicate the logic of getParamType(i), w/o the mutation
664 TypePtr
paramType(i
< m_maxParam
? m_paramTypes
[i
] : TypePtr());
665 if (!paramType
) paramType
= Type::Some
;
666 if (valid
&& !canSetParamType
&& i
< m_maxParam
&&
667 (!Option::HardTypeHints
|| !m_paramTypeSpecs
[i
])) {
669 * What is this magic, you might ask?
671 * Here, we take advantage of implicit conversion from every type to
672 * Variant. Essentially, we don't really care what type comes out of this
673 * expression since it'll just get converted anyways. Doing it this way
674 * allows us to generate less temporaries along the way.
676 TypePtr
optParamType(
677 paramType
->is(Type::KindOfVariant
) ? Type::Some
: paramType
);
678 expType
= param
->inferAndCheck(ar
, optParamType
, false);
680 expType
= param
->inferAndCheck(ar
, Type::Some
, false);
682 if (i
< m_maxParam
) {
683 if (!Option::HardTypeHints
|| !m_paramTypeSpecs
[i
]) {
684 if (canSetParamType
) {
685 if (!Type::SameType(paramType
, expType
) &&
686 !paramType
->is(Type::KindOfVariant
)) {
688 paramType
= setParamType(ar
, i
, expType
);
690 // do nothing - how is this safe? well, if we ever observe
691 // paramType == expType, then this means at some point in the past,
692 // somebody called setParamType() with expType. thus, by calling
693 // setParamType() again with expType, we contribute no "new"
694 // information. this argument also still applies in the face of
698 // See note above. If we have an implemented type, however, we
699 // should set the paramType to the implemented type to avoid an
701 if (paramType
->is(Type::KindOfVariant
)) {
702 TypePtr
it(param
->getImplementedType());
703 paramType
= it
? it
: expType
;
706 if (!Type::IsLegalCast(ar
, expType
, paramType
) &&
707 paramType
->isNonConvertibleType()) {
708 param
->inferAndCheck(ar
, paramType
, true);
710 param
->setExpectedType(paramType
);
714 // we do a best-effort check for bad pass-by-reference and do not report
715 // error for some vararg case (e.g., array_multisort can have either ref
716 // or value for the same vararg).
717 if (!isRefVararg
|| !isMixedVariableArgument()) {
718 Expression::CheckPassByReference(ar
, param
);
724 TypePtr
FunctionScope::setParamType(AnalysisResultConstPtr ar
, int index
,
726 assert(index
>= 0 && index
< (int)m_paramTypes
.size());
727 TypePtr paramType
= m_paramTypes
[index
];
729 if (!paramType
) paramType
= Type::Some
;
730 type
= Type::Coerce(ar
, paramType
, type
);
731 if (type
&& !Type::SameType(paramType
, type
)) {
732 addUpdates(UseKindCallerParam
);
733 if (!isFirstPass()) {
734 Logger::Verbose("Corrected type of parameter %d of %s: %s -> %s",
735 index
, m_name
.c_str(),
736 paramType
->toString().c_str(), type
->toString().c_str());
739 m_paramTypes
[index
] = type
;
743 TypePtr
FunctionScope::getParamType(int index
) {
744 assert(index
>= 0 && index
< (int)m_paramTypes
.size());
745 TypePtr paramType
= m_paramTypes
[index
];
747 paramType
= Type::Some
;
748 m_paramTypes
[index
] = paramType
;
753 bool FunctionScope::isRefParam(int index
) const {
754 assert(index
>= 0 && index
< (int)m_refs
.size());
755 return m_refs
[index
];
758 void FunctionScope::setRefParam(int index
) {
759 assert(index
>= 0 && index
< (int)m_refs
.size());
760 m_refs
[index
] = true;
763 bool FunctionScope::hasRefParam(int max
) const {
764 assert(max
>= 0 && max
< (int)m_refs
.size());
765 for (int i
= 0; i
< max
; i
++) {
766 if (m_refs
[i
]) return true;
771 const std::string
&FunctionScope::getParamName(int index
) const {
772 assert(index
>= 0 && index
< (int)m_paramNames
.size());
773 return m_paramNames
[index
];
776 void FunctionScope::setParamName(int index
, const std::string
&name
) {
777 assert(index
>= 0 && index
< (int)m_paramNames
.size());
778 m_paramNames
[index
] = name
;
781 void FunctionScope::setParamDefault(int index
, const char* value
, int64_t len
,
782 const std::string
&text
) {
783 assert(index
>= 0 && index
< (int)m_paramNames
.size());
784 StringData
* sd
= new StringData(value
, len
, AttachLiteral
);
786 m_paramDefaults
[index
] = String(sd
);
787 m_paramDefaultTexts
[index
] = text
;
790 CStrRef
FunctionScope::getParamDefault(int index
) {
791 return m_paramDefaults
[index
];
794 void FunctionScope::addModifier(int mod
) {
797 ModifierExpressionPtr(new ModifierExpression(
798 shared_from_this(), LocationPtr()));
800 m_modifiers
->add(mod
);
803 void FunctionScope::setReturnType(AnalysisResultConstPtr ar
, TypePtr type
) {
804 if (inTypeInference()) {
805 getInferTypesMutex().assertOwnedBySelf();
807 // no change can be made to virtual function's prototype
808 if (m_overriding
|| m_dynamicInvoke
) return;
812 if (!m_returnType
) return;
816 type
= Type::Variant
;
817 } else if (m_returnType
) {
818 type
= Type::Coerce(ar
, m_returnType
, type
);
821 assert(m_returnType
);
824 void FunctionScope::pushReturnType() {
825 getInferTypesMutex().assertOwnedBySelf();
826 m_prevReturn
= m_returnType
;
828 if (m_overriding
|| m_dynamicInvoke
|| m_perfectVirtual
|| m_pseudoMain
) {
831 m_returnType
.reset();
834 bool FunctionScope::popReturnType() {
835 getInferTypesMutex().assertOwnedBySelf();
836 if (m_overriding
|| m_dynamicInvoke
|| m_perfectVirtual
|| m_pseudoMain
) {
842 if (Type::SameType(m_returnType
, m_prevReturn
)) {
843 m_prevReturn
.reset();
846 if (!isFirstPass()) {
847 Logger::Verbose("Corrected function return type %s -> %s",
848 m_prevReturn
->toString().c_str(),
849 m_returnType
->toString().c_str());
852 } else if (!m_prevReturn
) {
856 m_prevReturn
.reset();
857 addUpdates(UseKindCallerReturn
);
858 #ifdef HPHP_INSTRUMENT_TYPE_INF
859 atomic_inc(RescheduleException::s_NumRetTypesChanged
);
860 #endif /* HPHP_INSTRUMENT_TYPE_INF */
864 void FunctionScope::resetReturnType() {
865 getInferTypesMutex().assertOwnedBySelf();
866 if (m_overriding
|| m_dynamicInvoke
|| m_perfectVirtual
|| m_pseudoMain
) {
869 m_returnType
= m_prevReturn
;
870 m_prevReturn
.reset();
873 void FunctionScope::addRetExprToFix(ExpressionPtr e
) {
874 m_retExprsToFix
.push_back(e
);
877 void FunctionScope::clearRetExprs() {
878 m_retExprsToFix
.clear();
881 void FunctionScope::fixRetExprs() {
882 for (ExpressionPtrVec::iterator it
= m_retExprsToFix
.begin(),
883 end
= m_retExprsToFix
.end(); it
!= end
; ++it
) {
884 (*it
)->setExpectedType(m_returnType
);
886 m_retExprsToFix
.clear();
889 void FunctionScope::setOverriding(TypePtr returnType
,
890 TypePtr param1
/* = TypePtr() */,
891 TypePtr param2
/* = TypePtr() */) {
892 m_returnType
= returnType
;
895 if (param1
&& m_paramTypes
.size() >= 1) m_paramTypes
[0] = param1
;
896 if (param2
&& m_paramTypes
.size() >= 2) m_paramTypes
[1] = param2
;
898 // TODO: remove this block and replace with stronger typing
899 // Right now, we have to avoid a situation where a parameter is assigned
900 // with different values, making them a Variant.
901 for (unsigned int i
= 0; i
< m_paramTypes
.size(); i
++) {
902 m_paramTypes
[i
] = Type::Variant
;
906 std::string
FunctionScope::getId() const {
907 string name
= CodeGenerator::FormatLabel(getOriginalName());
908 if (m_redeclaring
< 0) {
911 return name
+ Option::IdPrefix
+
912 boost::lexical_cast
<std::string
>(m_redeclaring
);
915 std::string
FunctionScope::getDocName() const {
916 string name
= getOriginalName();
917 if (m_redeclaring
< 0) {
920 return name
+ Option::IdPrefix
+
921 boost::lexical_cast
<std::string
>(m_redeclaring
);
924 std::string
FunctionScope::getDocFullName() const {
925 FunctionScope
*self
= const_cast<FunctionScope
*>(this);
926 const string
&docName
= getDocName();
927 if (ClassScopeRawPtr cls
= self
->getContainingClass()) {
928 return cls
->getDocName() + string("::") + docName
;
933 std::string
FunctionScope::getInjectionId() const {
934 string injectionName
= CodeGenerator::FormatLabel(getOriginalName());
935 MethodStatementPtr stmt
=
936 dynamic_pointer_cast
<MethodStatement
>(getStmt());
938 if (stmt
->getGeneratorFunc()) {
939 injectionName
= isClosureGenerator() ?
941 injectionName
+ "{continuation}";
942 } else if (stmt
->getOrigGeneratorFunc() &&
943 !getOrigGenFS()->isClosure()) {
944 injectionName
= CodeGenerator::FormatLabel(
945 stmt
->getOrigGeneratorFunc()->getOriginalName());
947 if (m_redeclaring
< 0) {
948 return injectionName
;
950 const string
&redecSuffix
= string(Option::IdPrefix
) +
951 boost::lexical_cast
<std::string
>(m_redeclaring
);
952 return injectionName
+ redecSuffix
;
955 ///////////////////////////////////////////////////////////////////////////////
957 void FunctionScope::outputPHP(CodeGenerator
&cg
, AnalysisResultPtr ar
) {
958 if (Option::GenerateInferredTypes
&& m_returnType
) {
959 cg_printf("// @return %s\n", m_returnType
->toString().c_str());
962 BlockScope::outputPHP(cg
, ar
);
965 void FunctionScope::serialize(JSON::CodeError::OutputStream
&out
) const {
966 JSON::CodeError::MapStream
ms(out
);
968 if (isPublic()) vis
= ClassScope::Public
;
969 else if (isProtected()) vis
= ClassScope::Protected
;
970 else if (isPrivate()) vis
= ClassScope::Protected
;
973 if (isAbstract()) mod
= ClassScope::Abstract
;
974 else if (isFinal()) mod
= ClassScope::Final
;
978 } else if (m_returnType
->isSpecificObject()) {
979 ms
.add("retTp", m_returnType
->getName());
981 ms
.add("retTp", m_returnType
->getKindOf());
983 ms
.add("minArgs", m_minParam
)
984 .add("maxArgs", m_maxParam
)
985 .add("varArgs", isVariableArgument())
986 .add("static", isStatic())
987 .add("modifier", mod
)
988 .add("visibility", vis
)
989 .add("argIsRef", m_refs
)
993 void FunctionScope::serialize(JSON::DocTarget::OutputStream
&out
) const {
994 JSON::DocTarget::MapStream
ms(out
);
996 ms
.add("name", getDocName());
997 ms
.add("line", getStmt() ? getStmt()->getLocation()->line0
: 0);
998 ms
.add("docs", m_docComment
);
1001 if (isPublic()) mods
|= ClassInfo::IsPublic
;
1002 if (isProtected()) mods
|= ClassInfo::IsProtected
;
1003 if (isPrivate()) mods
|= ClassInfo::IsPrivate
;
1004 if (isStatic()) mods
|= ClassInfo::IsStatic
;
1005 if (isFinal()) mods
|= ClassInfo::IsFinal
;
1006 if (isAbstract()) mods
|= ClassInfo::IsAbstract
;
1007 ms
.add("modifiers", mods
);
1009 ms
.add("refreturn", isRefReturn());
1010 ms
.add("return", getReturnType());
1012 vector
<SymParamWrapper
> paramSymbols
;
1013 for (int i
= 0; i
< m_maxParam
; i
++) {
1014 const string
&name
= getParamName(i
);
1015 const Symbol
*sym
= getVariables()->getSymbol(name
);
1016 assert(sym
&& sym
->isParameter());
1017 paramSymbols
.push_back(SymParamWrapper(sym
));
1019 ms
.add("parameters", paramSymbols
);
1021 // scopes that call this scope (callers)
1022 vector
<string
> callers
;
1023 const BlockScopeRawPtrFlagsPtrVec
&deps
= getDeps();
1024 for (BlockScopeRawPtrFlagsPtrVec::const_iterator it
= deps
.begin();
1025 it
!= deps
.end(); ++it
) {
1026 const BlockScopeRawPtrFlagsPtrPair
&p(*it
);
1027 if ((*p
.second
& BlockScope::UseKindCaller
) &&
1028 p
.first
->is(BlockScope::FunctionScope
)) {
1029 FunctionScopeRawPtr
f(
1030 static_pointer_cast
<FunctionScope
>(p
.first
));
1031 callers
.push_back(f
->getDocFullName());
1034 ms
.add("callers", callers
);
1036 // scopes that this scope calls (callees)
1037 // TODO(stephentu): this list only contains *user* functions,
1038 // we should also include builtins
1039 vector
<string
> callees
;
1040 const BlockScopeRawPtrFlagsVec
&users
= getOrderedUsers();
1041 for (BlockScopeRawPtrFlagsVec::const_iterator uit
= users
.begin();
1042 uit
!= users
.end(); ++uit
) {
1043 BlockScopeRawPtrFlagsVec::value_type pf
= *uit
;
1044 if ((pf
->second
& BlockScope::UseKindCaller
) &&
1045 pf
->first
->is(BlockScope::FunctionScope
)) {
1046 FunctionScopeRawPtr
f(
1047 static_pointer_cast
<FunctionScope
>(pf
->first
));
1048 callees
.push_back(f
->getDocFullName());
1051 ms
.add("callees", callees
);
1056 void FunctionScope::getClosureUseVars(
1057 ParameterExpressionPtrIdxPairVec
&useVars
,
1058 bool filterUsed
/* = true */) {
1060 if (!m_closureVars
) return;
1061 assert(isClosure());
1062 VariableTablePtr variables
= getVariables();
1063 for (int i
= 0; i
< m_closureVars
->getCount(); i
++) {
1064 ParameterExpressionPtr param
=
1065 dynamic_pointer_cast
<ParameterExpression
>((*m_closureVars
)[i
]);
1066 const string
&name
= param
->getName();
1067 if (!filterUsed
|| variables
->isUsed(name
)) {
1068 useVars
.push_back(ParameterExpressionPtrIdxPair(param
, i
));
1073 template <class U
, class V
>
1074 static U
pair_first_elem(std::pair
<U
, V
> p
) { return p
.first
; }
1076 bool FunctionScope::needsAnonClosureClass(ParameterExpressionPtrVec
&useVars
) {
1078 if (!isClosure()) return false;
1079 ParameterExpressionPtrIdxPairVec useVars0
;
1080 getClosureUseVars(useVars0
, !m_closureGenerator
);
1081 useVars
.resize(useVars0
.size());
1082 // C++ seems to be unable to infer the type here on pair_first_elem
1083 transform(useVars0
.begin(),
1086 pair_first_elem
<ParameterExpressionPtr
, int>);
1087 return useVars
.size() > 0 || getVariables()->hasStaticLocals();
1090 bool FunctionScope::needsAnonClosureClass(
1091 ParameterExpressionPtrIdxPairVec
&useVars
) {
1093 if (!isClosure()) return false;
1094 getClosureUseVars(useVars
, !m_closureGenerator
);
1095 return useVars
.size() > 0 || getVariables()->hasStaticLocals();
1098 FunctionScope::StringToFunctionInfoPtrMap
FunctionScope::s_refParamInfo
;
1099 static Mutex s_refParamInfoLock
;
1101 void FunctionScope::RecordFunctionInfo(string fname
, FunctionScopePtr func
) {
1102 VariableTablePtr variables
= func
->getVariables();
1103 if (Option::WholeProgram
) {
1104 Lock
lock(s_refParamInfoLock
);
1105 FunctionInfoPtr
&info
= s_refParamInfo
[fname
];
1107 info
= FunctionInfoPtr(new FunctionInfo());
1109 if (func
->isStatic()) {
1110 info
->setMaybeStatic();
1112 if (func
->isRefReturn()) {
1113 info
->setMaybeRefReturn();
1115 if (func
->isReferenceVariableArgument()) {
1116 info
->setRefVarArg(func
->getMaxParamCount());
1118 for (int i
= 0; i
< func
->getMaxParamCount(); i
++) {
1119 if (func
->isRefParam(i
)) info
->setRefParam(i
);
1122 for (int i
= 0; i
< func
->getMaxParamCount(); i
++) {
1123 variables
->addParam(func
->getParamName(i
),
1124 TypePtr(), AnalysisResultPtr(), ConstructPtr());
1128 FunctionScope::FunctionInfoPtr
FunctionScope::GetFunctionInfo(string fname
) {
1129 assert(Option::WholeProgram
);
1130 StringToFunctionInfoPtrMap::iterator it
= s_refParamInfo
.find(fname
);
1131 if (it
== s_refParamInfo
.end()) {
1132 return FunctionInfoPtr();