HEX
Server: Apache
System: Linux wp02.tdr-lab.com 3.10.0-1160.42.2.el7.x86_64 #1 SMP Tue Sep 7 14:49:57 UTC 2021 x86_64
User: kusanagi (1001)
PHP: 7.4.23
Disabled: NONE
Upload Files
File: //proc/self/root/usr/include/hphp/runtime/vm/class.h
/*
   +----------------------------------------------------------------------+
   | 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.               |
   +----------------------------------------------------------------------+
*/

#ifndef incl_HPHP_VM_CLASS_H_
#define incl_HPHP_VM_CLASS_H_

#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/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/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/trait-method-import-data.h"

#include "hphp/util/default-ptr.h"
#include "hphp/util/hash-map-typedefs.h"

#include <folly/Hash.h>
#include <folly/Range.h>

#include <list>
#include <memory>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

namespace HPHP {
///////////////////////////////////////////////////////////////////////////////

struct Class;
struct ClassInfo;
struct Func;
struct StringData;
struct c_WaitHandle;

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;
};

using ClassPtr = AtomicSharedLowPtr<Class>;

/*
 * 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
  };

  /*
   * Instance property information.
   */
  struct Prop {
    /*
     * name is "" for inaccessible properties (i.e. private properties declared
     * by parents).
     */
    LowStringPtr name;
    LowStringPtr mangledName;
    LowStringPtr originalMangledName;

    /* First parent class that declares this property. */
    LowPtr<Class> cls;

    Attr attrs;
    LowStringPtr typeConstraint;
    /*
     * 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;
    LowStringPtr docComment;
    int idx;
  };

  /*
   * Static property information.
   */
  struct SProp {
    LowStringPtr name;
    Attr attrs;
    LowStringPtr typeConstraint;
    LowStringPtr docComment;

    /* Most derived class that declared this property. */
    LowPtr<Class> cls;
    int idx;

    /* Used if (cls == this). */
    TypedValue val;

    RepoAuthType repoAuthType;
  };

  /*
   * Class constant information.
   */
  struct Const {
    /* Most derived class that declared this constant. */
    LowPtr<Class> cls;
    LowStringPtr name;
    TypedValueAux val;

    bool isAbstract() const { return val.constModifiers().m_isAbstract; }
    bool isType()     const { return val.constModifiers().m_isType; }
  };

  /*
   * 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.
   */
  struct PropInitVec {
    PropInitVec();
    ~PropInitVec();

    const PropInitVec& operator=(const PropInitVec&);

    using iterator = TypedValueAux*;

    iterator begin();
    iterator end();
    size_t size() const;

    TypedValueAux& operator[](size_t i);
    const TypedValueAux& operator[](size_t i) const;

    void push_back(const TypedValue& v);

    /*
     * Make a request-allocated copy of `src'.
     */
    static PropInitVec* allocWithReqAllocator(const PropInitVec& src);

    static constexpr size_t dataOff() {
      return offsetof(PropInitVec, m_data);
    }

  private:
    PropInitVec(const PropInitVec&);

    bool reqAllocated() const;

    TypedValueAux* 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<LowPtr<Func>> vtable;
    LowPtr<Class> iface;
  };

  /*
   * Container types.
   */
  using MethodMap         = FixedStringMap<Slot, false, Slot>;
  using MethodMapBuilder  = FixedStringMapBuilder<Func*, Slot, false, Slot>;
  using InterfaceMap      = IndexedStringMap<LowPtr<Class>, true, int>;
  using RequirementMap    = IndexedStringMap<
                              const PreClass::ClassRequirement*, true, int>;

  using TraitAliasVec = std::vector<PreClass::TraitAliasRule::NamePair>;

  /*
   * Scope context for a Closure subclass.
   */
  struct CloneScope {
    LowPtr<Class> ctx;
    Attr attrs;

    bool operator==(CloneScope o) const { return ctx == o.ctx &&
                                                 attrs == o.attrs; }
    bool operator!=(CloneScope o) const { return !(*this == o); }

    struct hash {
      size_t operator()(CloneScope cs) const {
        return folly::hash::hash_combine(
          cs.ctx.get(),
          static_cast<uint32_t>(cs.attrs)
        );
      }
    };
  };

  /*
   * 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<CloneScope,ClassPtr,CloneScope::hash>;

  /*
   * A reference to a scoped clone of a Closure subclass.  We omit the Class
   * ctx, since we only use this struct when the ctx is `this'.
   */
  struct ScopedCloneBackref {
    ClassPtr template_cls;
    /* LowPtr<Class> ctx_cls = this; */
    Attr ctx_attrs;
  };

  /*
   * 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.
   *
   * Passing a value of `attrs' that is not AttrNone indicates that the scoping
   * is dynamic---i.e., via Closure::bind(), as opposed to a CreateCl opcode.
   * If the specified `attrs' do not match those of the __invoke method, we
   * update them in the clone along with the scope.  All closure __invoke
   * methods have AttrPublic, so using AttrNone as a sentinel here is
   * unambiguous.
   *
   * 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, whereas
   * scoping events via Closure::bind() clone from another scoped clone (which
   * may or may not be the first clone, which aliases the template class).
   * Thus, when rescoping dynamically, we need to find the template class
   * first, since it owns the clone cache.
   *
   * Additionally, for dynamic rescopings, we always produce a clone.  Many
   * situations may arise in Closure::bind() that never do in CreateCl---e.g.,
   * a closure object whose class is Closure rather than an emitter-generated
   * subclass of Closure, a closure scoped to its own class, etc.  Requiring a
   * clone in the dynamic case keeps us from mucking up the template __invoke's
   * attrs, and gives us the invariant that the template class is being used as
   * a scoped clone iff its __invoke has a different cls().
   *
   * @requires: parent() == SystemLib::s_ClosureClass
   */
  Class* rescope(Class* ctx, Attr attrs = AttrNone);

  /*
   * 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();

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;

  /*
   * 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;

  /*
   * Uncounted String names of this class and of its parent.
   */
  StrNR nameStr() const;
  StrNR parentStr() const;

  /*
   * The attributes on this class.
   */
  Attr attrs() const;

  /*
   * ObjectData attributes, to be set during instance initialization.
   */
  int getODAttrs() 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()
   */
  bool isPersistent() 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() or ClassName(), as
   * opposed to the default 86ctor() method generated by the compiler.
   */
  const Func* getCtor() const;
  const Func* getDeclaredCtor() const;
  const Func* getDtor() const;
  const Func* getToString() 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.
   */
  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;

  /*
   * 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);


  /////////////////////////////////////////////////////////////////////////////
  // 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;

  /*
   * 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;

  /*
   * 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;

  /*
   * 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;


  /////////////////////////////////////////////////////////////////////////////
  // Property initialization.                                           [const]

  /*
   * Whether this Class requires initialization, either because of nonscalar
   * instance property initializers, or simply due to having static properties.
   */
  bool needInitialization() 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 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;

  /*
   * 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.
   */
  const FixedVector<const Func*>& pinitVec() 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> sPropLink(Slot index) const;
  rds::Link<bool> sPropInitLink() 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;


  /////////////////////////////////////////////////////////////////////////////
  // Property lookup and accessibility.                                 [const]

  template <class T>
  struct PropLookup {
    T prop;
    bool accessible;
  };

  /*
   * 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.
   */
  PropLookup<Slot> getDeclPropIndex(const Class*, const StringData*) const;

  /*
   * The equivalent of getDeclPropIndex(), but for static properties.
   */
  PropLookup<Slot> 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.
   *
   * May perform initialization.
   */
  PropLookup<TypedValue*> getSProp(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;

  /*
   * Look up the actual value of a class constant.  Perform dynamic
   * initialization if necessary.
   *
   * Return a Cell containing KindOfUninit if this class has no such constant.
   *
   * The returned Cell is guaranteed not to hold a reference counted object (it
   * may, however, be KindOfString for a static string).
   */
  Cell clsCnsGet(const StringData* clsCnsName,
                 bool includeTypeCns = false) 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 Cell* cnsNameToTV(const StringData* clsCnsName,
                          Slot& clsCnsInd,
                          bool includeTypeCns = false) const;

  /*
   * Provide the current runtime type of this class constant.
   *
   * This has predictive value for the translator.
   */
  DataType clsCnsType(const StringData* clsCnsName) 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;

  /*
   * 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 std::vector<ClassPtr>& usedTraitClasses() const;

  /*
   * Trait alias rules.
   *
   * This is only used by reflection.
   */
  const TraitAliasVec& traitAliases() const;

  /*
   * All trait and interface requirements imposed on this class, including
   * those imposed by traits.
   */
  const RequirementMap& allRequirements() const;


  /////////////////////////////////////////////////////////////////////////////
  // Objects.                                                           [const]

  /*
   * Offset of the declared instance property at `index' on an ObjectData
   * instantiated from this class.
   */
  size_t declPropOffset(Slot index) 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>> 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 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;


  /////////////////////////////////////////////////////////////////////////////
  // Other methods.
  //
  // Avoiding adding methods to this section.

  /*
   * Whether this class can be made persistent---i.e., if AttrPersistent is set
   * and all parents, interfaces, and traits for this class are persistent.
   */
  bool verifyPersistent() 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();
  bool checkInstanceBit(unsigned int bit) const;

  /*
   * Get the underlying enum base type if this is an enum.
   *
   * A return of folly::none represents the `mixed' type.
   */
  MaybeDataType enumBaseTy() const;


  bool needsInitSProps() const;

  // For assertions:
  void validate() const;

  /////////////////////////////////////////////////////////////////////////////
  // Offset accessors.                                                 [static]

#define OFF(f)                          \
  static constexpr ptrdiff_t f##Off() { \
    return offsetof(Class, m_##f);      \
  }
  OFF(classVec)
  OFF(classVecLen)
  OFF(instanceBits)
  OFF(invoke)
  OFF(preClass)
  OFF(propDataCache)
  OFF(vtableVecLen)
  OFF(vtableVec)
  OFF(funcVecLen)
#undef OFF


  /////////////////////////////////////////////////////////////////////////////
  // ExtraData.

private:
  struct ExtraData {
    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.
     */
    std::vector<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};
    BuiltinDtorFunction m_instanceDtor{nullptr};

    /*
     * 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'.
     */
    std::vector<ScopedCloneBackref> 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};
  };

  /*
   * Allocate the ExtraData; done only when necessary.
   */
  void allocExtraData();


  /////////////////////////////////////////////////////////////////////////////
  // Internal types.

