[Perf] Optimize injection
[hiphop-php.git] / src / compiler / expression / simple_function_call.cpp
blob22adb9a97c128c9cd50a75299a737261230ba785
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 <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>
46 using namespace HPHP;
47 using namespace std;
48 using namespace boost;
50 ///////////////////////////////////////////////////////////////////////////////
51 // statics
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),
104 m_hookData(NULL) {
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() {
120 if (m_hookData) {
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);
130 exp->m_hookData = 0;
131 return exp;
134 ///////////////////////////////////////////////////////////////////////////////
135 // parser functions
137 void SimpleFunctionCall::onParse(AnalysisResultPtr ar) {
138 if (m_class) return;
140 FileScopePtr fs = ar->getFileScope();
141 ConstructPtr self = shared_from_this();
142 if (m_className.empty()) {
143 CodeErrorPtr codeError = ar->getCodeError();
144 switch (m_type) {
145 case CreateFunction:
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 + ") "
155 "{" + body + "}";
156 ar->appendExtraCode(code);
158 break;
159 case VariableArgumentFunction:
161 Note:
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);
170 break;
171 case ExtractFunction:
172 ar->getCodeError()->record(self, CodeError::UseExtract, self);
173 ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable);
174 ar->getFileScope()->setAttribute(FileScope::ContainsExtract);
175 break;
176 case CompactFunction:
177 ar->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable);
178 ar->getFileScope()->setAttribute(FileScope::ContainsCompact);
179 break;
180 case ShellExecFunction:
181 ar->getCodeError()->record(self, CodeError::UseShellExec, self);
182 break;
183 case GetDefinedVarsFunction:
184 ar->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable);
185 ar->getFileScope()->setAttribute(FileScope::ContainsGetDefinedVars);
186 ar->getFileScope()->setAttribute(FileScope::ContainsCompact);
187 break;
188 default:
189 CHECK_HOOK(onSimpleFunctionCallFuncType);
190 break;
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) {
207 if (!m_class) {
208 if (m_className.empty()) {
209 addUserFunction(ar, m_name);
210 } else if (m_origClassName != "parent") {
211 addUserClass(ar, m_className);
212 } else {
213 m_parentClass = true;
218 void SimpleFunctionCall::addLateDependencies(AnalysisResultPtr ar) {
219 AnalysisResult::Phase phase = ar->getPhase();
220 ar->setPhase(AnalysisResult::AnalyzeAll);
221 addDependencies(ar);
222 ar->setPhase(phase);
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) {
231 if (!n) {
232 m_safeDef = boost::dynamic_pointer_cast<Expression>(cp);
233 } else {
234 FunctionCall::setNthKid(n, cp);
238 void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
239 if (m_class) {
240 m_class->analyzeProgram(ar);
241 setDynamicByIdentifier(ar, m_name);
242 } else {
243 addDependencies(ar);
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);
271 if (name) {
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;
300 ClassScopePtr cls;
301 if (!m_class && m_className.empty()) {
302 func = ar->findFunction(m_name);
303 } else {
304 cls = ar->resolveClass(m_className);
305 if (cls && cls->isRedeclaring()) {
306 cls = ar->findExactClass(m_className);
308 if (cls) {
309 m_classScope = cls;
310 if (m_name == "__construct") {
311 func = cls->findConstructor(ar, true);
312 } else {
313 func = cls->findFunction(ar, m_name, true, true);
317 if (func && !func->isRedeclaring()) {
318 if (m_funcScope != func) {
319 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();
337 switch (m_type) {
338 case DefinedFunction: {
339 ConstantTablePtr constants = ar->getConstants();
340 if (!constants->isPresent(symbol)) {
341 // user constant
342 BlockScopePtr block = ar->findConstantDeclarer(symbol);
343 if (block) { // found the constant
344 constants = block->getConstants();
345 // set to be dynamic
346 constants->setDynamic(ar, symbol);
349 break;
351 case FunctionExistsFunction: {
352 FunctionScopePtr func = ar->findFunction(Util::toLower(symbol));
353 if (func && func->isUserFunction()) {
354 func->setVolatile();
356 break;
358 case InterfaceExistsFunction:
359 case ClassExistsFunction: {
360 ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
361 if (cls && cls->isUserClass()) {
362 cls->setVolatile();
364 break;
366 default:
367 ASSERT(false);
374 if (m_params) {
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();
393 } else {
394 return false;
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);
418 sepm[name] = rep;
419 exp = rep;
422 break;
423 case Expression::KindOfSimpleFunctionCall:
425 static_pointer_cast<SimpleFunctionCall>(exp)->addLateDependencies(ar);
427 default:
428 break;
430 return exp;
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),
449 prefix, sepm, ar)) {
450 return ret;
453 return 0;
455 case Statement::KindOfExpStatement:
456 elist->addElement(cloneForInline(dynamic_pointer_cast<ExpStatement>(s)->
457 getExpression(), prefix, sepm, ar));
458 return 0;
459 case Statement::KindOfReturnStatement:
461 ExpressionPtr exp =
462 dynamic_pointer_cast<ReturnStatement>(s)->getRetExp();
464 if (exp) {
465 elist->addElement(cloneForInline(exp, prefix, sepm, ar));
466 return 1;
468 return -1;
470 default:
471 assert(false);
473 return 1;
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()) {
481 Array arr;
482 if (m_params) {
483 if (!m_params->isScalar()) return ExpressionPtr();
484 for (int i = 0, n = m_params->getCount(); i < n; ++i) {
485 Variant v;
486 if (!(*m_params)[i]->getScalarValue(v)) return ExpressionPtr();
487 arr.set(i, v);
490 try {
491 g_context->setThrowAllErrors(true);
492 Variant v = invoke_builtin(m_funcScope->getName().c_str(),
493 arr, -1, true);
494 g_context->setThrowAllErrors(false);
495 return MakeScalarExpression(ar, getLocation(), v);
496 } catch (...) {
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);
505 if (e) return e;
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();
536 MethodStatementPtr m
537 (dynamic_pointer_cast<MethodStatement>(m_funcScope->getStmt()));
538 ExpressionListPtr plist = m->getParams();
540 int i;
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,
555 var, arg, ref));
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"));
566 if (sepm.size()) {
567 ExpressionListPtr unset_list
568 (new ExpressionList(this->getLocation(), KindOfExpressionList));
570 for (StringToExpressionPtrMap::iterator it = sepm.begin(), end = sepm.end();
571 it != end; ++it) {
572 ExpressionPtr var = it->second->clone();
573 var->clearContext((Context)(unsigned)-1);
574 unset_list->addElement(var);
577 ExpressionPtr unset(
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);
584 } else {
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()));
595 return elist;
598 ExpressionPtr SimpleFunctionCall::preOptimize(AnalysisResultPtr ar) {
599 if (m_class) {
600 ar->preOptimize(m_class);
601 updateClassName();
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);
610 return tmp;
614 if (ar->getPhase() != AnalysisResult::SecondPreOptimize) {
615 return ExpressionPtr();
617 if (ExpressionPtr rep = optimize(ar)) {
618 return rep;
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();
633 switch (m_type) {
634 case DefinedFunction: {
635 ConstantTablePtr constants = ar->getConstants();
636 // system constant
637 if (constants->isPresent(symbol) && !constants->isDynamic(symbol)) {
638 return CONSTANT("true");
640 // user constant
641 BlockScopePtr block = ar->findConstantDeclarer(symbol);
642 // not found (i.e., undefined)
643 if (!block) {
644 if (symbol.find("::") == std::string::npos) {
645 return CONSTANT("false");
646 } else {
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");
658 } else {
659 return ExpressionPtr();
661 break;
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);
668 if (!func) {
669 return CONSTANT("false");
671 if (func->isUserFunction()) {
672 func->setVolatile();
674 if (!func->isVolatile()) {
675 return CONSTANT("true");
678 break;
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");
695 break;
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()) {
705 classFound = true;
708 if (!classFound) return CONSTANT("false");
709 if (classes.size() == 1 && !classes.back()->isVolatile()) {
710 return CONSTANT("true");
712 break;
714 default:
715 ASSERT(false);
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)) {
733 return rep;
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()) {
744 return 0;
747 return UnknownEffect;
750 TypePtr SimpleFunctionCall::inferTypes(AnalysisResultPtr ar, TypePtr type,
751 bool coerce) {
752 ASSERT(false);
753 return TypePtr();
756 TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
757 bool coerce) {
758 reset();
760 if (m_class) {
761 m_class->inferAndCheck(ar, Type::Any, false);
764 if (m_safeDef) {
765 m_safeDef->inferAndCheck(ar, Type::Any, false);
768 if (m_safe) {
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]);
780 string varName;
781 if (name) {
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);
790 if (!block) {
791 ar->getFileScope()->declareConstant(ar, varName);
792 block = ar->findConstantDeclarer(varName);
793 ASSERT(block);
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);
805 } else {
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);
831 } else {
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);
839 if (!cls) {
840 m_redeclaredClass = true;
844 if (!cls) {
845 if (ar->isFirstPass()) {
846 ar->getCodeError()->record(self, CodeError::UnknownClass, self);
848 if (m_params) {
849 m_params->inferAndCheck(ar, Type::Some, false);
850 m_params->markParams(canInvokeFewArgs());
852 return checkTypesImpl(ar, type, Type::Variant, coerce);
854 m_classScope = cls;
855 m_derivedFromRedeclaring = cls->derivesFromRedeclaring();
856 m_validClass = true;
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);
861 } else {
862 func = cls->findFunction(ar, m_name, true, true);
865 if (func && !func->isStatic()) {
866 ClassScopePtr clsThis = ar->getClassScope();
867 FunctionScopePtr funcThis = ar->getFunctionScope();
868 if (!clsThis ||
869 (clsThis->getName() != m_className &&
870 !clsThis->derivesFrom(ar, m_className, true, false)) ||
871 funcThis->isStatic()) {
872 func->setDynamic();
873 if (ar->isFirstPass()) {
874 ar->getCodeError()->record(self, CodeError::MissingObjectContext,
875 self);
876 errorFlagged = true;
878 func.reset();
882 if (!func || func->isRedeclaring()) {
883 if (m_funcScope) {
884 m_funcScope.reset();
885 Construct::recomputeEffects();
887 if (func) {
888 m_redeclared = true;
889 ar->getScope()->getVariables()->
890 setAttribute(VariableTable::NeedGlobalPointer);
892 if (!func && !errorFlagged && ar->isFirstPass()) {
893 ar->getCodeError()->record(self, CodeError::UnknownFunction, self);
895 if (m_params) {
896 if (func) {
897 FunctionScope::RefParamInfoPtr info =
898 FunctionScope::GetRefParamInfo(m_name);
899 ASSERT(info);
900 for (int i = m_params->getCount(); i--; ) {
901 if (info->isRefParam(i)) {
902 m_params->markParam(i, canInvokeFewArgs());
906 if (m_arrayParams) {
907 (*m_params)[0]->inferAndCheck(ar, Type::Array, false);
908 } else {
909 m_params->inferAndCheck(ar, Type::Some, false);
912 return checkTypesImpl(ar, type, Type::Variant, coerce);
913 } else if (func != m_funcScope) {
914 m_funcScope = func;
915 Construct::recomputeEffects();
918 m_builtinFunction = (!func->isUserFunction() || func->isSepExtension());
920 CHECK_HOOK(beforeSimpleFunctionCallCheck);
922 m_valid = true;
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());
931 if (m_safe) {
932 TypePtr atype = getActualType();
933 if (m_safe > 0 && !m_safeDef) {
934 atype = Type::Array;
935 } else if (!m_safeDef) {
936 atype = Type::Variant;
937 } else {
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);
949 return rtype;
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());
961 } else {
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);
983 cg_printf(")");
984 } else {
985 ExpressionPtr param = (*m_params)[i];
986 if (param) param->outputPHP(cg, ar);
989 cg_printf(")");
990 return;
994 cg_printf("%s(", m_name.c_str());
995 } else {
996 cg_printf("%s(", m_name.c_str());
1000 if (m_params) m_params->outputPHP(cg, ar);
1001 cg_printf(")");
1004 bool SimpleFunctionCall::preOutputCPP(CodeGenerator &cg, AnalysisResultPtr ar,
1005 int state) {
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.
1022 m_valid = false;
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;
1035 ClassScopePtr cls;
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);
1041 cg_printf(";\n");
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);
1049 } else {
1050 cg_printf("MethodCallPackage mcp%d;\n", m_ciTemp);
1052 bool safeCheck = false;
1053 if (m_safe) {
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);
1059 safeCheck = true;
1061 } else if (!m_funcScope || m_funcScope->isVolatile()) {
1062 cg_indentBegin("if (");
1063 cg_printf("%s->FVF(%s)",
1064 cg.getGlobals(ar),
1065 cg.formatLabel(m_name).c_str());
1066 safeCheck = true;
1069 if (safeCheck) {
1070 cg_printf(") {\n");
1072 if (!m_class && m_className.empty()) {
1073 if (m_redeclared && !m_dynamicInvoke) {
1074 needHash = false;
1075 cg_printf("cit%d = %s->%s%s;\n", m_ciTemp, cg.getGlobals(ar),
1076 Option::CallInfoPrefix, cg.formatLabel(m_name).c_str());
1077 if (!safeCheck) {
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());
1082 } else {
1083 cg_printf("get_call_info_or_fail(cit%d, vt%d, ", m_ciTemp, m_ciTemp);
1084 cg_printString(m_name, ar);
1085 cg_printf(");\n");
1086 needHash = false;
1088 } else {
1089 const MethodSlot *ms = NULL;
1090 if (!m_name.empty()) {
1091 ms = ar->getOrAddMethodSlot(m_name);
1093 if (safeCheck) {
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;
1101 string className;
1102 if (m_classScope) {
1103 if (m_redeclaredClass) {
1104 className = cg.formatLabel(m_classScope->getName());
1105 } else {
1106 className = m_classScope->getId(cg);
1108 } else {
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
1114 // StaticString.
1115 m_clsNameTemp = cg.createNewId(ar);
1116 cg_printf("CStrRef clsName%d(", m_clsNameTemp);
1117 cg_printString(m_origClassName, ar);
1118 cg_printf(");\n");
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) {
1126 if (parentCall) {
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);
1131 } else {
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,
1142 true, false);
1143 if (exCall) {
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);
1149 } else {
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);
1156 } else {
1157 if (m_class) {
1158 needHash = false;
1159 bool lsb = false;
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\"");
1165 lsb = true;
1166 } else {
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);
1173 } else {
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());
1180 if (ms) {
1181 cg_printf(", %s", ms->runObjParam().c_str());
1184 if (needHash) {
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);
1190 if (safeCheck) {
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);
1201 ar->popCallInfo();
1203 return true;
1206 void SimpleFunctionCall::outputCPPParamOrderControlled(CodeGenerator &cg,
1207 AnalysisResultPtr ar) {
1208 if (!m_class && m_className.empty()) {
1209 switch (m_type) {
1210 case ExtractFunction:
1211 cg_printf("extract(variables, ");
1212 FunctionScope::outputCPPArguments(m_params, cg, ar, 0, false);
1213 cg_printf(")");
1214 return;
1215 case CompactFunction:
1216 cg_printf("compact(variables, ");
1217 FunctionScope::outputCPPArguments(m_params, cg, ar, -1, true);
1218 cg_printf(")");
1219 return;
1220 default:
1221 break;
1224 bool volatileCheck = false;
1225 ClassScopePtr cls = m_classScope;
1227 TypePtr safeCast;
1228 if (m_safe) {
1229 if (!m_className.empty()) {
1230 if ((!cls || cls->isVolatile()) &&
1231 !ar->checkClassPresent(m_origClassName)) {
1232 if (m_valid) {
1233 cg_printf("(");
1234 ClassScope::OutputVolatileCheck(cg, ar, m_className, true);
1236 volatileCheck = true;
1238 } else if (!m_funcScope || m_funcScope->isVolatile()) {
1239 if (m_valid) {
1240 cg_printf("(%s->FVF(%s)",
1241 cg.getGlobals(ar),
1242 cg.formatLabel(m_name).c_str());
1244 volatileCheck = true;
1247 if (volatileCheck) {
1248 if (!m_valid) {
1249 // ci will be null if fn/cl not defined. Set in preoutput
1250 cg_printf("(cit%d", m_ciTemp);
1252 if (isUnused()) {
1253 cg_printf(" && (");
1254 } else {
1255 cg_printf(" ? ");
1258 if (!isUnused()) {
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(),
1266 getActualType())) {
1267 safeCast = getActualType();
1268 safeCast->outputCPPCast(cg, ar);
1269 cg_printf("(");
1271 if (m_funcScope && !m_funcScope->getReturnType()) {
1272 cg_printf("(");
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()) {
1286 assert(cls);
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());
1293 } else {
1294 int paramCount = m_params ? m_params->getCount() : 0;
1295 if (m_name == "get_class" && ar->getClassScope() && paramCount == 0) {
1296 cg_printf("(");
1297 cg_printString(ar->getClassScope()->getOriginalName(), ar);
1298 } else if (m_name == "get_parent_class" && ar->getClassScope() &&
1299 paramCount == 0) {
1300 const std::string parentClass = ar->getClassScope()->getParent();
1301 cg_printf("(");
1302 if (!parentClass.empty()) {
1303 cg_printString(ar->getClassScope()->getParent(), ar);
1304 } else {
1305 cg_printf("false");
1307 } else {
1308 if (m_noPrefix) {
1309 cg_printf("%s(", cg.formatLabel(m_name).c_str());
1310 } else {
1311 cg_printf("%s%s(",
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);
1320 } else {
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);
1327 } else {
1328 cg_printf("(cit%d->getFunc())(vt%d, ", m_ciTemp, m_ciTemp);
1330 } else {
1331 if (canInvokeFewArgs() && !m_arrayParams) {
1332 cg_printf("(cit%d->getMethFewArgs())(mcp%d, ", m_ciTemp, m_ciTemp);
1333 } else {
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);
1344 ar->popCallInfo();
1345 } else {
1346 cg_printf("0");
1348 for (int i = 0; i < left; i++) {
1349 cg_printf(", null");
1351 } else {
1352 if ((!m_params) || (m_params->getCount() == 0)) {
1353 cg_printf("Array()");
1354 } else {
1355 ar->pushCallInfo(m_ciTemp);
1356 FunctionScope::outputCPPArguments(m_params, cg, ar,
1357 m_arrayParams ? 0 : -1, false);
1358 ar->popCallInfo();
1362 cg_printf(")");
1364 if (m_safe) {
1365 if (isUnused()) {
1366 } else {
1367 if (m_funcScope && !m_funcScope->getReturnType()) {
1368 cg_printf(", null)");
1370 if (safeCast) {
1371 cg_printf(")");
1373 if (m_safe > 0 && !m_safeDef) {
1374 cg_printf(").create())");
1378 if (volatileCheck) {
1379 if (m_safe) {
1380 if (isUnused()) {
1381 cg_printf(", false)");
1382 } else {
1383 cg_printf(" : ");
1385 if (m_safeDef && m_safeDef->getActualType() &&
1386 !Type::SameType(m_safeDef->getActualType(), getActualType())) {
1387 safeCast = getActualType();
1388 safeCast->outputCPPCast(cg, ar);
1389 cg_printf("(");
1390 } else {
1391 safeCast.reset();
1394 if (m_safeDef) {
1395 m_safeDef->outputCPP(cg, ar);
1396 } else {
1397 if (m_safe < 0) {
1398 cg_printf("null");
1399 } else {
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);
1407 if (safeCast) {
1408 cg_printf(")");
1411 cg_printf(")");
1412 } else {
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());
1422 return;
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]);
1429 string varName;
1430 ExpressionPtr value = (*m_params)[1];
1431 if (name) {
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,
1438 varName.c_str());
1439 value->outputCPP(cg, ar);
1440 cg_printf(")");
1441 } else {
1442 bool needAssignment = true;
1443 bool isSystem = ar->getConstants()->isSystem(varName);
1444 if (isSystem ||
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);
1453 } else {
1454 bool close = false;
1455 if (value->hasEffect()) {
1456 cg_printf("(id(");
1457 value->outputCPP(cg, ar);
1458 cg_printf("),");
1459 close = true;
1461 cg_printf("throw_fatal(\"bad define\")");
1462 if (close) cg_printf(")");
1464 return;
1466 if (m_name == "func_num_args") {
1467 cg_printf("num_args");
1468 return;
1471 switch (m_type) {
1472 case VariableArgumentFunction:
1474 FunctionScopePtr func =
1475 dynamic_pointer_cast<FunctionScope>(ar->getScope());
1476 if (func) {
1477 cg_printf("%s(", m_name.c_str());
1478 func->outputCPPParamsCall(cg, ar, true);
1479 if (m_params) {
1480 cg_printf(",");
1481 m_params->outputCPP(cg, ar);
1483 cg_printf(")");
1484 return;
1487 break;
1488 case FunctionExistsFunction:
1489 case ClassExistsFunction:
1490 case InterfaceExistsFunction:
1492 bool literalString = false;
1493 string symbol;
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);
1507 switch (m_type) {
1508 case FunctionExistsFunction:
1510 bool dynInvoke = Option::DynamicInvokeFunctions.find(lname) !=
1511 Option::DynamicInvokeFunctions.end();
1512 if (!dynInvoke) {
1513 FunctionScopePtr func = ar->findFunction(lname);
1514 if (func) {
1515 if (func->isVolatile()) {
1516 cg_printf("%s->FVF(%s)",
1517 cg.getGlobals(ar),
1518 cg.formatLabel(lname).c_str());
1519 break;
1521 cg_printf("true");
1522 break;
1523 } else {
1524 cg_printf("false");
1525 break;
1528 cg_printf("f_function_exists(");
1529 cg_printString(symbol, ar);
1530 cg_printf(")");
1532 break;
1533 case ClassExistsFunction:
1534 case InterfaceExistsFunction:
1536 ClassScopePtrVec classes = ar->findClasses(Util::toLower(symbol));
1537 int found = 0;
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;
1544 } else {
1545 foundOther = true;
1549 if (!found) {
1550 cg_printf("false");
1551 } else if (!foundOther) {
1552 if (found == 1) {
1553 cg_printf("true");
1554 } else {
1555 if (m_type == ClassExistsFunction) {
1556 cg_printf("checkClassExists(");
1557 } else {
1558 cg_printf("checkInterfaceExists(");
1560 cg_printString(symbol, ar);
1561 cg_printf(", &%s->CDEC(%s), %s->FVF(__autoload), true)",
1562 cg.getGlobals(ar),
1563 cg.formatLabel(lname).c_str(),
1564 cg.getGlobals(ar));
1566 } else {
1567 if (m_type == ClassExistsFunction) {
1568 cg_printf("f_class_exists(");
1569 } else {
1570 cg_printf("f_interface_exists(");
1572 cg_printString(symbol, ar);
1573 cg_printf(")");
1576 break;
1577 default:
1578 break;
1580 return;
1583 break;
1584 case GetDefinedVarsFunction:
1585 cg_printf("get_defined_vars(variables)");
1586 return;
1587 default:
1588 break;
1592 outputCPPParamOrderControlled(cg, ar);
1595 bool SimpleFunctionCall::canInvokeFewArgs() {
1596 // We can always change out minds about saying yes, but once we say
1597 // no, it sticks.
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) {
1609 error = false;
1610 ExpressionListPtr params = call->getParams();
1611 if (params && params->getCount() >= firstParam) {
1612 ExpressionPtr p0 = (*params)[0];
1613 Variant v;
1614 if (p0->isScalar() && p0->getScalarValue(v)) {
1615 if (v.isString()) {
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()) {
1621 error = !func;
1622 return SimpleFunctionCallPtr();
1624 if (func->isUserFunction()) func->setVolatile();
1625 ExpressionListPtr p2;
1626 if (testOnly) {
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";
1633 } else {
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()));
1643 return rep;
1645 v = t;
1647 if (v.isArray()) {
1648 Array arr = v.toArray();
1649 if (arr.size() != 2 || !arr.exists(0) || !arr.exists(1)) {
1650 error = true;
1651 return SimpleFunctionCallPtr();
1653 Variant classname = arr.rvalAt(0LL);
1654 Variant methodname = arr.rvalAt(1LL);
1655 if (!methodname.isString()) {
1656 error = true;
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);
1666 if (!cls) {
1667 error = true;
1668 return SimpleFunctionCallPtr();
1670 if (cls->isRedeclaring()) {
1671 cls = ar->findExactClass(sclass);
1672 } else if (!cls->isVolatile() && cls->isUserClass() &&
1673 !ar->checkClassPresent(sclass)) {
1674 cls->setVolatile();
1676 if (!cls) {
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);
1693 if (!func) {
1694 error = 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)))) {
1703 error = true;
1704 return SimpleFunctionCallPtr();
1706 ExpressionPtr cl(
1707 Expression::MakeScalarExpression(ar, call->getLocation(), classname));
1708 ExpressionListPtr p2;
1709 if (testOnly) {
1710 p2 = ExpressionListPtr(
1711 new ExpressionList(call->getLocation(),
1712 Expression::KindOfExpressionList));
1713 p2->addElement(cl);
1714 cl.reset();
1715 smethod = "class_exists";
1716 } else {
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,
1725 smethod, p2, cl));
1726 return rep;
1730 return SimpleFunctionCallPtr();
1733 namespace HPHP {
1735 ExpressionPtr hphp_opt_call_user_func(CodeGenerator *cg,
1736 AnalysisResultPtr ar,
1737 SimpleFunctionCallPtr call, int mode) {
1738 bool error = false;
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,
1745 1, error));
1746 if (error) {
1747 rep.reset();
1748 } else if (rep) {
1749 rep->setSafeCall(-1);
1750 rep->addLateDependencies(ar);
1751 if (isArray) rep->setArrayParams();
1753 return rep;
1756 return ExpressionPtr();
1759 ExpressionPtr hphp_opt_fb_call_user_func(CodeGenerator *cg,
1760 AnalysisResultPtr ar,
1761 SimpleFunctionCallPtr call, int mode) {
1762 bool error = false;
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));
1771 if (error) {
1772 if (safe_ret) {
1773 return (*call->getParams())[1];
1774 } else {
1775 Array ret(Array::Create(0, false));
1776 ret.set(1, Variant());
1777 return Expression::MakeScalarExpression(ar, call->getLocation(), ret);
1780 if (rep) {
1781 if (isArray) rep->setArrayParams();
1782 rep->addLateDependencies(ar);
1783 rep->setSafeCall(1);
1784 if (safe_ret) {
1785 ExpressionPtr def = (*call->getParams())[1];
1786 rep->setSafeDefault(def);
1788 return rep;
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()) {
1799 bool error = false;
1800 SimpleFunctionCallPtr rep(
1801 SimpleFunctionCall::getFunctionCallForCallUserFunc(
1802 ar, call, true, 1, error));
1803 if (error) {
1804 return Expression::MakeConstant(ar, call->getLocation(), "false");
1806 return rep;
1809 return ExpressionPtr();