void DataFlowWalker::process(ExpressionPtr e, bool doAccessChains) { if ((e->getContext() & (Expression::AssignmentLHS|Expression::OprLValue)) || (!doAccessChains && e->hasContext(Expression::AccessContext))) { return; } switch (e->getKindOf()) { case Expression::KindOfListAssignment: processAccessChainLA(static_pointer_cast<ListAssignment>(e)); processAccess(e); break; case Expression::KindOfArrayElementExpression: case Expression::KindOfObjectPropertyExpression: if (!e->hasContext(Expression::AccessContext)) { processAccessChain(e); } // fall through case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: case Expression::KindOfIncludeExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfStaticMemberExpression: case Expression::KindOfConstantExpression: processAccess(e); break; case Expression::KindOfAssignmentExpression: case Expression::KindOfBinaryOpExpression: case Expression::KindOfUnaryOpExpression: { ExpressionPtr var = e->getStoreVariable(); if (var && var->getContext() & (Expression::AssignmentLHS| Expression::OprLValue)) { processAccessChain(var); processAccess(var); } // fall through } default: processAccess(e); break; } }
static bool isReadOnlyAccess(ExpressionPtr e) { if (e->getContext() & (Expression::UnsetContext| Expression::RefValue| Expression::LValue)) { return false; } switch (e->getKindOf()) { case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfDynamicVariable: return true; default: return false; } }
static ExpressionPtr cloneForInlineRecur(InlineCloneInfo &info, ExpressionPtr exp, const std::string &prefix, AnalysisResultConstPtr ar, FunctionScopePtr scope) { exp->getOriginalScope(); // make sure to cache the original scope exp->setBlockScope(scope); for (int i = 0, n = exp->getKidCount(); i < n; i++) { if (ExpressionPtr k = exp->getNthExpr(i)) { exp->setNthKid(i, cloneForInlineRecur(info, k, prefix, ar, scope)); } } StaticClassName *scn = dynamic_cast<StaticClassName*>(exp.get()); if (scn && scn->isStatic() && !info.staticClass.empty()) { scn->resolveStatic(info.staticClass); } switch (exp->getKindOf()) { case Expression::KindOfSimpleVariable: { SimpleVariablePtr sv(dynamic_pointer_cast<SimpleVariable>(exp)); if (sv->isSuperGlobal()) break; string name; if (sv->isThis()) { if (!info.callWithThis) { if (!sv->hasContext(Expression::ObjectContext)) { exp = sv->makeConstant(ar, "null"); } else { // This will produce the wrong error // we really want a "throw_fatal" ast node. exp = sv->makeConstant(ar, "null"); } break; } if (info.localThis.empty()) break; name = info.localThis; } else { name = prefix + sv->getName(); } SimpleVariablePtr rep(new SimpleVariable( exp->getScope(), exp->getLocation(), name)); rep->copyContext(sv); rep->updateSymbol(SimpleVariablePtr()); rep->getSymbol()->setHidden(); // Conservatively set flags to prevent // the alias manager from getting confused. // On the next pass, it will correct the values, // and optimize appropriately. rep->getSymbol()->setUsed(); rep->getSymbol()->setReferenced(); if (exp->getContext() & (Expression::LValue| Expression::RefValue| Expression::RefParameter)) { info.sepm[name] = rep; } exp = rep; } break; case Expression::KindOfObjectMethodExpression: { FunctionCallPtr call( static_pointer_cast<FunctionCall>(exp)); if (call->getFuncScope() == info.func) { call->setNoInline(); } break; } case Expression::KindOfSimpleFunctionCall: { SimpleFunctionCallPtr call(static_pointer_cast<SimpleFunctionCall>(exp)); call->addLateDependencies(ar); call->setLocalThis(info.localThis); if (call->getFuncScope() == info.func) { call->setNoInline(); } } default: break; } return exp; }
/* The classical use/def isnt quite enough here. There are two unusual issues: - the ref/non-ref issue. An assignment to a var which is referenced doesnt end its lifetime. In fact it could just be a "use" of the var. But it also counts as a def. - the destructor issue. variables which might need to be destroyed later are technically alive, but dont interfere with any other variables in the same state. They /do/ interfere with any truly "live" variables, however. These are "dying". So we end up defining, use, kill, def and dying. use : a read of the variable, an ordinary assignment if it could be referenced kill : an unset, a ref assignment, or, for non referenced vars, any assignment def : ref or normal assignment dying : a variable whose destructor is (partially/locally) anticipated. */ void LiveDict::updateAccess(ExpressionPtr e) { int cls = e->getExprClass(); if (cls & Expression::Store) { /* Handled when we see the lhs */ return; } int eid = e->getCanonID(); int context = e->getContext(); bool unset = false; bool store = false; if (context & Expression::LValue && context & Expression::UnsetContext) { unset = true; } else if (context & Expression::AssignmentLHS) { store = true; } if (e->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e)); bool use = false, kill = false, def = false; Symbol *sym = sv->getSymbol(); bool isReferenced = e->isReferencedValid() ? e->isReferenced() : sym && sym->isReferenced(); bool isNeeded = e->isNeededValid() ? e->isNeeded() : sym && sym->isNeeded(); if (unset) { kill = true; } else if (store) { if (context & Expression::RefAssignmentLHS || (!m_am.hasWildRefs() && !isReferenced)) { kill = true; } def = true; } else if ((context & Expression::Declaration) == Expression::Declaration) { // a global declaration def = kill = true; } else if (context & (Expression::LValue| Expression::RefValue| Expression::DeepReference| Expression::UnsetContext| Expression::OprLValue)) { use = def = true; } else { use = true; } if (kill && (!sym || isNeeded || isReferenced) && !BitOps::get_bit(eid, m_altered) && !BitOps::get_bit(eid, m_available)) { BitOps::set_bit(eid, m_dying, true); } if (use && !BitOps::get_bit(eid, m_altered) && !BitOps::get_bit(eid, m_available)) { BitOps::set_bit(eid, m_anticipated, true); e->setAnticipated(); } if (kill) { BitOps::set_bit(eid, m_altered, true); BitOps::set_bit(eid, m_available, def); } else if (def) { BitOps::set_bit(eid, m_available, true); } if (!m_am.couldBeAliased(sv)) { return; } } else if (!e->is(Expression::KindOfDynamicVariable) && (unset || (context & Expression::RefAssignmentLHS))) { // An unset, or a reference assignment to anything other // than a simple or dynamic variable can never affect a simple // variable (outside of pseudoMain). return; } if (store || cls & (Expression::Load|Expression::Call)) { bool mod = store || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference| Expression::OprLValue)); ExpressionPtr cur = m_refs, prev; bool isLoad; int depth = 0, effects = 0; while (cur) { ExpressionPtr next = cur->getCanonLVal(); int cid = cur->getCanonID(); if (cid != eid && m_am.checkAnyInterf(e, cur, isLoad, depth, effects) != AliasManager::DisjointAccess) { if (mod) { BitOps::set_bit(cid, m_available, true); } if (!BitOps::get_bit(cid, m_altered) && !BitOps::get_bit(cid, m_available)) { BitOps::set_bit(cid, m_anticipated, true); } if (!prev) { m_refs = next; } else { prev->setCanonPtr(next); } } else { prev = cur; } cur = next; } } }
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) { if (m_className.empty()) { addUserFunction(ar, m_name); } else if (m_className != "parent") { addUserClass(ar, m_className); } else { m_parentClass = true; } if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude); ConstructPtr self = shared_from_this(); // We need to know the name of the constant so that we can associate it // with this file before we do type inference. if (m_className.empty() && m_type == DefineFunction) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ar->getFileScope()->declareConstant(ar, varName); } } // handling define("CONSTANT", ...); if (m_params && m_params->getCount() >= 2) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>((*m_params)[0]); string varName; if (name) { varName = name->getIdentifier(); if (!varName.empty()) { ExpressionPtr value = (*m_params)[1]; ConstantTablePtr constants = ar->findConstantDeclarer(varName)->getConstants(); if (constants != ar->getConstants()) { constants->add(varName, NEW_TYPE(Some), value, ar, self); if (name->hasHphpNote("Dynamic")) { constants->setDynamic(ar, varName); } } } } } } if (m_type == UnserializeFunction) { ar->forceClassVariants(); } } if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // Look up the corresponding FunctionScope and ClassScope // for this function call { FunctionScopePtr func; ClassScopePtr cls; if (m_className.empty()) { func = ar->findFunction(m_name); } else { cls = ar->resolveClass(m_className); if (cls) { if (m_name == "__construct") { func = cls->findConstructor(ar, true); } else { func = cls->findFunction(ar, m_name, true, true); } } } if (func && !func->isRedeclaring()) { if (m_funcScope != func) { m_funcScope = func; Construct::recomputeEffects(); } } if (cls && !cls->isRedeclaring()) m_classScope = cls; } // check for dynamic constant and volatile function/class if (m_className.empty() && (m_type == DefinedFunction || m_type == FunctionExistsFunction || m_type == ClassExistsFunction || m_type == InterfaceExistsFunction) && m_params && m_params->getCount() >= 1) { ExpressionPtr value = (*m_params)[0]; if (value->isScalar()) { ScalarExpressionPtr name = dynamic_pointer_cast<ScalarExpression>(value); if (name && name->isLiteralString()) { string symbol = name->getLiteralString(); switch (m_type) { case DefinedFunction: { ConstantTablePtr constants = ar->getConstants(); if (!constants->isPresent(symbol)) { // user constant BlockScopePtr block = ar->findConstantDeclarer(symbol); if (block) { // found the constant constants = block->getConstants(); // set to be dynamic constants->setDynamic(ar, symbol); } } break; } case FunctionExistsFunction: { FunctionScopePtr func = ar->findFunction(Util::toLower(symbol)); if (func && func->isUserFunction()) { func->setVolatile(); } break; } case InterfaceExistsFunction: case ClassExistsFunction: { ClassScopePtr cls = ar->findClass(Util::toLower(symbol)); if (cls && cls->isUserClass()) { cls->setVolatile(); } break; } default: ASSERT(false); } } } } } if (m_params) { if (ar->getPhase() == AnalysisResult::AnalyzeAll) { if (m_funcScope) { ExpressionList ¶ms = *m_params; int mpc = m_funcScope->getMaxParamCount(); for (int i = params.getCount(); i--; ) { ExpressionPtr p = params[i]; if (i < mpc ? m_funcScope->isRefParam(i) : m_funcScope->isReferenceVariableArgument()) { p->setContext(Expression::RefValue); } else if (!(p->getContext() & Expression::RefParameter)) { p->clearContext(Expression::RefValue); } } } else { FunctionScopePtr func = ar->findFunction(m_name); if (func && func->isRedeclaring()) { FunctionScope::RefParamInfoPtr info = FunctionScope::GetRefParamInfo(m_name); if (info) { for (int i = m_params->getCount(); i--; ) { if (info->isRefParam(i)) { m_params->markParam(i, canInvokeFewArgs()); } } } } else { m_params->markParams(false); } } } m_params->analyzeProgram(ar); } }
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp, ExpressionListPtr params, bool &valid) { if (!params) { if (m_minParam > 0) { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt); } valid = false; setDynamic(); } return 0; } int ret = 0; if (params->getCount() < m_minParam) { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt); } valid = false; setDynamic(); } if (params->getCount() > m_maxParam) { if (isVariableArgument()) { ret = params->getCount() - m_maxParam; } else { if (ar->isFirstPass()) { ar->getCodeError()->record(CodeError::TooManyArgument, exp, m_stmt); } valid = false; setDynamic(); } } bool canSetParamType = isUserFunction() && !m_overriding; for (int i = 0; i < params->getCount(); i++) { ExpressionPtr param = (*params)[i]; if (valid && param->hasContext(Expression::InvokeArgument)) { param->clearContext(Expression::InvokeArgument); param->clearContext(Expression::RefValue); param->clearContext(Expression::NoRefWrapper); } TypePtr expType; if (!canSetParamType && i < m_maxParam) { expType = param->inferAndCheck(ar, getParamType(i), false); } else { expType = param->inferAndCheck(ar, NEW_TYPE(Some), false); } 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); } if (i < m_maxParam) { if (m_paramTypeSpecs[i] && ar->isFirstPass()) { if (!Type::Inferred(ar, expType, m_paramTypeSpecs[i])) { const char *file = m_stmt->getLocation()->file; Logger::Error("%s: parameter %d of %s requires %s, called with %s", file, i, m_name.c_str(), m_paramTypeSpecs[i]->toString().c_str(), expType->toString().c_str()); ar->getCodeError()->record(CodeError::BadArgumentType, m_stmt); } } TypePtr paramType = getParamType(i); if (canSetParamType) { paramType = setParamType(ar, i, expType); } 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; }
void ExprDict::updateAccess(ExpressionPtr e) { int cls = e->getExprClass(); int eid = e->getCanonID(); e->clearAnticipated(); e->clearAvailable(); // bail on non-canonical expressions if (!isCanonicalStructure(eid)) { // but record we saw a type assertion belonging to this block m_avlTypeAsserts.push_back(eid); return; } if (m_anticipated && (cls & Expression::Update ? !BitOps::get_bit(eid, m_altered) : !e->getLocalEffects())) { /* Anticipated can be computed bottom up as we go. But note that we only know altered for Load/Store expressions. */ int i = e->getKidCount(); while (true) { if (!i--) { e->setAnticipated(); if (!e->hasContext(Expression::AssignmentLHS)) { setStructureOps(eid, m_anticipated, true); } break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAnticipated()) { break; } } } } if (m_available) { /* Available has to be computed optimistically, because we dont yet know what is going to be altered between here and the end of the block So keep a list of the potentially-available accesses (avlAccess), and for each id, the last potentially-available expression (avlExpr). For each modifying expression that we process, we remove expressions from avlAccess, and at the end, we build up the available expressions bottom up. */ if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { bool isLoad; int depth = 0, effects = 0; for (int i = 0, n = m_avlAccess.size(); i < n; ) { ExpressionRawPtr a = m_avlAccess[i]; if (m_am.checkAnyInterf(e, a, isLoad, depth, effects) != AliasManager::DisjointAccess) { int aid = a->getCanonID(); assert(isCanonicalStructure(aid)); if (eid != aid || cls == Expression::Load) { BitOps::set_bit(aid, m_altered, true); } if (!(cls & Expression::Store) || a != e->getStoreVariable()) { a->clearAvailable(); m_avlAccess[i] = m_avlAccess[--n]; m_avlAccess.resize(n); continue; } } i++; } } if (cls & Expression::Update || !e->getContainedEffects()) { int i = e->getKidCount(); while (true) { if (!i--) { e->setAvailable(); if (cls & Expression::Update) { m_avlAccess.push_back(e); } m_avlExpr[eid] = e; break; } if (ExpressionPtr k = e->getNthExpr(i)) { if (!isCanonicalStructure(k->getCanonID())) continue; if (!k->isAvailable()) { break; } } } } } if ((cls & (Expression::Store|Expression::Call)) || (cls & Expression::Load && e->getContext() & (Expression::LValue| Expression::RefValue| Expression::UnsetContext| Expression::DeepReference))) { ExpressionPtr cur = m_active, prev; bool isLoad; int depth = 0, effects = 0; while (cur) { ExpressionPtr next = cur->getCanonLVal(); int cid = cur->getCanonID(); assert(isCanonicalStructure(cid)); if ((cid != eid || cls == Expression::Load) && (BitOps::get_bit(cid, m_altered) || m_am.checkAnyInterf(e, cur, isLoad, depth, effects) != AliasManager::DisjointAccess)) { BitOps::set_bit(cid, m_altered, true); if (!prev) { m_active = next; } else { prev->setCanonPtr(next); } } else { prev = cur; } cur = next; } } }
void RefDict::updateAccess(ExpressionPtr e) { always_assert(!e->getScope()->inPseudoMain()); int eid = e->getCanonID(); int context = e->getContext(); if (first_pass) { if (!e->is(Expression::KindOfSimpleVariable) && !e->is(Expression::KindOfDynamicVariable)) return; e->clearAvailable(); e->clearReferencedValid(); e->clearReferenced(); SimpleVariablePtr ptr(dynamic_pointer_cast<SimpleVariable>(e)); if (ptr && (ptr->isSuperGlobal() || ptr->isThis())) return; if (e->is(Expression::KindOfSimpleVariable)) { if (BitOps::get_bit(eid, m_referenced)) { e->setReferenced(); } else if (!BitOps::get_bit(eid, m_killed)) { // use as a temp place holder e->setAvailable(); } } } // let the first pass information propagate for both passes, since // we need it in both contexts if (context & Expression::RefAssignmentLHS || context & Expression::RefValue || context & Expression::RefParameter || ((context & Expression::Declaration) == Expression::Declaration)) { if (e->is(Expression::KindOfSimpleVariable)) { BitOps::set_bit(eid, m_referenced, true); BitOps::set_bit(eid, m_killed, false); } else { // for dynamic variables, we must assume the worst BitOps::set(size(), m_referenced, -1); BitOps::set(size(), m_killed, 0); } } else if (e->is(Expression::KindOfSimpleVariable) && context & Expression::LValue && context & Expression::UnsetContext) { BitOps::set_bit(eid, m_referenced, false); BitOps::set_bit(eid, m_killed, true); } if (first_pass) return; // now we're on the second pass if (context & Expression::AssignmentLHS || context & Expression::OprLValue) { // we dealt with this node as a store expression return; } int cls = e->getExprClass(); bool isRhsNeeded = false; bool canKill = false; ExpressionPtr lhs; ExpressionPtr rhs; if (cls & Expression::Store) { // we care about two cases here switch (e->getKindOf()) { case Expression::KindOfAssignmentExpression: // $x = ... { AssignmentExpressionPtr assign( static_pointer_cast<AssignmentExpression>(e)); lhs = assign->getVariable(); rhs = assign->getValue(); isRhsNeeded = Expression::CheckNeededRHS(rhs); canKill = true; } break; case Expression::KindOfBinaryOpExpression: // $x += ... { BinaryOpExpressionPtr binop( static_pointer_cast<BinaryOpExpression>(e)); if (binop->getOp() == T_PLUS_EQUAL) { lhs = binop->getExp1(); rhs = binop->getExp2(); isRhsNeeded = Expression::CheckNeededRHS(rhs); } } break; default: break; } } bool isLhsSimpleVar = false; bool isLhsDynamic = false; bool isRefd = false; if (lhs) { isLhsSimpleVar = lhs->is(Expression::KindOfSimpleVariable); // TODO: can a variable only be simple or dynamic? // If so, this is un-necessary isLhsDynamic = lhs->is(Expression::KindOfDynamicVariable); if (isLhsSimpleVar) { // clean up the LHS AST lhs->clearAvailable(); lhs->clearNeeded(); lhs->clearNeededValid(); if (BitOps::get_bit(lhs->getCanonID(), m_obj)) { lhs->setNeeded(); lhs->setNeededValid(); } else if (!BitOps::get_bit(lhs->getCanonID(), m_noobj)) { lhs->setAvailable(); } } if (lhs->isReferencedValid() && lhs->isReferenced() && isRhsNeeded) { // could we possibly have modified another referenced variable? isRefd = true; } if (isLhsSimpleVar && isRhsNeeded) { // we see this case: // $x = new ... // so we mark $x as being needed BitOps::set_bit(lhs->getCanonID(), m_obj, true); BitOps::set_bit(lhs->getCanonID(), m_noobj, false); } else if (isLhsSimpleVar && canKill && !isRhsNeeded) { // we saw an assignment that was of the form // $x = <primitive> // we can now set $x to be not an object BitOps::set_bit(lhs->getCanonID(), m_obj, false); BitOps::set_bit(lhs->getCanonID(), m_noobj, true); } } if (isLhsDynamic && isRhsNeeded) { // in this case, we must set EVERY variable to contain an object BitOps::set(size(), m_obj, -1 /* true for each bit */); BitOps::set(size(), m_noobj, 0 /* false for each bit */); // we're done, since it can be no worse (no more conservative) than this return; } // do we see a load which could cause the value of this expr to be changed? // for example: // function foo(&$x) { $x = 10; } // $x = 30; // foo($x); /* <-- this is what we care about */ if ((cls & (Expression::Load|Expression::Call)) && (context & (Expression::RefValue|Expression::DeepReference))) { isRefd = true; } // we want to propagate this information to other simple vars we see if (e->is(Expression::KindOfSimpleVariable)) { // clean up the AST e->clearAvailable(); e->clearNeeded(); e->clearNeededValid(); SimpleVariablePtr svp(static_pointer_cast<SimpleVariable>(e)); if (svp->isSuperGlobal() || svp->isThis()) return; // update the AST to *before* the modification if (BitOps::get_bit(eid, m_obj)) { e->setNeeded(); e->setNeededValid(); } else if (!BitOps::get_bit(eid, m_noobj)) { // use as a temp place holder e->setAvailable(); } if (context & Expression::LValue && context & Expression::UnsetContext) { always_assert(!isRefd); // unset($x); BitOps::set_bit(eid, m_obj, false); BitOps::set_bit(eid, m_noobj, true); } else if (isRefd || ((context & Expression::Declaration) == Expression::Declaration)) { // if a simple variable has isRefd, then we need to mark it // as potentially containing an object. // also, if the simple variable is in global context // then we also mark it as potentially containing an object BitOps::set_bit(eid, m_obj, true); BitOps::set_bit(eid, m_noobj, false); } } if (isRefd) { // do a scan for every simple variable referenced value // in the dictionary and mark it as potentially // containing an object (in the bit vector) for (int i = size(); i--; ) { if (ExpressionPtr e = get(i)) { always_assert(e->is(Expression::KindOfSimpleVariable)); always_assert(((unsigned int)i) == e->getCanonID()); if (BitOps::get_bit(i, m_referenced)) { BitOps::set_bit(i, m_obj, true); BitOps::set_bit(i, m_noobj, false); } } } } }
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; }
void AliasManager::collectAliasInfoRecur(ConstructPtr cs) { if (!cs) { return; } if (StatementPtr s = dpc(Statement, cs)) { switch (s->getKindOf()) { case Statement::KindOfFunctionStatement: case Statement::KindOfMethodStatement: case Statement::KindOfClassStatement: case Statement::KindOfInterfaceStatement: return; default: break; } } int nkid = cs->getKidCount(); for (int i = 0; i < nkid; i++) { ConstructPtr kid = cs->getNthKid(i); if (kid) { collectAliasInfoRecur(kid); } } if (ExpressionPtr e = dpc(Expression, cs)) { int context = e->getContext(); switch (e->getKindOf()) { case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae = spc(AssignmentExpression, e); ExpressionPtr var = ae->getVariable(); ExpressionPtr val = ae->getValue(); if (var->is(Expression::KindOfSimpleVariable)) { const std::string &name = spc(SimpleVariable, var)->getName(); AliasInfo &ai = m_aliasInfo[name]; if (val->getContext() & Expression::RefValue) { ai.setIsRefTo(); m_variables->addUsed(name); } else { Expression::checkUsed(m_arp, var, val); } } } break; case Expression::KindOfListAssignment: { ListAssignmentPtr la = spc(ListAssignment, e); ExpressionListPtr vars = la->getVariables(); for (int i = vars->getCount(); i--; ) { ExpressionPtr v = (*vars)[i]; if (v && v->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr sv = spc(SimpleVariable, v); m_variables->addUsed(sv->getName()); } } } break; case Expression::KindOfSimpleVariable: { const std::string &name = spc(SimpleVariable, e)->getName(); if (context & Expression::RefValue) { AliasInfo &ai = m_aliasInfo[name]; ai.addRefLevel(0); } if (!(context & (Expression::AssignmentLHS | Expression::UnsetContext))) { m_variables->addUsed(name); } } break; case Expression::KindOfDynamicVariable: if (context & Expression::RefValue) { m_wildRefs = true; } break; case Expression::KindOfArrayElementExpression: { int n = 1; while (n < 10 && e->is(Expression::KindOfArrayElementExpression)) { e = spc(ArrayElementExpression, e)->getVariable(); } if (e->is(Expression::KindOfSimpleVariable)) { const std::string &name = spc(SimpleVariable, e)->getName(); if (context & Expression::RefValue) { AliasInfo &ai = m_aliasInfo[name]; ai.addRefLevel(n); } m_variables->addUsed(name); // need this for UnsetContext } } break; case Expression::KindOfObjectPropertyExpression: { e = spc(ObjectPropertyExpression, e)->getObject(); if (e->is(Expression::KindOfSimpleVariable)) { const std::string &name = spc(SimpleVariable, e)->getName(); if (context & Expression::RefValue) { AliasInfo &ai = m_aliasInfo[name]; ai.addRefLevel(1); } m_variables->addUsed(name); // need this for UnsetContext } } break; default: break; } } else { StatementPtr s = spc(Statement, cs); switch (s->getKindOf()) { case Statement::KindOfCatchStatement: { const std::string &name = spc(CatchStatement, s)->getVariable(); m_variables->addUsed(name); break; } default: break; } } }
ExpressionPtr AliasManager::canonicalizeNode(ExpressionPtr e) { e->setCanonPtr(ExpressionPtr()); e->setCanonID(0); switch (e->getKindOf()) { case Expression::KindOfObjectMethodExpression: case Expression::KindOfDynamicFunctionCall: case Expression::KindOfSimpleFunctionCall: case Expression::KindOfNewObjectExpression: add(m_bucketMap[0], e); break; case Expression::KindOfListAssignment: add(m_bucketMap[0], e); break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr ae = spc(AssignmentExpression,e); if (e->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); return ae->getValue(); } ExpressionPtr rep; int interf = findInterf(ae->getVariable(), false, rep); if (interf == SameAccess) { switch (rep->getKindOf()) { default: break; case Expression::KindOfAssignmentExpression: { AssignmentExpressionPtr a = spc(AssignmentExpression, rep); ExpressionPtr value = a->getValue(); if (a->getValue()->getContext() & Expression::RefValue) { break; } } case Expression::KindOfUnaryOpExpression: case Expression::KindOfBinaryOpExpression: rep->setContext(Expression::DeadStore); break; } } add(m_bucketMap[0], e); break; } case Expression::KindOfConstantExpression: case Expression::KindOfSimpleVariable: case Expression::KindOfDynamicVariable: case Expression::KindOfArrayElementExpression: case Expression::KindOfObjectPropertyExpression: case Expression::KindOfStaticMemberExpression: if (!(e->getContext() & (Expression::AssignmentLHS| Expression::DeepAssignmentLHS| Expression::OprLValue))) { if (!(e->getContext() & (Expression::LValue| Expression::RefValue| Expression::RefParameter| Expression::UnsetContext))) { ExpressionPtr rep; int interf = findInterf(e, true, rep); if (interf == SameAccess) { if (rep->getKindOf() == e->getKindOf()) { e->setCanonID(rep->getCanonID()); e->setCanonPtr(rep); return ExpressionPtr(); } if (rep->getKindOf() == Expression::KindOfAssignmentExpression) { ExpressionPtr rhs = spc(AssignmentExpression,rep)->getValue(); if (rhs->is(Expression::KindOfScalarExpression)) { rhs = rhs->clone(); getCanonical(rhs); return rhs; } e->setCanonPtr(rhs); } } } add(m_bucketMap[0], e); } else { getCanonical(e); } break; case Expression::KindOfBinaryOpExpression: { BinaryOpExpressionPtr bop = spc(BinaryOpExpression, e); int rop = getOpForAssignmentOp(bop->getOp()); if (rop) { ExpressionPtr lhs = bop->getExp1(); ExpressionPtr rep; if (bop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr rhs = bop->getExp2()->clone(); lhs = lhs->clone(); lhs->clearContext(Expression::LValue); lhs->clearContext(Expression::NoLValueWrapper); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, lhs, rhs, rop)); } else { ExpressionPtr alt; int interf = findInterf(lhs, true, alt); if (interf == SameAccess && alt->is(Expression::KindOfAssignmentExpression)) { ExpressionPtr op0 = spc(AssignmentExpression,alt)->getValue(); if (op0->is(Expression::KindOfScalarExpression)) { ExpressionPtr op1 = bop->getExp2(); ExpressionPtr rhs (new BinaryOpExpression(e->getLocation(), Expression::KindOfBinaryOpExpression, op0->clone(), op1->clone(), rop)); lhs = lhs->clone(); lhs->clearContext(Expression::OprLValue); rep = ExpressionPtr (new AssignmentExpression(e->getLocation(), Expression::KindOfAssignmentExpression, lhs, rhs, false)); } } } if (rep) { ExpressionPtr c = canonicalizeRecur(rep); return c ? c : rep; } add(m_bucketMap[0], e); } else { getCanonical(e); } break; } case Expression::KindOfUnaryOpExpression: { UnaryOpExpressionPtr uop = spc(UnaryOpExpression, e); switch (uop->getOp()) { case T_INC: case T_DEC: if (uop->getContext() & Expression::DeadStore) { Construct::recomputeEffects(); ExpressionPtr val = uop->getExpression()->clone(); val->clearContext(Expression::LValue); val->clearContext(Expression::NoLValueWrapper); val->clearContext(Expression::OprLValue); if (uop->getFront()) { ExpressionPtr inc (new ScalarExpression(uop->getLocation(), Expression::KindOfScalarExpression, T_LNUMBER, string("1"))); val = ExpressionPtr (new BinaryOpExpression(uop->getLocation(), Expression::KindOfBinaryOpExpression, val, inc, uop->getOp() == T_INC ? '+' : '-')); } ExpressionPtr r = canonicalizeRecur(val); return r ? r : val; } add(m_bucketMap[0], e); break; default: getCanonical(e); break; } break; } default: getCanonical(e); break; } return ExpressionPtr(); }