private:
  using ConstMap = IndexedStringMap<Const,true,Slot>;
  using PropMap  = IndexedStringMap<Prop,true,Slot>;
  using SPropMap = IndexedStringMap<SProp,true,Slot>;

  struct TraitMethod {
    TraitMethod(const Class* trait_, const Func* method_, Attr modifiers_)
      : trait(trait_)
      , method(method_)
      , modifiers(modifiers_)
    {}

    using class_type = const Class*;
    using method_type = const Func*;
    using modifiers_type = Attr;

    const Class* trait;
    const Func* method;
    Attr modifiers;
  };

  struct TMIOps {
    using prec_type  = const PreClass::TraitPrecRule&;
    using alias_type = const PreClass::TraitAliasRule&;

    // Whether `str' is empty.
    static bool strEmpty(const StringData* str);

    // Return the name for the trait class.
    static const StringData* clsName(const Class* traitCls);

    // Is-a methods.
    static bool isTrait(const Class* traitCls);
    static bool isAbstract(Attr modifiers);

    // Whether to exclude methods with name `methName' when adding.
    static bool exclude(const StringData* methName);

    // TraitMethod constructor.
    static TraitMethod traitMethod(const Class* traitCls,
                                   const Func* traitMeth,
                                   alias_type rule);

    // Accessors for the precedence rule type.
    static const StringData* precMethodName(prec_type rule);
    static const StringData* precSelectedTraitName(prec_type rule);
    static TraitNameSet      precOtherTraitNames(prec_type rule);

    // Accessors for the alias rule type.
    static const StringData* aliasTraitName(alias_type rule);
    static const StringData* aliasOrigMethodName(alias_type rule);
    static const StringData* aliasNewMethodName(alias_type rule);
    static Attr aliasModifiers(alias_type rule);

    // Register a trait alias once the trait class is found.
    static void addTraitAlias(Class* cls, alias_type rule,
                              const Class* traitCls);

    // Trait class/method finders.
    static const Class* findSingleTraitWithMethod(const Class* cls,
                                       const StringData* origMethName);
    static const Class* findTraitClass(const Class* cls,
                                       const StringData* traitName);
    static const Func* findTraitMethod(const Class* cls,
                                       const Class* traitCls,
                                       const StringData* origMethName);

    // Errors.
    static void errorUnknownMethod(prec_type rule);
    static void errorUnknownMethod(alias_type rule,
                                   const StringData* methName);
    template <class Rule>
    static void errorUnknownTrait(const Rule& rule,
                                  const StringData* traitName);
    static void errorDuplicateMethod(const Class* cls,
                                     const StringData* methName);
    static void errorInconsistentInsteadOf(const Class* cls,
                                           const StringData* methName);
  };

  friend struct TMIOps;

  using TMIData = TraitMethodImportData<TraitMethod,
                                        TMIOps,
                                        const StringData*,
                                        string_data_hash,
                                        string_data_isame>;


  /////////////////////////////////////////////////////////////////////////////
  // Private methods.

