2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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 +----------------------------------------------------------------------+
21 #include "hphp/hhbbc/misc.h"
22 #include "hphp/hhbbc/representation.h"
23 #include "hphp/hhbbc/interp-state.h"
24 #include "hphp/hhbbc/interp.h"
25 #include "hphp/hhbbc/index.h"
26 #include "hphp/hhbbc/type-structure.h"
27 #include "hphp/hhbbc/type-system.h"
28 #include "hphp/hhbbc/context.h"
30 namespace HPHP::HHBBC
{
32 //////////////////////////////////////////////////////////////////////
35 * The result of a function-at-a-time type analysis, as needed for
36 * updating the index. Does not include per-block state.
38 struct FuncAnalysisResult
{
40 * Initializes this structure so rpoBlocks contains the func's
41 * blocks according to rpoSortAddDVs(), each bdata entry has an
42 * rpoId index, and all block states are uninitialized.
44 explicit FuncAnalysisResult(AnalysisContext
);
46 FuncAnalysisResult(FuncAnalysisResult
&&) = default;
47 FuncAnalysisResult
& operator=(FuncAnalysisResult
&&) = default;
50 * FuncAnalysis carries the Context it was created for because
51 * generally you'll need it again when you look at the analysis
54 * Note that the Context is adjusted to account for the fact that
55 * Closure __invoke methods run in the context of a class other than
56 * their declaring class. So ctx.func->cls will not be the same as
57 * ctx->cls in this case.
62 * If this function allocates closures, this maps each of those
63 * closure classes to the types of its used variables, in their
66 ClosureUseVarMap closureUseTypes
;
69 * The inferred function return type. May be TBottom if the
70 * function never returns.
75 * The number of times that inferredReturn was refined inside
76 * analyze_class. We need this to track accurately the total number
79 size_t localReturnRefinements
= 0;
82 * If the function returns one of its parameters, the index of that
83 * parameter. MaxLocalId and above indicate that it doesn't return a
86 LocalId retParam
{MaxLocalId
};
89 * Flag to indicate that the function is effectFree, in the sense
90 * that calls to it can be constant folded or dced (note that calls
91 * are never truly effect free, because profilers could be enabled,
92 * or other surprise flags could fire - but we ignore that for this
95 bool effectFree
{false};
98 * Flag to indicate whether the analysis should be repeated after the
99 * function's bytecode was updated. The analysis must have made progress
100 * irreversibly reducing the bytecode.
102 * Used to infer the effectFree flag after reduction of MemoGet to a constant.
104 bool reanalyzeOnUpdate
{false};
107 * Flag to indicate that an iterator's base was unchanged on at least one path
108 * to that iterator's release. If this is false, we can skip doing the more
109 * expensive Iter local optimization pass (because it will never succeed).
111 bool hasInvariantIterBase
{false};
114 * A set of call contexts that we failed to fold.
116 hphp_fast_set
<CallContext
, CallContextHasher
> unfoldableFuncs
;
119 * Bitset representing which parameters may affect the result of the
120 * function, assuming it produces one. Note that the parameter type
121 * verification does not count as a use in this context.
123 std::bitset
<64> usedParams
;
126 * For an 86cinit, any constants that we inferred a type for. For
127 * 86pinit or 86sinit, any resolved initial values for
128 * properties. These two cases are disjoint, so we save memory by
129 * using a variant type.
131 UniqueEither
<ResolvedConstants
*, ResolvedPropInits
*> resolvedInitializers
;
134 * Public static property mutations in this function.
136 PublicSPropMutations publicSPropMutations
;
139 * Vector of block updates
141 CompactVector
<std::pair
<BlockId
, CompressedBlockUpdate
>> blockUpdates
;
144 struct FuncAnalysis
: FuncAnalysisResult
{
151 explicit FuncAnalysis(AnalysisContext
);
153 FuncAnalysis(FuncAnalysis
&&) = default;
154 FuncAnalysis
& operator=(FuncAnalysis
&&) = default;
156 // Block ids in a reverse post order, with DV initializers.
157 std::vector
<BlockId
> rpoBlocks
;
159 // Block data is indexed by Block::id.
160 std::vector
<BlockData
> bdata
;
164 * Store the worklist of functions to be analyzed during
165 * class-at-a-time analysis (along with intra-class dependencies).
167 struct ClassAnalysisWorklist
{
168 const php::Func
* next();
170 void scheduleForProp(SString name
);
171 void scheduleForPropMutate(SString name
);
172 void scheduleForReturnType(const php::Func
& callee
);
174 void addPropDep(SString name
, const php::Func
& f
) {
175 propDeps
[name
].emplace(&f
);
177 void addPropMutateDep(SString name
, const php::Func
& f
) {
178 propMutateDeps
[name
].emplace(&f
);
180 void addReturnTypeDep(const php::Func
& callee
, const php::Func
& f
) {
181 returnTypeDeps
[&callee
].emplace(&f
);
184 bool empty() const { return worklist
.empty(); }
186 // Put a func on the worklist. Return true if the func was
187 // scheduled, false if it was already on the list.
188 bool schedule(const php::Func
& f
);
190 const hphp_fast_set
<const php::Func
*>*
191 depsForReturnType(const php::Func
& f
) const {
192 auto const it
= returnTypeDeps
.find(&f
);
193 if (it
== returnTypeDeps
.end()) return nullptr;
198 hphp_fast_set
<const php::Func
*> inWorklist
;
199 std::deque
<const php::Func
*> worklist
;
201 template <typename T
> using Deps
=
202 hphp_fast_map
<T
, hphp_fast_set
<const php::Func
*>>;
203 Deps
<SString
> propDeps
;
204 Deps
<SString
> propMutateDeps
;
205 Deps
<const php::Func
*> returnTypeDeps
;
208 struct ClassAnalysisWork
{
209 ClassAnalysisWorklist worklist
;
210 hphp_fast_map
<const php::Func
*, Index::ReturnType
> returnTypes
;
211 hphp_fast_map
<const php::Func
*, hphp_fast_set
<SString
>> propMutators
;
212 bool noPropRefine
= false;
216 * The result of a class-at-a-time analysis.
218 struct ClassAnalysis
{
219 explicit ClassAnalysis(const Context
& ctx
) :
222 ClassAnalysis(ClassAnalysis
&&) = default;
223 ClassAnalysis
& operator=(ClassAnalysis
&&) = default;
225 // The context that describes the class we did this analysis for.
228 // FuncAnalysis results for each of the methods on the class, and
229 // for each closure allocated in the class's context.
230 CompactVector
<FuncAnalysisResult
> methods
;
231 CompactVector
<FuncAnalysisResult
> closures
;
233 // Inferred types for private instance and static properties.
234 PropState privateProperties
;
235 PropState privateStatics
;
237 ResolvedPropInits resolvedProps
;
238 ResolvedClsTypeConsts resolvedTypeConsts
;
240 ClassAnalysisWork
* work
{nullptr};
243 //////////////////////////////////////////////////////////////////////
245 struct UnitAnalysis
{
246 explicit UnitAnalysis(const Context
& ctx
) : ctx
{ctx
} {}
248 UnitAnalysis(UnitAnalysis
&&) = default;
249 UnitAnalysis
& operator=(UnitAnalysis
&&) = default;
253 ResolvedTypeAliases resolvedTypeAliases
;
256 //////////////////////////////////////////////////////////////////////
258 // Local state for resolving class constants and reaching a fixed
260 struct ClsConstantWork
{
261 ClsConstantWork(const IIndex
&, const php::Class
&);
263 ClsConstLookupResult
lookup(SString
);
265 void update(SString
, Type
);
269 void setCurrent(SString n
) {
273 void clearCurrent(SString n
) {
274 assertx(current
== n
);
278 void schedule(SString
);
280 const php::Class
& cls
;
281 hphp_fast_map
<SString
, ClsConstInfo
> constants
;
283 SString current
{nullptr};
284 hphp_fast_map
<SString
, hphp_fast_set
<SString
>> deps
;
285 hphp_fast_set
<SString
> inWorklist
;
286 std::deque
<SString
> worklist
;
289 //////////////////////////////////////////////////////////////////////
292 * Perform a flow-sensitive type analysis on a function, using the
293 * given Index and Context when we need information about things
294 * outside of this function.
296 * This routine makes no changes to the php::Func.
298 FuncAnalysis
analyze_func(const IIndex
&, const AnalysisContext
&,
299 CollectionOpts opts
);
302 * Perform a flow-sensitive type analysis on a function, using the
303 * given Index and Context when we need information about things
304 * outside of this function, and assuming that the arguments to the
305 * function have the supplied types.
307 * This function is used to perform callsite-sensitive type inference.
309 * Currently this is not supported for closure bodies.
311 FuncAnalysis
analyze_func_inline(const IIndex
&,
312 const AnalysisContext
&,
313 const Type
& thisType
,
314 const CompactVector
<Type
>& args
,
315 ClsConstantWork
* clsCnsWork
= nullptr,
316 CollectionOpts opts
= {});
319 * Perform an analysis for a whole php::Class at a time.
321 * This involves doing a analyze_func call on each of its functions,
322 * and inferring some whole-class information at the same time.
324 ClassAnalysis
analyze_class(const IIndex
&, const Context
&);
327 * Perform an analysis of all the class constants for a php::Class
328 * (both declared and inherited).
330 ClassAnalysis
analyze_class_constants(const IIndex
&, const Context
&);
332 //////////////////////////////////////////////////////////////////////
335 * Perform an analysis for a php::Unit. This does not analyze any of
336 * the classes or funcs contained within the unit, that must be done
337 * explicitly. It analyzes type-aliases defined within the unit.
339 UnitAnalysis
analyze_unit(const AnalysisIndex
& index
, const Context
& ctx
);
341 //////////////////////////////////////////////////////////////////////
344 * Represents the various interp state at some particular instruction
345 * within a block, with an ability to "rewind" the state to previous
346 * instructions (up until the entry of the block).
348 struct PropagatedStates
{
349 // Initialize with the end of block state, and the gathered undo log
350 // corresponding to that block interp.
351 PropagatedStates(State
&&, StateMutationUndo
);
353 // Rewind the state one instruction. Must not be at the beginning of
357 // Stack and local types:
358 const CompactVector
<Type
>& stack() const { return m_stack
; }
359 const CompactVector
<Type
>& locals() const { return m_locals
; }
361 // The type for a particular local *after* the current instruction
363 const Type
& localAfter(LocalId id
) const {
364 for (auto const& p
: m_afterLocals
) {
365 if (p
.first
== id
) return p
.second
;
367 assertx(id
< m_locals
.size());
371 // The value pushed by current instruction (IE, the top of the stack
373 const Optional
<Type
>& lastPush() const { return m_lastPush
; }
375 // Interp flags for the current instruction.
376 bool wasPEI() const { return currentMark().wasPEI
; }
377 bool unreachable() const { return currentMark().unreachable
; }
378 auto const& mayReadLocalSet() const { return currentMark().mayReadLocalSet
; }
381 const StateMutationUndo::Mark
& currentMark() const {
382 assertx(!m_undos
.events
.empty());
384 boost::get
<StateMutationUndo::Mark
>(&m_undos
.events
.back());
389 Optional
<Type
> m_lastPush
;
390 CompactVector
<Type
> m_stack
;
391 CompactVector
<Type
> m_locals
;
392 CompactVector
<std::pair
<LocalId
, Type
>> m_afterLocals
;
393 StateMutationUndo m_undos
;
397 * Interpret a block and return a PropagatedStates corresponding to
398 * the state at the end of the block.
400 * Pre: stateIn.initialized == true
403 locally_propagated_states(const Index
&,
404 const AnalysisContext
&,
405 CollectedInfo
& collect
,
410 * Propagate a block input State to find the output state for a particular
411 * target of the block. This is used to update the in state for a block added
412 * to the CFG in between analysis rounds.
414 State
locally_propagated_bid_state(const Index
& index
,
415 const AnalysisContext
& ctx
,
416 CollectedInfo
& collect
,
421 //////////////////////////////////////////////////////////////////////
424 * Resolve a type-constraint into its equivalent set of HHBBC types.
426 * In general, a type-constraint cannot be represented exactly by a
427 * single HHBBC type, so a lower and upper bound is provided
430 * A "candidate" type can be provided which will be applied to the
431 * type-constraint and can further constrain the output types. This
432 * is useful for the magic interfaces, whose lower-bound cannot be
433 * precisely represented by a single type.
435 struct ConstraintType
{
436 // Lower bound of constraint. Any type which is a subtype of this
437 // is guaranteed to pass a type-check without any side-effects.
439 // Upper bound of constraint. Any type which does not intersect
440 // with this is guaranteed to always fail a type-check.
442 // If this type-constraint might promote a "classish" type to a
443 // static string as a side-effect.
444 TriBool coerceClassToString
{TriBool::No
};
445 // Whether this type-constraint might map to a mixed
446 // type-hint. The mixed type-hint has special semantics when it
447 // comes to properties.
448 bool maybeMixed
{false};
450 ConstraintType
lookup_constraint(const IIndex
&,
452 const TypeConstraint
&,
453 const Type
& candidate
= TCell
);
456 * Returns a tuple containing a type after the parameter type
457 * verification, a flag indicating whether the verification is a no-op
458 * (because it always passes without any conversion), and a flag
459 * indicating whether the verification is effect free (the
460 * verification could convert a type without causing a side-effect).
462 std::tuple
<Type
, bool, bool> verify_param_type(const IIndex
&,
468 * Given a type, adjust the type for the given type-constraint. If there's no
469 * type-constraint, or if property type-hints aren't being enforced, then return
470 * the type as is. This might return TBottom if the type is not compatible with
473 Type
adjust_type_for_prop(const IIndex
& index
,
474 const php::Class
& propCls
,
475 const TypeConstraint
* tc
,
479 * Resolve a type-constraint to its equivalent
480 * ConstraintType. Candidate can be used to narrow the type in some
481 * situations. The std::functions are called when classes need to be
482 * resolved, or for "this" type-hints.
485 type_from_constraint(const TypeConstraint
& tc
,
486 const Type
& candidate
,
487 const std::function
<Optional
<res::Class
>(SString
)>& resolve
,
488 const std::function
<Optional
<Type
>()>& self
);
491 * Given two ConstraintTypes, compute and return the union.
493 ConstraintType
union_constraint(const ConstraintType
& a
,
494 const ConstraintType
& b
);
495 std::string
show(const ConstraintType
&);
497 //////////////////////////////////////////////////////////////////////
500 * Try to resolve self/parent types in the given context.
502 Optional
<Type
> selfCls(const IIndex
&, const Context
&);
503 Optional
<Type
> selfClsExact(const IIndex
&, const Context
&);
505 Optional
<Type
> parentCls(const IIndex
&, const Context
&);
506 Optional
<Type
> parentClsExact(const IIndex
&, const Context
&);
508 //////////////////////////////////////////////////////////////////////
511 * Resolve a builtin class with the given name. This just calls
512 * index.resolve_class(), but has some additional sanity checks that
513 * the resultant class is a builtin.
515 res::Class
builtin_class(const IIndex
&, SString
);
517 //////////////////////////////////////////////////////////////////////