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_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.
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"
37 ///////////////////////////////////////////////////////////////////////////////
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.
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.)
72 class Variant
: private TypedValue
{
75 friend class VariantVectorBase
;
76 friend class c_Vector
;
80 * setUninitNull occurs frequently; use this version where possible.
82 inline void setUninitNull() {
83 m_type
= KindOfUninit
;
84 assert(!isInitialized());
91 enum class NullInit
{};
92 explicit Variant(NullInit
) { m_type
= KindOfNull
; }
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.
105 // only for special memory sweeping!
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();
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)) {
152 m_type
= s
->isStatic()
161 // Move ctor for arrays
162 /* implicit */ Variant(Array
&& v
) {
163 m_type
= KindOfArray
;
164 ArrayData
*a
= v
.get();
165 if (LIKELY(a
!= nullptr)) {
173 // Move ctor for objects
174 /* implicit */ Variant(Object
&& v
) {
175 m_type
= KindOfObject
;
176 ObjectData
*pobj
= v
.get();
185 // Move ctor for resources
186 /* implicit */ Variant(Resource
&& v
) {
187 m_type
= KindOfResource
;
188 ResourceData
*pres
= v
.get();
197 // These are prohibited, but declared just to prevent accidentally
198 // calling the bool constructor just because we had a pointer to
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
;
219 #ifdef INLINE_VARIANT_HELPER
221 /* implicit */ Variant(CVarRef v
) { constructValHelper(v
); }
223 /* implicit */ Variant(CVarStrongBind v
) { constructRefHelper(variant(v
)); }
225 /* implicit */ Variant(CVarWithRefBind v
) {
226 constructWithRefHelper(variant(v
));
229 /* implicit */ Variant(CVarRef v
);
230 /* implicit */ Variant(CVarStrongBind v
);
231 /* implicit */ Variant(CVarWithRefBind v
);
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
));
249 m_type
= v
.m_type
!= KindOfUninit
? v
.m_type
: KindOfNull
;
257 * Note: not semantically a move assignment operator. Like our
258 * "copy asignment operator", unboxes refs and turns uninits to
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
;
284 * Break bindings and set to null.
287 RefData
* d
= m_data
.pref
;
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;
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 ///////////////////////////////////////////////////////////////////////////////
314 ALWAYS_INLINE
int64_t asInt64Val() const {
315 assert(m_type
== KindOfInt64
);
319 ALWAYS_INLINE
int64_t toInt64Val() const {
320 assert(is(KindOfInt64
));
322 LIKELY(m_type
== KindOfInt64
) ?
323 m_data
.num
: m_data
.pref
->var()->m_data
.num
;
326 ///////////////////////////////////////////////////////////////////////////////
329 ALWAYS_INLINE
double asDoubleVal() const {
330 assert(m_type
== KindOfDouble
);
334 ALWAYS_INLINE
double toDoubleVal() const {
335 assert(is(KindOfDouble
));
337 LIKELY(m_type
== KindOfDouble
) ?
338 m_data
.dbl
: m_data
.pref
->var()->m_data
.dbl
;
341 ///////////////////////////////////////////////////////////////////////////////
344 ALWAYS_INLINE
bool asBooleanVal() const {
345 assert(m_type
== KindOfBoolean
);
349 ALWAYS_INLINE
bool toBooleanVal() const {
350 assert(is(KindOfBoolean
));
352 LIKELY(m_type
== KindOfBoolean
) ?
353 m_data
.num
: m_data
.pref
->var()->m_data
.num
;
356 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////
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 ///////////////////////////////////////////////////////////////////////////////
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();
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 {
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)
488 bool isScalar() const;
489 bool isObject() const {
490 return getType() == KindOfObject
;
492 bool isIntVal() const {
502 return m_data
.pref
->var()->isIntVal();
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
;
547 Variant
&assign(CVarRef v
);
548 Variant
&assignVal(CVarRef v
) { return assign(v
); }
549 Variant
&assignRef(CVarRef v
);
551 Variant
&operator=(CVarRef 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
) {
562 template<typename T
> Variant
&operator=(const T
&v
) {
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
)) {
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 {
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());
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
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;
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
);
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
,
810 template <typename T
>
811 ALWAYS_INLINE
static CVarRef
SetRefImpl(Variant
*self
, T key
, CVarRef v
,
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.
856 void prepend(CVarRef v
);
859 * For C++ library users to write "var.cast<c_MyClass>()->mf_func()".
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
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; }
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);
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()); }
992 CVarRef
set(const SmartObject
<T
> &v
) {
997 CVarRef
set(const SmartResource
<T
> &v
) {
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
;
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
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
));
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
;
1055 if (IS_REFCOUNTED_TYPE(t
)) destructData(d
, t
);
1059 ALWAYS_INLINE
void constructRefHelper(CVarRef v
) {
1060 assert(tvIsPlausible(v
));
1062 v
.m_data
.pref
->incRefCount();
1063 m_data
.pref
= v
.m_data
.pref
;
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
);
1094 void setWithRefHelper(CVarRef v
, bool destroy
) {
1095 assert(tvIsPlausible(*this) && tvIsPlausible(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
);
1115 void constructWithRefHelper(CVarRef v
) {
1116 setWithRefHelper(v
, false);
1119 #ifdef INLINE_VARIANT_HELPER
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
);
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".
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
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
;
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
{
1174 CVarRef
get() const { return m_var
; }
1179 class VRefParamValue
{
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 {
1190 VRefParamValue
&operator=(const VRefParamValue
&v
) const {
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(); }
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
1250 class VariantStrongBind
{ private: Variant m_var
; };
1251 class VariantWithRefBind
{ private: Variant m_var
; };
1253 ///////////////////////////////////////////////////////////////////////////////
1256 class VarNR
: private TypedValueAux
{
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();
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();
1299 /* implicit */ VarNR(litstr v
) = delete;
1300 /* implicit */ VarNR(const std::string
& v
) = delete;
1302 void init(DataType 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
);
1318 assert_refcount_realistic(m_data
.parr
->getCount());
1321 assert_refcount_realistic(m_data
.pstr
->getCount());
1324 assert_refcount_realistic(m_data
.pobj
->getCount());
1326 case KindOfResource
:
1327 assert_refcount_realistic(m_data
.pres
->getCount());
1336 ///////////////////////////////////////////////////////////////////////////////
1337 // breaking circular dependencies
1339 inline const Variant
Array::operator[](int key
) const {
1343 inline const Variant
Array::operator[](int64_t key
) const {
1347 inline const Variant
Array::operator[](const String
& key
) const {
1351 inline const Variant
Array::operator[](CVarRef key
) const {
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() {
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())};
1380 auto s1
= v1
.toString();
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_