Clean up, part 11: gut array provenance
[hiphop-php.git] / hphp / runtime / base / type-variant.h
blob82b8d8022ef7296d930fbd915ddd7ee75553e396
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 #pragma once
19 #include "hphp/runtime/base/array-data.h"
20 #include "hphp/runtime/base/record-data.h"
21 #include "hphp/runtime/base/tv-conversions.h"
22 #include "hphp/runtime/base/tv-mutate.h"
23 #include "hphp/runtime/base/tv-refcount.h"
24 #include "hphp/runtime/base/tv-val.h"
25 #include "hphp/runtime/base/tv-variant.h"
26 #include "hphp/runtime/base/type-object.h"
27 #include "hphp/runtime/base/type-resource.h"
28 #include "hphp/runtime/base/type-string.h"
29 #include "hphp/runtime/base/typed-value.h"
30 #include "hphp/runtime/vm/class-meth-data-ref.h"
31 #include "hphp/runtime/vm/rclass-meth-data.h"
32 #include "hphp/runtime/vm/rfunc-data.h"
34 #include <algorithm>
35 #include <type_traits>
37 namespace HPHP {
38 ///////////////////////////////////////////////////////////////////////////////
40 // Forward declare these to avoid including tv-conversions.h which has a
41 // circular dependency with this file.
43 struct OptionalVariant;
46 * These structs are substitutes for Variant& or const Variant&, when there's
47 * no actual Variant in memory to take a reference to (when working with a
48 * tv_lval from an Array, for example). They should be treated with the same
49 * care as normal references: when copying or storing a variant_ref or
50 * const_variant_ref, think carefully about the lifetime of the underlying
51 * data.
53 namespace variant_ref_detail {
54 template<bool is_const>
55 struct base {
56 private:
57 using tv_val_t = typename std::conditional<is_const, tv_rval, tv_lval>::type;
59 public:
60 explicit base(tv_val_t val) : m_val{val} {}
62 DataType getType() const {
63 return type(m_val);
65 bool isNull() const { return isNullType(getType()); }
66 bool isBoolean() const { return isBooleanType(getType()); }
67 bool isInteger() const { return isIntType(getType()); }
68 bool isDouble() const { return isDoubleType(getType()); }
69 bool isString() const { return isStringType(getType()); }
70 bool isArray() const { return isArrayLikeType(getType()); }
71 bool isVec() const { return isVecType(getType()); }
72 bool isDict() const { return isDictType(getType()); }
73 bool isKeyset() const { return isKeysetType(getType()); }
74 bool isObject() const { return isObjectType(getType()); }
75 bool isResource() const { return isResourceType(getType()); }
76 bool isFunc() const { return isFuncType(getType()); }
77 bool isClass() const { return isClassType(getType()); }
78 bool isLazyClass() const { return isLazyClassType(getType()); }
79 bool isClsMeth() const { return isClsMethType(getType()); }
81 bool isPrimitive() const { return !isRefcountedType(type(m_val)); }
83 auto toBoolean() const { return tvCastToBoolean(*m_val); }
84 auto toInt64() const { return tvCastToInt64(*m_val); }
85 auto toDouble() const { return tvCastToDouble(*m_val); }
86 auto toString(ConvNoticeLevel level = ConvNoticeLevel::None,
87 const StringData* notice_reason = nullptr) const {
88 if (isStringType(type(m_val))) return String{val(m_val).pstr};
89 return String::attach(tvCastToStringData(*m_val, level, notice_reason));
91 auto toArray() const {
92 if (isArrayLikeType(type(m_val))) return Array{val(m_val).parr};
93 return Array::attach(tvCastToArrayLikeData<IntishCast::None>(*m_val));
95 auto toObject() const {
96 if (isObjectType(type(m_val))) return Object{val(m_val).pobj};
97 return Object::attach(tvCastToObjectData(*m_val));
100 auto& asCStrRef() const { return HPHP::asCStrRef(m_val); }
101 auto& asCArrRef() const { return HPHP::asCArrRef(m_val); }
102 auto& asCObjRef() const { return HPHP::asCObjRef(m_val); }
104 tv_rval asTypedValue() const { return m_val; }
106 ArrayData *getArrayData() const {
107 assertx(isArray());
108 return val(m_val).parr;
111 auto toFuncVal() const {
112 assertx(isFunc());
113 return val(m_val).pfunc;
116 auto toClassVal() const {
117 assertx(isClass());
118 return val(m_val).pclass;
121 auto toLazyClassVal() const {
122 assertx(isLazyClass());
123 return val(m_val).plazyclass;
126 ClsMethDataRef toClsMethVal() const {
127 assertx(isClsMeth());
128 return val(m_val).pclsmeth;
131 RefCount getRefCount() const noexcept {
132 return isRefcountedType(type(m_val)) || hasPersistentFlavor(type(m_val))
133 ? val(m_val).pcnt->count()
134 : OneReference;
137 protected:
138 const tv_val_t m_val;
142 struct const_variant_ref;
144 struct variant_ref : variant_ref_detail::base<false> {
145 using variant_ref_detail::base<false>::base;
147 /* implicit */ variant_ref(Variant& v);
149 /* implicit */ operator const_variant_ref() const;
151 tv_lval lval() const { return m_val; }
153 void unset() const {
154 tvMove(make_tv<KindOfUninit>(), m_val);
157 void setNull() noexcept {
158 tvSetNull(m_val);
161 variant_ref& operator=(const Variant& v) noexcept;
163 variant_ref& assign(const Variant& v) noexcept;
165 variant_ref& operator=(Variant &&rhs) noexcept;
167 // Generic assignment operator. Forward argument (preserving rvalue-ness and
168 // lvalue-ness) to the appropriate set function, as long as its not a Variant.
169 template <typename T>
170 typename std::enable_if<
171 !std::is_same<
172 Variant,
173 typename std::remove_reference<typename std::remove_cv<T>::type>::type
174 >::value
176 !std::is_same<
177 VarNR,
178 typename std::remove_reference<typename std::remove_cv<T>::type>::type
179 >::value,
180 variant_ref&
181 >::type operator=(T&& v) {
182 set(std::forward<T>(v));
183 return *this;
186 void set(bool v) noexcept;
187 void set(int v) noexcept;
188 void set(int64_t v) noexcept;
189 void set(double v) noexcept;
190 void set(const char* v) = delete;
191 void set(const std::string & v) {
192 return set(String(v));
194 void set(StringData *v) noexcept;
195 void set(ArrayData *v) noexcept;
196 void set(ObjectData *v) noexcept;
197 void set(ResourceHdr *v) noexcept;
198 void set(ResourceData *v) noexcept { set(v->hdr()); }
199 void set(const StringData *v) = delete;
200 void set(const ArrayData *v) = delete;
201 void set(const ObjectData *v) = delete;
202 void set(const ResourceData *v) = delete;
203 void set(const ResourceHdr *v) = delete;
205 void set(const String& v) noexcept { set(v.get()); }
206 void set(const StaticString & v) noexcept;
207 void set(const Array& v) noexcept { set(v.get()); }
208 void set(const Object& v) noexcept { set(v.get()); }
209 void set(const Resource& v) noexcept { set(v.hdr()); }
211 void set(String&& v) noexcept { steal(v.detach()); }
212 void set(Array&& v) noexcept { steal(v.detach()); }
213 void set(Object&& v) noexcept { steal(v.detach()); }
214 void set(Resource&& v) noexcept { steal(v.detachHdr()); }
216 template<typename T>
217 void set(const req::ptr<T> &v) noexcept {
218 return set(v.get());
221 template <typename T>
222 void set(req::ptr<T>&& v) noexcept {
223 return steal(v.detach());
226 void steal(StringData* v) noexcept;
227 void steal(ArrayData* v) noexcept;
228 void steal(ObjectData* v) noexcept;
229 void steal(ResourceHdr* v) noexcept;
230 void steal(ResourceData* v) noexcept { steal(v->hdr()); }
233 struct const_variant_ref : variant_ref_detail::base<true> {
234 using variant_ref_detail::base<true>::base;
236 /* implicit */ const_variant_ref(const Variant& v);
238 tv_rval rval() const { return m_val; }
241 inline variant_ref::operator const_variant_ref() const {
242 return const_variant_ref{m_val};
246 * This class predates HHVM.
248 * In hphpc, when type inference failed to know type of a variable, we
249 * would use Variant to represent the php variable in generated C++.
251 * Now Variant is only used in C++ extensions, and the API is mostly
252 * legacy stuff. If you're writing a C++ extension, try to avoid
253 * Variant when you can (but you often can't, and we don't really have
254 * a replacement yet, sorry).
256 * In C++ extensions, this class can be used as a generic handle to
257 * one of our other data types (e.g. StringData, ArrayData)
259 * Beware:
261 * For historical reasons, this class does a lot of things you
262 * don't really expect in a well-behaved C++ class.
264 * For example, the copy constructor is not a copy constructor (it converts
265 * KindOfUninit to KindOfNull). A similar story applies to the move
266 * constructor. (And this means we may actually rely on whether copy
267 * elision (NRVO etc) is happening in some places for correctness.)
269 * Use carefully.
273 struct Variant : private TypedValue {
274 friend variant_ref;
276 // Used by VariantTraits to create a folly::Optional-like
277 // optional Variant which fits in 16 bytes.
278 using Optional = OptionalVariant;
280 enum class NullInit {};
281 enum class NoInit {};
282 enum class TVCopy {};
283 enum class TVDup {};
284 enum class ArrayInitCtor {};
285 enum class Attach {};
286 enum class Wrap {};
288 Variant() noexcept { m_type = KindOfUninit; }
289 explicit Variant(NullInit) noexcept { m_type = KindOfNull; }
290 explicit Variant(NoInit) noexcept {}
292 /* implicit */ Variant(bool v) noexcept {
293 m_type = KindOfBoolean; m_data.num = v;
295 /* implicit */ Variant(int v) noexcept {
296 m_type = KindOfInt64; m_data.num = v;
298 // The following two overloads will accept int64_t whether it's
299 // implemented as long or long long.
300 /* implicit */ Variant(long v) noexcept {
301 m_type = KindOfInt64; m_data.num = v;
303 /* implicit */ Variant(long long v) noexcept {
304 m_type = KindOfInt64; m_data.num = v;
306 /* implicit */ Variant(unsigned v) noexcept {
307 m_type = KindOfInt64; m_data.num = v;
309 /* implicit */ Variant(unsigned long v) noexcept {
310 m_type = KindOfInt64; m_data.num = v;
312 /* implicit */ Variant(unsigned long long v) noexcept {
313 m_type = KindOfInt64; m_data.num = v;
315 /* implicit */ Variant(double v) noexcept {
316 m_type = KindOfDouble; m_data.dbl = v;
319 /* implicit */ Variant(const char* v) {
320 m_type = KindOfString;
321 m_data.pstr = StringData::Make(v);
323 /* implicit */ Variant(const std::string &v) {
324 m_type = KindOfString;
325 StringData *s = StringData::Make(v.c_str(), v.size(), CopyString);
326 assertx(s);
327 m_data.pstr = s;
329 /* implicit */ Variant(const StaticString &v) noexcept {
330 assertx(v.get() && !v.get()->isRefCounted());
331 m_type = KindOfPersistentString;
332 m_data.pstr = v.get();
335 /* implicit */ Variant(const String& v) noexcept : Variant(v.get()) {}
336 /* implicit */ Variant(const Array& v) noexcept : Variant(v.get()) { }
337 /* implicit */ Variant(const Object& v) noexcept : Variant(v.get()) {}
338 /* implicit */ Variant(const Resource& v) noexcept
339 : Variant(v.hdr()) {}
341 /* implicit */ Variant(Class* v) {
342 m_type = KindOfClass;
343 m_data.pclass = v;
346 /* implicit */ Variant(LazyClassData v) {
347 m_type = KindOfLazyClass;
348 m_data.plazyclass = v;
351 /* implicit */ Variant(const ClsMethDataRef v) {
352 m_type = KindOfClsMeth;
353 m_data.pclsmeth = v;
354 incRefClsMeth(v);
358 * Explicit conversion constructors. These all manipulate ref-counts of bare
359 * pointers as a side-effect, so we want to be explicit when its happening.
361 explicit Variant(StringData* v) noexcept;
362 explicit Variant(ArrayData* v) noexcept {
363 if (v) {
364 m_data.parr = v;
365 if (v->isRefCounted()) {
366 m_type = v->toDataType();
367 v->rawIncRefCount();
368 } else {
369 m_type = v->toPersistentDataType();
371 } else {
372 m_type = KindOfNull;
375 explicit Variant(ObjectData* v) noexcept {
376 if (v) {
377 m_type = KindOfObject;
378 m_data.pobj = v;
379 v->incRefCount();
380 } else {
381 m_type = KindOfNull;
385 explicit Variant(const Func* f) noexcept {
386 assertx(f);
387 m_type = KindOfFunc;
388 m_data.pfunc = f;
391 explicit Variant(RFuncData* v) noexcept {
392 if (v) {
393 assertx(v);
394 m_type = KindOfRFunc;
395 m_data.prfunc = v;
396 v->incRefCount();
397 } else {
398 m_type = KindOfNull;
402 explicit Variant(RClsMethData* v) noexcept {
403 if (v) {
404 assertx(v);
405 m_type = KindOfRClsMeth;
406 m_data.prclsmeth = v;
407 v->incRefCount();
408 } else {
409 m_type = KindOfNull;
413 template <typename T>
414 explicit Variant(const req::ptr<T>& ptr) : Variant(ptr.get()) { }
415 template <typename T>
416 explicit Variant(req::ptr<T>&& ptr) noexcept
417 : Variant(ptr.detach(), Attach{}) { }
420 * Creation constructor from ArrayInit that avoids a null check and an
421 * inc-ref.
423 explicit Variant(ArrayData* ad, DataType dt, ArrayInitCtor) noexcept {
424 assertx(ad->toDataType() == dt);
425 m_type = dt;
426 m_data.parr = ad;
429 enum class PersistentArrInit {};
430 Variant(const ArrayData* ad, DataType dt, PersistentArrInit) noexcept {
431 // TODO(T58820726): Switch back to trusting caller and strict equality.
432 assertx(equivDataTypes(ad->toPersistentDataType(), dt));
433 assertx(!ad->isRefCounted());
434 m_data.parr = const_cast<ArrayData*>(ad);
435 m_type = dt;
438 enum class PersistentStrInit {};
439 explicit Variant(const StringData *s, PersistentStrInit) noexcept {
440 assertx(!s->isRefCounted());
441 m_data.pstr = const_cast<StringData*>(s);
442 m_type = KindOfPersistentString;
445 // These are prohibited, but declared just to prevent accidentally
446 // calling the bool constructor just because we had a pointer to
447 // const.
448 /* implicit */ Variant(const void*) = delete;
449 template<typename Ret, typename... Args>
450 /* implicit */ Variant(Ret (*)(Args...)) = delete;
451 template<class Class, typename Ret, typename... Args>
452 /* implicit */ Variant(Ret (Class::*)(Args...)) = delete;
453 template<class Class, typename Ret, typename... Args>
454 /* implicit */ Variant(Ret (Class::*)(Args...) const) = delete;
456 //////////////////////////////////////////////////////////////////////
459 * Copy constructor and copy assignment do not semantically make
460 * copies: they turn uninits to null.
463 Variant(const Variant& v) noexcept;
464 explicit Variant(const_variant_ref v) noexcept;
466 Variant(const Variant& v, TVCopy) noexcept {
467 m_type = v.m_type;
468 m_data = v.m_data;
471 Variant(const Variant& v, TVDup) noexcept {
472 m_type = v.m_type;
473 m_data = v.m_data;
474 tvIncRefGen(*asTypedValue());
477 Variant& operator=(const Variant& v) noexcept {
478 return assign(v);
482 * Move ctors
484 * Note: not semantically moves. Like our "copy constructor", these
485 * turn uninits to null.
488 Variant(Variant&& v) noexcept {
489 assertx(this != &v);
490 if (v.m_type != KindOfUninit) {
491 m_type = v.m_type;
492 m_data = v.m_data;
493 v.m_type = KindOfNull;
494 } else {
495 m_type = KindOfNull;
499 // Move ctor for strings
500 /* implicit */ Variant(String&& v) noexcept {
501 StringData *s = v.get();
502 if (LIKELY(s != nullptr)) {
503 m_data.pstr = s;
504 m_type = s->isRefCounted() ? KindOfString : KindOfPersistentString;
505 v.detach();
506 } else {
507 m_type = KindOfNull;
511 // Move ctor for arrays
512 /* implicit */ Variant(Array&& v) noexcept {
513 ArrayData *a = v.get();
514 if (LIKELY(a != nullptr)) {
515 m_data.parr = a;
516 m_type = a->isRefCounted() ? a->toDataType() : a->toPersistentDataType();
517 v.detach();
518 } else {
519 m_type = KindOfNull;
523 // Move ctor for objects
524 /* implicit */ Variant(Object&& v) noexcept {
525 ObjectData *pobj = v.get();
526 if (pobj) {
527 m_type = KindOfObject;
528 m_data.pobj = pobj;
529 v.detach();
530 } else {
531 m_type = KindOfNull;
535 // Move ctor for resources
536 /* implicit */ Variant(Resource&& v) noexcept {
537 auto hdr = v.hdr();
538 if (hdr) {
539 m_type = KindOfResource;
540 m_data.pres = hdr;
541 v.detachHdr();
542 } else {
543 m_type = KindOfNull;
548 * Move assign
550 * Note: not semantically moves. Like our "copies", these turn uninits
551 * to null.
553 Variant& operator=(Variant &&rhs) noexcept {
554 assertx(this != &rhs); // we end up as null on a self move-assign.
555 Variant& lhs = *this;
557 Variant goner((NoInit()));
558 goner.m_data = lhs.m_data;
559 goner.m_type = lhs.m_type;
561 if (rhs.m_type == KindOfUninit) {
562 lhs.m_type = KindOfNull;
563 } else {
564 lhs.m_type = rhs.m_type;
565 lhs.m_data = rhs.m_data;
566 rhs.m_type = KindOfNull;
569 return *this;
572 ALWAYS_INLINE ~Variant() noexcept {
573 tvDecRefGen(asTypedValue());
574 if (debug) {
575 memset(this, kTVTrashFill2, sizeof(*this));
580 //////////////////////////////////////////////////////////////////////
583 * During sweeping, request-allocated things are not allowed to be decref'd
584 * or manipulated. This function is used to cause a Variant to go
585 * into a state where its destructor will have no effects on the
586 * request local heap, in cases where sweepable objects can't
587 * organize things to avoid running Variant destructors.
589 void releaseForSweep() { m_type = KindOfNull; }
591 //////////////////////////////////////////////////////////////////////
594 * Break bindings and set to uninit.
596 void unset() {
597 auto const old = *asTypedValue();
598 m_type = KindOfUninit;
599 tvDecRefGen(old);
603 * set to null without breaking bindings (if any), faster than v_a = null;
605 void setNull() noexcept {
606 tvSetNull(*asTypedValue());
609 static Variant attach(TypedValue tv) noexcept {
610 return Variant{tv, Attach{}};
612 static Variant attach(StringData* var) noexcept {
613 return Variant{var, Attach{}};
615 static Variant attach(ArrayData* var) noexcept {
616 return Variant{var, Attach{}};
618 static Variant attach(ObjectData* var) noexcept {
619 return Variant{var, Attach{}};
621 static Variant attach(ResourceData* var) noexcept {
622 return Variant{var, Attach{}};
624 static Variant attach(ResourceHdr* var) noexcept {
625 return Variant{var, Attach{}};
628 static Variant wrap(TypedValue tv) noexcept {
629 return Variant{tv, Wrap{}};
632 ///////////////////////////////////////////////////////////////////////////////
633 // int64
635 ALWAYS_INLINE int64_t asInt64Val() const {
636 assertx(m_type == KindOfInt64);
637 return m_data.num;
640 ALWAYS_INLINE int64_t toInt64Val() const {
641 assertx(is(KindOfInt64));
642 return m_data.num;
645 ///////////////////////////////////////////////////////////////////////////////
646 // double
648 ALWAYS_INLINE double asDoubleVal() const {
649 assertx(m_type == KindOfDouble);
650 return m_data.dbl;
653 ALWAYS_INLINE double toDoubleVal() const {
654 assertx(is(KindOfDouble));
655 return m_data.dbl;
658 ///////////////////////////////////////////////////////////////////////////////
659 // boolean
661 ALWAYS_INLINE bool asBooleanVal() const {
662 assertx(m_type == KindOfBoolean);
663 return m_data.num;
666 ALWAYS_INLINE bool toBooleanVal() const {
667 assertx(is(KindOfBoolean));
668 return m_data.num;
671 ///////////////////////////////////////////////////////////////////////////////
672 // string
674 ALWAYS_INLINE const String& asCStrRef() const {
675 assertx(isStringType(m_type) && m_data.pstr);
676 return *reinterpret_cast<const String*>(&m_data.pstr);
679 ALWAYS_INLINE String& asStrRef() {
680 assertx(isStringType(m_type) && m_data.pstr);
681 // The caller is likely going to modify the string, so we have to eagerly
682 // promote KindOfPersistentString -> KindOfString.
683 m_type = KindOfString;
684 return *reinterpret_cast<String*>(&m_data.pstr);
687 ///////////////////////////////////////////////////////////////////////////////
688 // array
690 ALWAYS_INLINE const Array& asCArrRef() const {
691 assertx(isArrayLikeType(m_type) && m_data.parr);
692 return *reinterpret_cast<const Array*>(&m_data.parr);
695 ALWAYS_INLINE Array& asArrRef() {
696 assertx(isArrayLikeType(m_type) && m_data.parr);
697 m_type = m_data.parr->toDataType();
698 return *reinterpret_cast<Array*>(&m_data.parr);
701 ///////////////////////////////////////////////////////////////////////////////
702 // object
704 ALWAYS_INLINE const Object& asCObjRef() const {
705 assertx(m_type == KindOfObject && m_data.pobj);
706 return *reinterpret_cast<const Object*>(&m_data.pobj);
709 ALWAYS_INLINE Object & asObjRef() {
710 assertx(m_type == KindOfObject && m_data.pobj);
711 return *reinterpret_cast<Object*>(&m_data.pobj);
714 ALWAYS_INLINE const Resource& asCResRef() const {
715 assertx(m_type == KindOfResource && m_data.pres);
716 return *reinterpret_cast<const Resource*>(&m_data.pres);
719 ALWAYS_INLINE const Resource& toCResRef() const {
720 assertx(is(KindOfResource));
721 assertx(m_data.pres);
722 return *reinterpret_cast<const Resource*>(&m_data.pres);
725 ALWAYS_INLINE Resource & asResRef() {
726 assertx(m_type == KindOfResource && m_data.pres);
727 return *reinterpret_cast<Resource*>(&m_data.pres);
731 * Type testing functions
733 DataType getType() const {
734 return m_type;
736 bool is(DataType type) const {
737 return getType() == type;
739 bool isInitialized() const {
740 return m_type != KindOfUninit;
742 bool isNull() const {
743 return isNullType(getType());
745 bool isBoolean() const {
746 return getType() == KindOfBoolean;
748 bool isInteger() const {
749 return getType() == KindOfInt64;
751 bool isDouble() const {
752 return getType() == KindOfDouble;
754 bool isString() const {
755 return isStringType(getType());
757 bool isArray() const {
758 return isArrayLikeType(getType());
760 bool isVec() const {
761 return isVecType(getType());
763 bool isDict() const {
764 return isDictType(getType());
766 bool isKeyset() const {
767 return isKeysetType(getType());
769 bool isObject() const {
770 return getType() == KindOfObject;
772 bool isResource() const {
773 return getType() == KindOfResource;
775 bool isFunc() const {
776 return isFuncType(getType());
778 bool isClass() const {
779 return isClassType(getType());
781 bool isLazyClass() const {
782 return isLazyClassType(getType());
784 bool isClsMeth() const {
785 return isClsMethType(getType());
788 bool isNumeric(bool checkString = false) const noexcept;
789 DataType toNumeric(int64_t &ival, double &dval, bool checkString = false)
790 const;
791 bool isScalar() const noexcept;
792 bool isIntVal() const {
793 switch (m_type) {
794 case KindOfUninit:
795 case KindOfNull:
796 case KindOfBoolean:
797 case KindOfInt64:
798 case KindOfObject:
799 case KindOfResource:
800 return true;
801 case KindOfDouble:
802 case KindOfPersistentString:
803 case KindOfString:
804 case KindOfPersistentVec:
805 case KindOfVec:
806 case KindOfPersistentDict:
807 case KindOfDict:
808 case KindOfPersistentKeyset:
809 case KindOfKeyset:
810 case KindOfRFunc:
811 case KindOfFunc:
812 case KindOfClass:
813 case KindOfLazyClass:
814 case KindOfClsMeth:
815 case KindOfRClsMeth:
816 case KindOfRecord:
817 return false;
819 not_reached();
822 // Is "define('CONSTANT', <this value>)" legal?
823 enum class AllowedAsConstantValue {
824 Allowed
825 , NotAllowed
826 , ContainsObject // Allowed if constant of an "enum class".
828 AllowedAsConstantValue isAllowedAsConstantValue() const;
831 * Get reference count of weak or strong binding. For debugging purpose.
833 int getRefCount() const noexcept {
834 return const_variant_ref{*this}.getRefCount();
837 bool getBoolean() const {
838 assertx(getType() == KindOfBoolean);
839 return m_data.num;
841 int64_t getInt64() const {
842 assertx(getType() == KindOfInt64);
843 return m_data.num;
845 double getDouble() const {
846 assertx(getType() == KindOfDouble);
847 return m_data.dbl;
851 * Operators
853 Variant& assign(const Variant& v) noexcept {
854 tvSet(tvToInit(*v.asTypedValue()), *asTypedValue());
855 return *this;
858 // Generic assignment operator. Forward argument (preserving rvalue-ness and
859 // lvalue-ness) to the appropriate set function, as long as its not a Variant.
860 template <typename T>
861 typename std::enable_if<
862 !std::is_same<
863 Variant,
864 typename std::remove_reference<typename std::remove_cv<T>::type>::type
865 >::value
867 !std::is_same<
868 VarNR,
869 typename std::remove_reference<typename std::remove_cv<T>::type>::type
870 >::value,
871 Variant&
872 >::type operator=(T&& v) {
873 set(std::forward<T>(v));
874 return *this;
877 Variant operator + () const = delete;
878 Variant &operator += (const Variant& v) = delete;
879 Variant &operator += (int n) = delete;
880 Variant &operator += (int64_t n) = delete;
881 Variant &operator += (double n) = delete;
883 Variant operator - () const = delete;
884 Variant operator - (const Variant& v) const = delete;
885 Variant &operator -= (const Variant& v) = delete;
886 Variant &operator -= (int n) = delete;
887 Variant &operator -= (int64_t n) = delete;
888 Variant &operator -= (double n) = delete;
890 Variant operator * (const Variant& v) const = delete;
891 Variant &operator *= (const Variant& v) = delete;
892 Variant &operator *= (int n) = delete;
893 Variant &operator *= (int64_t n) = delete;
894 Variant &operator *= (double n) = delete;
896 Variant operator / (const Variant& v) const = delete;
897 Variant &operator /= (const Variant& v) = delete;
898 Variant &operator /= (int n) = delete;
899 Variant &operator /= (int64_t n) = delete;
900 Variant &operator /= (double n) = delete;
902 int64_t operator % (const Variant& v) const = delete;
903 Variant &operator %= (const Variant& v) = delete;
904 Variant &operator %= (int n) = delete;
905 Variant &operator %= (int64_t n) = delete;
906 Variant &operator %= (double n) = delete;
908 Variant operator | (const Variant& v) const = delete;
909 Variant &operator |= (const Variant& v) = delete;
910 Variant operator & (const Variant& v) const = delete;
911 Variant &operator &= (const Variant& v) = delete;
912 Variant operator ^ (const Variant& v) const = delete;
913 Variant &operator ^= (const Variant& v) = delete;
914 Variant &operator <<=(int64_t n) = delete;
915 Variant &operator >>=(int64_t n) = delete;
917 Variant &operator ++ () = delete;
918 Variant operator ++ (int) = delete;
919 Variant &operator -- () = delete;
920 Variant operator -- (int) = delete;
923 * Explicit type conversions
925 bool toBoolean() const {
926 if (isNullType(m_type)) return false;
927 if (hasNumData(m_type)) return m_data.num;
928 return toBooleanHelper();
930 char toByte() const { return (char)toInt64();}
931 short toInt16(int base = 10) const { return (short)toInt64(base);}
932 int toInt32(int base = 10) const { return (int)toInt64(base);}
933 int64_t toInt64() const {
934 if (isNullType(m_type)) return 0;
935 if (hasNumData(m_type)) return m_data.num;
936 return toInt64Helper(10);
938 int64_t toInt64(int base) const {
939 if (isNullType(m_type)) return 0;
940 if (hasNumData(m_type)) return m_data.num;
941 return toInt64Helper(base);
943 double toDouble() const {
944 if (m_type == KindOfDouble) return m_data.dbl;
945 return tvCastToDouble(*asTypedValue());
948 String toString(ConvNoticeLevel level = ConvNoticeLevel::None,
949 const StringData* notice_reason = nullptr) const& {
950 if (isStringType(m_type)) return String{m_data.pstr};
951 return String::attach(tvCastToStringData(*this, level, notice_reason));
954 String toString(ConvNoticeLevel level = ConvNoticeLevel::None,
955 const StringData* notice_reason = nullptr) && {
956 if (isStringType(m_type)) {
957 m_type = KindOfNull;
958 return String::attach(m_data.pstr);
960 return toString(level, notice_reason);
963 // Convert a non-array-like type to a PHP array, leaving PHP arrays and Hack
964 // arrays unchanged. Use toPHPArray() if you want the result to always be a
965 // PHP array.
966 template <IntishCast IC = IntishCast::None>
967 Array toArray() const {
968 if (isArrayLikeType(m_type)) return Array{m_data.parr};
969 return Array::attach(tvCastToArrayLikeData<IC>(*this));
971 Array toPHPArray() const {
972 return toPHPArrayHelper();
974 Object toObject() const {
975 if (isObjectType(m_type)) return Object{m_data.pobj};
976 return Object::attach(tvCastToObjectData(*this));
978 Resource toResource() const {
979 if (m_type == KindOfResource) return Resource{m_data.pres};
980 return toResourceHelper();
983 Array toVec() const {
984 if (isVecType(m_type)) return Array{m_data.parr};
985 auto copy = *this;
986 tvCastToVecInPlace(copy.asTypedValue());
987 assertx(copy.isVec());
988 return Array::attach(copy.detach().m_data.parr);
991 Array toDict() const {
992 if (isDictType(m_type)) return Array{m_data.parr};
993 auto copy = *this;
994 tvCastToDictInPlace(copy.asTypedValue());
995 assertx(copy.isDict());
996 return Array::attach(copy.detach().m_data.parr);
999 Array toKeyset() const {
1000 if (isKeysetType(m_type)) return Array{m_data.parr};
1001 auto copy = *this;
1002 tvCastToKeysetInPlace(copy.asTypedValue());
1003 assertx(copy.isKeyset());
1004 return Array::attach(copy.detach().m_data.parr);
1007 Array toVArray() const { return toVec(); }
1008 Array toDArray() const { return toDict(); }
1010 template <typename T>
1011 typename std::enable_if<std::is_base_of<ResourceData,T>::value, bool>::type
1012 isa() const {
1013 if (m_type == KindOfResource) {
1014 return m_data.pres->data()->instanceof<T>();
1016 return false;
1019 template <typename T>
1020 typename std::enable_if<std::is_base_of<ObjectData,T>::value, bool>::type
1021 isa() const {
1022 if (m_type == KindOfObject) {
1023 return m_data.pobj->instanceof<T>();
1025 return false;
1029 * Convert to a valid key or throw an exception. If convertStrKeys is true
1030 * int-like string keys will be converted to int keys.
1032 template <IntishCast IC = IntishCast::None>
1033 VarNR toKey(const ArrayData*) const;
1035 /* Creating a temporary Array, String, or Object with no ref-counting and
1036 * no type checking, use it only when we have checked the variant type and
1037 * we are sure the internal data will have a reference until the temporary
1038 * one gets out-of-scope.
1040 StrNR toStrNR() const {
1041 return StrNR(getStringData());
1043 ArrNR toArrNR() const {
1044 return ArrNR(getArrayData());
1046 ObjNR toObjNR() const {
1047 return ObjNR(getObjectData());
1050 auto toFuncVal() const {
1051 return const_variant_ref{*this}.toFuncVal();
1053 auto toClassVal() const {
1054 return const_variant_ref{*this}.toClassVal();
1056 auto toLazyClassVal() const {
1057 return const_variant_ref{*this}.toLazyClassVal();
1059 ClsMethDataRef toClsMethVal() const {
1060 return const_variant_ref{*this}.toClsMethVal();
1064 * Low level access that should be restricted to internal use.
1066 int64_t *getInt64Data() const {
1067 assertx(getType() == KindOfInt64);
1068 return const_cast<int64_t*>(&m_data.num);
1070 double *getDoubleData() const {
1071 assertx(getType() == KindOfDouble);
1072 return const_cast<double*>(&m_data.dbl);
1074 StringData *getStringData() const {
1075 assertx(isStringType(getType()));
1076 return m_data.pstr;
1078 StringData *getStringDataOrNull() const {
1079 // This is a necessary evil because getStringData() returns
1080 // an undefined result if this is a null variant
1081 assertx(isNull() || isString());
1082 return isNullType(m_type) ? nullptr : m_data.pstr;
1084 ArrayData *getArrayData() const {
1085 assertx(isArray());
1086 return m_data.parr;
1088 ArrayData *getArrayDataOrNull() const {
1089 // This is a necessary evil because getArrayData() returns
1090 // an undefined result if this is a null variant
1091 assertx(isNull() || isArray());
1092 return isNullType(m_type) ? nullptr : m_data.parr;
1094 ObjectData* getObjectData() const {
1095 assertx(is(KindOfObject));
1096 return m_data.pobj;
1098 ObjectData *getObjectDataOrNull() const {
1099 // This is a necessary evil because getObjectData() returns
1100 // an undefined result if this is a null variant
1101 assertx(isNull() || is(KindOfObject));
1102 return isNullType(m_type) ? nullptr : m_data.pobj;
1105 int64_t getNumData() const { return m_data.num; }
1108 * Make any Variant strings and arrays not ref counted (e.g., static).
1109 * Use it, for example, if you need a long-lived Variant before the Memory
1110 * Manager has been initialized.
1111 * You will still get an assertion if the Variant is an object, resource, etc.
1113 void setEvalScalar();
1116 * Access this Variant as a TypedValue.
1118 const TypedValue* asTypedValue() const { return this; }
1119 TypedValue* asTypedValue() { return this; }
1122 * Read this Variant as an InitCell, without incrementing the
1123 * reference count. I.e. turn KindOfUninit into KindOfNull.
1125 TypedValue asInitTVTmp() const {
1126 if (m_type == KindOfUninit) return make_tv<KindOfNull>();
1127 return *this;
1130 TypedValue detach() noexcept {
1131 auto tv = *asTypedValue();
1132 m_type = KindOfNull;
1133 return tv;
1136 private:
1137 ResourceData* getResourceData() const {
1138 assertx(is(KindOfResource));
1139 return m_data.pres->data();
1142 ResourceData* detachResourceData() {
1143 assertx(is(KindOfResource));
1144 assertx(m_type == KindOfResource);
1145 m_type = KindOfNull;
1146 return m_data.pres->data();
1149 ObjectData* detachObjectData() {
1150 assertx(is(KindOfObject));
1151 assertx(m_type == KindOfObject);
1152 m_type = KindOfNull;
1153 return m_data.pobj;
1156 template <typename T>
1157 friend typename std::enable_if<
1158 std::is_base_of<ResourceData,T>::value,
1159 ResourceData*
1160 >::type deref(const Variant& v) { return v.getResourceData(); }
1162 template <typename T>
1163 friend typename std::enable_if<
1164 std::is_base_of<ObjectData,T>::value,
1165 ObjectData*
1166 >::type deref(const Variant& v) { return v.getObjectData(); }
1168 template <typename T>
1169 friend typename std::enable_if<
1170 std::is_base_of<ResourceData,T>::value,
1171 ResourceData*
1172 >::type detach(Variant&& v) { return v.detachResourceData(); }
1174 template <typename T>
1175 friend typename std::enable_if<
1176 std::is_base_of<ObjectData,T>::value,
1177 ObjectData*
1178 >::type detach(Variant&& v) { return v.detachObjectData(); }
1180 explicit Variant(ResourceData* v) noexcept {
1181 if (v) {
1182 m_type = KindOfResource;
1183 m_data.pres = v->hdr();
1184 v->incRefCount();
1185 } else {
1186 m_type = KindOfNull;
1189 explicit Variant(ResourceHdr* v) noexcept {
1190 if (v) {
1191 m_type = KindOfResource;
1192 m_data.pres = v;
1193 v->incRefCount();
1194 } else {
1195 m_type = KindOfNull;
1200 * This set of constructors act like the normal constructors for the
1201 * given types except that they do not increment the reference count
1202 * of the passed value. They are used for the req::ptr move constructor.
1204 Variant(StringData* var, Attach) noexcept {
1205 if (var) {
1206 m_type = var->isRefCounted() ? KindOfString : KindOfPersistentString;
1207 m_data.pstr = var;
1208 } else {
1209 m_type = KindOfNull;
1212 Variant(ArrayData* var, Attach) noexcept {
1213 if (var) {
1214 m_type =
1215 var->isRefCounted() ? var->toDataType() : var->toPersistentDataType();
1216 m_data.parr = var;
1217 } else {
1218 m_type = KindOfNull;
1221 Variant(ObjectData* var, Attach) noexcept {
1222 if (var) {
1223 m_type = KindOfObject;
1224 m_data.pobj = var;
1225 } else {
1226 m_type = KindOfNull;
1229 Variant(ResourceData* var, Attach) noexcept {
1230 if (var) {
1231 m_type = KindOfResource;
1232 m_data.pres = var->hdr();
1233 } else {
1234 m_type = KindOfNull;
1237 Variant(ResourceHdr* var, Attach) noexcept {
1238 if (var) {
1239 m_type = KindOfResource;
1240 m_data.pres = var;
1241 } else {
1242 m_type = KindOfNull;
1245 Variant(TypedValue tv, Attach) noexcept : TypedValue(tv) {}
1246 Variant(TypedValue tv, Wrap) noexcept : TypedValue(tv) {
1247 tvIncRefGen(*asTypedValue());
1250 bool isPrimitive() const { return !isRefcountedType(m_type); }
1251 bool isObjectConvertable() {
1252 return isNullType(m_type) ||
1253 (m_type == KindOfBoolean && !m_data.num) ||
1254 (isStringType(m_type) && m_data.pstr->empty());
1257 void set(bool v) noexcept;
1258 void set(int v) noexcept;
1259 void set(int64_t v) noexcept;
1260 void set(double v) noexcept;
1261 void set(const char* v) = delete;
1262 void set(const std::string & v) {
1263 return set(String(v));
1265 void set(StringData *v) noexcept;
1266 void set(ArrayData *v) noexcept;
1267 void set(ObjectData *v) noexcept;
1268 void set(ResourceHdr *v) noexcept;
1269 void set(ResourceData *v) noexcept { set(v->hdr()); }
1270 void set(const StringData *v) = delete;
1271 void set(const ArrayData *v) = delete;
1272 void set(const ObjectData *v) = delete;
1273 void set(const ResourceData *v) = delete;
1274 void set(const ResourceHdr *v) = delete;
1276 void set(const String& v) noexcept { set(v.get()); }
1277 void set(const StaticString & v) noexcept;
1278 void set(const Array& v) noexcept { set(v.get()); }
1279 void set(const Object& v) noexcept { set(v.get()); }
1280 void set(const Resource& v) noexcept { set(v.hdr()); }
1282 void set(String&& v) noexcept { steal(v.detach()); }
1283 void set(Array&& v) noexcept { steal(v.detach()); }
1284 void set(Object&& v) noexcept { steal(v.detach()); }
1285 void set(Resource&& v) noexcept { steal(v.detachHdr()); }
1287 template<typename T>
1288 void set(const req::ptr<T> &v) noexcept {
1289 return set(v.get());
1292 template <typename T>
1293 void set(req::ptr<T>&& v) noexcept {
1294 return steal(v.detach());
1297 void steal(StringData* v) noexcept;
1298 void steal(ArrayData* v) noexcept;
1299 void steal(ObjectData* v) noexcept;
1300 void steal(ResourceHdr* v) noexcept;
1301 void steal(ResourceData* v) noexcept { steal(v->hdr()); }
1303 private:
1304 bool toBooleanHelper() const;
1305 int64_t toInt64Helper(int base = 10) const;
1306 Array toPHPArrayHelper() const;
1307 Resource toResourceHelper() const;
1309 DataType convertToNumeric(int64_t *lval, double *dval) const;
1312 Variant operator+(const Variant & lhs, const Variant & rhs) = delete;
1314 ///////////////////////////////////////////////////////////////////////////////
1317 * Definitions for some members of variant_ref et al. that use Variant.
1320 inline variant_ref::variant_ref(Variant& v)
1321 : variant_ref_detail::base<false>{v.asTypedValue()}
1324 inline const_variant_ref::const_variant_ref(const Variant& v)
1325 : variant_ref_detail::base<true>{v.asTypedValue()}
1328 inline variant_ref& variant_ref::operator=(const Variant& v) noexcept {
1329 return assign(v);
1332 inline variant_ref& variant_ref::assign(const Variant& v) noexcept {
1333 tvSet(tvToInit(*v.asTypedValue()), m_val);
1334 return *this;
1337 inline variant_ref& variant_ref::operator=(Variant &&rhs) noexcept {
1338 variant_ref lhs = *this;
1340 Variant goner((Variant::NoInit()));
1341 goner.m_data = val(lhs.m_val);
1342 goner.m_type = type(lhs.m_val);
1344 if (rhs.m_type == KindOfUninit) {
1345 type(lhs.m_val) = KindOfNull;
1346 } else {
1347 type(lhs.m_val) = rhs.m_type;
1348 val(lhs.m_val) = rhs.m_data;
1349 rhs.m_type = KindOfNull;
1352 return *this;
1355 ///////////////////////////////////////////////////////////////////////////////
1356 // VarNR
1358 struct VarNR : private TypedValue {
1359 static VarNR MakeKey(const String& s) {
1360 if (s.empty()) return VarNR(staticEmptyString());
1361 return VarNR(s);
1364 // Use to hold variant that do not need ref-counting
1365 explicit VarNR(bool v) { m_type = KindOfBoolean; m_data.num = (v?1:0);}
1366 explicit VarNR(int v) { m_type = KindOfInt64; m_data.num = v;}
1367 // The following two overloads will accept int64_t whether it's
1368 // implemented as long or long long.
1369 explicit VarNR(long v) { m_type = KindOfInt64; m_data.num = v;}
1370 explicit VarNR(long long v) { m_type = KindOfInt64; m_data.num = v;}
1371 explicit VarNR(uint64_t v) { m_type = KindOfInt64; m_data.num = v;}
1372 explicit VarNR(double v) { m_type = KindOfDouble; m_data.dbl = v;}
1374 explicit VarNR(const StaticString &v) {
1375 assertx(v.get() && !v.get()->isRefCounted());
1376 m_type = KindOfPersistentString;
1377 m_data.pstr = v.get();
1380 explicit VarNR(const String& v) : VarNR(v.get()) {}
1381 explicit VarNR(const Array& v) : VarNR(v.get()) {}
1382 explicit VarNR(const Object& v) : VarNR(v.get()) {}
1384 explicit VarNR(StringData *v);
1385 explicit VarNR(const StringData *v) {
1386 assertx(v && !v->isRefCounted());
1387 m_type = KindOfPersistentString;
1388 m_data.pstr = const_cast<StringData*>(v);
1390 explicit VarNR(ArrayData *v);
1391 explicit VarNR(const ArrayData*) = delete;
1392 explicit VarNR(ObjectData *v);
1393 explicit VarNR(const ObjectData*) = delete;
1395 explicit VarNR(TypedValue tv) { m_type = tv.m_type; m_data = tv.m_data; }
1396 explicit VarNR(const Variant& v) : VarNR{*v.asTypedValue()} {}
1398 VarNR(const VarNR&) = delete;
1399 VarNR& operator=(const VarNR&) = delete;
1401 explicit VarNR() { asVariant()->asTypedValue()->m_type = KindOfUninit; }
1403 ~VarNR() {
1404 if (debug) {
1405 checkRefCount();
1406 memset(this, kTVTrashFill2, sizeof(*this));
1410 TypedValue tv() const { return *this; }
1412 operator const Variant&() const { return *asVariant(); }
1414 bool isNull() const {
1415 return asVariant()->isNull();
1417 private:
1418 /* implicit */ VarNR(const char* v) = delete;
1419 /* implicit */ VarNR(const std::string & v) = delete;
1421 const Variant *asVariant() const {
1422 return &tvAsCVarRef(static_cast<const TypedValue*>(this));
1424 Variant* asVariant() {
1425 return &tvAsVariant(static_cast<TypedValue*>(this));
1427 void checkRefCount() {
1428 assertx(isRealType(m_type));
1430 switch (m_type) {
1431 DT_UNCOUNTED_CASE:
1432 return;
1433 case KindOfString:
1434 assertx(m_data.pstr->checkCount());
1435 return;
1436 case KindOfVec:
1437 case KindOfDict:
1438 case KindOfKeyset:
1439 assertx(m_data.parr->checkCount());
1440 return;
1441 case KindOfClsMeth:
1442 assertx(checkCountClsMeth(m_data.pclsmeth));
1443 return;
1444 case KindOfRClsMeth:
1445 assertx(m_data.prclsmeth->checkCount());
1446 case KindOfObject:
1447 assertx(m_data.pobj->checkCount());
1448 return;
1449 case KindOfResource:
1450 assertx(m_data.pres->checkCount());
1451 return;
1452 case KindOfRecord:
1453 assertx(m_data.prec->checkCount());
1454 return;
1455 case KindOfRFunc:
1456 assertx(m_data.prfunc->checkCount());
1457 return;
1459 not_reached();
1463 //////////////////////////////////////////////////////////////////////
1466 * The lvalBlackHole is used in array operations when a NewElem can't
1467 * create a new slot. (Basically if the next integer key in an array
1468 * is already at the maximum integer.)
1470 Variant& lvalBlackHole();
1473 * The lvalBlackHole has request lifetime.
1475 void initBlackHole();
1476 void clearBlackHole();
1478 ///////////////////////////////////////////////////////////////////////////////
1479 // breaking circular dependencies
1481 inline Variant Array::operator[](TypedValue key) const {
1482 return Variant::wrap(lookup(key));
1484 inline Variant Array::operator[](int key) const {
1485 return Variant::wrap(lookup(key));
1487 inline Variant Array::operator[](int64_t key) const {
1488 return Variant::wrap(lookup(key));
1490 inline Variant Array::operator[](const String& key) const {
1491 return Variant::wrap(lookup(key));
1493 inline Variant Array::operator[](const Variant& key) const {
1494 return Variant::wrap(lookup(key));
1497 inline void Array::append(const Variant& v) {
1498 append(*v.asTypedValue());
1501 ALWAYS_INLINE Variant uninit_null() {
1502 return Variant();
1505 ALWAYS_INLINE Variant init_null() {
1506 return Variant(Variant::NullInit());
1509 inline void concat_assign(Variant &v1, const char* s2) = delete;
1511 inline void concat_assign(tv_lval lhs, const String& s2) {
1512 if (!isStringType(type(lhs))) tvCastToStringInPlace(lhs);
1513 asStrRef(lhs) += s2;
1516 //////////////////////////////////////////////////////////////////////
1518 inline Array& forceToArray(Variant& var) {
1519 if (!var.isArray()) var = Variant(Array::CreateDict());
1520 return var.asArrRef();
1523 inline Array& forceToArray(tv_lval lval) {
1524 if (!isArrayLikeType(lval.type())) {
1525 tvMove(make_array_like_tv(ArrayData::CreateDict()), lval);
1527 return asArrRef(lval);
1530 inline Array& forceToDict(Variant& var) {
1531 if (!var.isDict()) var = Variant(Array::CreateDict());
1532 return var.asArrRef();
1535 inline Array& forceToDict(tv_lval lval) {
1536 if (!isDictType(lval.type())) {
1537 tvSet(make_tv<KindOfDict>(ArrayData::CreateDict()), lval);
1539 return asArrRef(lval);
1542 //////////////////////////////////////////////////////////////////////
1544 ALWAYS_INLINE Variant empty_string_variant() {
1545 return Variant(staticEmptyString(), Variant::PersistentStrInit{});
1548 template <typename T>
1549 inline Variant toVariant(const req::ptr<T>& p) {
1550 return p ? Variant(p) : Variant(false);
1553 template <typename T>
1554 inline Variant toVariant(req::ptr<T>&& p) {
1555 return p ? Variant(std::move(p)) : Variant(false);
1558 template <>
1559 inline bool ptr_is_null(const Variant& v) {
1560 return v.isNull();
1563 template <typename T>
1564 inline bool isa_non_null(const Variant& v) {
1565 return v.isa<T>();
1568 // Defined here to avoid introducing a dependency cycle between type-variant
1569 // and type-array
1570 template <IntishCast IC>
1571 ALWAYS_INLINE TypedValue Array::convertKey(TypedValue k) const {
1572 return tvToKey<IC>(k, m_arr ? m_arr.get() : ArrayData::CreateDict());
1574 template <IntishCast IC>
1575 ALWAYS_INLINE TypedValue Array::convertKey(const Variant& k) const {
1576 return convertKey<IC>(*k.asTypedValue());
1579 template <IntishCast IC>
1580 inline VarNR Variant::toKey(const ArrayData* ad) const {
1581 return VarNR(tvToKey<IC>(*this, ad));
1584 struct alignas(16) OptionalVariant {
1585 OptionalVariant() {
1586 m_tv.m_type = kInvalidDataType;
1588 ~OptionalVariant() {
1589 clear();
1591 OptionalVariant(const OptionalVariant& other) {
1592 if (other.hasValue()) {
1593 construct(other.value());
1594 return;
1596 m_tv.m_type = kInvalidDataType;
1598 OptionalVariant(OptionalVariant&& other) noexcept {
1599 if (other.hasValue()) {
1600 construct(std::move(other.value()));
1601 other.m_tv.m_type = kInvalidDataType;
1602 return;
1604 m_tv.m_type = kInvalidDataType;
1607 template<typename Arg>
1608 OptionalVariant& operator=(Arg&& arg) {
1609 assign(std::forward<Arg>(arg));
1610 return *this;
1612 OptionalVariant& operator=(const OptionalVariant& arg) {
1613 assign(arg);
1614 return *this;
1616 OptionalVariant& operator=(OptionalVariant&& arg) {
1617 assign(std::move(arg));
1618 return *this;
1621 bool hasValue() const {
1622 return m_tv.m_type != kInvalidDataType;
1624 bool has_value() const {
1625 return hasValue();
1627 Variant& value() {
1628 assertx(hasValue());
1629 return tvAsVariant(&m_tv);
1631 const Variant& value() const {
1632 assertx(hasValue());
1633 return tvAsCVarRef(&m_tv);
1635 void clear() {
1636 if (hasValue()) {
1637 auto const old = m_tv;
1638 m_tv.m_type = kInvalidDataType;
1639 tvDecRefGen(old);
1642 template <class... Args>
1643 Variant& emplace(Args&&... args) {
1644 clear();
1645 construct(std::forward<Args>(args)...);
1646 return value();
1648 void assign(const Variant& other) {
1649 if (hasValue()) {
1650 value() = other;
1651 return;
1653 construct(other);
1655 void assign(Variant&& other) {
1656 if (hasValue()) {
1657 value() = std::move(other);
1658 return;
1660 construct(std::move(other));
1662 void assign(const OptionalVariant& other) {
1663 if (other.hasValue()) return assign(other.value());
1664 clear();
1666 void assign(OptionalVariant&& other) {
1667 if (other.hasValue()) {
1668 assign(std::move(other.value()));
1669 other.m_tv.m_type = kInvalidDataType;
1670 return;
1672 clear();
1675 operator bool() const { return hasValue(); }
1677 const Variant* operator->() const { return &value(); }
1678 Variant* operator->() { return &value(); }
1680 const Variant& operator*() const & { return value(); }
1681 Variant& operator*() & { return value(); }
1682 Variant&& operator*() && { return std::move(value()); }
1683 private:
1684 template<typename... Args>
1685 void construct(Args&&... args) {
1686 new (&m_tv) Variant(std::forward<Args>(args)...);
1688 TypedValue m_tv;
1691 //////////////////////////////////////////////////////////////////////