move more helpers out of code-gen
[hiphop-php.git] / hphp / runtime / base / object-data.h
blobeb9846e8539e1861a8f40a70065608e9b1bc4fe8
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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>
29 namespace HPHP {
30 ///////////////////////////////////////////////////////////////////////////////
32 class ArrayIter;
33 class MutableArrayIter;
35 class HphpArray;
36 struct TypedValue;
37 class PreClass;
38 class Class;
40 void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData,
41 size_t nProps);
43 /**
44 * Base class of all PHP objects and PHP resources.
46 class ObjectData {
47 public:
48 enum Attribute {
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
59 // type of collection
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),
68 enum {
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;
80 private:
81 static DECLARE_THREAD_LOCAL_NO_CHECK(int, os_max_id);
83 public:
84 explicit ObjectData(Class* cls)
85 : o_attribute(0)
86 , m_count(0)
87 , m_cls(cls) {
88 assert(uintptr_t(this) % sizeof(TypedValue) == 0);
89 o_id = ++(*os_max_id);
90 instanceInit(cls);
93 private:
94 enum class NoInit { noinit };
95 explicit ObjectData(Class* cls, NoInit)
96 : o_attribute(0)
97 , m_count(0)
98 , m_cls(cls) {
99 assert(uintptr_t(this) % sizeof(TypedValue) == 0);
100 o_id = ++(*os_max_id);
103 // Disallow copy construction
104 ObjectData(const ObjectData&) = delete;
106 public:
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();
138 return obj;
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);
146 private:
147 void instanceInit(Class* cls) {
148 setAttributes(cls->getODAttrs());
149 size_t nProps = cls->numDeclProperties();
150 if (cls->needInitialization()) {
151 cls->initialize();
153 if (nProps > 0) {
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));
160 } else {
161 deepInitHelper(propVec(), &(*propInitVec)[0], nProps);
163 } else {
164 assert(nProps == cls->declPropInit().size());
165 memcpy(propVec(), &cls->declPropInit()[0], nProps * sizeof(TypedValue));
170 public:
171 void operator delete(void* p);
173 void release() {
174 assert(getCount() == 0);
175 destruct();
176 if (UNLIKELY(getCount() != 0)) {
177 // Object was resurrected.
178 return;
180 delete this;
183 Class* getVMClass() const {
184 return m_cls;
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;
224 // class info
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();
232 return true;
235 int64_t o_toInt64() const {
236 if (getAttribute(CallToImpl)) {
237 return o_toInt64Impl();
239 raiseObjToIntNotice(o_getClassName().data());
240 return 1;
243 double o_toDouble() const {
244 if (getAttribute(CallToImpl)) {
245 return o_toDoubleImpl();
247 return o_toInt64();
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;
256 void destruct();
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;
292 void dump() const;
293 virtual ObjectData* clone();
295 Variant offsetGet(Variant key);
296 String invokeToString();
297 bool hasToString();
299 virtual Variant t___sleep();
300 virtual Variant t___wakeup();
302 static int GetMaxId() ATTRIBUTE_COLD;
304 public:
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();
318 if (props) {
319 props->getChildren(out);
323 protected:
324 TypedValue* propVec();
325 const TypedValue* propVec() const;
327 public:
328 ObjectData* callCustomInstanceInit();
330 //============================================================================
331 // Miscellaneous.
333 void cloneSet(ObjectData* clone);
334 ObjectData* cloneImpl();
336 void invokeUserMethod(TypedValue* retval, const Func* method,
337 CArrRef params);
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);
346 return sz;
349 //============================================================================
350 // Properties.
351 public:
352 int builtinPropSize() const { return m_cls->builtinPropSize(); }
354 private:
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);
362 public:
363 TypedValue* getProp(Class* ctx, const StringData* key, bool& visible,
364 bool& accessible, bool& unset);
365 private:
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;
379 public:
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);
395 private:
396 template <bool setResult>
397 void incDecPropImpl(TypedValue& tvRef, Class* ctx, unsigned char op,
398 const StringData* key, TypedValue& dest);
399 public:
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);
409 private:
410 static void compileTimeAssertions() {
411 static_assert(offsetof(ObjectData, m_count) == FAST_REFCOUNT_OFFSET, "");
414 friend struct MemoryProfile;
416 //============================================================================
417 // ObjectData fields
419 private:
420 // Various per-instance flags
421 mutable int16_t o_attribute;
422 protected:
423 // 16 bits of memory that can be reused by subclasses
424 union {
425 uint16_t u16;
426 uint8_t u8[2];
427 } o_subclassData;
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
432 Class* m_cls;
433 // Storage for dynamic properties
434 ArrNR o_properties;
435 // Numeric identifier of this object (used for var_dump())
436 int o_id;
438 } __attribute__((aligned(16)));
440 template<> inline SmartPtr<ObjectData>::~SmartPtr() {}
442 typedef GlobalNameValueTableWrapper GlobalVariables;
444 inline
445 CountableHelper::CountableHelper(ObjectData* object) : m_object(object) {
446 object->incRefCount();
449 inline
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 };
464 public:
465 enum {
466 value = ALIGN_WORD(prevSize + (prevSize >> 1))
470 template<> struct ObjectSizeTable<0> {
471 enum { value = sizeof(ObjectData) };
474 #undef WORD_SIZE
475 #undef ALIGN_WORD
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
485 * enough.
487 template<int Index>
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>
493 >::type type;
495 const int NumObjectSizeClasses = DetermineLargestSizeClass<0>::type::value;
497 template<size_t Sz, int Index> struct LookupObjSizeIndex {
498 enum { index =
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 };
506 template<size_t Sz>
507 struct ObjectSizeClass {
508 enum {
509 index = LookupObjSizeIndex<Sz,0>::index,
510 value = ObjectSizeTable<index>::value
514 typedef ObjectAllocatorBase*(*ObjectAllocatorBaseGetter)(void);
516 class ObjectAllocatorCollector {
517 public:
518 static std::map<int, ObjectAllocatorBaseGetter> &getWrappers() {
519 static std::map<int, ObjectAllocatorBaseGetter> wrappers;
520 return 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 ///////////////////////////////////////////////////////////////////////////////
545 // Attribute helpers
546 class AttributeSetter {
547 public:
548 AttributeSetter(ObjectData::Attribute a, ObjectData* o) : m_a(a), m_o(o) {
549 o->setAttribute(a);
551 ~AttributeSetter() {
552 m_o->clearAttribute(m_a);
554 private:
555 ObjectData::Attribute m_a;
556 ObjectData* m_o;
559 class AttributeClearer {
560 public:
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);
567 private:
568 ObjectData::Attribute m_a;
569 ObjectData* m_o;
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 {
585 public:
586 explicit ExtObjectData(HPHP::Class* cls) : ObjectData(cls) {
587 assert(!m_cls->callsCustomInstanceInit());
591 template <int flags> class ExtObjectDataFlags : public ExtObjectData {
592 public:
593 explicit ExtObjectDataFlags(HPHP::Class* cb) : ExtObjectData(cb) {
594 ObjectData::setAttributes(flags);
598 } // HPHP
600 #endif