Updating submodules
[hiphop-php.git] / hphp / hhbbc / index.h
bloba780d645aed934ba8d81d9d8e267f13e22917584
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 +----------------------------------------------------------------------+
16 #pragma once
18 #include <memory>
19 #include <tuple>
20 #include <vector>
21 #include <map>
22 #include <exception>
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 //////////////////////////////////////////////////////////////////////
48 struct Index;
49 struct AnalysisIndex;
50 struct PublicSPropMutations;
51 struct FuncAnalysisResult;
52 struct ClassAnalysis;
53 struct UnitAnalysis;
54 struct Context;
55 struct ContextHash;
56 struct CallContext;
57 struct PropertiesInfo;
58 struct MethodsInfo;
60 namespace php {
61 struct Class;
62 struct ClassBytecode;
63 struct Prop;
64 struct Const;
65 struct Func;
66 struct FuncBytecode;
67 struct Unit;
68 struct Program;
69 struct TypeAlias;
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 */
89 ReturnTy = (1u << 0),
90 /* This dependency should trigger when a DefCns is resolved */
91 ConstVal = (1u << 1),
92 /* This dependency should trigger when a class constant is resolved */
93 ClsConst = (1u << 2),
94 /* This dependency should trigger when the bad initial prop value bit for a
95 * class changes */
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 {
114 Func,
115 Class,
116 Prop,
117 FuncFamily
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
150 * Type.
152 struct PropStateElem {
153 Type ty;
154 const TypeConstraint* tc = nullptr;
155 Attr attrs;
156 bool everModified;
158 bool operator==(const PropStateElem& o) const {
159 return
160 ty == o.ty &&
161 tc == o.tc &&
162 attrs == o.attrs &&
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);
187 a.ty |= b.ty;
188 a.found |= b.found;
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;
194 return a;
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
207 // can throw.
210 inline PropMergeResult& operator|=(PropMergeResult& a,
211 const PropMergeResult& b) {
212 a.adjusted |= b.adjusted;
213 a.throws |= b.throws;
214 return a;
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
224 // scalar).
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) {
231 a.ty |= b.ty;
232 a.found |= b.found;
233 a.mightThrow |= b.mightThrow;
234 return a;
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;
259 a.found |= b.found;
260 return a;
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).
272 struct ConstIndex {
273 using Idx = uint32_t;
275 ConstIndex() = default;
276 ConstIndex(SString cls, Idx idx)
277 : cls{cls}, idx{idx} {}
279 SString cls;
280 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);
290 struct Hasher {
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) {
297 sd(cls)(idx);
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 {
308 Type type;
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 {
321 SArray resolved;
322 bool contextInsensitive;
323 ConstIndex from;
326 using ResolvedClsTypeConsts = CompactVector<ResolvedClsTypeConst>;
328 //////////////////////////////////////////////////////////////////////
330 struct ResolvedTypeAlias {
331 size_t idx;
332 SArray resolved;
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).
343 struct MethRef {
344 MethRef() = default;
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);
371 struct Hash {
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) {
378 sd(cls)(idx);
382 using MethRefSet = hphp_fast_set<MethRef, MethRef::Hash>;
384 std::string show(const MethRef&);
386 //////////////////////////////////////////////////////////////////////
388 struct PropInitInfo {
389 TypedValue val;
390 bool satisfies;
391 bool deepInit;
393 using ResolvedPropInits = CompactVector<std::pair<size_t, PropInitInfo>>;
395 //////////////////////////////////////////////////////////////////////
397 // private types
398 struct ClassGraph;
399 struct ClassInfo;
400 struct ClassInfo2;
401 struct FuncInfo2;
402 struct FuncFamily2;
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
409 // worker.
410 template <typename T> struct AnalysisIndexParam {
411 AnalysisIndexParam() = default;
413 void serde(BlobEncoder&) const;
414 void serde(BlobDecoder&);
415 private:
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.
437 namespace res {
440 * A resolved runtime Class, for a particular php::Class.
442 * Provides various lookup tables that allow querying the Class'
443 * information.
445 struct 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,
471 bool nonRegularL,
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,
494 bool nonRegularL,
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
509 * instantiated).
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
517 * be final.
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,
608 * or std::nullopt.
610 Optional<Class> parent() const;
613 * Returns the php::Class for this Class if there is one, or
614 * nullptr.
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,
663 bool isSub1,
664 bool isSub2,
665 bool nonRegular1,
666 bool nonRegular2);
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,
678 bool nonRegular1,
679 bool nonRegular2,
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
686 * classes.
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,
697 bool nonRegular1,
698 bool nonRegular2);
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();
735 #ifndef NDEBUG
736 bool isMissingDebug() const;
737 #endif
739 private:
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*>;
753 E opaque;
755 explicit Class(ClassGraph);
756 explicit Class(E o): opaque{o} {}
760 * This is an abstraction layer to represent possible runtime function
761 * resolutions.
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.
768 struct Func {
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
781 * exist.
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
808 * function.
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;
845 * Coeffects
847 const RuntimeCoeffects* requiredCoeffects() const;
848 // Returns nullptr if we cant tell whether there are coeffect rules
849 const CompactVector<CoeffectRule>* coeffectRules() const;
851 struct FuncInfo;
852 struct FuncFamily;
854 private:
855 friend struct ::HPHP::HHBBC::Index;
856 friend struct ::HPHP::HHBBC::AnalysisIndex;
858 struct FuncName {
859 SString name;
861 struct MethodName {
862 SString cls;
863 SString name;
865 struct Fun {
866 const FuncInfo* finfo;
868 struct Fun2 {
869 const FuncInfo2* finfo;
871 struct Method {
872 const FuncInfo* finfo;
874 struct Method2 {
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
886 struct MissingFunc {
887 SString name;
889 struct MissingMethod {
890 SString cls;
891 SString name;
893 // Group of methods (a wrapper around a FuncFamily).
894 struct MethodFamily {
895 FuncFamily* family;
896 bool regularOnly;
898 struct MethodFamily2 {
899 const FuncFamily2* family;
900 bool regularOnly;
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.
905 struct Isect {
906 CompactVector<FuncFamily*> families;
907 bool regularOnly{false};
909 struct Isect2 {
910 CompactVector<const FuncFamily2*> families;
911 bool regularOnly{false};
913 using Rep = boost::variant< FuncName
914 , MethodName
915 , Fun
916 , Fun2
917 , Method
918 , Method2
919 , MethodFamily
920 , MethodFamily2
921 , MethodOrMissing
922 , MethodOrMissing2
923 , MissingFunc
924 , MissingMethod
925 , Isect
926 , Isect2
929 private:
930 explicit Func(Rep);
931 friend std::string show(const Func&);
933 private:
934 Rep val;
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.
952 struct TypeMapping {
953 LSString name;
954 // If an enum, this is the same value as name. Otherwise it's the
955 // first enum encountered when resolving a type-alias.
956 LSString firstEnum;
957 TypeConstraint value;
958 bool isTypeAlias;
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).
986 struct Index {
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).
991 struct Input {
992 template <typename T> using R = extern_worker::Ref<std::unique_ptr<T>>;
994 struct ClassMeta {
995 R<php::Class> cls;
996 LSString name;
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;
1002 LSString unit;
1003 bool has86init;
1004 // If this class is an enum, the type-mapping representing it's
1005 // base type.
1006 Optional<TypeMapping> typeMapping;
1007 std::vector<SString> unresolvedTypes;
1010 struct FuncMeta {
1011 R<php::Func> func;
1012 LSString name;
1013 LSString unit;
1014 bool methCaller;
1015 std::vector<SString> unresolvedTypes;
1018 struct UnitMeta {
1019 R<php::Unit> unit;
1020 LSString name;
1021 std::vector<TypeMapping> typeMappings;
1022 std::vector<std::pair<SString, bool>> constants;
1025 struct FuncBytecodeMeta {
1026 R<php::FuncBytecode> bc;
1027 LSString name;
1028 LSString unit;
1029 bool methCaller;
1032 struct ClassBytecodeMeta {
1033 R<php::ClassBytecode> bc;
1034 LSString name;
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.
1050 Index(Input,
1051 Config,
1052 std::unique_ptr<TicketExecutor>,
1053 std::unique_ptr<extern_worker::Client>,
1054 DisposeCallback,
1055 StructuredLogEntry*);
1056 ~Index();
1058 Index(const Index&) = delete;
1059 Index(Index&&);
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
1083 * anymore.
1085 * These are the functions to query and transition to frozen state.
1087 bool frozen() const;
1088 void freeze();
1089 void thaw();
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
1100 * stage.
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"
1107 * way.
1109 void make_local();
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
1136 * them.
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
1158 * constant.
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
1179 * given Unit.
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
1196 * php::Class.
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
1203 * traits.
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;
1231 bool maybeExists;
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
1238 * res::Func.
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,
1257 * etc.
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
1278 * analysis.
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
1288 * dependency.
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(
1311 const Type& cls,
1312 const Type& name,
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.
1343 struct ReturnType {
1344 Type t;
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,
1371 MethodsInfo*,
1372 const CompactVector<Type>& args,
1373 const Type& context,
1374 res::Func,
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
1398 * unit).
1400 CompactVector<Type>
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
1417 * unit).
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
1434 * unit).
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,
1452 const Type& cls,
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
1484 * dependencies.
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
1500 * constraints.
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,
1509 const Type& cls,
1510 const Type& name,
1511 const Type& val,
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
1530 * by this 86cinit.
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
1553 * analysis.
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
1566 * analysis.
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
1578 * of analysis.
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,
1584 const PropState&);
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,
1594 const PropState&);
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
1618 * type has changed.
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&);
1632 struct IndexData;
1634 private:
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;
1644 private:
1645 std::unique_ptr<IndexData> m_data;
1648 //////////////////////////////////////////////////////////////////////
1650 // Abstracts away particular Index implementation from the analysis
1651 // logic.
1652 struct IIndex {
1653 IIndex() = default;
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(
1702 const Type& cls,
1703 const Type& name,
1704 const Index::ClsTypeConstLookupResolver& resolver = {}
1705 ) const = 0;
1707 virtual ClsTypeConstLookupResult lookup_class_type_constant(
1708 const php::Class&,
1709 SString,
1710 ConstIndex
1711 ) const = 0;
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,
1729 MethodsInfo*,
1730 const CompactVector<Type>& args,
1731 const Type& context,
1732 res::Func,
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,
1750 const Type& cls,
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,
1759 const Type& cls,
1760 const Type& name,
1761 const Type& val,
1762 bool checkUB = false,
1763 bool ignoreConst = false,
1764 bool mustBeReadOnly = false) const = 0;
1766 virtual bool using_class_dependencies() const = 0;
1767 private:
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(); }
1786 private:
1787 const IIndex& index;
1790 //////////////////////////////////////////////////////////////////////
1792 // RAII class to mark that we're resolving a class' type-constants.
1793 struct InTypeCns {
1794 explicit InTypeCns(const IIndex& index, bool b = true)
1795 : index{index}
1796 , was{index.set_in_type_cns(b)} {}
1797 ~InTypeCns() { index.set_in_type_cns(was); }
1798 private:
1799 const IIndex& index;
1800 bool was;
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,
1866 const Type& t,
1867 const Type& n) const override {
1868 return index.lookup_class_constant(c, t, n);
1870 ClsTypeConstLookupResult
1871 lookup_class_type_constant(
1872 const Type& c,
1873 const Type& n,
1874 const Index::ClsTypeConstLookupResolver& r = {}
1875 ) const override {
1876 return index.lookup_class_type_constant(c, n, r);
1878 ClsTypeConstLookupResult
1879 lookup_class_type_constant(const php::Class& ctx,
1880 SString n,
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);
1894 Index::ReturnType
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,
1904 MethodsInfo* m,
1905 const CompactVector<Type>& a,
1906 const Type& t,
1907 res::Func f,
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);
1915 CompactVector<Type>
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,
1929 const Type& c2,
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);
1936 PropMergeResult
1937 merge_static_type(Context ctx,
1938 PublicSPropMutations& publicMutations,
1939 PropertiesInfo& privateProps,
1940 const Type& cls,
1941 const Type& name,
1942 const Type& val,
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,
1949 mustBeReadOnly
1952 bool using_class_dependencies() const override {
1953 return index.using_class_dependencies();
1956 private:
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; }
1962 Index& index;
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 {
1975 None = 0,
1976 // The existence of this thing and basic metadata and nothing
1977 // more.
1978 Meta = (1 << 0),
1979 // Return-type of function
1980 RetType = (1 << 1),
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>(
1996 Type::RetType |
1997 Type::ScalarRetType |
1998 Type::RetParam |
1999 Type::UnusedParams |
2000 Type::Bytecode
2003 static constexpr bool isValidForChanges(Type t) {
2004 return
2005 t != Type::None &&
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);
2020 bool add(Constant);
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 {
2028 return
2029 funcs.empty() &&
2030 methods.empty() &&
2031 classes.empty() &&
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{})
2055 private:
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) {
2086 a = a | b;
2087 return a;
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) {
2098 a = a & b;
2099 return a;
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
2117 // re-analyzed.
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&,
2136 const FSStringSet&,
2137 const SStringSet&,
2138 const SStringSet&);
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{})
2152 private:
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.
2192 struct BucketSet {
2193 BucketSet() = default;
2195 bool isSubset(const BucketSet&) const;
2196 bool contains(size_t) const;
2197 bool empty() const;
2198 size_t hash() const;
2199 bool operator==(const BucketSet& o) const { return buckets == o.buckets; }
2201 void add(size_t);
2202 void clear();
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); }
2212 private:
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();
2230 struct Meta {
2231 Meta() = default;
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;
2254 uint32_t bucketIdx;
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{})
2266 (classBuckets)
2267 (funcBuckets)
2268 (unitBuckets)
2269 (badConstantBuckets)
2270 (processDepCls, string_data_lt_type{})
2271 (processDepFunc, string_data_lt_func{})
2272 (processDepUnit, string_data_lt{})
2273 (bucketIdx)
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>);
2295 private:
2296 SString m_key{nullptr};
2297 const Index::IndexData* index{nullptr};
2299 enum class Kind : uint8_t {
2300 None = 0,
2301 Rep = (1 << 0),
2302 Bytecode = (1 << 1),
2303 Info = (1 << 2),
2304 Dep = (1 << 3),
2305 MInfo = (1 << 4)
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);
2322 Meta meta;
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 {
2339 struct Meta {
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 _;
2348 sd(funcDeps)
2349 (classDeps)
2350 (unitDeps)
2351 (changed)
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;
2376 Meta meta;
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
2391 // dependency).
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
2405 // given size.
2406 std::vector<AnalysisInput> schedule(size_t bucketSize,
2407 size_t maxBucketSize);
2409 size_t workItems() const { return totalWorkItems; }
2411 private:
2412 using Type = AnalysisDeps::Type;
2414 // Represents a func, class, or unit. The most basic unit of
2415 // scheduling.
2416 struct DepState {
2417 enum Kind {
2418 Func,
2419 Class,
2420 Unit
2423 DepState(SString name, Kind kind) : name{name}, kind{kind} {}
2424 SString name;
2425 Kind kind;
2426 AnalysisDeps deps;
2427 // If toSchedule is true, then the func/class/unit has changed
2428 // information.
2429 bool toSchedule{true};
2432 // These wrap a DepState, but also include information about what
2433 // changed (which varies according to the type).
2434 struct FuncState {
2435 explicit FuncState(SString name) : depState{name, DepState::Func} {}
2436 DepState depState;
2437 Type changed{};
2439 struct ClassState {
2440 explicit ClassState(SString name) : depState{name, DepState::Class} {}
2441 DepState depState;
2442 CompactVector<Type> methodChanges;
2443 boost::dynamic_bitset<> cnsChanges;
2444 boost::dynamic_bitset<> cnsFixed;
2445 TSStringSet typeCnsNames;
2446 bool allCnsFixed{false};
2448 struct UnitState {
2449 explicit UnitState(SString name) : depState{name, DepState::Unit} {}
2450 DepState depState;
2451 TSStringSet typeCnsNames;
2452 bool fixed{false};
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.
2460 struct TraceState {
2461 TSStringSet trace;
2462 TSStringSet deps;
2463 TinyVector<const DepState*> depStates;
2464 bool eligible{false};
2465 CopyableAtomic<bool> leaf{true};
2466 CopyableAtomic<bool> covered{false};
2467 AnalysisInput::BucketPresence buckets;
2468 size_t idx{0};
2471 void removeFuncs();
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 {
2510 None,
2511 Dep,
2512 DepWithBytecode,
2513 Full
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;
2522 struct Bucket;
2524 void tracePass1();
2525 void tracePass2();
2526 void tracePass3();
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;
2546 Index& index;
2548 size_t round{0};
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;
2564 struct Untracked {
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 {
2589 FuncClsUnit next();
2591 void schedule(FuncClsUnit);
2592 private:
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>>;
2604 enum class Mode {
2605 Constants,
2606 Full,
2607 Final
2610 AnalysisIndex(AnalysisWorklist&,
2611 VU<php::Class>,
2612 VU<php::Func>,
2613 VU<php::Unit>,
2614 VU<php::ClassBytecode>,
2615 VU<php::FuncBytecode>,
2616 V<AnalysisIndexCInfo>,
2617 V<AnalysisIndexFInfo>,
2618 V<AnalysisIndexMInfo>,
2619 VU<php::Class>,
2620 VU<php::Func>,
2621 VU<php::Unit>,
2622 AnalysisInput::Meta,
2623 Mode);
2624 ~AnalysisIndex();
2626 // Must be called in the worker's init() and fini() functions.
2627 static void start();
2628 static void stop();
2630 void freeze();
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(
2658 const Type& cls,
2659 const Type& name,
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>&,
2674 const Type&,
2675 res::Func) const;
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;
2693 PropMergeResult
2694 merge_static_type(PublicSPropMutations&,
2695 PropertiesInfo&,
2696 const Type&,
2697 const Type&,
2698 const Type&,
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
2722 Output finish();
2724 struct IndexData;
2725 private:
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&);
2734 void pop_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,
2783 const Type&,
2784 const Type&) const override;
2786 ClsTypeConstLookupResult
2787 lookup_class_type_constant(
2788 const Type&,
2789 const Type&,
2790 const Index::ClsTypeConstLookupResolver& r = {}) const override;
2792 ClsTypeConstLookupResult
2793 lookup_class_type_constant(const php::Class&,
2794 SString,
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;
2802 Index::ReturnType
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,
2807 MethodsInfo*,
2808 const CompactVector<Type>&,
2809 const Type&,
2810 res::Func,
2811 Dep d = Dep::ReturnTy) const override;
2813 std::pair<Index::ReturnType, size_t>
2814 lookup_return_type_raw(const php::Func*) const override;
2816 CompactVector<Type>
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&,
2824 const Type&,
2825 const Type&) const override;
2827 Type lookup_public_prop(const Type&, const Type&) const override;
2829 PropMergeResult
2830 merge_static_type(Context,
2831 PublicSPropMutations&,
2832 PropertiesInfo&,
2833 const Type&,
2834 const Type&,
2835 const Type&,
2836 bool checkUB = false,
2837 bool ignoreConst = false,
2838 bool mustBeReadOnly = false) const override;
2840 bool using_class_dependencies() const override;
2842 private:
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);
2858 private:
2859 friend struct Index;
2861 struct KnownKey {
2862 ClassInfo* cinfo;
2863 SString prop;
2864 bool operator==(const KnownKey& o) const {
2865 return cinfo == o.cinfo && prop == o.prop;
2867 struct Hasher {
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.
2879 struct Data {
2880 bool m_nothing_known{false};
2881 UnknownMap m_unknown;
2882 KnownMap m_known;
2884 std::unique_ptr<Data> m_data;
2885 bool m_enabled;
2887 Data& get();
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 //////////////////////////////////////////////////////////////////////