c3d067f65c
We would normally have to load the class and do a whole bunch of other noise. Now we don't.
904 linhas
32 KiB
C++
904 linhas
32 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010-2013 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 <bitset>
|
|
#include "tbb/concurrent_hash_map.h"
|
|
#include <atomic>
|
|
|
|
#include "hphp/runtime/base/types.h"
|
|
#include "hphp/util/fixed_vector.h"
|
|
#include "hphp/util/range.h"
|
|
#include "hphp/runtime/vm/fixed_string_map.h"
|
|
#include "hphp/runtime/vm/indexed_string_map.h"
|
|
|
|
namespace HPHP {
|
|
|
|
// Forward declaration.
|
|
class ClassInfo;
|
|
class ClassInfoVM;
|
|
class HphpArray;
|
|
class ObjectData;
|
|
struct HhbcExtClassInfo;
|
|
|
|
|
|
// Forward declarations.
|
|
class Func;
|
|
class FuncEmitter;
|
|
class Unit;
|
|
class UnitEmitter;
|
|
class Class;
|
|
class NamedEntity;
|
|
class PreClass;
|
|
|
|
typedef hphp_hash_set<const StringData*, string_data_hash,
|
|
string_data_isame> TraitNameSet;
|
|
typedef hphp_hash_set<const Class*, pointer_hash<Class> > ClassSet;
|
|
|
|
typedef ObjectData*(*BuiltinCtorFunction)(Class*);
|
|
|
|
/*
|
|
* A PreClass represents the source-level definition of a php class,
|
|
* interface, or trait. Includes things like the names of the parent
|
|
* class (if any), and the names of any interfaces implemented or
|
|
* traits used. Also contains metadata about properites and methods
|
|
* of the class.
|
|
*
|
|
* This is separate from an actual Class because in different requests
|
|
* (depending on include order), the actual instantiation of a
|
|
* PreClass may differ since names may have different meanings. For
|
|
* example, if the PreClass "extends Foo", and Foo is defined
|
|
* differently in different requests, we will make a different Class.
|
|
*
|
|
* Hoistability:
|
|
*
|
|
* When a Unit is loaded at run time, each PreClass in the Unit
|
|
* which is determined to be `hoistable' will be loaded by the
|
|
* runtime (in the order they appear in the source) before the
|
|
* Unit's pseudo-main is executed. The hoistability rules ensure
|
|
* that loading a PreClass which is determined to be hoistable will
|
|
* never cause the autoload facility to be invoked.
|
|
*
|
|
* A class is considered `hoistable' iff the following conditions
|
|
* apply:
|
|
*
|
|
* - It's defined at the top level.
|
|
*
|
|
* - There is no other definition for a class of the same name in
|
|
* its Unit.
|
|
*
|
|
* - It uses no traits. (It may however *be* a trait.)
|
|
*
|
|
* - It implements no interfaces. (It may however *be* an
|
|
* interface.)
|
|
*
|
|
* - It has no parent OR
|
|
* The parent is hoistable and defined earlier in the unit OR
|
|
* The parent is already defined when the unit is required
|
|
*
|
|
* The very last condition here (parent already defined when the
|
|
* unit is required) is not known at parse time. This leads to the
|
|
* Maybe/Always split below.
|
|
*
|
|
*/
|
|
class PreClass : public AtomicCountable {
|
|
friend class PreClassEmitter;
|
|
friend class Peephole;
|
|
|
|
public:
|
|
enum Hoistable {
|
|
NotHoistable,
|
|
Mergeable,
|
|
MaybeHoistable,
|
|
AlwaysHoistable
|
|
};
|
|
|
|
struct Prop {
|
|
Prop() {}
|
|
Prop(PreClass* preClass,
|
|
const StringData* n,
|
|
Attr attrs,
|
|
const StringData* typeConstraint,
|
|
const StringData* docComment,
|
|
const TypedValue& val,
|
|
DataType hphpcType);
|
|
|
|
void prettyPrint(std::ostream& out) const;
|
|
|
|
PreClass* preClass() const { return m_preClass; }
|
|
const StringData* name() const { return m_name; }
|
|
CStrRef nameRef() const { return *(String*)&m_name; }
|
|
const StringData* mangledName() const { return m_mangledName; }
|
|
CStrRef mangledNameRef() const { return *(String*)(&m_mangledName); }
|
|
Attr attrs() const { return m_attrs; }
|
|
const StringData* typeConstraint() const { return m_typeConstraint; }
|
|
DataType hphpcType() const { return m_hphpcType; }
|
|
const StringData* docComment() const { return m_docComment; }
|
|
const TypedValue& val() const { return m_val; }
|
|
|
|
private:
|
|
PreClass* m_preClass;
|
|
const StringData* m_name;
|
|
const StringData* m_mangledName;
|
|
Attr m_attrs;
|
|
const StringData* m_typeConstraint;
|
|
const StringData* m_docComment;
|
|
TypedValue m_val;
|
|
DataType m_hphpcType;
|
|
};
|
|
|
|
struct Const {
|
|
Const() {}
|
|
Const(PreClass* preClass, const StringData* n,
|
|
const StringData* typeConstraint, const TypedValue& val,
|
|
const StringData* phpCode);
|
|
|
|
void prettyPrint(std::ostream& out) const;
|
|
|
|
PreClass* preClass() const { return m_preClass; }
|
|
const StringData* name() const { return m_name; }
|
|
CStrRef nameRef() const { return *(String*)&m_name; }
|
|
const StringData* typeConstraint() const { return m_typeConstraint; }
|
|
const TypedValue& val() const { return m_val; }
|
|
const StringData* phpCode() const { return m_phpCode; }
|
|
|
|
private:
|
|
PreClass* m_preClass;
|
|
const StringData* m_name;
|
|
const StringData* m_typeConstraint;
|
|
TypedValue m_val;
|
|
const StringData* m_phpCode;
|
|
};
|
|
|
|
class TraitPrecRule {
|
|
public:
|
|
TraitPrecRule()
|
|
: m_methodName(0)
|
|
, m_selectedTraitName(0)
|
|
{}
|
|
|
|
TraitPrecRule(const StringData* selectedTraitName,
|
|
const StringData* methodName) :
|
|
m_methodName(methodName), m_selectedTraitName(selectedTraitName),
|
|
m_otherTraitNames() {}
|
|
|
|
void addOtherTraitName(const StringData* traitName) {
|
|
m_otherTraitNames.insert(traitName);
|
|
}
|
|
const StringData* getMethodName() const { return m_methodName; }
|
|
const StringData* getSelectedTraitName() const {
|
|
return m_selectedTraitName;
|
|
}
|
|
void getOtherTraitNames(TraitNameSet& nameSet) const {
|
|
nameSet = m_otherTraitNames;
|
|
}
|
|
|
|
template<class SerDe> void serde(SerDe& sd) {
|
|
sd(m_methodName)(m_selectedTraitName)(m_otherTraitNames);
|
|
}
|
|
|
|
private:
|
|
const StringData* m_methodName;
|
|
const StringData* m_selectedTraitName;
|
|
TraitNameSet m_otherTraitNames;
|
|
};
|
|
|
|
class TraitAliasRule {
|
|
public:
|
|
TraitAliasRule()
|
|
: m_traitName(0)
|
|
, m_origMethodName(0)
|
|
, m_newMethodName(0)
|
|
, m_modifiers(AttrNone)
|
|
{}
|
|
|
|
TraitAliasRule(const StringData* traitName,
|
|
const StringData* origMethodName,
|
|
const StringData* newMethodName,
|
|
Attr modifiers) :
|
|
m_traitName(traitName),
|
|
m_origMethodName(origMethodName),
|
|
m_newMethodName(newMethodName),
|
|
m_modifiers(modifiers) {}
|
|
|
|
const StringData* getTraitName() const { return m_traitName; }
|
|
const StringData* getOrigMethodName() const { return m_origMethodName; }
|
|
const StringData* getNewMethodName() const { return m_newMethodName; }
|
|
Attr getModifiers() const { return m_modifiers; }
|
|
|
|
template<class SerDe> void serde(SerDe& sd) {
|
|
sd(m_traitName)(m_origMethodName)(m_newMethodName)(m_modifiers);
|
|
}
|
|
|
|
private:
|
|
const StringData* m_traitName;
|
|
const StringData* m_origMethodName;
|
|
const StringData* m_newMethodName;
|
|
Attr m_modifiers;
|
|
};
|
|
|
|
typedef FixedVector<const StringData*> InterfaceVec;
|
|
typedef FixedVector<const StringData*> UsedTraitVec;
|
|
typedef FixedVector<TraitPrecRule> TraitPrecRuleVec;
|
|
typedef FixedVector<TraitAliasRule> TraitAliasRuleVec;
|
|
typedef hphp_hash_map<const StringData*, TypedValue, string_data_hash,
|
|
string_data_isame> UserAttributeMap;
|
|
|
|
PreClass(Unit* unit, int line1, int line2, Offset o, const StringData* n,
|
|
Attr attrs, const StringData* parent, const StringData* docComment,
|
|
Id id, Hoistable hoistable);
|
|
~PreClass();
|
|
void atomicRelease();
|
|
|
|
Unit* unit() const { return m_unit; }
|
|
int line1() const { return m_line1; }
|
|
int line2() const { return m_line2; }
|
|
Offset getOffset() const { return m_offset; }
|
|
const StringData* name() const { return m_name; }
|
|
CStrRef nameRef() const { return *(String*)(&m_name); }
|
|
Attr attrs() const { return m_attrs; }
|
|
static Offset attrsOffset() { return offsetof(PreClass, m_attrs); }
|
|
const StringData* parent() const { return m_parent; }
|
|
CStrRef parentRef() const { return *(String*)(&m_parent); }
|
|
const StringData* docComment() const { return m_docComment; }
|
|
Id id() const { return m_id; }
|
|
const InterfaceVec& interfaces() const { return m_interfaces; }
|
|
const UsedTraitVec& usedTraits() const { return m_usedTraits; }
|
|
const TraitPrecRuleVec& traitPrecRules() const { return m_traitPrecRules; }
|
|
const TraitAliasRuleVec& traitAliasRules() const { return m_traitAliasRules; }
|
|
const UserAttributeMap& userAttributes() const { return m_userAttributes; }
|
|
bool isPersistent() const { return m_attrs & AttrPersistent; }
|
|
|
|
/*
|
|
* Funcs, Consts, and Props all behave similarly. Define raw accessors
|
|
* foo() and numFoos() for people munging by hand, and ranges.
|
|
* methods(); numMethods(); FuncRange allMethods();
|
|
* consts(); numConsts(); ConstRange allConsts();
|
|
* properties; numProperties(); PropRange allProperties();
|
|
*/
|
|
|
|
#define DEF_ACCESSORS(Type, TypeName, fields, Fields) \
|
|
Type const* fields() const { return m_##fields.accessList(); } \
|
|
Type* mutable##Fields() { return m_##fields.mutableAccessList(); } \
|
|
size_t num##Fields() const { return m_##fields.size(); } \
|
|
typedef IterRange<Type const*> TypeName##Range; \
|
|
TypeName##Range all##Fields() const { \
|
|
return TypeName##Range(fields(), fields() + m_##fields.size() - 1); \
|
|
}
|
|
|
|
DEF_ACCESSORS(Func*, Func, methods, Methods)
|
|
DEF_ACCESSORS(Const, Const, constants, Constants)
|
|
DEF_ACCESSORS(Prop, Prop, properties, Properties)
|
|
|
|
#undef DEF_ACCESSORS
|
|
|
|
bool hasMethod(const StringData* methName) const {
|
|
return m_methods.contains(methName);
|
|
}
|
|
|
|
bool hasProp(const StringData* propName) const {
|
|
return m_properties.contains(propName);
|
|
}
|
|
|
|
Func* lookupMethod(const StringData* methName) const {
|
|
Func* f = m_methods.lookupDefault(methName, 0);
|
|
assert(f != nullptr);
|
|
return f;
|
|
}
|
|
|
|
const Prop* lookupProp(const StringData* propName) const {
|
|
Slot s = m_properties.findIndex(propName);
|
|
assert(s != kInvalidSlot);
|
|
return &m_properties[s];
|
|
}
|
|
|
|
BuiltinCtorFunction instanceCtor() const { return m_InstanceCtor; }
|
|
int builtinPropSize() const { return m_builtinPropSize; }
|
|
|
|
void prettyPrint(std::ostream& out) const;
|
|
|
|
NamedEntity* namedEntity() const { return m_namedEntity; }
|
|
|
|
static const StringData* manglePropName(const StringData* className,
|
|
const StringData* propName,
|
|
Attr attrs);
|
|
|
|
private:
|
|
typedef IndexedStringMap<Func*,false,Slot> MethodMap;
|
|
typedef IndexedStringMap<Prop,true,Slot> PropMap;
|
|
typedef IndexedStringMap<Const,true,Slot> ConstMap;
|
|
|
|
private:
|
|
Unit* m_unit;
|
|
NamedEntity* m_namedEntity;
|
|
int m_line1;
|
|
int m_line2;
|
|
Offset m_offset;
|
|
Id m_id;
|
|
int m_builtinPropSize;
|
|
Attr m_attrs;
|
|
Hoistable m_hoistable;
|
|
const StringData* m_name;
|
|
const StringData* m_parent;
|
|
const StringData* m_docComment;
|
|
BuiltinCtorFunction m_InstanceCtor;
|
|
InterfaceVec m_interfaces;
|
|
UsedTraitVec m_usedTraits;
|
|
TraitPrecRuleVec m_traitPrecRules;
|
|
TraitAliasRuleVec m_traitAliasRules;
|
|
UserAttributeMap m_userAttributes;
|
|
MethodMap m_methods;
|
|
PropMap m_properties;
|
|
ConstMap m_constants;
|
|
};
|
|
|
|
// It is possible for multiple Class'es to refer to the same PreClass, and we
|
|
// need to make sure that the PreClass lives for as long as an associated Class
|
|
// still exists (even if the associated Unit has been unloaded). Therefore,
|
|
// use AtomicSmartPtr's to enforce this invariant.
|
|
typedef AtomicSmartPtr<PreClass> PreClassPtr;
|
|
|
|
/*
|
|
* Class represents the full definition of a user class in a given
|
|
* request context.
|
|
*
|
|
* See PreClass for more on the distinction.
|
|
*/
|
|
typedef AtomicSmartPtr<Class> ClassPtr;
|
|
class Class : public AtomicCountable {
|
|
public:
|
|
friend class ExecutionContext;
|
|
friend class ObjectData;
|
|
friend class Unit;
|
|
|
|
enum class Avail {
|
|
False,
|
|
True,
|
|
Fail
|
|
};
|
|
|
|
struct Prop {
|
|
// m_name is "" for inaccessible properties (i.e. private properties
|
|
// declared by parents).
|
|
const StringData* m_name;
|
|
const StringData* m_mangledName;
|
|
const StringData* m_originalMangledName;
|
|
Class* m_class; // First parent class that declares this property.
|
|
Attr m_attrs;
|
|
const StringData* m_typeConstraint;
|
|
|
|
/*
|
|
* Set when the frontend can infer a particular type for a
|
|
* declared property. When this is not KindOfInvalid, the type
|
|
* here is actually m_hphpcType OR KindOfNull, but we know
|
|
* KindOfUninit is not possible.
|
|
*/
|
|
DataType m_hphpcType;
|
|
|
|
const StringData* m_docComment;
|
|
};
|
|
|
|
struct SProp {
|
|
const StringData* m_name;
|
|
Attr m_attrs;
|
|
const StringData* m_typeConstraint;
|
|
const StringData* m_docComment;
|
|
Class* m_class; // Most derived class that declared this property.
|
|
TypedValue m_val; // Used if (m_class == this).
|
|
};
|
|
|
|
struct Const {
|
|
Class* m_class; // Most derived class that declared this constant.
|
|
const StringData* m_name;
|
|
TypedValue m_val;
|
|
const StringData* m_phpCode;
|
|
const StringData* m_typeConstraint;
|
|
CStrRef nameRef() const { return *(String*)&m_name; }
|
|
};
|
|
|
|
class PropInitVec {
|
|
public:
|
|
PropInitVec();
|
|
const PropInitVec& operator=(const PropInitVec&);
|
|
~PropInitVec();
|
|
static PropInitVec* allocInRequestArena(const PropInitVec& src);
|
|
static size_t dataOff() { return offsetof(PropInitVec, m_data); }
|
|
|
|
typedef TypedValueAux* iterator;
|
|
iterator begin() { return m_data; }
|
|
iterator end() { return m_data + m_size; }
|
|
TypedValueAux& operator[](size_t i) {
|
|
assert(i < m_size); return m_data[i];
|
|
}
|
|
const TypedValueAux& operator[](size_t i) const {
|
|
assert(i < m_size); return m_data[i];
|
|
}
|
|
void push_back(const TypedValue& v);
|
|
size_t size() const { return m_size; }
|
|
|
|
private:
|
|
PropInitVec(const PropInitVec&);
|
|
TypedValueAux* m_data;
|
|
unsigned m_size;
|
|
bool m_smart;
|
|
};
|
|
|
|
typedef std::vector<const Func*> InitVec;
|
|
typedef std::vector<std::pair<const StringData*, const StringData*> >
|
|
TraitAliasVec;
|
|
|
|
typedef IndexedStringMap<Class*,true,int> InterfaceMap;
|
|
|
|
public:
|
|
// Call newClass() instead of directly calling new.
|
|
static Class* newClass(PreClass* preClass, Class* parent);
|
|
Class(PreClass* preClass, Class* parent, unsigned classVecLen);
|
|
~Class();
|
|
/*
|
|
* destroy() is called when a Class becomes unreachable. This may happen
|
|
* before its refCount hits zero, because it is referred to by
|
|
* its NamedEntity, any derived classes, and (for interfaces) and Class
|
|
* that implents it, and (for traits) any class that uses it.
|
|
* Such referring classes must also be logically dead at the time
|
|
* destroy is called, but we dont have back pointers to find them,
|
|
* so 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 refernce to this
|
|
* Class.
|
|
*/
|
|
void destroy();
|
|
/*
|
|
* atomicRelease() is called when the (atomic) refCount hits zero.
|
|
* The class is completely dead at this point, and its memory is
|
|
* freed immediately.
|
|
*/
|
|
void atomicRelease();
|
|
/*
|
|
* releaseRefs() is called when a Class is put into the zombie state,
|
|
* to free any references to child classes, interfaces and traits
|
|
* Its safe to call multiple times, so is also called from the destructor
|
|
* (in case we bypassed the zombie state).
|
|
*/
|
|
void releaseRefs();
|
|
/*
|
|
* isZombie() returns true if this class has been logically destroyed,
|
|
* but needed to be preserved due to outstanding references.
|
|
*/
|
|
bool isZombie() const { return !m_cachedOffset; }
|
|
|
|
static size_t sizeForNClasses(unsigned nClasses) {
|
|
return offsetof(Class, m_classVec) + (sizeof(Class*) * nClasses);
|
|
}
|
|
|
|
Avail avail(Class *&parent, bool tryAutoload = false) const;
|
|
bool classof(const Class* cls) const {
|
|
/*
|
|
If cls is an interface, we can simply check to
|
|
see if cls is in this->m_interfaces.
|
|
Otherwise, if this is not an interface,
|
|
the classVec check will determine whether its
|
|
an instance of cls (including the case where
|
|
this and cls are the same trait).
|
|
Otherwise, this is an interface, and cls
|
|
is not, so we need to return false. But the classVec
|
|
check can never return true in that case (cls's
|
|
classVec contains only non-interfaces,
|
|
while this->classVec is either empty, or contains
|
|
interfaces).
|
|
*/
|
|
if (UNLIKELY(cls->attrs() & AttrInterface)) {
|
|
return m_interfaces.lookupDefault(cls->m_preClass->name(), nullptr)
|
|
== cls;
|
|
}
|
|
if (m_classVecLen >= cls->m_classVecLen) {
|
|
return (m_classVec[cls->m_classVecLen-1] == cls);
|
|
}
|
|
return false;
|
|
}
|
|
bool ifaceofDirect(const StringData* name) const {
|
|
return m_interfaces.lookupDefault(name, nullptr) != nullptr;
|
|
}
|
|
const StringData* name() const {
|
|
return m_preClass->name();
|
|
}
|
|
Class* parent() const {
|
|
return m_parent.get();
|
|
}
|
|
CStrRef nameRef() const {
|
|
return m_preClass->nameRef();
|
|
}
|
|
CStrRef parentRef() const {
|
|
return m_preClass->parentRef();
|
|
}
|
|
|
|
Func* const* methods() const { return m_methods.accessList(); }
|
|
size_t numMethods() const { return m_methods.size(); }
|
|
typedef IterRange<Func*const*> MethodRange;
|
|
MethodRange methodRange() const {
|
|
return MethodRange(methods(), methods() + numMethods());
|
|
}
|
|
typedef IterRange<Func**> MutableMethodRange;
|
|
MutableMethodRange mutableMethodRange() {
|
|
return MutableMethodRange(m_methods.mutableAccessList(),
|
|
m_methods.mutableAccessList() + m_methods.size());
|
|
}
|
|
const SProp* staticProperties() const
|
|
{ return m_staticProperties.accessList(); }
|
|
size_t numStaticProperties() const { return m_staticProperties.size(); }
|
|
const Prop* declProperties() const { return m_declProperties.accessList(); }
|
|
size_t numDeclProperties() const { return m_declProperties.size(); }
|
|
const Const* constants() const { return m_constants.accessList(); }
|
|
size_t numConstants() const { return m_constants.size(); }
|
|
Attr attrs() const {
|
|
assert(Attr(m_attrCopy) == m_preClass->attrs());
|
|
return Attr(m_attrCopy);
|
|
}
|
|
bool verifyPersistent() const;
|
|
const Func* getCtor() const { return m_ctor; }
|
|
const Func* getDeclaredCtor() const;
|
|
const Func* getDtor() const { return m_dtor; }
|
|
const Func* getToString() const { return m_toString; }
|
|
const PreClass* preClass() const { return m_preClass.get(); }
|
|
const ClassInfo* clsInfo() const { return m_clsInfo; }
|
|
|
|
// We use the TypedValue::_count field to indicate whether a property
|
|
// requires "deep" initialization (0 = no, 1 = yes)
|
|
const PropInitVec* getPropData() const;
|
|
|
|
bool hasDeepInitProps() const { return m_hasDeepInitProps; }
|
|
bool needInitialization() const { return m_needInitialization; }
|
|
bool hasInitMethods() const { return m_hasInitMethods; }
|
|
bool callsCustomInstanceInit() const { return m_callsCustomInstanceInit; }
|
|
const InterfaceMap& allInterfaces() const { return m_interfaces; }
|
|
const std::vector<ClassPtr>& usedTraits() const {
|
|
return m_usedTraits;
|
|
}
|
|
const TraitAliasVec& traitAliases() const { return m_traitAliases; }
|
|
const InitVec& pinitVec() const { return m_pinitVec; }
|
|
const PropInitVec& declPropInit() const { return m_declPropInit; }
|
|
|
|
// ObjectData attributes, to be set during instance initialization.
|
|
int getODAttrs() const { return m_ODAttrs; }
|
|
|
|
int builtinPropSize() const { return m_builtinPropSize; }
|
|
BuiltinCtorFunction instanceCtor() const { return m_InstanceCtor; }
|
|
|
|
// Interfaces this class declares in its "implements" clause.
|
|
const std::vector<ClassPtr>& declInterfaces() const {
|
|
return m_declInterfaces;
|
|
}
|
|
|
|
// These two fields are used by getClassInfo to locate all trait
|
|
// methods present in methods(): indices [traitsBeginIdx, traitsEndIdx)
|
|
Slot traitsBeginIdx() const { return m_traitsBeginIdx; }
|
|
Slot traitsEndIdx() const { return m_traitsEndIdx; }
|
|
|
|
Func* lookupMethod(const StringData* methName) const {
|
|
return m_methods.lookupDefault(methName, 0);
|
|
}
|
|
|
|
bool isPersistent() const { return m_attrCopy & AttrPersistent; }
|
|
|
|
/*
|
|
* We have a call site for an object method, which previously
|
|
* invoked func, but this call has a different Class (*this). See
|
|
* if we can figure out the correct Func to call.
|
|
*
|
|
* Since this exists to be inlined into a single callsite in targetcache,
|
|
* and the dependencies are a bit hairy, it's defined in targetcache.cpp.
|
|
*/
|
|
const Func* wouldCall(const Func* prev) const;
|
|
|
|
// Finds the base class defining the given method (NULL if none).
|
|
// Note: for methods imported via traits, the base class is the one that
|
|
// uses/imports the trait.
|
|
Class* findMethodBaseClass(const StringData* methName);
|
|
|
|
void getMethodNames(const Class* ctx, HphpArray* methods) const;
|
|
|
|
// Returns true iff this class declared the given method.
|
|
// For trait methods, the class declaring them is the one that uses/imports
|
|
// the trait.
|
|
bool declaredMethod(const Func* method);
|
|
|
|
bool hasConstant(const StringData* clsCnsName) const {
|
|
return m_constants.contains(clsCnsName);
|
|
}
|
|
|
|
/*
|
|
* Look up the actual value of a class constant.
|
|
*
|
|
* Performs dynamic initialization if necessary.
|
|
*/
|
|
TypedValue* clsCnsGet(const StringData* clsCnsName) const;
|
|
|
|
/*
|
|
* Look up a class constant's TypedValue if it doesn't require
|
|
* dynamic initialization.
|
|
*
|
|
* 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.)
|
|
*/
|
|
TypedValue* cnsNameToTV(const StringData* name, Slot& slot) const;
|
|
|
|
/*
|
|
* Provide the current runtime type of this class constant. This
|
|
* has predictive value for the translator.
|
|
*/
|
|
DataType clsCnsType(const StringData* clsCnsName) const;
|
|
|
|
void initialize() const;
|
|
void initPropHandle() const;
|
|
unsigned propHandle() const { return m_propDataCache; }
|
|
void initSPropHandle() const;
|
|
unsigned sPropHandle() const { return m_propSDataCache; }
|
|
void initProps() const;
|
|
TypedValue* initSProps() const;
|
|
Class* getCached() const;
|
|
void setCached();
|
|
|
|
// Returns kInvalidSlot if we can't find this property.
|
|
Slot lookupDeclProp(const StringData* propName) const {
|
|
return m_declProperties.findIndex(propName);
|
|
}
|
|
|
|
Slot getDeclPropIndex(Class* ctx, const StringData* key,
|
|
bool& accessible) const;
|
|
|
|
TypedValue* getSProp(Class* ctx, const StringData* sPropName,
|
|
bool& visible, bool& accessible) const;
|
|
|
|
static bool IsPropAccessible(const Prop& prop, Class* ctx);
|
|
|
|
// Returns kInvalidSlot if we can't find this static property.
|
|
Slot lookupSProp(const StringData* sPropName) const {
|
|
return m_staticProperties.findIndex(sPropName);
|
|
}
|
|
|
|
TypedValue getStaticPropInitVal(const SProp& prop);
|
|
|
|
void getClassInfo(ClassInfoVM* ci);
|
|
|
|
size_t declPropOffset(Slot index) const;
|
|
|
|
DataType declPropHphpcType(Slot index) const {
|
|
auto& prop = m_declProperties[index];
|
|
return prop.m_hphpcType;
|
|
}
|
|
|
|
unsigned classVecLen() const {
|
|
return m_classVecLen;
|
|
}
|
|
static size_t preClassOff() { return offsetof(Class, m_preClass); }
|
|
static size_t classVecOff() { return offsetof(Class, m_classVec); }
|
|
static size_t classVecLenOff() { return offsetof(Class, m_classVecLen); }
|
|
static Offset getMethodsOffset() { return offsetof(Class, m_methods); }
|
|
typedef IndexedStringMap<Func*,false,Slot> MethodMap;
|
|
|
|
public:
|
|
static hphp_hash_map<const StringData*, const HhbcExtClassInfo*,
|
|
string_data_hash, string_data_isame> s_extClassHash;
|
|
|
|
/*
|
|
* During warmup, we profile the most common classes involved in
|
|
* instanceof checks in order to set up a bitmask for each class to
|
|
* allow these checks to be performed quickly by the JIT.
|
|
*
|
|
* initInstanceBits() must be called by the first translation which
|
|
* uses instance bits, while holding the write lease. The accessors
|
|
* for instance bits (haveInstanceBit, getInstanceBitMask) also
|
|
* require holding the write lease.
|
|
*/
|
|
static size_t instanceBitsOff() { return offsetof(Class, m_instanceBits); }
|
|
static void profileInstanceOf(const StringData* name);
|
|
static void initInstanceBits();
|
|
static bool haveInstanceBit(const StringData* name);
|
|
static bool getInstanceBitMask(const StringData* name,
|
|
int& offset, uint8_t& mask);
|
|
|
|
private:
|
|
typedef tbb::concurrent_hash_map<
|
|
const StringData*, uint64_t, pointer_hash<StringData>> InstanceCounts;
|
|
typedef hphp_hash_map<const StringData*, unsigned,
|
|
pointer_hash<StringData>> InstanceBitsMap;
|
|
typedef std::bitset<128> InstanceBits;
|
|
static const size_t kInstanceBits = sizeof(InstanceBits) * CHAR_BIT;
|
|
static InstanceCounts s_instanceCounts;
|
|
static ReadWriteMutex s_instanceCountsLock;
|
|
static InstanceBitsMap s_instanceBits;
|
|
static ReadWriteMutex s_instanceBitsLock;
|
|
static std::atomic<bool> s_instanceBitsInit;
|
|
|
|
struct TraitMethod {
|
|
Class* m_trait;
|
|
Func* m_method;
|
|
Attr m_modifiers;
|
|
TraitMethod(Class* trait, Func* method, Attr modifiers) :
|
|
m_trait(trait), m_method(method), m_modifiers(modifiers) { }
|
|
};
|
|
|
|
typedef IndexedStringMap<Const,true,Slot> ConstMap;
|
|
typedef IndexedStringMap<Prop,true,Slot> PropMap;
|
|
typedef IndexedStringMap<SProp,true,Slot> SPropMap;
|
|
typedef std::list<TraitMethod> TraitMethodList;
|
|
typedef hphp_hash_map<const StringData*, TraitMethodList, string_data_hash,
|
|
string_data_isame> MethodToTraitListMap;
|
|
|
|
void initialize(TypedValue*& sPropData) const;
|
|
HphpArray* initClsCnsData() const;
|
|
PropInitVec* initPropsImpl() const;
|
|
TypedValue* initSPropsImpl() const;
|
|
void setPropData(PropInitVec* propData) const;
|
|
void setSPropData(TypedValue* sPropData) const;
|
|
TypedValue* getSPropData() const;
|
|
|
|
void importTraitMethod(const TraitMethod& traitMethod,
|
|
const StringData* methName,
|
|
MethodMap::Builder& curMethodMap);
|
|
Class* findSingleTraitWithMethod(const StringData* methName);
|
|
void setImportTraitMethodModifiers(TraitMethodList& methList,
|
|
Class* traitCls,
|
|
Attr modifiers);
|
|
void importTraitMethods(MethodMap::Builder& curMethodMap);
|
|
void addTraitPropInitializers(bool staticProps);
|
|
void applyTraitRules(MethodToTraitListMap& importMethToTraitMap);
|
|
void applyTraitPrecRule(const PreClass::TraitPrecRule& rule,
|
|
MethodToTraitListMap& importMethToTraitMap);
|
|
void applyTraitAliasRule(const PreClass::TraitAliasRule& rule,
|
|
MethodToTraitListMap& importMethToTraitMap);
|
|
void importTraitProps(PropMap::Builder& curPropMap,
|
|
SPropMap::Builder& curSPropMap);
|
|
void importTraitInstanceProp(Class* trait,
|
|
Prop& traitProp,
|
|
TypedValue& traitPropVal,
|
|
PropMap::Builder& curPropMap);
|
|
void importTraitStaticProp(Class* trait,
|
|
SProp& traitProp,
|
|
PropMap::Builder& curPropMap,
|
|
SPropMap::Builder& curSPropMap);
|
|
void addTraitAlias(const StringData* traitName,
|
|
const StringData* origMethName,
|
|
const StringData* newMethName);
|
|
|
|
void checkInterfaceMethods();
|
|
void methodOverrideCheck(const Func* parentMethod, const Func* method);
|
|
|
|
static bool compatibleTraitPropInit(TypedValue& tv1, TypedValue& tv2);
|
|
void removeSpareTraitAbstractMethods(
|
|
MethodToTraitListMap& importMethToTraitMap);
|
|
|
|
void setParent();
|
|
void setSpecial();
|
|
void setMethods();
|
|
void setODAttributes();
|
|
void setConstants();
|
|
void setProperties();
|
|
void setInitializers();
|
|
void setInterfaces();
|
|
void setClassVec();
|
|
void setUsedTraits();
|
|
void setInstanceBits();
|
|
void setInstanceBitsAndParents();
|
|
template<bool setParents> void setInstanceBitsImpl();
|
|
|
|
PreClassPtr m_preClass;
|
|
ClassPtr m_parent;
|
|
std::vector<ClassPtr> m_declInterfaces; // interfaces this class declares in
|
|
// its "implements" clause
|
|
InterfaceMap m_interfaces;
|
|
|
|
std::vector<ClassPtr> m_usedTraits;
|
|
TraitAliasVec m_traitAliases;
|
|
|
|
MethodMap m_methods;
|
|
|
|
Slot m_traitsBeginIdx;
|
|
Slot m_traitsEndIdx;
|
|
Func* m_ctor;
|
|
Func* m_dtor;
|
|
Func* m_toString;
|
|
|
|
// 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.
|
|
InitVec m_pinitVec;
|
|
InitVec m_sinitVec;
|
|
const ClassInfo* m_clsInfo;
|
|
unsigned m_needInitialization : 1; // requires initialization,
|
|
// due to [ps]init or simply
|
|
// having static members
|
|
unsigned m_hasInitMethods : 1; // any __[ps]init() methods?
|
|
unsigned m_callsCustomInstanceInit : 1; // should we always call __init__
|
|
// on new instances?
|
|
unsigned m_hasDeepInitProps : 1;
|
|
unsigned m_attrCopy : 28; // cache of m_preClass->attrs().
|
|
int m_ODAttrs;
|
|
|
|
int m_builtinPropSize;
|
|
int m_declPropNumAccessible;
|
|
unsigned m_classVecLen;
|
|
public:
|
|
unsigned m_cachedOffset; // used by Unit
|
|
private:
|
|
unsigned m_propDataCache;
|
|
unsigned m_propSDataCache;
|
|
|
|
BuiltinCtorFunction m_InstanceCtor;
|
|
|
|
ConstMap m_constants;
|
|
|
|
// Properties.
|
|
//
|
|
// 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;
|
|
PropInitVec m_declPropInit;
|
|
|
|
SPropMap m_staticProperties;
|
|
|
|
MethodToTraitListMap m_importMethToTraitMap;
|
|
|
|
public:
|
|
void getChildren(std::vector<TypedValue *> &out);
|
|
|
|
public: // used in Unit
|
|
Class* m_nextClass;
|
|
private:
|
|
// m_instanceBits and m_classVec are both used for efficient
|
|
// instanceof checking in translated code.
|
|
|
|
// Bitmap of parent classes and implemented interfaces. Each bit corresponds
|
|
// to a commonly used class name, determined during the profiling warmup
|
|
// requests.
|
|
InstanceBits m_instanceBits;
|
|
// Vector of Class pointers that encodes the inheritance hierarchy, including
|
|
// this Class as the last element.
|
|
Class* m_classVec[1]; // Dynamically sized; must come last.
|
|
};
|
|
|
|
struct class_hash {
|
|
size_t operator()(const Class* c) const {
|
|
return hash_int64((intptr_t)c);
|
|
}
|
|
};
|
|
|
|
struct class_same {
|
|
bool operator()(const Class* c1, const Class* c2) const {
|
|
assert(c1 && c2);
|
|
return (void*)c1 == (void*)c2;
|
|
}
|
|
};
|
|
|
|
} // HPHP
|
|
|
|
#endif
|