2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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 #include "hphp/runtime/base/tv-helpers.h"
19 #include "hphp/runtime/base/dummy-resource.h"
20 #include "hphp/runtime/base/packed-array.h"
21 #include "hphp/runtime/base/set-array.h"
22 #include "hphp/runtime/base/runtime-error.h"
23 #include "hphp/runtime/base/type-conversions.h"
24 #include "hphp/runtime/base/zend-functions.h"
25 #include "hphp/runtime/vm/func.h"
26 #include "hphp/runtime/vm/vm-regs.h"
28 #include "hphp/runtime/vm/jit/translator-inline.h"
30 #include "hphp/system/systemlib.h"
34 //////////////////////////////////////////////////////////////////////
36 bool cellIsPlausible(const Cell cell
) {
37 assert(cell
.m_type
!= KindOfRef
);
39 auto assertPtr
= [](void* ptr
) {
40 assert(ptr
&& (uintptr_t(ptr
) % sizeof(ptr
) == 0));
44 switch (cell
.m_type
) {
49 assert(cell
.m_data
.num
== 0 || cell
.m_data
.num
== 1);
54 case KindOfPersistentString
:
55 assertPtr(cell
.m_data
.pstr
);
56 assert(cell
.m_data
.pstr
->kindIsValid());
57 assert(!cell
.m_data
.pstr
->isRefCounted());
60 assertPtr(cell
.m_data
.pstr
);
61 assert(cell
.m_data
.pstr
->kindIsValid());
62 assert(cell
.m_data
.pstr
->checkCount());
64 case KindOfPersistentVec
:
65 assertPtr(cell
.m_data
.parr
);
66 assert(!cell
.m_data
.parr
->isRefCounted());
67 assert(cell
.m_data
.parr
->isVecArray());
70 assertPtr(cell
.m_data
.parr
);
71 assert(cell
.m_data
.parr
->checkCount());
72 assert(cell
.m_data
.parr
->isVecArray());
74 case KindOfPersistentDict
:
75 assertPtr(cell
.m_data
.parr
);
76 assert(!cell
.m_data
.parr
->isRefCounted());
77 assert(cell
.m_data
.parr
->isDict());
80 assertPtr(cell
.m_data
.parr
);
81 assert(cell
.m_data
.parr
->checkCount());
82 assert(cell
.m_data
.parr
->isDict());
84 case KindOfPersistentKeyset
:
85 assertPtr(cell
.m_data
.parr
);
86 assert(!cell
.m_data
.parr
->isRefCounted());
87 assert(cell
.m_data
.parr
->isKeyset());
90 assertPtr(cell
.m_data
.parr
);
91 assert(cell
.m_data
.parr
->checkCount());
92 assert(cell
.m_data
.parr
->isKeyset());
94 case KindOfPersistentArray
:
95 assertPtr(cell
.m_data
.parr
);
96 assert(cell
.m_data
.parr
->kindIsValid());
97 assert(!cell
.m_data
.parr
->isRefCounted());
98 assert(cell
.m_data
.parr
->isPHPArray());
101 assertPtr(cell
.m_data
.parr
);
102 assert(cell
.m_data
.parr
->kindIsValid());
103 assert(cell
.m_data
.parr
->checkCount());
104 assert(cell
.m_data
.parr
->isPHPArray());
107 assertPtr(cell
.m_data
.pobj
);
108 assert(cell
.m_data
.pobj
->kindIsValid());
109 assert(cell
.m_data
.pobj
->checkCount());
112 assertPtr(cell
.m_data
.pres
);
113 assert(cell
.m_data
.pres
->kindIsValid());
114 assert(cell
.m_data
.pres
->checkCount());
117 assert(!"KindOfRef found in a Cell");
120 assert(!"Invalid Cell type");
129 bool tvIsPlausible(TypedValue tv
) {
130 if (tv
.m_type
== KindOfRef
) {
131 assert(tv
.m_data
.pref
);
132 assert(uintptr_t(tv
.m_data
.pref
) % sizeof(void*) == 0);
133 assert(tv
.m_data
.pref
->kindIsValid());
134 assert(tv
.m_data
.pref
->checkCount());
135 tv
= *tv
.m_data
.pref
->tv();
137 return cellIsPlausible(tv
);
140 bool refIsPlausible(const Ref ref
) {
141 assert(ref
.m_type
== KindOfRef
);
142 return tvIsPlausible(ref
);
145 bool tvDecRefWillRelease(TypedValue
* tv
) {
146 if (!isRefcountedType(tv
->m_type
)) {
149 if (tv
->m_type
== KindOfRef
) {
150 return tv
->m_data
.pref
->getRealCount() <= 1;
152 return TV_GENERIC_DISPATCH(*tv
, decWillRelease
);
155 void tvCastToBooleanInPlace(TypedValue
* tv
) {
156 assert(tvIsPlausible(*tv
));
161 switch (tv
->m_type
) {
171 b
= (tv
->m_data
.num
!= 0LL);
175 b
= (tv
->m_data
.dbl
!= 0);
178 case KindOfPersistentString
:
179 b
= tv
->m_data
.pstr
->toBoolean();
183 b
= tv
->m_data
.pstr
->toBoolean();
187 case KindOfPersistentVec
:
188 case KindOfPersistentDict
:
189 case KindOfPersistentKeyset
:
190 case KindOfPersistentArray
:
191 b
= !tv
->m_data
.parr
->empty();
198 b
= !tv
->m_data
.parr
->empty();
203 b
= tv
->m_data
.pobj
->toBoolean();
208 b
= tv
->m_data
.pres
->data()->o_toBoolean();
220 tv
->m_type
= KindOfBoolean
;
223 void tvCastToDoubleInPlace(TypedValue
* tv
) {
224 assert(tvIsPlausible(*tv
));
229 switch (tv
->m_type
) {
236 assert(tv
->m_data
.num
== 0LL || tv
->m_data
.num
== 1LL);
239 d
= (double)(tv
->m_data
.num
);
245 case KindOfPersistentString
:
246 d
= tv
->m_data
.pstr
->toDouble();
250 d
= tv
->m_data
.pstr
->toDouble();
254 case KindOfPersistentVec
:
255 case KindOfPersistentDict
:
256 case KindOfPersistentKeyset
:
257 case KindOfPersistentArray
:
258 d
= tv
->m_data
.parr
->empty() ? 0 : 1;
265 d
= tv
->m_data
.parr
->empty() ? 0 : 1;
270 d
= tv
->m_data
.pobj
->toDouble();
275 d
= tv
->m_data
.pres
->data()->o_toDouble();
287 tv
->m_type
= KindOfDouble
;
290 void cellCastToInt64InPlace(Cell
* cell
) {
291 assert(cellIsPlausible(*cell
));
295 switch (cell
->m_type
) {
298 cell
->m_data
.num
= 0LL;
301 assert(cell
->m_data
.num
== 0LL || cell
->m_data
.num
== 1LL);
302 cell
->m_type
= KindOfInt64
;
308 i
= toInt64(cell
->m_data
.dbl
);
311 case KindOfPersistentString
:
312 i
= cell
->m_data
.pstr
->toInt64();
316 i
= cell
->m_data
.pstr
->toInt64();
320 case KindOfPersistentVec
:
321 case KindOfPersistentDict
:
322 case KindOfPersistentKeyset
:
323 case KindOfPersistentArray
:
324 i
= cell
->m_data
.parr
->empty() ? 0 : 1;
331 i
= cell
->m_data
.parr
->empty() ? 0 : 1;
336 i
= cell
->m_data
.pobj
->toInt64();
341 i
= cell
->m_data
.pres
->data()->o_toInt64();
352 cell
->m_data
.num
= i
;
353 cell
->m_type
= KindOfInt64
;
356 void tvCastToInt64InPlace(TypedValue
* tv
) {
357 assert(tvIsPlausible(*tv
));
359 cellCastToInt64InPlace(tv
);
362 double tvCastToDouble(TypedValue
* tv
) {
363 assert(tvIsPlausible(*tv
));
364 if (tv
->m_type
== KindOfRef
) {
365 tv
= tv
->m_data
.pref
->tv();
368 switch (tv
->m_type
) {
374 assert(tv
->m_data
.num
== 0LL || tv
->m_data
.num
== 1LL);
377 return (double)(tv
->m_data
.num
);
380 return tv
->m_data
.dbl
;
382 case KindOfPersistentString
:
384 return tv
->m_data
.pstr
->toDouble();
386 case KindOfPersistentVec
:
388 case KindOfPersistentDict
:
390 case KindOfPersistentKeyset
:
392 case KindOfPersistentArray
:
394 return tv
->m_data
.parr
->empty() ? 0.0 : 1.0;
397 return tv
->m_data
.pobj
->toDouble();
400 return tv
->m_data
.pres
->data()->o_toDouble();
413 void tvCastToStringInPlace(TypedValue
* tv
) {
414 assert(tvIsPlausible(*tv
));
417 auto string
= [&](StringData
* s
) {
418 tv
->m_type
= KindOfString
;
421 auto persistentString
= [&](StringData
* s
) {
422 assert(!s
->isRefCounted());
423 tv
->m_type
= KindOfPersistentString
;
427 switch (tv
->m_type
) {
430 return persistentString(staticEmptyString());
433 return persistentString(tv
->m_data
.num
? s_1
.get() : staticEmptyString());
436 return string(buildStringData(tv
->m_data
.num
));
439 return string(buildStringData(tv
->m_data
.dbl
));
441 case KindOfPersistentString
:
446 case KindOfPersistentVec
:
447 raise_notice("Vec to string conversion");
448 if (tv
->m_type
== KindOfVec
) tvDecRefArr(tv
);
449 return persistentString(vec_string
.get());
452 case KindOfPersistentDict
:
453 raise_notice("Dict to string conversion");
454 if (tv
->m_type
== KindOfDict
) tvDecRefArr(tv
);
455 return persistentString(dict_string
.get());
458 case KindOfPersistentKeyset
:
459 raise_notice("Keyset to string conversion");
460 if (tv
->m_type
== KindOfKeyset
) tvDecRefArr(tv
);
461 return persistentString(keyset_string
.get());
464 case KindOfPersistentArray
:
465 raise_notice("Array to string conversion");
466 if (tv
->m_type
== KindOfArray
) tvDecRefArr(tv
);
467 return persistentString(array_string
.get());
470 // For objects, we fall back on the Variant machinery
471 tvAsVariant(tv
) = tv
->m_data
.pobj
->invokeToString();
475 // For resources, we fall back on the Variant machinery
476 tvAsVariant(tv
) = tv
->m_data
.pres
->data()->o_toString();
486 StringData
* tvCastToString(const TypedValue
* tv
) {
487 assert(tvIsPlausible(*tv
));
488 if (tv
->m_type
== KindOfRef
) {
489 tv
= tv
->m_data
.pref
->tv();
492 switch (tv
->m_type
) {
495 return staticEmptyString();
498 return tv
->m_data
.num
? s_1
.get() : staticEmptyString();
501 return buildStringData(tv
->m_data
.num
);
504 return buildStringData(tv
->m_data
.dbl
);
506 case KindOfPersistentString
:
507 return tv
->m_data
.pstr
;
510 auto s
= tv
->m_data
.pstr
;
515 case KindOfPersistentVec
:
517 raise_notice("Vec to string conversion");
518 return vec_string
.get();
520 case KindOfPersistentDict
:
522 raise_notice("Dict to string conversion");
523 return dict_string
.get();
525 case KindOfPersistentKeyset
:
527 raise_notice("Keyset to string conversion");
528 return keyset_string
.get();
530 case KindOfPersistentArray
:
532 raise_notice("Array to string conversion");
533 return array_string
.get();
536 return tv
->m_data
.pobj
->invokeToString().detach();
539 return tv
->m_data
.pres
->data()->o_toString().detach();
548 void tvCastToArrayInPlace(TypedValue
* tv
) {
549 assert(tvIsPlausible(*tv
));
554 switch (tv
->m_type
) {
557 a
= ArrayData::Create();
563 case KindOfPersistentString
:
564 a
= ArrayData::Create(tvAsVariant(tv
));
568 a
= ArrayData::Create(tvAsVariant(tv
));
572 case KindOfPersistentVec
: {
573 auto* adIn
= tv
->m_data
.parr
;
574 assert(adIn
->isVecArray());
575 a
= PackedArray::ToPHPArrayVec(adIn
, true);
581 auto* adIn
= tv
->m_data
.parr
;
582 assert(adIn
->isVecArray());
583 a
= PackedArray::ToPHPArrayVec(adIn
, adIn
->cowCheck());
584 if (a
!= adIn
) tvDecRefArr(tv
);
588 case KindOfPersistentDict
: {
589 auto* adIn
= tv
->m_data
.parr
;
590 assert(adIn
->isDict());
591 a
= MixedArray::ToPHPArrayDict(adIn
, true);
597 auto* adIn
= tv
->m_data
.parr
;
598 assert(adIn
->isDict());
599 a
= MixedArray::ToPHPArrayDict(adIn
, adIn
->cowCheck());
600 if (a
!= adIn
) tvDecRefArr(tv
);
604 case KindOfPersistentKeyset
: {
605 auto* adIn
= tv
->m_data
.parr
;
606 assert(adIn
->isKeyset());
607 a
= SetArray::ToPHPArray(adIn
, true);
613 auto* adIn
= tv
->m_data
.parr
;
614 assert(adIn
->isKeyset());
615 a
= SetArray::ToPHPArray(adIn
, adIn
->cowCheck());
616 if (a
!= adIn
) tvDecRefArr(tv
);
620 case KindOfPersistentArray
:
622 assert(tv
->m_data
.parr
->isPHPArray());
626 // For objects, we fall back on the Variant machinery
627 tvAsVariant(tv
) = tv
->m_data
.pobj
->toArray();
631 a
= ArrayData::Create(tvAsVariant(tv
));
642 assert(!a
->isRefCounted() || a
->hasExactlyOneRef());
645 tv
->m_type
= KindOfArray
;
646 assert(cellIsPlausible(*tv
));
649 static Array
arrayFromCollection(ObjectData
* obj
) {
650 if (auto ad
= collections::asArray(obj
)) {
651 return ArrNR
{ad
}.asArray();
653 return collections::toArray(obj
);
656 void tvCastToVecInPlace(TypedValue
* tv
) {
657 assert(tvIsPlausible(*tv
));
662 switch (tv
->m_type
) {
665 raise_warning("Null to vec conversion");
666 a
= staticEmptyVecArray();
670 raise_warning("Bool to vec conversion");
671 a
= staticEmptyVecArray();
675 raise_warning("Int to vec conversion");
676 a
= staticEmptyVecArray();
680 raise_warning("Double to vec conversion");
681 a
= staticEmptyVecArray();
684 case KindOfPersistentString
:
686 raise_warning("String to vec conversion");
687 a
= staticEmptyVecArray();
688 decRefStr(tv
->m_data
.pstr
);
692 raise_warning("Resource to vec conversion");
693 a
= staticEmptyVecArray();
694 decRefRes(tv
->m_data
.pres
);
697 case KindOfPersistentDict
:
699 auto* adIn
= tv
->m_data
.parr
;
700 assert(adIn
->isDict());
701 a
= MixedArray::ToVecDict(adIn
, adIn
->cowCheck());
707 case KindOfPersistentKeyset
:
709 auto* adIn
= tv
->m_data
.parr
;
710 assert(adIn
->isKeyset());
711 a
= SetArray::ToVec(adIn
, adIn
->cowCheck());
717 case KindOfPersistentArray
:
719 auto* adIn
= tv
->m_data
.parr
;
720 assert(adIn
->isPHPArray());
721 a
= adIn
->toVec(adIn
->cowCheck());
722 if (a
!= adIn
) decRefArr(adIn
);
726 case KindOfPersistentVec
:
728 assert(tv
->m_data
.parr
->isVecArray());
732 auto* obj
= tv
->m_data
.pobj
;
733 if (obj
->isCollection()) {
734 a
= arrayFromCollection(obj
).toVec().detach();
735 } else if (obj
->instanceof(SystemLib::s_IteratorClass
)) {
736 auto arr
= Array::CreateVec();
737 for (ArrayIter
iter(obj
); iter
; ++iter
) {
738 arr
.append(iter
.second());
742 raise_warning("Non-iterable object conversion to vec");
743 a
= staticEmptyVecArray();
756 assert(!a
->isRefCounted() || a
->hasExactlyOneRef());
759 tv
->m_type
= KindOfVec
;
760 assert(cellIsPlausible(*tv
));
763 void tvCastToDictInPlace(TypedValue
* tv
) {
764 assert(tvIsPlausible(*tv
));
769 switch (tv
->m_type
) {
772 raise_warning("Null to dict conversion");
773 a
= staticEmptyDictArray();
777 raise_warning("Bool to dict conversion");
778 a
= staticEmptyDictArray();
782 raise_warning("Int to dict conversion");
783 a
= staticEmptyDictArray();
787 raise_warning("Double to dict conversion");
788 a
= staticEmptyDictArray();
791 case KindOfPersistentString
:
793 raise_warning("String to dict conversion");
794 a
= staticEmptyDictArray();
795 decRefStr(tv
->m_data
.pstr
);
799 raise_warning("Resource to dict conversion");
800 a
= staticEmptyDictArray();
801 decRefRes(tv
->m_data
.pres
);
804 case KindOfPersistentVec
:
806 auto* adIn
= tv
->m_data
.parr
;
807 assert(adIn
->isVecArray());
808 a
= PackedArray::ToDictVec(adIn
, adIn
->cowCheck());
814 case KindOfPersistentKeyset
:
816 auto* adIn
= tv
->m_data
.parr
;
817 assert(adIn
->isKeyset());
818 a
= SetArray::ToDict(adIn
, adIn
->cowCheck());
819 if (a
!= adIn
) decRefArr(adIn
);
823 case KindOfPersistentArray
:
825 auto* adIn
= tv
->m_data
.parr
;
826 assert(adIn
->isPHPArray());
827 a
= adIn
->toDict(adIn
->cowCheck());
828 if (a
!= adIn
) decRefArr(adIn
);
832 case KindOfPersistentDict
:
834 assert(tv
->m_data
.parr
->isDict());
838 auto* obj
= tv
->m_data
.pobj
;
839 if (obj
->isCollection()) {
840 a
= arrayFromCollection(obj
).toDict().detach();
841 } else if (obj
->instanceof(SystemLib::s_IteratorClass
)) {
842 auto arr
= Array::CreateDict();
843 for (ArrayIter
iter(obj
); iter
; ++iter
) {
844 arr
.set(iter
.first(), iter
.second());
848 raise_warning("Non-iterable object conversion to dict");
849 a
= staticEmptyDictArray();
862 assert(!a
->isRefCounted() || a
->hasExactlyOneRef());
865 tv
->m_type
= KindOfDict
;
866 assert(cellIsPlausible(*tv
));
869 void tvCastToKeysetInPlace(TypedValue
* tv
) {
870 assert(tvIsPlausible(*tv
));
875 switch (tv
->m_type
) {
878 raise_warning("Null to keyset conversion");
879 a
= staticEmptyKeysetArray();
883 raise_warning("Bool to keyset conversion");
884 a
= staticEmptyKeysetArray();
888 raise_warning("Int to keyset conversion");
889 a
= staticEmptyKeysetArray();
893 raise_warning("Double to keyset conversion");
894 a
= staticEmptyKeysetArray();
897 case KindOfPersistentString
:
899 raise_warning("String to keyset conversion");
900 a
= staticEmptyKeysetArray();
901 decRefStr(tv
->m_data
.pstr
);
905 raise_warning("Resource to keyset conversion");
906 a
= staticEmptyKeysetArray();
907 decRefRes(tv
->m_data
.pres
);
910 case KindOfPersistentVec
:
912 auto* adIn
= tv
->m_data
.parr
;
913 assert(adIn
->isVecArray());
914 a
= PackedArray::ToKeysetVec(adIn
, adIn
->cowCheck());
920 case KindOfPersistentDict
:
922 auto* adIn
= tv
->m_data
.parr
;
923 assert(adIn
->isDict());
924 a
= MixedArray::ToKeysetDict(adIn
, adIn
->cowCheck());
925 if (a
!= adIn
) decRefArr(adIn
);
929 case KindOfPersistentArray
:
931 auto* adIn
= tv
->m_data
.parr
;
932 assert(adIn
->isPHPArray());
933 a
= adIn
->toKeyset(adIn
->cowCheck());
934 if (a
!= adIn
) decRefArr(adIn
);
938 case KindOfPersistentKeyset
:
940 assert(tv
->m_data
.parr
->isKeyset());
944 auto* obj
= tv
->m_data
.pobj
;
945 if (obj
->isCollection()) {
946 a
= arrayFromCollection(obj
).toKeyset().detach();
947 } else if (obj
->instanceof(SystemLib::s_IteratorClass
)) {
948 auto arr
= Array::CreateKeyset();
949 for (ArrayIter
iter(obj
); iter
; ++iter
) {
950 arr
.append(iter
.first());
954 raise_warning("Non-iterable object conversion to keyset");
955 a
= staticEmptyKeysetArray();
968 assert(!a
->isRefCounted() || a
->hasExactlyOneRef());
971 tv
->m_type
= KindOfKeyset
;
972 assert(cellIsPlausible(*tv
));
975 void tvCastToObjectInPlace(TypedValue
* tv
) {
976 assert(tvIsPlausible(*tv
));
981 switch (tv
->m_type
) {
984 o
= SystemLib::AllocStdClassObject().detach();
990 case KindOfPersistentString
:
992 o
= SystemLib::AllocStdClassObject().detach();
993 o
->o_set(s_scalar
, tvAsVariant(tv
));
997 o
= SystemLib::AllocStdClassObject().detach();
998 o
->o_set(s_scalar
, tvAsVariant(tv
));
1002 case KindOfPersistentVec
:
1004 case KindOfPersistentDict
:
1006 case KindOfPersistentKeyset
:
1008 tvCastToArrayInPlace(tv
);
1009 // Fall-through to array case
1010 case KindOfPersistentArray
:
1012 // For arrays, we fall back on the Variant machinery
1013 tvAsVariant(tv
) = ObjectData::FromArray(tv
->m_data
.parr
);
1026 tv
->m_data
.pobj
= o
;
1027 tv
->m_type
= KindOfObject
;
1028 assert(cellIsPlausible(*tv
));
1031 void tvCastToNullableObjectInPlace(TypedValue
* tv
) {
1032 if (isNullType(tv
->m_type
)) {
1033 // XXX(t3879280) This happens immediately before calling an extension
1034 // function that takes an optional Object argument. We want to end up
1035 // passing const Object& holding nullptr, so by clearing out m_data.pobj we
1036 // can unconditionally treat &tv->m_data.pobj as a const Object& in the
1037 // function being called. This violates the invariant that the value of
1038 // m_data doesn't matter in a KindOfNull TypedValue.
1039 tv
->m_data
.pobj
= nullptr;
1041 tvCastToObjectInPlace(tv
);
1045 void tvCastToResourceInPlace(TypedValue
* tv
) {
1046 assert(tvIsPlausible(*tv
));
1047 tvUnboxIfNeeded(tv
);
1050 switch (tv
->m_type
) {
1061 case KindOfResource
:
1071 tv
->m_type
= KindOfResource
;
1072 tv
->m_data
.pres
= req::make
<DummyResource
>().detach()->hdr();
1073 assert(cellIsPlausible(*tv
));
1076 bool tvCoerceParamToBooleanInPlace(TypedValue
* tv
) {
1077 assert(tvIsPlausible(*tv
));
1078 tvUnboxIfNeeded(tv
);
1080 switch (tv
->m_type
) {
1086 case KindOfPersistentString
:
1088 // In PHP 7 mode handling of null types is stricter
1089 if (tv
->m_type
== KindOfNull
&& RuntimeOption::PHP7_ScalarTypes
) {
1092 tvCastToBooleanInPlace(tv
);
1095 case KindOfPersistentVec
:
1097 case KindOfPersistentDict
:
1099 case KindOfPersistentKeyset
:
1101 case KindOfPersistentArray
:
1104 case KindOfResource
:
1114 bool tvCanBeCoercedToNumber(TypedValue
* tv
) {
1115 switch (tv
->m_type
) {
1123 // In PHP 7 mode handling of null types is stricter
1124 return !RuntimeOption::PHP7_ScalarTypes
;
1126 case KindOfPersistentString
:
1127 case KindOfString
: {
1128 // Simplified version of is_numeric_string
1129 // which also allows for non-numeric garbage
1131 auto str
= tv
->m_data
.pstr
;
1132 auto p
= str
->data();
1133 auto l
= tv
->m_data
.pstr
->size();
1134 while (l
&& isspace(*p
)) { ++p
; --l
; }
1135 if (l
&& (*p
== '+' || *p
== '-')) { ++p
; --l
; }
1136 if (l
&& *p
== '.') { ++p
; --l
; }
1137 bool okay
= l
&& isdigit(*p
);
1138 // In PHP7 garbage at the end of a numeric string will trigger a notice
1139 if (RuntimeOption::PHP7_ScalarTypes
&& okay
&& !str
->isNumeric()) {
1140 raise_notice("A non well formed numeric value encountered");
1145 case KindOfPersistentVec
:
1147 case KindOfPersistentDict
:
1149 case KindOfPersistentKeyset
:
1151 case KindOfPersistentArray
:
1154 case KindOfResource
:
1164 bool tvCoerceParamToInt64InPlace(TypedValue
* tv
) {
1165 assert(tvIsPlausible(*tv
));
1166 tvUnboxIfNeeded(tv
);
1167 if (!tvCanBeCoercedToNumber(tv
)) {
1170 // In PHP 7 mode doubles only convert to integers when the conversion is non-
1172 if (RuntimeOption::PHP7_ScalarTypes
&& tv
->m_type
== KindOfDouble
) {
1173 if (tv
->m_data
.dbl
< std::numeric_limits
<int64_t>::min()) return false;
1174 if (tv
->m_data
.dbl
> std::numeric_limits
<int64_t>::max()) return false;
1175 if (std::isnan(tv
->m_data
.dbl
)) return false;
1177 tvCastToInt64InPlace(tv
);
1181 bool tvCoerceParamToDoubleInPlace(TypedValue
* tv
) {
1182 assert(tvIsPlausible(*tv
));
1183 tvUnboxIfNeeded(tv
);
1184 if (!tvCanBeCoercedToNumber(tv
)) {
1187 tvCastToDoubleInPlace(tv
);
1191 bool tvCoerceParamToStringInPlace(TypedValue
* tv
) {
1192 assert(tvIsPlausible(*tv
));
1193 tvUnboxIfNeeded(tv
);
1195 switch (tv
->m_type
) {
1201 case KindOfPersistentString
:
1203 // In PHP 7 mode handling of null types is stricter
1204 if (tv
->m_type
== KindOfNull
&& RuntimeOption::PHP7_ScalarTypes
) {
1207 tvCastToStringInPlace(tv
);
1210 case KindOfPersistentVec
:
1212 case KindOfPersistentDict
:
1214 case KindOfPersistentKeyset
:
1216 case KindOfPersistentArray
:
1221 if (tv
->m_data
.pobj
->hasToString()) {
1222 tvAsVariant(tv
) = tv
->m_data
.pobj
->invokeToString();
1227 case KindOfResource
:
1237 bool tvCoerceParamToArrayInPlace(TypedValue
* tv
) {
1238 assert(tvIsPlausible(*tv
));
1239 tvUnboxIfNeeded(tv
);
1241 switch (tv
->m_type
) {
1247 case KindOfPersistentString
:
1249 case KindOfPersistentVec
:
1251 case KindOfPersistentDict
:
1253 case KindOfPersistentKeyset
:
1257 case KindOfPersistentArray
:
1262 if (LIKELY(tv
->m_data
.pobj
->isCollection())) {
1263 tvAsVariant(tv
) = tv
->m_data
.pobj
->toArray();
1267 case KindOfResource
:
1277 bool tvCoerceParamToVecInPlace(TypedValue
* tv
) {
1278 assert(tvIsPlausible(*tv
));
1279 tvUnboxIfNeeded(tv
);
1281 switch (tv
->m_type
) {
1287 case KindOfPersistentString
:
1290 case KindOfResource
:
1291 case KindOfPersistentDict
:
1293 case KindOfPersistentKeyset
:
1295 case KindOfPersistentArray
:
1299 case KindOfPersistentVec
:
1310 bool tvCoerceParamToDictInPlace(TypedValue
* tv
) {
1311 assert(tvIsPlausible(*tv
));
1312 tvUnboxIfNeeded(tv
);
1314 switch (tv
->m_type
) {
1320 case KindOfPersistentString
:
1323 case KindOfResource
:
1324 case KindOfPersistentVec
:
1326 case KindOfPersistentKeyset
:
1328 case KindOfPersistentArray
:
1332 case KindOfPersistentDict
:
1343 bool tvCoerceParamToKeysetInPlace(TypedValue
* tv
) {
1344 assert(tvIsPlausible(*tv
));
1345 tvUnboxIfNeeded(tv
);
1347 switch (tv
->m_type
) {
1353 case KindOfPersistentString
:
1356 case KindOfResource
:
1357 case KindOfPersistentVec
:
1359 case KindOfPersistentDict
:
1361 case KindOfPersistentArray
:
1365 case KindOfPersistentKeyset
:
1376 bool tvCoerceParamToObjectInPlace(TypedValue
* tv
) {
1377 assert(tvIsPlausible(*tv
));
1378 tvUnboxIfNeeded(tv
);
1379 return tv
->m_type
== KindOfObject
;
1382 bool tvCoerceParamToNullableObjectInPlace(TypedValue
* tv
) {
1383 assert(tvIsPlausible(*tv
));
1384 tvUnboxIfNeeded(tv
);
1385 if (isNullType(tv
->m_type
)) {
1386 // See comment in tvCastToNullableObjectInPlace
1387 tv
->m_data
.pobj
= nullptr;
1390 return tv
->m_type
== KindOfObject
;
1393 bool tvCoerceParamToResourceInPlace(TypedValue
* tv
) {
1394 assert(tvIsPlausible(*tv
));
1395 tvUnboxIfNeeded(tv
);
1396 return tv
->m_type
== KindOfResource
;
1401 * Sometimes calls to builtin functions are inlined so that the call itself can
1402 * occur via CallBuiltin rather than NativeImpl. In these instances it's
1403 * possible that no ActRec was pushed for the builtin call, in which case the
1404 * liveFunc() will be the caller rather than the callee.
1406 * If no ActRec was pushed for the builtin function inspect the caller to
1407 * determine if the call used strict types.
1409 bool useStrictTypes(const Func
* callee
) {
1410 return liveFunc() == callee
1411 ? !liveFrame()->useWeakTypes()
1412 : liveUnit()->useStrictTypes() && !liveUnit()->isHHFile();
1416 void tvCoerceIfStrict(TypedValue
& tv
, int64_t argNum
, const Func
* func
) {
1417 if (LIKELY(!RuntimeOption::PHP7_ScalarTypes
||
1418 RuntimeOption::EnableHipHopSyntax
)) {
1423 if (!useStrictTypes(func
)) return;
1425 auto const& tc
= func
->params()[argNum
- 1].typeConstraint
;
1426 tc
.verifyParam(&tv
, func
, argNum
- 1, true);
1429 #define XX(kind, expkind) \
1430 void tvCoerceParamTo##kind##OrThrow(TypedValue* tv, \
1431 const Func* callee, \
1432 unsigned int arg_num) { \
1433 tvCoerceIfStrict(*tv, arg_num, callee); \
1434 if (LIKELY(tvCoerceParamTo##kind##InPlace(tv))) { \
1437 raise_param_type_warning(callee->displayName()->data(), \
1438 arg_num, KindOf##expkind, tv->m_type); \
1439 throw TVCoercionException(callee, arg_num, tv->m_type, \
1442 #define X(kind) XX(kind, kind)
1452 XX(NullableObject
, Object
)
1457 TVCoercionException::TVCoercionException(const Func
* func
,
1461 : std::runtime_error(
1462 folly::format("Unable to coerce param {} to {}() "
1469 if (func
->attrs() & AttrParamCoerceModeFalse
) {
1470 m_tv
= make_tv
<KindOfBoolean
>(false);
1472 m_tv
= make_tv
<KindOfNull
>();
1476 ///////////////////////////////////////////////////////////////////////////////