Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / object-data.h
blob42cf0d99c5052b94d9890a15d845e2cdceb517e1
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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/memory-manager.h"
22 #include "hphp/runtime/base/classname-is.h"
23 #include "hphp/runtime/base/req-ptr.h"
24 #include "hphp/runtime/base/weakref-data.h"
25 #include "hphp/runtime/base/member-val.h"
27 #include "hphp/runtime/vm/class.h"
28 #include "hphp/runtime/vm/hhbc.h"
30 #include "hphp/util/low-ptr.h"
32 #include <vector>
34 namespace HPHP {
35 ///////////////////////////////////////////////////////////////////////////////
37 struct TypedValue;
39 #define INVOKE_FEW_ARGS_COUNT 6
40 #define INVOKE_FEW_ARGS_DECL3 \
41 const Variant& a0 = uninit_variant, \
42 const Variant& a1 = uninit_variant, \
43 const Variant& a2 = uninit_variant
44 #define INVOKE_FEW_ARGS_DECL6 \
45 INVOKE_FEW_ARGS_DECL3, \
46 const Variant& a3 = uninit_variant, \
47 const Variant& a4 = uninit_variant, \
48 const Variant& a5 = uninit_variant
49 #define INVOKE_FEW_ARGS_DECL10 \
50 INVOKE_FEW_ARGS_DECL6, \
51 const Variant& a6 = uninit_variant, \
52 const Variant& a7 = uninit_variant, \
53 const Variant& a8 = uninit_variant, \
54 const Variant& a9 = uninit_variant
55 #define INVOKE_FEW_ARGS_HELPER(kind,num) kind##num
56 #define INVOKE_FEW_ARGS(kind,num) \
57 INVOKE_FEW_ARGS_HELPER(INVOKE_FEW_ARGS_##kind,num)
58 #define INVOKE_FEW_ARGS_DECL_ARGS INVOKE_FEW_ARGS(DECL,INVOKE_FEW_ARGS_COUNT)
60 void deepInitHelper(TypedValue* propVec, const TypedValueAux* propData,
61 size_t nProps);
63 namespace Native {
64 ObjectData* nativeDataInstanceCopyCtor(ObjectData *src, Class* cls,
65 size_t nProps);
68 struct InvokeResult {
69 TypedValue val;
70 InvokeResult() {}
71 InvokeResult(bool ok, TypedValue v) : val(v) {
72 val.m_aux.u_ok = ok;
74 InvokeResult(bool ok, Variant&& v);
75 bool ok() const { return val.m_aux.u_ok; }
76 explicit operator bool() const { return ok(); }
79 #ifdef _MSC_VER
80 #pragma pack(push, 1)
81 #endif
82 struct ObjectData : Countable, type_scan::MarkCollectable<ObjectData> {
83 enum Attribute : uint8_t {
84 NoDestructor = 0x01, // __destruct()
85 IsWeakRefed = 0x02, // Is pointed to by at least one WeakRef
86 HasDynPropArr = 0x04, // has a dynamic properties array
87 IsBeingConstructed = 0x08, // Constructor for most derived class has not
88 // finished. Only set during construction when
89 // the class has immutable properties (to
90 // temporarily allow writing to them).
93 private:
94 static __thread uint32_t os_max_id;
96 public:
97 static void resetMaxId();
99 explicit ObjectData(Class*, uint8_t flags = 0,
100 HeaderKind = HeaderKind::Object);
101 ~ObjectData();
103 // Disallow copy construction and assignemt
104 ObjectData(const ObjectData&) = delete;
105 ObjectData& operator=(const ObjectData&) = delete;
107 protected:
108 enum class NoInit {};
109 enum class InitRaw {};
111 // for JIT-generated instantiation with inlined property init
112 explicit ObjectData(Class* cls, InitRaw, uint8_t flags = 0,
113 HeaderKind = HeaderKind::Object) noexcept;
115 // for C++ subclasses with no declared properties
116 explicit ObjectData(Class* cls, NoInit, uint8_t flags = 0,
117 HeaderKind = HeaderKind::Object) noexcept;
119 public:
120 ALWAYS_INLINE void decRefAndRelease() {
121 assert(kindIsValid());
122 if (decReleaseCheck()) release();
124 bool kindIsValid() const { return isObjectKind(headerKind()); }
126 void scan(type_scan::Scanner&) const;
128 size_t heapSize() const;
130 // WeakRef control methods.
131 inline void invalidateWeakRef() {
132 if (UNLIKELY(getAttribute(IsWeakRefed))) {
133 WeakRefData::invalidateWeakRef((uintptr_t)this);
137 inline void setWeakRefed(bool flag) {
138 setAttribute(IsWeakRefed);
141 public:
144 * Call newInstance() to instantiate a PHP object. The initial ref-count will
145 * be greater than zero. Since this gives you a raw pointer, it is your
146 * responsibility to manage the ref-count yourself. Whenever possible, prefer
147 * using the Object class instead, which takes care of this for you.
149 static ObjectData* newInstance(Class*);
152 * Instantiate a new object without initializing its declared properties. The
153 * given Class must be a concrete, regular Class, without an instanceCtor or
154 * customInit.
156 static ObjectData* newInstanceNoPropInit(Class*);
159 * Given a Class that is assumed to be a concrete, regular (not a trait or
160 * interface), pure PHP class, and an allocation size, return a new,
161 * uninitialized object of that class. These are meant to be called from the
162 * JIT, where the cls, size, and attributes are constants at JIT time.
164 * newInstanceRaw<> should be called only when and cls->getODAttrs() ==
165 * DefaultAttrs; otherwise, use newInstanceRawAttrs. The big=true versions
166 * should be called when size > kMaxSmallSize.
168 * The initial ref-count will be set to one.
170 static const uint8_t DefaultAttrs = NoDestructor;
171 template<bool Big>
172 static ObjectData* newInstanceRaw(Class*, size_t);
173 template<bool Big>
174 static ObjectData* newInstanceRawAttrs(Class*, size_t, uint8_t attrs);
176 void release() noexcept;
177 void releaseNoObjDestructCheck() noexcept;
179 Class* getVMClass() const;
180 void setVMClass(Class* cls);
181 StrNR getClassName() const;
182 uint32_t getId() const;
184 // instanceof() can be used for both classes and interfaces.
185 bool instanceof(const String&) const;
186 bool instanceof(const Class*) const;
188 template <typename T>
189 typename std::enable_if<
190 std::is_same<ObjectData,T>::value,
191 bool
192 >::type instanceof() { return true; }
194 template <typename T>
195 typename std::enable_if<
196 !std::is_same<ObjectData,T>::value,
197 bool
198 >::type instanceof() { return instanceof(T::classof()); }
200 // Whether the object implements Iterator.
201 bool isIterator() const;
203 // Has a custom instanceCtor and instanceDtor. If you subclass ObjectData
204 // in C++, you need this.
205 bool isCppBuiltin() const;
207 // Is this an object with (some) immutable properties for which construction
208 // has not finished yet?
209 bool isBeingConstructed() const;
211 // Whether the object is a collection, [and [not] mutable].
212 bool isCollection() const;
213 bool isMutableCollection() const;
214 bool isImmutableCollection() const;
215 CollectionType collectionType() const; // asserts(isCollection())
216 HeaderKind headerKind() const;
218 // True if this is a c_WaitHandle or derived
219 bool isWaitHandle() const;
221 bool getAttribute(Attribute) const;
222 void setAttribute(Attribute);
223 bool hasInstanceDtor() const;
224 bool hasNativeData() const;
225 bool noDestruct() const;
226 void setNoDestruct();
227 void clearNoDestruct();
229 Object iterableObject(bool& isIterable, bool mayImplementIterator = true);
232 * Type conversions. Some subclasses of ObjectData have custom conversions.
233 * (e.g. SimpleXMLElement -> bool)
235 bool toBoolean() const;
236 int64_t toInt64() const;
237 double toDouble() const;
238 Array toArray(bool pubOnly = false) const;
241 * Comparisons.
243 * Note that for objects !(X < Y) does *not* imply (X >= Y).
245 bool equal(const ObjectData&) const;
246 bool less(const ObjectData&) const;
247 bool more(const ObjectData&) const;
248 int64_t compare(const ObjectData&) const;
251 * Call this object's destructor, if it has one. No restrictions are placed
252 * on the object's refcount, since this is used on objects still alive at
253 * request shutdown.
255 void destructForExit();
257 private:
258 void instanceInit(Class*);
259 bool destructImpl();
261 public:
263 enum IterMode { EraseRefs, CreateRefs, PreserveRefs };
265 * Create an array of object properties suitable for iteration.
267 * EraseRefs - array should contain unboxed properties
268 * CreateRefs - array should contain boxed properties
269 * PreserveRefs - reffiness of properties should be preserved in returned
270 * array
272 Array o_toIterArray(const String& context, IterMode mode);
274 Variant o_get(const String& s, bool error = true,
275 const String& context = null_string);
277 void o_set(const String& s, const Variant& v,
278 const String& context = null_string);
280 void o_setArray(const Array& properties);
281 void o_getArray(Array& props, bool pubOnly = false) const;
283 static Object FromArray(ArrayData* properties);
285 // TODO Task #2584896: o_invoke and o_invoke_few_args are deprecated. These
286 // APIs don't properly take class context into account when looking up the
287 // method, and they duplicate some of the functionality from invokeFunc(),
288 // invokeFuncFew(), and vm_decode_function(). We should remove these APIs and
289 // migrate all callers to use invokeFunc(), invokeFuncFew(), and
290 // vm_decode_function() instead.
291 Variant o_invoke(const String& s, const Variant& params, bool fatal = true);
292 Variant o_invoke_few_args(const String& s, int count,
293 INVOKE_FEW_ARGS_DECL_ARGS);
295 ObjectData* clone();
297 Variant offsetGet(Variant key);
298 String invokeToString();
299 bool hasToString();
301 Variant invokeSleep();
302 Variant invokeToDebugDisplay();
303 Variant invokeWakeup();
304 Variant invokeDebugInfo();
307 * Returns whether this object has any dynamic properties.
309 bool hasDynProps() const;
312 * Returns the dynamic properties array for this object.
314 * Note: you're generally not going to want to copy-construct the
315 * return value of this function. If you want to make changes to
316 * the property array, we need to keep its ref count at 1.
318 * Pre: getAttribute(HasDynPropArr)
320 Array& dynPropArray() const;
323 * Create the dynamic property array for this ObjectData if it
324 * doesn't already exist yet.
326 * Post: getAttribute(HasDynPropArr)
328 Array& reserveProperties(int nProp = 2);
331 * Use the given array for this object's dynamic properties. HasDynPropArry
332 * must not already be set. Returns a reference to the Array in its final
333 * location.
335 Array& setDynPropArray(const Array&);
337 // accessors for the declared properties area
338 TypedValue* propVecForWrite();
339 TypedValue* propVecForConstruct();
340 const TypedValue* propVec() const;
342 // accessors for declared properties at statically known offsets
343 // in the lval case, the property must be statically known to be mutable
344 member_lval propLvalAtOffset(Slot);
345 member_rval propRvalAtOffset(Slot) const;
347 public:
348 const Func* methodNamed(const StringData*) const;
349 static size_t sizeForNProps(Slot);
351 //============================================================================
352 // Properties.
353 private:
354 Slot declPropInd(const TypedValue* prop) const;
355 [[noreturn]] NEVER_INLINE
356 void throwMutateImmutable(const TypedValue* prop) const;
357 [[noreturn]] NEVER_INLINE
358 void throwBindImmutable(const TypedValue* prop) const;
360 public:
361 // never box the lval returned from getPropLval; use propB or vGetProp instead
362 member_lval getPropLval(const Class*, const StringData*);
363 member_rval getProp(const Class*, const StringData*) const;
364 member_lval vGetProp(const Class*, const StringData*);
365 // don't use vGetPropIgnoreAccessibility in new code
366 member_lval vGetPropIgnoreAccessibility(const StringData*);
368 private:
369 template <class T>
370 struct PropLookup {
371 T prop;
372 bool accessible;
373 bool immutable;
376 template <bool forWrite>
377 ALWAYS_INLINE
378 PropLookup<TypedValue*> getPropImpl(const Class*, const StringData*);
380 enum class PropMode : int {
381 ReadNoWarn,
382 ReadWarn,
383 DimForWrite,
384 Bind,
387 template<PropMode mode>
388 TypedValue* propImpl(TypedValue* tvRef, const Class* ctx,
389 const StringData* key);
391 bool propEmptyImpl(const Class* ctx, const StringData* key);
393 template<typename K>
394 TypedValue* makeDynProp(K key, AccessFlags);
396 bool invokeSet(const StringData* key, Cell val);
397 InvokeResult invokeGet(const StringData* key);
398 InvokeResult invokeIsset(const StringData* key);
399 bool invokeUnset(const StringData* key);
400 InvokeResult invokeNativeGetProp(const StringData* key);
401 bool invokeNativeSetProp(const StringData* key, Cell val);
402 InvokeResult invokeNativeIssetProp(const StringData* key);
403 bool invokeNativeUnsetProp(const StringData* key);
405 void getProp(const Class* klass, bool pubOnly, const PreClass::Prop* prop,
406 Array& props, std::vector<bool>& inserted) const;
407 void getProps(const Class* klass, bool pubOnly, const PreClass* pc,
408 Array& props, std::vector<bool>& inserted) const;
409 void getTraitProps(const Class* klass, bool pubOnly, const Class* trait,
410 Array& props, std::vector<bool>& inserted) const;
412 public:
413 TypedValue* prop(TypedValue* tvRef, const Class* ctx, const StringData* key);
414 TypedValue* propW(TypedValue* tvRef, const Class* ctx, const StringData* key);
415 TypedValue* propD(TypedValue* tvRef, const Class* ctx, const StringData* key);
416 TypedValue* propB(TypedValue* tvRef, const Class* ctx, const StringData* key);
418 bool propIsset(const Class* ctx, const StringData* key);
419 bool propEmpty(const Class* ctx, const StringData* key);
421 void setProp(Class* ctx, const StringData* key, Cell val);
422 TypedValue* setOpProp(TypedValue& tvRef, Class* ctx, SetOpOp op,
423 const StringData* key, Cell* val);
425 Cell incDecProp(Class* ctx, IncDecOp op, const StringData* key);
427 void unsetProp(Class* ctx, const StringData* key);
429 static void raiseObjToIntNotice(const char*);
430 static void raiseObjToDoubleNotice(const char*);
431 static void raiseAbstractClassError(Class*);
432 void raiseUndefProp(const StringData*);
434 static constexpr ptrdiff_t getVMClassOffset() {
435 return offsetof(ObjectData, m_cls);
437 const char* classname_cstr() const;
439 private:
440 friend struct MemoryProfile;
441 friend ObjectData* Native::nativeDataInstanceCopyCtor(
442 ObjectData* src, Class* cls, size_t nProps);
444 bool toBooleanImpl() const noexcept;
445 int64_t toInt64Impl() const noexcept;
446 double toDoubleImpl() const noexcept;
448 // offset: 0 8 12 16 20 32
449 // 64bit: header cls id [subclass] [props...]
450 // lowptr: header cls id [subclass][props...]
452 private:
453 LowPtr<Class> m_cls;
454 uint32_t o_id; // id of this object (used for var_dump(), and WeakRefs)
456 #ifdef _MSC_VER
457 #pragma pack(pop)
458 #endif
460 struct CountableHelper {
461 explicit CountableHelper(ObjectData* object) : m_object(object) {
462 object->incRefCount();
464 ~CountableHelper() {
465 m_object->decRefCount();
468 CountableHelper(const CountableHelper&) = delete;
469 CountableHelper& operator=(const CountableHelper&) = delete;
471 private:
472 ObjectData *m_object;
475 ///////////////////////////////////////////////////////////////////////////////
477 ALWAYS_INLINE void decRefObj(ObjectData* obj) {
478 obj->decRefAndRelease();
482 * Write a value to `to', with Dup semantics.
484 * @see: tv-mutate.h
486 ALWAYS_INLINE void tvWriteObject(ObjectData* pobj, TypedValue* to) {
487 to->m_type = KindOfObject;
488 to->m_data.pobj = pobj;
489 to->m_data.pobj->incRefCount();
492 inline ObjectData* instanceFromTv(TypedValue* tv) {
493 assert(tv->m_type == KindOfObject);
494 assert(dynamic_cast<ObjectData*>(tv->m_data.pobj));
495 return tv->m_data.pobj;
498 ///////////////////////////////////////////////////////////////////////////////
500 #define DECLARE_CLASS_NO_SWEEP(originalName) \
501 public: \
502 CLASSNAME_IS(#originalName) \
503 friend ObjectData* new_##originalName##_Instance(Class*); \
504 friend void delete_##originalName(ObjectData*, const Class*); \
505 static HPHP::LowPtr<Class> s_classOf; \
506 static inline HPHP::LowPtr<Class>& classof() { \
507 return s_classOf; \
510 #define IMPLEMENT_CLASS_NO_SWEEP(cls) \
511 HPHP::LowPtr<Class> c_##cls::s_classOf;
513 namespace req {
515 template<class T, class... Args>
516 typename std::enable_if<
517 std::is_convertible<T*, ObjectData*>::value,
518 req::ptr<T>
519 >::type make(Args&&... args) {
520 auto const mem = tl_heap->objMalloc(sizeof(T));
521 (void)type_scan::getIndexForMalloc<T>(); // ensure T* ptrs are interesting
522 try {
523 auto t = new (mem) T(std::forward<Args>(args)...);
524 assert(t->hasExactlyOneRef());
525 return req::ptr<T>::attach(t);
526 } catch (...) {
527 tl_heap->objFree(mem, sizeof(T));
528 throw;
531 } // namespace req
533 ///////////////////////////////////////////////////////////////////////////////
536 ///////////////////////////////////////////////////////////////////////////////
538 #define incl_HPHP_OBJECT_DATA_INL_H_
539 #include "hphp/runtime/base/object-data-inl.h"
540 #undef incl_HPHP_OBJECT_DATA_INL_H_
542 ///////////////////////////////////////////////////////////////////////////////
544 #endif