bool AnalysisResult::checkClassPresent(ConstructPtr cs, const std::string &name) const { if (name == "self" || name == "parent") return true; std::string lowerName = toLower(name); if (ClassScopePtr currentCls = cs->getClassScope()) { if (lowerName == currentCls->getName() || currentCls->derivesFrom(shared_from_this(), lowerName, true, false)) { return true; } } if (FileScopePtr currentFile = cs->getFileScope()) { StatementList &stmts = *currentFile->getStmt(); for (int i = stmts.getCount(); i--; ) { StatementPtr s = stmts[i]; if (s && s->is(Statement::KindOfClassStatement)) { ClassScopePtr scope = static_pointer_cast<ClassStatement>(s)->getClassScope(); if (lowerName == scope->getName()) { return true; } if (scope->derivesFrom(shared_from_this(), lowerName, true, false)) { return true; } } } } return false; }
void ClassScope::informClosuresAboutScopeClone( ConstructPtr root, FunctionScopePtr outerScope, AnalysisResultPtr ar) { if (!root) { return; } for (int i = 0; i < root->getKidCount(); i++) { ConstructPtr cons = root->getNthKid(i); ClosureExpressionPtr closure = dynamic_pointer_cast<ClosureExpression>(cons); if (!closure) { informClosuresAboutScopeClone(cons, outerScope, ar); continue; } FunctionStatementPtr func = closure->getClosureFunction(); HPHP::FunctionScopePtr funcScope = func->getFunctionScope(); assert(funcScope->isClosure()); funcScope->addClonedTraitOuterScope(outerScope); // Don't need to recurse } }
void AnalysisResult::analyzeProgram(ConstructPtr c) const { if (!c) return; for (auto i = 0, n = c->getKidCount(); i < n; ++i) { analyzeProgram(c->getNthKid(i)); } c->analyzeProgram(AnalysisResultConstRawPtr{this}); }
void Parser::addHphpNote(ConstructPtr c, const std::string ¬e) { if (note[0] == '@') { CodeError::ErrorType e; if (CodeError::lookupErrorType(note.substr(1), e)) { c->addSuppressError(e); } else { c->addHphpNote(note); } } else { c->addHphpNote(note); } }
int Construct::getChildrenEffects() const { int childrenEffects = NoEffect; for (int i = getKidCount(); i--; ) { ConstructPtr child = getNthKid(i); if (child) { childrenEffects |= child->getContainedEffects(); if ((childrenEffects & UnknownEffect) == UnknownEffect) { break; } } } return childrenEffects; }
bool TestDependGraph::TestFunctionCall() { // functions VD4(FunctionCall, "<?php include $_SERVER['PHP_ROOT'].'f2';\n test();", "<?php function test() {}", "test", "test()"); VD4(FunctionCall, "<?php test(); function test() {}", "", "test", "test()"); VD4(FunctionCall, "<?php function test() {} test();", "", "test", "test()"); // methods VD4(FunctionCall, "<?php include $_SERVER['PHP_ROOT'].'f2';\n" "function test() { $a = new C(); $a->test();}", "<?php class C { public function test() {}}", "c::test", "$a->test()"); VD4(FunctionCall, "<?php function test() { $a = new C(); $a->test();} " "class C { public function test() {}}", "", "c::test", "$a->test()"); VD4(FunctionCall, "<?php class C { public function test() {}} " "function test() { $a = new C(); $a->test();}", "", "c::test", "$a->test()"); #ifdef HPHP_NOTE // making sure a "MasterCopy" marked function will always be used for // dependency's parent { Option::IncludeRoots["$_SERVER['PHP_ROOT']"] = ""; AnalysisResultPtr ar(new AnalysisResult()); BuiltinSymbols::Load(ar); Parser::ParseString("<?php function test() {}", ar, "f1"); Parser::ParseString("<?php /*|MasterCopy|*/ function test() {}", ar, "f2"); ar->analyzeProgram(); ar->inferTypes(); DependencyGraphPtr dg = ar->getDependencyGraph(); ConstructPtr parent = dg->getParent(DependencyGraph::KindOfFunctionCall, "test"); if (!parent || strcmp(parent->getLocation()->file, "f2") != 0) { printf("%s:%d: incorrect parent found at %s:%d\n", __FILE__, __LINE__, parent ? parent->getLocation()->file : "", parent ? parent->getLocation()->line0 : 0); return false; } } #endif return true; }
int Construct::getChildrenEffects() const { int childrenEffects = NoEffect; for (int i = getKidCount(); i--; ) { ConstructPtr child = getNthKid(i); if (child) { if (FunctionWalker::SkipRecurse(child)) continue; childrenEffects |= child->getContainedEffects(); if ((childrenEffects & UnknownEffect) == UnknownEffect) { break; } } } return childrenEffects; }
int CodeGenerator::createNewLocalId(ConstructPtr ar) { if (m_wrappedExpression[m_curStream]) { return m_localId[m_curStream]++; } FunctionScopePtr func = ar->getFunctionScope(); if (func) { return func->nextInlineIndex(); } FileScopePtr fs = ar->getFileScope(); if (fs) { return createNewId(fs->getName()); } return createNewId(""); }
int CodeGenerator::createNewId(ConstructPtr cs) { FileScopePtr fs = cs->getFileScope(); if (fs) { return createNewId(fs->getName()); } return createNewId(""); }
string IncludeExpression::CheckInclude(ConstructPtr includeExp, ExpressionPtr fileExp, bool &documentRoot) { string container = includeExp->getLocation()->file; string var, lit; parse_string_arg(fileExp, var, lit); if (lit.empty()) return lit; if (var == "__DIR__") { var = ""; // get_include_file_path will check relative to the current file's dir // as long as the first char isn't a / if (lit[0] == '/') { lit = lit.substr(1); } } string included = get_include_file_path(container, var, lit, documentRoot); if (!included.empty()) { if (included == container) { Compiler::Error(Compiler::BadPHPIncludeFile, includeExp); } included = Util::canonicalize(included); if (!var.empty()) documentRoot = true; } return included; }
string DependencyGraph::add(KindOf kindOf, ConstructPtr childExp, ExpressionPtr parentExp, CodeErrorPtr codeError, bool documentRoot /* = false */) { string child = childExp->getLocation()->file; string parent; switch (kindOf) { case KindOfPHPInclude: if (!checkInclude(childExp, parentExp, codeError, documentRoot, child, parent)) { return ""; } break; default: if (!m_hookHandler || !m_hookHandler(this, kindOf, childExp, parentExp, codeError, documentRoot, child, parent, beforeDependencyGraphAdd)) { return ""; } break; } string program; // no program is associated add(kindOf, program, child, childExp, parent, ConstructPtr(), codeError); if (m_hookHandler) { m_hookHandler(this, kindOf, childExp, parentExp, codeError, documentRoot, child, parent, afterDependencyGraphAdd); } return parent; }
ExpressionPtr Option::GetValue(VariableTablePtr variables, const char *varName) { ConstructPtr decl = variables->getDeclaration(varName); if (!decl) { return ExpressionPtr(); } AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(decl); if (!assignment) { Logger::Error("Line %d: Ignored option %s", decl->getLocation()->line1, varName); return ExpressionPtr(); } return assignment->getValue(); }
void Error(ErrorType error, ConstructPtr construct) { if (hhvm) return; ErrorInfoPtr errorInfo(new ErrorInfo()); errorInfo->m_error = error; errorInfo->m_construct1 = construct; errorInfo->m_data = construct->getText(); s_code_errors.record(errorInfo); }
void ClassConstantExpression::outputCPPImpl(CodeGenerator &cg, AnalysisResultPtr ar) { const char *globals = "g"; if (cg.getContext() == CodeGenerator::CppParameterDefaultValueDecl || cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) { globals = cg.getGlobals(); } if (m_valid) { ClassScopePtr foundCls; string trueClassName; for (ClassScopePtr cls = ar->findClass(m_className); cls; cls = cls->getParentScope(ar)) { if (cls->getConstants()->isPresent(m_varName)) { foundCls = cls; trueClassName = cls->getName(); break; } } ASSERT(!trueClassName.empty()); ConstructPtr decl = foundCls->getConstants()->getValue(m_varName); if (decl) { decl->outputCPP(cg, ar); if (cg.getContext() == CodeGenerator::CppImplementation || cg.getContext() == CodeGenerator::CppParameterDefaultValueImpl) { cg.printf("(%s::%s)", m_className.c_str(), m_varName.c_str()); } else { cg.printf("/* %s::%s */", m_className.c_str(), m_varName.c_str()); } } else { if (foundCls->getConstants()->isDynamic(m_varName)) { cg.printf("%s%s::lazy_initializer(%s)->", Option::ClassPrefix, trueClassName.c_str(), globals); } cg.printf("%s%s_%s", Option::ClassConstantPrefix, trueClassName.c_str(), m_varName.c_str()); } } else if (m_redeclared) { cg.printf("%s->%s%s->os_constant(\"%s\")", globals, Option::ClassStaticsObjectPrefix, m_className.c_str(), m_varName.c_str()); } else { cg.printf("throw_fatal(\"unknown class constant %s::%s\")", m_className.c_str(), m_varName.c_str()); } }
void Error(ErrorType error, ConstructPtr construct1, ConstructPtr construct2) { if (!Option::RecordErrors) return; ErrorInfoPtr errorInfo(new ErrorInfo()); errorInfo->m_error = error; errorInfo->m_construct1 = construct1; errorInfo->m_construct2 = construct2; errorInfo->m_data = construct1->getText(); s_code_errors.record(errorInfo); }
void AliasManager::canonicalizeKid(ConstructPtr e, ExpressionPtr kid, int i) { if (kid) { kid = canonicalizeRecur(kid); if (kid) { e->setNthKid(i, kid); m_changed = true; } } }
void walk_ast(ConstructPtr node) { if (!node) return; if (dynamic_pointer_cast<MethodStatement>(node)) { // Don't descend into nested non-closure functions, or functions // in the psuedo-main. return; } if (auto ce = dynamic_pointer_cast<ClosureExpression>(node)) { visit_closure(ce); return; } for (int i = 0; i < node->getKidCount(); ++i) { walk_ast(node->getNthKid(i)); } }
void ConstantExpression::analyzeProgram(AnalysisResultPtr ar) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { Symbol *sym = resolveNS(ar); if (!(m_context & LValue) && !m_dynamic) { if (sym && !sym->isSystem()) { if (sym->isDynamic()) { m_dynamic = true; } else { ConstructPtr decl = sym->getDeclaration(); if (decl) { decl->getScope()->addUse( getScope(), BlockScope::UseKindConstRef); m_depsSet = true; } } } } } }
ClassScopePtr AnalysisResult::findExactClass(ConstructPtr cs, const std::string &name) const { ClassScopePtr cls = findClass(name); if (!cls || !cls->isRedeclaring()) return cls; if (ClassScopePtr currentCls = cs->getClassScope()) { if (cls->isNamed(currentCls->getScopeName())) { return currentCls; } } return ClassScopePtr(); }
void Symbol::import(BlockScopeRawPtr scope, const Symbol &src_sym) { setName(src_sym.getName()); setSystem(); if (src_sym.declarationSet()) { ConstructPtr sc = src_sym.getDeclaration(); if (sc) sc->resetScope(scope); setDeclaration(sc); } if (src_sym.valueSet()) { ConstructPtr sc = src_sym.getValue(); if (sc) sc->resetScope(scope); setValue(sc); } if (src_sym.isDynamic()) { setDynamic(); } if (src_sym.isConstant()) { setConstant(); } }
bool ClosureExpression::hasStaticLocalsImpl(ConstructPtr root) { if (!root) { return false; } if (root->getFunctionScope() != m_func->getFunctionScope()) { // new scope, new statics return false; } for (int i = 0; i < root->getKidCount(); i++) { ConstructPtr cons = root->getNthKid(i); if (StatementPtr s = dynamic_pointer_cast<Statement>(cons)) { if (s->is(Statement::KindOfStaticStatement)) { return true; } } if (hasStaticLocalsImpl(cons)) { return true; } } return false; }
int Construct::getChildrenEffects() const { int childrenEffects = NoEffect; for (int i = getKidCount(); i--; ) { ConstructPtr child = getNthKid(i); if (child) { if (StatementPtr s = boost::dynamic_pointer_cast<Statement>(child)) { switch (s->getKindOf()) { case Statement::KindOfMethodStatement: case Statement::KindOfFunctionStatement: case Statement::KindOfClassStatement: case Statement::KindOfInterfaceStatement: continue; default: break; } } childrenEffects |= child->getContainedEffects(); if ((childrenEffects & UnknownEffect) == UnknownEffect) { break; } } } return childrenEffects; }
// Returns: whether or not $this is implicitly used in this part of the AST, // e.g. via a call using parent::. bool walk_ast(ConstructPtr node) { if (!node) return false; if (dynamic_pointer_cast<MethodStatement>(node)) { // Don't descend into nested non-closure functions, or functions // in the pseudo-main. return false; } if (auto ce = dynamic_pointer_cast<ClosureExpression>(node)) { return visit_closure(ce); } auto ret = false; if (auto fc = dynamic_pointer_cast<FunctionCall>(node)) { if (fc->isParent()) ret = true; } for (int i = 0; i < node->getKidCount(); ++i) { if (walk_ast(node->getNthKid(i))) ret = true; } return ret; }
bool DependencyGraph::checkInclude(ConstructPtr childExp, ExpressionPtr parentExp, CodeErrorPtr codeError, bool documentRoot, string &child, string &parent) { child = childExp->getLocation()->file; parent = parseInclude(child, parentExp, documentRoot); if ((parent.empty() || parent == child) && Option::AllowedBadPHPIncludes.find(parentExp->getText()) == Option::AllowedBadPHPIncludes.end()) { if (codeError) { codeError->record(CodeError::BadPHPIncludeFile, childExp); } return false; } if (parent.empty()) return false; return true; }
void DependencyGraph::addParent(KindOf kindOf, const std::string &program, const std::string &parentName, ConstructPtr parent) { ASSERT(kindOf >= 0 && kindOf < KindOfCount); ASSERT(!parentName.empty()); DependencyPtr &dep = m_parents[kindOf][parentName]; if (!dep) { dep = DependencyPtr(new Dependency()); dep->m_parent = parent; dep->m_programCount = 1; if (Option::DependencyMaxProgram && !program.empty()) { dep->m_programs.push_back(program); } m_parents[kindOf][parentName] = dep; } else if (parent && parent->hasHphpNote("MasterCopy")) { dep->m_parent = parent; } }
string IncludeExpression::CheckInclude(ConstructPtr includeExp, ExpressionPtr fileExp, bool &documentRoot, bool relative) { string container = includeExp->getLocation()->file; string var, lit; parse_string_arg(fileExp, var, lit); if (lit.empty()) return lit; string included = get_include_file_path(container, var, lit, documentRoot, relative); if (!included.empty()) { if (included == container) { Compiler::Error(Compiler::BadPHPIncludeFile, includeExp); } included = Util::canonicalize(included); if (!var.empty()) documentRoot = true; } return included; }
string IncludeExpression::CheckInclude(ConstructPtr includeExp, ExpressionPtr fileExp, bool documentRoot) { string container = includeExp->getLocation()->file; string var, lit; parse_string_arg(fileExp, var, lit); if (!lit.empty()) { if (!var.empty()) { var += " . "; } var += "'" + lit + "'"; } string included = get_include_file_path(container, var, documentRoot); included = Util::canonicalize(included); if (included.empty() || container == included) { if (!included.empty() && included.find(' ') == string::npos) { Compiler::Error(Compiler::BadPHPIncludeFile, includeExp); } return ""; } return included; }
void Construct::copyLocationTo(ConstructPtr other) { always_assert(other->getFileScope() == getFileScope()); other->m_r = m_r; }
void Construct::dump(int spc, AnalysisResultPtr ar) { int nkid = getKidCount(); std::string name; int type = 0; std::string scontext = ""; std::string value = ""; std::string type_info = ""; unsigned id = 0; ExpressionPtr idPtr = ExpressionPtr(); if (Statement *s = dynamic_cast<Statement*>(this)) { Statement::KindOf stype = s->getKindOf(); switch (stype) { case Statement::KindOfFunctionStatement: name="FunctionStatement"; break; case Statement::KindOfClassStatement: name="ClassStatement"; break; case Statement::KindOfInterfaceStatement: name="InterfaceStatement"; break; case Statement::KindOfClassVariable: name="ClassVariable"; break; case Statement::KindOfClassConstant: name="ClassConstant"; break; case Statement::KindOfMethodStatement: name="MethodStatement"; break; case Statement::KindOfStatementList: name="StatementList"; break; case Statement::KindOfBlockStatement: name="BlockStatement"; break; case Statement::KindOfIfBranchStatement: name="IfBranchStatement"; break; case Statement::KindOfIfStatement: name="IfStatement"; break; case Statement::KindOfWhileStatement: name="WhileStatement"; break; case Statement::KindOfDoStatement: name="DoStatement"; break; case Statement::KindOfForStatement: name="ForStatement"; break; case Statement::KindOfSwitchStatement: name="SwitchStatement"; break; case Statement::KindOfCaseStatement: name="CaseStatement"; break; case Statement::KindOfBreakStatement: name="BreakStatement"; break; case Statement::KindOfContinueStatement: name="ContinueStatement"; break; case Statement::KindOfReturnStatement: name="ReturnStatement"; break; case Statement::KindOfGlobalStatement: name="GlobalStatement"; break; case Statement::KindOfStaticStatement: name="StaticStatement"; break; case Statement::KindOfEchoStatement: name="EchoStatement"; break; case Statement::KindOfUnsetStatement: name="UnsetStatement"; break; case Statement::KindOfExpStatement: name="ExpStatement"; break; case Statement::KindOfForEachStatement: name="ForEachStatement"; break; case Statement::KindOfCatchStatement: name="CatchStatement"; break; case Statement::KindOfTryStatement: name="TryStatement"; break; case Statement::KindOfThrowStatement: name="ThrowStatement"; break; } type = (int)stype; } else if (Expression *e = dynamic_cast<Expression*>(this)) { id = e->getCanonID(); idPtr = e->getCanonPtr(); Expression::KindOf etype = e->getKindOf(); switch (etype) { case Expression::KindOfSimpleFunctionCall: name="SimpleFunctionCall"; value = static_cast<SimpleFunctionCall*>(e)->getName(); break; case Expression::KindOfSimpleVariable: name="SimpleVariable"; value = static_cast<SimpleVariable*>(e)->getName(); break; case Expression::KindOfConstantExpression: name="ConstantExpression"; value = e->getText(); break; case Expression::KindOfScalarExpression: name="ScalarExpression"; value = e->getText(); break; case Expression::KindOfExpressionList: name="ExpressionList"; break; case Expression::KindOfAssignmentExpression: name="AssignmentExpression"; break; case Expression::KindOfDynamicVariable: name="DynamicVariable"; break; case Expression::KindOfStaticMemberExpression: name="StaticMemberExpression"; break; case Expression::KindOfArrayElementExpression: name="ArrayElementExpression"; break; case Expression::KindOfDynamicFunctionCall: name="DynamicFunctionCall"; break; case Expression::KindOfObjectPropertyExpression: name="ObjectPropertyExpression"; break; case Expression::KindOfObjectMethodExpression: name="ObjectMethodExpression"; break; case Expression::KindOfListAssignment: name="ListAssignment"; break; case Expression::KindOfNewObjectExpression: name="NewObjectExpression"; break; case Expression::KindOfUnaryOpExpression: name="UnaryOpExpression"; break; case Expression::KindOfIncludeExpression: name="IncludeExpression"; break; case Expression::KindOfBinaryOpExpression: name="BinaryOpExpression"; break; case Expression::KindOfQOpExpression: name="QOpExpression"; break; case Expression::KindOfArrayPairExpression: name="ArrayPairExpression"; break; case Expression::KindOfClassConstantExpression: name="ClassConstantExpression"; break; case Expression::KindOfParameterExpression: name="ParameterExpression"; break; case Expression::KindOfModifierExpression: name="ModifierExpression"; break; case Expression::KindOfEncapsListExpression: name="EncapsListExpression"; break; } int c = e->getContext(); if ((c & Expression::Declaration) == Expression::Declaration) { scontext += "|Declaration"; } else if (c & Expression::LValue) { scontext += "|LValue"; } if (c & Expression::NoLValueWrapper) { scontext += "|NoLValueWrapper"; } if (c & Expression::RefValue) { scontext += "|RefValue"; } if (c & Expression::RefParameter) { scontext += "|RefParameter"; } if (c & Expression::DeepReference) { scontext += "|DeepReference"; } if (c & Expression::NoRefWrapper) { scontext += "|NoRefWrapper"; } if (c & Expression::ObjectContext) { scontext += "|ObjectContext"; } if (c & Expression::InParameterExpression) { scontext += "|InParameterExpression"; } if (c & Expression::ExistContext) { scontext += "|ExistContext"; } if (c & Expression::UnsetContext) { scontext += "|UnsetContext"; } if (c & Expression::AssignmentLHS) { scontext += "|AssignmentLHS"; } if (c & Expression::DeepAssignmentLHS) { scontext += "|DeepAssignmentLHS"; } if (c & Expression::InvokeArgument) { scontext += "|InvokeArgument"; } if (c & Expression::OprLValue) { scontext += "|OprLValue"; } if (c & Expression::DeepOprLValue) { scontext += "|DeepOprLValue"; } if (scontext != "") { scontext = " (" + scontext.substr(1) + ")"; } type = (int)etype; if (e->getActualType()) { type_info = e->getActualType()->toString(); if (e->getExpectedType()) { type_info += ":" + e->getExpectedType()->toString(); } if (e->getImplementedType()) { type_info += ";" + e->getImplementedType()->toString(); } type_info = "{" + type_info + "} "; } } else { ASSERT(FALSE); } int s = spc; while (s > 0) { int n = s > 10 ? 10 : s; std::cout << (" "+10-n); s -= n; } std::cout << "-> 0x" << hex << setfill('0') << setw(10) << (int64)this << dec; std::cout << " " << name << "(" << type << ") "; if (id) { std::cout << "id=" << id << " "; } if (idPtr) { std::cout << "idp=0x" << hex << setfill('0') << setw(10) << (int64)idPtr.get() << " "; } if (value != "") { std::cout << "[" << value << "] "; } std::cout << type_info << nkid << scontext; if (m_loc) { std::cout << " " << m_loc->file << ":" << m_loc->line1 << "@" << m_loc->char1; } std::cout << "\n"; for (int i = 0; i < nkid; i++) { ConstructPtr kid = getNthKid(i); if (kid) { kid->dump(spc+2, ar); } else { int s = spc+2; while (s > 0) { int n = s > 10 ? 10 : s; std::cout << (" "+10-n); s -= n; } std::cout << "-> (nokid)\n"; } } }
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp, ExpressionListPtr params, bool &valid) { if (!params) { if (m_minParam > 0) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } return 0; } int ret = 0; if (params->getCount() < m_minParam) { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooFewArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } if (params->getCount() > m_maxParam) { if (isVariableArgument()) { ret = params->getCount() - m_maxParam; } else { if (exp->getScope()->isFirstPass()) { Compiler::Error(Compiler::TooManyArgument, exp, m_stmt); } valid = false; if (!Option::AllDynamic) setDynamic(); } } bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual; for (int i = 0; i < params->getCount(); i++) { ExpressionPtr param = (*params)[i]; if (i < m_maxParam && param->hasContext(Expression::RefParameter)) { /** * This should be very un-likely, since call time pass by ref is a * deprecated, not very widely used (at least in FB codebase) feature. */ TRY_LOCK_THIS(); Symbol *sym = getVariables()->addSymbol(m_paramNames[i]); sym->setLvalParam(); sym->setCallTimeRef(); } if (valid && param->hasContext(Expression::InvokeArgument)) { param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::RefValue); param->clearContext(Expression::NoRefWrapper); } bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument()); if ((i < m_maxParam && isRefParam(i)) || isRefVararg) { param->setContext(Expression::LValue); param->setContext(Expression::RefValue); param->inferAndCheck(ar, Type::Variant, true); } else if (!(param->getContext() & Expression::RefParameter)) { param->clearContext(Expression::LValue); param->clearContext(Expression::RefValue); param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::NoRefWrapper); } TypePtr expType; /** * Duplicate the logic of getParamType(i), w/o the mutation */ TypePtr paramType(i < m_maxParam && !isZendParamMode() ? m_paramTypes[i] : TypePtr()); if (!paramType) paramType = Type::Some; if (valid && !canSetParamType && i < m_maxParam && (!Option::HardTypeHints || !m_paramTypeSpecs[i])) { /** * What is this magic, you might ask? * * Here, we take advantage of implicit conversion from every type to * Variant. Essentially, we don't really care what type comes out of this * expression since it'll just get converted anyways. Doing it this way * allows us to generate less temporaries along the way. */ TypePtr optParamType(paramType->is(Type::KindOfVariant) ? Type::Some : paramType); expType = param->inferAndCheck(ar, optParamType, false); } else { expType = param->inferAndCheck(ar, Type::Some, false); } if (i < m_maxParam) { if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) { if (canSetParamType) { if (!Type::SameType(paramType, expType) && !paramType->is(Type::KindOfVariant)) { TRY_LOCK_THIS(); paramType = setParamType(ar, i, expType); } else { // do nothing - how is this safe? well, if we ever observe // paramType == expType, then this means at some point in the past, // somebody called setParamType() with expType. thus, by calling // setParamType() again with expType, we contribute no "new" // information. this argument also still applies in the face of // concurrency } } // See note above. If we have an implemented type, however, we // should set the paramType to the implemented type to avoid an // un-necessary cast if (paramType->is(Type::KindOfVariant)) { TypePtr it(param->getImplementedType()); paramType = it ? it : expType; } if (valid) { if (!Type::IsLegalCast(ar, expType, paramType) && paramType->isNonConvertibleType()) { param->inferAndCheck(ar, paramType, true); } param->setExpectedType(paramType); } } } // we do a best-effort check for bad pass-by-reference and do not report // error for some vararg case (e.g., array_multisort can have either ref // or value for the same vararg). if (!isRefVararg || !isMixedVariableArgument()) { Expression::CheckPassByReference(ar, param); } } return ret; }