Fixes and cleanup for isset() on collections
[hiphop-php.git] / hphp / runtime / base / type-variant.h
blob9446de6537a94d48574b9249b7254e7fc6fb805f
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_VARIANT_H_
18 #define incl_HPHP_VARIANT_H_
20 #ifndef incl_HPHP_INSIDE_HPHP_COMPLEX_TYPES_H_
21 #error Directly including 'type_variant.h' is prohibited. \
22 Include 'complex_types.h' instead.
23 #endif
25 #include <type_traits>
27 #include "hphp/util/trace.h"
28 #include "hphp/runtime/base/types.h"
29 #include "hphp/runtime/base/typed-value.h"
30 #include "hphp/runtime/base/type-string.h"
31 #include "hphp/runtime/base/type-object.h"
32 #include "hphp/runtime/base/type-array.h"
33 #include "hphp/runtime/base/array-data.h"
34 #include "hphp/runtime/base/macros.h"
36 namespace HPHP {
37 ///////////////////////////////////////////////////////////////////////////////
39 class ArrayIter;
40 class MutableArrayIter;
43 * This class predates HHVM.
45 * In hphpc, when type inference failed to know type of a variable, we
46 * would use Variant to represent the php variable in generated C++.
48 * Now Variant is only used in C++ extensions, and the API is mostly
49 * legacy stuff. If you're writing a C++ extension, try to avoid
50 * Variant when you can (but you often can't, and we don't really have
51 * a replacement yet, sorry).
53 * In C++ extensions, this class can be used as a generic handle to
54 * one of our other data types (e.g. StringData, ArrayData), and it
55 * may also be a handle to a RefData.
57 * Beware:
59 * For historical reasons, this class does a lot of things you
60 * don't really expect in a well-behaved C++ class.
62 * For example, the copy constructor is not a copy constructor (it
63 * unboxes refs and converts KindOfUninit to KindOfNull). A
64 * similar story applies to the move constructor. (And this means
65 * we may actually rely on whether copy elision (NRVO etc) is
66 * happening in some places for correctness.)
68 * Use carefully.
72 class Variant : private TypedValue {
73 public:
74 friend class Array;
75 friend class VariantVectorBase;
76 friend class c_Vector;
77 friend class c_Map;
79 /**
80 * setUninitNull occurs frequently; use this version where possible.
82 inline void setUninitNull() {
83 m_type = KindOfUninit;
84 assert(!isInitialized());
87 Variant() {
88 setUninitNull();
91 enum class NullInit {};
92 explicit Variant(NullInit) { m_type = KindOfNull; }
93 enum class NoInit {};
94 explicit Variant(NoInit) {}
95 enum NoInc { noInc = 0 };
97 static ALWAYS_INLINE void destructData(RefData* num, DataType t) {
98 tvDecRefHelper(t, uint64_t(num));
101 // D462768 showed no gain from inlining, even just with INLINE_VARIANT_HELPER.
102 ~Variant();
104 void reset() {
105 // only for special memory sweeping!
106 m_type = KindOfNull;
110 * Constructors. We can't really use template<T> here, since that will make
111 * Variant being able to take many other external types, messing up those
112 * operator overloads.
114 /* implicit */ Variant(bool v) { m_type = KindOfBoolean; m_data.num = v; }
115 /* implicit */ Variant(int v) { m_type = KindOfInt64; m_data.num = v; }
116 // The following two overloads will accept int64_t whether it's
117 // implemented as long or long long.
118 /* implicit */ Variant(long v) { m_type = KindOfInt64; m_data.num = v; }
119 /* implicit */ Variant(long long v) { m_type = KindOfInt64; m_data.num = v; }
120 /* implicit */ Variant(uint64_t v) { m_type = KindOfInt64; m_data.num = v; }
122 /* implicit */ Variant(double v) { m_type = KindOfDouble; m_data.dbl = v; }
124 /* implicit */ Variant(litstr v);
125 /* implicit */ Variant(const std::string &v);
126 /* implicit */ Variant(const StaticString &v) {
127 m_type = KindOfStaticString;
128 StringData *s = v.get();
129 assert(s);
130 m_data.pstr = s;
133 /* implicit */ Variant(const String& v);
134 /* implicit */ Variant(CArrRef v);
135 /* implicit */ Variant(CObjRef v);
136 /* implicit */ Variant(CResRef v);
137 /* implicit */ Variant(StringData *v);
138 /* implicit */ Variant(ArrayData *v);
139 /* implicit */ Variant(ObjectData *v);
140 /* implicit */ Variant(ResourceData *v);
141 /* implicit */ Variant(RefData *r);
142 /* implicit */ Variant(RefData *r, NoInc);
144 // for static strings only
145 explicit Variant(const StringData *v);
147 // Move ctor for strings
148 /* implicit */ Variant(String&& v) {
149 StringData *s = v.get();
150 if (LIKELY(s != nullptr)) {
151 m_data.pstr = s;
152 m_type = s->isStatic()
153 ? KindOfStaticString
154 : KindOfString;
155 v.detach();
156 } else {
157 m_type = KindOfNull;
161 // Move ctor for arrays
162 /* implicit */ Variant(Array&& v) {
163 m_type = KindOfArray;
164 ArrayData *a = v.get();
165 if (LIKELY(a != nullptr)) {
166 m_data.parr = a;
167 v.detach();
168 } else {
169 m_type = KindOfNull;
173 // Move ctor for objects
174 /* implicit */ Variant(Object&& v) {
175 m_type = KindOfObject;
176 ObjectData *pobj = v.get();
177 if (pobj) {
178 m_data.pobj = pobj;
179 v.detach();
180 } else {
181 m_type = KindOfNull;
185 // Move ctor for resources
186 /* implicit */ Variant(Resource&& v) {
187 m_type = KindOfResource;
188 ResourceData *pres = v.get();
189 if (pres) {
190 m_data.pres = pres;
191 v.detach();
192 } else {
193 m_type = KindOfNull;
197 // These are prohibited, but declared just to prevent accidentally
198 // calling the bool constructor just because we had a pointer to
199 // const.
200 /* implicit */ Variant(const ArrayData *v) = delete;
201 /* implicit */ Variant(const ObjectData *v) = delete;
202 /* implicit */ Variant(const ResourceData *v) = delete;
203 /* implicit */ Variant(const RefData *v) = delete;
204 /* implicit */ Variant(const TypedValue *v) = delete;
205 /* implicit */ Variant(TypedValue *v) = delete;
206 /* implicit */ Variant(const /* implicit */ Variant *v) = delete;
207 /* implicit */ Variant(/* implicit */ Variant *v) = delete;
210 * Creation constructor from ArrayInit that avoids a null check.
212 enum class ArrayInitCtor { Tag };
213 explicit Variant(ArrayData* ad, ArrayInitCtor) {
214 m_type = KindOfArray;
215 m_data.parr = ad;
216 ad->incRefCount();
219 #ifdef INLINE_VARIANT_HELPER
220 ALWAYS_INLINE
221 /* implicit */ Variant(CVarRef v) { constructValHelper(v); }
222 ALWAYS_INLINE
223 /* implicit */ Variant(CVarStrongBind v) { constructRefHelper(variant(v)); }
224 ALWAYS_INLINE
225 /* implicit */ Variant(CVarWithRefBind v) {
226 constructWithRefHelper(variant(v));
228 #else
229 /* implicit */ Variant(CVarRef v);
230 /* implicit */ Variant(CVarStrongBind v);
231 /* implicit */ Variant(CVarWithRefBind v);
232 #endif
235 * Move ctor
237 * Note: not semantically a move constructor. Like our "copy
238 * constructor", unboxes refs and turns uninits to null.
240 Variant(Variant&& v) {
241 if (UNLIKELY(v.m_type == KindOfRef)) {
242 // We can't avoid the refcounting when it's a ref. Do basically
243 // what a copy would have done.
244 moveRefHelper(std::move(v));
245 return;
248 assert(this != &v);
249 m_type = v.m_type != KindOfUninit ? v.m_type : KindOfNull;
250 m_data = v.m_data;
251 v.reset();
255 * Move assign
257 * Note: not semantically a move assignment operator. Like our
258 * "copy asignment operator", unboxes refs and turns uninits to
259 * null.
261 Variant& operator=(Variant &&rhs) {
262 // a = std::move(a), ILLEGAL per C++11 17.6.4.9
263 assert(this != &rhs);
264 if (rhs.m_type == KindOfRef) return *this = *rhs.m_data.pref->var();
266 Variant& lhs = m_type == KindOfRef ? *m_data.pref->var() : *this;
268 Variant goner((NoInit()));
269 goner.m_data = lhs.m_data;
270 goner.m_type = lhs.m_type;
272 lhs.m_data = rhs.m_data;
273 lhs.m_type = rhs.m_type == KindOfUninit ? KindOfNull : rhs.m_type;
275 rhs.reset();
276 return *this;
279 private:
280 friend class VarNR;
282 public:
284 * Break bindings and set to null.
286 void unset() {
287 RefData* d = m_data.pref;
288 DataType t = m_type;
289 m_type = KindOfUninit;
290 if (IS_REFCOUNTED_TYPE(t)) destructData(d, t);
294 * set to null without breaking bindings (if any), faster than v_a = null;
296 void setNull();
299 * Clear the original data, and set it to be the same as in v, and if
300 * v is referenced, keep the reference.
301 * In order to correctly copy circular arrays, even if v is the only
302 * strong reference to arr, we still keep the reference.
304 Variant &setWithRef(CVarRef v);
307 * Fast accessors that can be used by generated code when type inference can
308 * prove that m_type will have a certain value at a given point in time
311 ///////////////////////////////////////////////////////////////////////////////
312 // int64
314 ALWAYS_INLINE int64_t asInt64Val() const {
315 assert(m_type == KindOfInt64);
316 return m_data.num;
319 ALWAYS_INLINE int64_t toInt64Val() const {
320 assert(is(KindOfInt64));
321 return
322 LIKELY(m_type == KindOfInt64) ?
323 m_data.num : m_data.pref->var()->m_data.num;
326 ///////////////////////////////////////////////////////////////////////////////
327 // double
329 ALWAYS_INLINE double asDoubleVal() const {
330 assert(m_type == KindOfDouble);
331 return m_data.dbl;
334 ALWAYS_INLINE double toDoubleVal() const {
335 assert(is(KindOfDouble));
336 return
337 LIKELY(m_type == KindOfDouble) ?
338 m_data.dbl : m_data.pref->var()->m_data.dbl;
341 ///////////////////////////////////////////////////////////////////////////////
342 // boolean
344 ALWAYS_INLINE bool asBooleanVal() const {
345 assert(m_type == KindOfBoolean);
346 return m_data.num;
349 ALWAYS_INLINE bool toBooleanVal() const {
350 assert(is(KindOfBoolean));
351 return
352 LIKELY(m_type == KindOfBoolean) ?
353 m_data.num : m_data.pref->var()->m_data.num;
356 ///////////////////////////////////////////////////////////////////////////////
357 // string
359 ALWAYS_INLINE const String& asCStrRef() const {
360 assert(IS_STRING_TYPE(m_type) && m_data.pstr);
361 return *reinterpret_cast<const String*>(&m_data.pstr);
364 ALWAYS_INLINE const String& toCStrRef() const {
365 assert(is(KindOfString) || is(KindOfStaticString));
366 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.pstr : m_data.pstr);
367 return *reinterpret_cast<const String*>(LIKELY(IS_STRING_TYPE(m_type)) ?
368 &m_data.pstr : &m_data.pref->tv()->m_data.pstr);
371 ALWAYS_INLINE String& asStrRef() {
372 assert(IS_STRING_TYPE(m_type) && m_data.pstr);
373 return *reinterpret_cast<String*>(&m_data.pstr);
376 ALWAYS_INLINE String& toStrRef() {
377 assert(is(KindOfString) || is(KindOfStaticString));
378 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.pstr : m_data.pstr);
379 return *reinterpret_cast<String*>(LIKELY(IS_STRING_TYPE(m_type)) ?
380 &m_data.pstr : &m_data.pref->tv()->m_data.pstr);
383 ///////////////////////////////////////////////////////////////////////////////
384 // array
386 ALWAYS_INLINE const Array& asCArrRef() const {
387 assert(m_type == KindOfArray && m_data.parr);
388 return *reinterpret_cast<const Array*>(&m_data.parr);
391 ALWAYS_INLINE const Array& toCArrRef() const {
392 assert(is(KindOfArray));
393 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.parr : m_data.parr);
394 return *reinterpret_cast<const Array*>(LIKELY(m_type == KindOfArray) ?
395 &m_data.parr : &m_data.pref->tv()->m_data.parr);
398 ALWAYS_INLINE Array& asArrRef() {
399 assert(m_type == KindOfArray && m_data.parr);
400 return *reinterpret_cast<Array*>(&m_data.parr);
403 ALWAYS_INLINE Array& toArrRef() {
404 assert(is(KindOfArray));
405 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.parr : m_data.parr);
406 return *reinterpret_cast<Array*>(LIKELY(m_type == KindOfArray) ?
407 &m_data.parr : &m_data.pref->tv()->m_data.parr);
410 ///////////////////////////////////////////////////////////////////////////////
411 // object
413 ALWAYS_INLINE const Object& asCObjRef() const {
414 assert(m_type == KindOfObject && m_data.pobj);
415 return *reinterpret_cast<const Object*>(&m_data.pobj);
418 ALWAYS_INLINE const Object& toCObjRef() const {
419 assert(is(KindOfObject));
420 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.pobj : m_data.pobj);
421 return *reinterpret_cast<const Object*>(LIKELY(m_type == KindOfObject) ?
422 &m_data.pobj : &m_data.pref->tv()->m_data.pobj);
425 ALWAYS_INLINE Object & asObjRef() {
426 assert(m_type == KindOfObject && m_data.pobj);
427 return *reinterpret_cast<Object*>(&m_data.pobj);
430 ALWAYS_INLINE const Resource& asCResRef() const {
431 assert(m_type == KindOfResource && m_data.pres);
432 return *reinterpret_cast<const Resource*>(&m_data.pres);
435 ALWAYS_INLINE Resource & asResRef() {
436 assert(m_type == KindOfResource && m_data.pres);
437 return *reinterpret_cast<Resource*>(&m_data.pres);
440 ALWAYS_INLINE Object& toObjRef() {
441 assert(is(KindOfObject));
442 assert(m_type == KindOfRef ? m_data.pref->var()->m_data.pobj : m_data.pobj);
443 return *reinterpret_cast<Object*>(LIKELY(m_type == KindOfObject) ?
444 &m_data.pobj : &m_data.pref->tv()->m_data.pobj);
447 ObjectData *objectForCall() const {
448 if (m_type == KindOfObject) return m_data.pobj;
449 if (m_type == KindOfRef) {
450 Variant *t = m_data.pref->var();
451 if (t->m_type == KindOfObject) return t->m_data.pobj;
453 throw_call_non_object();
454 return nullptr;
458 * Type testing functions
460 DataType getType() const {
461 return m_type == KindOfRef ? m_data.pref->var()->m_type : m_type;
463 DataType getRawType() const {
464 return m_type;
466 bool is(DataType type) const {
467 return getType() == type;
469 bool isInitialized() const {
470 return m_type != KindOfUninit;
472 bool isNull() const {
473 return IS_NULL_TYPE(getType());
475 bool isBoolean() const {
476 return getType() == KindOfBoolean;
478 bool isDouble() const {
479 return getType() == KindOfDouble;
481 bool isString() const {
482 return IS_STRING_TYPE(getType());
484 bool isInteger() const;
485 bool isNumeric(bool checkString = false) const;
486 DataType toNumeric(int64_t &ival, double &dval, bool checkString = false)
487 const;
488 bool isScalar() const;
489 bool isObject() const {
490 return getType() == KindOfObject;
492 bool isIntVal() const {
493 switch (m_type) {
494 case KindOfUninit:
495 case KindOfNull:
496 case KindOfBoolean:
497 case KindOfInt64:
498 case KindOfObject:
499 case KindOfResource:
500 return true;
501 case KindOfRef:
502 return m_data.pref->var()->isIntVal();
503 default:
504 break;
506 return false;
508 bool isArray() const {
509 return getType() == KindOfArray;
511 // Is "define('CONSTANT', <this value>)" legal?
512 bool isAllowedAsConstantValue() const {
513 return (m_type & kNotConstantValueTypeMask) == 0;
515 bool isResource() const;
516 bool instanceof(const String& s) const;
517 bool instanceof(Class* cls) const;
520 * Whether or not there are at least two variables that are strongly bound.
522 bool isReferenced() const {
523 return m_type == KindOfRef && m_data.pref->isReferenced();
527 * Get reference count of weak or strong binding. For debugging purpose.
529 int getRefCount() const;
531 bool getBoolean() const {
532 assert(getType() == KindOfBoolean);
533 return m_type == KindOfRef ? m_data.pref->var()->m_data.num : m_data.num;
535 int64_t getInt64() const {
536 assert(getType() == KindOfInt64);
537 return m_type == KindOfRef ? m_data.pref->var()->m_data.num : m_data.num;
539 double getDouble() const {
540 assert(getType() == KindOfDouble);
541 return m_type == KindOfRef ? m_data.pref->var()->m_data.dbl : m_data.dbl;
545 * Operators
547 Variant &assign(CVarRef v);
548 Variant &assignVal(CVarRef v) { return assign(v); }
549 Variant &assignRef(CVarRef v);
551 Variant &operator=(CVarRef v) {
552 return assign(v);
554 Variant &operator=(RefResult v) { return assignRef(variant(v)); }
555 Variant &operator=(CVarStrongBind v) { return assignRef(variant(v)); }
556 Variant &operator=(CVarWithRefBind v) { return setWithRef(variant(v)); }
558 Variant &operator=(const StaticString &v) {
559 set(v);
560 return *this;
562 template<typename T> Variant &operator=(const T &v) {
563 set(v);
564 return *this;
567 Variant operator + () const = delete;
568 Variant &operator += (CVarRef v) = delete;
569 Variant &operator += (int n) = delete;
570 Variant &operator += (int64_t n) = delete;
571 Variant &operator += (double n) = delete;
573 Variant operator - () const = delete;
574 Variant operator - (CVarRef v) const = delete;
575 Variant &operator -= (CVarRef v) = delete;
576 Variant &operator -= (int n) = delete;
577 Variant &operator -= (int64_t n) = delete;
578 Variant &operator -= (double n) = delete;
580 Variant operator * (CVarRef v) const = delete;
581 Variant &operator *= (CVarRef v) = delete;
582 Variant &operator *= (int n) = delete;
583 Variant &operator *= (int64_t n) = delete;
584 Variant &operator *= (double n) = delete;
586 Variant operator / (CVarRef v) const = delete;
587 Variant &operator /= (CVarRef v) = delete;
588 Variant &operator /= (int n) = delete;
589 Variant &operator /= (int64_t n) = delete;
590 Variant &operator /= (double n) = delete;
592 int64_t operator % (CVarRef v) const = delete;
593 Variant &operator %= (CVarRef v) = delete;
594 Variant &operator %= (int n) = delete;
595 Variant &operator %= (int64_t n) = delete;
596 Variant &operator %= (double n) = delete;
598 Variant operator | (CVarRef v) const = delete;
599 Variant &operator |= (CVarRef v) = delete;
600 Variant operator & (CVarRef v) const = delete;
601 Variant &operator &= (CVarRef v) = delete;
602 Variant operator ^ (CVarRef v) const = delete;
603 Variant &operator ^= (CVarRef v) = delete;
604 Variant &operator <<=(int64_t n) = delete;
605 Variant &operator >>=(int64_t n) = delete;
607 Variant &operator ++ () = delete;
608 Variant operator ++ (int) = delete;
609 Variant &operator -- () = delete;
610 Variant operator -- (int) = delete;
613 * Iterator functions. See array-iterator.h for end() and next().
615 ArrayIter begin(const String& context = null_string) const;
616 // used by generated code
617 MutableArrayIter begin(Variant *key, Variant &val,
618 const String& context = null_string);
621 * Variant used to implicitly convert to all these types. (It still
622 * implicitly converts *from* most of them.)
624 * We're leaving these functions deleted for now because we fear the
625 * possibility of changes to overload resolution by not declaring
626 * them. Eventually when fewer of these types have implicit
627 * conversions we'll remove them.
629 /* implicit */ operator bool () const = delete;
630 /* implicit */ operator char () const = delete;
631 /* implicit */ operator short () const = delete;
632 /* implicit */ operator int () const = delete;
633 /* implicit */ operator int64_t () const = delete;
634 /* implicit */ operator double () const = delete;
635 /* implicit */ operator String () const = delete;
636 /* implicit */ operator Array () const = delete;
637 /* implicit */ operator Object () const = delete;
638 template<typename T> /* implicit */ operator SmartObject<T>() const = delete;
641 * Explicit type conversions
643 bool toBoolean() const {
644 if (IS_NULL_TYPE(m_type)) return false;
645 if (m_type <= KindOfInt64) return m_data.num;
646 return toBooleanHelper();
648 char toByte () const { return (char)toInt64();}
649 short toInt16 (int base = 10) const { return (short)toInt64(base);}
650 int toInt32 (int base = 10) const { return (int)toInt64(base);}
651 int64_t toInt64 () const {
652 if (IS_NULL_TYPE(m_type)) return 0;
653 if (m_type <= KindOfInt64) return m_data.num;
654 return toInt64Helper(10);
656 int64_t toInt64 (int base) const {
657 if (IS_NULL_TYPE(m_type)) return 0;
658 if (m_type <= KindOfInt64) return m_data.num;
659 return toInt64Helper(base);
661 double toDouble () const {
662 if (m_type == KindOfDouble) return m_data.dbl;
663 return toDoubleHelper();
665 String toString () const {
666 if (IS_STRING_TYPE(m_type)) {
667 return m_data.pstr;
669 return toStringHelper();
671 Array toArray () const {
672 if (m_type == KindOfArray) return m_data.parr;
673 return toArrayHelper();
675 Object toObject () const {
676 if (m_type == KindOfObject) return m_data.pobj;
677 return toObjectHelper();
679 Resource toResource () const {
680 if (m_type == KindOfResource) return m_data.pres;
681 return toResourceHelper();
684 * Whether or not calling toKey() will throw a bad type exception
686 bool canBeValidKey() const {
687 switch (getType()) {
688 case KindOfArray: return false;
689 case KindOfObject: return false;
690 default: return true;
693 VarNR toKey () const;
694 /* Creating a temporary Array, String, or Object with no ref-counting and
695 * no type checking, use it only when we have checked the variant type and
696 * we are sure the internal data will have a reference until the temporary
697 * one gets out-of-scope.
699 StrNR toStrNR () const {
700 return StrNR(getStringData());
702 ArrNR toArrNR () const {
703 return ArrNR(getArrayData());
705 ObjNR toObjNR() const {
706 return ObjNR(getObjectData());
710 * Output functions
712 void serialize(VariableSerializer *serializer,
713 bool isArrayKey = false,
714 bool skipNestCheck = false) const;
715 void unserialize(VariableUnserializer *unserializer,
716 Uns::Mode mode = Uns::Mode::Value);
719 * Get the wrapped APCHandle, if any.
721 APCHandle *getAPCHandle() const;
724 * Print information about a variant to stdout. For debugging
725 * purposes.
727 void dump() const;
730 * Offset functions
732 Variant rvalAtHelper(int64_t offset, ACCESSPARAMS_DECL) const;
733 Variant rvalAt(int offset, ACCESSPARAMS_DECL) const {
734 return rvalAt((int64_t)offset, flags);
736 Variant rvalAt(int64_t offset, ACCESSPARAMS_DECL) const {
737 if (m_type == KindOfArray) {
738 return m_data.parr->get(offset, flags & AccessFlags::Error);
740 return rvalAtHelper(offset, flags);
742 Variant rvalAt(double offset, ACCESSPARAMS_DECL) const = delete;
743 Variant rvalAt(litstr offset, ACCESSPARAMS_DECL) const = delete;
744 Variant rvalAt(const String& offset, ACCESSPARAMS_DECL) const;
745 Variant rvalAt(CVarRef offset, ACCESSPARAMS_DECL) const;
747 // for when we know its an array or null
748 template <typename T>
749 CVarRef rvalAtRefHelper(T offset, ACCESSPARAMS_DECL) const;
750 CVarRef rvalAtRef(int offset, ACCESSPARAMS_DECL) const {
751 return rvalAtRefHelper((int64_t)offset, flags);
753 CVarRef rvalAtRef(double offset, ACCESSPARAMS_DECL) const = delete;
754 CVarRef rvalAtRef(int64_t offset, ACCESSPARAMS_DECL) const {
755 return rvalAtRefHelper(offset, flags);
757 CVarRef rvalAtRef(const String& offset, ACCESSPARAMS_DECL) const {
758 return rvalAtRefHelper<const String&>(offset, flags);
760 CVarRef rvalAtRef(CVarRef offset, ACCESSPARAMS_DECL) const {
761 return rvalAtRefHelper<CVarRef>(offset, flags);
763 const Variant operator[](int key) const { return rvalAt(key);}
764 const Variant operator[](int64_t key) const { return rvalAt(key);}
765 const Variant operator[](double key) const = delete;
766 const Variant operator[](const String& key) const { return rvalAt(key);}
767 const Variant operator[](CArrRef key) const { return rvalAt(key);}
768 const Variant operator[](CObjRef key) const { return rvalAt(key);}
769 const Variant operator[](CVarRef key) const { return rvalAt(key);}
770 const Variant operator[](const char*) const = delete;
772 template<typename T>
773 Variant &lval(const T &key) {
774 if (m_type == KindOfRef) {
775 return m_data.pref->var()->lval(key);
778 assert(m_type == KindOfArray);
779 Variant *ret = nullptr;
780 ArrayData *arr = m_data.parr;
781 ArrayData *escalated = arr->lval(key, ret, arr->hasMultipleRefs());
782 if (escalated != arr) set(escalated);
783 assert(ret);
784 return *ret;
787 Variant &lvalAt();
789 static Variant &lvalInvalid();
790 static Variant &lvalBlackHole();
792 Variant &lvalAt(int key, ACCESSPARAMS_DECL);
793 Variant &lvalAt(int64_t key, ACCESSPARAMS_DECL);
794 Variant &lvalAt(double key, ACCESSPARAMS_DECL) = delete;
795 Variant &lvalAt(litstr key, ACCESSPARAMS_DECL) = delete;
796 Variant &lvalAt(const String& key, ACCESSPARAMS_DECL);
797 Variant &lvalAt(CVarRef key, ACCESSPARAMS_DECL);
799 Variant &lvalRef(int key, Variant& tmp, ACCESSPARAMS_DECL);
800 Variant &lvalRef(int64_t key, Variant& tmp, ACCESSPARAMS_DECL);
801 Variant &lvalRef(double key, Variant& tmp, ACCESSPARAMS_DECL) = delete;
802 Variant &lvalRef(litstr key, Variant& tmp, ACCESSPARAMS_DECL) = delete;
803 Variant &lvalRef(const String& key, Variant& tmp, ACCESSPARAMS_DECL);
804 Variant &lvalRef(CVarRef key, Variant& tmp, ACCESSPARAMS_DECL);
806 template <typename T>
807 ALWAYS_INLINE static CVarRef SetImpl(Variant *self, T key, CVarRef v,
808 bool isKey);
810 template <typename T>
811 ALWAYS_INLINE static CVarRef SetRefImpl(Variant *self, T key, CVarRef v,
812 bool isKey);
814 CVarRef set(int key, CVarRef v) { return set((int64_t)key, v); }
815 CVarRef set(int64_t key, CVarRef v);
816 CVarRef set(double key, CVarRef v) = delete;
817 CVarRef set(litstr key, CVarRef v, bool isString = false) = delete;
818 CVarRef set(const String& key, CVarRef v, bool isString = false);
819 CVarRef set(CVarRef key, CVarRef v);
821 CVarRef append(CVarRef v);
823 CVarRef setRef(int key, CVarRef v) { return setRef((int64_t)key, v); }
824 CVarRef setRef(int64_t key, CVarRef v);
825 CVarRef setRef(double key, CVarRef v) = delete;
826 CVarRef setRef(litstr key, CVarRef v, bool isString = false) = delete;
827 CVarRef setRef(const String& key, CVarRef v, bool isString = false);
828 CVarRef setRef(CVarRef key, CVarRef v);
830 CVarRef set(int key, RefResult v) { return setRef(key, variant(v)); }
831 CVarRef set(int64_t key, RefResult v) { return setRef(key, variant(v)); }
832 CVarRef set(double key, RefResult v) = delete;
833 CVarRef set(litstr key, RefResult v, bool isString = false) = delete;
834 CVarRef set(const String& key, RefResult v, bool isString = false) {
835 return setRef(key, variant(v), isString);
837 CVarRef set(CVarRef key, RefResult v) { return setRef(key, variant(v)); }
839 CVarRef appendRef(CVarRef v);
840 CVarRef append(RefResult v) { return appendRef(variant(v)); }
842 void remove(int key) { removeImpl((int64_t)key);}
843 void remove(int64_t key) { removeImpl(key);}
844 void remove(double key) = delete;
845 void remove(litstr key, bool isString = false) = delete;
846 void remove(const String& key, bool isString = false) {
847 removeImpl(key, isString);
849 void remove(CVarRef key);
852 * More array operations.
854 Variant pop();
855 Variant dequeue();
856 void prepend(CVarRef v);
859 * For C++ library users to write "var.cast<c_MyClass>()->mf_func()".
861 template<typename T>
862 T *cast() const {
863 return toObject().getTyped<T>();
867 * Low level access that should be restricted to internal use.
869 int64_t *getInt64Data() const {
870 assert(getType() == KindOfInt64);
871 return m_type == KindOfRef ? &m_data.pref->var()->m_data.num :
872 const_cast<int64_t*>(&m_data.num);
874 double *getDoubleData() const {
875 assert(getType() == KindOfDouble);
876 return m_type == KindOfRef ? &m_data.pref->var()->m_data.dbl :
877 const_cast<double*>(&m_data.dbl);
879 StringData *getStringData() const {
880 assert(IS_STRING_TYPE(getType()));
881 return m_type == KindOfRef ? m_data.pref->var()->m_data.pstr : m_data.pstr;
883 StringData *getStringDataOrNull() const {
884 // This is a necessary evil because getStringData() returns
885 // an undefined result if this is a null variant
886 assert(isNull() || is(KindOfString) || is(KindOfStaticString));
887 return m_type == KindOfRef ?
888 (m_data.pref->var()->m_type <= KindOfNull ? nullptr :
889 m_data.pref->var()->m_data.pstr) :
890 (m_type <= KindOfNull ? nullptr : m_data.pstr);
892 ArrayData *getArrayData() const {
893 assert(is(KindOfArray));
894 return m_type == KindOfRef ? m_data.pref->var()->m_data.parr : m_data.parr;
896 ArrayData *getArrayDataOrNull() const {
897 // This is a necessary evil because getArrayData() returns
898 // an undefined result if this is a null variant
899 assert(isNull() || is(KindOfArray));
900 return m_type == KindOfRef ?
901 (m_data.pref->var()->m_type <= KindOfNull ? nullptr :
902 m_data.pref->var()->m_data.parr) :
903 (m_type <= KindOfNull ? nullptr : m_data.parr);
905 ObjectData *getObjectData() const {
906 assert(is(KindOfObject));
907 return m_type == KindOfRef ? m_data.pref->var()->m_data.pobj : m_data.pobj;
909 ObjectData *getObjectDataOrNull() const {
910 // This is a necessary evil because getObjectData() returns
911 // an undefined result if this is a null variant
912 assert(isNull() || is(KindOfObject));
913 return m_type == KindOfRef ?
914 (m_data.pref->var()->m_type <= KindOfNull ? nullptr :
915 m_data.pref->var()->m_data.pobj) :
916 (m_type <= KindOfNull ? nullptr : m_data.pobj);
918 ResourceData* getResourceData() const {
919 assert(is(KindOfResource));
920 return m_type == KindOfRef ? m_data.pref->var()->m_data.pres : m_data.pres;
922 Variant *getRefData() const {
923 assert(m_type == KindOfRef);
924 return m_data.pref->var();
927 ObjectData *getArrayAccess() const;
928 void callOffsetUnset(CVarRef key);
929 int64_t getNumData() const { return m_data.num; }
930 void setEvalScalar();
932 void setToDefaultObject();
935 * Access this Variant as a TypedValue. Does not unbox refs, etc.
937 const TypedValue* asTypedValue() const { return this; }
938 TypedValue* asTypedValue() { return this; }
941 * Access this Variant as a Cell. I.e. unboxes it if it was a
942 * KindOfRef.
944 const Cell* asCell() const { return tvToCell(asTypedValue()); }
945 Cell* asCell() { return tvToCell(asTypedValue()); }
948 * Access this Variant as a Ref. Promotes this Variant to a ref
949 * if it is not already a ref.
951 const Ref* asRef() const { PromoteToRef(*this); return this; }
952 Ref* asRef() { PromoteToRef(*this); return this; }
954 private:
955 bool isPrimitive() const { return !IS_REFCOUNTED_TYPE(m_type); }
956 bool isObjectConvertable() {
957 assert(m_type != KindOfRef);
958 return m_type <= KindOfNull ||
959 (m_type == KindOfBoolean && !m_data.num) ||
960 (IS_STRING_TYPE(m_type) && m_data.pstr->empty());
963 void removeImpl(double key) = delete;
964 void removeImpl(int64_t key);
965 void removeImpl(CVarRef key, bool isString = false);
966 void removeImpl(const String& key, bool isString = false);
968 CVarRef set(bool v);
969 CVarRef set(int v);
970 CVarRef set(int64_t v);
971 CVarRef set(double v);
972 CVarRef set(litstr v) = delete;
973 CVarRef set(const std::string & v) {
974 return set(String(v));
976 CVarRef set(StringData *v);
977 CVarRef set(ArrayData *v);
978 CVarRef set(ObjectData *v);
979 CVarRef set(ResourceData *v);
980 CVarRef set(const StringData *v) = delete;
981 CVarRef set(const ArrayData *v) = delete;
982 CVarRef set(const ObjectData *v) = delete;
983 CVarRef set(const ResourceData *v) = delete;
985 CVarRef set(const String& v) { return set(v.get()); }
986 CVarRef set(const StaticString & v);
987 CVarRef set(CArrRef v) { return set(v.get()); }
988 CVarRef set(CObjRef v) { return set(v.get()); }
989 CVarRef set(CResRef v) { return set(v.get()); }
991 template<typename T>
992 CVarRef set(const SmartObject<T> &v) {
993 return set(v.get());
996 template<typename T>
997 CVarRef set(const SmartResource<T> &v) {
998 return set(v.get());
1001 // only called from constructor
1002 void init(ObjectData *v);
1004 static ALWAYS_INLINE
1005 void AssignValHelper(Variant *self, const Variant *other) {
1006 assert(tvIsPlausible(*self) && tvIsPlausible(*other));
1008 if (UNLIKELY(self->m_type == KindOfRef)) self = self->m_data.pref->var();
1009 if (UNLIKELY(other->m_type == KindOfRef)) other = other->m_data.pref->var();
1010 // An early check for self == other here would be faster in that case, but
1011 // slows down the frequent case of self != other.
1012 // The following code is correct even if self == other.
1013 const DataType stype = self->m_type;
1014 const Value sdata = self->m_data;
1015 const DataType otype = other->m_type;
1016 if (UNLIKELY(otype == KindOfUninit)) {
1017 self->m_type = KindOfNull;
1018 } else {
1019 const Value odata = other->m_data;
1020 if (IS_REFCOUNTED_TYPE(otype)) {
1021 odata.pstr->incRefCount();
1023 self->m_data = odata;
1024 self->m_type = otype;
1026 tvRefcountedDecRefHelper(stype, sdata.num);
1029 #ifdef INLINE_VARIANT_HELPER
1030 public:
1031 #endif
1032 static ALWAYS_INLINE void PromoteToRef(CVarRef v) {
1033 assert(&v != &null_variant);
1034 if (v.m_type != KindOfRef) {
1035 auto const ref = RefData::Make(*v.asTypedValue());
1036 const_cast<Variant&>(v).m_type = KindOfRef;
1037 const_cast<Variant&>(v).m_data.pref = ref;
1041 ALWAYS_INLINE void assignValHelper(CVarRef v) {
1042 AssignValHelper(this, &v);
1045 ALWAYS_INLINE void assignRefHelper(CVarRef v) {
1046 assert(tvIsPlausible(*this) && tvIsPlausible(v));
1048 PromoteToRef(v);
1049 RefData* r = v.m_data.pref;
1050 r->incRefCount(); // in case destruct() triggers deletion of v
1051 RefData* d = m_data.pref;
1052 DataType t = m_type;
1053 m_type = KindOfRef;
1054 m_data.pref = r;
1055 if (IS_REFCOUNTED_TYPE(t)) destructData(d, t);
1058 public:
1059 ALWAYS_INLINE void constructRefHelper(CVarRef v) {
1060 assert(tvIsPlausible(v));
1061 PromoteToRef(v);
1062 v.m_data.pref->incRefCount();
1063 m_data.pref = v.m_data.pref;
1064 m_type = KindOfRef;
1067 ALWAYS_INLINE void constructValHelper(CVarRef v) {
1068 assert(tvIsPlausible(v));
1070 const Variant *other =
1071 UNLIKELY(v.m_type == KindOfRef) ? v.m_data.pref->var() : &v;
1072 assert(this != other);
1073 if (IS_REFCOUNTED_TYPE(other->m_type)) {
1074 other->m_data.pstr->incRefCount();
1076 m_type = other->m_type != KindOfUninit ? other->m_type : KindOfNull;
1077 m_data = other->m_data;
1080 void moveRefHelper(Variant&& v) {
1081 assert(tvIsPlausible(v));
1083 assert(v.m_type == KindOfRef);
1084 m_type = v.m_data.pref->tv()->m_type; // Can't be KindOfUninit.
1085 m_data = v.m_data.pref->tv()->m_data;
1086 if (IS_REFCOUNTED_TYPE(m_type)) {
1087 m_data.pstr->incRefCount();
1089 decRefRef(v.m_data.pref);
1090 v.reset();
1093 ALWAYS_INLINE
1094 void setWithRefHelper(CVarRef v, bool destroy) {
1095 assert(tvIsPlausible(*this) && tvIsPlausible(v));
1097 assert(this != &v);
1099 CVarRef rhs = v.m_type == KindOfRef && !v.m_data.pref->isReferenced()
1100 ? *v.m_data.pref->var() : v;
1101 if (IS_REFCOUNTED_TYPE(rhs.m_type)) {
1102 assert(rhs.m_data.pstr);
1103 rhs.m_data.pstr->incRefCount();
1106 RefData* d = m_data.pref;
1107 DataType t = m_type;
1108 m_type = rhs.m_type;
1109 if (m_type == KindOfUninit) m_type = KindOfNull; // drop uninit
1110 m_data.num = rhs.m_data.num;
1111 if (destroy) destructData(d, t);
1114 ALWAYS_INLINE
1115 void constructWithRefHelper(CVarRef v) {
1116 setWithRefHelper(v, false);
1119 #ifdef INLINE_VARIANT_HELPER
1120 private:
1121 #endif
1123 template<typename T>
1124 static ALWAYS_INLINE Variant &LvalAtImpl0(Variant *self, T key, Variant *tmp,
1125 bool blackHole, ACCESSPARAMS_DECL);
1127 template<typename T>
1128 ALWAYS_INLINE Variant &lvalAtImpl(T key, ACCESSPARAMS_DECL);
1130 private:
1132 * Checks whether the LHS array needs to be copied for a *one-level*
1133 * array set, e.g., "$a[] = $v" or "$a['x'] = $v".
1135 * Note:
1136 * (1) The semantics is equivalent to having a temporary variable
1137 * holding to RHS value, i.e., "$tmp = $v; $a[] = $tmp". This is NOT
1138 * exactly the same as PHP 5.3, where "$a = &$b; $a = array(); $a = $b;"
1139 * creates a recursive array, although the final assignment is not
1140 * strong-binding.
1141 * (2) It does NOT work with multi-level array set, i.e., "$a[][] = $v".
1142 * The compiler needs to generate a real temporary.
1144 bool needCopyForSet(CVarRef v) {
1145 assert(m_type == KindOfArray);
1146 if (m_data.parr->hasMultipleRefs()) return true;
1147 if (v.m_type == KindOfArray) return m_data.parr == v.m_data.parr;
1148 if (v.m_type == KindOfRef) {
1149 return m_data.parr == v.m_data.pref->var()->m_data.parr;
1151 return false;
1154 bool needCopyForSetRef(CVarRef v) {
1155 assert(m_type == KindOfArray);
1156 return m_data.parr->hasMultipleRefs();
1159 bool toBooleanHelper() const;
1160 int64_t toInt64Helper(int base = 10) const;
1161 double toDoubleHelper() const;
1162 String toStringHelper() const;
1163 Array toArrayHelper() const;
1164 Object toObjectHelper() const;
1165 Resource toResourceHelper() const;
1167 DataType convertToNumeric(int64_t *lval, double *dval) const;
1170 Variant operator+(const Variant & lhs, const Variant & rhs) = delete;
1172 class RefResultValue {
1173 public:
1174 CVarRef get() const { return m_var; }
1175 private:
1176 Variant m_var;
1179 class VRefParamValue {
1180 public:
1181 template <class T> /* implicit */ VRefParamValue(const T &v) : m_var(v) {}
1183 /* implicit */ VRefParamValue() : m_var(Variant::NullInit()) {}
1184 /* implicit */ VRefParamValue(RefResult v) : m_var(strongBind(v)) {}
1185 template <typename T>
1186 Variant &operator=(const T &v) const {
1187 m_var = v;
1188 return m_var;
1190 VRefParamValue &operator=(const VRefParamValue &v) const {
1191 m_var = v.m_var;
1192 return *const_cast<VRefParamValue*>(this);
1194 operator Variant&() const { return m_var; }
1195 Variant *operator&() const { return &m_var; } // FIXME
1196 Variant *operator->() const { return &m_var; }
1198 Variant& wrapped() const { return m_var; }
1200 explicit operator bool () const { return m_var.toBoolean();}
1201 operator int () const { return m_var.toInt32();}
1202 operator int64_t () const { return m_var.toInt64();}
1203 operator double () const { return m_var.toDouble();}
1204 operator String () const { return m_var.toString();}
1205 operator Array () const { return m_var.toArray();}
1206 operator Object () const { return m_var.toObject();}
1207 explicit operator Resource () const { return m_var.toResource();}
1209 bool is(DataType type) const { return m_var.is(type); }
1210 bool isString() const { return m_var.isString(); }
1211 bool isObject() const { return m_var.isObject(); }
1212 bool isReferenced() const { return m_var.isReferenced(); }
1213 bool isNull() const { return m_var.isNull(); }
1215 bool toBoolean() const { return m_var.toBoolean(); }
1216 int64_t toInt64() const { return m_var.toInt64(); }
1217 double toDouble() const { return m_var.toDouble(); }
1218 String toString() const { return m_var.toString(); }
1219 StringData *getStringData() const { return m_var.getStringData(); }
1220 Array toArray() const { return m_var.toArray(); }
1221 Object toObject() const { return m_var.toObject(); }
1222 Resource toResource() const { return m_var.toResource(); }
1223 ObjectData *getObjectData() const { return m_var.getObjectData(); }
1225 CVarRef append(CVarRef v) const { return m_var.append(v); }
1227 Variant pop() const { return m_var.pop(); }
1228 Variant dequeue() const { return m_var.dequeue(); }
1229 void prepend(CVarRef v) const { m_var.prepend(v); }
1231 bool isArray() const { return m_var.isArray(); }
1232 ArrNR toArrNR() const { return m_var.toArrNR(); }
1234 private:
1235 mutable Variant m_var;
1238 inline VRefParamValue vref(CVarRef v) {
1239 return VRefParamValue(strongBind(v));
1242 inline VRefParam directRef(CVarRef v) {
1243 return *(VRefParamValue*)&v;
1247 these two classes are just to help choose the correct
1248 overload.
1250 class VariantStrongBind { private: Variant m_var; };
1251 class VariantWithRefBind { private: Variant m_var; };
1253 ///////////////////////////////////////////////////////////////////////////////
1254 // VarNR
1256 class VarNR : private TypedValueAux {
1257 public:
1258 // Use to hold variant that do not need ref-counting
1259 explicit VarNR(bool v) { init(KindOfBoolean); m_data.num = (v?1:0);}
1260 explicit VarNR(int v) { init(KindOfInt64 ); m_data.num = v;}
1261 // The following two overloads will accept int64_t whether it's
1262 // implemented as long or long long.
1263 explicit VarNR(long v) { init(KindOfInt64 ); m_data.num = v;}
1264 explicit VarNR(long long v) { init(KindOfInt64 ); m_data.num = v;}
1265 explicit VarNR(uint64_t v) { init(KindOfInt64 ); m_data.num = v;}
1266 explicit VarNR(double v) { init(KindOfDouble ); m_data.dbl = v;}
1268 explicit VarNR(const StaticString &v) {
1269 init(KindOfStaticString);
1270 StringData *s = v.get();
1271 assert(s);
1272 m_data.pstr = s;
1275 explicit VarNR(const String& v);
1276 explicit VarNR(CArrRef v);
1277 explicit VarNR(CObjRef v);
1278 explicit VarNR(StringData *v);
1279 explicit VarNR(const StringData *v) {
1280 assert(v && v->isStatic());
1281 init(KindOfStaticString);
1282 m_data.pstr = const_cast<StringData*>(v);
1284 explicit VarNR(ArrayData *v);
1285 explicit VarNR(ObjectData *v);
1287 VarNR(const VarNR &v) : TypedValueAux(v) {}
1289 explicit VarNR() { asVariant()->setUninitNull(); }
1291 ~VarNR() { if (debug) checkRefCount(); }
1293 operator CVarRef() const { return *asVariant(); }
1295 bool isNull() const {
1296 return asVariant()->isNull();
1298 private:
1299 /* implicit */ VarNR(litstr v) = delete;
1300 /* implicit */ VarNR(const std::string & v) = delete;
1302 void init(DataType dt) {
1303 m_type = dt;
1304 if (debug) varNrFlag() = NR_FLAG;
1306 const Variant *asVariant() const {
1307 return (const Variant*)this;
1309 Variant *asVariant() {
1310 return (Variant*)this;
1312 void checkRefCount() {
1313 assert(m_type != KindOfRef);
1314 if (!IS_REFCOUNTED_TYPE(m_type)) return;
1315 assert(varNrFlag() == NR_FLAG);
1316 switch (m_type) {
1317 case KindOfArray:
1318 assert_refcount_realistic(m_data.parr->getCount());
1319 return;
1320 case KindOfString:
1321 assert_refcount_realistic(m_data.pstr->getCount());
1322 return;
1323 case KindOfObject:
1324 assert_refcount_realistic(m_data.pobj->getCount());
1325 return;
1326 case KindOfResource:
1327 assert_refcount_realistic(m_data.pres->getCount());
1328 return;
1329 default:
1330 break;
1332 assert(false);
1336 ///////////////////////////////////////////////////////////////////////////////
1337 // breaking circular dependencies
1339 inline const Variant Array::operator[](int key) const {
1340 return rvalAt(key);
1343 inline const Variant Array::operator[](int64_t key) const {
1344 return rvalAt(key);
1347 inline const Variant Array::operator[](const String& key) const {
1348 return rvalAt(key);
1351 inline const Variant Array::operator[](CVarRef key) const {
1352 return rvalAt(key);
1355 inline void Array::setWithRef(CVarRef k, CVarRef v,
1356 bool isKey /* = false */) {
1357 lvalAt(k, isKey ? AccessFlags::Key : AccessFlags::None).setWithRef(v);
1360 inline Variant uninit_null() {
1361 return Variant();
1364 inline Variant init_null() {
1365 return Variant(Variant::NullInit());
1368 // TODO(#2298051) litstr must die
1369 inline Variant &concat_assign(Variant &v1, litstr s2) = delete;
1371 inline Variant &concat_assign(Variant &v1, const String& s2) {
1372 if (v1.getType() == KindOfString) {
1373 auto& str = v1.asStrRef();
1374 if (str->getCount() == 1) {
1375 str += StringSlice{s2.data(), static_cast<uint32_t>(s2.size())};
1376 return v1;
1380 auto s1 = v1.toString();
1381 s1 += s2;
1382 v1 = s1;
1383 return v1;
1386 //////////////////////////////////////////////////////////////////////
1388 // Defined here for include order reasons.
1389 inline RefData::~RefData() {
1390 assert(m_magic == Magic::kMagic);
1391 tvAsVariant(&m_tv).~Variant();
1394 //////////////////////////////////////////////////////////////////////
1398 #endif // incl_HPHP_VARIANT_H_