2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_OBJECT_DATA_H_
18 #define incl_HPHP_OBJECT_DATA_H_
20 #include "hphp/runtime/base/countable.h"
21 #include "hphp/runtime/base/smart-ptr.h"
22 #include "hphp/runtime/base/types.h"
23 #include "hphp/runtime/base/macros.h"
24 #include "hphp/runtime/vm/class.h"
25 #include "hphp/system/systemlib.h"
26 #include <boost/mpl/eval_if.hpp>
27 #include <boost/mpl/int.hpp>
30 ///////////////////////////////////////////////////////////////////////////////
33 class MutableArrayIter
;
40 void deepInitHelper(TypedValue
* propVec
, const TypedValueAux
* propData
,
44 * Base class of all PHP objects and PHP resources.
49 NoDestructor
= 0x0001, // __destruct()
50 HasSleep
= 0x0002, // __sleep()
51 UseSet
= 0x0004, // __set()
52 UseGet
= 0x0008, // __get()
53 UseIsset
= 0x0010, // __isset()
54 UseUnset
= 0x0020, // __unset()
55 HasCall
= 0x0080, // defines __call
56 HasCallStatic
= 0x0100, // defines __callStatic
57 CallToImpl
= 0x0200, // call o_to{Boolean,Int64,Double}Impl
58 // The top 3 bits of o_attributes are reserved to indicate the
60 CollectionTypeAttrMask
= (7 << 13),
61 VectorAttrInit
= (Collection::VectorType
<< 13),
62 MapAttrInit
= (Collection::MapType
<< 13),
63 StableMapAttrInit
= (Collection::StableMapType
<< 13),
64 SetAttrInit
= (Collection::SetType
<< 13),
65 PairAttrInit
= (Collection::PairType
<< 13),
69 RealPropCreate
= 1, // Property should be created if it doesn't exist
70 RealPropUnchecked
= 8, // Don't check property accessibility
71 RealPropExist
= 16, // For property_exists
74 // ResourceData overrides this setting IsResourceClass to true;
75 // various macros check whether type T is a resource type by
76 // inspecting "T::IsResourceClass".
77 static const bool IsResourceClass
= false;
79 static int ObjAllocatorSizeClassCount
;
81 static DECLARE_THREAD_LOCAL_NO_CHECK(int, os_max_id
);
84 explicit ObjectData(Class
* cls
)
88 assert(uintptr_t(this) % sizeof(TypedValue
) == 0);
89 o_id
= ++(*os_max_id
);
94 enum class NoInit
{ noinit
};
95 explicit ObjectData(Class
* cls
, NoInit
)
99 assert(uintptr_t(this) % sizeof(TypedValue
) == 0);
100 o_id
= ++(*os_max_id
);
103 // Disallow copy construction
104 ObjectData(const ObjectData
&) = delete;
107 void setStatic() const { assert(false); }
108 bool isStatic() const { return false; }
109 IMPLEMENT_COUNTABLENF_METHODS_NO_STATIC
111 virtual ~ObjectData(); // all PHP objects need virtual tables
113 // Call newInstance() to instantiate a PHP object
114 static ObjectData
* newInstance(Class
* cls
) {
115 if (cls
->m_InstanceCtor
) {
116 return cls
->m_InstanceCtor(cls
);
118 Attr attrs
= cls
->attrs();
119 if (UNLIKELY(attrs
& (AttrAbstract
| AttrInterface
| AttrTrait
))) {
120 raiseAbstractClassError(cls
);
122 size_t nProps
= cls
->numDeclProperties();
123 size_t size
= sizeForNProps(nProps
);
124 ObjectData
* obj
= (ObjectData
*)ALLOCOBJSZ(size
);
125 new (obj
) ObjectData(cls
);
126 if (UNLIKELY(cls
->callsCustomInstanceInit())) {
128 This must happen after the constructor finishes,
129 because it can leak references to obj AND it can
130 throw exceptions. If we have this in the ObjectData
131 constructor, and it throws, obj will be partially
132 destroyed (ie ~ObjectData will be called, resetting
133 the vtable pointer) leaving dangling references
134 to the object (eg in backtraces).
136 obj
->callCustomInstanceInit();
141 // Given a Class that is assumed to be a concrete, regular (not a
142 // trait or interface), pure PHP class, and an allocator index,
143 // return a new, uninitialized object of that class.
144 static ObjectData
* newInstanceRaw(Class
* cls
, int idx
);
147 void instanceInit(Class
* cls
) {
148 setAttributes(cls
->getODAttrs());
149 size_t nProps
= cls
->numDeclProperties();
150 if (cls
->needInitialization()) {
154 if (cls
->pinitVec().size() > 0) {
155 const Class::PropInitVec
* propInitVec
= m_cls
->getPropData();
156 assert(propInitVec
!= nullptr);
157 assert(nProps
== propInitVec
->size());
158 if (!cls
->hasDeepInitProps()) {
159 memcpy(propVec(), &(*propInitVec
)[0], nProps
* sizeof(TypedValue
));
161 deepInitHelper(propVec(), &(*propInitVec
)[0], nProps
);
164 assert(nProps
== cls
->declPropInit().size());
165 memcpy(propVec(), &cls
->declPropInit()[0], nProps
* sizeof(TypedValue
));
171 void operator delete(void* p
);
174 assert(getCount() == 0);
176 if (UNLIKELY(getCount() != 0)) {
177 // Object was resurrected.
183 Class
* getVMClass() const {
186 static size_t getVMClassOffset() {
187 // For assembly linkage.
188 return offsetof(ObjectData
, m_cls
);
190 static size_t attributeOff() { return offsetof(ObjectData
, o_attribute
); }
191 bool instanceof(const Class
* c
) const;
193 bool isCollection() const {
194 return getCollectionType() != Collection::InvalidType
;
196 Collection::Type
getCollectionType() const {
197 // Return the upper 3 bits of o_attribute
198 return (Collection::Type
)((uint16_t)(o_attribute
>> 13) & 7);
201 bool implementsIterator() {
202 return (instanceof(SystemLib::s_IteratorClass
));
205 void setAttributes(int attrs
) { o_attribute
|= attrs
; }
206 void setAttributes(const ObjectData
* o
) { o_attribute
|= o
->o_attribute
; }
207 bool getAttribute(Attribute attr
) const { return o_attribute
& attr
; }
208 void setAttribute(Attribute attr
) const { o_attribute
|= attr
;}
209 void clearAttribute(Attribute attr
) const { o_attribute
&= ~attr
;}
210 bool noDestruct() const { return getAttribute(NoDestructor
); }
211 void setNoDestruct() { setAttribute(NoDestructor
); }
212 ObjectData
* clearNoDestruct() { clearAttribute(NoDestructor
); return this; }
214 Object
iterableObject(bool& isIterable
, bool mayImplementIterator
= true);
215 ArrayIter
begin(CStrRef context
= null_string
);
216 MutableArrayIter
begin(Variant
* key
, Variant
& val
,
217 CStrRef context
= null_string
);
220 * o_instanceof() can be used for both classes and interfaces.
222 bool o_instanceof(CStrRef s
) const;
225 CStrRef
o_getClassName() const;
226 int o_getId() const { return o_id
;}
228 bool o_toBoolean() const {
229 if (getAttribute(CallToImpl
)) {
230 return o_toBooleanImpl();
235 int64_t o_toInt64() const {
236 if (getAttribute(CallToImpl
)) {
237 return o_toInt64Impl();
239 raiseObjToIntNotice(o_getClassName().data());
243 double o_toDouble() const {
244 if (getAttribute(CallToImpl
)) {
245 return o_toDoubleImpl();
250 // overridable casting
251 virtual bool o_toBooleanImpl() const noexcept
;
252 virtual int64_t o_toInt64Impl() const noexcept
;
253 virtual double o_toDoubleImpl() const noexcept
;
254 virtual Array
o_toArray() const;
258 Array
o_toIterArray(CStrRef context
, bool getRef
= false);
260 Variant
* o_realProp(CStrRef s
, int flags
,
261 CStrRef context
= null_string
) const;
263 Variant
o_get(CStrRef s
, bool error
= true,
264 CStrRef context
= null_string
);
266 Variant
o_set(CStrRef s
, CVarRef v
);
267 Variant
o_set(CStrRef s
, RefResult v
);
268 Variant
o_setRef(CStrRef s
, CVarRef v
);
270 Variant
o_set(CStrRef s
, CVarRef v
, CStrRef context
);
271 Variant
o_set(CStrRef s
, RefResult v
, CStrRef context
);
272 Variant
o_setRef(CStrRef s
, CVarRef v
, CStrRef context
);
274 void o_setArray(CArrRef properties
);
275 void o_getArray(Array
& props
, bool pubOnly
= false) const;
277 static Object
FromArray(ArrayData
* properties
);
279 // TODO Task #2584896: o_invoke and o_invoke_few_args are deprecated. These
280 // APIs don't properly take class context into account when looking up the
281 // method, and they duplicate some of the functionality from invokeFunc(),
282 // invokeFuncFew(), and vm_decode_function(). We should remove these APIs
283 // and migrate all callers to use invokeFunc(), invokeFuncFew(), and
284 // vm_decode_function() instead.
285 Variant
o_invoke(CStrRef s
, CArrRef params
, bool fatal
= true);
286 Variant
o_invoke_few_args(CStrRef s
, int count
,
287 INVOKE_FEW_ARGS_DECL_ARGS
);
289 void serialize(VariableSerializer
* serializer
) const;
290 void serializeImpl(VariableSerializer
* serializer
) const;
291 bool hasInternalReference(PointerSet
& vars
, bool ds
= false) const;
293 virtual ObjectData
* clone();
295 Variant
offsetGet(Variant key
);
296 String
invokeToString();
299 virtual Variant
t___sleep();
300 virtual Variant
t___wakeup();
302 static int GetMaxId() ATTRIBUTE_COLD
;
305 CArrRef
getDynProps() const { return o_properties
; }
306 void initProperties(int nProp
);
308 // heap profiling helpers
309 void getChildren(std::vector
<TypedValue
*> &out
) {
310 // statically declared properties
311 Slot nProps
= m_cls
->numDeclProperties();
312 for (Slot i
= 0; i
< nProps
; ++i
) {
313 out
.push_back(&propVec()[i
]);
316 // dynamic properties
317 ArrayData
* props
= o_properties
.get();
319 props
->getChildren(out
);
324 TypedValue
* propVec();
325 const TypedValue
* propVec() const;
328 ObjectData
* callCustomInstanceInit();
330 //============================================================================
333 void cloneSet(ObjectData
* clone
);
334 ObjectData
* cloneImpl();
336 void invokeUserMethod(TypedValue
* retval
, const Func
* method
,
339 const Func
* methodNamed(const StringData
* sd
) const {
340 return getVMClass()->lookupMethod(sd
);
343 static size_t sizeForNProps(Slot nProps
) {
344 size_t sz
= sizeof(ObjectData
) + (sizeof(TypedValue
) * nProps
);
345 assert((sz
& (sizeof(TypedValue
) - 1)) == 0);
349 //============================================================================
352 int builtinPropSize() const { return m_cls
->builtinPropSize(); }
355 void initDynProps(int numDynamic
= 0);
356 Slot
declPropInd(TypedValue
* prop
) const;
358 inline Variant
o_getImpl(CStrRef propName
, int flags
,
359 bool error
= true, CStrRef context
= null_string
);
360 template <typename T
>
361 inline Variant
o_setImpl(CStrRef propName
, T v
, CStrRef context
);
363 TypedValue
* getProp(Class
* ctx
, const StringData
* key
, bool& visible
,
364 bool& accessible
, bool& unset
);
366 template <bool warn
, bool define
>
367 void propImpl(TypedValue
*& retval
, TypedValue
& tvRef
, Class
* ctx
,
368 const StringData
* key
);
369 void invokeSet(TypedValue
* retval
, const StringData
* key
, TypedValue
* val
);
370 void invokeGet(TypedValue
* retval
, const StringData
* key
);
371 void invokeGetProp(TypedValue
*& retval
, TypedValue
& tvRef
,
372 const StringData
* key
);
373 void invokeIsset(TypedValue
* retval
, const StringData
* key
);
374 void invokeUnset(TypedValue
* retval
, const StringData
* key
);
375 void getProp(const Class
* klass
, bool pubOnly
, const PreClass::Prop
* prop
,
376 Array
& props
, std::vector
<bool>& inserted
) const;
377 void getProps(const Class
* klass
, bool pubOnly
, const PreClass
* pc
,
378 Array
& props
, std::vector
<bool>& inserted
) const;
380 void prop(TypedValue
*& retval
, TypedValue
& tvRef
, Class
* ctx
,
381 const StringData
* key
);
382 void propD(TypedValue
*& retval
, TypedValue
& tvRef
, Class
* ctx
,
383 const StringData
* key
);
384 void propW(TypedValue
*& retval
, TypedValue
& tvRef
, Class
* ctx
,
385 const StringData
* key
);
386 void propWD(TypedValue
*& retval
, TypedValue
& tvRef
, Class
* ctx
,
387 const StringData
* key
);
388 bool propIsset(Class
* ctx
, const StringData
* key
);
389 bool propEmpty(Class
* ctx
, const StringData
* key
);
391 void setProp(Class
* ctx
, const StringData
* key
, TypedValue
* val
,
392 bool bindingAssignment
= false);
393 TypedValue
* setOpProp(TypedValue
& tvRef
, Class
* ctx
, unsigned char op
,
394 const StringData
* key
, Cell
* val
);
396 template <bool setResult
>
397 void incDecPropImpl(TypedValue
& tvRef
, Class
* ctx
, unsigned char op
,
398 const StringData
* key
, TypedValue
& dest
);
400 template <bool setResult
>
401 void incDecProp(TypedValue
& tvRef
, Class
* ctx
, unsigned char op
,
402 const StringData
* key
, TypedValue
& dest
);
403 void unsetProp(Class
* ctx
, const StringData
* key
);
405 static void raiseObjToIntNotice(const char*);
406 static void raiseAbstractClassError(Class
* cls
);
407 void raiseUndefProp(const StringData
* name
);
410 static void compileTimeAssertions() {
411 static_assert(offsetof(ObjectData
, m_count
) == FAST_REFCOUNT_OFFSET
, "");
414 friend struct MemoryProfile
;
416 //============================================================================
420 // Various per-instance flags
421 mutable int16_t o_attribute
;
423 // 16 bits of memory that can be reused by subclasses
428 // Counter to keep track of the number of references to this object
429 // (i.e. the object's "refcount")
430 mutable RefCount m_count
;
431 // Pointer to this object's class
433 // Storage for dynamic properties
435 // Numeric identifier of this object (used for var_dump())
438 } __attribute__((aligned(16)));
440 template<> inline SmartPtr
<ObjectData
>::~SmartPtr() {}
442 typedef GlobalNameValueTableWrapper GlobalVariables
;
445 CountableHelper::CountableHelper(ObjectData
* object
) : m_object(object
) {
446 object
->incRefCount();
450 CountableHelper::~CountableHelper() {
451 m_object
->decRefCount();
454 ///////////////////////////////////////////////////////////////////////////////
455 // Calculate item sizes for object allocators
457 #define WORD_SIZE sizeof(TypedValue)
458 #define ALIGN_WORD(n) ((n) + (WORD_SIZE - (n) % WORD_SIZE) % WORD_SIZE)
460 // Mapping from index to size class for objects. Mapping in the other
461 // direction is available from ObjectSizeClass<> below.
462 template<int Idx
> class ObjectSizeTable
{
463 enum { prevSize
= ObjectSizeTable
<Idx
- 1>::value
};
466 value
= ALIGN_WORD(prevSize
+ (prevSize
>> 1))
470 template<> struct ObjectSizeTable
<0> {
471 enum { value
= sizeof(ObjectData
) };
478 * This determines the highest size class we can have by looking for
479 * the first entry in our table that is larger than the hard coded
480 * SmartAllocator SLAB_SIZE. This is because you can't (currently)
481 * SmartAllocate chunks that are potentially bigger than a slab. If
482 * you introduce a bigger size class, SmartAllocator will hit an
483 * assertion at runtime. The last size class currently goes up to
484 * 97096 bytes -- enough room for 6064 TypedValues. Hopefully that's
488 struct DetermineLargestSizeClass
{
489 typedef typename
boost::mpl::eval_if_c
<
490 (ObjectSizeTable
<Index
>::value
> SLAB_SIZE
),
491 boost::mpl::int_
<Index
>,
492 DetermineLargestSizeClass
<Index
+ 1>
495 const int NumObjectSizeClasses
= DetermineLargestSizeClass
<0>::type::value
;
497 template<size_t Sz
, int Index
> struct LookupObjSizeIndex
{
499 Sz
<= ObjectSizeTable
<Index
>::value
500 ? Index
: LookupObjSizeIndex
<Sz
,Index
+ 1>::index
};
502 template<size_t Sz
> struct LookupObjSizeIndex
<Sz
,NumObjectSizeClasses
> {
503 enum { index
= NumObjectSizeClasses
};
507 struct ObjectSizeClass
{
509 index
= LookupObjSizeIndex
<Sz
,0>::index
,
510 value
= ObjectSizeTable
<index
>::value
514 typedef ObjectAllocatorBase
*(*ObjectAllocatorBaseGetter
)(void);
516 class ObjectAllocatorCollector
{
518 static std::map
<int, ObjectAllocatorBaseGetter
> &getWrappers() {
519 static std::map
<int, ObjectAllocatorBaseGetter
> wrappers
;
524 template <typename T
>
525 void* ObjectAllocatorInitSetup() {
526 ThreadLocalSingleton
<ObjectAllocator
<
527 ObjectSizeClass
<sizeof(T
)>::value
> > tls
;
528 int index
= ObjectSizeClass
<sizeof(T
)>::index
;
529 ObjectAllocatorCollector::getWrappers()[index
] =
530 (ObjectAllocatorBaseGetter
)tls
.getCheck
;
531 GetAllocatorInitList().insert((AllocatorThreadLocalInit
)(tls
.getCheck
));
532 return (void*)tls
.getNoCheck
;
536 * Return the index in ThreadInfo::m_allocators for the allocator
537 * responsible for a given object size.
539 * There is a maximum limit on the size of allocatable objects. If
540 * this is reached, this function returns -1.
542 int object_alloc_size_to_index(size_t size
);
544 ///////////////////////////////////////////////////////////////////////////////
546 class AttributeSetter
{
548 AttributeSetter(ObjectData::Attribute a
, ObjectData
* o
) : m_a(a
), m_o(o
) {
552 m_o
->clearAttribute(m_a
);
555 ObjectData::Attribute m_a
;
559 class AttributeClearer
{
561 AttributeClearer(ObjectData::Attribute a
, ObjectData
* o
) : m_a(a
), m_o(o
) {
562 o
->clearAttribute(a
);
564 ~AttributeClearer() {
565 m_o
->setAttribute(m_a
);
568 ObjectData::Attribute m_a
;
572 ALWAYS_INLINE
inline void decRefObj(ObjectData
* obj
) {
573 if (obj
->decRefCount() == 0) obj
->release();
576 ///////////////////////////////////////////////////////////////////////////////
578 inline ObjectData
* instanceFromTv(TypedValue
* tv
) {
579 assert(tv
->m_type
== KindOfObject
);
580 assert(dynamic_cast<ObjectData
*>(tv
->m_data
.pobj
));
581 return tv
->m_data
.pobj
;
584 class ExtObjectData
: public ObjectData
{
586 explicit ExtObjectData(HPHP::Class
* cls
) : ObjectData(cls
) {
587 assert(!m_cls
->callsCustomInstanceInit());
591 template <int flags
> class ExtObjectDataFlags
: public ExtObjectData
{
593 explicit ExtObjectDataFlags(HPHP::Class
* cb
) : ExtObjectData(cb
) {
594 ObjectData::setAttributes(flags
);