2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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/statement/method_statement.h"
18 #include "hphp/compiler/statement/return_statement.h"
19 #include "hphp/compiler/statement/statement_list.h"
20 #include "hphp/compiler/statement/try_statement.h"
21 #include "hphp/compiler/statement/label_statement.h"
22 #include "hphp/compiler/statement/goto_statement.h"
23 #include "hphp/compiler/statement/exp_statement.h"
24 #include "hphp/compiler/statement/switch_statement.h"
25 #include "hphp/compiler/statement/case_statement.h"
26 #include "hphp/compiler/statement/catch_statement.h"
28 #include "hphp/compiler/expression/modifier_expression.h"
29 #include "hphp/compiler/expression/expression_list.h"
30 #include "hphp/compiler/expression/constant_expression.h"
31 #include "hphp/compiler/expression/parameter_expression.h"
32 #include "hphp/compiler/expression/assignment_expression.h"
33 #include "hphp/compiler/expression/simple_variable.h"
34 #include "hphp/compiler/expression/closure_expression.h"
36 #include "hphp/compiler/analysis/ast_walker.h"
37 #include "hphp/compiler/analysis/analysis_result.h"
38 #include "hphp/compiler/analysis/code_error.h"
39 #include "hphp/compiler/analysis/file_scope.h"
40 #include "hphp/compiler/analysis/variable_table.h"
41 #include "hphp/compiler/analysis/class_scope.h"
42 #include "hphp/compiler/analysis/function_scope.h"
44 #include "hphp/compiler/option.h"
45 #include "hphp/compiler/builtin_symbols.h"
46 #include "hphp/compiler/analysis/alias_manager.h"
48 #include "hphp/runtime/base/complex_types.h"
50 #include "hphp/util/parser/parser.h"
51 #include "hphp/util/util.h"
56 ///////////////////////////////////////////////////////////////////////////////
57 // constructors/destructors
59 MethodStatement::MethodStatement
60 (STATEMENT_CONSTRUCTOR_BASE_PARAMETERS
,
61 ModifierExpressionPtr modifiers
, bool ref
, const string
&name
,
62 ExpressionListPtr params
, const std::string
&retTypeConstraint
,
63 StatementListPtr stmt
, int attr
, const string
&docComment
,
64 ExpressionListPtr attrList
, bool method
/* = true */)
65 : Statement(STATEMENT_CONSTRUCTOR_BASE_PARAMETER_VALUES
),
66 m_method(method
), m_ref(ref
), m_hasCallToGetArgs(false), m_attribute(attr
),
67 m_cppLength(-1), m_modifiers(modifiers
),
68 m_originalName(name
), m_params(params
),
69 m_retTypeConstraint(retTypeConstraint
), m_stmt(stmt
),
70 m_docComment(docComment
), m_attrList(attrList
) {
71 m_name
= Util::toLower(name
);
75 MethodStatement::MethodStatement
76 (STATEMENT_CONSTRUCTOR_PARAMETERS
,
77 ModifierExpressionPtr modifiers
, bool ref
, const string
&name
,
78 ExpressionListPtr params
, const std::string
&retTypeConstraint
,
79 StatementListPtr stmt
,
80 int attr
, const string
&docComment
, ExpressionListPtr attrList
,
81 bool method
/* = true */)
82 : Statement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(MethodStatement
)),
83 m_method(method
), m_ref(ref
), m_hasCallToGetArgs(false), m_attribute(attr
),
84 m_cppLength(-1), m_modifiers(modifiers
), m_originalName(name
),
85 m_params(params
), m_retTypeConstraint(retTypeConstraint
),
86 m_stmt(stmt
), m_docComment(docComment
), m_attrList(attrList
) {
87 m_name
= Util::toLower(name
);
91 StatementPtr
MethodStatement::clone() {
92 MethodStatementPtr
stmt(new MethodStatement(*this));
93 stmt
->m_stmt
= Clone(m_stmt
);
94 stmt
->m_params
= Clone(m_params
);
95 stmt
->m_modifiers
= Clone(m_modifiers
);
99 string
MethodStatement::getFullName() const {
100 if (m_className
.empty()) return m_name
;
101 return m_className
+ "::" + m_name
;
104 string
MethodStatement::getOriginalFullName() const {
105 if (m_originalClassName
.empty()) return m_originalName
;
106 return m_originalClassName
+ "::" + m_originalName
;
109 string
MethodStatement::getOriginalFullNameForInjection() const {
110 FunctionScopeRawPtr funcScope
= getFunctionScope();
111 string injectionName
;
112 if (getGeneratorFunc()) {
113 injectionName
= funcScope
->isClosureGenerator() ?
115 m_originalName
+ "{continuation}";
116 } else if (getOrigGeneratorFunc()) {
117 bool needsOrig
= !funcScope
->getOrigGenFS()->isClosure();
118 injectionName
= needsOrig
?
119 getOrigGeneratorFunc()->getOriginalName() :
122 injectionName
= m_originalName
;
124 return m_originalClassName
.empty() ?
126 m_originalClassName
+ "::" + injectionName
;
129 bool MethodStatement::isRef(int index
/* = -1 */) const {
130 if (index
== -1) return m_ref
;
131 assert(index
>= 0 && index
< m_params
->getCount());
132 ParameterExpressionPtr param
=
133 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[index
]);
134 return param
->isRef();
137 int MethodStatement::getRecursiveCount() const {
138 return m_stmt
? m_stmt
->getRecursiveCount() : 0;
141 ///////////////////////////////////////////////////////////////////////////////
144 FunctionScopePtr
MethodStatement::onInitialParse(AnalysisResultConstPtr ar
,
146 int minParam
, maxParam
;
147 ConstructPtr self
= shared_from_this();
148 minParam
= maxParam
= 0;
151 std::set
<string
> names
, allDeclNames
;
153 maxParam
= m_params
->getCount();
154 for (i
= maxParam
; i
--; ) {
155 ParameterExpressionPtr param
=
156 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
157 if (param
->isRef()) hasRef
= true;
158 if (!param
->isOptional()) {
159 if (!minParam
) minParam
= i
+ 1;
160 } else if (minParam
&& !param
->hasTypeHint()) {
161 Compiler::Error(Compiler::RequiredAfterOptionalParam
, param
);
163 allDeclNames
.insert(param
->getName());
166 for (i
= maxParam
-1; i
>= 0; i
--) {
167 ParameterExpressionPtr param
=
168 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
169 if (names
.find(param
->getName()) != names
.end()) {
170 Compiler::Error(Compiler::RedundantParameter
, param
);
171 for (int j
= 0; j
< 1000; j
++) {
172 string name
= param
->getName() + lexical_cast
<string
>(j
);
173 if (names
.find(name
) == names
.end() &&
174 allDeclNames
.find(name
) == allDeclNames
.end()) {
180 names
.insert(param
->getName());
184 if (hasRef
|| m_ref
) {
185 m_attribute
|= FileScope::ContainsReference
;
188 vector
<UserAttributePtr
> attrs
;
190 for (int i
= 0; i
< m_attrList
->getCount(); ++i
) {
192 dynamic_pointer_cast
<UserAttribute
>((*m_attrList
)[i
]);
197 StatementPtr stmt
= dynamic_pointer_cast
<Statement
>(shared_from_this());
198 FunctionScopePtr funcScope
199 (new FunctionScope(ar
, m_method
, m_name
, stmt
, m_ref
, minParam
, maxParam
,
200 m_modifiers
, m_attribute
, m_docComment
, fs
, attrs
));
202 funcScope
->setVirtual();
204 setBlockScope(funcScope
);
206 funcScope
->setParamCounts(ar
, -1, -1);
210 void MethodStatement::onParseRecur(AnalysisResultConstPtr ar
,
211 ClassScopePtr classScope
) {
214 if (classScope
->isInterface()) {
215 if (m_modifiers
->isProtected() || m_modifiers
->isPrivate() ||
216 m_modifiers
->isAbstract() || m_modifiers
->isFinal()) {
217 m_modifiers
->parseTimeFatal(
218 Compiler::InvalidAttribute
,
219 "Access type for interface method %s::%s() must be omitted",
220 classScope
->getOriginalName().c_str(), getOriginalName().c_str());
223 if (m_modifiers
->isAbstract()) {
224 if (m_modifiers
->isPrivate() || m_modifiers
->isFinal()) {
225 m_modifiers
->parseTimeFatal(
226 Compiler::InvalidAttribute
,
227 "Cannot declare abstract method %s::%s() %s",
228 classScope
->getOriginalName().c_str(),
229 getOriginalName().c_str(),
230 m_modifiers
->isPrivate() ? "private" : "final");
232 if (!classScope
->isInterface() && !classScope
->isAbstract()) {
233 /* note that classScope->isAbstract() returns true for traits */
234 m_modifiers
->parseTimeFatal(Compiler::InvalidAttribute
,
235 "Class %s contains abstract method %s and "
236 "must therefore be declared abstract",
237 classScope
->getOriginalName().c_str(),
238 getOriginalName().c_str());
241 parseTimeFatal(Compiler::InvalidAttribute
,
242 "Abstract method %s::%s() cannot contain body",
243 classScope
->getOriginalName().c_str(),
244 getOriginalName().c_str());
248 if ((!m_modifiers
|| !m_modifiers
->isAbstract()) &&
249 !getStmts() && !classScope
->isInterface()) {
250 parseTimeFatal(Compiler::InvalidAttribute
,
251 "Non-abstract method %s::%s() must contain body",
252 classScope
->getOriginalName().c_str(),
253 getOriginalName().c_str());
256 FunctionScopeRawPtr fs
= getFunctionScope();
258 classScope
->addFunction(ar
, fs
);
260 m_className
= classScope
->getName();
261 m_originalClassName
= classScope
->getOriginalName();
263 setSpecialMethod(classScope
);
265 if (Option::DynamicInvokeFunctions
.find(getFullName()) !=
266 Option::DynamicInvokeFunctions
.end()) {
267 fs
->setDynamicInvoke();
270 for (int i
= 0; i
< m_params
->getCount(); i
++) {
271 ParameterExpressionPtr param
=
272 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
273 param
->parseHandler(classScope
);
276 FunctionScope::RecordFunctionInfo(m_name
, fs
);
279 void MethodStatement::fixupSelfAndParentTypehints(ClassScopePtr scope
) {
281 for (int i
= 0; i
< m_params
->getCount(); i
++) {
282 ParameterExpressionPtr param
=
283 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
284 param
->fixupSelfAndParentTypehints(scope
);
289 void MethodStatement::setSpecialMethod(ClassScopePtr classScope
) {
290 if (m_name
.size() < 2 || m_name
.substr(0,2) != "__") {
294 bool isStatic
= false;
295 if (m_name
== "__construct") {
296 classScope
->setAttribute(ClassScope::HasConstructor
);
297 } else if (m_name
== "__destruct") {
298 classScope
->setAttribute(ClassScope::HasDestructor
);
299 } else if (m_name
== "__call") {
300 classScope
->setAttribute(ClassScope::HasUnknownMethodHandler
);
302 } else if (m_name
== "__get") {
303 classScope
->setAttribute(ClassScope::HasUnknownPropGetter
);
305 } else if (m_name
== "__set") {
306 classScope
->setAttribute(ClassScope::HasUnknownPropSetter
);
308 } else if (m_name
== "__isset") {
309 classScope
->setAttribute(ClassScope::HasUnknownPropTester
);
311 } else if (m_name
== "__unset") {
312 classScope
->setAttribute(ClassScope::HasPropUnsetter
);
314 } else if (m_name
== "__call") {
315 classScope
->setAttribute(ClassScope::HasUnknownMethodHandler
);
317 } else if (m_name
== "__callstatic") {
318 classScope
->setAttribute(ClassScope::HasUnknownStaticMethodHandler
);
321 } else if (m_name
== "__invoke") {
322 classScope
->setAttribute(ClassScope::HasInvokeMethod
);
323 } else if (m_name
== "__tostring") {
327 // Fatal if the number of arguments is wrong
328 int n
= m_params
? m_params
->getCount() : 0;
330 parseTimeFatal(Compiler::InvalidMagicMethod
,
331 "Method %s::%s() must take exactly %d argument%s",
332 m_originalClassName
.c_str(), m_originalName
.c_str(),
333 numArgs
, (numArgs
== 1) ? "" : "s");
335 // Fatal if any arguments are pass by reference
336 if (m_params
&& hasRefParam()) {
337 parseTimeFatal(Compiler::InvalidMagicMethod
,
338 "Method %s::%s() cannot take arguments by reference",
339 m_originalClassName
.c_str(), m_originalName
.c_str());
341 // Fatal if protected/private or if the staticness is wrong
342 if (m_modifiers
->isProtected() || m_modifiers
->isPrivate() ||
343 m_modifiers
->isStatic() != isStatic
) {
344 parseTimeFatal(Compiler::InvalidMagicMethod
,
345 "Method %s::%s() must have public visibility and %sbe static",
346 m_originalClassName
.c_str(), m_originalName
.c_str(),
347 isStatic
? "" : "cannot ");
352 void MethodStatement::addTraitMethodToScope(AnalysisResultConstPtr ar
,
353 ClassScopePtr classScope
) {
354 FunctionScopeRawPtr funcScope
= getFunctionScope();
355 classScope
->addFunction(ar
, funcScope
);
356 setSpecialMethod(classScope
);
357 FunctionScope::RecordFunctionInfo(m_name
, funcScope
);
360 ///////////////////////////////////////////////////////////////////////////////
361 // static analysis functions
363 int MethodStatement::getLocalEffects() const {
364 if (m_method
) return NoEffect
;
365 FunctionScopeRawPtr scope
= getFunctionScope();
366 return scope
->isVolatile() ? OtherEffect
| CanThrow
: NoEffect
;
369 void MethodStatement::analyzeProgram(AnalysisResultPtr ar
) {
370 FunctionScopeRawPtr funcScope
= getFunctionScope();
373 m_params
->analyzeProgram(ar
);
375 if (m_stmt
) m_stmt
->analyzeProgram(ar
);
377 if (ar
->getPhase() == AnalysisResult::AnalyzeAll
) {
378 funcScope
->setParamSpecs(ar
);
379 if (funcScope
->isGenerator()) {
380 MethodStatementRawPtr orig
= getOrigGeneratorFunc();
381 VariableTablePtr variables
= funcScope
->getVariables();
383 Symbol
*cont
= variables
->getSymbol(CONTINUATION_OBJECT_NAME
);
386 orig
->getFunctionScope()->addUse(funcScope
, BlockScope::UseKindClosure
);
387 orig
->getFunctionScope()->setContainsBareThis(
388 funcScope
->containsBareThis(), funcScope
->containsRefThis());
389 orig
->getFunctionScope()->setContainsThis(funcScope
->containsThis());
391 if (ExpressionListPtr params
= orig
->getParams()) {
392 for (int i
= 0; i
< params
->getCount(); ++i
) {
393 auto param
= dynamic_pointer_cast
<ParameterExpression
>((*params
)[i
]);
394 Symbol
*gp
= variables
->addDeclaredSymbol(param
->getName(), param
);
395 gp
->setGeneratorParameter();
396 if (param
->isRef()) {
397 gp
->setRefGeneratorParameter();
403 if (ClosureExpressionRawPtr closure
= orig
->getContainingClosure()) {
404 if (ExpressionListPtr cvars
= closure
->getClosureVariables()) {
405 for (int i
= 0; i
< cvars
->getCount(); ++i
) {
406 auto param
= dynamic_pointer_cast
<ParameterExpression
>((*cvars
)[i
]);
407 Symbol
*gp
= variables
->addDeclaredSymbol(
408 param
->getName(), ConstructPtr());
409 gp
->setGeneratorParameter();
410 if (param
->isRef()) {
411 gp
->setRefGeneratorParameter();
418 if (funcScope
->isSepExtension() ||
419 Option::IsDynamicFunction(m_method
, m_name
) || Option::AllDynamic
) {
420 funcScope
->setDynamic();
422 // TODO: this may have to expand to a concept of "virtual" functions...
424 funcScope
->disableInline();
425 if (m_name
.length() > 2 && m_name
.substr(0,2) == "__") {
428 if (m_name
== "__destruct") {
429 funcScope
->setOverriding(Type::Variant
);
430 } else if (m_name
== "__call") {
431 funcScope
->setOverriding(Type::Variant
, Type::String
, Type::Array
);
433 } else if (m_name
== "__set") {
434 funcScope
->setOverriding(Type::Variant
, Type::String
, Type::Variant
);
436 } else if (m_name
== "__get") {
437 funcScope
->setOverriding(Type::Variant
, Type::String
);
439 } else if (m_name
== "__isset") {
440 funcScope
->setOverriding(Type::Boolean
, Type::String
);
442 } else if (m_name
== "__unset") {
443 funcScope
->setOverriding(Type::Variant
, Type::String
);
445 } else if (m_name
== "__sleep") {
446 funcScope
->setOverriding(Type::Variant
);
447 } else if (m_name
== "__wakeup") {
448 funcScope
->setOverriding(Type::Variant
);
449 } else if (m_name
== "__set_state") {
450 funcScope
->setOverriding(Type::Variant
, Type::Variant
);
452 } else if (m_name
== "__tostring") {
453 funcScope
->setOverriding(Type::String
);
454 } else if (m_name
== "__clone") {
455 funcScope
->setOverriding(Type::Variant
);
458 if (m_name
!= "__construct") {
462 if (paramCount
>= 0 && paramCount
!= funcScope
->getMaxParamCount()) {
463 Compiler::Error(Compiler::InvalidMagicMethod
, shared_from_this());
466 if (magic
) funcScope
->setMagicMethod();
468 // ArrayAccess methods
469 else if (m_name
.length() > 6 && m_name
.substr(0, 6) == "offset") {
470 if (m_name
== "offsetexists") {
471 funcScope
->setOverriding(Type::Boolean
, Type::Variant
);
472 } else if (m_name
== "offsetget") {
473 funcScope
->setOverriding(Type::Variant
, Type::Variant
);
474 } else if (m_name
== "offsetset") {
475 funcScope
->setOverriding(Type::Variant
, Type::Variant
, Type::Variant
);
476 } else if (m_name
== "offsetunset") {
477 funcScope
->setOverriding(Type::Variant
, Type::Variant
);
481 } else if (ar
->getPhase() == AnalysisResult::AnalyzeFinal
) {
482 TypePtr ret
= funcScope
->getReturnType();
483 if (ret
&& ret
->isSpecificObject()) {
484 FileScopePtr fs
= getFileScope();
485 if (fs
) fs
->addClassDependency(ar
, ret
->getName());
487 if (!getFunctionScope()->usesLSB()) {
488 if (StatementPtr orig
= getOrigGeneratorFunc()) {
489 orig
->getFunctionScope()->clearUsesLSB();
495 ConstructPtr
MethodStatement::getNthKid(int n
) const {
507 return ConstructPtr();
510 int MethodStatement::getKidCount() const {
514 void MethodStatement::setNthKid(int n
, ConstructPtr cp
) {
517 m_modifiers
= boost::dynamic_pointer_cast
<ModifierExpression
>(cp
);
520 m_params
= boost::dynamic_pointer_cast
<ExpressionList
>(cp
);
523 m_stmt
= boost::dynamic_pointer_cast
<StatementList
>(cp
);
531 void MethodStatement::inferTypes(AnalysisResultPtr ar
) {
534 void MethodStatement::inferFunctionTypes(AnalysisResultPtr ar
) {
535 IMPLEMENT_INFER_AND_CHECK_ASSERT(getFunctionScope());
537 FunctionScopeRawPtr funcScope
= getFunctionScope();
538 bool pseudoMain
= funcScope
->inPseudoMain();
540 if (m_stmt
&& funcScope
->isFirstPass()) {
542 funcScope
->getReturnType() ||
543 m_stmt
->hasRetExp()) {
544 bool lastIsReturn
= false;
545 if (m_stmt
->getCount()) {
546 StatementPtr lastStmt
= (*m_stmt
)[m_stmt
->getCount()-1];
547 if (lastStmt
->is(Statement::KindOfReturnStatement
)) {
552 ExpressionPtr constant
=
553 makeScalarExpression(ar
, funcScope
->inPseudoMain() ?
555 Variant(Variant::NullInit()));
556 ReturnStatementPtr returnStmt
=
558 new ReturnStatement(getScope(), getLocation(), constant
));
559 m_stmt
->addElement(returnStmt
);
565 m_params
->inferAndCheck(ar
, Type::Any
, false);
568 // must also include params and use vars if this is a generator. note: we are
569 // OK reading the params from the AST nodes of the original generator
570 // function, since we have the dependency links set up
571 if (funcScope
->isGenerator()) {
572 // orig function params
573 MethodStatementRawPtr m
= getOrigGeneratorFunc();
576 VariableTablePtr variables
= funcScope
->getVariables();
577 ExpressionListPtr params
= m
->getParams();
579 for (int i
= 0; i
< params
->getCount(); i
++) {
580 ParameterExpressionPtr param
=
581 dynamic_pointer_cast
<ParameterExpression
>((*params
)[i
]);
582 const string
&name
= param
->getName();
583 assert(!param
->isRef() || param
->getType()->is(Type::KindOfVariant
));
584 variables
->addParamLike(name
, param
->getType(), ar
, param
,
585 funcScope
->isFirstPass());
590 ExpressionListPtr useVars
= m
->getFunctionScope()->getClosureVars();
592 for (int i
= 0; i
< useVars
->getCount(); i
++) {
593 ParameterExpressionPtr param
=
594 dynamic_pointer_cast
<ParameterExpression
>((*useVars
)[i
]);
595 const string
&name
= param
->getName();
596 assert(!param
->isRef() || param
->getType()->is(Type::KindOfVariant
));
597 variables
->addParamLike(name
, param
->getType(), ar
, param
,
598 funcScope
->isFirstPass());
604 m_stmt
->inferTypes(ar
);
608 ///////////////////////////////////////////////////////////////////////////////
609 // code generation functions
611 void MethodStatement::outputPHP(CodeGenerator
&cg
, AnalysisResultPtr ar
) {
612 FunctionScopeRawPtr funcScope
= getFunctionScope();
614 m_modifiers
->outputPHP(cg
, ar
);
615 cg_printf(" function ");
616 if (m_ref
) cg_printf("&");
617 if (!ParserBase::IsClosureOrContinuationName(m_name
)) {
618 cg_printf("%s", m_originalName
.c_str());
621 if (m_params
) m_params
->outputPHP(cg
, ar
);
623 cg_indentBegin(") {\n");
624 funcScope
->outputPHP(cg
, ar
);
625 m_stmt
->outputPHP(cg
, ar
);
632 bool MethodStatement::hasRefParam() {
633 for (int i
= 0; i
< m_params
->getCount(); i
++) {
634 ParameterExpressionPtr param
=
635 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
636 if (param
->isRef()) return true;
641 void MethodStatement::checkParameters() {
642 // only allow paramenter modifiers (public, private, protected)
643 // on constructor for promotion
647 bool isCtor
= m_name
== "__construct";
648 for (int i
= 0; i
< m_params
->getCount(); i
++) {
650 dynamic_pointer_cast
<ParameterExpression
>((*m_params
)[i
]);
651 switch (param
->getModifier()) {
662 param
->parseTimeFatal(Compiler::InvalidAttribute
,
663 "Invalid modifier on __construct, only public, "
664 "private or protected allowed");
666 param
->parseTimeFatal(Compiler::InvalidAttribute
,
667 "Parameters modifiers not allowed on methods");