private:
  Class(PreClass* preClass,
        Class* parent,
        std::vector<ClassPtr>&& usedTraits,
        unsigned classVecLen,
        unsigned funcVecLen);
  ~Class();

  /*
   * Trait method import routines.
   */
  void importTraitMethod(const TMIData::MethodData& mdata,
                         MethodMapBuilder& curMethodMap);
  void importTraitMethods(MethodMapBuilder& curMethodMap);
  void applyTraitRules(TMIData& tmid);

  void importTraitProps(int idxOffset,
                        PropMap::Builder& curPropMap,
                        SPropMap::Builder& curSPropMap);
  void importTraitInstanceProp(Class*      trait,
                               Prop&       traitProp,
                               TypedValue& traitPropVal,
                               const int idxOffset,
                               PropMap::Builder& curPropMap);
  void importTraitStaticProp(Class*   trait,
                             SProp&   traitProp,
                             const int idxOffset,
                             PropMap::Builder& curPropMap,
                             SPropMap::Builder& curSPropMap);
  void addTraitPropInitializers(std::vector<const Func*>&, bool staticProps);

  void checkInterfaceMethods();
  void checkInterfaceConstraints();
  void methodOverrideCheck(const Func* parentMethod, const Func* method);

  static bool compatibleTraitPropInit(TypedValue& tv1, TypedValue& tv2);

  void setParent();
  void setSpecial();
  void setMethods();
  void setODAttributes();
  void setConstants();
  void setProperties();
  void setInitializers();
  void setInterfaces();
  void setInterfaceVtables();
  void setClassVec();
  void setFuncVec(MethodMapBuilder& builder);
  void setRequirements();
  void setEnumType();
  void checkRequirementConstraints() const;
  void raiseUnsatisfiedRequirement(const PreClass::ClassRequirement*) const;
  void setNativeDataInfo();

  template<bool setParents> void setInstanceBitsImpl();
  void addInterfacesFromUsedTraits(InterfaceMap::Builder& builder) const;


  /////////////////////////////////////////////////////////////////////////////
  // Static data members.

