Arquivos
hhvm/hphp/runtime/base/object_data.h
T
Drew Paroski d336b46cc7 Merge ObjectData and Instance together, part 2
When object support was first added to HHVM, a class named "Instance"
was introduced (deriving from ObjectData) to represent instances of user
defined classes. Since then, things have evolved and HPHPc and HPHPi have
been retired, and now there really is no needed to have ObjectData and
Instance be separate classes anymore.

This diff moves all of the functionality from the Instance class to the
ObjectData and removes the Instance class. In the process, I got rid of
a bunch of dead methods and fixed some indentation and other style issues.
2013-07-08 10:30:57 -07:00

599 linhas
20 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_OBJECT_DATA_H_
#define incl_HPHP_OBJECT_DATA_H_
#include "hphp/runtime/base/util/countable.h"
#include "hphp/runtime/base/util/smart_ptr.h"
#include "hphp/runtime/base/types.h"
#include "hphp/runtime/base/macros.h"
#include "hphp/runtime/vm/class.h"
#include "hphp/system/systemlib.h"
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/int.hpp>
namespace HPHP {
///////////////////////////////////////////////////////////////////////////////
class ArrayIter;
class MutableArrayIter;
class HphpArray;
class TypedValue;
class PreClass;
class Class;
void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData,
size_t nProps);
/**
* Base class of all PHP objects and PHP resources.
*/
class ObjectData : public CountableNF {
public:
enum Attribute {
NoDestructor = 0x0001, // __destruct()
HasSleep = 0x0002, // __sleep()
UseSet = 0x0004, // __set()
UseGet = 0x0008, // __get()
UseIsset = 0x0010, // __isset()
UseUnset = 0x0020, // __unset()
HasCall = 0x0080, // defines __call
HasCallStatic = 0x0100, // defines __callStatic
CallToImpl = 0x0200, // call o_to{Boolean,Int64,Double}Impl
// The top 3 bits of o_attributes are reserved to indicate the
// type of collection
CollectionTypeAttrMask = (7 << 13),
VectorAttrInit = (Collection::VectorType << 13),
MapAttrInit = (Collection::MapType << 13),
StableMapAttrInit = (Collection::StableMapType << 13),
SetAttrInit = (Collection::SetType << 13),
PairAttrInit = (Collection::PairType << 13),
};
enum {
RealPropCreate = 1, // Property should be created if it doesn't exist
RealPropNoDynamic = 4, // Don't return dynamic properties
RealPropUnchecked = 8, // Don't check property accessibility
RealPropExist = 16, // For property_exists
};
// ResourceData overrides this setting IsResourceClass to true;
// various macros check whether type T is a resource type by
// inspecting "T::IsResourceClass".
static const bool IsResourceClass = false;
static int ObjAllocatorSizeClassCount;
private:
static DECLARE_THREAD_LOCAL_NO_CHECK(int, os_max_id);
public:
// This constructor is used for all cppext classes (including resources)
// and their descendents.
ObjectData(Class* cls, bool isResource) : o_attribute(0), m_cls(cls) {
assert(uintptr_t(this) % sizeof(TypedValue) == 0);
if (!isResource) {
o_id = ++(*os_max_id);
}
instanceInit(cls);
}
private:
// The two constructors below are used for all pure classes that are not
// descendents of cppext classes
explicit ObjectData(Class* cls) : o_attribute(0), m_cls(cls) {
assert(uintptr_t(this) % sizeof(TypedValue) == 0);
o_id = ++(*os_max_id);
instanceInit(cls);
}
enum class NoInit { noinit };
explicit ObjectData(Class* cls, NoInit) : o_attribute(0), m_cls(cls) {
assert(uintptr_t(this) % sizeof(TypedValue) == 0);
o_id = ++(*os_max_id);
}
// Disallow copy construction
ObjectData(const ObjectData&) = delete;
public:
virtual ~ObjectData(); // all PHP classes need virtual tables
// Call newInstance() to instantiate a PHP object
static ObjectData* newInstance(Class* cls) {
if (cls->m_InstanceCtor) {
return cls->m_InstanceCtor(cls);
}
Attr attrs = cls->attrs();
if (UNLIKELY(attrs & (AttrAbstract | AttrInterface | AttrTrait))) {
raiseAbstractClassError(cls);
}
size_t nProps = cls->numDeclProperties();
size_t size = sizeForNProps(nProps);
ObjectData* obj = (ObjectData*)ALLOCOBJSZ(size);
new (obj) ObjectData(cls);
if (UNLIKELY(cls->callsCustomInstanceInit())) {
/*
This must happen after the constructor finishes,
because it can leak references to obj AND it can
throw exceptions. If we have this in the ObjectData
constructor, and it throws, obj will be partially
destroyed (ie ~ObjectData will be called, resetting
the vtable pointer) leaving dangling references
to the object (eg in backtraces).
*/
obj->callCustomInstanceInit();
}
return obj;
}
// Given a Class that is assumed to be a concrete, regular (not a
// trait or interface), pure PHP class, and an allocator index,
// return a new, uninitialized object of that class.
static ObjectData* newInstanceRaw(Class* cls, int idx);
private:
void instanceInit(Class* cls) {
setAttributes(cls->getODAttrs());
size_t nProps = cls->numDeclProperties();
if (cls->needInitialization()) {
cls->initialize();
}
if (nProps > 0) {
if (cls->pinitVec().size() > 0) {
const Class::PropInitVec* propInitVec = m_cls->getPropData();
assert(propInitVec != nullptr);
assert(nProps == propInitVec->size());
if (!cls->hasDeepInitProps()) {
memcpy(propVec(), &(*propInitVec)[0], nProps * sizeof(TypedValue));
} else {
deepInitHelper(propVec(), &(*propInitVec)[0], nProps);
}
} else {
assert(nProps == cls->declPropInit().size());
memcpy(propVec(), &cls->declPropInit()[0], nProps * sizeof(TypedValue));
}
}
}
public:
void operator delete(void* p);
void release() {
assert(getCount() == 0);
destruct();
if (UNLIKELY(getCount() != 0)) {
// Object was resurrected.
return;
}
delete this;
}
void setId(const ObjectData* r) { if (r) o_id = r->o_id; }
Class* getVMClass() const {
return m_cls;
}
static size_t getVMClassOffset() {
// For assembly linkage.
return offsetof(ObjectData, m_cls);
}
static size_t attributeOff() { return offsetof(ObjectData, o_attribute); }
bool instanceof(const Class* c) const;
bool isCollection() const {
return getCollectionType() != Collection::InvalidType;
}
Collection::Type getCollectionType() const {
// Return the upper 3 bits of o_attribute
return (Collection::Type)((uint16_t)(o_attribute >> 13) & 7);
}
bool implementsIterator() {
return (instanceof(SystemLib::s_IteratorClass));
}
void setAttributes(int attrs) { o_attribute |= attrs; }
void setAttributes(const ObjectData* o) { o_attribute |= o->o_attribute; }
bool getAttribute(Attribute attr) const { return o_attribute & attr; }
void setAttribute(Attribute attr) const { o_attribute |= attr;}
void clearAttribute(Attribute attr) const { o_attribute &= ~attr;}
bool noDestruct() const { return getAttribute(NoDestructor); }
void setNoDestruct() { setAttribute(NoDestructor); }
ObjectData* clearNoDestruct() { clearAttribute(NoDestructor); return this; }
Object iterableObject(bool& isIterable, bool mayImplementIterator = true);
ArrayIter begin(CStrRef context = null_string);
MutableArrayIter begin(Variant* key, Variant& val,
CStrRef context = null_string);
/**
* o_instanceof() can be used for both classes and interfaces.
* It is also worth noting that o_instanceof will always return
* false for classes that are descendents of ResourceData.
*/
bool o_instanceof(CStrRef s) const;
// class info
CStrRef o_getClassName() const;
CStrRef o_getParentName() const;
virtual CStrRef o_getClassNameHook() const;
virtual bool isResource() const { return false; }
int o_getId() const { return o_id;}
bool o_toBoolean() const {
if (getAttribute(CallToImpl)) {
return o_toBooleanImpl();
}
return true;
}
int64_t o_toInt64() const {
if (getAttribute(CallToImpl)) {
return o_toInt64Impl();
}
raiseObjToIntNotice(o_getClassName().data());
return 1;
}
double o_toDouble() const {
if (getAttribute(CallToImpl)) {
return o_toDoubleImpl();
}
return o_toInt64();
}
// overridable casting
virtual bool o_toBooleanImpl() const noexcept;
virtual int64_t o_toInt64Impl() const noexcept;
virtual double o_toDoubleImpl() const noexcept;
virtual Array o_toArray() const;
void destruct();
Array o_toIterArray(CStrRef context, bool getRef = false);
Variant* o_realProp(CStrRef s, int flags,
CStrRef context = null_string) const;
Variant o_get(CStrRef s, bool error = true,
CStrRef context = null_string);
Variant o_set(CStrRef s, CVarRef v);
Variant o_set(CStrRef s, RefResult v);
Variant o_setRef(CStrRef s, CVarRef v);
Variant o_set(CStrRef s, CVarRef v, CStrRef context);
Variant o_set(CStrRef s, RefResult v, CStrRef context);
Variant o_setRef(CStrRef s, CVarRef v, CStrRef context);
void o_setArray(CArrRef properties);
void o_getArray(Array& props, bool pubOnly = false) const;
static Object FromArray(ArrayData* properties);
// TODO Task #2584896: o_invoke and o_invoke_few_args are deprecated. These
// APIs don't properly take class context into account when looking up the
// method, and they duplicate some of the functionality from invokeFunc(),
// invokeFuncFew(), and vm_decode_function(). We should remove these APIs
// and migrate all callers to use invokeFunc(), invokeFuncFew(), and
// vm_decode_function() instead.
Variant o_invoke(CStrRef s, CArrRef params, bool fatal = true);
Variant o_invoke_few_args(CStrRef s, int count,
INVOKE_FEW_ARGS_DECL_ARGS);
void serialize(VariableSerializer* serializer) const;
virtual void serializeImpl(VariableSerializer* serializer) const;
bool hasInternalReference(PointerSet& vars, bool ds = false) const;
virtual void dump() const;
virtual ObjectData* clone();
Variant offsetGet(Variant key);
virtual Variant t___sleep();
virtual Variant t___wakeup();
virtual String t___tostring();
static int GetMaxId() ATTRIBUTE_COLD;
protected:
virtual bool php_sleep(Variant& ret);
public:
CArrRef getDynProps() const { return o_properties; }
void initProperties(int nProp);
void getChildren(std::vector<TypedValue*> &out) {
ArrayData* props = o_properties.get();
if (props) {
props->getChildren(out);
}
}
protected:
TypedValue* propVec();
const TypedValue* propVec() const;
public:
ObjectData* callCustomInstanceInit();
//============================================================================
// Miscellaneous.
void cloneSet(ObjectData* clone);
ObjectData* cloneImpl();
void invokeUserMethod(TypedValue* retval, const Func* method,
CArrRef params);
const Func* methodNamed(const StringData* sd) const {
return getVMClass()->lookupMethod(sd);
}
static size_t sizeForNProps(Slot nProps) {
size_t sz = sizeof(ObjectData) + (sizeof(TypedValue) * nProps);
assert((sz & (sizeof(TypedValue) - 1)) == 0);
return sz;
}
//============================================================================
// Properties.
public:
int builtinPropSize() const { return m_cls->builtinPropSize(); }
private:
void initDynProps(int numDynamic = 0);
Slot declPropInd(TypedValue* prop) const;
inline Variant o_getImpl(CStrRef propName, int flags,
bool error = true, CStrRef context = null_string);
template <typename T>
inline Variant o_setImpl(CStrRef propName, T v,
bool forInit, CStrRef context);
template <bool declOnly>
TypedValue* getPropImpl(Class* ctx, const StringData* key, bool& visible,
bool& accessible, bool& unset);
public:
TypedValue* getProp(Class* ctx, const StringData* key, bool& visible,
bool& accessible, bool& unset);
TypedValue* getDeclProp(Class* ctx, const StringData* key, bool& visible,
bool& accessible, bool& unset);
private:
template <bool warn, bool define>
void propImpl(TypedValue*& retval, TypedValue& tvRef, Class* ctx,
const StringData* key);
void invokeSet(TypedValue* retval, const StringData* key, TypedValue* val);
void invokeGet(TypedValue* retval, const StringData* key);
void invokeGetProp(TypedValue*& retval, TypedValue& tvRef,
const StringData* key);
void invokeIsset(TypedValue* retval, const StringData* key);
void invokeUnset(TypedValue* retval, const StringData* key);
void getProp(const Class* klass, bool pubOnly, const PreClass::Prop* prop,
Array& props, std::vector<bool>& inserted) const;
void getProps(const Class* klass, bool pubOnly, const PreClass* pc,
Array& props, std::vector<bool>& inserted) const;
public:
void prop(TypedValue*& retval, TypedValue& tvRef, Class* ctx,
const StringData* key);
void propD(TypedValue*& retval, TypedValue& tvRef, Class* ctx,
const StringData* key);
void propW(TypedValue*& retval, TypedValue& tvRef, Class* ctx,
const StringData* key);
void propWD(TypedValue*& retval, TypedValue& tvRef, Class* ctx,
const StringData* key);
bool propIsset(Class* ctx, const StringData* key);
bool propEmpty(Class* ctx, const StringData* key);
TypedValue* setProp(Class* ctx, const StringData* key, TypedValue* val,
bool bindingAssignment = false);
TypedValue* setOpProp(TypedValue& tvRef, Class* ctx, unsigned char op,
const StringData* key, Cell* val);
private:
template <bool setResult>
void incDecPropImpl(TypedValue& tvRef, Class* ctx, unsigned char op,
const StringData* key, TypedValue& dest);
public:
template <bool setResult>
void incDecProp(TypedValue& tvRef, Class* ctx, unsigned char op,
const StringData* key, TypedValue& dest);
void unsetProp(Class* ctx, const StringData* key);
static void raiseObjToIntNotice(const char*);
static void raiseAbstractClassError(Class* cls);
void raiseUndefProp(const StringData* name);
private:
static void compileTimeAssertions() {
static_assert(offsetof(ObjectData, _count) == FAST_REFCOUNT_OFFSET,
"Offset of ObjectData._count must be FAST_REFCOUNT_OFFSET");
}
//============================================================================
// ObjectData fields
// o_attribute and o_subclassData will hopefully be packed together
// with _count from parent class
private:
// Various per-instance flags
mutable int16_t o_attribute;
protected:
// 16 bits of memory that can be reused by subclasses
union {
uint16_t u16;
uint8_t u8[2];
} o_subclassData;
// Pointer to this object's class
Class* m_cls;
// Storage for dynamic properties
ArrNR o_properties;
// Numeric identifier of this object (used for var_dump())
int o_id;
} __attribute__((aligned(16)));
template<> inline SmartPtr<ObjectData>::~SmartPtr() {}
typedef GlobalNameValueTableWrapper GlobalVariables;
///////////////////////////////////////////////////////////////////////////////
// Calculate item sizes for object allocators
#define WORD_SIZE sizeof(TypedValue)
#define ALIGN_WORD(n) ((n) + (WORD_SIZE - (n) % WORD_SIZE) % WORD_SIZE)
// Mapping from index to size class for objects. Mapping in the other
// direction is available from ObjectSizeClass<> below.
template<int Idx> class ObjectSizeTable {
enum { prevSize = ObjectSizeTable<Idx - 1>::value };
public:
enum {
value = ALIGN_WORD(prevSize + (prevSize >> 1))
};
};
template<> struct ObjectSizeTable<0> {
enum { value = sizeof(ObjectData) };
};
#undef WORD_SIZE
#undef ALIGN_WORD
/*
* This determines the highest size class we can have by looking for
* the first entry in our table that is larger than the hard coded
* SmartAllocator SLAB_SIZE. This is because you can't (currently)
* SmartAllocate chunks that are potentially bigger than a slab. If
* you introduce a bigger size class, SmartAllocator will hit an
* assertion at runtime. The last size class currently goes up to
* 97096 bytes -- enough room for 6064 TypedValues. Hopefully that's
* enough.
*/
template<int Index>
struct DetermineLargestSizeClass {
typedef typename boost::mpl::eval_if_c<
(ObjectSizeTable<Index>::value > SLAB_SIZE),
boost::mpl::int_<Index>,
DetermineLargestSizeClass<Index + 1>
>::type type;
};
const int NumObjectSizeClasses = DetermineLargestSizeClass<0>::type::value;
template<size_t Sz, int Index> struct LookupObjSizeIndex {
enum { index =
Sz <= ObjectSizeTable<Index>::value
? Index : LookupObjSizeIndex<Sz,Index + 1>::index };
};
template<size_t Sz> struct LookupObjSizeIndex<Sz,NumObjectSizeClasses> {
enum { index = NumObjectSizeClasses };
};
template<size_t Sz>
struct ObjectSizeClass {
enum {
index = LookupObjSizeIndex<Sz,0>::index,
value = ObjectSizeTable<index>::value
};
};
typedef ObjectAllocatorBase*(*ObjectAllocatorBaseGetter)(void);
class ObjectAllocatorCollector {
public:
static std::map<int, ObjectAllocatorBaseGetter> &getWrappers() {
static std::map<int, ObjectAllocatorBaseGetter> wrappers;
return wrappers;
}
};
template <typename T>
void* ObjectAllocatorInitSetup() {
ThreadLocalSingleton<ObjectAllocator<
ObjectSizeClass<sizeof(T)>::value> > tls;
int index = ObjectSizeClass<sizeof(T)>::index;
ObjectAllocatorCollector::getWrappers()[index] =
(ObjectAllocatorBaseGetter)tls.getCheck;
GetAllocatorInitList().insert((AllocatorThreadLocalInit)(tls.getCheck));
return (void*)tls.getNoCheck;
}
/*
* Return the index in ThreadInfo::m_allocators for the allocator
* responsible for a given object size.
*
* There is a maximum limit on the size of allocatable objects. If
* this is reached, this function returns -1.
*/
int object_alloc_size_to_index(size_t size);
///////////////////////////////////////////////////////////////////////////////
// Attribute helpers
class AttributeSetter {
public:
AttributeSetter(ObjectData::Attribute a, ObjectData* o) : m_a(a), m_o(o) {
o->setAttribute(a);
}
~AttributeSetter() {
m_o->clearAttribute(m_a);
}
private:
ObjectData::Attribute m_a;
ObjectData* m_o;
};
class AttributeClearer {
public:
AttributeClearer(ObjectData::Attribute a, ObjectData* o) : m_a(a), m_o(o) {
o->clearAttribute(a);
}
~AttributeClearer() {
m_o->setAttribute(m_a);
}
private:
ObjectData::Attribute m_a;
ObjectData* m_o;
};
ALWAYS_INLINE inline void decRefObj(ObjectData* obj) {
if (obj->decRefCount() == 0) obj->release();
}
///////////////////////////////////////////////////////////////////////////////
inline ObjectData* instanceFromTv(TypedValue* tv) {
assert(tv->m_type == KindOfObject);
assert(dynamic_cast<ObjectData*>(tv->m_data.pobj));
return tv->m_data.pobj;
}
class ExtObjectData : public ObjectData {
public:
explicit ExtObjectData(HPHP::Class* cls) : ObjectData(cls, false) {
assert(!m_cls->callsCustomInstanceInit());
}
};
template <int flags> class ExtObjectDataFlags : public ExtObjectData {
public:
explicit ExtObjectDataFlags(HPHP::Class* cb) : ExtObjectData(cb) {
ObjectData::setAttributes(flags);
}
};
} // HPHP
#endif