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 +----------------------------------------------------------------------+
24 #include <boost/variant.hpp>
25 #include <tbb/concurrent_hash_map.h>
27 #include <folly/Hash.h>
29 #include "hphp/util/compact-vector.h"
30 #include "hphp/util/either.h"
31 #include "hphp/util/tiny-vector.h"
32 #include "hphp/util/tribool.h"
34 #include "hphp/runtime/base/repo-auth-type.h"
36 #include "hphp/runtime/vm/coeffects.h"
37 #include "hphp/runtime/vm/type-constraint.h"
39 #include "hphp/hhbbc/hhbbc.h"
40 #include "hphp/hhbbc/misc.h"
41 #include "hphp/hhbbc/type-structure.h"
42 #include "hphp/hhbbc/type-system.h"
44 namespace HPHP::HHBBC
{
46 //////////////////////////////////////////////////////////////////////
50 struct PublicSPropMutations
;
51 struct FuncAnalysisResult
;
57 struct PropertiesInfo
;
72 //////////////////////////////////////////////////////////////////////
75 * This module contains functions for building and querying an Index
76 * of data relating to "resolved" versions of the names in of a
77 * php::Program. It also records dependencies so it is possible to
78 * tell which parts of the program may be interested in new inferred
79 * information about other parts of the program.
81 * The main entry point here is the Index class. The Index is built
82 * after parse time, and then analysis can query it for information.
85 //////////////////////////////////////////////////////////////////////
87 enum class Dep
: uintptr_t {
88 /* This dependency should trigger when the return type changes */
90 /* This dependency should trigger when a DefCns is resolved */
92 /* This dependency should trigger when a class constant is resolved */
94 /* This dependency should trigger when the bad initial prop value bit for a
96 PropBadInitialValues
= (1u << 3),
97 /* This dependency should trigger when a public static property changes */
98 PublicSProp
= (1u << 4),
99 /* This dependency means that we refused to do inline analysis on
100 * this function due to inline analysis depth. The dependency will
101 * trigger if the target function becomes effect-free, or gets a
102 * literal return value.
104 InlineDepthLimit
= (1u << 5),
108 * A DependencyContext encodes enough of the context to record a dependency - a
109 * php::Func, if we're doing private property analysis and its a suitable class,
110 * a php::Class, or a public static property.
113 enum class DependencyContextType
: uint16_t {
120 using DependencyContext
= CompactTaggedPtr
<const void, DependencyContextType
>;
122 struct DependencyContextEquals
{
123 bool operator()(const DependencyContext
& a
,
124 const DependencyContext
& b
) const {
125 return a
.getOpaque() == b
.getOpaque();
129 struct DependencyContextHash
{
130 size_t operator()(const DependencyContext
& d
) const {
131 return pointer_hash
<void>{}(reinterpret_cast<void*>(d
.getOpaque()));
135 struct DependencyContextHashCompare
: DependencyContextHash
{
136 bool equal(const DependencyContext
& a
, const DependencyContext
& b
) const {
137 return a
.getOpaque() == b
.getOpaque();
139 size_t hash(const DependencyContext
& d
) const { return (*this)(d
); }
142 using DependencyContextSet
= hphp_fast_set
<DependencyContext
,
143 DependencyContextHash
,
144 DependencyContextEquals
>;
146 std::string
show(Context
);
149 * State of properties on a class. Map from property name to its
152 struct PropStateElem
{
154 const TypeConstraint
* tc
= nullptr;
158 bool operator==(const PropStateElem
& o
) const {
163 everModified
== o
.everModified
;
166 using PropState
= std::map
<LSString
,PropStateElem
>;
169 * The result of Index::lookup_static
171 struct PropLookupResult
{
172 Type ty
; // The best known type of the property (TBottom if not found)
173 SString name
; // The statically known name of the string, if any
174 TriBool found
; // If the property was found
175 TriBool isConst
; // If the property is AttrConst
176 TriBool readOnly
; // If the property is AttrIsReadonly
177 TriBool lateInit
; // If the property is AttrLateInit
178 TriBool internal
; // If the property is Internal
179 bool classInitMightRaise
; // If class initialization during the
180 // property access can raise (unlike the
181 // others, this is only no or maybe).
184 inline PropLookupResult
& operator|=(PropLookupResult
& a
,
185 const PropLookupResult
& b
) {
186 assertx(a
.name
== b
.name
);
189 a
.isConst
|= b
.isConst
;
190 a
.readOnly
|= b
.readOnly
;
191 a
.lateInit
|= b
.lateInit
;
192 a
.internal
|= b
.internal
;
193 a
.classInitMightRaise
|= b
.classInitMightRaise
;
197 std::string
show(const PropLookupResult
&);
200 * The result of Index::merge_static_type
202 struct PropMergeResult
{
203 Type adjusted
; // The merged type, potentially adjusted according to
204 // the prop's type-constraint (it's the subtype of
205 // the merged type that would succeed).
206 TriBool throws
; // Whether the mutation this merge represents
210 inline PropMergeResult
& operator|=(PropMergeResult
& a
,
211 const PropMergeResult
& b
) {
212 a
.adjusted
|= b
.adjusted
;
213 a
.throws
|= b
.throws
;
217 std::string
show(const PropMergeResult
&);
220 * The result of Index::lookup_class_constant
222 struct ClsConstLookupResult
{
223 Type ty
; // The best known type of the constant (might not be a
225 TriBool found
; // If the constant was found
226 bool mightThrow
; // If accessing the constant can throw
229 inline ClsConstLookupResult
& operator|=(ClsConstLookupResult
& a
,
230 const ClsConstLookupResult
& b
) {
233 a
.mightThrow
|= b
.mightThrow
;
237 std::string
show(const ClsConstLookupResult
&);
240 * The result of Index::lookup_class_type_constant
242 struct ClsTypeConstLookupResult
{
243 TypeStructureResolution resolution
; // The result from resolving
244 // the type-structure
245 TriBool found
; // If the constant was found
246 TriBool abstract
; // If the constant was abstract (this only applies
247 // to the subset which wasn't found).
250 inline ClsTypeConstLookupResult
& operator|=(
251 ClsTypeConstLookupResult
& a
,
252 const ClsTypeConstLookupResult
& b
) {
253 a
.resolution
|= b
.resolution
;
254 if (a
.found
== TriBool::Yes
) {
255 a
.abstract
= b
.abstract
;
256 } else if (b
.found
!= TriBool::Yes
) {
257 a
.abstract
|= b
.abstract
;
263 std::string
show(const ClsTypeConstLookupResult
&);
265 //////////////////////////////////////////////////////////////////////
268 * Represents a class constant, pointing to where the constant was
269 * originally declared (the class name and it's position in the
270 * class' constant table).
273 using Idx
= uint32_t;
275 ConstIndex() = default;
276 ConstIndex(SString cls
, Idx idx
)
277 : cls
{cls
}, idx
{idx
} {}
282 bool operator==(const ConstIndex
& o
) const {
283 return idx
== o
.idx
&& cls
->tsame(o
.cls
);
285 bool operator<(const ConstIndex
& o
) const {
286 if (idx
!= o
.idx
) return idx
< o
.idx
;
287 return string_data_lt_type
{}(cls
, o
.cls
);
291 size_t operator()(const ConstIndex
& idx
) const {
292 return folly::hash::hash_combine(idx
.cls
->hash(), idx
.idx
);
296 template <typename SerDe
> void serde(SerDe
& sd
) {
301 std::string
show(const ConstIndex
&);
302 std::string
show(const ConstIndex
&, const IIndex
&);
304 //////////////////////////////////////////////////////////////////////
306 // Inferred class constant type from a 86cinit.
307 struct ClsConstInfo
{
309 size_t refinements
= 0;
310 template <typename SerDe
> void serde(SerDe
& sd
) {
311 sd(type
)(refinements
);
315 using ResolvedConstants
=
316 CompactVector
<std::pair
<ConstIndex::Idx
, ClsConstInfo
>>;
318 //////////////////////////////////////////////////////////////////////
320 struct ResolvedClsTypeConst
{
322 bool contextInsensitive
;
326 using ResolvedClsTypeConsts
= CompactVector
<ResolvedClsTypeConst
>;
328 //////////////////////////////////////////////////////////////////////
330 struct ResolvedTypeAlias
{
335 using ResolvedTypeAliases
= CompactVector
<ResolvedTypeAlias
>;
337 //////////////////////////////////////////////////////////////////////
340 * Represents a method, without requiring an explicit pointer to a
341 * php::Func (so can be used across remote workers).
345 explicit MethRef(const php::Func
& f
)
346 : cls
{f
.cls
->name
}, idx
{f
.clsIdx
} {}
347 MethRef(SString cls
, uint32_t idx
)
348 : cls
{cls
}, idx
{idx
} {}
350 SString cls
{nullptr};
351 // Index in the class' methods table.
352 uint32_t idx
{std::numeric_limits
<uint32_t>::max()};
354 bool operator==(const MethRef
& o
) const {
355 return cls
->tsame(o
.cls
) && idx
== o
.idx
;
357 bool operator!=(const MethRef
& o
) const {
358 return !(*this == o
);
360 bool operator<(const MethRef
& o
) const {
361 // The ordering for MethRef is arbitrary. Compare by idx and then
362 // by the class name's hash to avoid having to do the more
363 // expensive string comparison.
364 if (idx
!= o
.idx
) return idx
< o
.idx
;
365 auto const hash1
= cls
->hash();
366 auto const hash2
= o
.cls
->hash();
367 if (hash1
!= hash2
) return hash1
< hash2
;
368 return string_data_lt_type
{}(cls
, o
.cls
);
372 size_t operator()(const MethRef
& m
) const {
373 return folly::hash::hash_combine(m
.cls
->hash(), m
.idx
);
377 template <typename SerDe
> void serde(SerDe
& sd
) {
382 using MethRefSet
= hphp_fast_set
<MethRef
, MethRef::Hash
>;
384 std::string
show(const MethRef
&);
386 //////////////////////////////////////////////////////////////////////
388 struct PropInitInfo
{
393 using ResolvedPropInits
= CompactVector
<std::pair
<size_t, PropInitInfo
>>;
395 //////////////////////////////////////////////////////////////////////
403 struct MethodsWithoutCInfo
;
405 //////////////////////////////////////////////////////////////////////
407 // Opaque input to an AnalysisIndex. Avoids exposing AnalysisIndex
408 // internals to those who need to convey the data into a remote
410 template <typename T
> struct AnalysisIndexParam
{
411 AnalysisIndexParam() = default;
413 void serde(BlobEncoder
&) const;
414 void serde(BlobDecoder
&);
416 struct Deleter
{ void operator()(T
*) const; };
417 std::unique_ptr
<T
, Deleter
> ptr
;
419 friend struct ::HPHP::HHBBC::AnalysisIndex
;
422 using AnalysisIndexCInfo
= AnalysisIndexParam
<ClassInfo2
>;
423 using AnalysisIndexFInfo
= AnalysisIndexParam
<FuncInfo2
>;
424 using AnalysisIndexMInfo
= AnalysisIndexParam
<MethodsWithoutCInfo
>;
426 //////////////////////////////////////////////////////////////////////
429 * References to "resolved" entities with information in the index are
430 * in the res:: namespace.
432 * These represent handles to program entities that may have variable
433 * amounts of information. For example, we may know the name of a
434 * class in a res::Class, but do not know for sure which php::Class
435 * struct is actually associated with it.
440 * A resolved runtime Class, for a particular php::Class.
442 * Provides various lookup tables that allow querying the Class'
447 * Returns whether two (exact) classes are definitely same at
448 * runtime. This ignores all complications due to non-regular
449 * classes and is usually not what you want to use.
451 bool same(const Class
& o
) const;
454 * Returns true if this class is a subtype of 'o'. That is, every
455 * subclass of 'this' (including 'this' itself) is a subclass of
456 * 'o'. For the exact variant, 'this' is considered to be exact
457 * (that is, exactly that class). For the sub variant, 'this' could
458 * also be a subclass. This distinction only matters for non-regular
459 * classes. 'o' is considered to be a subclass except for
460 * exactSubtypeOfExact. This is needed since two exact classes, even
461 * if the same, may not be a sub-type of one another if
462 * nonRegularL/nonRegularR differ.
464 * Classes can have implementations/subclasses which aren't
465 * "regular" classes (interfaces/traits/abstract/etc). Whether these
466 * will be considered as part of the check (on either side) is
467 * context dependent and specified by nonRegularL and nonRegularR.
469 bool exactSubtypeOf(const Class
& o
, bool nonRegularL
, bool nonRegularR
) const;
470 bool exactSubtypeOfExact(const Class
& o
,
472 bool nonRegularR
) const;
473 bool subSubtypeOf(const Class
& o
, bool nonRegularL
, bool nonRegularR
) const;
476 * Returns false if this class has no subclasses in common with
477 * 'o'. This is equivalent to saying that their intersection is
478 * empty. For the exact variant, 'this' is considered to be exactly
479 * that class (no sub-classes). For the sub variant, 'this' might be
480 * that class, or any subclass of that class.
482 * Since couldBe is symmetric, this covers all of the cases. 'o' is
483 * considered to be a subclass, except for exactCouldBeExact. This
484 * is needed since two exact classes, even if the same, may not have
485 * anything in common if nonRegularL/nonRegularR differ.
487 * Classes can have implementations/subclasses which aren't
488 * "regular" classes (interfaces/traits/abstract/etc). Whether these
489 * will be considered as part of the check (on either side) is
490 * context dependent and specified by nonRegularL and nonRegularR.
492 bool exactCouldBe(const Class
& o
, bool nonRegularL
, bool nonRegularR
) const;
493 bool exactCouldBeExact(const Class
& o
,
495 bool nonRegularR
) const;
496 bool subCouldBe(const Class
& o
, bool nonRegularL
, bool nonRegularR
) const;
499 * Returns the name of this class. Non-null guarantee.
501 SString
name() const;
504 * Returns whether this class is a final class as determined by
505 * static analysis. The first variant considers all classes, while
506 * the second variant only considers potentially overriding regular
507 * classes (this distinction is important if you already have an
508 * instance of the class, as abstract classes cannot be
511 * NB: Traits never can be overridden and will always return false
512 * (their subclass list reflects users which the trait will be
513 * flattened into at runtime).
515 * When returning false the class is guaranteed to be final. When
516 * returning true the system cannot tell though the class may still
519 bool couldBeOverridden() const;
520 bool couldBeOverriddenByRegular() const;
523 * Returns whether this class might be a regular/non-regular class
524 * at runtime. For resolved classes this check is precise, but for
525 * unresolved classes this will always conservatively return
526 * true. This only checks the class itself and says nothing about
527 * any potential subclasses of this.
529 bool mightBeRegular() const;
530 bool mightBeNonRegular() const;
533 * Whether this class (or its subtypes) might be a non-regular
534 * class. For resolved classes this check is precise, but for
535 * unresolved classes this will always conservatively return true.
537 bool mightContainNonRegular() const;
540 * Return the best class equivalent to this, but with any
541 * non-regular classes removed. By equivalent, we mean the most
542 * specific representable class which contains all of the same
543 * regular subclasses that the original did. If std::nullopt is
544 * returned, this class contains no regular classes (and therefore
545 * the result is a Bottom). The returned classes may be the original
546 * class, and for interfaces and abstract classes, the result may
547 * not be an interface or abstract class.
549 Optional
<res::Class
> withoutNonRegular() const;
552 * Whether this class (or its subtypes) could possibly have have
553 * a magic toBoolean() method.
555 bool couldHaveMagicBool() const;
558 * Whether this class could possibly have a sub-class that is
559 * mocked, including itself.
561 bool couldHaveMockedSubClass() const;
564 * Whether this class could possibly be mocked.
566 bool couldBeMocked() const;
569 * Whether this class could have reified generics
571 bool couldHaveReifiedGenerics() const;
574 * Whether this class must have reified generics
576 bool mustHaveReifiedGenerics() const;
579 * Whether this class could have a reified parent
581 bool couldHaveReifiedParent() const;
584 * Whether this class must have a reified parent
586 bool mustHaveReifiedParent() const;
589 * Returns whether this resolved class might distinguish being constructed
590 * dynamically versus being constructed normally (IE, might raise a notice).
592 bool mightCareAboutDynConstructs() const;
595 * Returns whether this class will raise a notice if it is loaded with
596 * HH\class_to_classname;
598 bool mightCareAboutDynamicallyReferenced() const;
601 * Whether this class (or clases derived from it) could have const props.
603 bool couldHaveConstProp() const;
604 bool subCouldHaveConstProp() const;
607 * Returns the res::Class for this Class's parent if there is one,
610 Optional
<Class
> parent() const;
613 * Returns the php::Class for this Class if there is one, or
616 const php::Class
* cls() const;
619 * Returns true if this class has it's full child class information.
621 bool hasCompleteChildren() const;
623 * A class is complete if it has full child class information, or if
624 * it's a "conservative" class (a class which has too many child
625 * classes to track completely).
627 bool isComplete() const;
630 * Classes that come out of the BlobEncoder start out as
631 * "serialized". This means it just wraps a string. Almost nothing
632 * can be done on a serialized class, except call unserialize on
633 * it. Unserialize will produce the "real" class, recording
634 * dependencies as necessary. If unserialize returns std::nullopt
635 * then the class definitely doesn't exist.
637 bool isSerialized() const;
638 Optional
<Class
> unserialize(const IIndex
&) const;
641 * Invoke the given function on every possible subclass of this
642 * class (including itself), providing the name and the Attr bits of
643 * the subclass. Only the Attr bits corresponding to the type of
644 * class are provided (AttrEnum/AttrTrait/AttrInterface). Returns
645 * false and doesn't call the function if this class does not know
646 * it's complete children, true otherwise.
648 bool forEachSubclass(const std::function
<void(SString
, Attr
)>&) const;
650 using ClassVec
= TinyVector
<Class
, 4>;
653 * Given two lists of classes, calculate the union between them (in
654 * canonical form). A list of size 1 represents a single class, and
655 * a larger list represents an intersection of classes. The input
656 * lists are assumed to be in canonical form. If the output is an
657 * empty list, the union is *all* classes (corresponding to TObj or
658 * TCls). This function is really an implementation detail of
659 * union_of() and not a general purpose interface.
661 static ClassVec
combine(folly::Range
<const Class
*> classes1
,
662 folly::Range
<const Class
*> classes2
,
668 * Given two lists of classes, calculate the intersection between
669 * them (in canonical form). A list of size 1 represents a single
670 * class, and a larger list represents an intersection of
671 * classes. The input lists are assumed to be in canonical form. If
672 * the output is an empty list, the intersection is empty
673 * (equivalent to Bottom). This function is really an implementation
674 * detail of intersection_of() and not a general purpose interface.
676 static ClassVec
intersect(folly::Range
<const Class
*> classes1
,
677 folly::Range
<const Class
*> classes2
,
680 bool& nonRegularOut
);
683 * Given a list of classes, return a new list of classes
684 * representing all of the non-regular classes removed (and
685 * canonicalized). If the output list is empty, there are no regular
688 static ClassVec
removeNonRegular(folly::Range
<const Class
*> classes
);
691 * Given two lists of classes, calculate whether their intersection
692 * is non-empty. This is equivalent to calling intersect and
693 * checking the result, but more efficient.
695 static bool couldBeIsect(folly::Range
<const Class
*> classes1
,
696 folly::Range
<const Class
*> classes2
,
701 * Convert this class to/from an opaque integer. The integer is
702 * "pointerish" (has upper bits cleared), so can be used in
703 * something like CompactTaggedPtr. It is not, however, guaranteed
704 * to be aligned (lower bits may be set).
706 uintptr_t toOpaque() const { return opaque
.toOpaque(); }
707 static Class
fromOpaque(uintptr_t o
) { return Class
{E::fromOpaque(o
)}; }
709 size_t hash() const { return toOpaque(); }
712 * Obtain with a given name or associated with the given ClassInfo
713 * (must already exist).
715 static Class
get(SString
);
716 static Class
get(const ClassInfo
&);
717 static Class
get(const ClassInfo2
&);
720 * Obtain with a given name. If does not already exist, create an
721 * unresolved Class and return that.
723 static Class
getOrCreate(SString
);
726 * Make an unresolved Class representing the given name. The name
727 * cannot be an existing class.
729 static Class
getUnresolved(SString
);
731 void serde(BlobEncoder
&) const;
732 static Class
makeForSerde(BlobDecoder
&);
734 void makeConservativeForTest();
736 bool isMissingDebug() const;
740 ClassGraph
graph() const;
741 ClassInfo
* cinfo() const;
742 ClassInfo2
* cinfo2() const;
744 template <typename F
>
745 static void visitEverySub(folly::Range
<const Class
*>, bool, const F
&);
747 friend std::string
show(const Class
&);
749 friend struct ::HPHP::HHBBC::Index
;
750 friend struct ::HPHP::HHBBC::AnalysisIndex
;
752 using E
= Either
<void*, const StringData
*>;
755 explicit Class(ClassGraph
);
756 explicit Class(E o
): opaque
{o
} {}
760 * This is an abstraction layer to represent possible runtime function
763 * Internally, this may only know the name of the function (or method), or we
764 * may know exactly which source-code-level function it refers to, or we may
765 * only have ruled it down to one of a few functions in a class hierarchy. The
766 * interpreter can treat all these cases the same way using this.
770 * Returns the (fully qualified) name of this function.
772 std::string
name() const;
775 * If this resolved function represents exactly one php::Func, return it.
777 const php::Func
* exactFunc() const;
780 * Whether this function definitely exists or definitely does not
783 TriBool
exists() const;
786 * Returns whether this resolved function is definitely safe to constant fold.
788 bool isFoldable() const;
791 * Whether this function could have reified generics
793 bool couldHaveReifiedGenerics() const;
796 * Returns whether this resolved function might distinguish being called
797 * dynamically versus being called normally (IE, might raise a notice).
799 bool mightCareAboutDynCalls() const;
802 * Returns whether this resolved function might be a builtin.
804 bool mightBeBuiltin() const;
807 * Minimum/maximum bound on the number of non-variadic parameters of the
810 uint32_t minNonVariadicParams() const;
811 uint32_t maxNonVariadicParams() const;
814 * Return if the function supports async eager return.
816 TriBool
supportsAsyncEagerReturn() const;
819 * Returns the number of inout parameters expected by func (if known).
821 Optional
<uint32_t> lookupNumInoutParams() const;
824 * Returns the parameter preparation kind (if known) for parameter
825 * `paramId' on this function.
827 PrepKind
lookupParamPrep(uint32_t paramId
) const;
830 * Returns whether the function's return value is readonly
832 TriBool
lookupReturnReadonly() const;
835 * Returns whether the function is marked as readonly
837 TriBool
lookupReadonlyThis() const;
840 * Returns the wrapped HHVM builtin iff this is a trivial HHVM builtin wrapper
842 Optional
<SString
> triviallyWrappedFunc() const;
847 const RuntimeCoeffects
* requiredCoeffects() const;
848 // Returns nullptr if we cant tell whether there are coeffect rules
849 const CompactVector
<CoeffectRule
>* coeffectRules() const;
855 friend struct ::HPHP::HHBBC::Index
;
856 friend struct ::HPHP::HHBBC::AnalysisIndex
;
866 const FuncInfo
* finfo
;
869 const FuncInfo2
* finfo
;
872 const FuncInfo
* finfo
;
875 const FuncInfo2
* finfo
;
877 // Like Method, but the method is not guaranteed to actually exist
878 // (this only matters for things like exactFunc()).
879 struct MethodOrMissing
{
880 const FuncInfo
* finfo
;
882 struct MethodOrMissing2
{
883 const FuncInfo2
* finfo
;
885 // Method/Func is known to not exist
889 struct MissingMethod
{
893 // Group of methods (a wrapper around a FuncFamily).
894 struct MethodFamily
{
898 struct MethodFamily2
{
899 const FuncFamily2
* family
;
902 // Simultaneously a group of func families. Any data must be
903 // intersected across all of the func families in the list. Used for
904 // method resolution on a DCls where isIsect() is true.
906 CompactVector
<FuncFamily
*> families
;
907 bool regularOnly
{false};
910 CompactVector
<const FuncFamily2
*> families
;
911 bool regularOnly
{false};
913 using Rep
= boost::variant
< FuncName
931 friend std::string
show(const Func
&);
938 * Produce a trace-able string for a res::Func or res::Class.
940 std::string
show(const Func
&);
941 std::string
show(const Class
&);
945 //////////////////////////////////////////////////////////////////////
948 * A type which is an alias to another. This includes standard
949 * type-aliases, but also enums (which are aliases of their underlying
950 * base type). Type mappings can alias to another type mapping.
954 // If an enum, this is the same value as name. Otherwise it's the
955 // first enum encountered when resolving a type-alias.
957 TypeConstraint value
;
960 bool operator==(const TypeMapping
& o
) const {
961 return name
->tsame(o
.name
);
963 bool operator<(const TypeMapping
& o
) const {
964 return string_data_lt_type
{}(name
, o
.name
);
967 template <typename SerDe
> void serde(SerDe
& sd
) {
968 sd(name
)(firstEnum
)(value
)(isTypeAlias
);
972 //////////////////////////////////////////////////////////////////////
975 * This class encapsulates the known facts about the program, with a
976 * whole-program view.
978 * This structure contains unowned pointers into the php::Program it
979 * was created for. It should not out-live the Program.
981 * The const member functions of this class are thread safe for
982 * concurrent reads and writes. The non-const functions should be
983 * called in a single threaded context only (they are used during the
984 * "update" step in between whole program analysis rounds).
987 // The input used to build the Index is largely the extern-worker
988 // refs representing the program components. However, some
989 // additional metadata is needed locally to know what the refs
990 // represent (and schedule some initial jobs).
992 template <typename T
> using R
= extern_worker::Ref
<std::unique_ptr
<T
>>;
997 std::vector
<SString
> dependencies
;
998 // If this class is a closure declared in a top-level func, this
999 // is the name of that func.
1000 LSString closureFunc
;
1001 std::vector
<SString
> closures
;
1004 // If this class is an enum, the type-mapping representing it's
1006 Optional
<TypeMapping
> typeMapping
;
1007 std::vector
<SString
> unresolvedTypes
;
1015 std::vector
<SString
> unresolvedTypes
;
1021 std::vector
<TypeMapping
> typeMappings
;
1022 std::vector
<std::pair
<SString
, bool>> constants
;
1025 struct FuncBytecodeMeta
{
1026 R
<php::FuncBytecode
> bc
;
1032 struct ClassBytecodeMeta
{
1033 R
<php::ClassBytecode
> bc
;
1037 static std::vector
<SString
> makeDeps(const php::Class
&);
1039 std::vector
<ClassMeta
> classes
;
1040 std::vector
<UnitMeta
> units
;
1041 std::vector
<FuncMeta
> funcs
;
1042 std::vector
<ClassBytecodeMeta
> classBC
;
1043 std::vector
<FuncBytecodeMeta
> funcBC
;
1047 * Create an Index for a php::Program. Performs some initial
1048 * analysis of the program.
1052 std::unique_ptr
<TicketExecutor
>,
1053 std::unique_ptr
<extern_worker::Client
>,
1055 StructuredLogEntry
*);
1058 Index(const Index
&) = delete;
1060 Index
& operator=(const Index
&) = delete;
1061 Index
& operator=(Index
&&);
1064 * The index operates in two modes: frozen, and unfrozen.
1066 * Conceptually, the index is mutable and may acquire new
1067 * information until it has been frozen, and once frozen, it retains
1068 * the information it had at the point it was frozen.
1070 * The reason this exists is because certain functions on the index
1071 * may cause it to need to consult information in the bodies of
1072 * functions other than the Context passed in. Specifically, if the
1073 * interpreter tries to look up the return type for a callee in a
1074 * given CallContext, the index may choose to recursively invoke
1075 * type inference on that callee's function body to see if more
1076 * precise information can be determined, unless it is frozen.
1078 * This is fine until the final pass, because all bytecode is
1079 * read-only at that stage. However, in the final pass, other
1080 * threads might be optimizing a callee's bytecode and changing it,
1081 * so we should not be reading from it to perform type inference
1082 * concurrently. Freezing the index tells it it can't do that
1085 * These are the functions to query and transition to frozen state.
1087 bool frozen() const;
1092 * Throw away data structures that won't be needed during or after
1093 * the final pass. Currently the dependency map, which can take a
1094 * long time to destroy.
1096 void cleanup_for_final();
1099 * Throw away data structures that won't be needed after the emit
1102 void cleanup_post_emit();
1105 * Prepare the index for local execution. This marks the boundary
1106 * between running in extern-worker and running in the "classic"
1112 * Access the StructuredLogEntry that the Index is using (if any).
1114 StructuredLogEntry
* sample() const;
1117 * Obtain the extern-worker related state that the Index used.
1119 TicketExecutor
& executor() const;
1120 extern_worker::Client
& client() const;
1121 const CoroAsyncValue
<extern_worker::Ref
<Config
>>& configRef() const;
1124 * The names of all classes which has a 86*init function.
1126 const TSStringSet
& classes_with_86inits() const;
1129 * The names of all top-level functions which are initializers for
1130 * "dynamic" constants.
1132 const FSStringSet
& constant_init_funcs() const;
1135 * The names of all units which have type-aliases defined within
1138 const SStringSet
& units_with_type_aliases() const;
1141 * Access the php::Program this Index is analyzing.
1143 const php::Program
& program() const;
1146 * Obtain a pointer to the unit which defined the given func.
1148 const php::Unit
* lookup_func_unit(const php::Func
&) const;
1149 const php::Unit
* lookup_func_original_unit(const php::Func
&) const;
1152 * Obtain a pointer to the unit which defined the given class.
1154 const php::Unit
* lookup_class_unit(const php::Class
&) const;
1157 * Obtain a pointer to the class which defines the given class
1160 const php::Class
* lookup_const_class(const php::Const
&) const;
1163 * Obtain a pointer to the class which serves as the context for the
1164 * given class. For non-closures, this is just the input, but may be
1165 * different in closures.
1167 const php::Class
* lookup_closure_context(const php::Class
&) const;
1170 * Look up the php::Class with the given name, returning nullptr if
1171 * it does not exist. The presence of a php::Class does not
1172 * necessarily mean the class is actually instantiable or won't
1173 * fatal when referenced.
1175 const php::Class
* lookup_class(SString
) const;
1178 * Call the given callback for each (top-level) func defined in the
1181 void for_each_unit_func(const php::Unit
&,
1182 std::function
<void(const php::Func
&)>) const;
1183 void for_each_unit_func_mutable(php::Unit
&,
1184 std::function
<void(php::Func
&)>);
1187 * Call the given callback for each class defined in the given Unit.
1189 void for_each_unit_class(const php::Unit
&,
1190 std::function
<void(const php::Class
&)>) const;
1191 void for_each_unit_class_mutable(php::Unit
&,
1192 std::function
<void(php::Class
&)>);
1195 * Find all the closures created inside the context of a given
1198 const CompactVector
<const php::Class
*>*
1199 lookup_closures(const php::Class
*) const;
1202 * Find all the extra methods associated with a class from its
1205 const hphp_fast_set
<const php::Func
*>*
1206 lookup_extra_methods(const php::Class
*) const;
1209 * Resolve the given class name to a res::Class.
1211 * Returns std::nullopt if no such class with that name exists, or
1212 * if the class is not definable.
1214 Optional
<res::Class
> resolve_class(SString name
) const;
1215 Optional
<res::Class
> resolve_class(const php::Class
&) const;
1218 * Find a type-alias with the given name. If a nullptr is returned,
1219 * then no type-alias exists with that name.
1221 const php::TypeAlias
* lookup_type_alias(SString name
) const;
1224 * Find a class or a type-alias with the given name. This can be
1225 * more efficient than doing two different lookups.
1227 struct ClassOrTypeAlias
{
1228 // At most one of these will be non-null.
1229 const php::Class
* cls
;
1230 const php::TypeAlias
* typeAlias
;
1233 ClassOrTypeAlias
lookup_class_or_type_alias(SString name
) const;
1236 * Resolve the given php::Func, which can be a function or
1237 * method. resolved() is guaranteed to be true for the returned
1240 res::Func
resolve_func_or_method(const php::Func
&) const;
1243 * Try to resolve a function named `name'.
1245 res::Func
resolve_func(SString name
) const;
1248 * Try to resolve a method named `name' with a this type of
1249 * `thisType' within a given Context.
1251 * The type of `thisType' determines if the method is meant to be
1252 * static or not. A this type of BCls is a static method and a BObj
1253 * is for an instance method.
1255 * Note: a resolved method does not imply the function is actually
1256 * callable at runtime. You still need to apply visibility checks,
1259 * Pre: thisType.subtypeOf(BCls) || thisType.subtypeOf(BObj)
1261 res::Func
resolve_method(Context
, const Type
& thisType
, SString name
) const;
1264 * Resolve a class constructor for the supplied object type.
1266 * Pre: obj.subtypeOf(BObj)
1268 res::Func
resolve_ctor(const Type
& obj
) const;
1271 * Lookup metadata about the constant access `cls'::`name', in the
1272 * current context `ctx'. The returned metadata not only includes
1273 * the best known type of the constant, but whether it is definitely
1274 * found, and whether accessing the constant might throw. This
1275 * function is responsible for walking the class hierarchy to find
1276 * all possible constants and combining the results. This is
1277 * intended to be the source of truth about constants during
1280 * This function only looks up non-type, non-context constants.
1282 ClsConstLookupResult
1283 lookup_class_constant(Context ctx
, const Type
& cls
, const Type
& name
) const;
1286 * Retrieve the information the Index knows about all of the class
1287 * constants defined on the given class. This does not register any
1290 std::vector
<std::pair
<SString
, ClsConstInfo
>>
1291 lookup_class_constants(const php::Class
&) const;
1294 * Lookup metadata about the constant access `cls'::`name', where
1295 * that constant is meant to be a type-constant. The returned
1296 * metadata includes the best known type of the resolved
1297 * type-structure, whether it was found, and whether it was
1298 * abstract. This is intended to be the source of truth about
1299 * type-constants during analysis. The returned type-structure type
1300 * will always be static.
1302 * By default, lookup_class_type_constant calls
1303 * resolve_type_structure to resolve any found type-structure. This
1304 * behavior can be overridden by providing a customer resolver.
1306 using ClsTypeConstLookupResolver
=
1307 std::function
<TypeStructureResolution(const php::Const
&,const php::Class
&)>;
1309 ClsTypeConstLookupResult
1310 lookup_class_type_constant(
1313 const ClsTypeConstLookupResolver
& resolver
= {}) const;
1316 * Lookup metadata about the constant given by the ConstIndex (with
1317 * the given name). The php::Class is used to provide the context.
1319 ClsTypeConstLookupResult
1320 lookup_class_type_constant(const php::Class
&, SString
, ConstIndex
) const;
1323 * Retrive all type constants on the given class, whether declared
1324 * on the class or inherited.
1326 std::vector
<std::pair
<SString
, HHBBC::ConstIndex
>>
1327 lookup_flattened_class_type_constants(const php::Class
&) const;
1330 * Lookup what the best known Type for a constant would be, using a
1331 * given Index and Context, if a constant of that name were defined.
1333 Type
lookup_constant(Context ctx
, SString cnsName
) const;
1336 * Return true if the return value of the function might depend on arg.
1338 bool func_depends_on_arg(const php::Func
* func
, size_t arg
) const;
1341 * Return type knowledge. The type and whether it's effect-free.
1345 bool effectFree
{false};
1349 * If func is effect-free when called with args, and it returns a constant,
1350 * return that constant; otherwise return TInitCell.
1352 ReturnType
lookup_foldable_return_type(Context ctx
,
1353 const CallContext
& calleeCtx
) const;
1356 * Return the best known return type for a resolved function, in a
1357 * context insensitive way. Returns TInitCell at worst.
1359 ReturnType
lookup_return_type(Context
, MethodsInfo
*, res::Func
,
1360 Dep dep
= Dep::ReturnTy
) const;
1363 * Return the best known return type for a resolved function and
1364 * whether it is effect-free, given the supplied calling context.
1365 * Returns TInitCell at worst.
1367 * During analyze phases, this function may re-enter analyze in
1368 * order to interpret the callee with these argument types.
1370 ReturnType
lookup_return_type(Context caller
,
1372 const CompactVector
<Type
>& args
,
1373 const Type
& context
,
1375 Dep dep
= Dep::ReturnTy
) const;
1378 * Look up raw return type information for an unresolved
1379 * function. This is the best known return type, and the number of
1380 * refinements done to that type.
1382 * This function does not register a dependency on the return type
1383 * information, so should not be used during analysis.
1385 * Nothing may be writing to the index when this function is used,
1386 * but concurrent readers are allowed.
1388 std::pair
<ReturnType
, size_t> lookup_return_type_raw(const php::Func
*) const;
1391 * Return the best known types of a closure's used variables (on
1392 * entry to the closure). The function is the closure body.
1394 * If move is true, the value will be moved out of the index. This
1395 * should only be done at emit time. (note that the only other user
1396 * of this info is analysis, which only uses it when processing the
1397 * owning class, so its safe to kill after emitting the owning
1401 lookup_closure_use_vars(const php::Func
*,
1402 bool move
= false) const;
1405 * Returns the control-flow insensitive inferred private instance
1406 * property types for a Class. The Class doesn't need to be
1407 * resolved, because private properties don't depend on the
1408 * inheritance hierarchy.
1410 * The Index tracks the largest types for private properties that
1411 * are guaranteed to hold at any program point.
1413 * If move is true, the value will be moved out of the index. This
1414 * should only be done at emit time. (note that the only other user
1415 * of this info is analysis, which only uses it when processing the
1416 * owning class, so its safe to kill after emitting the owning
1419 PropState
lookup_private_props(const php::Class
*,
1420 bool move
= false) const;
1423 * Returns the control-flow insensitive inferred private static
1424 * property types for a Class. The class doesn't need to be
1425 * resolved for the same reasons as for instance properties.
1427 * The Index tracks the largest types for private static properties
1428 * that are guaranteed to hold at any program point.
1430 * If move is true, the value will be moved out of the index. This
1431 * should only be done at emit time. (note that the only other user
1432 * of this info is analysis, which only uses it when processing the
1433 * owning class, so its safe to kill after emitting the owning
1436 PropState
lookup_private_statics(const php::Class
*,
1437 bool move
= false) const;
1438 PropState
lookup_public_statics(const php::Class
*) const;
1441 * Lookup metadata about the static property access `cls'::`name',
1442 * in the current context `ctx'. The returned metadata not only
1443 * includes the best known type of the property, but whether it is
1444 * definitely found, and whether the access might raise for various
1445 * reasons. This function is responsible for walking the class
1446 * hierarchy to find the appropriate property while applying
1447 * accessibility rules. This is intended to be the source of truth
1448 * about static properties during analysis.
1450 PropLookupResult
lookup_static(Context ctx
,
1451 const PropertiesInfo
& privateProps
,
1453 const Type
& name
) const;
1456 * Lookup if initializing (which is a side-effect of several bytecodes) the
1457 * given class might raise.
1459 bool lookup_class_init_might_raise(Context
, res::Class
) const;
1462 * Lookup the best known type for a public (non-static) property. Since we
1463 * don't do analysis on public properties, this just inferred from the
1464 * property's type-hint (if enforced).
1466 Type
lookup_public_prop(const Type
& obj
, const Type
& name
) const;
1467 Type
lookup_public_prop(const php::Class
* cls
, SString name
) const;
1470 * Returns the computed vtable slot for the given class, if it's an interface
1471 * that was given a vtable slot. No two interfaces implemented by the same
1472 * class will share the same vtable slot. May return kInvalidSlot, if the
1473 * given class isn't an interface or if it wasn't assigned a slot.
1475 Slot
lookup_iface_vtable_slot(const php::Class
*) const;
1478 * Return the DependencyContext for ctx.
1480 DependencyContext
dependency_context(const Context
& ctx
) const;
1483 * Determine whether to use class-at-a-time, or function-at-a-time
1486 * Must be called in single-threaded context.
1488 void use_class_dependencies(bool f
);
1489 bool using_class_dependencies() const;
1492 * Merge the type `val' into the known type for static property
1493 * `cls'::`name'. Depending on what we know about `cls' and `name',
1494 * this might affect multiple properties. This function is
1495 * responsible for walking the class hierarchy to find the
1496 * appropriate property while applying accessibility
1497 * rules. Mutations of AttrConst properties are ignored unless
1498 * `ignoreConst' is set to true. If `checkUB' is true, upper-bound
1499 * type constraints are consulted in addition to the normal type
1502 * The result tells you the subtype of val that would be
1503 * successfully set (according to the type constraints), and if the
1504 * mutation would throw or not.
1506 PropMergeResult
merge_static_type(Context ctx
,
1507 PublicSPropMutations
& publicMutations
,
1508 PropertiesInfo
& privateProps
,
1512 bool checkUB
= false,
1513 bool ignoreConst
= false,
1514 bool mustBeReadOnly
= false) const;
1517 * Attempt to pre-resolve as many type-structures as possible in
1518 * type-constants and type-aliases.
1520 void preresolve_type_structures();
1523 * Refine the types of the class constants defined by an 86cinit,
1524 * based on a round of analysis.
1526 * No other threads should be using ctx.cls->constants or deps when
1527 * this function is called.
1529 * Merges the set of Contexts that depended on the constants defined
1532 void refine_class_constants(const Context
& ctx
,
1533 const ResolvedConstants
& resolved
,
1534 DependencyContextSet
& deps
);
1537 * Refine the types of the constants defined by a function, based on
1538 * a round of analysis.
1540 * Constants not defined by a pseudomain are considered unknowable
1542 * No other threads should be calling functions on this Index when
1543 * this function is called.
1545 * Merges the set of Contexts that depended on the constants defined
1546 * by this php::Func into deps.
1548 void refine_constants(const FuncAnalysisResult
& fa
,
1549 DependencyContextSet
& deps
);
1552 * Refine the return type for a function, based on a round of
1555 * No other threads should be calling functions on this Index when
1556 * this function is called.
1558 * Merges the set of Contexts that depended on the return type of
1559 * this php::Func into deps.
1561 void refine_return_info(const FuncAnalysisResult
& fa
,
1562 DependencyContextSet
& deps
);
1565 * Refine the used var types for a closure, based on a round of
1568 * No other threads should be calling functions on this Index when
1569 * this function is called.
1571 * Returns: true if the types have changed.
1573 bool refine_closure_use_vars(const php::Class
*,
1574 const CompactVector
<Type
>&);
1577 * Refine the private property types for a class, based on a round
1580 * No other threads should be calling functions on this Index when
1581 * this function is called.
1583 void refine_private_props(const php::Class
* cls
,
1587 * Refine the static private property types for a class, based on a
1588 * round of analysis.
1590 * No other threads should be calling functions on this Index when
1591 * this function is called.
1593 void refine_private_statics(const php::Class
* cls
,
1597 * Record in the index that the given set of public static property mutations
1598 * has been found while analyzing the given function. During a round of
1599 * analysis, the mutations are gathered from the analysis results for each
1600 * function, recorded in the index, and then refine_public_statics is called
1601 * to process the mutations and update the index.
1603 * No other threads should be calling functions on this Index when this
1604 * function is called.
1606 void record_public_static_mutations(const php::Func
& func
,
1607 PublicSPropMutations mutations
);
1610 * After a round of analysis with all the public static property mutations
1611 * being recorded with record_public_static_mutations, the types can be
1612 * reflected into the index for use during another type inference pass.
1614 * No other threads should be calling functions on this Index when this
1615 * function is called.
1617 * Merges the set of Contexts that depended on a public static property whose
1620 void refine_public_statics(DependencyContextSet
& deps
);
1623 * Update the initial values for properties inferred from
1624 * 86[p/s]init functions during analysis. The modification of the
1625 * relevant php::Prop instances must be done here when we know
1626 * nobody else is reading them.
1628 void update_prop_initial_values(const Context
&,
1629 const ResolvedPropInits
&,
1630 DependencyContextSet
&);
1635 friend struct AnalysisScheduler
;
1636 friend struct PublicSPropMutations
;
1638 template <typename F
>
1639 bool visit_every_dcls_cls(const DCls
&, const F
&) const;
1641 template <typename P
, typename G
>
1642 res::Func
rfunc_from_dcls(const DCls
&, SString
, const P
&, const G
&) const;
1645 std::unique_ptr
<IndexData
> m_data
;
1648 //////////////////////////////////////////////////////////////////////
1650 // Abstracts away particular Index implementation from the analysis
1654 IIndex(const IIndex
&) = delete;
1655 IIndex(IIndex
&&) = delete;
1656 IIndex
& operator=(const IIndex
&) = delete;
1657 IIndex
& operator=(IIndex
&&) = delete;
1659 virtual bool frozen() const = 0;
1661 virtual const php::Unit
* lookup_func_unit(const php::Func
&) const = 0;
1663 virtual const php::Unit
* lookup_class_unit(const php::Class
&) const = 0;
1665 virtual const php::Class
* lookup_const_class(const php::Const
&) const = 0;
1667 virtual const php::Class
* lookup_closure_context(const php::Class
&) const = 0;
1669 virtual const php::Class
* lookup_class(SString
) const = 0;
1671 virtual const CompactVector
<const php::Class
*>*
1672 lookup_closures(const php::Class
*) const = 0;
1674 virtual const hphp_fast_set
<const php::Func
*>*
1675 lookup_extra_methods(const php::Class
*) const = 0;
1677 virtual Optional
<res::Class
> resolve_class(SString
) const = 0;
1678 virtual Optional
<res::Class
> resolve_class(const php::Class
&) const = 0;
1680 virtual std::pair
<const php::TypeAlias
*, bool>
1681 lookup_type_alias(SString
) const = 0;
1683 virtual Index::ClassOrTypeAlias
lookup_class_or_type_alias(SString
) const = 0;
1685 virtual res::Func
resolve_func_or_method(const php::Func
&) const = 0;
1687 virtual res::Func
resolve_func(SString
) const = 0;
1689 virtual res::Func
resolve_method(Context
,
1690 const Type
& thisType
,
1691 SString name
) const = 0;
1693 virtual res::Func
resolve_ctor(const Type
& obj
) const = 0;
1695 virtual std::vector
<std::pair
<SString
, ClsConstInfo
>>
1696 lookup_class_constants(const php::Class
&) const = 0;
1698 virtual ClsConstLookupResult
1699 lookup_class_constant(Context
, const Type
& cls
, const Type
& name
) const = 0;
1701 virtual ClsTypeConstLookupResult
lookup_class_type_constant(
1704 const Index::ClsTypeConstLookupResolver
& resolver
= {}
1707 virtual ClsTypeConstLookupResult
lookup_class_type_constant(
1713 virtual std::vector
<std::pair
<SString
, ConstIndex
>>
1714 lookup_flattened_class_type_constants(const php::Class
&) const = 0;
1716 virtual Type
lookup_constant(Context
, SString
) const = 0;
1718 virtual bool func_depends_on_arg(const php::Func
* func
, size_t arg
) const = 0;
1720 virtual Index::ReturnType
1721 lookup_foldable_return_type(Context
, const CallContext
&) const = 0;
1723 virtual Index::ReturnType
1724 lookup_return_type(Context
, MethodsInfo
*, res::Func
,
1725 Dep dep
= Dep::ReturnTy
) const = 0;
1727 virtual Index::ReturnType
1728 lookup_return_type(Context caller
,
1730 const CompactVector
<Type
>& args
,
1731 const Type
& context
,
1733 Dep dep
= Dep::ReturnTy
) const = 0;
1735 virtual std::pair
<Index::ReturnType
, size_t>
1736 lookup_return_type_raw(const php::Func
*) const = 0;
1738 virtual CompactVector
<Type
>
1739 lookup_closure_use_vars(const php::Func
*,
1740 bool move
= false) const = 0;
1742 virtual PropState
lookup_private_props(const php::Class
*,
1743 bool move
= false) const = 0;
1745 virtual PropState
lookup_private_statics(const php::Class
*,
1746 bool move
= false) const = 0;
1748 virtual PropLookupResult
lookup_static(Context
,
1749 const PropertiesInfo
& privateProps
,
1751 const Type
& name
) const = 0;
1753 virtual Type
lookup_public_prop(const Type
& obj
, const Type
& name
) const = 0;
1755 virtual PropMergeResult
1756 merge_static_type(Context ctx
,
1757 PublicSPropMutations
& publicMutations
,
1758 PropertiesInfo
& privateProps
,
1762 bool checkUB
= false,
1763 bool ignoreConst
= false,
1764 bool mustBeReadOnly
= false) const = 0;
1766 virtual bool using_class_dependencies() const = 0;
1768 virtual void push_context(const Context
&) const = 0;
1769 virtual void pop_context() const = 0;
1771 virtual bool set_in_type_cns(bool) const = 0;
1773 friend struct ContextPusher
;
1774 friend struct InTypeCns
;
1777 //////////////////////////////////////////////////////////////////////
1779 // Push the given Context onto the Index's stack of Contexts, and
1780 // remove it automatically.
1781 struct ContextPusher
{
1782 ContextPusher(const IIndex
& index
, const Context
& ctx
) : index
{index
} {
1783 index
.push_context(ctx
);
1785 ~ContextPusher() { index
.pop_context(); }
1787 const IIndex
& index
;
1790 //////////////////////////////////////////////////////////////////////
1792 // RAII class to mark that we're resolving a class' type-constants.
1794 explicit InTypeCns(const IIndex
& index
, bool b
= true)
1796 , was
{index
.set_in_type_cns(b
)} {}
1797 ~InTypeCns() { index
.set_in_type_cns(was
); }
1799 const IIndex
& index
;
1803 //////////////////////////////////////////////////////////////////////
1805 struct IndexAdaptor
: public IIndex
{
1806 explicit IndexAdaptor(const Index
& index
)
1807 : index
{const_cast<Index
&>(index
)} {}
1809 bool frozen() const override
{ return index
.frozen(); }
1811 const php::Unit
* lookup_func_unit(const php::Func
& f
) const override
{
1812 return index
.lookup_func_unit(f
);
1814 const php::Unit
* lookup_class_unit(const php::Class
& c
) const override
{
1815 return index
.lookup_class_unit(c
);
1817 const php::Class
* lookup_const_class(const php::Const
& c
) const override
{
1818 return index
.lookup_const_class(c
);
1820 const php::Class
* lookup_closure_context(const php::Class
& c
) const override
{
1821 return index
.lookup_closure_context(c
);
1823 const php::Class
* lookup_class(SString c
) const override
{
1824 return index
.lookup_class(c
);
1826 const CompactVector
<const php::Class
*>*
1827 lookup_closures(const php::Class
* c
) const override
{
1828 return index
.lookup_closures(c
);
1830 const hphp_fast_set
<const php::Func
*>*
1831 lookup_extra_methods(const php::Class
* c
) const override
{
1832 return index
.lookup_extra_methods(c
);
1834 Optional
<res::Class
> resolve_class(SString c
) const override
{
1835 return index
.resolve_class(c
);
1837 Optional
<res::Class
> resolve_class(const php::Class
& c
) const override
{
1838 return index
.resolve_class(c
);
1840 std::pair
<const php::TypeAlias
*, bool>
1841 lookup_type_alias(SString a
) const override
{
1842 auto const ta
= index
.lookup_type_alias(a
);
1843 return std::make_pair(ta
, (bool)ta
);
1845 Index::ClassOrTypeAlias
1846 lookup_class_or_type_alias(SString name
) const override
{
1847 return index
.lookup_class_or_type_alias(name
);
1849 res::Func
resolve_func_or_method(const php::Func
& f
) const override
{
1850 return index
.resolve_func_or_method(f
);
1852 res::Func
resolve_func(SString s
) const override
{
1853 return index
.resolve_func(s
);
1855 res::Func
resolve_method(Context c
, const Type
& t
, SString n
) const override
{
1856 return index
.resolve_method(c
, t
, n
);
1858 res::Func
resolve_ctor(const Type
& o
) const override
{
1859 return index
.resolve_ctor(o
);
1861 std::vector
<std::pair
<SString
, ClsConstInfo
>>
1862 lookup_class_constants(const php::Class
& c
) const override
{
1863 return index
.lookup_class_constants(c
);
1865 ClsConstLookupResult
lookup_class_constant(Context c
,
1867 const Type
& n
) const override
{
1868 return index
.lookup_class_constant(c
, t
, n
);
1870 ClsTypeConstLookupResult
1871 lookup_class_type_constant(
1874 const Index::ClsTypeConstLookupResolver
& r
= {}
1876 return index
.lookup_class_type_constant(c
, n
, r
);
1878 ClsTypeConstLookupResult
1879 lookup_class_type_constant(const php::Class
& ctx
,
1881 ConstIndex idx
) const override
{
1882 return index
.lookup_class_type_constant(ctx
, n
, idx
);
1884 std::vector
<std::pair
<SString
, ConstIndex
>>
1885 lookup_flattened_class_type_constants(const php::Class
& cls
) const override
{
1886 return index
.lookup_flattened_class_type_constants(cls
);
1888 Type
lookup_constant(Context c
, SString s
) const override
{
1889 return index
.lookup_constant(c
, s
);
1891 bool func_depends_on_arg(const php::Func
* f
, size_t p
) const override
{
1892 return index
.func_depends_on_arg(f
, p
);
1895 lookup_foldable_return_type(Context c1
,
1896 const CallContext
& c2
) const override
{
1897 return index
.lookup_foldable_return_type(c1
, c2
);
1899 Index::ReturnType
lookup_return_type(Context c
, MethodsInfo
* m
, res::Func f
,
1900 Dep d
= Dep::ReturnTy
) const override
{
1901 return index
.lookup_return_type(c
, m
, f
, d
);
1903 Index::ReturnType
lookup_return_type(Context c
,
1905 const CompactVector
<Type
>& a
,
1908 Dep d
= Dep::ReturnTy
) const override
{
1909 return index
.lookup_return_type(c
, m
, a
, t
, f
, d
);
1911 std::pair
<Index::ReturnType
, size_t>
1912 lookup_return_type_raw(const php::Func
* f
) const override
{
1913 return index
.lookup_return_type_raw(f
);
1916 lookup_closure_use_vars(const php::Func
* f
, bool m
= false) const override
{
1917 return index
.lookup_closure_use_vars(f
, m
);
1919 PropState
lookup_private_props(const php::Class
* c
,
1920 bool m
= false) const override
{
1921 return index
.lookup_private_props(c
, m
);
1923 PropState
lookup_private_statics(const php::Class
* c
,
1924 bool m
= false) const override
{
1925 return index
.lookup_private_statics(c
, m
);
1927 PropLookupResult
lookup_static(Context c1
,
1928 const PropertiesInfo
& p
,
1930 const Type
& n
) const override
{
1931 return index
.lookup_static(c1
, p
, c2
, n
);
1933 Type
lookup_public_prop(const Type
& o
, const Type
& n
) const override
{
1934 return index
.lookup_public_prop(o
, n
);
1937 merge_static_type(Context ctx
,
1938 PublicSPropMutations
& publicMutations
,
1939 PropertiesInfo
& privateProps
,
1943 bool checkUB
= false,
1944 bool ignoreConst
= false,
1945 bool mustBeReadOnly
= false) const override
{
1946 return index
.merge_static_type(
1947 ctx
, publicMutations
, privateProps
,
1948 cls
, name
, val
, checkUB
, ignoreConst
,
1952 bool using_class_dependencies() const override
{
1953 return index
.using_class_dependencies();
1957 void push_context(const Context
&) const override
{}
1958 void pop_context() const override
{}
1960 bool set_in_type_cns(bool) const override
{ return false; }
1965 //////////////////////////////////////////////////////////////////////
1967 // Represents all of the things a class or func might depend up for
1968 // analysis. If any of these things change, the class or func will be
1969 // scheduled to be analyzed again.
1970 struct AnalysisDeps
{
1971 // Some dependencies (for example, functions) can have multiple
1972 // "types" (IE, return-type, bytecode, etc). A class or func's
1973 // dependency on a function will be some union of these types.
1974 enum Type
: uint8_t {
1976 // The existence of this thing and basic metadata and nothing
1979 // Return-type of function
1981 // Return-type of function but only if it becomes a scalar
1982 ScalarRetType
= (1 << 2),
1983 // Whether any parameters are returned unchanged
1984 RetParam
= (1 << 3),
1985 // Whether any parameters are unused
1986 UnusedParams
= (1 << 4),
1987 // Bytecode of class or function
1988 Bytecode
= (1 << 5),
1991 // Some types can change as a result of analysis and hence can
1992 // trigger reschedules. The only type right now that cannot change
1993 // is "Meta" (since it represents the basic metadata of a class or
1994 // function which doesn't change).
1995 static constexpr Type kValidForChanges
= static_cast<Type
>(
1997 Type::ScalarRetType
|
1999 Type::UnusedParams
|
2003 static constexpr bool isValidForChanges(Type t
) {
2006 (t
& kValidForChanges
) == t
;
2009 // Add dependencies on various entities
2011 struct Class
{ SString name
; };
2012 struct Func
{ SString name
; };
2013 struct Constant
{ SString name
; };
2014 // Dependency on an unspecified class constant (name is the class).
2015 struct AnyClassConstant
{ SString name
; };
2017 bool add(Class
, bool inTypeCns
= false);
2019 bool add(ConstIndex
, bool inTypeCns
= false);
2021 bool add(AnyClassConstant
, bool inTypeCns
= false);
2023 Type
add(const php::Func
&, Type
);
2024 Type
add(MethRef
, Type
);
2025 Type
add(Func
, Type
);
2027 bool empty() const {
2032 clsConstants
.empty() &&
2033 constants
.empty() &&
2034 anyClsConstants
.empty() &&
2035 typeCnsClasses
.empty() &&
2036 typeCnsClsConstants
.empty() &&
2037 typeCnsAnyClsConstants
.empty();
2040 AnalysisDeps
& operator|=(const AnalysisDeps
&);
2042 template <typename SerDe
> void serde(SerDe
& sd
) {
2043 sd(funcs
, string_data_lt_func
{})
2044 (methods
, std::less
<>{})
2045 (classes
, string_data_lt_type
{})
2046 (clsConstants
, std::less
<>{})
2047 (anyClsConstants
, string_data_lt_type
{})
2048 (constants
, string_data_lt
{})
2049 (typeCnsClasses
, string_data_lt_type
{})
2050 (typeCnsClsConstants
, std::less
<>{})
2051 (typeCnsAnyClsConstants
, string_data_lt_type
{})
2056 FSStringToOneT
<Type
> funcs
;
2057 hphp_fast_map
<MethRef
, Type
, MethRef::Hash
> methods
;
2059 TSStringSet classes
;
2060 hphp_fast_set
<ConstIndex
, ConstIndex::Hasher
> clsConstants
;
2061 TSStringSet anyClsConstants
;
2062 SStringSet constants
;
2064 TSStringSet typeCnsClasses
;
2065 hphp_fast_set
<ConstIndex
, ConstIndex::Hasher
> typeCnsClsConstants
;
2066 TSStringSet typeCnsAnyClsConstants
;
2068 static Type
merge(Type
&, Type
);
2070 friend std::string
show(const AnalysisDeps
&);
2072 friend struct AnalysisIndex
;
2073 friend struct AnalysisScheduler
;
2076 // Make Type enum behave like bitset.
2077 inline AnalysisDeps::Type
operator|(AnalysisDeps::Type a
,
2078 AnalysisDeps::Type b
) {
2079 using T
= std::underlying_type_t
<AnalysisDeps::Type
>;
2080 return static_cast<AnalysisDeps::Type
>(
2081 static_cast<T
>(a
) | static_cast<T
>(b
)
2084 inline AnalysisDeps::Type
& operator|=(AnalysisDeps::Type
& a
,
2085 AnalysisDeps::Type b
) {
2089 inline AnalysisDeps::Type
operator&(AnalysisDeps::Type a
,
2090 AnalysisDeps::Type b
) {
2091 using T
= std::underlying_type_t
<AnalysisDeps::Type
>;
2092 return static_cast<AnalysisDeps::Type
>(
2093 static_cast<T
>(a
) & static_cast<T
>(b
)
2096 inline AnalysisDeps::Type
& operator&=(AnalysisDeps::Type
& a
,
2097 AnalysisDeps::Type b
) {
2101 inline AnalysisDeps::Type
operator-(AnalysisDeps::Type a
,
2102 AnalysisDeps::Type b
) {
2103 using T
= std::underlying_type_t
<AnalysisDeps::Type
>;
2104 return static_cast<AnalysisDeps::Type
>(
2105 static_cast<T
>(a
) & ~static_cast<T
>(b
)
2109 std::string
show(AnalysisDeps::Type
);
2111 //////////////////////////////////////////////////////////////////////
2113 // Represents the changes made to a class or function during
2114 // analysis. This is the other side of AnalysisDeps. A function or
2115 // class marking a change in its AnalysisChangeSet will trigger other
2116 // classes or functions which have a dependency on the same type to be
2118 struct AnalysisChangeSet
{
2119 using Type
= AnalysisDeps::Type
;
2120 using Class
= AnalysisDeps::Class
;
2122 void changed(ConstIndex
);
2123 void changed(const php::Constant
&);
2124 void changed(const php::Func
&, Type
);
2126 void fixed(ConstIndex
);
2127 void fixed(const php::Class
&);
2128 void fixed(const php::Unit
&);
2130 void typeCnsName(const php::Class
&, Class
);
2131 void typeCnsName(const php::Unit
&, Class
);
2133 void remove(const php::Func
& f
) { funcs
.erase(f
.name
); }
2135 void filter(const TSStringSet
&,
2140 template <typename SerDe
> void serde(SerDe
& sd
) {
2141 sd(funcs
, string_data_lt_func
{})
2142 (methods
, std::less
<>{})
2143 (constants
, string_data_lt
{})
2144 (clsConstants
, std::less
<>{})
2145 (fixedClsConstants
, std::less
<>{})
2146 (allClsConstantsFixed
, string_data_lt_type
{})
2147 (unitsFixed
, string_data_lt
{})
2148 (clsTypeCnsNames
, string_data_lt_type
{}, string_data_lt_type
{})
2149 (unitTypeCnsNames
, string_data_lt
{}, string_data_lt_type
{})
2153 FSStringToOneT
<Type
> funcs
;
2154 hphp_fast_map
<MethRef
, Type
, MethRef::Hash
> methods
;
2155 SStringSet constants
;
2156 hphp_fast_set
<ConstIndex
, ConstIndex::Hasher
> clsConstants
;
2157 hphp_fast_set
<ConstIndex
, ConstIndex::Hasher
> fixedClsConstants
;
2158 TSStringSet allClsConstantsFixed
;
2159 SStringSet unitsFixed
;
2161 TSStringToOneT
<TSStringSet
> clsTypeCnsNames
;
2162 TSStringToOneT
<TSStringSet
> unitTypeCnsNames
;
2164 friend struct AnalysisScheduler
;
2167 //////////////////////////////////////////////////////////////////////
2169 // Wraps up all of the inputs to a particular analysis job without
2170 // exposing implementation details. These are produced from
2171 // AnalysisScheduler::schedule().
2172 struct AnalysisInput
{
2173 AnalysisInput() = default;
2174 AnalysisInput(const AnalysisInput
&) = delete;
2175 AnalysisInput(AnalysisInput
&&) = default;
2176 AnalysisInput
& operator=(const AnalysisInput
&) = delete;
2177 AnalysisInput
& operator=(AnalysisInput
&&) = default;
2179 std::vector
<SString
> classNames() const;
2180 std::vector
<SString
> funcNames() const;
2181 std::vector
<SString
> unitNames() const;
2183 std::vector
<SString
> cinfoNames() const;
2184 std::vector
<SString
> minfoNames() const;
2186 bool empty() const {
2187 return classes
.empty() && funcs
.empty() && units
.empty();
2189 SString
key() const { return m_key
; }
2191 // Efficient set of buckets.
2193 BucketSet() = default;
2195 bool isSubset(const BucketSet
&) const;
2196 bool contains(size_t) const;
2198 size_t hash() const;
2199 bool operator==(const BucketSet
& o
) const { return buckets
== o
.buckets
; }
2204 static const BucketSet
* intern(BucketSet
);
2205 static void clearIntern();
2207 BucketSet
& operator|=(const BucketSet
&);
2209 std::string
toString() const;
2211 template <typename SerDe
> void serde(SerDe
& sd
) { sd(buckets
); }
2213 folly::sorted_vector_set
<uint32_t> buckets
;
2216 // The "presence" of an item in buckets. This is used for permission
2217 // checks on the worker. An item can only use information about
2218 // another item if the second item is present in every bucket the
2219 // first item is in. This can be done cheaply using a subset check.
2220 struct BucketPresence
{
2221 const BucketSet
* present
;
2222 const BucketSet
* withBC
;
2223 const BucketSet
* process
;
2224 void serde(BlobEncoder
&);
2225 void serde(BlobDecoder
&);
2226 static void serdeStart();
2227 static void serdeEnd();
2232 Meta(const Meta
&) = delete;
2233 Meta(Meta
&&) = default;
2234 Meta
& operator=(const Meta
&) = delete;
2235 Meta
& operator=(Meta
&&) = default;
2237 TSStringSet badClasses
;
2238 FSStringSet badFuncs
;
2239 SStringSet badConstants
;
2240 TSStringToOneT
<AnalysisDeps
> classDeps
;
2241 FSStringToOneT
<AnalysisDeps
> funcDeps
;
2242 SStringToOneT
<AnalysisDeps
> unitDeps
;
2244 using BucketVec
= std::vector
<std::pair
<SString
, BucketPresence
>>;
2245 BucketVec classBuckets
;
2246 BucketVec funcBuckets
;
2247 BucketVec unitBuckets
;
2248 BucketVec badConstantBuckets
;
2250 TSStringSet processDepCls
;
2251 FSStringSet processDepFunc
;
2252 SStringSet processDepUnit
;
2256 template <typename SerDe
> void serde(SerDe
& sd
) {
2257 ScopedStringDataIndexer _
;
2258 BucketPresence::serdeStart();
2259 SCOPE_EXIT
{ BucketPresence::serdeEnd(); };
2260 sd(badClasses
, string_data_lt_type
{})
2261 (badFuncs
, string_data_lt_func
{})
2262 (badConstants
, string_data_lt
{})
2263 (classDeps
, string_data_lt_type
{})
2264 (funcDeps
, string_data_lt_func
{})
2265 (unitDeps
, string_data_lt
{})
2269 (badConstantBuckets
)
2270 (processDepCls
, string_data_lt_type
{})
2271 (processDepFunc
, string_data_lt_func
{})
2272 (processDepUnit
, string_data_lt
{})
2277 Meta
takeMeta() { return std::move(meta
); }
2279 // Produces input for the analysis job.
2280 using Tuple
= std::tuple
<
2281 UniquePtrRefVec
<php::Class
>,
2282 UniquePtrRefVec
<php::Func
>,
2283 UniquePtrRefVec
<php::Unit
>,
2284 UniquePtrRefVec
<php::ClassBytecode
>,
2285 UniquePtrRefVec
<php::FuncBytecode
>,
2286 RefVec
<AnalysisIndexCInfo
>,
2287 RefVec
<AnalysisIndexFInfo
>,
2288 RefVec
<AnalysisIndexMInfo
>,
2289 UniquePtrRefVec
<php::Class
>,
2290 UniquePtrRefVec
<php::Func
>,
2291 UniquePtrRefVec
<php::Unit
>,
2292 extern_worker::Ref
<Meta
>
2294 Tuple
toTuple(extern_worker::Ref
<Meta
>);
2296 SString m_key
{nullptr};
2297 const Index::IndexData
* index
{nullptr};
2299 enum class Kind
: uint8_t {
2302 Bytecode
= (1 << 1),
2307 TSStringToOneT
<Kind
> classes
;
2308 FSStringToOneT
<Kind
> funcs
;
2309 SStringToOneT
<Kind
> units
;
2311 friend Kind
operator|(Kind k1
, Kind k2
) {
2312 return Kind((uint8_t)k1
| (uint8_t)k2
);
2314 friend Kind
& operator|=(Kind
& k1
, Kind k2
) {
2315 return (k1
= Kind((uint8_t)k1
| (uint8_t)k2
));
2317 friend Kind
operator&(Kind k1
, Kind k2
) {
2318 return Kind((uint8_t)k1
& (uint8_t)k2
);
2320 friend bool any(Kind
);
2324 friend struct AnalysisScheduler
;
2327 inline bool any(AnalysisInput::Kind k
) {
2328 return k
!= AnalysisInput::Kind::None
;
2331 std::string
show(const AnalysisInput::BucketPresence
&);
2333 //////////////////////////////////////////////////////////////////////
2335 // These represent the output of an analysis job without exposing
2336 // implementation details. This is consumed by
2337 // AnalysisScheduler::record().
2338 struct AnalysisOutput
{
2340 std::vector
<AnalysisDeps
> funcDeps
;
2341 std::vector
<AnalysisDeps
> classDeps
;
2342 std::vector
<AnalysisDeps
> unitDeps
;
2343 AnalysisChangeSet changed
;
2344 FSStringSet removedFuncs
;
2345 TSStringToOneT
<TSStringSet
> cnsBases
;
2346 template <typename SerDe
> void serde(SerDe
& sd
) {
2347 ScopedStringDataIndexer _
;
2352 (removedFuncs
, string_data_lt_func
{})
2353 (cnsBases
, string_data_lt_type
{}, string_data_lt_type
{})
2358 std::vector
<SString
> classNames
;
2359 std::vector
<SString
> cinfoNames
;
2360 std::vector
<SString
> minfoNames
;
2362 UniquePtrRefVec
<php::Class
> classes
;
2363 UniquePtrRefVec
<php::ClassBytecode
> clsBC
;
2364 RefVec
<AnalysisIndexCInfo
> cinfos
;
2366 std::vector
<SString
> funcNames
;
2367 UniquePtrRefVec
<php::Func
> funcs
;
2368 UniquePtrRefVec
<php::FuncBytecode
> funcBC
;
2369 RefVec
<AnalysisIndexFInfo
> finfos
;
2371 RefVec
<AnalysisIndexMInfo
> minfos
;
2373 std::vector
<SString
> unitNames
;
2374 UniquePtrRefVec
<php::Unit
> units
;
2379 //////////////////////////////////////////////////////////////////////
2381 // Scheduler to coordinate analysis rounds. This encapsulates all of
2382 // the logic to track dependencies and changes in a way so that the
2383 // user doesn't need to know any implementation details.
2384 struct AnalysisScheduler
{
2385 explicit AnalysisScheduler(Index
&);
2386 ~AnalysisScheduler();
2388 // Register a class or function with the given name to be
2389 // tracked. If a class or function isn't tracked, it won't be
2390 // eligible for scheduling (though it still might be pulled in as a
2392 void registerClass(SString
);
2393 void registerFunc(SString
);
2394 void registerUnit(SString
);
2396 // Record the output of an analysis job. This can be called in a
2397 // multi-threaded context.
2398 void record(AnalysisOutput
);
2400 // Called when all analysis jobs for this round have finished. This
2401 // calculates what has changed and what needs to be re-analyzed.
2402 void recordingDone();
2404 // Schedule the work that needs to run into buckets of (roughly) the
2406 std::vector
<AnalysisInput
> schedule(size_t bucketSize
,
2407 size_t maxBucketSize
);
2409 size_t workItems() const { return totalWorkItems
; }
2412 using Type
= AnalysisDeps::Type
;
2414 // Represents a func, class, or unit. The most basic unit of
2423 DepState(SString name
, Kind kind
) : name
{name
}, kind
{kind
} {}
2427 // If toSchedule is true, then the func/class/unit has changed
2429 bool toSchedule
{true};
2432 // These wrap a DepState, but also include information about what
2433 // changed (which varies according to the type).
2435 explicit FuncState(SString name
) : depState
{name
, DepState::Func
} {}
2440 explicit ClassState(SString name
) : depState
{name
, DepState::Class
} {}
2442 CompactVector
<Type
> methodChanges
;
2443 boost::dynamic_bitset
<> cnsChanges
;
2444 boost::dynamic_bitset
<> cnsFixed
;
2445 TSStringSet typeCnsNames
;
2446 bool allCnsFixed
{false};
2449 explicit UnitState(SString name
) : depState
{name
, DepState::Unit
} {}
2451 TSStringSet typeCnsNames
;
2455 // TraceState contains all the state necessary for the scheduling
2456 // algorithm. A TraceState can represent multiple DepStates. For
2457 // scheduling purposes we might want certain items always be
2458 // processed together. This can be accomplished by giving them all
2459 // the same TraceState.
2463 TinyVector
<const DepState
*> depStates
;
2464 bool eligible
{false};
2465 CopyableAtomic
<bool> leaf
{true};
2466 CopyableAtomic
<bool> covered
{false};
2467 AnalysisInput::BucketPresence buckets
;
2472 void findToSchedule();
2473 void resetChanges();
2475 void recordChanges(const AnalysisOutput
&);
2476 void updateDepState(AnalysisOutput
&);
2478 void addClassToInput(SString
, AnalysisInput
&) const;
2479 void addFuncToInput(SString
, AnalysisInput
&) const;
2480 void addUnitToInput(SString
, AnalysisInput
&) const;
2481 void addDepConstantToInput(SString
, SString
, AnalysisInput
&) const;
2482 void addDepUnitToInput(SString
, SString
, AnalysisInput
&) const;
2483 void addDepClassToInput(SString
, SString
, bool, AnalysisInput
&,
2484 bool = false) const;
2485 void addDepFuncToInput(SString
, SString
, Type
, AnalysisInput
&) const;
2486 void addTraceDepToInput(const DepState
&, AnalysisInput
&) const;
2487 void addDepsToInput(const DepState
&, AnalysisInput
&) const;
2488 void addAllDepsToInput(AnalysisInput
&) const;
2489 void addDepsMeta(const DepState
&, AnalysisInput
&) const;
2491 template <typename F
>
2492 void onTransitiveDep(SString
, const DepState
&, const F
&) const;
2493 void addAllDeps(TSStringSet
&, const DepState
&) const;
2494 void addDepsForTypeCns(TraceState
&);
2496 const TraceState
* lookupTrace(DepState::Kind
, SString
) const;
2498 const TraceState
* traceForClass(SString
) const;
2499 const TraceState
* traceForFunc(SString
) const;
2500 const TraceState
* traceForUnit(SString
) const;
2501 const TraceState
* traceForConstant(SString
) const;
2502 const TraceState
* traceForTypeAlias(SString
) const;
2503 const TraceState
* traceForClassOrTypeAlias(SString
) const;
2504 const TraceState
* traceForDepState(const DepState
&) const;
2506 Either
<const ClassState
*, const UnitState
*>
2507 stateForClassOrTypeAlias(SString
) const;
2509 enum class Presence
{
2515 Presence
presenceOf(const AnalysisInput::BucketPresence
&,
2516 const AnalysisInput::BucketPresence
&) const;
2517 Presence
presenceOfClass(const TraceState
&, SString
) const;
2518 Presence
presenceOfClassOrTypeAlias(const TraceState
&, SString
) const;
2519 Presence
presenceOfFunc(const TraceState
&, SString
) const;
2520 Presence
presenceOfConstant(const TraceState
&, SString
) const;
2527 std::vector
<SString
> tracePass4();
2528 std::vector
<Bucket
> tracePass5(size_t, size_t, std::vector
<SString
>);
2530 struct InputsAndUntracked
{
2531 std::vector
<AnalysisInput
> inputs
;
2532 std::vector
<SString
> untrackedFuncs
;
2533 std::vector
<SString
> untrackedClasses
;
2534 std::vector
<SString
> untrackedUnits
;
2535 std::vector
<SString
> badConstants
;
2537 InputsAndUntracked
tracePass6(const std::vector
<Bucket
>&);
2538 void tracePass7(InputsAndUntracked
&);
2539 void tracePass8(std::vector
<Bucket
>, std::vector
<AnalysisInput
>&);
2540 void tracePass9(const std::vector
<AnalysisInput
>&);
2542 void scheduleTraces(size_t);
2544 void maybeDumpTraces() const;
2550 std::vector
<SString
> classNames
;
2551 std::vector
<SString
> funcNames
;
2552 std::vector
<SString
> unitNames
;
2553 std::vector
<SString
> traceNames
;
2555 // Keep the address of the states (and therefore their contained
2556 // DepStates) the same, so we can keep pointers to them.
2557 FSStringToOneNodeT
<FuncState
> funcState
;
2558 TSStringToOneNodeT
<ClassState
> classState
;
2559 SStringToOneNodeT
<UnitState
> unitState
;
2560 SStringToOneNodeT
<std::atomic
<bool>> cnsChanged
;
2562 TSStringToOneT
<TraceState
> traceState
;
2565 using B
= AnalysisInput::BucketPresence
;
2566 FSStringToOneT
<B
> funcs
;
2567 TSStringToOneT
<B
> classes
;
2568 SStringToOneT
<B
> units
;
2569 SStringToOneT
<B
> badConstants
;
2571 Untracked untracked
;
2573 CopyableAtomic
<size_t> totalWorkItems
;
2575 FSStringSet funcsToRemove
;
2577 // Keep AnalysisScheduler moveable
2578 std::unique_ptr
<std::mutex
> lock
;
2581 //////////////////////////////////////////////////////////////////////
2583 // Worklist for handling dependencies within the same analysis job. If
2584 // a class or function updates something, if another class or function
2585 // within the same job has a dependency, that will be re-analyzed
2586 // within the same job. This speeds up convergence by avoiding another
2587 // whole roundtrip through the scheduler.
2588 struct AnalysisWorklist
{
2591 void schedule(FuncClsUnit
);
2593 hphp_fast_set
<FuncClsUnit
, FuncClsUnitHasher
> in
;
2594 std::deque
<FuncClsUnit
> list
;
2597 //////////////////////////////////////////////////////////////////////
2599 // Equivalent of Index, but for an analysis job.
2600 struct AnalysisIndex
{
2601 template<typename T
> using V
= std::vector
<T
>;
2602 template<typename T
> using VU
= V
<std::unique_ptr
<T
>>;
2610 AnalysisIndex(AnalysisWorklist
&,
2614 VU
<php::ClassBytecode
>,
2615 VU
<php::FuncBytecode
>,
2616 V
<AnalysisIndexCInfo
>,
2617 V
<AnalysisIndexFInfo
>,
2618 V
<AnalysisIndexMInfo
>,
2622 AnalysisInput::Meta
,
2626 // Must be called in the worker's init() and fini() functions.
2627 static void start();
2631 bool frozen() const;
2633 const php::Unit
& lookup_func_unit(const php::Func
&) const;
2635 const php::Unit
& lookup_class_unit(const php::Class
&) const;
2637 const php::Unit
& lookup_unit(SString
) const;
2639 const php::Class
* lookup_const_class(const php::Const
&) const;
2641 const php::Class
& lookup_closure_context(const php::Class
&) const;
2643 const php::Class
* lookup_class(SString
) const;
2645 Optional
<res::Class
> resolve_class(SString
) const;
2646 Optional
<res::Class
> resolve_class(const php::Class
&) const;
2648 Type
lookup_constant(SString
) const;
2650 std::vector
<std::pair
<SString
, ClsConstInfo
>>
2651 lookup_class_constants(const php::Class
&) const;
2653 ClsConstLookupResult
2654 lookup_class_constant(const Type
&, const Type
&) const;
2656 ClsTypeConstLookupResult
2657 lookup_class_type_constant(
2660 const Index::ClsTypeConstLookupResolver
& resolver
= {}) const;
2662 ClsTypeConstLookupResult
2663 lookup_class_type_constant(const php::Class
&, SString
, ConstIndex
) const;
2665 std::vector
<std::pair
<SString
, ConstIndex
>>
2666 lookup_flattened_class_type_constants(const php::Class
&) const;
2668 PropState
lookup_private_props(const php::Class
&) const;
2669 PropState
lookup_private_statics(const php::Class
&) const;
2671 Index::ReturnType
lookup_return_type(MethodsInfo
*, res::Func
) const;
2672 Index::ReturnType
lookup_return_type(MethodsInfo
*,
2673 const CompactVector
<Type
>&,
2676 Index::ReturnType
lookup_foldable_return_type(const CallContext
&) const;
2678 std::pair
<Index::ReturnType
, size_t>
2679 lookup_return_type_raw(const php::Func
& f
) const;
2681 bool func_depends_on_arg(const php::Func
&, size_t) const;
2683 res::Func
resolve_func(SString
) const;
2685 res::Func
resolve_method(const Type
&, SString
) const;
2686 res::Func
resolve_ctor(const Type
&) const;
2687 res::Func
resolve_func_or_method(const php::Func
&) const;
2689 std::pair
<const php::TypeAlias
*, bool> lookup_type_alias(SString
) const;
2691 Index::ClassOrTypeAlias
lookup_class_or_type_alias(SString
) const;
2694 merge_static_type(PublicSPropMutations
&,
2699 bool checkUB
= false,
2700 bool ignoreConst
= false,
2701 bool mustBeReadOnly
= false) const;
2703 void refine_constants(const FuncAnalysisResult
&);
2704 void refine_class_constants(const FuncAnalysisResult
&);
2705 void refine_return_info(const FuncAnalysisResult
&);
2706 void update_prop_initial_values(const FuncAnalysisResult
&);
2707 void update_type_consts(const ClassAnalysis
&);
2708 void update_bytecode(FuncAnalysisResult
&);
2709 void update_type_aliases(const UnitAnalysis
&);
2711 using Output
= extern_worker::Multi
<
2712 extern_worker::Variadic
<std::unique_ptr
<php::Class
>>,
2713 extern_worker::Variadic
<std::unique_ptr
<php::Func
>>,
2714 extern_worker::Variadic
<std::unique_ptr
<php::Unit
>>,
2715 extern_worker::Variadic
<std::unique_ptr
<php::ClassBytecode
>>,
2716 extern_worker::Variadic
<std::unique_ptr
<php::FuncBytecode
>>,
2717 extern_worker::Variadic
<AnalysisIndexCInfo
>,
2718 extern_worker::Variadic
<AnalysisIndexFInfo
>,
2719 extern_worker::Variadic
<AnalysisIndexMInfo
>,
2720 AnalysisOutput::Meta
2726 std::unique_ptr
<IndexData
> const m_data
;
2728 void initialize_worklist(const AnalysisInput::Meta
&,
2729 std::vector
<SString
>,
2730 std::vector
<SString
>,
2731 std::vector
<SString
>);
2733 void push_context(const Context
&);
2736 bool set_in_type_cns(bool);
2738 template <typename P
, typename G
>
2739 res::Func
rfunc_from_dcls(const DCls
&, SString
, const P
&, const G
&) const;
2741 Type
unserialize_type(Type
) const;
2743 friend struct AnalysisIndexAdaptor
;
2746 //////////////////////////////////////////////////////////////////////
2748 struct AnalysisIndexAdaptor
: public IIndex
{
2749 explicit AnalysisIndexAdaptor(const AnalysisIndex
& index
)
2750 : index
{const_cast<AnalysisIndex
&>(index
)} {}
2752 bool frozen() const override
;
2754 const php::Unit
* lookup_func_unit(const php::Func
&) const override
;
2755 const php::Unit
* lookup_class_unit(const php::Class
&) const override
;
2756 const php::Class
* lookup_const_class(const php::Const
&) const override
;
2757 const php::Class
* lookup_closure_context(const php::Class
&) const override
;
2758 const php::Class
* lookup_class(SString
) const override
;
2760 const CompactVector
<const php::Class
*>*
2761 lookup_closures(const php::Class
*) const override
;
2763 const hphp_fast_set
<const php::Func
*>*
2764 lookup_extra_methods(const php::Class
*) const override
;
2766 Optional
<res::Class
> resolve_class(SString
) const override
;
2767 Optional
<res::Class
> resolve_class(const php::Class
&) const override
;
2769 std::pair
<const php::TypeAlias
*, bool>
2770 lookup_type_alias(SString
) const override
;
2772 Index::ClassOrTypeAlias
lookup_class_or_type_alias(SString
) const override
;
2774 res::Func
resolve_func_or_method(const php::Func
&) const override
;
2775 res::Func
resolve_func(SString
) const override
;
2776 res::Func
resolve_method(Context
, const Type
&, SString
) const override
;
2777 res::Func
resolve_ctor(const Type
&) const override
;
2779 std::vector
<std::pair
<SString
, ClsConstInfo
>>
2780 lookup_class_constants(const php::Class
&) const override
;
2782 ClsConstLookupResult
lookup_class_constant(Context
,
2784 const Type
&) const override
;
2786 ClsTypeConstLookupResult
2787 lookup_class_type_constant(
2790 const Index::ClsTypeConstLookupResolver
& r
= {}) const override
;
2792 ClsTypeConstLookupResult
2793 lookup_class_type_constant(const php::Class
&,
2795 ConstIndex
) const override
;
2797 std::vector
<std::pair
<SString
, ConstIndex
>>
2798 lookup_flattened_class_type_constants(const php::Class
&) const override
;
2800 Type
lookup_constant(Context
, SString
) const override
;
2801 bool func_depends_on_arg(const php::Func
*, size_t) const override
;
2803 lookup_foldable_return_type(Context
, const CallContext
&) const override
;
2804 Index::ReturnType
lookup_return_type(Context
, MethodsInfo
*, res::Func
,
2805 Dep d
= Dep::ReturnTy
) const override
;
2806 Index::ReturnType
lookup_return_type(Context
,
2808 const CompactVector
<Type
>&,
2811 Dep d
= Dep::ReturnTy
) const override
;
2813 std::pair
<Index::ReturnType
, size_t>
2814 lookup_return_type_raw(const php::Func
*) const override
;
2817 lookup_closure_use_vars(const php::Func
*, bool m
= false) const override
;
2819 PropState
lookup_private_props(const php::Class
*, bool m
= false) const override
;
2820 PropState
lookup_private_statics(const php::Class
*, bool m
= false) const override
;
2822 PropLookupResult
lookup_static(Context
,
2823 const PropertiesInfo
&,
2825 const Type
&) const override
;
2827 Type
lookup_public_prop(const Type
&, const Type
&) const override
;
2830 merge_static_type(Context
,
2831 PublicSPropMutations
&,
2836 bool checkUB
= false,
2837 bool ignoreConst
= false,
2838 bool mustBeReadOnly
= false) const override
;
2840 bool using_class_dependencies() const override
;
2843 void push_context(const Context
&) const override
;
2844 void pop_context() const override
;
2846 bool set_in_type_cns(bool) const override
;
2848 AnalysisIndex
& index
;
2851 //////////////////////////////////////////////////////////////////////
2854 * Used for collecting all mutations of public static property types.
2856 struct PublicSPropMutations
{
2857 explicit PublicSPropMutations(bool enabled
= true);
2859 friend struct Index
;
2864 bool operator==(const KnownKey
& o
) const {
2865 return cinfo
== o
.cinfo
&& prop
== o
.prop
;
2868 size_t operator()(const KnownKey
& k
) const {
2869 return folly::hash::hash_combine(k
.cinfo
, k
.prop
);
2874 using UnknownMap
= SStringToOneT
<Type
>;
2875 using KnownMap
= hphp_fast_map
<KnownKey
, Type
, KnownKey::Hasher
>;
2877 // Public static property mutations are actually rare, so defer allocating the
2878 // maps until we actually see one.
2880 bool m_nothing_known
{false};
2881 UnknownMap m_unknown
;
2884 std::unique_ptr
<Data
> m_data
;
2889 void mergeKnown(const ClassInfo
* ci
, const php::Prop
& prop
, const Type
& val
);
2890 void mergeUnknownClass(SString prop
, const Type
& val
);
2891 void mergeUnknown(Context
);
2894 //////////////////////////////////////////////////////////////////////