public:
  /////////////////////////////////////////////////////////////////////////////
  // Data members.
  //
  // Ordered by usage frequency.  Do not re-order for cosmetic reasons.
  //
  // The ordering is reverse order of hotness because m_classVec is relatively
  // hot, and must be the last member.

  LowPtr<Class> m_nextClass{nullptr}; // used by NamedEntity

private:
  static constexpr uint32_t kMagic = 0xce7adb33;

#ifdef DEBUG
  // For asserts only.
  uint32_t m_magic;
#endif

  default_ptr<ExtraData> m_extra;
  template<class T> friend typename
    std::enable_if<std::is_base_of<c_WaitHandle, T>::value, void>::type
  finish_class();

  friend struct collections::CollectionsExtension;

  RequirementMap m_requirements;
  std::unique_ptr<ClassPtr[]> m_declInterfaces;
  uint32_t m_numDeclInterfaces{0};
  mutable rds::Link<Array, true /* normal_only */>
    m_nonScalarConstantCache{rds::kInvalidHandle};

  LowPtr<Func> m_toString;
  LowPtr<Func> m_invoke; // __invoke, iff non-static (or closure)

  ConstMap m_constants;

  ClassPtr m_parent;
  int32_t m_declPropNumAccessible;
  mutable rds::Link<LowPtr<Class>> m_cachedClass{rds::kInvalidHandle};

  /*
   * 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};

  // NB: 24 bytes available here (in USE_LOWPTR builds).

  /*
   * 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.
   */
  FixedVector<const Func*> m_sinitVec;
  LowPtr<Func> m_ctor;
  LowPtr<Func> m_dtor;
  PropInitVec m_declPropInit;
  FixedVector<const Func*> m_pinitVec;
  SPropMap m_staticProperties;
  PreClassPtr m_preClass;
  InterfaceMap m_interfaces;

  /*
   * 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;
  MethodMap m_methods;

  /*
   * 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.
   */
  mutable rds::Link<StaticPropData>* m_sPropCache{nullptr};
  mutable rds::Link<bool> m_sPropCacheInit{rds::kInvalidHandle};

  veclen_t m_classVecLen;
  veclen_t m_funcVecLen;
  veclen_t m_vtableVecLen{0};
  LowPtr<VtableVecSlot> m_vtableVec{nullptr};

  /*
   * Each ObjectData is created with enough trailing space to directly store
   * the vector of declared properties. To look up a property by name and
   * determine whether it is declared, use m_declPropMap. If the declared
   * property index is already known (as may be the case when executing via the
   * TC), property metadata in m_declPropInfo can be directly accessed.
   *
   * m_declPropInit is indexed by the Slot values from m_declProperties, and
   * contains initialization information.
   */
  PropMap m_declProperties;

  MaybeDataType m_enumBaseTy;
  uint16_t m_ODAttrs;
  mutable rds::Link<PropInitVec*, true /* normal_only */>
    m_propDataCache{rds::kInvalidHandle};

  /*
   * Whether the Class requires initialization, because it has either
   * {p,s}init() methods or static members.
   */
  bool m_needInitialization : 1;

  bool m_needsInitThrowable : 1;
  bool m_hasDeepInitProps : 1;

  /*
   * Cache of m_preClass->attrs().
   */
  unsigned m_attrCopy : 28;

  /*
   * 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;

///////////////////////////////////////////////////////////////////////////////

/*
 * 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
};

Attr classKindAsAttr(ClassKind kind);

bool isTrait(const Class* cls);
bool isInterface(const Class* cls);
bool isEnum(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);

/*
 * Returns whether cls or any of its children may have magic property methods.
 */
bool classMayHaveMagicPropMethods(const Class* cls);

/*
 * Return the class that "owns" f.  This will normally be f->cls(), but for
 * Funcs with static locals, f may have been cloned into a derived class.
 *
 * @requires: RuntimeOption::EvalPerfDataMap
 */
const Class* getOwningClassForFunc(const Func* f);

///////////////////////////////////////////////////////////////////////////////
}

#define incl_HPHP_VM_CLASS_INL_H_
#include "hphp/runtime/vm/class-inl.h"
#undef incl_HPHP_VM_CLASS_INL_H_

#endif // incl_HPHP_VM_CLASS_H_