2 +----------------------------------------------------------------------+
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_VARIANT_H_
18 #define incl_HPHP_VARIANT_H_
20 #include "hphp/runtime/base/array-data.h"
21 #include "hphp/runtime/base/ref-data.h"
22 #include "hphp/runtime/base/tv-conversions.h"
23 #include "hphp/runtime/base/tv-mutate.h"
24 #include "hphp/runtime/base/tv-refcount.h"
25 #include "hphp/runtime/base/tv-variant.h"
26 #include "hphp/runtime/base/typed-value.h"
27 #include "hphp/runtime/base/type-array.h"
28 #include "hphp/runtime/base/type-object.h"
29 #include "hphp/runtime/base/type-resource.h"
30 #include "hphp/runtime/base/type-string.h"
33 #include <type_traits>
36 ///////////////////////////////////////////////////////////////////////////////
38 // Forward declare these to avoid including tv-conversions.h which has a
39 // circular dependency with this file.
40 void tvCastToVecInPlace(TypedValue
*);
41 void tvCastToDictInPlace(TypedValue
*);
42 void tvCastToKeysetInPlace(TypedValue
*);
43 void tvCastToVArrayInPlace(TypedValue
*);
44 void tvCastToDArrayInPlace(TypedValue
*);
47 * This class predates HHVM.
49 * In hphpc, when type inference failed to know type of a variable, we
50 * would use Variant to represent the php variable in generated C++.
52 * Now Variant is only used in C++ extensions, and the API is mostly
53 * legacy stuff. If you're writing a C++ extension, try to avoid
54 * Variant when you can (but you often can't, and we don't really have
55 * a replacement yet, sorry).
57 * In C++ extensions, this class can be used as a generic handle to
58 * one of our other data types (e.g. StringData, ArrayData), and it
59 * may also be a handle to a RefData.
63 * For historical reasons, this class does a lot of things you
64 * don't really expect in a well-behaved C++ class.
66 * For example, the copy constructor is not a copy constructor (it
67 * unboxes refs and converts KindOfUninit to KindOfNull). A
68 * similar story applies to the move constructor. (And this means
69 * we may actually rely on whether copy elision (NRVO etc) is
70 * happening in some places for correctness.)
76 struct Variant
: private TypedValue
{
77 enum class NullInit
{};
79 enum class CellCopy
{};
80 enum class CellDup
{};
81 enum class ArrayInitCtor
{};
82 enum class StrongBind
{};
84 enum class WithRefBind
{};
87 Variant() noexcept
{ m_type
= KindOfUninit
; }
88 explicit Variant(NullInit
) noexcept
{ m_type
= KindOfNull
; }
89 explicit Variant(NoInit
) noexcept
{}
91 /* implicit */ Variant(bool v
) noexcept
{
92 m_type
= KindOfBoolean
; m_data
.num
= v
;
94 /* implicit */ Variant(int v
) noexcept
{
95 m_type
= KindOfInt64
; m_data
.num
= v
;
97 // The following two overloads will accept int64_t whether it's
98 // implemented as long or long long.
99 /* implicit */ Variant(long v
) noexcept
{
100 m_type
= KindOfInt64
; m_data
.num
= v
;
102 /* implicit */ Variant(long long v
) noexcept
{
103 m_type
= KindOfInt64
; m_data
.num
= v
;
105 /* implicit */ Variant(unsigned long v
) noexcept
{
106 m_type
= KindOfInt64
; m_data
.num
= v
;
108 /* implicit */ Variant(unsigned long long v
) noexcept
{
109 m_type
= KindOfInt64
; m_data
.num
= v
;
111 /* implicit */ Variant(double v
) noexcept
{
112 m_type
= KindOfDouble
; m_data
.dbl
= v
;
115 /* implicit */ Variant(const char* v
) {
116 m_type
= KindOfString
;
117 m_data
.pstr
= StringData::Make(v
);
119 /* implicit */ Variant(const std::string
&v
) {
120 m_type
= KindOfString
;
121 StringData
*s
= StringData::Make(v
.c_str(), v
.size(), CopyString
);
125 /* implicit */ Variant(const StaticString
&v
) noexcept
{
126 assert(v
.get() && !v
.get()->isRefCounted());
127 m_type
= KindOfPersistentString
;
128 m_data
.pstr
= v
.get();
131 Variant(const Variant
& other
, WithRefBind
) {
132 tvDupWithRef(*other
.asTypedValue(), *asTypedValue());
133 if (m_type
== KindOfUninit
) m_type
= KindOfNull
;
136 /* implicit */ Variant(const String
& v
) noexcept
: Variant(v
.get()) {}
137 /* implicit */ Variant(const Array
& v
) noexcept
: Variant(v
.get()) { }
138 /* implicit */ Variant(const Object
& v
) noexcept
: Variant(v
.get()) {}
139 /* implicit */ Variant(const Resource
& v
) noexcept
140 : Variant(v
.hdr()) {}
143 * Explicit conversion constructors. These all manipulate ref-counts of bare
144 * pointers as a side-effect, so we want to be explicit when its happening.
146 explicit Variant(StringData
* v
) noexcept
;
147 explicit Variant(ArrayData
* v
) noexcept
{
150 if (v
->isRefCounted()) {
151 m_type
= v
->toDataType();
154 m_type
= v
->toPersistentDataType();
160 explicit Variant(ObjectData
* v
) noexcept
{
162 m_type
= KindOfObject
;
169 explicit Variant(RefData
* r
) noexcept
{
179 template <typename T
>
180 explicit Variant(const req::ptr
<T
>& ptr
) : Variant(ptr
.get()) { }
181 template <typename T
>
182 explicit Variant(req::ptr
<T
>&& ptr
) noexcept
183 : Variant(ptr
.detach(), Attach
{}) { }
186 * Creation constructor from ArrayInit that avoids a null check and an
189 explicit Variant(ArrayData
* ad
, DataType dt
, ArrayInitCtor
) noexcept
{
190 assert(ad
->toDataType() == dt
);
195 enum class PersistentArrInit
{};
196 Variant(const ArrayData
* ad
, DataType dt
, PersistentArrInit
) noexcept
{
197 assert(ad
->toPersistentDataType() == dt
);
198 assert(!ad
->isRefCounted());
199 m_data
.parr
= const_cast<ArrayData
*>(ad
);
203 enum class PersistentStrInit
{};
204 explicit Variant(const StringData
*s
, PersistentStrInit
) noexcept
{
205 assert(!s
->isRefCounted());
206 m_data
.pstr
= const_cast<StringData
*>(s
);
207 m_type
= KindOfPersistentString
;
210 // These are prohibited, but declared just to prevent accidentally
211 // calling the bool constructor just because we had a pointer to
213 /* implicit */ Variant(const void*) = delete;
214 template<typename Ret
, typename
... Args
>
215 /* implicit */ Variant(Ret (*)(Args
...)) = delete;
216 template<class Class
, typename Ret
, typename
... Args
>
217 /* implicit */ Variant(Ret (Class::*)(Args
...)) = delete;
218 template<class Class
, typename Ret
, typename
... Args
>
219 /* implicit */ Variant(Ret (Class::*)(Args
...) const) = delete;
221 //////////////////////////////////////////////////////////////////////
224 * Copy constructor and copy assignment do not semantically make
225 * copies: they unbox refs and turn uninits to null.
228 Variant(const Variant
& v
) noexcept
;
230 Variant(const Variant
& v
, CellCopy
) noexcept
{
235 Variant(const Variant
& v
, CellDup
) noexcept
{
238 tvIncRefGen(*asTypedValue());
241 Variant(StrongBind
, Variant
& v
) {
242 assert(tvIsPlausible(v
));
243 tvBoxIfNeeded(*v
.asTypedValue());
244 refDup(*v
.asTypedValue(), *asTypedValue());
247 Variant
& operator=(const Variant
& v
) noexcept
{
254 * Note: not semantically moves. Like our "copy constructor", these
255 * unbox refs and turn uninits to null.
258 Variant(Variant
&& v
) noexcept
{
259 if (UNLIKELY(v
.m_type
== KindOfRef
)) {
260 // We can't avoid the refcounting when it's a ref. Do basically
261 // what a copy would have done.
262 moveRefHelper(std::move(v
));
267 if (v
.m_type
!= KindOfUninit
) {
270 v
.m_type
= KindOfNull
;
276 // Move ctor for strings
277 /* implicit */ Variant(String
&& v
) noexcept
{
278 StringData
*s
= v
.get();
279 if (LIKELY(s
!= nullptr)) {
281 m_type
= s
->isRefCounted() ? KindOfString
: KindOfPersistentString
;
288 // Move ctor for arrays
289 /* implicit */ Variant(Array
&& v
) noexcept
{
290 ArrayData
*a
= v
.get();
291 if (LIKELY(a
!= nullptr)) {
293 m_type
= a
->isRefCounted() ? a
->toDataType() : a
->toPersistentDataType();
300 // Move ctor for objects
301 /* implicit */ Variant(Object
&& v
) noexcept
{
302 ObjectData
*pobj
= v
.get();
304 m_type
= KindOfObject
;
312 // Move ctor for resources
313 /* implicit */ Variant(Resource
&& v
) noexcept
{
316 m_type
= KindOfResource
;
327 * Note: not semantically moves. Like our "copies", these unbox
328 * refs and turn uninits to null.
330 Variant
& operator=(Variant
&&rhs
) noexcept
{
331 assert(this != &rhs
); // we end up as null on a self move-assign.
332 if (rhs
.m_type
== KindOfRef
) return *this = *rhs
.m_data
.pref
->var();
334 Variant
& lhs
= m_type
== KindOfRef
? *m_data
.pref
->var() : *this;
336 Variant
goner((NoInit()));
337 goner
.m_data
= lhs
.m_data
;
338 goner
.m_type
= lhs
.m_type
;
340 if (rhs
.m_type
== KindOfUninit
) {
341 lhs
.m_type
= KindOfNull
;
343 lhs
.m_type
= rhs
.m_type
;
344 lhs
.m_data
= rhs
.m_data
;
345 rhs
.m_type
= KindOfNull
;
351 ALWAYS_INLINE
~Variant() noexcept
{
352 tvDecRefGen(asTypedValue());
354 memset(this, kTVTrashFill2
, sizeof(*this));
359 //////////////////////////////////////////////////////////////////////
362 * During sweeping, request-allocated things are not allowed to be decref'd
363 * or manipulated. This function is used to cause a Variant to go
364 * into a state where its destructor will have no effects on the
365 * request local heap, in cases where sweepable objects can't
366 * organize things to avoid running Variant destructors.
368 void releaseForSweep() { m_type
= KindOfNull
; }
370 //////////////////////////////////////////////////////////////////////
373 * Break bindings and set to uninit.
376 auto const old
= *asTypedValue();
377 m_type
= KindOfUninit
;
382 * set to null without breaking bindings (if any), faster than v_a = null;
384 void setNull() noexcept
{
385 tvSetNull(*asTypedValue());
389 * Clear the original data, and set it to be the same as in v, and if
390 * v is referenced, keep the reference.
392 Variant
& setWithRef(TypedValue v
) noexcept
{
393 tvSetWithRef(v
, *asTypedValue());
394 if (m_type
== KindOfUninit
) m_type
= KindOfNull
;
397 Variant
& setWithRef(const Variant
& v
) noexcept
{
398 return setWithRef(*v
.asTypedValue());
401 static Variant
attach(TypedValue tv
) noexcept
{
402 return Variant
{tv
, Attach
{}};
404 static Variant
attach(StringData
* var
) noexcept
{
405 return Variant
{var
, Attach
{}};
407 static Variant
attach(ArrayData
* var
) noexcept
{
408 return Variant
{var
, Attach
{}};
410 static Variant
attach(ObjectData
* var
) noexcept
{
411 return Variant
{var
, Attach
{}};
413 static Variant
attach(ResourceData
* var
) noexcept
{
414 return Variant
{var
, Attach
{}};
416 static Variant
attach(ResourceHdr
* var
) noexcept
{
417 return Variant
{var
, Attach
{}};
419 static Variant
attach(RefData
* var
) noexcept
{
420 return Variant
{var
, Attach
{}};
423 static Variant
wrap(TypedValue tv
) noexcept
{
424 return Variant
{tv
, Wrap
{}};
427 ///////////////////////////////////////////////////////////////////////////////
430 ALWAYS_INLINE
int64_t asInt64Val() const {
431 assert(m_type
== KindOfInt64
);
435 ALWAYS_INLINE
int64_t toInt64Val() const {
436 assert(is(KindOfInt64
));
438 LIKELY(m_type
== KindOfInt64
) ?
439 m_data
.num
: m_data
.pref
->var()->m_data
.num
;
442 ///////////////////////////////////////////////////////////////////////////////
445 ALWAYS_INLINE
double asDoubleVal() const {
446 assert(m_type
== KindOfDouble
);
450 ALWAYS_INLINE
double toDoubleVal() const {
451 assert(is(KindOfDouble
));
453 LIKELY(m_type
== KindOfDouble
) ?
454 m_data
.dbl
: m_data
.pref
->var()->m_data
.dbl
;
457 ///////////////////////////////////////////////////////////////////////////////
460 ALWAYS_INLINE
bool asBooleanVal() const {
461 assert(m_type
== KindOfBoolean
);
465 ALWAYS_INLINE
bool toBooleanVal() const {
466 assert(is(KindOfBoolean
));
468 LIKELY(m_type
== KindOfBoolean
) ?
469 m_data
.num
: m_data
.pref
->var()->m_data
.num
;
472 ///////////////////////////////////////////////////////////////////////////////
475 ALWAYS_INLINE
const String
& asCStrRef() const {
476 assert(isStringType(m_type
) && m_data
.pstr
);
477 return *reinterpret_cast<const String
*>(&m_data
.pstr
);
480 ALWAYS_INLINE
const String
& toCStrRef() const {
482 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pstr
: m_data
.pstr
);
483 return *reinterpret_cast<const String
*>(LIKELY(isStringType(m_type
)) ?
484 &m_data
.pstr
: &m_data
.pref
->tv()->m_data
.pstr
);
487 ALWAYS_INLINE String
& asStrRef() {
488 assert(isStringType(m_type
) && m_data
.pstr
);
489 // The caller is likely going to modify the string, so we have to eagerly
490 // promote KindOfPersistentString -> KindOfString.
491 m_type
= KindOfString
;
492 return *reinterpret_cast<String
*>(&m_data
.pstr
);
495 ALWAYS_INLINE String
& toStrRef() {
497 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pstr
: m_data
.pstr
);
498 // The caller is likely going to modify the string, so we have to eagerly
499 // promote KindOfPersistentString -> KindOfString.
500 auto tv
= LIKELY(isStringType(m_type
)) ? this : m_data
.pref
->tv();
501 tv
->m_type
= KindOfString
;
502 return *reinterpret_cast<String
*>(&tv
->m_data
.pstr
);
505 ///////////////////////////////////////////////////////////////////////////////
508 ALWAYS_INLINE
const Array
& asCArrRef() const {
509 assert(isArrayLikeType(m_type
) && m_data
.parr
);
510 return *reinterpret_cast<const Array
*>(&m_data
.parr
);
513 ALWAYS_INLINE
const Array
& toCArrRef() const {
515 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.parr
: m_data
.parr
);
516 return *reinterpret_cast<const Array
*>(LIKELY(isArrayLikeType(m_type
)) ?
517 &m_data
.parr
: &m_data
.pref
->tv()->m_data
.parr
);
520 ALWAYS_INLINE Array
& asArrRef() {
521 assert(isArrayLikeType(m_type
) && m_data
.parr
);
522 m_type
= m_data
.parr
->toDataType();
523 return *reinterpret_cast<Array
*>(&m_data
.parr
);
526 ALWAYS_INLINE Array
& toArrRef() {
528 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.parr
: m_data
.parr
);
529 auto tv
= LIKELY(isArrayLikeType(m_type
)) ? this : m_data
.pref
->tv();
530 tv
->m_type
= tv
->m_data
.parr
->toDataType();
531 return *reinterpret_cast<Array
*>(&tv
->m_data
.parr
);
534 ///////////////////////////////////////////////////////////////////////////////
537 ALWAYS_INLINE
const Object
& asCObjRef() const {
538 assert(m_type
== KindOfObject
&& m_data
.pobj
);
539 return *reinterpret_cast<const Object
*>(&m_data
.pobj
);
542 ALWAYS_INLINE
const Object
& toCObjRef() const {
543 assert(is(KindOfObject
));
544 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pobj
: m_data
.pobj
);
545 return *reinterpret_cast<const Object
*>(LIKELY(m_type
== KindOfObject
) ?
546 &m_data
.pobj
: &m_data
.pref
->tv()->m_data
.pobj
);
549 ALWAYS_INLINE Object
& asObjRef() {
550 assert(m_type
== KindOfObject
&& m_data
.pobj
);
551 return *reinterpret_cast<Object
*>(&m_data
.pobj
);
554 ALWAYS_INLINE
const Resource
& asCResRef() const {
555 assert(m_type
== KindOfResource
&& m_data
.pres
);
556 return *reinterpret_cast<const Resource
*>(&m_data
.pres
);
559 ALWAYS_INLINE
const Resource
& toCResRef() const {
560 assert(is(KindOfResource
));
561 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pres
: m_data
.pres
);
562 return *reinterpret_cast<const Resource
*>(LIKELY(m_type
== KindOfResource
) ?
563 &m_data
.pres
: &m_data
.pref
->tv()->m_data
.pres
);
566 ALWAYS_INLINE Resource
& asResRef() {
567 assert(m_type
== KindOfResource
&& m_data
.pres
);
568 return *reinterpret_cast<Resource
*>(&m_data
.pres
);
571 ALWAYS_INLINE Object
& toObjRef() {
572 assert(is(KindOfObject
));
573 assert(m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pobj
: m_data
.pobj
);
574 return *reinterpret_cast<Object
*>(LIKELY(m_type
== KindOfObject
) ?
575 &m_data
.pobj
: &m_data
.pref
->tv()->m_data
.pobj
);
579 * Type testing functions
581 DataType
getType() const {
582 return m_type
== KindOfRef
? m_data
.pref
->var()->m_type
: m_type
;
584 DataType
getRawType() const {
587 bool is(DataType type
) const {
588 return getType() == type
;
590 bool isInitialized() const {
591 return m_type
!= KindOfUninit
;
593 bool isNull() const {
594 return isNullType(getType());
596 bool isBoolean() const {
597 return getType() == KindOfBoolean
;
599 bool isInteger() const {
600 return getType() == KindOfInt64
;
602 bool isDouble() const {
603 return getType() == KindOfDouble
;
605 bool isString() const {
606 return isStringType(getType());
608 bool isArray() const {
609 return isArrayLikeType(getType());
611 bool isPHPArray() const {
612 return isArrayType(getType());
614 bool isVecArray() const {
615 return isVecType(getType());
617 bool isDict() const {
618 return isDictType(getType());
620 bool isKeyset() const {
621 return isKeysetType(getType());
623 bool isHackArray() const {
624 return isHackArrayType(getType());
626 bool isObject() const {
627 return getType() == KindOfObject
;
629 bool isResource() const {
630 return getType() == KindOfResource
;
633 bool isNumeric(bool checkString
= false) const noexcept
;
634 DataType
toNumeric(int64_t &ival
, double &dval
, bool checkString
= false)
636 bool isScalar() const noexcept
;
637 bool isIntVal() const {
647 case KindOfPersistentString
:
649 case KindOfPersistentVec
:
651 case KindOfPersistentDict
:
653 case KindOfPersistentKeyset
:
655 case KindOfPersistentArray
:
659 return m_data
.pref
->var()->isIntVal();
664 // Is "define('CONSTANT', <this value>)" legal?
665 bool isAllowedAsConstantValue() const;
668 * Whether or not there are at least two variables that are strongly bound.
670 bool isReferenced() const {
671 return m_type
== KindOfRef
&& m_data
.pref
->isReferenced();
675 * Get reference count of weak or strong binding. For debugging purpose.
677 int getRefCount() const noexcept
;
679 bool getBoolean() const {
680 assert(getType() == KindOfBoolean
);
681 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.num
: m_data
.num
;
683 int64_t getInt64() const {
684 assert(getType() == KindOfInt64
);
685 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.num
: m_data
.num
;
687 double getDouble() const {
688 assert(getType() == KindOfDouble
);
689 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.dbl
: m_data
.dbl
;
695 Variant
& assign(const Variant
& v
) noexcept
{
696 tvSet(tvToInitCell(*v
.asTypedValue()), *asTypedValue());
699 Variant
& assignRef(Variant
& v
) noexcept
{
700 assert(&v
!= &uninit_variant
);
701 tvBoxIfNeeded(*v
.asTypedValue());
702 tvBind(*v
.asTypedValue(), *asTypedValue());
705 Variant
& assignRef(VRefParam v
) = delete;
707 // Generic assignment operator. Forward argument (preserving rvalue-ness and
708 // lvalue-ness) to the appropriate set function, as long as its not a Variant.
709 template <typename T
>
710 typename
std::enable_if
<
713 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
718 typename
std::remove_reference
<typename
std::remove_cv
<T
>::type
>::type
721 >::type
operator=(T
&& v
) {
722 set(std::forward
<T
>(v
));
726 Variant
operator + () const = delete;
727 Variant
&operator += (const Variant
& v
) = delete;
728 Variant
&operator += (int n
) = delete;
729 Variant
&operator += (int64_t n
) = delete;
730 Variant
&operator += (double n
) = delete;
732 Variant
operator - () const = delete;
733 Variant
operator - (const Variant
& v
) const = delete;
734 Variant
&operator -= (const Variant
& v
) = delete;
735 Variant
&operator -= (int n
) = delete;
736 Variant
&operator -= (int64_t n
) = delete;
737 Variant
&operator -= (double n
) = delete;
739 Variant
operator * (const Variant
& v
) const = delete;
740 Variant
&operator *= (const Variant
& v
) = delete;
741 Variant
&operator *= (int n
) = delete;
742 Variant
&operator *= (int64_t n
) = delete;
743 Variant
&operator *= (double n
) = delete;
745 Variant
operator / (const Variant
& v
) const = delete;
746 Variant
&operator /= (const Variant
& v
) = delete;
747 Variant
&operator /= (int n
) = delete;
748 Variant
&operator /= (int64_t n
) = delete;
749 Variant
&operator /= (double n
) = delete;
751 int64_t operator % (const Variant
& v
) const = delete;
752 Variant
&operator %= (const Variant
& v
) = delete;
753 Variant
&operator %= (int n
) = delete;
754 Variant
&operator %= (int64_t n
) = delete;
755 Variant
&operator %= (double n
) = delete;
757 Variant
operator | (const Variant
& v
) const = delete;
758 Variant
&operator |= (const Variant
& v
) = delete;
759 Variant
operator & (const Variant
& v
) const = delete;
760 Variant
&operator &= (const Variant
& v
) = delete;
761 Variant
operator ^ (const Variant
& v
) const = delete;
762 Variant
&operator ^= (const Variant
& v
) = delete;
763 Variant
&operator <<=(int64_t n
) = delete;
764 Variant
&operator >>=(int64_t n
) = delete;
766 Variant
&operator ++ () = delete;
767 Variant
operator ++ (int) = delete;
768 Variant
&operator -- () = delete;
769 Variant
operator -- (int) = delete;
772 * Variant used to implicitly convert to all these types. (It still
773 * implicitly converts *from* most of them.)
775 * We're leaving these functions deleted for now because we fear the
776 * possibility of changes to overload resolution by not declaring
777 * them. Eventually when fewer of these types have implicit
778 * conversions we'll remove them.
780 /* implicit */ operator bool () const = delete;
781 /* implicit */ operator char () const = delete;
782 /* implicit */ operator short () const = delete;
783 /* implicit */ operator int () const = delete;
784 /* implicit */ operator int64_t () const = delete;
785 /* implicit */ operator double () const = delete;
786 /* implicit */ operator String () const = delete;
787 /* implicit */ operator Array () const = delete;
788 /* implicit */ operator Object () const = delete;
791 * Explicit type conversions
793 bool toBoolean() const {
794 if (isNullType(m_type
)) return false;
795 if (m_type
<= KindOfInt64
) return m_data
.num
;
796 return toBooleanHelper();
798 char toByte() const { return (char)toInt64();}
799 short toInt16(int base
= 10) const { return (short)toInt64(base
);}
800 int toInt32(int base
= 10) const { return (int)toInt64(base
);}
801 int64_t toInt64() const {
802 if (isNullType(m_type
)) return 0;
803 if (m_type
<= KindOfInt64
) return m_data
.num
;
804 return toInt64Helper(10);
806 int64_t toInt64(int base
) const {
807 if (isNullType(m_type
)) return 0;
808 if (m_type
<= KindOfInt64
) return m_data
.num
;
809 return toInt64Helper(base
);
811 double toDouble() const {
812 if (m_type
== KindOfDouble
) return m_data
.dbl
;
813 return toDoubleHelper();
816 String
toString() const& {
817 if (isStringType(m_type
)) return String
{m_data
.pstr
};
818 return toStringHelper();
821 String
toString() && {
822 if (isStringType(m_type
)) {
824 return String::attach(m_data
.pstr
);
826 return toStringHelper();
829 // Convert a non-array-like type to a PHP array, leaving PHP arrays and Hack
830 // arrays unchanged. Use toPHPArray() if you want the result to always be a
832 Array
toArray() const {
833 if (isArrayLikeType(m_type
)) return Array(m_data
.parr
);
834 return toArrayHelper();
836 Array
toPHPArray() const {
837 if (isArrayType(m_type
)) return Array(m_data
.parr
);
838 return toPHPArrayHelper();
840 Object
toObject() const {
841 if (m_type
== KindOfObject
) return Object
{m_data
.pobj
};
842 return toObjectHelper();
844 Resource
toResource() const {
845 if (m_type
== KindOfResource
) return Resource
{m_data
.pres
};
846 return toResourceHelper();
849 Array
toVecArray() const {
850 if (isVecType(m_type
)) return Array
{m_data
.parr
};
852 tvCastToVecInPlace(copy
.asTypedValue());
853 assertx(copy
.isVecArray());
854 return Array::attach(copy
.detach().m_data
.parr
);
857 Array
toDict() const {
858 if (isDictType(m_type
)) return Array
{m_data
.parr
};
860 tvCastToDictInPlace(copy
.asTypedValue());
861 assertx(copy
.isDict());
862 return Array::attach(copy
.detach().m_data
.parr
);
865 Array
toKeyset() const {
866 if (isKeysetType(m_type
)) return Array
{m_data
.parr
};
868 tvCastToKeysetInPlace(copy
.asTypedValue());
869 assertx(copy
.isKeyset());
870 return Array::attach(copy
.detach().m_data
.parr
);
873 Array
toVArray() const {
874 if (isArrayType(m_type
)) return asCArrRef().toVArray();
876 tvCastToVArrayInPlace(copy
.asTypedValue());
877 assertx(copy
.isPHPArray() && copy
.asCArrRef().isVArray());
878 return Array::attach(copy
.detach().m_data
.parr
);
881 Array
toDArray() const {
882 if (isArrayType(m_type
)) return asCArrRef().toDArray();
884 tvCastToDArrayInPlace(copy
.asTypedValue());
885 assertx(copy
.isPHPArray() && copy
.asCArrRef().isDArray());
886 return Array::attach(copy
.detach().m_data
.parr
);
889 template <typename T
>
890 typename
std::enable_if
<std::is_base_of
<ResourceData
,T
>::value
, bool>::type
892 if (m_type
== KindOfResource
) {
893 return m_data
.pres
->data()->instanceof
<T
>();
895 if (m_type
== KindOfRef
&& m_data
.pref
->var()->m_type
== KindOfResource
) {
896 return m_data
.pref
->var()->m_data
.pres
->data()->instanceof
<T
>();
901 template <typename T
>
902 typename
std::enable_if
<std::is_base_of
<ObjectData
,T
>::value
, bool>::type
904 if (m_type
== KindOfObject
) {
905 return m_data
.pobj
->instanceof
<T
>();
907 if (m_type
== KindOfRef
&&
908 m_data
.pref
->var()->m_type
== KindOfObject
) {
909 return m_data
.pref
->var()->m_data
.pobj
->instanceof
<T
>();
915 * Whether or not calling toKey() will throw a bad type exception
917 bool canBeValidKey() const {
918 return !isArrayType(getType()) && getType() != KindOfObject
;
922 * Convert to a valid key or throw an exception. If convertStrKeys is true
923 * int-like string keys will be converted to int keys.
925 VarNR
toKey(const ArrayData
*) const;
927 /* Creating a temporary Array, String, or Object with no ref-counting and
928 * no type checking, use it only when we have checked the variant type and
929 * we are sure the internal data will have a reference until the temporary
930 * one gets out-of-scope.
932 StrNR
toStrNR() const {
933 return StrNR(getStringData());
935 ArrNR
toArrNR() const {
936 return ArrNR(getArrayData());
938 ObjNR
toObjNR() const {
939 return ObjNR(getObjectData());
943 * Low level access that should be restricted to internal use.
945 int64_t *getInt64Data() const {
946 assert(getType() == KindOfInt64
);
947 return m_type
== KindOfRef
? &m_data
.pref
->var()->m_data
.num
:
948 const_cast<int64_t*>(&m_data
.num
);
950 double *getDoubleData() const {
951 assert(getType() == KindOfDouble
);
952 return m_type
== KindOfRef
? &m_data
.pref
->var()->m_data
.dbl
:
953 const_cast<double*>(&m_data
.dbl
);
955 StringData
*getStringData() const {
956 assert(isStringType(getType()));
957 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pstr
: m_data
.pstr
;
959 StringData
*getStringDataOrNull() const {
960 // This is a necessary evil because getStringData() returns
961 // an undefined result if this is a null variant
962 assert(isNull() || isString());
963 return m_type
== KindOfRef
?
964 (m_data
.pref
->var()->m_type
<= KindOfNull
? nullptr :
965 m_data
.pref
->var()->m_data
.pstr
) :
966 (m_type
<= KindOfNull
? nullptr : m_data
.pstr
);
968 ArrayData
*getArrayData() const {
970 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.parr
: m_data
.parr
;
972 ArrayData
*getArrayDataOrNull() const {
973 // This is a necessary evil because getArrayData() returns
974 // an undefined result if this is a null variant
975 assert(isNull() || isArray());
976 return m_type
== KindOfRef
?
977 (m_data
.pref
->var()->m_type
<= KindOfNull
? nullptr :
978 m_data
.pref
->var()->m_data
.parr
) :
979 (m_type
<= KindOfNull
? nullptr : m_data
.parr
);
981 ObjectData
* getObjectData() const {
982 assert(is(KindOfObject
));
983 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pobj
: m_data
.pobj
;
985 ObjectData
*getObjectDataOrNull() const {
986 // This is a necessary evil because getObjectData() returns
987 // an undefined result if this is a null variant
988 assert(isNull() || is(KindOfObject
));
989 return m_type
== KindOfRef
?
990 (m_data
.pref
->var()->m_type
<= KindOfNull
? nullptr :
991 m_data
.pref
->var()->m_data
.pobj
) :
992 (m_type
<= KindOfNull
? nullptr : m_data
.pobj
);
994 Variant
*getRefData() const {
995 assert(m_type
== KindOfRef
);
996 return m_data
.pref
->var();
999 int64_t getNumData() const { return m_data
.num
; }
1002 * Make any Variant strings and arrays not ref counted (e.g., static).
1003 * Use it, for example, if you need a long-lived Variant before the Memory
1004 * Manager has been initialized.
1005 * You will still get an assertion if the Variant is an object, resource, etc.
1007 void setEvalScalar();
1010 * Access this Variant as a TypedValue. Does not unbox refs, etc.
1012 const TypedValue
* asTypedValue() const { return this; }
1013 TypedValue
* asTypedValue() { return this; }
1016 * Access this Variant as a Cell. I.e. unboxes it if it was a
1019 const Cell
* asCell() const { return tvToCell(asTypedValue()); }
1020 Cell
* asCell() { return tvToCell(asTypedValue()); }
1023 * Read this Variant as an InitCell, without incrementing the
1024 * reference count. I.e. unbox if it is boxed, and turn
1025 * KindOfUninit into KindOfNull.
1027 Cell
asInitCellTmp() const {
1028 if (UNLIKELY(m_type
== KindOfRef
)) {
1029 return *m_data
.pref
->tv();
1031 if (m_type
== KindOfUninit
) return make_tv
<KindOfNull
>();
1036 * Access this Variant as a Ref, converting it to a Ref it isn't
1039 Ref
* asRef() { tvBoxIfNeeded(*asTypedValue()); return this; }
1041 TypedValue
detach() noexcept
{
1042 auto tv
= *asTypedValue();
1043 m_type
= KindOfNull
;
1048 ResourceData
* getResourceData() const {
1049 assert(is(KindOfResource
));
1050 return m_type
== KindOfRef
? m_data
.pref
->var()->m_data
.pres
->data() :
1051 m_data
.pres
->data();
1054 ResourceData
* detachResourceData() {
1055 assert(is(KindOfResource
));
1056 if (LIKELY(m_type
== KindOfResource
)) {
1057 m_type
= KindOfNull
;
1058 return m_data
.pres
->data();
1060 m_type
= KindOfNull
;
1061 return m_data
.pref
->tv()->m_data
.pres
->data();
1064 ObjectData
* detachObjectData() {
1065 assert(is(KindOfObject
));
1066 if (LIKELY(m_type
== KindOfObject
)) {
1067 m_type
= KindOfNull
;
1070 m_type
= KindOfNull
;
1071 return m_data
.pref
->tv()->m_data
.pobj
;
1075 template <typename T
>
1076 friend typename
std::enable_if
<
1077 std::is_base_of
<ResourceData
,T
>::value
,
1079 >::type
deref(const Variant
& v
) { return v
.getResourceData(); }
1081 template <typename T
>
1082 friend typename
std::enable_if
<
1083 std::is_base_of
<ObjectData
,T
>::value
,
1085 >::type
deref(const Variant
& v
) { return v
.getObjectData(); }
1087 template <typename T
>
1088 friend typename
std::enable_if
<
1089 std::is_base_of
<ResourceData
,T
>::value
,
1091 >::type
detach(Variant
&& v
) { return v
.detachResourceData(); }
1093 template <typename T
>
1094 friend typename
std::enable_if
<
1095 std::is_base_of
<ObjectData
,T
>::value
,
1097 >::type
detach(Variant
&& v
) { return v
.detachObjectData(); }
1099 explicit Variant(ResourceData
* v
) noexcept
{
1101 m_type
= KindOfResource
;
1102 m_data
.pres
= v
->hdr();
1105 m_type
= KindOfNull
;
1108 explicit Variant(ResourceHdr
* v
) noexcept
{
1110 m_type
= KindOfResource
;
1114 m_type
= KindOfNull
;
1119 * This set of constructors act like the normal constructors for the
1120 * given types except that they do not increment the reference count
1121 * of the passed value. They are used for the req::ptr move constructor.
1123 Variant(StringData
* var
, Attach
) noexcept
{
1125 m_type
= var
->isRefCounted() ? KindOfString
: KindOfPersistentString
;
1128 m_type
= KindOfNull
;
1131 Variant(ArrayData
* var
, Attach
) noexcept
{
1134 var
->isRefCounted() ? var
->toDataType() : var
->toPersistentDataType();
1137 m_type
= KindOfNull
;
1140 Variant(ObjectData
* var
, Attach
) noexcept
{
1142 m_type
= KindOfObject
;
1145 m_type
= KindOfNull
;
1148 Variant(ResourceData
* var
, Attach
) noexcept
{
1150 m_type
= KindOfResource
;
1151 m_data
.pres
= var
->hdr();
1153 m_type
= KindOfNull
;
1156 Variant(ResourceHdr
* var
, Attach
) noexcept
{
1158 m_type
= KindOfResource
;
1161 m_type
= KindOfNull
;
1164 Variant(RefData
* var
, Attach
) noexcept
{
1169 m_type
= KindOfNull
;
1172 Variant(TypedValue tv
, Attach
) noexcept
: TypedValue(tv
) {}
1173 Variant(TypedValue tv
, Wrap
) noexcept
: TypedValue(tv
) {
1174 tvIncRefGen(*asTypedValue());
1177 bool isPrimitive() const { return !isRefcountedType(m_type
); }
1178 bool isObjectConvertable() {
1179 assert(m_type
!= KindOfRef
);
1180 return m_type
<= KindOfNull
||
1181 (m_type
== KindOfBoolean
&& !m_data
.num
) ||
1182 (isStringType(m_type
) && m_data
.pstr
->empty());
1185 void set(bool v
) noexcept
;
1186 void set(int v
) noexcept
;
1187 void set(int64_t v
) noexcept
;
1188 void set(double v
) noexcept
;
1189 void set(const char* v
) = delete;
1190 void set(const std::string
& v
) {
1191 return set(String(v
));
1193 void set(StringData
*v
) noexcept
;
1194 void set(ArrayData
*v
) noexcept
;
1195 void set(ObjectData
*v
) noexcept
;
1196 void set(ResourceHdr
*v
) noexcept
;
1197 void set(ResourceData
*v
) noexcept
{ set(v
->hdr()); }
1198 void set(const StringData
*v
) = delete;
1199 void set(const ArrayData
*v
) = delete;
1200 void set(const ObjectData
*v
) = delete;
1201 void set(const ResourceData
*v
) = delete;
1202 void set(const ResourceHdr
*v
) = delete;
1204 void set(const String
& v
) noexcept
{ set(v
.get()); }
1205 void set(const StaticString
& v
) noexcept
;
1206 void set(const Array
& v
) noexcept
{ set(v
.get()); }
1207 void set(const Object
& v
) noexcept
{ set(v
.get()); }
1208 void set(const Resource
& v
) noexcept
{ set(v
.hdr()); }
1210 void set(String
&& v
) noexcept
{ steal(v
.detach()); }
1211 void set(Array
&& v
) noexcept
{ steal(v
.detach()); }
1212 void set(Object
&& v
) noexcept
{ steal(v
.detach()); }
1213 void set(Resource
&& v
) noexcept
{ steal(v
.detachHdr()); }
1215 template<typename T
>
1216 void set(const req::ptr
<T
> &v
) noexcept
{
1217 return set(v
.get());
1220 template <typename T
>
1221 void set(req::ptr
<T
>&& v
) noexcept
{
1222 return steal(v
.detach());
1225 void steal(StringData
* v
) noexcept
;
1226 void steal(ArrayData
* v
) noexcept
;
1227 void steal(ObjectData
* v
) noexcept
;
1228 void steal(ResourceHdr
* v
) noexcept
;
1229 void steal(ResourceData
* v
) noexcept
{ steal(v
->hdr()); }
1232 void moveRefHelper(Variant
&& v
) {
1233 assert(tvIsPlausible(v
));
1235 assert(v
.m_type
== KindOfRef
);
1236 m_type
= v
.m_data
.pref
->tv()->m_type
; // Can't be KindOfUninit.
1237 m_data
= v
.m_data
.pref
->tv()->m_data
;
1238 tvIncRefGen(*asTypedValue());
1239 decRefRef(v
.m_data
.pref
);
1240 v
.m_type
= KindOfNull
;
1243 bool toBooleanHelper() const;
1244 int64_t toInt64Helper(int base
= 10) const;
1245 double toDoubleHelper() const;
1246 String
toStringHelper() const;
1247 Array
toArrayHelper() const;
1248 Array
toPHPArrayHelper() const;
1249 Object
toObjectHelper() const;
1250 Resource
toResourceHelper() const;
1252 DataType
convertToNumeric(int64_t *lval
, double *dval
) const;
1255 Variant
operator+(const Variant
& lhs
, const Variant
& rhs
) = delete;
1257 struct RefResultValue
{
1258 const Variant
& get() const { return m_var
; }
1263 struct VRefParamValue
{
1264 template <class T
> /* implicit */ VRefParamValue(const T
&v
) : m_var(v
) {}
1266 /* implicit */ VRefParamValue() : m_var(Variant::NullInit()) {}
1267 /* implicit */ VRefParamValue(RefResult v
)
1268 : m_var(Variant::StrongBind
{},
1269 const_cast<Variant
&>(reinterpret_cast<const Variant
&>(v
))) {} // XXX
1270 template <typename T
>
1271 Variant
&operator=(const T
&v
) const = delete;
1272 operator const Variant
&() const { return m_var
; }
1273 const Variant
*operator&() const { return &m_var
; } // FIXME
1274 const Variant
*operator->() const { return &m_var
; }
1276 const Variant
& wrapped() const { return m_var
; }
1278 explicit operator bool () const { return m_var
.toBoolean();}
1279 operator int () const { return m_var
.toInt32();}
1280 operator int64_t() const { return m_var
.toInt64();}
1281 operator double () const { return m_var
.toDouble();}
1282 operator String () const { return m_var
.toString();}
1283 operator Array () const { return m_var
.toArray();}
1284 operator Object () const { return m_var
.toObject();}
1285 explicit operator Resource () const { return m_var
.toResource();}
1287 bool is(DataType type
) const { return m_var
.is(type
); }
1288 bool isString() const { return m_var
.isString(); }
1289 bool isObject() const { return m_var
.isObject(); }
1290 bool isReferenced() const { return m_var
.isReferenced(); }
1291 bool isNull() const { return m_var
.isNull(); }
1292 bool isRefData() const { return m_var
.asTypedValue()->m_type
== KindOfRef
; }
1294 bool toBoolean() const { return m_var
.toBoolean(); }
1295 int64_t toInt64() const { return m_var
.toInt64(); }
1296 double toDouble() const { return m_var
.toDouble(); }
1297 String
toString() const { return m_var
.toString(); }
1298 StringData
*getStringData() const { return m_var
.getStringData(); }
1299 Array
toArray() const { return m_var
.toArray(); }
1300 Object
toObject() const { return m_var
.toObject(); }
1301 Resource
toResource() const { return m_var
.toResource(); }
1302 ObjectData
*getObjectData() const { return m_var
.getObjectData(); }
1304 bool isArray() const { return m_var
.isArray(); }
1305 bool isHackArray() const { return m_var
.isHackArray(); }
1306 bool isPHPArray() const { return m_var
.isPHPArray(); }
1307 ArrNR
toArrNR() const { return m_var
.toArrNR(); }
1309 RefData
* getRefData() const {
1310 assert(isRefData());
1311 return m_var
.asTypedValue()->m_data
.pref
;
1313 RefData
* getRefDataOrNull() const {
1314 return isRefData() ? m_var
.asTypedValue()->m_data
.pref
: nullptr;
1316 Variant
* getVariantOrNull() const {
1317 return isRefData() ? m_var
.asTypedValue()->m_data
.pref
->var() : nullptr;
1319 void assignIfRef(const Variant
& other
) const {
1320 if (auto ref
= getVariantOrNull()) *ref
= other
;
1322 void assignIfRef(Variant
&& other
) const {
1323 if (auto ref
= getVariantOrNull()) *ref
= std::move(other
);
1329 ///////////////////////////////////////////////////////////////////////////////
1332 struct VarNR
: private TypedValueAux
{
1333 static VarNR
MakeKey(const String
& s
) {
1334 if (s
.empty()) return VarNR(staticEmptyString());
1336 if (UNLIKELY(s
.get()->isStrictlyInteger(n
))) {
1337 if (RuntimeOption::EvalHackArrCompatNotices
) {
1338 raise_intish_index_cast();
1345 // Use to hold variant that do not need ref-counting
1346 explicit VarNR(bool v
) { init(KindOfBoolean
); m_data
.num
= (v
?1:0);}
1347 explicit VarNR(int v
) { init(KindOfInt64
); m_data
.num
= v
;}
1348 // The following two overloads will accept int64_t whether it's
1349 // implemented as long or long long.
1350 explicit VarNR(long v
) { init(KindOfInt64
); m_data
.num
= v
;}
1351 explicit VarNR(long long v
) { init(KindOfInt64
); m_data
.num
= v
;}
1352 explicit VarNR(uint64_t v
) { init(KindOfInt64
); m_data
.num
= v
;}
1353 explicit VarNR(double v
) { init(KindOfDouble
); m_data
.dbl
= v
;}
1355 explicit VarNR(const StaticString
&v
) {
1356 assert(v
.get() && !v
.get()->isRefCounted());
1357 init(KindOfPersistentString
);
1358 m_data
.pstr
= v
.get();
1361 explicit VarNR(const String
& v
);
1362 explicit VarNR(const Array
& v
);
1363 explicit VarNR(const Object
& v
);
1364 explicit VarNR(StringData
*v
);
1365 explicit VarNR(const StringData
*v
) {
1366 assert(v
&& !v
->isRefCounted());
1367 init(KindOfPersistentString
);
1368 m_data
.pstr
= const_cast<StringData
*>(v
);
1370 explicit VarNR(ArrayData
*v
);
1371 explicit VarNR(const ArrayData
*) = delete;
1372 explicit VarNR(ObjectData
*v
);
1373 explicit VarNR(const ObjectData
*) = delete;
1375 explicit VarNR(TypedValue tv
) { init(tv
.m_type
); m_data
= tv
.m_data
; }
1377 VarNR(const VarNR
&v
) : TypedValueAux(v
) {}
1379 explicit VarNR() { asVariant()->asTypedValue()->m_type
= KindOfUninit
; }
1384 memset(this, kTVTrashFill2
, sizeof(*this));
1388 TypedValue
tv() const { return *this; }
1390 operator const Variant
&() const { return *asVariant(); }
1392 bool isNull() const {
1393 return asVariant()->isNull();
1396 /* implicit */ VarNR(const char* v
) = delete;
1397 /* implicit */ VarNR(const std::string
& v
) = delete;
1399 void init(DataType dt
) {
1401 if (debug
) varNrFlag() = NR_FLAG
;
1403 const Variant
*asVariant() const {
1404 return &tvAsCVarRef(static_cast<const TypedValue
*>(this));
1406 Variant
* asVariant() {
1407 return &tvAsVariant(static_cast<TypedValue
*>(this));
1409 void checkRefCount() {
1410 assert(isRefcountedType(m_type
) ? varNrFlag() == NR_FLAG
: true);
1416 assert(m_data
.pstr
->checkCount());
1422 assert(m_data
.parr
->checkCount());
1425 assert(m_data
.pobj
->checkCount());
1427 case KindOfResource
:
1428 assert(m_data
.pres
->checkCount());
1431 assert(m_data
.pref
->checkCount());
1438 //////////////////////////////////////////////////////////////////////
1441 * The lvalBlackHole is used in array operations when a NewElem can't
1442 * create a new slot. (Basically if the next integer key in an array
1443 * is already at the maximum integer.)
1445 Variant
& lvalBlackHole();
1448 * The lvalBlackHole has request lifetime.
1450 void initBlackHole();
1451 void clearBlackHole();
1453 ///////////////////////////////////////////////////////////////////////////////
1454 // breaking circular dependencies
1456 inline Variant
Array::operator[](Cell key
) const {
1457 return Variant::wrap(rvalAt(key
).tv());
1459 inline Variant
Array::operator[](int key
) const {
1460 return Variant::wrap(rvalAt(key
).tv());
1462 inline Variant
Array::operator[](int64_t key
) const {
1463 return Variant::wrap(rvalAt(key
).tv());
1465 inline Variant
Array::operator[](const String
& key
) const {
1466 return Variant::wrap(rvalAt(key
).tv());
1468 inline Variant
Array::operator[](const Variant
& key
) const {
1469 return Variant::wrap(rvalAt(key
).tv());
1472 inline void Array::append(const Variant
& v
) {
1473 append(*v
.asTypedValue());
1475 inline void Array::appendWithRef(const Variant
& v
) {
1476 appendWithRef(*v
.asTypedValue());
1478 inline void Array::prepend(const Variant
& v
) {
1479 prepend(*v
.asTypedValue());
1482 ALWAYS_INLINE Variant
uninit_null() {
1486 ALWAYS_INLINE Variant
init_null() {
1487 return Variant(Variant::NullInit());
1490 inline Variant
&concat_assign(Variant
&v1
, const char* s2
) = delete;
1492 inline Variant
&concat_assign(Variant
&v1
, const String
& s2
) {
1493 if (v1
.getType() == KindOfString
) {
1494 auto& str
= v1
.asStrRef();
1495 if (!str
.get()->cowCheck()) {
1501 auto s1
= v1
.toString();
1507 //////////////////////////////////////////////////////////////////////
1509 // Defined here for include order reasons.
1510 inline RefData::~RefData() {
1511 assert(m_kind
== HeaderKind::Ref
);
1512 tvAsVariant(&m_tv
).~Variant();
1515 //////////////////////////////////////////////////////////////////////
1517 inline Array
& forceToArray(Variant
& var
) {
1518 if (!var
.isArray()) var
= Variant(Array::Create());
1519 return var
.toArrRef();
1522 inline Array
& forceToArray(member_lval lval
) {
1523 auto const inner
= lval
.unboxed();
1524 if (!isArrayLikeType(inner
.type())) {
1525 tvSet(make_tv
<KindOfArray
>(ArrayData::Create()), inner
);
1527 return asArrRef(inner
);
1530 inline Array
& forceToDict(Variant
& var
) {
1531 if (!var
.isDict()) var
= Variant(Array::CreateDict());
1532 return var
.toArrRef();
1535 //////////////////////////////////////////////////////////////////////
1537 ALWAYS_INLINE Variant
empty_string_variant() {
1538 return Variant(staticEmptyString(), Variant::PersistentStrInit
{});
1541 template <typename T
>
1542 inline Variant
toVariant(const req::ptr
<T
>& p
) {
1543 return p
? Variant(p
) : Variant(false);
1546 template <typename T
>
1547 inline Variant
toVariant(req::ptr
<T
>&& p
) {
1548 return p
? Variant(std::move(p
)) : Variant(false);
1552 inline bool is_null(const Variant
& v
) {
1556 template <typename T
>
1557 inline bool isa_non_null(const Variant
& v
) {
1561 // Defined here to avoid introducing a dependency cycle between type-variant
1563 ALWAYS_INLINE Cell
Array::convertKey(Cell k
) const {
1564 return cellToKey(k
, m_arr
? m_arr
.get() : staticEmptyArray());
1566 ALWAYS_INLINE Cell
Array::convertKey(const Variant
& k
) const {
1567 return convertKey(*k
.asCell());
1570 inline VarNR
Variant::toKey(const ArrayData
* ad
) const {
1571 return VarNR(tvToKey(*this, ad
));
1574 //////////////////////////////////////////////////////////////////////
1578 #endif // incl_HPHP_VARIANT_H_