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 <compiler/expression/simple_function_call.h>
18 #include <compiler/analysis/dependency_graph.h>
19 #include <compiler/analysis/file_scope.h>
20 #include <compiler/analysis/function_scope.h>
21 #include <compiler/analysis/class_scope.h>
22 #include <compiler/analysis/code_error.h>
23 #include <compiler/expression/expression_list.h>
24 #include <compiler/statement/statement_list.h>
25 #include <compiler/statement/exp_statement.h>
26 #include <compiler/statement/return_statement.h>
27 #include <compiler/statement/method_statement.h>
28 #include <compiler/expression/scalar_expression.h>
29 #include <compiler/expression/constant_expression.h>
30 #include <compiler/expression/parameter_expression.h>
31 #include <compiler/expression/assignment_expression.h>
32 #include <compiler/expression/unary_op_expression.h>
33 #include <compiler/analysis/constant_table.h>
34 #include <compiler/analysis/variable_table.h>
35 #include <util/util.h>
36 #include <compiler/option.h>
37 #include <compiler/expression/simple_variable.h>
38 #include <compiler/parser/parser.h>
39 #include <util/parser/hphp.tab.hpp>
40 #include <runtime/base/complex_types.h>
41 #include <runtime/base/externals.h>
42 #include <runtime/base/execution_context.h>
43 #include <runtime/base/array/array_init.h>
44 #include <runtime/base/string_util.h>
48 using namespace boost
;
50 ///////////////////////////////////////////////////////////////////////////////
53 std::map
<std::string
, int>SimpleFunctionCall::FunctionTypeMap
;
55 #define CHECK_HOOK(n) \
56 (SimpleFunctionCall::m_hookHandler ? \
57 SimpleFunctionCall::m_hookHandler(ar, this, n) : 0)
59 Expression
*(*SimpleFunctionCall::m_hookHandler
)
60 (AnalysisResultPtr ar
, SimpleFunctionCall
*call
, HphpHookUniqueId id
);
62 void SimpleFunctionCall::InitFunctionTypeMap() {
63 if (FunctionTypeMap
.empty()) {
64 FunctionTypeMap
["define"] = DefineFunction
;
65 FunctionTypeMap
["create_function"] = CreateFunction
;
67 FunctionTypeMap
["func_get_arg"] = VariableArgumentFunction
;
68 FunctionTypeMap
["func_get_args"] = VariableArgumentFunction
;
69 FunctionTypeMap
["func_num_args"] = VariableArgumentFunction
;
71 FunctionTypeMap
["extract"] = ExtractFunction
;
72 FunctionTypeMap
["compact"] = CompactFunction
;
74 FunctionTypeMap
["shell_exec"] = ShellExecFunction
;
75 FunctionTypeMap
["exec"] = ShellExecFunction
;
76 FunctionTypeMap
["passthru"] = ShellExecFunction
;
77 FunctionTypeMap
["system"] = ShellExecFunction
;
79 FunctionTypeMap
["defined"] = DefinedFunction
;
80 FunctionTypeMap
["function_exists"] = FunctionExistsFunction
;
81 FunctionTypeMap
["class_exists"] = ClassExistsFunction
;
82 FunctionTypeMap
["interface_exists"] = InterfaceExistsFunction
;
83 FunctionTypeMap
["constant"] = ConstantFunction
;
85 FunctionTypeMap
["unserialize"] = UnserializeFunction
;
86 FunctionTypeMap
["apc_fetch"] = UnserializeFunction
;
88 FunctionTypeMap
["get_defined_vars"] = GetDefinedVarsFunction
;
92 ///////////////////////////////////////////////////////////////////////////////
93 // constructors/destructors
95 SimpleFunctionCall::SimpleFunctionCall
96 (EXPRESSION_CONSTRUCTOR_PARAMETERS
,
97 const std::string
&name
, ExpressionListPtr params
, ExpressionPtr cls
)
98 : FunctionCall(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES
,
99 ExpressionPtr(), name
, params
, cls
),
100 m_type(UnknownType
), m_dynamicConstant(false),
101 m_parentClass(false), m_builtinFunction(false), m_noPrefix(false),
102 m_invokeFewArgsDecision(true),
103 m_dynamicInvoke(false), m_safe(0), m_arrayParams(false),
106 if (!m_class
&& m_className
.empty()) {
107 m_dynamicInvoke
= Option::DynamicInvokeFunctions
.find(m_name
) !=
108 Option::DynamicInvokeFunctions
.end();
110 if (FunctionTypeMap
.empty()) InitFunctionTypeMap();
111 map
<string
, int>::const_iterator iter
=
112 FunctionTypeMap
.find(m_name
);
113 if (iter
!= FunctionTypeMap
.end()) {
114 m_type
= iter
->second
;
119 SimpleFunctionCall::~SimpleFunctionCall() {
121 ASSERT(m_hookHandler
);
122 m_hookHandler(AnalysisResultPtr(), this, hphpUniqueDtor
);
126 ExpressionPtr
SimpleFunctionCall::clone() {
127 SimpleFunctionCallPtr
exp(new SimpleFunctionCall(*this));
128 FunctionCall::deepCopy(exp
);
129 exp
->m_safeDef
= Clone(m_safeDef
);
134 ///////////////////////////////////////////////////////////////////////////////
137 void SimpleFunctionCall::onParse(AnalysisResultPtr ar
) {
140 FileScopePtr fs
= ar
->getFileScope();
141 ConstructPtr self
= shared_from_this();
142 if (m_className
.empty()) {
143 CodeErrorPtr codeError
= ar
->getCodeError();
146 if (m_params
->getCount() == 2 &&
147 (*m_params
)[0]->isLiteralString() &&
148 (*m_params
)[1]->isLiteralString()) {
149 FunctionScopePtr func
= ar
->getFunctionScope();
150 if (func
) func
->disableInline();
151 string params
= (*m_params
)[0]->getLiteralString();
152 string body
= (*m_params
)[1]->getLiteralString();
153 m_lambda
= CodeGenerator::GetNewLambda();
154 string code
= "function " + m_lambda
+ "(" + params
+ ") "
156 ar
->appendExtraCode(code
);
159 case VariableArgumentFunction
:
162 At this point, we dont have a function scope, so we set
163 the flags on the FileScope.
164 The FileScope maintains a stack of attributes, so that
165 it correctly handles each function.
166 But note that later phases should set/get the attribute
167 directly on the FunctionScope, rather than on the FileScope
169 ar
->getFileScope()->setAttribute(FileScope::VariableArgument
);
171 case ExtractFunction
:
172 ar
->getCodeError()->record(self
, CodeError::UseExtract
, self
);
173 ar
->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable
);
174 ar
->getFileScope()->setAttribute(FileScope::ContainsExtract
);
176 case CompactFunction
:
177 ar
->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable
);
178 ar
->getFileScope()->setAttribute(FileScope::ContainsCompact
);
180 case ShellExecFunction
:
181 ar
->getCodeError()->record(self
, CodeError::UseShellExec
, self
);
183 case GetDefinedVarsFunction
:
184 ar
->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable
);
185 ar
->getFileScope()->setAttribute(FileScope::ContainsGetDefinedVars
);
186 ar
->getFileScope()->setAttribute(FileScope::ContainsCompact
);
189 CHECK_HOOK(onSimpleFunctionCallFuncType
);
194 string call
= getText();
195 string name
= m_name
;
196 if (!m_className
.empty()) {
197 name
= m_className
+ "::" + name
;
199 ar
->getDependencyGraph()->add(DependencyGraph::KindOfFunctionCall
, call
,
200 shared_from_this(), name
);
203 ///////////////////////////////////////////////////////////////////////////////
204 // static analysis functions
206 void SimpleFunctionCall::addDependencies(AnalysisResultPtr ar
) {
208 if (m_className
.empty()) {
209 addUserFunction(ar
, m_name
);
210 } else if (m_origClassName
!= "parent") {
211 addUserClass(ar
, m_className
);
213 m_parentClass
= true;
218 void SimpleFunctionCall::addLateDependencies(AnalysisResultPtr ar
) {
219 AnalysisResult::Phase phase
= ar
->getPhase();
220 ar
->setPhase(AnalysisResult::AnalyzeAll
);
225 ConstructPtr
SimpleFunctionCall::getNthKid(int n
) const {
226 if (!n
) return m_safeDef
;
227 return FunctionCall::getNthKid(n
);
230 void SimpleFunctionCall::setNthKid(int n
, ConstructPtr cp
) {
232 m_safeDef
= boost::dynamic_pointer_cast
<Expression
>(cp
);
234 FunctionCall::setNthKid(n
, cp
);
238 void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar
) {
240 m_class
->analyzeProgram(ar
);
241 setDynamicByIdentifier(ar
, m_name
);
246 if (m_safeDef
) m_safeDef
->analyzeProgram(ar
);
247 if (m_params
) m_params
->analyzeProgram(ar
);
249 if (ar
->getPhase() == AnalysisResult::AnalyzeInclude
) {
251 CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude
);
253 ConstructPtr self
= shared_from_this();
255 // We need to know the name of the constant so that we can associate it
256 // with this file before we do type inference.
257 if (!m_class
&& m_className
.empty() && m_type
== DefineFunction
&&
258 m_params
&& m_params
->getCount() >= 2) {
259 ExpressionPtr ename
= (*m_params
)[0];
260 if (ConstantExpressionPtr cname
=
261 dynamic_pointer_cast
<ConstantExpression
>(ename
)) {
263 ename
= ExpressionPtr(
264 new ScalarExpression(cname
->getLocation(), KindOfScalarExpression
,
265 T_STRING
, cname
->getName(), true));
266 m_params
->removeElement(0);
267 m_params
->insertElement(ename
);
269 ScalarExpressionPtr name
=
270 dynamic_pointer_cast
<ScalarExpression
>(ename
);
272 string varName
= name
->getIdentifier();
273 if (!varName
.empty()) {
274 ar
->getFileScope()->declareConstant(ar
, varName
);
276 // handling define("CONSTANT", ...);
277 ExpressionPtr value
= (*m_params
)[1];
278 BlockScopePtr block
= ar
->findConstantDeclarer(varName
);
279 ConstantTablePtr constants
= block
->getConstants();
280 if (constants
!= ar
->getConstants()) {
281 constants
->add(varName
, Type::Some
, value
, ar
, self
);
282 if (name
->hasHphpNote("Dynamic")) {
283 constants
->setDynamic(ar
, varName
);
290 if (m_type
== UnserializeFunction
) {
291 ar
->forceClassVariants(getOriginalScope(ar
), false);
295 if (ar
->getPhase() == AnalysisResult::AnalyzeAll
) {
296 // Look up the corresponding FunctionScope and ClassScope
297 // for this function call
299 FunctionScopePtr func
;
301 if (!m_class
&& m_className
.empty()) {
302 func
= ar
->findFunction(m_name
);
304 cls
= ar
->resolveClass(m_className
);
305 if (cls
&& cls
->isRedeclaring()) {
306 cls
= ar
->findExactClass(m_className
);
310 if (m_name
== "__construct") {
311 func
= cls
->findConstructor(ar
, true);
313 func
= cls
->findFunction(ar
, m_name
, true, true);
317 if (func
&& !func
->isRedeclaring()) {
318 if (m_funcScope
!= func
) {
320 Construct::recomputeEffects();
324 // check for dynamic constant and volatile function/class
325 if (!m_class
&& m_className
.empty() &&
326 (m_type
== DefinedFunction
||
327 m_type
== FunctionExistsFunction
||
328 m_type
== ClassExistsFunction
||
329 m_type
== InterfaceExistsFunction
) &&
330 m_params
&& m_params
->getCount() >= 1) {
331 ExpressionPtr value
= (*m_params
)[0];
332 if (value
->isScalar()) {
333 ScalarExpressionPtr name
=
334 dynamic_pointer_cast
<ScalarExpression
>(value
);
335 if (name
&& name
->isLiteralString()) {
336 string symbol
= name
->getLiteralString();
338 case DefinedFunction
: {
339 ConstantTablePtr constants
= ar
->getConstants();
340 if (!constants
->isPresent(symbol
)) {
342 BlockScopePtr block
= ar
->findConstantDeclarer(symbol
);
343 if (block
) { // found the constant
344 constants
= block
->getConstants();
346 constants
->setDynamic(ar
, symbol
);
351 case FunctionExistsFunction
: {
352 FunctionScopePtr func
= ar
->findFunction(Util::toLower(symbol
));
353 if (func
&& func
->isUserFunction()) {
358 case InterfaceExistsFunction
:
359 case ClassExistsFunction
: {
360 ClassScopePtr cls
= ar
->findClass(Util::toLower(symbol
));
361 if (cls
&& cls
->isUserClass()) {
375 if (ar
->getPhase() == AnalysisResult::AnalyzeAll
) {
376 markRefParams(m_funcScope
, m_name
, canInvokeFewArgs());
381 bool SimpleFunctionCall::isDefineWithoutImpl(AnalysisResultPtr ar
) {
382 if (m_class
|| !m_className
.empty()) return false;
383 if (m_type
== DefineFunction
&& m_params
&& m_params
->getCount() >= 2) {
384 if (m_dynamicConstant
) return false;
385 ScalarExpressionPtr name
=
386 dynamic_pointer_cast
<ScalarExpression
>((*m_params
)[0]);
387 if (!name
) return false;
388 string varName
= name
->getIdentifier();
389 if (varName
.empty()) return false;
390 if (ar
->getConstants()->isSystem(varName
)) return true;
391 ExpressionPtr value
= (*m_params
)[1];
392 return (!ar
->isConstantRedeclared(varName
)) && value
->isScalar();
398 //typedef std::map<std::string, ExpressionPtr> StringToExpressionPtrMap;
400 static ExpressionPtr
cloneForInlineRecur(ExpressionPtr exp
,
401 const std::string
&prefix
,
402 StringToExpressionPtrMap
&sepm
,
403 AnalysisResultPtr ar
) {
404 for (int i
= 0, n
= exp
->getKidCount(); i
< n
; i
++) {
405 if (ExpressionPtr k
= exp
->getNthExpr(i
)) {
406 exp
->setNthKid(i
, cloneForInlineRecur(k
, prefix
, sepm
, ar
));
409 switch (exp
->getKindOf()) {
410 case Expression::KindOfSimpleVariable
:
412 SimpleVariablePtr
sv(dynamic_pointer_cast
<SimpleVariable
>(exp
));
413 if (!sv
->isThis() && !sv
->isSuperGlobal()) {
414 string name
= prefix
+ sv
->getName();
415 ExpressionPtr
rep(new SimpleVariable(exp
->getLocation(),
416 exp
->getKindOf(), name
));
417 rep
->copyContext(exp
);
423 case Expression::KindOfSimpleFunctionCall
:
425 static_pointer_cast
<SimpleFunctionCall
>(exp
)->addLateDependencies(ar
);
433 static ExpressionPtr
cloneForInline(ExpressionPtr exp
,
434 const std::string
&prefix
,
435 StringToExpressionPtrMap
&sepm
,
436 AnalysisResultPtr ar
) {
437 return cloneForInlineRecur(exp
->clone(), prefix
, sepm
, ar
);
440 static int cloneStmtsForInline(ExpressionListPtr elist
, StatementPtr s
,
441 const std::string
&prefix
,
442 StringToExpressionPtrMap
&sepm
,
443 AnalysisResultPtr ar
) {
444 switch (s
->getKindOf()) {
445 case Statement::KindOfStatementList
:
447 for (int i
= 0, n
= s
->getKidCount(); i
< n
; ++i
) {
448 if (int ret
= cloneStmtsForInline(elist
, s
->getNthStmt(i
),
455 case Statement::KindOfExpStatement
:
456 elist
->addElement(cloneForInline(dynamic_pointer_cast
<ExpStatement
>(s
)->
457 getExpression(), prefix
, sepm
, ar
));
459 case Statement::KindOfReturnStatement
:
462 dynamic_pointer_cast
<ReturnStatement
>(s
)->getRetExp();
465 elist
->addElement(cloneForInline(exp
, prefix
, sepm
, ar
));
476 ExpressionPtr
SimpleFunctionCall::optimize(AnalysisResultPtr ar
) {
477 if (m_class
|| !m_className
.empty() || !m_funcScope
) return ExpressionPtr();
479 if (!m_funcScope
->isUserFunction()) {
480 if (m_type
== UnknownType
&& m_funcScope
->isFoldable()) {
483 if (!m_params
->isScalar()) return ExpressionPtr();
484 for (int i
= 0, n
= m_params
->getCount(); i
< n
; ++i
) {
486 if (!(*m_params
)[i
]->getScalarValue(v
)) return ExpressionPtr();
491 g_context
->setThrowAllErrors(true);
492 Variant v
= invoke_builtin(m_funcScope
->getName().c_str(),
494 g_context
->setThrowAllErrors(false);
495 return MakeScalarExpression(ar
, getLocation(), v
);
497 g_context
->setThrowAllErrors(false);
499 return ExpressionPtr();
501 if (m_funcScope
->getOptFunction()) {
502 SimpleFunctionCallPtr
self(
503 static_pointer_cast
<SimpleFunctionCall
>(shared_from_this()));
504 ExpressionPtr e
= (m_funcScope
->getOptFunction())(0, ar
, self
, 0);
509 if (!m_funcScope
->getInlineAsExpr() ||
510 !m_funcScope
->getStmt() ||
511 m_funcScope
->getStmt()->getRecursiveCount() > 10) {
512 return ExpressionPtr();
515 FunctionScopePtr fs
= ar
->getFunctionScope();
516 if (!fs
|| fs
->inPseudoMain()) return ExpressionPtr();
517 VariableTablePtr vt
= fs
->getVariables();
518 int nAct
= m_params
? m_params
->getCount() : 0;
519 int nMax
= m_funcScope
->getMaxParamCount();
520 if (unsigned(nAct
- m_funcScope
->getMinParamCount()) > (unsigned)nMax
||
521 vt
->getAttribute(VariableTable::ContainsDynamicVariable
) ||
522 vt
->getAttribute(VariableTable::ContainsExtract
) ||
523 vt
->getAttribute(VariableTable::ContainsCompact
) ||
524 vt
->getAttribute(VariableTable::ContainsGetDefinedVars
)) {
525 return ExpressionPtr();
528 ExpressionListPtr
elist(new ExpressionList(getLocation(),
529 KindOfExpressionList
,
530 ExpressionList::ListKindWrapped
));
532 std::ostringstream oss
;
533 oss
<< "inl" << m_funcScope
->nextInlineIndex() << "_" << m_name
<< "_";
534 std::string prefix
= oss
.str();
537 (dynamic_pointer_cast
<MethodStatement
>(m_funcScope
->getStmt()));
538 ExpressionListPtr plist
= m
->getParams();
541 StringToExpressionPtrMap sepm
;
543 for (i
= 0; i
< nMax
; i
++) {
544 ParameterExpressionPtr param
545 (dynamic_pointer_cast
<ParameterExpression
>((*plist
)[i
]));
546 ExpressionPtr arg
= i
< nAct
? (*m_params
)[i
] :
547 param
->defaultValue()->clone();
548 SimpleVariablePtr var
549 (new SimpleVariable((i
< nAct
? arg
.get() : this)->getLocation(),
550 KindOfSimpleVariable
,
551 prefix
+ param
->getName()));
552 bool ref
= m_funcScope
->isRefParam(i
);
553 AssignmentExpressionPtr ae
554 (new AssignmentExpression(arg
->getLocation(), KindOfAssignmentExpression
,
556 elist
->addElement(ae
);
557 if (i
< nAct
&& (ref
|| !arg
->isScalar())) {
558 sepm
[var
->getName()] = var
;
562 if (cloneStmtsForInline(elist
, m
->getStmts(), prefix
, sepm
, ar
) <= 0) {
563 elist
->addElement(CONSTANT("null"));
567 ExpressionListPtr unset_list
568 (new ExpressionList(this->getLocation(), KindOfExpressionList
));
570 for (StringToExpressionPtrMap::iterator it
= sepm
.begin(), end
= sepm
.end();
572 ExpressionPtr var
= it
->second
->clone();
573 var
->clearContext((Context
)(unsigned)-1);
574 unset_list
->addElement(var
);
578 new UnaryOpExpression(this->getLocation(), KindOfUnaryOpExpression
,
579 unset_list
, T_UNSET
, true));
580 i
= elist
->getCount();
581 ExpressionPtr ret
= (*elist
)[--i
];
582 if (ret
->isScalar()) {
583 elist
->insertElement(unset
, i
);
585 ExpressionListPtr result_list
586 (new ExpressionList(this->getLocation(), KindOfExpressionList
,
587 ExpressionList::ListKindLeft
));
588 result_list
->addElement(ret
);
589 result_list
->addElement(unset
);
590 (*elist
)[i
] = result_list
;
594 elist
->copyContext(static_pointer_cast
<Expression
>(shared_from_this()));
598 ExpressionPtr
SimpleFunctionCall::preOptimize(AnalysisResultPtr ar
) {
600 ar
->preOptimize(m_class
);
603 ar
->preOptimize(m_safeDef
);
604 ar
->preOptimize(m_nameExp
);
605 ar
->preOptimize(m_params
);
606 if (ar
->getPhase() >= AnalysisResult::FirstPreOptimize
) {
607 if (Expression
*rep
= CHECK_HOOK(onSimpleFunctionCallPreOptimize
)) {
608 ExpressionPtr
tmp(rep
);
609 ar
->preOptimize(tmp
);
614 if (ar
->getPhase() != AnalysisResult::SecondPreOptimize
) {
615 return ExpressionPtr();
617 if (ExpressionPtr rep
= optimize(ar
)) {
620 // optimize away various "exists" functions, this may trigger
621 // dead code elimination and improve type-inference.
622 if (!m_class
&& m_className
.empty() &&
623 (m_type
== DefinedFunction
||
624 m_type
== FunctionExistsFunction
||
625 m_type
== ClassExistsFunction
||
626 m_type
== InterfaceExistsFunction
) &&
627 m_params
&& m_params
->getCount() == 1) {
628 ExpressionPtr value
= (*m_params
)[0];
629 if (value
->isScalar()) {
630 ScalarExpressionPtr name
= dynamic_pointer_cast
<ScalarExpression
>(value
);
631 if (name
&& name
->isLiteralString()) {
632 string symbol
= name
->getLiteralString();
634 case DefinedFunction
: {
635 ConstantTablePtr constants
= ar
->getConstants();
637 if (constants
->isPresent(symbol
) && !constants
->isDynamic(symbol
)) {
638 return CONSTANT("true");
641 BlockScopePtr block
= ar
->findConstantDeclarer(symbol
);
642 // not found (i.e., undefined)
644 if (symbol
.find("::") == std::string::npos
) {
645 return CONSTANT("false");
647 // e.g., defined("self::ZERO")
648 return ExpressionPtr();
651 constants
= block
->getConstants();
652 // already set to be dynamic
653 if (constants
->isDynamic(symbol
)) return ExpressionPtr();
654 ConstructPtr decl
= constants
->getValue(symbol
);
655 ExpressionPtr constValue
= dynamic_pointer_cast
<Expression
>(decl
);
656 if (constValue
->isScalar()) {
657 return CONSTANT("true");
659 return ExpressionPtr();
663 case FunctionExistsFunction
: {
664 const std::string
&lname
= Util::toLower(symbol
);
665 if (Option::DynamicInvokeFunctions
.find(lname
) ==
666 Option::DynamicInvokeFunctions
.end()) {
667 FunctionScopePtr func
= ar
->findFunction(lname
);
669 return CONSTANT("false");
671 if (func
->isUserFunction()) {
674 if (!func
->isVolatile()) {
675 return CONSTANT("true");
680 case InterfaceExistsFunction
: {
681 ClassScopePtrVec classes
= ar
->findClasses(Util::toLower(symbol
));
682 bool interfaceFound
= false;
683 for (ClassScopePtrVec::const_iterator it
= classes
.begin();
684 it
!= classes
.end(); ++it
) {
685 ClassScopePtr cls
= *it
;
686 if (cls
->isUserClass()) cls
->setVolatile();
687 if (cls
->isInterface()) {
688 interfaceFound
= true;
691 if (!interfaceFound
) return CONSTANT("false");
692 if (classes
.size() == 1 && !classes
.back()->isVolatile()) {
693 return CONSTANT("true");
697 case ClassExistsFunction
: {
698 ClassScopePtrVec classes
= ar
->findClasses(Util::toLower(symbol
));
699 bool classFound
= false;
700 for (ClassScopePtrVec::const_iterator it
= classes
.begin();
701 it
!= classes
.end(); ++it
) {
702 ClassScopePtr cls
= *it
;
703 if (cls
->isUserClass()) cls
->setVolatile();
704 if (!cls
->isInterface()) {
708 if (!classFound
) return CONSTANT("false");
709 if (classes
.size() == 1 && !classes
.back()->isVolatile()) {
710 return CONSTANT("true");
720 return ExpressionPtr();
723 ExpressionPtr
SimpleFunctionCall::postOptimize(AnalysisResultPtr ar
) {
724 if (!Option::KeepStatementsWithNoEffect
&& isDefineWithoutImpl(ar
)) {
725 Construct::recomputeEffects();
726 return CONSTANT("true");
729 Dont do this for now. Need to take account of newly created
730 variables etc (which would normally be handled by inferTypes).
732 if (ExpressionPtr rep = optimize(ar)) {
736 ar
->postOptimize(m_safeDef
);
737 return FunctionCall::postOptimize(ar
);
740 int SimpleFunctionCall::getLocalEffects() const {
741 if (m_class
) return UnknownEffect
;
743 if (m_funcScope
&& !m_funcScope
->hasEffect()) {
747 return UnknownEffect
;
750 TypePtr
SimpleFunctionCall::inferTypes(AnalysisResultPtr ar
, TypePtr type
,
756 TypePtr
SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar
, TypePtr type
,
761 m_class
->inferAndCheck(ar
, Type::Any
, false);
765 m_safeDef
->inferAndCheck(ar
, Type::Any
, false);
769 ar
->getScope()->getVariables()->
770 setAttribute(VariableTable::NeedGlobalPointer
);
773 ConstructPtr self
= shared_from_this();
775 // handling define("CONSTANT", ...);
776 if (!m_class
&& m_className
.empty()) {
777 if (m_type
== DefineFunction
&& m_params
&& m_params
->getCount() >= 2) {
778 ScalarExpressionPtr name
=
779 dynamic_pointer_cast
<ScalarExpression
>((*m_params
)[0]);
782 varName
= name
->getIdentifier();
783 if (!varName
.empty()) {
784 ExpressionPtr value
= (*m_params
)[1];
785 TypePtr varType
= value
->inferAndCheck(ar
, Type::Some
, false);
786 ar
->getDependencyGraph()->
787 addParent(DependencyGraph::KindOfConstant
,
788 ar
->getName(), varName
, self
);
789 BlockScopePtr block
= ar
->findConstantDeclarer(varName
);
791 ar
->getFileScope()->declareConstant(ar
, varName
);
792 block
= ar
->findConstantDeclarer(varName
);
795 ConstantTablePtr constants
= block
->getConstants();
796 if (constants
!= ar
->getConstants()) {
797 if (value
&& !value
->isScalar()) {
798 constants
->setDynamic(ar
, varName
);
799 varType
= Type::Variant
;
801 if (constants
->isDynamic(varName
)) {
802 m_dynamicConstant
= true;
803 ar
->getScope()->getVariables()->
804 setAttribute(VariableTable::NeedGlobalPointer
);
806 constants
->setType(ar
, varName
, varType
, true);
808 // in case the old 'value' has been optimized
809 constants
->setValue(ar
, varName
, value
);
811 return checkTypesImpl(ar
, type
, Type::Boolean
, coerce
);
814 if (varName
.empty() && ar
->isFirstPass()) {
815 ar
->getCodeError()->record(self
, CodeError::BadDefine
, self
);
817 } else if (m_type
== ExtractFunction
) {
818 ar
->getScope()->getVariables()->forceVariants(ar
, VariableTable::AnyVars
);
822 FunctionScopePtr func
;
824 // avoid raising both MissingObjectContext and UnknownFunction
825 bool errorFlagged
= false;
827 if (!m_class
&& m_className
.empty()) {
828 if (!m_dynamicInvoke
) {
829 func
= ar
->findFunction(m_name
);
832 ClassScopePtr cls
= ar
->resolveClass(m_className
);
833 if (cls
&& cls
->isVolatile()) {
834 ar
->getScope()->getVariables()
835 ->setAttribute(VariableTable::NeedGlobalPointer
);
837 if (cls
->isRedeclaring()) {
838 cls
= ar
->findExactClass(m_className
);
840 m_redeclaredClass
= true;
845 if (ar
->isFirstPass()) {
846 ar
->getCodeError()->record(self
, CodeError::UnknownClass
, self
);
849 m_params
->inferAndCheck(ar
, Type::Some
, false);
850 m_params
->markParams(canInvokeFewArgs());
852 return checkTypesImpl(ar
, type
, Type::Variant
, coerce
);
855 m_derivedFromRedeclaring
= cls
->derivesFromRedeclaring();
858 if (m_name
== "__construct") {
859 // if the class is known, php will try to identify class-name ctor
860 func
= cls
->findConstructor(ar
, true);
862 func
= cls
->findFunction(ar
, m_name
, true, true);
865 if (func
&& !func
->isStatic()) {
866 ClassScopePtr clsThis
= ar
->getClassScope();
867 FunctionScopePtr funcThis
= ar
->getFunctionScope();
869 (clsThis
->getName() != m_className
&&
870 !clsThis
->derivesFrom(ar
, m_className
, true, false)) ||
871 funcThis
->isStatic()) {
873 if (ar
->isFirstPass()) {
874 ar
->getCodeError()->record(self
, CodeError::MissingObjectContext
,
882 if (!func
|| func
->isRedeclaring()) {
885 Construct::recomputeEffects();
889 ar
->getScope()->getVariables()->
890 setAttribute(VariableTable::NeedGlobalPointer
);
892 if (!func
&& !errorFlagged
&& ar
->isFirstPass()) {
893 ar
->getCodeError()->record(self
, CodeError::UnknownFunction
, self
);
897 FunctionScope::RefParamInfoPtr info
=
898 FunctionScope::GetRefParamInfo(m_name
);
900 for (int i
= m_params
->getCount(); i
--; ) {
901 if (info
->isRefParam(i
)) {
902 m_params
->markParam(i
, canInvokeFewArgs());
907 (*m_params
)[0]->inferAndCheck(ar
, Type::Array
, false);
909 m_params
->inferAndCheck(ar
, Type::Some
, false);
912 return checkTypesImpl(ar
, type
, Type::Variant
, coerce
);
913 } else if (func
!= m_funcScope
) {
915 Construct::recomputeEffects();
918 m_builtinFunction
= (!func
->isUserFunction() || func
->isSepExtension());
920 CHECK_HOOK(beforeSimpleFunctionCallCheck
);
923 TypePtr rtype
= checkParamsAndReturn(ar
, type
, coerce
, func
, m_arrayParams
);
925 if (m_arrayParams
&& func
&& !m_builtinFunction
) func
->setDirectInvoke();
927 if (!m_valid
&& m_params
) {
928 m_params
->markParams(canInvokeFewArgs());
932 TypePtr atype
= getActualType();
933 if (m_safe
> 0 && !m_safeDef
) {
935 } else if (!m_safeDef
) {
936 atype
= Type::Variant
;
938 TypePtr t
= m_safeDef
->getActualType();
939 if (!t
|| !atype
|| !Type::SameType(t
, atype
)) {
940 atype
= Type::Variant
;
943 rtype
= checkTypesImpl(ar
, type
, atype
, coerce
);
944 m_voidReturn
= m_voidWrapper
= false;
947 CHECK_HOOK(afterSimpleFunctionCallCheck
);
952 ///////////////////////////////////////////////////////////////////////////////
953 // code generation functions
955 void SimpleFunctionCall::outputPHP(CodeGenerator
&cg
, AnalysisResultPtr ar
) {
956 outputLineMap(cg
, ar
);
958 if (m_class
|| !m_className
.empty()) {
959 StaticClassName::outputPHP(cg
, ar
);
960 cg_printf("::%s(", m_name
.c_str());
963 if (cg
.getOutput() == CodeGenerator::InlinedPHP
||
964 cg
.getOutput() == CodeGenerator::TrimmedPHP
) {
966 if (cg
.getOutput() == CodeGenerator::TrimmedPHP
&&
967 cg
.usingStream(CodeGenerator::PrimaryStream
) &&
968 Option::DynamicFunctionCalls
.find(m_name
) !=
969 Option::DynamicFunctionCalls
.end()) {
970 int funcNamePos
= Option::DynamicFunctionCalls
[m_name
];
971 if (m_params
&& m_params
->getCount() &&
972 m_params
->getCount() >= funcNamePos
+ 1) {
973 if (funcNamePos
== -1) funcNamePos
= m_params
->getCount() - 1;
974 ExpressionPtr funcName
= (*m_params
)[funcNamePos
];
975 if (!funcName
->is(Expression::KindOfScalarExpression
)) {
977 cg_printf("%s(", m_name
.c_str());
978 for (int i
= 0; i
< m_params
->getCount(); i
++) {
979 if (i
> 0) cg_printf(", ");
980 if (i
== funcNamePos
) {
981 cg_printf("%sdynamic_load(", Option::IdPrefix
.c_str());
982 funcName
->outputPHP(cg
, ar
);
985 ExpressionPtr param
= (*m_params
)[i
];
986 if (param
) param
->outputPHP(cg
, ar
);
994 cg_printf("%s(", m_name
.c_str());
996 cg_printf("%s(", m_name
.c_str());
1000 if (m_params
) m_params
->outputPHP(cg
, ar
);
1004 bool SimpleFunctionCall::preOutputCPP(CodeGenerator
&cg
, AnalysisResultPtr ar
,
1006 if (m_validClass
&& m_classScope
&& m_classScope
->isRedeclaring() &&
1007 (!m_funcScope
|| !m_funcScope
->isStatic())) {
1009 In this case, even though its redeclaring, we were able
1010 to figure out the actual ClassScope for cls. But for a non-static
1011 call in a method of class D derived from cls, we cant
1012 use the "cls$$n::func()" syntax, because, cls$$n wont actually
1013 be a base class of D (D will derive from DynamicObjectData).
1015 We should improve class derivation so that if D knows which cls
1016 its derived from, it should use it.
1018 And we should improve code gen here, so that we can use
1019 p_cls$$n(parent)->cls$$n::func(), rather than resorting to
1020 fully dynamic dispatch.
1023 m_redeclaredClass
= true;
1024 m_validClass
= false;
1026 if (m_valid
&& (!m_arrayParams
||
1027 (!m_class
&& m_className
.empty() && m_funcScope
->isUserFunction())))
1028 return FunctionCall::preOutputCPP(cg
, ar
, state
);
1029 // Short circuit out if inExpression() returns false
1030 if (!ar
->inExpression()) return true;
1031 m_ciTemp
= cg
.createNewId(ar
);
1032 bool needHash
= true;
1033 string
escapedName(cg
.escapeLabel(m_name
));
1034 string escapedClass
;
1036 if (!m_className
.empty()) {
1037 cls
= ar
->findClass(m_className
);
1038 if (!m_safe
&& cls
&& !ar
->checkClassPresent(m_origClassName
) &&
1039 cls
->isVolatile()) {
1040 ClassScope::OutputVolatileCheck(cg
, ar
, cls
->getOriginalName(), false);
1043 escapedClass
= cg
.escapeLabel(m_className
);
1045 ar
->wrapExpressionBegin(cg
);
1046 cg_printf("const CallInfo *cit%d = NULL;\n", m_ciTemp
);
1047 if (!m_class
&& m_className
.empty()) {
1048 cg_printf("void *vt%d = NULL;\n", m_ciTemp
);
1050 cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp
);
1052 bool safeCheck
= false;
1054 if (!m_className
.empty()) {
1055 if ((!cls
|| cls
->isVolatile()) &&
1056 !ar
->checkClassPresent(m_origClassName
)) {
1057 cg_indentBegin("if (");
1058 ClassScope::OutputVolatileCheck(cg
, ar
, m_className
, true);
1061 } else if (!m_funcScope
|| m_funcScope
->isVolatile()) {
1062 cg_indentBegin("if (");
1063 cg_printf("%s->FVF(%s)",
1065 cg
.formatLabel(m_name
).c_str());
1072 if (!m_class
&& m_className
.empty()) {
1073 if (m_redeclared
&& !m_dynamicInvoke
) {
1075 cg_printf("cit%d = %s->%s%s;\n", m_ciTemp
, cg
.getGlobals(ar
),
1076 Option::CallInfoPrefix
, cg
.formatLabel(m_name
).c_str());
1078 // If m_safe, check cit later, if null then yield null or safeDef
1079 cg_printf("if (!cit%d) invoke_failed(\"%s\", null_array, -1);\n",
1080 m_ciTemp
, escapedName
.c_str());
1083 cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp
, m_ciTemp
);
1084 cg_printString(m_name
, ar
);
1089 const MethodSlot
*ms
= NULL
;
1090 if (!m_name
.empty()) {
1091 ms
= ar
->getOrAddMethodSlot(m_name
);
1094 cg_printf("mcp%d.noFatal();\n", m_ciTemp
);
1096 ClassScopePtr cscope
= ar
->getClassScope();
1097 // The call is happening in an instance method
1098 bool inObj
= cscope
&& !ar
->getFunctionScope()->isStatic();
1099 // The call was like parent::
1100 bool parentCall
= m_parentClass
&& inObj
;
1103 if (m_redeclaredClass
) {
1104 className
= cg
.formatLabel(m_classScope
->getName());
1106 className
= m_classScope
->getId(cg
);
1109 className
= cg
.formatLabel(m_className
);
1110 if (!m_className
.empty() && m_cppTemp
.empty() &&
1111 m_origClassName
!= "self" && m_origClassName
!= "parent" &&
1112 m_origClassName
!= "static") {
1113 // Create a temporary to hold the class name, in case it is not a
1115 m_clsNameTemp
= cg
.createNewId(ar
);
1116 cg_printf("CStrRef clsName%d(", m_clsNameTemp
);
1117 cg_printString(m_origClassName
, ar
);
1121 if (cscope
&& cscope
->derivesFromRedeclaring()) {
1122 // In a derived from redeclaring class
1123 cg_printf("mcp%d.dynamicNamedCall%s(\"%s\", \"%s\"", m_ciTemp
,
1124 ms
? "WithIndex" : "", escapedClass
.c_str(), escapedName
.c_str());
1125 } else if (m_redeclaredClass
) {
1127 cg_printf("mcp%d.methodCallEx(this, \"%s\");\n", m_ciTemp
,
1128 escapedName
.c_str());
1129 cg_printf("parent->%sget_call_info%s(mcp%d", Option::ClassPrefix
,
1130 ms
? "_with_index" : "", m_ciTemp
);
1132 cg_printf("mcp%d.staticMethodCall(\"%s\", \"%s\");\n", m_ciTemp
,
1133 escapedClass
.c_str(), escapedName
.c_str());
1134 cg_printf("%s->%s%s->%sget_call_info%s(mcp%d",
1135 cg
.getGlobals(ar
), Option::ClassStaticsObjectPrefix
,
1136 className
.c_str(), Option::ObjectStaticPrefix
,
1137 ms
? "with_index" : "", m_ciTemp
);
1139 } else if (m_validClass
) {
1140 // In an object, calling a superclass's method
1141 bool exCall
= inObj
&& ar
->getClassScope()->derivesFrom(ar
, m_className
,
1144 cg_printf("mcp%d.methodCallEx(this, \"%s\");\n", m_ciTemp
,
1145 escapedName
.c_str());
1146 cg_printf("%s%s::%sget_call_info%s(mcp%d",
1147 Option::ClassPrefix
, className
.c_str(), Option::ObjectPrefix
,
1148 ms
? "_with_index" : "", m_ciTemp
);
1150 cg_printf("mcp%d.staticMethodCall(\"%s\", \"%s\");\n", m_ciTemp
,
1151 escapedClass
.c_str(), escapedName
.c_str());
1152 cg_printf("%s%s::%sget_call_info%s(mcp%d",
1153 Option::ClassPrefix
, className
.c_str(), Option::ObjectStaticPrefix
,
1154 ms
? "_with_index" : "", m_ciTemp
);
1160 if (m_class
->is(KindOfScalarExpression
)) {
1161 cg_printf("mcp%d.staticMethodCall(", m_ciTemp
);
1162 ASSERT(strcasecmp(dynamic_pointer_cast
<ScalarExpression
>(m_class
)->
1163 getString().c_str(), "static") == 0);
1164 cg_printf("\"static\"");
1167 cg_printf("mcp%d.dynamicNamedCall%s(", m_ciTemp
,
1168 ms
? "_with_index" : "");
1169 m_class
->outputCPP(cg
, ar
);
1171 cg_printf(", \"%s\");\n", escapedName
.c_str());
1172 if (lsb
) cg_printf("mcp%d.lateStaticBind(info);\n", m_ciTemp
);
1174 // Nonexistent method
1175 cg_printf("mcp%d.dynamicNamedCall%s(\"%s\", \"%s\"", m_ciTemp
,
1176 ms
? "_with_index" : "", escapedClass
.c_str(),
1177 escapedName
.c_str());
1181 cg_printf(", %s", ms
->runObjParam().c_str());
1185 cg_printf(", 0x%016llXLL);\n", hash_string_i(m_name
.data(), m_name
.size()));
1187 if (m_class
|| !m_className
.empty()) {
1188 cg_printf("cit%d = mcp%d.ci;\n", m_ciTemp
, m_ciTemp
);
1191 cg_indentEnd("}\n");
1195 if (m_class
) m_class
->preOutputCPP(cg
, ar
, state
);
1196 if (m_safeDef
) m_safeDef
->preOutputCPP(cg
, ar
, state
);
1198 if (m_params
&& m_params
->getCount() > 0) {
1199 ar
->pushCallInfo(m_ciTemp
);
1200 m_params
->preOutputCPP(cg
, ar
, state
);
1206 void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator
&cg
,
1207 AnalysisResultPtr ar
) {
1208 if (!m_class
&& m_className
.empty()) {
1210 case ExtractFunction
:
1211 cg_printf("extract(variables, ");
1212 FunctionScope::outputCPPArguments(m_params
, cg
, ar
, 0, false);
1215 case CompactFunction
:
1216 cg_printf("compact(variables, ");
1217 FunctionScope::outputCPPArguments(m_params
, cg
, ar
, -1, true);
1224 bool volatileCheck
= false;
1225 ClassScopePtr cls
= m_classScope
;
1229 if (!m_className
.empty()) {
1230 if ((!cls
|| cls
->isVolatile()) &&
1231 !ar
->checkClassPresent(m_origClassName
)) {
1234 ClassScope::OutputVolatileCheck(cg
, ar
, m_className
, true);
1236 volatileCheck
= true;
1238 } else if (!m_funcScope
|| m_funcScope
->isVolatile()) {
1240 cg_printf("(%s->FVF(%s)",
1242 cg
.formatLabel(m_name
).c_str());
1244 volatileCheck
= true;
1247 if (volatileCheck
) {
1249 // ci will be null if fn/cl not defined. Set in preoutput
1250 cg_printf("(cit%d", m_ciTemp
);
1259 if (m_safe
> 0 && !m_safeDef
) {
1260 cg_printf("Array(ArrayInit(2, true).set(0, true).set(1, ");
1262 Array
ret(Array::Create(0, false));
1263 ret
.set(1, Variant());
1264 } else if (m_funcScope
&& m_funcScope
->getReturnType() &&
1265 !Type::SameType(m_funcScope
->getReturnType(),
1267 safeCast
= getActualType();
1268 safeCast
->outputCPPCast(cg
, ar
);
1271 if (m_funcScope
&& !m_funcScope
->getReturnType()) {
1275 } else if (!m_className
.empty()) {
1276 if ((!cls
|| cls
->isVolatile()) &&
1277 !ar
->checkClassPresent(m_origClassName
)) {
1278 volatileCheck
= true;
1279 ClassScope::OutputVolatileCheckBegin(cg
, ar
, m_origClassName
);
1284 if (m_valid
&& !m_arrayParams
) {
1285 if (!m_className
.empty()) {
1287 cg_printf("%s%s::", Option::ClassPrefix
, cls
->getId(cg
).c_str());
1288 std::string name
= m_name
== "__construct" ?
1289 cls
->findConstructor(ar
, true)->getName() :
1290 cg
.formatLabel(m_name
);
1292 cg_printf("%s%s(", Option::MethodPrefix
, name
.c_str());
1294 int paramCount
= m_params
? m_params
->getCount() : 0;
1295 if (m_name
== "get_class" && ar
->getClassScope() && paramCount
== 0) {
1297 cg_printString(ar
->getClassScope()->getOriginalName(), ar
);
1298 } else if (m_name
== "get_parent_class" && ar
->getClassScope() &&
1300 const std::string parentClass
= ar
->getClassScope()->getParent();
1302 if (!parentClass
.empty()) {
1303 cg_printString(ar
->getClassScope()->getParent(), ar
);
1309 cg_printf("%s(", cg
.formatLabel(m_name
).c_str());
1312 m_builtinFunction
? Option::BuiltinFunctionPrefix
:
1313 Option::FunctionPrefix
, m_funcScope
->getId(cg
).c_str());
1317 FunctionScope::outputCPPArguments(m_params
, cg
, ar
, m_extraArg
,
1318 m_variableArgument
, m_argArrayId
,
1319 m_argArrayHash
, m_argArrayIndex
);
1321 if (!m_class
&& m_className
.empty()) {
1322 if (m_valid
&& m_funcScope
->isUserFunction()) {
1323 cg_printf("HPHP::%s%s(NULL, ", Option::InvokePrefix
,
1324 m_funcScope
->getId(cg
).c_str());
1325 } else if (canInvokeFewArgs() && !m_arrayParams
) {
1326 cg_printf("(cit%d->getFuncFewArgs())(vt%d, ", m_ciTemp
, m_ciTemp
);
1328 cg_printf("(cit%d->getFunc())(vt%d, ", m_ciTemp
, m_ciTemp
);
1331 if (canInvokeFewArgs() && !m_arrayParams
) {
1332 cg_printf("(cit%d->getMethFewArgs())(mcp%d, ", m_ciTemp
, m_ciTemp
);
1334 cg_printf("(cit%d->getMeth())(mcp%d, ", m_ciTemp
, m_ciTemp
);
1337 if (canInvokeFewArgs() && !m_arrayParams
) {
1338 int left
= Option::InvokeFewArgsCount
;
1339 if (m_params
&& m_params
->getCount()) {
1340 left
-= m_params
->getCount();
1341 cg_printf("%d, ", m_params
->getCount());
1342 ar
->pushCallInfo(m_ciTemp
);
1343 FunctionScope::outputCPPArguments(m_params
, cg
, ar
, 0, false);
1348 for (int i
= 0; i
< left
; i
++) {
1349 cg_printf(", null");
1352 if ((!m_params
) || (m_params
->getCount() == 0)) {
1353 cg_printf("Array()");
1355 ar
->pushCallInfo(m_ciTemp
);
1356 FunctionScope::outputCPPArguments(m_params
, cg
, ar
,
1357 m_arrayParams
? 0 : -1, false);
1367 if (m_funcScope
&& !m_funcScope
->getReturnType()) {
1368 cg_printf(", null)");
1373 if (m_safe
> 0 && !m_safeDef
) {
1374 cg_printf(").create())");
1378 if (volatileCheck
) {
1381 cg_printf(", false)");
1385 if (m_safeDef
&& m_safeDef
->getActualType() &&
1386 !Type::SameType(m_safeDef
->getActualType(), getActualType())) {
1387 safeCast
= getActualType();
1388 safeCast
->outputCPPCast(cg
, ar
);
1395 m_safeDef
->outputCPP(cg
, ar
);
1400 Array
ret(Array::Create(0, false));
1401 ret
.set(1, Variant());
1402 ExpressionPtr t
= Expression::MakeScalarExpression(
1403 ar
, this->getLocation(), ret
);
1404 t
->outputCPP(cg
, ar
);
1413 ClassScope::OutputVolatileCheckEnd(cg
);
1418 void SimpleFunctionCall::outputCPPImpl(CodeGenerator
&cg
,
1419 AnalysisResultPtr ar
) {
1420 if (!m_lambda
.empty()) {
1421 cg_printf("\"%s\"", m_lambda
.c_str());
1425 if (!m_class
&& m_className
.empty()) {
1426 if (m_type
== DefineFunction
&& m_params
&& m_params
->getCount() >= 2) {
1427 ScalarExpressionPtr name
=
1428 dynamic_pointer_cast
<ScalarExpression
>((*m_params
)[0]);
1430 ExpressionPtr value
= (*m_params
)[1];
1432 varName
= name
->getIdentifier();
1433 if (varName
.empty()) {
1434 cg_printf("throw_fatal(\"bad define\")");
1435 } else if (m_dynamicConstant
) {
1436 cg_printf("g->declareConstant(\"%s\", g->%s%s, ",
1437 varName
.c_str(), Option::ConstantPrefix
,
1439 value
->outputCPP(cg
, ar
);
1442 bool needAssignment
= true;
1443 bool isSystem
= ar
->getConstants()->isSystem(varName
);
1445 ((!ar
->isConstantRedeclared(varName
)) && value
->isScalar())) {
1446 needAssignment
= false;
1448 if (needAssignment
) {
1449 cg_printf("%s%s = ", Option::ConstantPrefix
, varName
.c_str());
1450 value
->outputCPP(cg
, ar
);
1455 if (value
->hasEffect()) {
1457 value
->outputCPP(cg
, ar
);
1461 cg_printf("throw_fatal(\"bad define\")");
1462 if (close
) cg_printf(")");
1466 if (m_name
== "func_num_args") {
1467 cg_printf("num_args");
1472 case VariableArgumentFunction
:
1474 FunctionScopePtr func
=
1475 dynamic_pointer_cast
<FunctionScope
>(ar
->getScope());
1477 cg_printf("%s(", m_name
.c_str());
1478 func
->outputCPPParamsCall(cg
, ar
, true);
1481 m_params
->outputCPP(cg
, ar
);
1488 case FunctionExistsFunction
:
1489 case ClassExistsFunction
:
1490 case InterfaceExistsFunction
:
1492 bool literalString
= false;
1494 if (m_params
&& m_params
->getCount() == 1) {
1495 ExpressionPtr value
= (*m_params
)[0];
1496 if (value
->isScalar()) {
1497 ScalarExpressionPtr name
=
1498 dynamic_pointer_cast
<ScalarExpression
>(value
);
1499 if (name
&& name
->isLiteralString()) {
1500 literalString
= true;
1501 symbol
= name
->getLiteralString();
1505 if (literalString
) {
1506 const std::string
&lname
= Util::toLower(symbol
);
1508 case FunctionExistsFunction
:
1510 bool dynInvoke
= Option::DynamicInvokeFunctions
.find(lname
) !=
1511 Option::DynamicInvokeFunctions
.end();
1513 FunctionScopePtr func
= ar
->findFunction(lname
);
1515 if (func
->isVolatile()) {
1516 cg_printf("%s->FVF(%s)",
1518 cg
.formatLabel(lname
).c_str());
1528 cg_printf("f_function_exists(");
1529 cg_printString(symbol
, ar
);
1533 case ClassExistsFunction
:
1534 case InterfaceExistsFunction
:
1536 ClassScopePtrVec classes
= ar
->findClasses(Util::toLower(symbol
));
1538 bool foundOther
= false;
1539 for (ClassScopePtrVec::const_iterator it
= classes
.begin();
1540 it
!= classes
.end(); ++it
) {
1541 ClassScopePtr cls
= *it
;
1542 if (cls
->isInterface() == (m_type
== InterfaceExistsFunction
)) {
1543 found
+= cls
->isVolatile() ? 2 : 1;
1551 } else if (!foundOther
) {
1555 if (m_type
== ClassExistsFunction
) {
1556 cg_printf("checkClassExists(");
1558 cg_printf("checkInterfaceExists(");
1560 cg_printString(symbol
, ar
);
1561 cg_printf(", &%s->CDEC(%s), %s->FVF(__autoload), true)",
1563 cg
.formatLabel(lname
).c_str(),
1567 if (m_type
== ClassExistsFunction
) {
1568 cg_printf("f_class_exists(");
1570 cg_printf("f_interface_exists(");
1572 cg_printString(symbol
, ar
);
1584 case GetDefinedVarsFunction
:
1585 cg_printf("get_defined_vars(variables)");
1592 outputCPPParamOrderControlled(cg
, ar
);
1595 bool SimpleFunctionCall::canInvokeFewArgs() {
1596 // We can always change out minds about saying yes, but once we say
1598 if (m_dynamicInvoke
||
1599 (m_invokeFewArgsDecision
&& m_params
&&
1600 m_params
->getCount() > Option::InvokeFewArgsCount
)) {
1601 m_invokeFewArgsDecision
= false;
1603 return m_invokeFewArgsDecision
;
1606 SimpleFunctionCallPtr
SimpleFunctionCall::getFunctionCallForCallUserFunc(
1607 AnalysisResultPtr ar
, SimpleFunctionCallPtr call
, bool testOnly
,
1608 int firstParam
, bool &error
) {
1610 ExpressionListPtr params
= call
->getParams();
1611 if (params
&& params
->getCount() >= firstParam
) {
1612 ExpressionPtr p0
= (*params
)[0];
1614 if (p0
->isScalar() && p0
->getScalarValue(v
)) {
1616 Variant t
= StringUtil::Explode(v
.toString(), "::", 3);
1617 if (!t
.isArray() || t
.toArray().size() != 2) {
1618 std::string name
= Util::toLower(v
.toString().data());
1619 FunctionScopePtr func
= ar
->findFunction(name
);
1620 if (!func
|| func
->isDynamicInvoke()) {
1622 return SimpleFunctionCallPtr();
1624 if (func
->isUserFunction()) func
->setVolatile();
1625 ExpressionListPtr p2
;
1627 p2
= ExpressionListPtr(
1628 new ExpressionList(call
->getLocation(),
1629 Expression::KindOfExpressionList
));
1630 p2
->addElement(Expression::MakeScalarExpression(
1631 ar
, call
->getLocation(), v
));
1632 name
= "function_exists";
1634 p2
= static_pointer_cast
<ExpressionList
>(params
->clone());
1635 while (firstParam
--) {
1636 p2
->removeElement(0);
1639 SimpleFunctionCallPtr
rep(
1640 new SimpleFunctionCall(call
->getLocation(),
1641 Expression::KindOfSimpleFunctionCall
,
1642 name
, p2
, ExpressionPtr()));
1648 Array arr
= v
.toArray();
1649 if (arr
.size() != 2 || !arr
.exists(0) || !arr
.exists(1)) {
1651 return SimpleFunctionCallPtr();
1653 Variant classname
= arr
.rvalAt(0LL);
1654 Variant methodname
= arr
.rvalAt(1LL);
1655 if (!methodname
.isString()) {
1657 return SimpleFunctionCallPtr();
1659 if (!classname
.isString()) {
1660 return SimpleFunctionCallPtr();
1662 std::string sclass
= classname
.toString().data();
1663 std::string smethod
= Util::toLower(methodname
.toString().data());
1665 ClassScopePtr cls
= ar
->findClass(sclass
);
1668 return SimpleFunctionCallPtr();
1670 if (cls
->isRedeclaring()) {
1671 cls
= ar
->findExactClass(sclass
);
1672 } else if (!cls
->isVolatile() && cls
->isUserClass() &&
1673 !ar
->checkClassPresent(sclass
)) {
1677 return SimpleFunctionCallPtr();
1680 size_t c
= smethod
.find("::");
1681 if (c
!= 0 && c
!= string::npos
&& c
+2 < smethod
.size()) {
1682 string name
= smethod
.substr(0, c
);
1683 if (cls
->getName() != name
) {
1684 if (!cls
->derivesFrom(ar
, name
, true, false)) {
1685 error
= cls
->derivesFromRedeclaring() == ClassScope::FromNormal
;
1686 return SimpleFunctionCallPtr();
1689 smethod
= smethod
.substr(c
+2);
1692 FunctionScopePtr func
= cls
->findFunction(ar
, smethod
, true);
1695 return SimpleFunctionCallPtr();
1697 if (func
->isPrivate() ?
1698 (cls
!= ar
->getClassScope() ||
1699 !cls
->findFunction(ar
, smethod
, false)) :
1700 (func
->isProtected() &&
1701 (!ar
->getClassScope() ||
1702 !ar
->getClassScope()->derivesFrom(ar
, sclass
, true, false)))) {
1704 return SimpleFunctionCallPtr();
1707 Expression::MakeScalarExpression(ar
, call
->getLocation(), classname
));
1708 ExpressionListPtr p2
;
1710 p2
= ExpressionListPtr(
1711 new ExpressionList(call
->getLocation(),
1712 Expression::KindOfExpressionList
));
1715 smethod
= "class_exists";
1717 p2
= static_pointer_cast
<ExpressionList
>(params
->clone());
1718 while (firstParam
--) {
1719 p2
->removeElement(0);
1722 SimpleFunctionCallPtr
rep(
1723 new SimpleFunctionCall(call
->getLocation(),
1724 Expression::KindOfSimpleFunctionCall
,
1730 return SimpleFunctionCallPtr();
1735 ExpressionPtr
hphp_opt_call_user_func(CodeGenerator
*cg
,
1736 AnalysisResultPtr ar
,
1737 SimpleFunctionCallPtr call
, int mode
) {
1739 if (!cg
&& !mode
&& !ar
->isSystem()) {
1740 const std::string
&name
= call
->getName();
1741 bool isArray
= name
== "call_user_func_array";
1742 if (name
== "call_user_func" || isArray
) {
1743 SimpleFunctionCallPtr
rep(
1744 SimpleFunctionCall::getFunctionCallForCallUserFunc(ar
, call
, false,
1749 rep
->setSafeCall(-1);
1750 rep
->addLateDependencies(ar
);
1751 if (isArray
) rep
->setArrayParams();
1756 return ExpressionPtr();
1759 ExpressionPtr
hphp_opt_fb_call_user_func(CodeGenerator
*cg
,
1760 AnalysisResultPtr ar
,
1761 SimpleFunctionCallPtr call
, int mode
) {
1763 if (!cg
&& !mode
&& !ar
->isSystem()) {
1764 const std::string
&name
= call
->getName();
1765 bool isArray
= name
== "fb_call_user_func_array_safe";
1766 bool safe_ret
= name
== "fb_call_user_func_safe_return";
1767 if (isArray
|| safe_ret
|| name
== "fb_call_user_func_safe") {
1768 SimpleFunctionCallPtr
rep(
1769 SimpleFunctionCall::getFunctionCallForCallUserFunc(
1770 ar
, call
, false, safe_ret
? 2 : 1, error
));
1773 return (*call
->getParams())[1];
1775 Array
ret(Array::Create(0, false));
1776 ret
.set(1, Variant());
1777 return Expression::MakeScalarExpression(ar
, call
->getLocation(), ret
);
1781 if (isArray
) rep
->setArrayParams();
1782 rep
->addLateDependencies(ar
);
1783 rep
->setSafeCall(1);
1785 ExpressionPtr def
= (*call
->getParams())[1];
1786 rep
->setSafeDefault(def
);
1792 return ExpressionPtr();
1795 ExpressionPtr
hphp_opt_is_callable(CodeGenerator
*cg
,
1796 AnalysisResultPtr ar
,
1797 SimpleFunctionCallPtr call
, int mode
) {
1798 if (!cg
&& !mode
&& !ar
->isSystem()) {
1800 SimpleFunctionCallPtr
rep(
1801 SimpleFunctionCall::getFunctionCallForCallUserFunc(
1802 ar
, call
, true, 1, error
));
1804 return Expression::MakeConstant(ar
, call
->getLocation(), "false");
1809 return ExpressionPtr();