rename IgnoreRedefinition to AllowOverride
[hiphop-php.git] / hphp / compiler / analysis / function_scope.cpp
blobf1fd6754c1cfe4ae407b8c8aea0810569f44870b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
43 using namespace HPHP;
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,
52 FileScopePtr file,
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) {
70 init(ar);
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,
83 const string &name,
84 const string &originalName,
85 StatementPtr stmt,
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) {
111 init(ar);
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;
119 if (m_pseudoMain) {
120 canInline = false;
121 m_variables->forceVariants(ar, VariableTable::AnyVars);
122 setReturnType(ar, Type::Variant);
125 if (m_refReturn) {
126 m_returnType = Type::Variant;
129 if (!strcasecmp(m_name.c_str(), "__autoload")) {
130 setVolatile();
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) {
156 m_volatile = true;
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()) {
163 setDynamicInvoke();
165 if (m_modifiers) {
166 m_virtual = m_modifiers->isAbstract();
169 if (m_stmt) {
170 MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
171 StatementListPtr stmts = stmt->getStmts();
172 if (stmts) {
173 if (stmts->getRecursiveCount() > Option::InlineFunctionThreshold)
174 canInline = false;
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();
181 exp->setTopLevel();
185 } else {
186 canInline = false;
188 m_inlineable = canInline;
191 FunctionScope::FunctionScope(bool method, const std::string &name,
192 bool reference)
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),
207 m_optFunction(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()) {
213 setDynamicInvoke();
217 void FunctionScope::setDynamicInvoke() {
218 m_returnType = Type::Variant;
219 m_dynamicInvoke = true;
222 void FunctionScope::setParamCounts(AnalysisResultConstPtr ar, int minParam,
223 int maxParam) {
224 if (minParam >= 0) {
225 m_minParam = minParam;
226 m_maxParam = maxParam;
227 } else {
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);
239 if (m_stmt) {
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);
263 if (specType &&
264 !specType->is(Type::KindOfSome) &&
265 !specType->is(Type::KindOfVariant)) {
266 m_paramTypeSpecs[i] = specType;
268 ExpressionPtr exp = param->defaultValue();
269 if (exp) {
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;
302 return res;
305 bool FunctionScope::allowOverride() const {
306 return m_attribute & FileScope::AllowOverride;
309 bool FunctionScope::isReferenceVariableArgument() const {
310 bool res = (m_attribute & FileScope::ReferenceVariableArgument) &&
311 !m_overriding;
312 // If this method returns true, then isVariableArgument() must also
313 // return true.
314 assert(!res || isVariableArgument());
315 return res;
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());
323 return res;
326 bool FunctionScope::needsActRec() const {
327 bool res = (m_attribute & FileScope::NeedsActRec);
328 return res;
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());
356 return res;
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;
373 if (reference) {
374 m_attribute |= FileScope::ReferenceVariableArgument;
375 if (reference < 0) {
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 */) {
416 m_containsThis = f;
418 BlockScopePtr bs(this->getOuterScope());
419 while (bs && bs->is(BlockScope::FunctionScope)) {
420 FunctionScopePtr fs = static_pointer_cast<FunctionScope>(bs);
421 if (!fs->isClosure()) {
422 break;
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 */) {
434 if (f) {
435 m_containsBareThis |= ref ? 2 : 1;
436 } else {
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()) {
444 break;
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();
459 if (m_stmt) {
460 MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
461 return stmt->getStmts();
463 return false;
466 bool FunctionScope::isConstructor(ClassScopePtr cls) const {
467 return m_stmt && cls
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() &&
478 (inPseudoMain() ||
479 containsRefThis() ||
480 isStatic() ||
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;
488 if (m_stmt) {
489 MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
490 return stmt->getOriginalName();
492 return m_originalName;
495 string FunctionScope::getFullName() const {
496 if (m_stmt) {
497 MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
498 return stmt->getFullName();
500 return m_name;
503 string FunctionScope::getOriginalFullName() const {
504 if (m_stmt) {
505 MethodStatementPtr stmt = dynamic_pointer_cast<MethodStatement>(m_stmt);
506 return stmt->getOriginalFullName();
508 return m_name;
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() ||
526 isGenerator() ||
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) {
541 return false;
543 if (isVariableArgument() != func->isVariableArgument() ||
544 isReferenceVariableArgument() != func->isReferenceVariableArgument() ||
545 isMixedVariableArgument() != func->isMixedVariableArgument()) {
546 return false;
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;
561 return true;
564 void FunctionScope::setPerfectVirtual() {
565 m_virtual = true;
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])) {
582 return true;
586 return false;
589 bool FunctionScope::needsClassParam() {
590 if (!isStatic()) return false;
591 ClassScopeRawPtr cls = getContainingClass();
592 if (!ClassScope::NeedStaticArray(cls, FunctionScopeRawPtr(this))) {
593 return false;
595 return getVariables()->hasStatic();
598 int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
599 ExpressionListPtr params, bool &valid) {
600 if (!params) {
601 if (m_minParam > 0) {
602 if (exp->getScope()->isFirstPass()) {
603 Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
605 valid = false;
606 if (!Option::AllDynamic) setDynamic();
608 return 0;
611 int ret = 0;
612 if (params->getCount() < m_minParam) {
613 if (exp->getScope()->isFirstPass()) {
614 Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
616 valid = false;
617 if (!Option::AllDynamic) setDynamic();
619 if (params->getCount() > m_maxParam) {
620 if (isVariableArgument()) {
621 ret = params->getCount() - m_maxParam;
622 } else {
623 if (exp->getScope()->isFirstPass()) {
624 Compiler::Error(Compiler::TooManyArgument, exp, m_stmt);
626 valid = false;
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.
639 TRY_LOCK_THIS();
640 Symbol *sym = getVariables()->addSymbol(m_paramNames[i]);
641 sym->setLvalParam();
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);
660 TypePtr expType;
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);
679 } else {
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)) {
687 TRY_LOCK_THIS();
688 paramType = setParamType(ar, i, expType);
689 } else {
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
695 // concurrency
698 // See note above. If we have an implemented type, however, we
699 // should set the paramType to the implemented type to avoid an
700 // un-necessary cast
701 if (paramType->is(Type::KindOfVariant)) {
702 TypePtr it(param->getImplementedType());
703 paramType = it ? it : expType;
705 if (valid) {
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);
721 return ret;
724 TypePtr FunctionScope::setParamType(AnalysisResultConstPtr ar, int index,
725 TypePtr type) {
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;
740 return type;
743 TypePtr FunctionScope::getParamType(int index) {
744 assert(index >= 0 && index < (int)m_paramTypes.size());
745 TypePtr paramType = m_paramTypes[index];
746 if (!paramType) {
747 paramType = Type::Some;
748 m_paramTypes[index] = paramType;
750 return 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;
768 return false;
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);
785 sd->setStatic();
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) {
795 if (!m_modifiers) {
796 m_modifiers =
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;
810 if (!type) {
811 m_hasVoid = true;
812 if (!m_returnType) return;
815 if (m_hasVoid) {
816 type = Type::Variant;
817 } else if (m_returnType) {
818 type = Type::Coerce(ar, m_returnType, type);
820 m_returnType = type;
821 assert(m_returnType);
824 void FunctionScope::pushReturnType() {
825 getInferTypesMutex().assertOwnedBySelf();
826 m_prevReturn = m_returnType;
827 m_hasVoid = false;
828 if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
829 return;
831 m_returnType.reset();
834 bool FunctionScope::popReturnType() {
835 getInferTypesMutex().assertOwnedBySelf();
836 if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
837 return false;
840 if (m_returnType) {
841 if (m_prevReturn) {
842 if (Type::SameType(m_returnType, m_prevReturn)) {
843 m_prevReturn.reset();
844 return false;
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) {
853 return false;
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 */
861 return true;
864 void FunctionScope::resetReturnType() {
865 getInferTypesMutex().assertOwnedBySelf();
866 if (m_overriding || m_dynamicInvoke || m_perfectVirtual || m_pseudoMain) {
867 return;
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;
893 m_overriding = true;
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) {
909 return name;
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) {
918 return name;
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;
930 return docName;
933 std::string FunctionScope::getInjectionId() const {
934 string injectionName = CodeGenerator::FormatLabel(getOriginalName());
935 MethodStatementPtr stmt =
936 dynamic_pointer_cast<MethodStatement>(getStmt());
937 assert(stmt);
938 if (stmt->getGeneratorFunc()) {
939 injectionName = isClosureGenerator() ?
940 injectionName :
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);
967 int vis = 0;
968 if (isPublic()) vis = ClassScope::Public;
969 else if (isProtected()) vis = ClassScope::Protected;
970 else if (isPrivate()) vis = ClassScope::Protected;
972 int mod = 0;
973 if (isAbstract()) mod = ClassScope::Abstract;
974 else if (isFinal()) mod = ClassScope::Final;
976 if (!m_returnType) {
977 ms.add("retTp", -1);
978 } else if (m_returnType->isSpecificObject()) {
979 ms.add("retTp", m_returnType->getName());
980 } else {
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)
990 .done();
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);
1000 int mods = 0;
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);
1053 ms.done();
1056 void FunctionScope::getClosureUseVars(
1057 ParameterExpressionPtrIdxPairVec &useVars,
1058 bool filterUsed /* = true */) {
1059 useVars.clear();
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) {
1077 useVars.clear();
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(),
1084 useVars0.end(),
1085 useVars.begin(),
1086 pair_first_elem<ParameterExpressionPtr, int>);
1087 return useVars.size() > 0 || getVariables()->hasStaticLocals();
1090 bool FunctionScope::needsAnonClosureClass(
1091 ParameterExpressionPtrIdxPairVec &useVars) {
1092 useVars.clear();
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];
1106 if (!info) {
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();
1134 return it->second;