hphp/runtime/vm/class.h (646 lines of code) (raw):

/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #pragma once #include "hphp/runtime/base/attr.h" #include "hphp/runtime/base/datatype.h" #include "hphp/runtime/base/rds-util.h" #include "hphp/runtime/base/repo-auth-type.h" #include "hphp/runtime/base/tv-layout.h" #include "hphp/runtime/base/type-array.h" #include "hphp/runtime/base/type-string.h" #include "hphp/runtime/base/typed-value.h" #include "hphp/runtime/base/atomic-countable.h" #include "hphp/runtime/vm/containers.h" #include "hphp/runtime/vm/fixed-string-map.h" #include "hphp/runtime/vm/indexed-string-map.h" #include "hphp/runtime/vm/instance-bits.h" #include "hphp/runtime/vm/preclass.h" #include "hphp/runtime/vm/reified-generics-info.h" #include "hphp/util/bitset-view.h" #include "hphp/util/compact-vector.h" #include "hphp/util/compilation-flags.h" #include "hphp/util/default-ptr.h" #include "hphp/util/hash-map.h" #include <folly/Hash.h> #include <folly/Range.h> #include <boost/container/flat_map.hpp> #include <list> #include <memory> #include <type_traits> #include <utility> #include <vector> namespace HPHP { /////////////////////////////////////////////////////////////////////////////// extern const StaticString s_86cinit; extern const StaticString s_86pinit; extern const StaticString s_86sinit; extern const StaticString s_86linit; extern const StaticString s_86ctor; extern const StaticString s_86metadata; extern const StaticString s_86reified_prop; extern const StaticString s_86reifiedinit; extern const StaticString s_coeffects_var; extern const StaticString s___MockClass; extern const StaticString s___Reified; struct Class; struct ClassInfo; struct EnumValues; struct Func; struct RuntimeCoeffects; struct StringData; struct c_Awaitable; namespace collections { struct CollectionsExtension; } namespace Native { struct NativeDataInfo; struct NativePropHandler; } /////////////////////////////////////////////////////////////////////////////// /* * Utility wrapper for static properties. Allows distinguishing them * via type_scan::Index. */ struct StaticPropData { TypedValue val; }; /* * Class kinds---classes, interfaces, traits, and enums. * * "Normal class" refers to any classes that are not interfaces, traits, enums. */ enum class ClassKind { Class = AttrNone, Interface = AttrInterface, Trait = AttrTrait, Enum = AttrEnum }; using ClassPtr = AtomicSharedLowPtr<Class>; // Since native instance dtors can be release functions, they have to have // compatible signatures. using ObjReleaseFunc = BuiltinDtorFunction; using ObjectProps = std::conditional<tv_layout::stores_unaligned_typed_values, tv_layout::UnalignedTVLayout, tv_layout::Tv7Up>::type; /* * Class represents the full definition of a user class in a given request * context. * * See PreClass for more on the distinction. * * The method table is allocated at negative offset from the start of the Class * object, and the method slot is used as the negative offset from the object * to index into the method table. * * +------------+ * Func Slot n (offset -(n+1)) --> | | * .... * | | * +------------+ * Func Slot 1 (offset -2) ------> | | * +------------+ * Func Slot 0 (offset -1) ------> | | * Class* -----------------------> +------------+ * | | * .... * | | * +------------+ */ struct Class : AtomicCountable { ///////////////////////////////////////////////////////////////////////////// // Types. /* * Class availability. * * @see: Class::avail() */ enum class Avail { False, True, Fail }; /* * Attributes computed at runtime class init time, used to short * circuit more expensive checks. Not to be confused with enum Attr, * which are a-priori attributes computed by the compiler. */ enum RuntimeAttribute : uint8_t { CallToImpl = 0x01, // call to{Boolean,Int64,Double}Impl HasSleep = 0x02, // __sleep() HasClone = 0x04, // defines __clone PHP method; only valid // when !isCppBuiltin() HasNativePropHandler = 0x08, // class has native magic props handler }; /* * Instance property information. */ using UpperBoundVec = PreClass::UpperBoundVec; struct Prop { const PreClass::Prop* preProp; /* * When built in RepoAuthoritative mode, this is a control-flow insensitive, * always-true type assertion for this property. (It may be Gen if there * was nothing interesting known.) */ RepoAuthType repoAuthType; TypeConstraint typeConstraint; UpperBoundVec ubs; LowStringPtr name; LowStringPtr mangledName; /* Most derived class that declared this property. */ LowPtr<Class> cls; /* Least derived class that declared this property. */ LowPtr<Class> baseCls; Attr attrs; /* * Slot number that is only valid for reflection and serialization. */ Slot serializationIdx; }; /* * Static property information. */ struct SProp { const PreClass::Prop* preProp; RepoAuthType repoAuthType; TypeConstraint typeConstraint; UpperBoundVec ubs; LowStringPtr name; /* Most derived class that declared this property. */ LowPtr<Class> cls; Attr attrs; Slot serializationIdx; /* Used if (cls == this). */ TypedValue val; }; /* * Class constant information. */ struct Const { /* Most derived class that declared this constant. */ LowPtr<const Class> cls; LowStringPtr name; TypedValueAux val; const PreClass::Const* preConst; #ifndef USE_LOWPTR StringData* pointedClsName; #endif bool isAbstractAndUninit() const { return val.constModifiers().isAbstract() && !val.is_init() && val.is_const_val_missing(); } bool isAbstract() const { return val.constModifiers().isAbstract(); } void concretize() { // Type constant is abstract and has a default assertx(isAbstract() && val.is_init()); val.constModifiers().setIsAbstract(false); } ConstModifiers::Kind kind() const { return val.constModifiers().kind(); } StringData* getPointedClsName() const { #ifndef USE_LOWPTR return pointedClsName; #else return val.constModifiers().getPointedClsName(); #endif } void setPointedClsName(StringData* pClsName) { #ifndef USE_LOWPTR pointedClsName = pClsName; #else val.constModifiers().setPointedClsName(pClsName); #endif } }; /* * Initialization vector for declared properties. * * This is a vector which contains default values for all of a Class's * declared instance properties. It is used when instantiating new objects * from a Class. * * This vector is indexed by the physical index of the property within the * objects (and not by its logical slot). */ struct PropInitVec { PropInitVec(); ~PropInitVec(); const PropInitVec& operator=(const PropInitVec&); template <bool is_const> struct Entry { tv_val<is_const> val; typename BitsetView<is_const>::bit_reference deepInit; }; template <bool is_const> struct iterator_impl { using char_t = typename std::conditional_t<is_const, const unsigned char, unsigned char>; using tv_iter_t = typename std::conditional_t<is_const, ObjectProps::const_iterator, ObjectProps::iterator>; using bit_iter_t = typename BitsetView<is_const>::iterator; iterator_impl(tv_iter_t tv, bit_iter_t bit); bool operator==(const iterator_impl& o) const; bool operator!=(const iterator_impl& o) const; iterator_impl& operator++(); iterator_impl operator++(int); Entry<is_const> operator*() const; Entry<is_const> operator->() const; using value_type = Entry<is_const>; using reference = Entry<is_const>&; using pointer = void; using difference_type = void; using iterator_category = std::forward_iterator_tag; tv_iter_t m_val; bit_iter_t m_bit; }; using iterator = iterator_impl<false>; using const_iterator = iterator_impl<true>; size_t size() const; template <typename T> Entry<false> operator[](T i); template <typename T> Entry<true> operator[](T i) const; iterator begin(); iterator end(); const_iterator cbegin() const; const_iterator cend() const; void push_back(const TypedValue& v); const ObjectProps* data() const; static constexpr size_t dataOff() { return offsetof(PropInitVec, m_data); } size_t dataSize() const { auto const cap = m_capacity < 0 ? ~m_capacity : m_capacity; return ObjectProps::sizeFor(cap) + BitsetView<true>::sizeFor(cap); } /* * Make a request-allocated copy of `src'. */ static PropInitVec* allocWithReqAllocator(const PropInitVec& src); TYPE_SCAN_CUSTOM() { // We don't need to worry about scanning the pointer m_data itself because // when we're heap-allocated, it always points inside of this allocation. // // The only time that's not the case is when we're allocated in general // heap and we shouldn't be type-scanned under those circumstances assertx(reqAllocated()); assertx(m_data == static_cast<const void*>(this + 1)); m_data->scan(ObjectProps::quickIndex(m_size), scanner); } private: PropInitVec(const PropInitVec&); bool reqAllocated() const; BitsetView<false> deepInitBits(); BitsetView<true> deepInitBits() const; ObjectProps* m_data; uint32_t m_size; // m_capacity > 0, allocated on global huge heap // m_capacity = 0, not request allocated, m_data is nullptr // m_capacity < 0, request allocated, with '~m_capacity' slots int32_t m_capacity; }; static_assert(sizeof(PropInitVec) <= 16, ""); /* * A slot in a Class vtable vector, pointing to the vtable for an interface * and the interface itself. Used for efficient interface method dispatch and * instance checks. */ struct VtableVecSlot { LowPtr<Func>* vtable; LowPtr<Class> iface; }; /* * Container types. */ using MethodMap = FixedStringMap<Slot, Slot>; using MethodMapBuilder = FixedStringToSlotMapBuilder<Func*, Slot>; using InterfaceMap = IndexedStringMap<LowPtr<Class>, int>; using IncludedEnumMap = IndexedStringMap<LowPtr<Class>, int>; using RequirementMap = IndexedStringMap< const PreClass::ClassRequirement*, int>; using TraitAliasVec = vm_vector<PreClass::TraitAliasRule::NamePair>; /* * Map from a Closure subclass C's scope context to the appropriately scoped * clone of C. * * @see: Class::ExtraData::m_scopedClones */ using ScopedClonesMap = hphp_hash_map<LowPtr<Class>, ClassPtr, smart_pointer_hash<LowPtr<Class>>>; /* * We store the length of vectors of methods, parent classes and interfaces. * * In lowptr builds, we limit all of these quantities to 2^16-1 to save * memory. */ using veclen_t = std::conditional<use_lowptr, uint16_t, uint32_t>::type; ///////////////////////////////////////////////////////////////////////////// // Creation and destruction. /* * Allocate a new Class object. * * Eventually deallocated using atomicRelease(), but can go through some * phase changes before that (see destroy()). */ static Class* newClass(PreClass* preClass, Class* parent); /* * Make a clone of this Closure subclass, with `ctx' as the closure scope. * * If the scoping already exists in m_extra->m_scopedClones, or if this class * is already scoped correctly, just return it. Otherwise, we scope our own * m_invoke if it's not already scoped, or clone ourselves and scope the * clone's m_invoke, then add the mapping to m_scopedClones. It is required * for correctness that all clones be added to the cache, because the cache * participates in synchronization with instance bits initialization. * * Note that all scoping events via CreateCl opcodes clone from the * "template" Closure subclass that is generated by the emitter. * * @requires: parent() == SystemLib::s_ClosureClass */ Class* rescope(Class* ctx); /* * Called when a Class becomes unreachable. * * This may happen before its refcount hits zero if it is still referred to * by any of: * - its NamedEntity; * - any derived Class; * - any Class that implements it (for interfaces); or * - any Class that uses it (for traits) * * Such referring classes must also be logically dead at the time destroy() * is called. However, since we don't have back pointers to find them, * instead we leave the Class in a zombie state. When we try to instantiate * one of its referrers, we will notice that it depends on a zombie and * destroy *that*, releasing its reference to this Class. */ void destroy(); /* * Called when the (atomic) refcount hits zero. * * The Class is completely dead at this point, and its memory is freed * immediately. */ void atomicRelease(); private: /* * Free any references to child classes, interfaces, and traits. * * releaseRefs() is called when a Class is put into the zombie state. It's * safe to call multiple times, so it is also called from the destructor (in * case we bypassed the zombie state). */ void releaseRefs(); /* * Unbind any static prop RDS handles. Doing so before destroying a Class is * necessary because we identify and dedupe these handles by a Symbol that * includes the Class* as part of the key. */ void releaseSProps(); public: /* * Whether this class has been logically destroyed, but needed to be * preserved due to outstanding references. */ bool isZombie() const; /* * Check whether a Class from a previous request is available to be defined. * The caller should check that it has the same PreClass that is being * defined. Being available means that the parent, the interfaces, and the * traits are already defined (or become defined via autoload, if tryAutoload * is true). * * @returns: Avail::True: if it's available * Avail::Fail: if at least one of the parent, interfaces, and * traits is not defined at all at this point * Avail::False: if at least one of the parent, interfaces, and * traits is defined but does not correspond to this * particular Class* * * The parent parameter is used for two purposes: first, it lets us avoid * looking up the active parent class for each potential Class*; and second, * it is used on Fail to return the problem class so the caller can report * the error correctly. */ Avail avail(Class*& parent, bool tryAutoload = false) const; ///////////////////////////////////////////////////////////////////////////// // Pre- and post-allocations. [const] /* * Pointer to this Class's FuncVec, which is allocated before this. */ LowPtr<Func>* funcVec() const; /* * The start of malloc'd memory for `this' (i.e., including anything * allocated before the object itself.). */ void* mallocPtr() const; /* * Address of the end of the Class's variable-length memory allocation. */ const void* mallocEnd() const; /* * Pointer to the array of Class pointers, allocated immediately after * `this', which contain this class's inheritance hierarchy (including `this' * as the last element). */ const LowPtr<Class>* classVec() const; /* * The size of the classVec. */ veclen_t classVecLen() const; ///////////////////////////////////////////////////////////////////////////// // Ancestry. [const] /* * Determine if this represents a non-strict subtype of `cls'. The nonIFace * variant is faster, but has the additional precondition that `cls' is not * an interface. */ bool classof(const Class*) const; bool classofNonIFace(const Class*) const; bool subtypeOf(const Class*) const; /* * Whether this class implements an interface called `name'. */ bool ifaceofDirect(const StringData* name) const; /* * Assuming this and cls are both regular classes (not interfaces or traits), * return their lowest common ancestor, or nullptr if they're unrelated. */ const Class* commonAncestor(const Class* cls) const; /* * Given that this class exists, return a class named "name" that is * also guaranteed to exist, or nullptr if there is none. */ const Class* getClassDependency(const StringData* name) const; ///////////////////////////////////////////////////////////////////////////// // Basic info. [const] /* * The name, PreClass, and parent class of this class. */ const StringData* name() const; const PreClass* preClass() const; Class* parent() const; /* * A hash for this class that will remain constant across process restarts. */ size_t stableHash() const; /* * Uncounted String names of this class and of its parent. */ StrNR nameStr() const; StrNR parentStr() const; /* * The attributes on this class. */ Attr attrs() const; /* * Runtime class attributes, computed during class initialization. */ bool rtAttribute(RuntimeAttribute) const; void initRTAttributes(uint8_t); /* * Whether this class is uniquely named across the codebase. * * It's legal in PHP to define multiple classes in different pseudomains * with the same name, so long as both are not required in the same request. */ bool isUnique() const; /* * Whether we can load this class once and persist it across requests. * * Persistence is possible when a Class is uniquely named and is defined in a * pseudomain that has no side-effects (except other persistent definitions). * * A class which satisfies isPersistent() may not actually /be/ persistent, * if we had to allocate its RDS handle before we loaded the class. * * @see: classHasPersistentRDS() * @implies: isUnique() */ bool isPersistent() const; /* * Is this class allowed to be constructed dynamically? */ bool isDynamicallyConstructible() const; /* * If the class is called dynamically should we sample the calls? */ Optional<int64_t> dynConstructSampleRate() const; ///////////////////////////////////////////////////////////////////////////// // Magic methods. [const] /* * Get the constructor, destructor, or __toString() method on this class, or * nullptr if no such method exists. * * DeclaredCtor refers to a user-declared __construct(), as opposed to the * (shared) empty method generated by the compiler. */ const Func* getCtor() const; const Func* getDeclaredCtor() const; const Func* getToString() const; const Func* get86pinit() const; const Func* get86sinit() const; const Func* get86linit() const; /* * Look up a class' cached __invoke function. We only cache __invoke methods * if they are instance methods or if the class is a static closure. */ const Func* getCachedInvoke() const; ///////////////////////////////////////////////////////////////////////////// // Builtin classes. [const] /* * Is the class a builtin, whether PHP or C++? */ bool isBuiltin() const; /* * Custom initialization and destruction routines for C++ extension classes. * * instanceCtor() returns true iff the class is a C++ extension class. */ template <bool Unlocked = false> BuiltinCtorFunction instanceCtor() const; BuiltinDtorFunction instanceDtor() const; /* * Whether this C++ extension class has opted into serialization. * * @requires: instanceCtor() */ bool isCppSerializable() const; /* * Whether this is a class for a Hack collection. */ bool isCollectionClass() const; ///////////////////////////////////////////////////////////////////////////// // Methods. /* * Number of methods on this class. * * Note that this may differ from m_funcVecLen, since numMethods() is the * exact number of methods, and m_funcVecLen is only required to be an upper * bound. * * In particular, outside of RepoAuth mode, trait methods are not transcluded * into the Classes which use them, and we are conservative when initially * counting methods since we do not resolve trait precedence first. */ size_t numMethods() const; /* * Get or set a method by its index in the funcVec, which is allocated * contiguously before `this' in memory. */ Func* getMethod(Slot idx) const; void setMethod(Slot idx, Func* func); /* * Look up a method by name. * * Return null if no such method exists. */ Func* lookupMethod(const StringData* methName) const; /* * public because its used by importTraitMethod. */ void methodOverrideCheck(const Func* parentMethod, const Func* method); /* * Return an Array (via `out') of all the methods of `cls' visible in the * context of `ctx' (which may be nullptr). * * The Array has the form [lowercase name => declared name], ordered with * methods implemented by `cls' first, followed by its parents' methods, and * so on, in declaration order for each Class in the hierarchy. Any * unimplemented interface methods come last. */ static void getMethodNames(const Class* cls, const Class* ctx, Array& out); ///////////////////////////////////////////////////////////////////////////// // Object release. // // Every class has a static release function responsible for destroying and // freeing object instances of this class. This might be ObjectData::release, // or a custom native instance dtor. ObjReleaseFunc releaseFunc() const; ///////////////////////////////////////////////////////////////////////////// // Property metadata. [const] // // Unless otherwise specified, the terms "declared instance properties" and // "static properties" both refer to properties declared on this class as // well as those declared on its ancestors. Note that this includes private // properties in both cases. /* * Number of declared instance properties or static properties. */ size_t numDeclProperties() const; size_t numStaticProperties() const; /* * An exclusive upper limit on the post-sort indices of properties of this * class that may be countable. See m_countablePropsEnd for more details. */ ObjectProps::quick_index countablePropsEnd() const { return m_countablePropsEnd; } /* * Number of declared instance properties that are actually accessible from * this class's context. * * Only really used when iterating over an object's properties. */ uint32_t declPropNumAccessible() const; /* * The info vector for declared instance properties or static properties. */ folly::Range<const Prop*> declProperties() const; folly::Range<const SProp*> staticProperties() const; /* * Look up the index of a declared instance property or static property. * * Return kInvalidSlot if no such property exists. */ Slot lookupDeclProp(const StringData* propName) const; Slot lookupSProp(const StringData* sPropName) const; /* * Returns the 86reified_init property's slot */ Slot lookupReifiedInitProp() const; /* * Returns whether this closure class that uses coeffects prop * to carry its coeffects * Requires this to be a closure class */ bool hasClosureCoeffectsProp() const; /* * Returns the coeffects prop's slot. * @requires: hasClosureCoeffectsProp() */ Slot getCoeffectsProp() const; /* * The RepoAuthType of the declared instance property or static property at * `index' in the corresponding table. */ RepoAuthType declPropRepoAuthType(Slot index) const; RepoAuthType staticPropRepoAuthType(Slot index) const; const TypeConstraint& declPropTypeConstraint(Slot index) const; const TypeConstraint& staticPropTypeConstraint(Slot index) const; /* * Whether this class has any properties that require deep initialization. * * Deep initialization means that the property cannot simply be memcpy'd when * creating new objects. */ bool hasDeepInitProps() const; /* * Whether this class forbids the use of dynamic (non-declared) properties. */ bool forbidsDynamicProps() const; ///////////////////////////////////////////////////////////////////////////// // Property initialization. [const] /* * Whether this Class requires initialization, either because of nonscalar * instance property initializers, simply due to having static properties, or * possible property type invariance violations. */ bool needInitialization() const; /* * Whether this Class potentially has properties which redefine properties in * a parent class, and the properties might have inequivalent type-hints. If * so, a runtime check is needed during class initialization to possibly raise * an error. */ bool maybeRedefinesPropTypes() const; /* * Whether this Class has properties that require a runtime initial value * check. */ bool needsPropInitialValueCheck() const; /* * Perform request-local initialization. * * For declared instance properties, this means creating a request-local copy * of this Class's PropInitVec. This is necessary in order to accommodate * non-scalar defaults (e.g., class constants), which may not be consistent * across requests. * * For static properties, this means setting up request-local memory for the * actual static properties, if necessary, and initializing them to their * default values. */ void initialize() const; void initProps() const; void initSProps() const; /* * Perform a property type-hint redefinition check for the property at a * particular slot. */ void checkPropTypeRedefinition(Slot) const; /* * Check if class has been initialized. */ bool initialized() const; /* * PropInitVec for this class's declared properties, with default values for * scalars only. * * This is the base from which the request-local copy is made. */ const PropInitVec& declPropInit() const; /* * Vector of 86pinit non-scalar instance property initializer functions. * * These are invoked during initProps() to populate the copied PropInitVec. * * This vector is indexed by the properties' logical slot number. */ const VMFixedVector<const Func*>& pinitVec() const; /* * RDS handle which marks whether a property type-hint redefinition check has * been performed for this class in this request yet. */ rds::Handle checkedPropTypeRedefinesHandle() const; /* * RDS handle which marks whether the initial value check has been performed * for this class in this request yet. */ rds::Handle checkedPropInitialValuesHandle() const; ///////////////////////////////////////////////////////////////////////////// // Property storage. [const] /* * Initialize the RDS handles for the request-local PropInitVec and for the * static properties. */ void initPropHandle() const; void initSPropHandles() const; /* * RDS handle of the request-local PropInitVec. */ rds::Handle propHandle() const; /* * RDS handle for the static properties' is-initialized flag. */ rds::Handle sPropInitHandle() const; /* * RDS handle for the static property at `index'. */ rds::Handle sPropHandle(Slot index) const; rds::Link<StaticPropData, rds::Mode::NonNormal> sPropLink(Slot index) const; /* * Get the PropInitVec for the current request. */ PropInitVec* getPropData() const; /* * Get the value of the static variable at `index' for the current request. */ TypedValue* getSPropData(Slot index) const; /* * Map the logical slot of a property to its physical index within the object * in memory. */ ObjectProps::quick_index propSlotToIndex(Slot slot) const { return m_slotIndex[slot]; } /* * Map the physical index of a property within the object to its logical slot. */ Slot propIndexToSlot(uint16_t index) const; ///////////////////////////////////////////////////////////////////////////// // Property lookup and accessibility. [const] struct PropValLookup { TypedValue* val; Slot slot; bool accessible; bool constant; bool readonly; }; struct PropSlotLookup { Slot slot; bool accessible; bool constant; bool readonly; }; /* * Get the slot and accessibility of a declared instance property on a class * from the given context. * * Accessibility refers to the public/protected/private attribute of the * property. * * Return kInvalidInd for the property iff the property was not declared on * this class or any ancestor. Note that if the return is marked as * accessible, then the property must exist. */ PropSlotLookup getDeclPropSlot(const Class*, const StringData*) const; /* * The equivalent of getDeclPropSlot(), but for static properties. */ PropSlotLookup findSProp(const Class*, const StringData*) const; /* * Get the request-local value of the static property `sPropName', as well as * its accessibility, from the given context. * * The behavior is identical to that of findSProp(), except substituting * nullptr for kInvalidInd. * * getSProp() will throw if the property is AttrLateInit and the value is * Uninit. getSPropIgnoreLateInit() will not. * * May perform initialization. */ PropValLookup getSProp(const Class*, const StringData*) const; PropValLookup getSPropIgnoreLateInit(const Class*, const StringData*) const; /* * Return whether or not a declared instance property is accessible from the * given context. */ static bool IsPropAccessible(const Prop&, Class*); ///////////////////////////////////////////////////////////////////////////// // Constants. [const] /* * Number of class constants. */ size_t numConstants() const; /* * The info vector for this class's constants. */ const Const* constants() const; /* * Whether this class has a constant named `clsCnsName'. */ bool hasConstant(const StringData* clsCnsName) const; /* * Whether this class has a type constant named `typeCnsName'. */ bool hasTypeConstant(const StringData* typeCnsName, bool includeAbs = false) const; /* * Returns the runtime coeffect value of the class context constant. * When failIsFatal is set, raises an error if the context constant * is not defined, is abstract or is a type/value constant. */ Optional<RuntimeCoeffects> clsCtxCnsGet(const StringData* name, bool failIsFatal) const; /* * Look up the actual value of a class constant. Perform dynamic * initialization if necessary. * * If resolve not is set, then returns an unresolved structure. * * Return a TypedValue containing KindOfUninit if this class has no * such constant. */ TypedValue clsCnsGet(const StringData* clsCnsName, ConstModifiers::Kind what = ConstModifiers::Kind::Value, bool resolve = true) const; /* * Look up a class constant's TypedValue if it doesn't require dynamic * initialization. The index of the constant is output via `clsCnsInd'. * * Return nullptr if this class has no constant of the given name. * * Return nullptr if the constant is abstract. * * The TypedValue represents the constant's value iff it is a scalar, * otherwise it has m_type set to KindOfUninit. Non-scalar class constants * need to run 86cinit code to determine their value at runtime. */ const TypedValue* cnsNameToTV(const StringData* clsCnsName, Slot& clsCnsInd, ConstModifiers::Kind what = ConstModifiers::Kind::Value) const; /* * Get the slot for a constant with name, which can optionally be abstract and * either must be or must not be a type constant. */ Slot clsCnsSlot(const StringData* name, ConstModifiers::Kind want, bool allowAbstract) const; ///////////////////////////////////////////////////////////////////////////// // Interfaces and traits. /* * Interfaces this class declared in its "implements" clause. */ folly::Range<const ClassPtr*> declInterfaces() const; /* * All interfaces implemented by this class, including those declared in * traits. */ const InterfaceMap& allInterfaces() const; /* * All enums directly or transitively included by this enum */ const bool hasIncludedEnums() const; const IncludedEnumMap& allIncludedEnums() const; /* * Start and end offsets in m_methods of methods that come from used traits. * * The trait methods are precisely in [m_traitsBeginIdx, m_traitsEndIdx). */ Slot traitsBeginIdx() const; Slot traitsEndIdx() const; /* * Traits used by this class. * * In RepoAuthoritative mode, we flatten all traits into their users in the * compile phase, which leaves m_usedTraits empty as a result. */ const VMCompactVector<ClassPtr>& usedTraitClasses() const; /* * Trait alias rules. * * This is only used by reflection. */ const TraitAliasVec& traitAliases() const; void addTraitAlias(const PreClass::TraitAliasRule& rule) const; /* * All trait and interface requirements imposed on this class, including * those imposed by traits. */ const RequirementMap& allRequirements() const; /* * Store a cache of enum values. Takes ownership of 'values'. */ EnumValues* setEnumValues(EnumValues* values); EnumValues* getEnumValues() const; ///////////////////////////////////////////////////////////////////////////// // Objects. [const] /* * Whether instances of this class implement Throwable interface, which * requires additional initialization on construction. */ bool needsInitThrowable() const; ///////////////////////////////////////////////////////////////////////////// // JIT data. /* * Get and set the RDS handle for the class with this class's name. * * We can burn these into the TC even when classes are not persistent, since * only a single name-to-class mapping will exist per request. */ rds::Handle classHandle() const; void setClassHandle(rds::Link<LowPtr<Class>, rds::Mode::NonLocal> link) const; /* * Get and set the RDS-cached class with this class's name. */ Class* getCached() const; void setCached(); ///////////////////////////////////////////////////////////////////////////// // Native data. /* * NativeData type declared in <<__NativeData("Type")>>. */ const Native::NativeDataInfo* getNativeDataInfo() const; /* * Whether the class registered native handler of magic props. */ bool hasNativePropHandler() const; /* * Return the actual native handler of magic props. * * @requires hasNativePropHandler() */ const Native::NativePropHandler* getNativePropHandler() const; ///////////////////////////////////////////////////////////////////////////// // Closure subclasses. /* * Is this a subclass of Closure? */ bool isClosureClass() const; /* * Is this a scoped subclass of Closure? */ bool isScopedClosure() const; /* * Return all the scoped clones of this closure class, or an empty map when * this is not a closure class. * * NOTE: Accessing this table is only permitted when synchronized with * instance bits initialization. * * @see: ExtraData::m_scopedClones */ const ScopedClonesMap& scopedClones() const; ///////////////////////////////////////////////////////////////////////////// // Memoization // /* * Whether an object of this class will have memo slots. */ bool hasMemoSlots() const; /* * Return the number of memo slots an object of this class will require. */ size_t numMemoSlots() const; /* * Given a function (belonging to this class or a parent), return the slot it * should use for memoization, and whether that slot is shared. The function * must be a memoize wrapper. */ std::pair<Slot, bool> memoSlotForFunc(FuncId func) const; /* * Returns an offset from the object base pointer to be used for memory * free routines */ uint32_t memoSize() const; /* * Returns an index that represent the size bin of the MemoryManager */ uint8_t sizeIdx() const; ///////////////////////////////////////////////////////////////////////////// // LSB Memoize methods // /* * Retrieve the slot corresponding to the value/cache for a function * when bound to this class or a subclass. * These are for use by the JIT. */ Slot lsbMemoSlot(const Func* func, bool forValue) const; /* * Get the offset into the Class of the extra structure * Used by the JIT to load m_extra */ static constexpr size_t extraOffset() { static_assert( sizeof(m_extra) == sizeof(ExtraData*), "The JIT loads m_extra as a bare pointer"); return offsetof(Class, m_extra); } /* * Get the offset into the extra structure of m_handles. * Used by the JIT. */ static constexpr size_t lsbMemoExtraHandlesOffset() { return offsetof(ExtraData, m_lsbMemoExtra) + offsetof(LSBMemoExtra, m_handles); } ///////////////////////////////////////////////////////////////////////////// // Other methods. // // Avoiding adding methods to this section. /* * Verify that the AttrPersistent is set correctly. */ void verifyPersistence() const; /* * Set the instance bits on this class. * * The instance bits are a bitfield cache for instanceof checks. During * warmup, we profile the classes and interfaces most commonly checked * against in instanceof checks. Then we cache whether or not this Class * satisifes the check in the corresponding bit. */ void setInstanceBits(); void setInstanceBitsAndParents(); void setInstanceBitsIndex(unsigned int bit); bool checkInstanceBit(unsigned int bit) const; /* * Get the underlying enum base type if this is an enum. * * A return of std::nullopt represents the `mixed' type. */ MaybeDataType enumBaseTy() const; /* * Returns whether this class has reified generics */ bool hasReifiedGenerics() const; /* * Returns ReifiedGenericsInfo containing how many generics this class has, * indices of its reified generics, and which ones are soft reified */ const ReifiedGenericsInfo& getReifiedGenericsInfo() const; /* * Returns whether any of this class's parents have reified generics */ bool hasReifiedParent() const; bool needsInitSProps() const; static bool compatibleTraitPropInit(const TypedValue& tv1, const TypedValue& tv2); // For assertions: bool validate() const; ///////////////////////////////////////////////////////////////////////////// // Offset accessors. [static] #define OFF(f) \ static constexpr ptrdiff_t f##Off() { \ return offsetof(Class, m_##f); \ } OFF(attrCopy) OFF(classVec) OFF(classVecLen) OFF(instanceBits) OFF(invoke) OFF(preClass) OFF(propDataCache) OFF(vtableVecLen) OFF(vtableVec) OFF(funcVecLen) OFF(RTAttrs) OFF(releaseFunc) #undef OFF static constexpr ptrdiff_t constantsVecOff() { return offsetof(Class, m_constants) + ConstMap::vecOff(); } static constexpr ptrdiff_t constantsVecLenOff() { return offsetof(Class, m_constants) + ConstMap::sizeOff(); } static constexpr size_t constantsVecLenSize() { return ConstMap::sizeSize(); } ///////////////////////////////////////////////////////////////////////////// // Lookup. [static] /* * Define a new Class from `preClass' for this request. * * Raises a fatal error in various conditions (e.g., Class already defined, * parent Class not defined, etc.) if `failIsFatal' is set). * * Also always fatals if a type alias already exists in this request with the * same name as that of `preClass', regardless of the value of `failIsFatal'. */ static Class* def(const PreClass* preClass, bool failIsFatal = true); /* * Define a closure from preClass. */ static Class* defClosure(const PreClass* preClass, bool cache); /* * Look up the Class in this request with name `name', or with the name * mapped to the NamedEntity `ne'. * * Return nullptr if the class is not yet defined in this request. */ static Class* lookup(const NamedEntity* ne); static Class* lookup(const StringData* name); /* * Finds a class which is guaranteed to be unique in the specified * context. The class has not necessarily been loaded in the * current request. * * Return nullptr if there is no such class. */ static const Class* lookupUniqueInContext(const NamedEntity* ne, const Class* ctx, const Unit* unit); static const Class* lookupUniqueInContext(const StringData* name, const Class* ctx, const Unit* unit); /* * Look up, or autoload and define, the Class in this request with name * `name', or with the name mapped to the NamedEntity `ne'. * * @requires: NamedEntity::get(name) == ne */ static Class* load(const NamedEntity* ne, const StringData* name); static Class* load(const StringData* name); /* * Autoload the Class with name `name' and bind it `ne' in this request. * * @requires: NamedEntity::get(name) == ne */ static Class* loadMissing(const NamedEntity* ne, const StringData* name); /* * Same as lookupClass(), but if `tryAutoload' is set, call and return * loadMissingClass(). */ static Class* get(const NamedEntity* ne, const StringData* name, bool tryAutoload); static Class* get(const StringData* name, bool tryAutoload); /* * Whether a Class with name `name' of type `kind' has been defined in this * request, autoloading it if `autoload' is set. */ static bool exists(const StringData* name, bool autoload, ClassKind kind); std::atomic<void*>* getThriftData() const; ///////////////////////////////////////////////////////////////////////////// // ExtraData. private: struct LSBMemoExtra { /* * Mapping of methods (declared by this class only) to their assigned slots * for LSB memoization. This is populated in the Class ctor when LSB * memoized methods are present. */ boost::container::flat_map<FuncId, Slot> m_slots; /* * The total number of memo slots, and also the next LSB memo slot to * assign. This must be larger than our parent's m_numSlots, since slots * are inherited. */ Slot m_numSlots{0}; /* * Cached handles to LSB memoization for this class. * This array is initialized in the Class ctor, when LSB memoized * methods are present. */ rds::Handle* m_handles{nullptr}; VMCompactVector<std::pair<rds::Symbol, rds::Handle>> m_symbols; }; struct ExtraData { ExtraData() = default; ~ExtraData(); /* * Vector of (new name, original name) pairs, representing trait aliases. */ TraitAliasVec m_traitAliases; /* * In RepoAuthoritative mode, we rely on trait flattening in the compile * phase to import the contents of traits. As a result, m_usedTraits is * always empty. */ VMCompactVector<ClassPtr> m_usedTraits; /* * Only used by reflection for method ordering. Whenever we have no traits * (e.g., in repo mode, where traits are flattened), these will both be 0. */ Slot m_traitsBeginIdx{0}; Slot m_traitsEndIdx{0}; /* * Builtin-specific data. */ BuiltinCtorFunction m_instanceCtor{nullptr}; BuiltinCtorFunction m_instanceCtorUnlocked{nullptr}; BuiltinDtorFunction m_instanceDtor{nullptr}; /* * Cache for reified generics info */ ReifiedGenericsInfo m_reifiedGenericsInfo{0, false, 0, {}}; /* * Cache for Closure subclass scopings. * * Only meaningful when `this' is the "template" for a family of Closure * subclasses. When we need to create a closure in the scope of a Class C * (and with attrs A), we clone `this', rescope its __invoke() * appropriately, and then cache the (C,A) => clone binding here. * * @see: rescope() */ ScopedClonesMap m_scopedClones; /* * List of references to Closure subclasses whose scoped Class context is * `this'. */ VMCompactVector<ClassPtr> m_clonesWithThisScope; /* * Objects with the <<__NativeData("T")>> UA are allocated with extra space * prior to the ObjectData structure itself. */ const Native::NativeDataInfo *m_nativeDataInfo{nullptr}; /* * Cache of persistent enum values, managed by EnumCache. */ std::atomic<EnumValues*> m_enumValues{nullptr}; /* * Mapping of functions (in this class only) to their assigned slots and * whether the slot is shared. This is not inherited from the parent. */ vm_flat_map<FuncId, std::pair<Slot, bool>> m_memoMappings; /* * Maps a parameter count to an assigned slot for that count. This is * inherited from the parent. */ vm_flat_map<size_t, Slot> m_sharedMemoSlots; /* * The next memo slot to assign. This is inherited from the parent. */ Slot m_nextMemoSlot{0}; /* * The thrift spec, if present. */ mutable std::atomic<void*> m_thriftData{nullptr}; /* * MemoizeLSB extra data */ mutable LSBMemoExtra m_lsbMemoExtra; /* * If initialized, then this class has already performed a property * type-hint redefinition check. */ mutable rds::Link<bool, rds::Mode::Normal> m_checkedPropTypeRedefs; /* * If initialized, then this class has already performed an initial value * check. */ mutable rds::Link<bool, rds::Mode::Normal> m_checkedPropInitialValues; /* * List of enums included by an enum class */ mutable IncludedEnumMap m_includedEnums; /* * List of enums included directly by this class */ mutable VMCompactVector<ClassPtr> m_declIncludedEnums; }; /* * Allocate the ExtraData; done only when necessary. */ void allocExtraData() const; ///////////////////////////////////////////////////////////////////////////// // Internal types. private: using ConstMap = IndexedStringMap<Const,Slot>; using PropMap = IndexedStringMap<Prop,Slot>; using SPropMap = IndexedStringMap<SProp,Slot>; ///////////////////////////////////////////////////////////////////////////// // Private methods. private: Class(PreClass* preClass, Class* parent, VMCompactVector<ClassPtr>&& usedTraits, unsigned classVecLen, unsigned funcVecLen); ~Class(); /* * Trait method import routines. */ void importTraitMethods(MethodMapBuilder& curMethodMap); template<typename XProp> void initProp(XProp& prop, const PreClass::Prop* preProp); void initProp(Prop& prop, const PreClass::Prop* preProp); void initProp(SProp& prop, const PreClass::Prop* preProp); template<typename XProp> void checkPrePropVal(XProp& prop, const PreClass::Prop* preProp); void sortOwnProps(const PropMap::Builder& curPropMap, uint32_t first, uint32_t past, std::vector<uint16_t>& slotIndex); void sortOwnPropsInitVec(uint32_t first, uint32_t past, const std::vector<uint16_t>& slotIndex); void importTraitProps(int traitIdx, PropMap::Builder& curPropMap, SPropMap::Builder& curSPropMap, std::vector<uint16_t>& slotIndex, Slot& serializationIdx, std::vector<bool>& serializationVisited, Slot& staticSerializationIdx, std::vector<bool>& staticSerializationVisited); void importTraitInstanceProp(Prop& traitProp, TypedValue traitPropVal, PropMap::Builder& curPropMap, SPropMap::Builder& curSPropMap, std::vector<uint16_t>& slotIndex, Slot& serializationIdx, std::vector<bool>& serializationVisited); void importTraitStaticProp(SProp& traitProp, PropMap::Builder& curPropMap, SPropMap::Builder& curSPropMap, Slot& staticSerializationIdx, std::vector<bool>& staticSerializationVisited); void addTraitPropInitializers(std::vector<const Func*>&, Attr which); void importTraitConsts(ConstMap::Builder& constMap); void checkInterfaceMethods(); void checkInterfaceConstraints(); void setParent(); void setSpecial(); void setMethods(); void setRTAttributes(); void setConstants(); void setProperties(); void setReifiedData(); void setInitializers(); void setInterfaces(); void setInterfaceVtables(); void setClassVec(); void setFuncVec(MethodMapBuilder& builder); void setRequirements(); void setEnumType(); void setIncludedEnums(); void checkRequirementConstraints() const; void raiseUnsatisfiedRequirement(const PreClass::ClassRequirement*) const; void setNativeDataInfo(); void initClosure(); void setInstanceMemoCacheInfo(); void setLSBMemoCacheInfo(); void setReleaseData(); template<bool setParents> void setInstanceBitsImpl(); void addInterfacesFromUsedTraits(InterfaceMap::Builder& builder) const; void initLSBMemoHandles(); void checkPropTypeRedefinitions() const; void checkPropInitialValues() const; void setupSProps(); ///////////////////////////////////////////////////////////////////////////// // Friendship. private: template<class T> friend typename std::enable_if<std::is_base_of<c_Awaitable, T>::value, void>::type finish_class(); friend struct collections::CollectionsExtension; friend struct StandardExtension; ///////////////////////////////////////////////////////////////////////////// // Static data members. private: static constexpr uint32_t kMagic = 0xce7adb33; static constexpr int8_t kNoInstanceBit = -1; static constexpr int8_t kProfileInstanceBit = -2; ///////////////////////////////////////////////////////////////////////////// // Data members. // // Ordered by usage frequency. Do not re-order for cosmetic reasons. // // The ordering is order of hotness because the funcVec() preallocation is // relatively hot, and must be the first member. private: /* * Atomic refcount; inherited from AtomicCountable. */ // mutable std::atomic<RefCount> m_count; /* * An exclusive upper limit on the post-sort indices of properties of this * class that may be countable. Properties that may be countable will have * indices less than this bound. If we can guarantee that all properties of * this class are uncounted, the bound will be 0. * * (Note that there may still be uncounted properties with indices less than * this bound; in particular, we can't sort parent class properties freely.) */ ObjectProps::quick_index m_countablePropsEnd; /* * An index that represent the size bin in the MemoryManager. */ uint8_t m_sizeIdx{0}; /* * Runtime attributes computed at runtime init-time. * * Not to be confused with m_attrCopy which are compile-time and stored in * the repo. */ uint8_t m_RTAttrs; /* * Bitmap of parent classes and implemented interfaces. * * Each bit corresponds to a commonly used class name, determined during the * profiling warmup requests. */ InstanceBits::BitSet m_instanceBits; /* * Map from logical slot to physical memory index for object properties. */ VMFixedVector<ObjectProps::quick_index> m_slotIndex; /* * Metadata about static properties, indexable in two ways: * * 1. Key is property name or Slot. This is the normal use case. * * 2. Key is sequence ID for serialization. When accessed in this manner, * only `serializationIdx` is valid, and all other fields are garbage. * (This is also the only use case in which `serializationIdx` is valid.) */ SPropMap m_staticProperties; /* * Vector of interfaces and their vtables. */ VtableVecSlot* m_vtableVec{nullptr}; /* * Instance property metadata. Access is analogous to access for * m_staticProperties. */ PropMap m_declProperties; /* * Pointer to a function that releases object instances of this class type. */ ObjReleaseFunc m_releaseFunc; /* * Static properties are stored in RDS. There are three phases of sprop * initialization: * * 1. The array of links is itself allocated on Class creation. * 2. The links are bound either when codegen needs the handle value, or when * initSProps() is called in any request. Afterwards, m_sPropCacheInit is * bound, defaulting to false. * 3. The RDS value at m_sPropCacheInit is set to true when initSProps() is * called, and the values are actually initialized. * * For non-persistent classes, we put m_sPropCache in rds::Local, but use the * m_sPropCacheInit flag to indicate whether m_sPropCache needs to be * reinitialized. */ mutable rds::Link<bool, rds::Mode::NonLocal> m_sPropCacheInit; mutable rds::Link< StaticPropData, rds::Mode::NonNormal >* m_sPropCache{nullptr}; mutable default_ptr<ExtraData> m_extra; uint32_t m_memoSize{0}; LowPtr<Func> m_invoke; // __invoke, iff non-static (or closure) ConstMap m_constants; PreClassPtr m_preClass; unsigned m_attrCopy; veclen_t m_classVecLen; veclen_t m_funcVecLen; veclen_t m_vtableVecLen{0}; /* * This class, or one of its ancestors, has a property which maybe redefines * an existing property in an incompatible way. */ bool m_maybeRedefsPropTy : 1; /* * This class (and not any of its transitive parents) has a property which * maybe redefines an existing property in an incompatible way. */ bool m_selfMaybeRedefsPropTy : 1; /* * This class has a property with an initial value which might not satisfy * its type-hint (and therefore requires a check when initialized). */ bool m_needsPropInitialCheck : 1; /* * This class has reified generics. */ bool m_hasReifiedGenerics : 1; /* * This class has a refied parent. */ bool m_hasReifiedParent : 1; /* * Whether the Class requires initialization, because it has either * {p,s}init() methods or static members, or possibly has prop type * invariance violations. */ bool m_needInitialization : 1; bool m_needsInitThrowable : 1; bool m_hasDeepInitProps : 1; // Set if this class is assigned an InstanceBits index; else, one of the // two special values kNoInstanceBit or kProfileInstanceBit. std::atomic<int8_t> m_instanceBitsIndex; ClassPtr m_parent; MethodMap m_methods; InterfaceMap m_interfaces; /* * Vector of 86pinit() methods that need to be called to complete instance * property initialization, and a pointer to a 86sinit() method that needs to * be called to complete static property initialization (or NULL). Such * initialization is triggered only once, the first time one of the following * happens: * - An instance of this class is created. * - A static property of this class is accessed. */ VMFixedVector<const Func*> m_sinitVec; VMFixedVector<const Func*> m_linitVec; VMFixedVector<const Func*> m_pinitVec; /* * Initialization information about instance properties. * * Indexed by the _physical_ index of the property within an object, not its * logical Slot. */ mutable PropInitVec m_declPropInit; MaybeDataType m_enumBaseTy; /* * Whether this is a subclass of Closure whose m_invoke->m_cls has been set * to the closure's context class. */ std::atomic<bool> m_scoped{false}; int32_t m_declPropNumAccessible; LowPtr<Func> m_ctor; LowPtr<Func> m_toString; mutable rds::Link<PropInitVec*, rds::Mode::Normal> m_propDataCache; mutable rds::Link<LowPtr<Class>, rds::Mode::NonLocal> m_cachedClass; RequirementMap m_requirements; VMCompactVector<ClassPtr> m_declInterfaces; mutable rds::Link<Array, rds::Mode::Normal> m_nonScalarConstantCache; public: LowPtr<Class> m_next{nullptr}; // used by NamedEntity private: #ifndef NDEBUG // For asserts only. uint32_t m_magic; #endif /* * Vector of Class pointers that encodes the inheritance hierarchy, including * this Class as the last element. */ LowPtr<Class> m_classVec[1]; // Dynamically sized; must come last. }; /////////////////////////////////////////////////////////////////////////////// /* * Global lock used during class loading. */ extern Mutex g_classesMutex; /////////////////////////////////////////////////////////////////////////////// Attr classKindAsAttr(ClassKind kind); bool isTrait(const Class* cls); bool isInterface(const Class* cls); bool isEnum(const Class* cls); bool isEnumClass(const Class* cls); bool isAnyEnum(const Class* cls); bool isAbstract(const Class* cls); bool isNormalClass(const Class* cls); /* * Whether a class is persistent /and/ has a persistent RDS handle. You * probably mean this instead of cls->isPersistent(), which only checks the * attributes. * * A persistent class can end up with a non-persistent RDS handle if we had to * allocate the handle before we loaded the class. */ bool classHasPersistentRDS(const Class* cls); /* * Convert a class pointer where a string is needed in some context. A warning * will be raised when compiler option Eval.RaiseClassConversionWarning is true. */ const StringData* classToStringHelper(const Class* cls); std::vector<Class*> prioritySerializeClasses(); /////////////////////////////////////////////////////////////////////////////// } #define incl_HPHP_VM_CLASS_INL_H_ #include "hphp/runtime/vm/class-inl.h" #undef incl_HPHP_VM_CLASS_INL_H_