/* * Helper for do_analyze to initialize the states for all function entries * (i.e. each dv init and the main entry), and all of them count as places the * function could be entered, so they all must be visited at least once. * * If we're entering at a DV-init, all higher parameter locals must be * Uninit, with the possible exception of a final variadic param * (which will be an array). It is also possible that the DV-init is * reachable from within the function with these parameter locals * already initialized (although the normal php emitter can't do * this), but that case will be discovered when iterating. */ dataflow_worklist<uint32_t> prepare_incompleteQ(const Index& index, FuncAnalysis& ai, ClassAnalysis* clsAnalysis, const std::vector<Type>* knownArgs) { auto incompleteQ = dataflow_worklist<uint32_t>(ai.rpoBlocks.size()); auto const ctx = ai.ctx; auto const numParams = ctx.func->params.size(); auto const entryState = [&] { if (!is_pseudomain(ctx.func)) { return entry_state(index, ctx, clsAnalysis, knownArgs); } assert(!knownArgs && !clsAnalysis); assert(numParams == 0); return pseudomain_entry_state(ctx.func); }(); if (knownArgs) { // When we have known args, we only need to add one of the entry points to // the initial state, since we know how many arguments were passed. auto const useDvInit = [&] { if (knownArgs->size() >= numParams) return false; for (auto i = knownArgs->size(); i < numParams; ++i) { auto const dv = ctx.func->params[i].dvEntryPoint; if (dv != NoBlockId) { ai.bdata[dv].stateIn = entryState; incompleteQ.push(rpoId(ai, dv)); return true; } } return false; }(); if (!useDvInit) { ai.bdata[ctx.func->mainEntry].stateIn = entryState; incompleteQ.push(rpoId(ai, ctx.func->mainEntry)); } return incompleteQ; } for (auto paramId = uint32_t{0}; paramId < numParams; ++paramId) { auto const dv = ctx.func->params[paramId].dvEntryPoint; if (dv != NoBlockId) { ai.bdata[dv].stateIn = entryState; incompleteQ.push(rpoId(ai, dv)); for (auto locId = paramId; locId < numParams; ++locId) { ai.bdata[dv].stateIn.locals[locId] = ctx.func->params[locId].isVariadic ? TVArr : TUninit; } } } ai.bdata[ctx.func->mainEntry].stateIn = entryState; incompleteQ.push(rpoId(ai, ctx.func->mainEntry)); return incompleteQ; }
bool is_volatile_local(borrowed_ptr<const php::Func> func, borrowed_ptr<const php::Local> l) { if (is_pseudomain(func)) return true; // Note: unnamed locals in a pseudomain probably are safe (i.e. can't be // changed through $GLOBALS), but for now we don't bother. if (!l->name) return false; return l->name->same(s_http_response_header.get()) || l->name->same(s_php_errormsg.get()); }
bool is_volatile_local(const php::Func* func, LocalId lid) { auto const& l = func->locals[lid]; if (!l.name) return false; // Named pseudomain locals are bound to $GLOBALS. if (is_pseudomain(func)) return true; return (RuntimeOption::EnableArgsInBacktraces && l.name->same(s_reified_generics_var.get())) || l.name->same(s_86metadata.get()); }