Don't return Variant& from Array functions
[hiphop-php.git] / hphp / runtime / base / type-variant.h
blob5eea8fae78249bec913b60c36683168d26b1756b
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_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"
32 #include <algorithm>
33 #include <type_traits>
35 namespace HPHP {
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.
61 * Beware:
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.)
72 * Use carefully.
76 struct Variant : private TypedValue {
77 enum class NullInit {};
78 enum class NoInit {};
79 enum class CellCopy {};
80 enum class CellDup {};
81 enum class ArrayInitCtor {};
82 enum class StrongBind {};
83 enum class Attach {};
84 enum class WithRefBind {};
85 enum class Wrap {};
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);
122 assert(s);
123 m_data.pstr = s;
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 {
148 if (v) {
149 m_data.parr = v;
150 if (v->isRefCounted()) {
151 m_type = v->toDataType();
152 v->rawIncRefCount();
153 } else {
154 m_type = v->toPersistentDataType();
156 } else {
157 m_type = KindOfNull;
160 explicit Variant(ObjectData* v) noexcept {
161 if (v) {
162 m_type = KindOfObject;
163 m_data.pobj = v;
164 v->incRefCount();
165 } else {
166 m_type = KindOfNull;
169 explicit Variant(RefData* r) noexcept {
170 if (r) {
171 m_type = KindOfRef;
172 m_data.pref = r;
173 r->incRefCount();
174 } else {
175 m_type = KindOfNull;
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
187 * inc-ref.
189 explicit Variant(ArrayData* ad, DataType dt, ArrayInitCtor) noexcept {
190 assert(ad->toDataType() == dt);
191 m_type = dt;
192 m_data.parr = ad;
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);
200 m_type = dt;
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
212 // const.
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 {
231 m_type = v.m_type;
232 m_data = v.m_data;
235 Variant(const Variant& v, CellDup) noexcept {
236 m_type = v.m_type;
237 m_data = v.m_data;
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 {
248 return assign(v);
252 * Move ctors
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));
263 return;
266 assert(this != &v);
267 if (v.m_type != KindOfUninit) {
268 m_type = v.m_type;
269 m_data = v.m_data;
270 v.m_type = KindOfNull;
271 } else {
272 m_type = KindOfNull;
276 // Move ctor for strings
277 /* implicit */ Variant(String&& v) noexcept {
278 StringData *s = v.get();
279 if (LIKELY(s != nullptr)) {
280 m_data.pstr = s;
281 m_type = s->isRefCounted() ? KindOfString : KindOfPersistentString;
282 v.detach();
283 } else {
284 m_type = KindOfNull;
288 // Move ctor for arrays
289 /* implicit */ Variant(Array&& v) noexcept {
290 ArrayData *a = v.get();
291 if (LIKELY(a != nullptr)) {
292 m_data.parr = a;
293 m_type = a->isRefCounted() ? a->toDataType() : a->toPersistentDataType();
294 v.detach();
295 } else {
296 m_type = KindOfNull;
300 // Move ctor for objects
301 /* implicit */ Variant(Object&& v) noexcept {
302 ObjectData *pobj = v.get();
303 if (pobj) {
304 m_type = KindOfObject;
305 m_data.pobj = pobj;
306 v.detach();
307 } else {
308 m_type = KindOfNull;
312 // Move ctor for resources
313 /* implicit */ Variant(Resource&& v) noexcept {
314 auto hdr = v.hdr();
315 if (hdr) {
316 m_type = KindOfResource;
317 m_data.pres = hdr;
318 v.detachHdr();
319 } else {
320 m_type = KindOfNull;
325 * Move assign
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;
342 } else {
343 lhs.m_type = rhs.m_type;
344 lhs.m_data = rhs.m_data;
345 rhs.m_type = KindOfNull;
348 return *this;
351 ALWAYS_INLINE ~Variant() noexcept {
352 tvDecRefGen(asTypedValue());
353 if (debug) {
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.
375 void unset() {
376 auto const old = *asTypedValue();
377 m_type = KindOfUninit;
378 tvDecRefGen(old);
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;
395 return *this;
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 ///////////////////////////////////////////////////////////////////////////////
428 // int64
430 ALWAYS_INLINE int64_t asInt64Val() const {
431 assert(m_type == KindOfInt64);
432 return m_data.num;
435 ALWAYS_INLINE int64_t toInt64Val() const {
436 assert(is(KindOfInt64));
437 return
438 LIKELY(m_type == KindOfInt64) ?
439 m_data.num : m_data.pref->var()->m_data.num;
442 ///////////////////////////////////////////////////////////////////////////////
443 // double
445 ALWAYS_INLINE double asDoubleVal() const {
446 assert(m_type == KindOfDouble);
447 return m_data.dbl;
450 ALWAYS_INLINE double toDoubleVal() const {
451 assert(is(KindOfDouble));
452 return
453 LIKELY(m_type == KindOfDouble) ?
454 m_data.dbl : m_data.pref->var()->m_data.dbl;
457 ///////////////////////////////////////////////////////////////////////////////
458 // boolean
460 ALWAYS_INLINE bool asBooleanVal() const {
461 assert(m_type == KindOfBoolean);
462 return m_data.num;
465 ALWAYS_INLINE bool toBooleanVal() const {
466 assert(is(KindOfBoolean));
467 return
468 LIKELY(m_type == KindOfBoolean) ?
469 m_data.num : m_data.pref->var()->m_data.num;
472 ///////////////////////////////////////////////////////////////////////////////
473 // string
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 {
481 assert(isString());
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() {
496 assert(isString());
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 ///////////////////////////////////////////////////////////////////////////////
506 // array
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 {
514 assert(isArray());
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() {
527 assert(isArray());
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 ///////////////////////////////////////////////////////////////////////////////
535 // object
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 {
585 return m_type;
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)
635 const;
636 bool isScalar() const noexcept;
637 bool isIntVal() const {
638 switch (m_type) {
639 case KindOfUninit:
640 case KindOfNull:
641 case KindOfBoolean:
642 case KindOfInt64:
643 case KindOfObject:
644 case KindOfResource:
645 return true;
646 case KindOfDouble:
647 case KindOfPersistentString:
648 case KindOfString:
649 case KindOfPersistentVec:
650 case KindOfVec:
651 case KindOfPersistentDict:
652 case KindOfDict:
653 case KindOfPersistentKeyset:
654 case KindOfKeyset:
655 case KindOfPersistentArray:
656 case KindOfArray:
657 return false;
658 case KindOfRef:
659 return m_data.pref->var()->isIntVal();
661 not_reached();
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;
693 * Operators
695 Variant& assign(const Variant& v) noexcept {
696 tvSet(tvToInitCell(*v.asTypedValue()), *asTypedValue());
697 return *this;
699 Variant& assignRef(Variant& v) noexcept {
700 assert(&v != &uninit_variant);
701 tvBoxIfNeeded(*v.asTypedValue());
702 tvBind(*v.asTypedValue(), *asTypedValue());
703 return *this;
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<
711 !std::is_same<
712 Variant,
713 typename std::remove_reference<typename std::remove_cv<T>::type>::type
714 >::value
716 !std::is_same<
717 VarNR,
718 typename std::remove_reference<typename std::remove_cv<T>::type>::type
719 >::value,
720 Variant&
721 >::type operator=(T&& v) {
722 set(std::forward<T>(v));
723 return *this;
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)) {
823 m_type = KindOfNull;
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
831 // PHP array.
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};
851 auto copy = *this;
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};
859 auto copy = *this;
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};
867 auto copy = *this;
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();
875 auto copy = *this;
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();
883 auto copy = *this;
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
891 isa() const {
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>();
898 return false;
901 template <typename T>
902 typename std::enable_if<std::is_base_of<ObjectData,T>::value, bool>::type
903 isa() const {
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>();
911 return false;
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 {
969 assert(isArray());
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
1017 * KindOfRef.
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>();
1032 return *this;
1036 * Access this Variant as a Ref, converting it to a Ref it isn't
1037 * one.
1039 Ref* asRef() { tvBoxIfNeeded(*asTypedValue()); return this; }
1041 TypedValue detach() noexcept {
1042 auto tv = *asTypedValue();
1043 m_type = KindOfNull;
1044 return tv;
1047 private:
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;
1068 return m_data.pobj;
1069 } else {
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,
1078 ResourceData*
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,
1084 ObjectData*
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,
1090 ResourceData*
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,
1096 ObjectData*
1097 >::type detach(Variant&& v) { return v.detachObjectData(); }
1099 explicit Variant(ResourceData* v) noexcept {
1100 if (v) {
1101 m_type = KindOfResource;
1102 m_data.pres = v->hdr();
1103 v->incRefCount();
1104 } else {
1105 m_type = KindOfNull;
1108 explicit Variant(ResourceHdr* v) noexcept {
1109 if (v) {
1110 m_type = KindOfResource;
1111 m_data.pres = v;
1112 v->incRefCount();
1113 } else {
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 {
1124 if (var) {
1125 m_type = var->isRefCounted() ? KindOfString : KindOfPersistentString;
1126 m_data.pstr = var;
1127 } else {
1128 m_type = KindOfNull;
1131 Variant(ArrayData* var, Attach) noexcept {
1132 if (var) {
1133 m_type =
1134 var->isRefCounted() ? var->toDataType() : var->toPersistentDataType();
1135 m_data.parr = var;
1136 } else {
1137 m_type = KindOfNull;
1140 Variant(ObjectData* var, Attach) noexcept {
1141 if (var) {
1142 m_type = KindOfObject;
1143 m_data.pobj = var;
1144 } else {
1145 m_type = KindOfNull;
1148 Variant(ResourceData* var, Attach) noexcept {
1149 if (var) {
1150 m_type = KindOfResource;
1151 m_data.pres = var->hdr();
1152 } else {
1153 m_type = KindOfNull;
1156 Variant(ResourceHdr* var, Attach) noexcept {
1157 if (var) {
1158 m_type = KindOfResource;
1159 m_data.pres = var;
1160 } else {
1161 m_type = KindOfNull;
1164 Variant(RefData* var, Attach) noexcept {
1165 if (var) {
1166 m_type = KindOfRef;
1167 m_data.pref = var;
1168 } else {
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()); }
1231 private:
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; }
1259 private:
1260 Variant 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);
1325 private:
1326 Variant m_var;
1329 ///////////////////////////////////////////////////////////////////////////////
1330 // VarNR
1332 struct VarNR : private TypedValueAux {
1333 static VarNR MakeKey(const String& s) {
1334 if (s.empty()) return VarNR(staticEmptyString());
1335 int64_t n;
1336 if (UNLIKELY(s.get()->isStrictlyInteger(n))) {
1337 if (RuntimeOption::EvalHackArrCompatNotices) {
1338 raise_intish_index_cast();
1340 return VarNR(n);
1342 return VarNR(s);
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; }
1381 ~VarNR() {
1382 if (debug) {
1383 checkRefCount();
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();
1395 private:
1396 /* implicit */ VarNR(const char* v) = delete;
1397 /* implicit */ VarNR(const std::string & v) = delete;
1399 void init(DataType dt) {
1400 m_type = 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);
1412 switch (m_type) {
1413 DT_UNCOUNTED_CASE:
1414 return;
1415 case KindOfString:
1416 assert(m_data.pstr->checkCount());
1417 return;
1418 case KindOfVec:
1419 case KindOfDict:
1420 case KindOfKeyset:
1421 case KindOfArray:
1422 assert(m_data.parr->checkCount());
1423 return;
1424 case KindOfObject:
1425 assert(m_data.pobj->checkCount());
1426 return;
1427 case KindOfResource:
1428 assert(m_data.pres->checkCount());
1429 return;
1430 case KindOfRef:
1431 assert(m_data.pref->checkCount());
1432 return;
1434 not_reached();
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() {
1483 return Variant();
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()) {
1496 str += s2.slice();
1497 return v1;
1501 auto s1 = v1.toString();
1502 s1 += s2;
1503 v1 = s1;
1504 return v1;
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);
1551 template <>
1552 inline bool is_null(const Variant& v) {
1553 return v.isNull();
1556 template <typename T>
1557 inline bool isa_non_null(const Variant& v) {
1558 return v.isa<T>();
1561 // Defined here to avoid introducing a dependency cycle between type-variant
1562 // and type-array
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_