Fix collection->hack array conversions
[hiphop-php.git] / hphp / runtime / base / tv-helpers.cpp
blob96c6ddc78223b2583fc1495c55c3ccb84ad16ec5
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
32 namespace HPHP {
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));
43 [&] {
44 switch (cell.m_type) {
45 case KindOfUninit:
46 case KindOfNull:
47 return;
48 case KindOfBoolean:
49 assert(cell.m_data.num == 0 || cell.m_data.num == 1);
50 return;
51 case KindOfInt64:
52 case KindOfDouble:
53 return;
54 case KindOfPersistentString:
55 assertPtr(cell.m_data.pstr);
56 assert(cell.m_data.pstr->kindIsValid());
57 assert(!cell.m_data.pstr->isRefCounted());
58 return;
59 case KindOfString:
60 assertPtr(cell.m_data.pstr);
61 assert(cell.m_data.pstr->kindIsValid());
62 assert(cell.m_data.pstr->checkCount());
63 return;
64 case KindOfPersistentVec:
65 assertPtr(cell.m_data.parr);
66 assert(!cell.m_data.parr->isRefCounted());
67 assert(cell.m_data.parr->isVecArray());
68 return;
69 case KindOfVec:
70 assertPtr(cell.m_data.parr);
71 assert(cell.m_data.parr->checkCount());
72 assert(cell.m_data.parr->isVecArray());
73 return;
74 case KindOfPersistentDict:
75 assertPtr(cell.m_data.parr);
76 assert(!cell.m_data.parr->isRefCounted());
77 assert(cell.m_data.parr->isDict());
78 return;
79 case KindOfDict:
80 assertPtr(cell.m_data.parr);
81 assert(cell.m_data.parr->checkCount());
82 assert(cell.m_data.parr->isDict());
83 return;
84 case KindOfPersistentKeyset:
85 assertPtr(cell.m_data.parr);
86 assert(!cell.m_data.parr->isRefCounted());
87 assert(cell.m_data.parr->isKeyset());
88 return;
89 case KindOfKeyset:
90 assertPtr(cell.m_data.parr);
91 assert(cell.m_data.parr->checkCount());
92 assert(cell.m_data.parr->isKeyset());
93 return;
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());
99 return;
100 case KindOfArray:
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());
105 return;
106 case KindOfObject:
107 assertPtr(cell.m_data.pobj);
108 assert(cell.m_data.pobj->kindIsValid());
109 assert(cell.m_data.pobj->checkCount());
110 return;
111 case KindOfResource:
112 assertPtr(cell.m_data.pres);
113 assert(cell.m_data.pres->kindIsValid());
114 assert(cell.m_data.pres->checkCount());
115 return;
116 case KindOfRef:
117 assert(!"KindOfRef found in a Cell");
118 break;
119 case KindOfClass:
120 assert(!"Invalid Cell type");
121 break;
123 not_reached();
124 }();
126 return true;
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)) {
147 return false;
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));
157 tvUnboxIfNeeded(tv);
158 bool b;
160 do {
161 switch (tv->m_type) {
162 case KindOfUninit:
163 case KindOfNull:
164 b = false;
165 continue;
167 case KindOfBoolean:
168 return;
170 case KindOfInt64:
171 b = (tv->m_data.num != 0LL);
172 continue;
174 case KindOfDouble:
175 b = (tv->m_data.dbl != 0);
176 continue;
178 case KindOfPersistentString:
179 b = tv->m_data.pstr->toBoolean();
180 continue;
182 case KindOfString:
183 b = tv->m_data.pstr->toBoolean();
184 tvDecRefStr(tv);
185 continue;
187 case KindOfPersistentVec:
188 case KindOfPersistentDict:
189 case KindOfPersistentKeyset:
190 case KindOfPersistentArray:
191 b = !tv->m_data.parr->empty();
192 continue;
194 case KindOfVec:
195 case KindOfDict:
196 case KindOfKeyset:
197 case KindOfArray:
198 b = !tv->m_data.parr->empty();
199 tvDecRefArr(tv);
200 continue;
202 case KindOfObject:
203 b = tv->m_data.pobj->toBoolean();
204 tvDecRefObj(tv);
205 continue;
207 case KindOfResource:
208 b = tv->m_data.pres->data()->o_toBoolean();
209 tvDecRefRes(tv);
210 continue;
212 case KindOfRef:
213 case KindOfClass:
214 break;
216 not_reached();
217 } while (0);
219 tv->m_data.num = b;
220 tv->m_type = KindOfBoolean;
223 void tvCastToDoubleInPlace(TypedValue* tv) {
224 assert(tvIsPlausible(*tv));
225 tvUnboxIfNeeded(tv);
226 double d;
228 do {
229 switch (tv->m_type) {
230 case KindOfUninit:
231 case KindOfNull:
232 d = 0.0;
233 continue;
235 case KindOfBoolean:
236 assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL);
237 // fallthru
238 case KindOfInt64:
239 d = (double)(tv->m_data.num);
240 continue;
242 case KindOfDouble:
243 return;
245 case KindOfPersistentString:
246 d = tv->m_data.pstr->toDouble();
247 continue;
249 case KindOfString:
250 d = tv->m_data.pstr->toDouble();
251 tvDecRefStr(tv);
252 continue;
254 case KindOfPersistentVec:
255 case KindOfPersistentDict:
256 case KindOfPersistentKeyset:
257 case KindOfPersistentArray:
258 d = tv->m_data.parr->empty() ? 0 : 1;
259 continue;
261 case KindOfVec:
262 case KindOfDict:
263 case KindOfKeyset:
264 case KindOfArray:
265 d = tv->m_data.parr->empty() ? 0 : 1;
266 tvDecRefArr(tv);
267 continue;
269 case KindOfObject:
270 d = tv->m_data.pobj->toDouble();
271 tvDecRefObj(tv);
272 continue;
274 case KindOfResource:
275 d = tv->m_data.pres->data()->o_toDouble();
276 tvDecRefRes(tv);
277 continue;
279 case KindOfRef:
280 case KindOfClass:
281 break;
283 not_reached();
284 } while (0);
286 tv->m_data.dbl = d;
287 tv->m_type = KindOfDouble;
290 void cellCastToInt64InPlace(Cell* cell) {
291 assert(cellIsPlausible(*cell));
292 int64_t i;
294 do {
295 switch (cell->m_type) {
296 case KindOfUninit:
297 case KindOfNull:
298 cell->m_data.num = 0LL;
299 // fallthru
300 case KindOfBoolean:
301 assert(cell->m_data.num == 0LL || cell->m_data.num == 1LL);
302 cell->m_type = KindOfInt64;
303 // fallthru
304 case KindOfInt64:
305 return;
307 case KindOfDouble:
308 i = toInt64(cell->m_data.dbl);
309 continue;
311 case KindOfPersistentString:
312 i = cell->m_data.pstr->toInt64();
313 continue;
315 case KindOfString:
316 i = cell->m_data.pstr->toInt64();
317 tvDecRefStr(cell);
318 continue;
320 case KindOfPersistentVec:
321 case KindOfPersistentDict:
322 case KindOfPersistentKeyset:
323 case KindOfPersistentArray:
324 i = cell->m_data.parr->empty() ? 0 : 1;
325 continue;
327 case KindOfVec:
328 case KindOfDict:
329 case KindOfKeyset:
330 case KindOfArray:
331 i = cell->m_data.parr->empty() ? 0 : 1;
332 tvDecRefArr(cell);
333 continue;
335 case KindOfObject:
336 i = cell->m_data.pobj->toInt64();
337 tvDecRefObj(cell);
338 continue;
340 case KindOfResource:
341 i = cell->m_data.pres->data()->o_toInt64();
342 tvDecRefRes(cell);
343 continue;
345 case KindOfRef:
346 case KindOfClass:
347 break;
349 not_reached();
350 } while (0);
352 cell->m_data.num = i;
353 cell->m_type = KindOfInt64;
356 void tvCastToInt64InPlace(TypedValue* tv) {
357 assert(tvIsPlausible(*tv));
358 tvUnboxIfNeeded(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) {
369 case KindOfUninit:
370 case KindOfNull:
371 return 0;
373 case KindOfBoolean:
374 assert(tv->m_data.num == 0LL || tv->m_data.num == 1LL);
375 // fallthru
376 case KindOfInt64:
377 return (double)(tv->m_data.num);
379 case KindOfDouble:
380 return tv->m_data.dbl;
382 case KindOfPersistentString:
383 case KindOfString:
384 return tv->m_data.pstr->toDouble();
386 case KindOfPersistentVec:
387 case KindOfVec:
388 case KindOfPersistentDict:
389 case KindOfDict:
390 case KindOfPersistentKeyset:
391 case KindOfKeyset:
392 case KindOfPersistentArray:
393 case KindOfArray:
394 return tv->m_data.parr->empty() ? 0.0 : 1.0;
396 case KindOfObject:
397 return tv->m_data.pobj->toDouble();
399 case KindOfResource:
400 return tv->m_data.pres->data()->o_toDouble();
402 case KindOfRef:
403 case KindOfClass:
404 break;
406 not_reached();
409 const StaticString
410 s_1("1"),
411 s_scalar("scalar");
413 void tvCastToStringInPlace(TypedValue* tv) {
414 assert(tvIsPlausible(*tv));
415 tvUnboxIfNeeded(tv);
417 auto string = [&](StringData* s) {
418 tv->m_type = KindOfString;
419 tv->m_data.pstr = s;
421 auto persistentString = [&](StringData* s) {
422 assert(!s->isRefCounted());
423 tv->m_type = KindOfPersistentString;
424 tv->m_data.pstr = s;
427 switch (tv->m_type) {
428 case KindOfUninit:
429 case KindOfNull:
430 return persistentString(staticEmptyString());
432 case KindOfBoolean:
433 return persistentString(tv->m_data.num ? s_1.get() : staticEmptyString());
435 case KindOfInt64:
436 return string(buildStringData(tv->m_data.num));
438 case KindOfDouble:
439 return string(buildStringData(tv->m_data.dbl));
441 case KindOfPersistentString:
442 case KindOfString:
443 return;
445 case KindOfVec:
446 case KindOfPersistentVec:
447 raise_notice("Vec to string conversion");
448 if (tv->m_type == KindOfVec) tvDecRefArr(tv);
449 return persistentString(vec_string.get());
451 case KindOfDict:
452 case KindOfPersistentDict:
453 raise_notice("Dict to string conversion");
454 if (tv->m_type == KindOfDict) tvDecRefArr(tv);
455 return persistentString(dict_string.get());
457 case KindOfKeyset:
458 case KindOfPersistentKeyset:
459 raise_notice("Keyset to string conversion");
460 if (tv->m_type == KindOfKeyset) tvDecRefArr(tv);
461 return persistentString(keyset_string.get());
463 case KindOfArray:
464 case KindOfPersistentArray:
465 raise_notice("Array to string conversion");
466 if (tv->m_type == KindOfArray) tvDecRefArr(tv);
467 return persistentString(array_string.get());
469 case KindOfObject:
470 // For objects, we fall back on the Variant machinery
471 tvAsVariant(tv) = tv->m_data.pobj->invokeToString();
472 return;
474 case KindOfResource:
475 // For resources, we fall back on the Variant machinery
476 tvAsVariant(tv) = tv->m_data.pres->data()->o_toString();
477 return;
479 case KindOfRef:
480 case KindOfClass:
481 break;
483 not_reached();
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) {
493 case KindOfUninit:
494 case KindOfNull:
495 return staticEmptyString();
497 case KindOfBoolean:
498 return tv->m_data.num ? s_1.get() : staticEmptyString();
500 case KindOfInt64:
501 return buildStringData(tv->m_data.num);
503 case KindOfDouble:
504 return buildStringData(tv->m_data.dbl);
506 case KindOfPersistentString:
507 return tv->m_data.pstr;
509 case KindOfString: {
510 auto s = tv->m_data.pstr;
511 s->incRefCount();
512 return s;
515 case KindOfPersistentVec:
516 case KindOfVec:
517 raise_notice("Vec to string conversion");
518 return vec_string.get();
520 case KindOfPersistentDict:
521 case KindOfDict:
522 raise_notice("Dict to string conversion");
523 return dict_string.get();
525 case KindOfPersistentKeyset:
526 case KindOfKeyset:
527 raise_notice("Keyset to string conversion");
528 return keyset_string.get();
530 case KindOfPersistentArray:
531 case KindOfArray:
532 raise_notice("Array to string conversion");
533 return array_string.get();
535 case KindOfObject:
536 return tv->m_data.pobj->invokeToString().detach();
538 case KindOfResource:
539 return tv->m_data.pres->data()->o_toString().detach();
541 case KindOfRef:
542 case KindOfClass:
543 not_reached();
545 not_reached();
548 void tvCastToArrayInPlace(TypedValue* tv) {
549 assert(tvIsPlausible(*tv));
550 tvUnboxIfNeeded(tv);
551 ArrayData* a;
553 do {
554 switch (tv->m_type) {
555 case KindOfUninit:
556 case KindOfNull:
557 a = ArrayData::Create();
558 continue;
560 case KindOfBoolean:
561 case KindOfInt64:
562 case KindOfDouble:
563 case KindOfPersistentString:
564 a = ArrayData::Create(tvAsVariant(tv));
565 continue;
567 case KindOfString:
568 a = ArrayData::Create(tvAsVariant(tv));
569 tvDecRefStr(tv);
570 continue;
572 case KindOfPersistentVec: {
573 auto* adIn = tv->m_data.parr;
574 assert(adIn->isVecArray());
575 a = PackedArray::ToPHPArrayVec(adIn, true);
576 assert(a != adIn);
577 continue;
580 case KindOfVec: {
581 auto* adIn = tv->m_data.parr;
582 assert(adIn->isVecArray());
583 a = PackedArray::ToPHPArrayVec(adIn, adIn->cowCheck());
584 if (a != adIn) tvDecRefArr(tv);
585 continue;
588 case KindOfPersistentDict: {
589 auto* adIn = tv->m_data.parr;
590 assert(adIn->isDict());
591 a = MixedArray::ToPHPArrayDict(adIn, true);
592 assert(a != adIn);
593 continue;
596 case KindOfDict: {
597 auto* adIn = tv->m_data.parr;
598 assert(adIn->isDict());
599 a = MixedArray::ToPHPArrayDict(adIn, adIn->cowCheck());
600 if (a != adIn) tvDecRefArr(tv);
601 continue;
604 case KindOfPersistentKeyset: {
605 auto* adIn = tv->m_data.parr;
606 assert(adIn->isKeyset());
607 a = SetArray::ToPHPArray(adIn, true);
608 assert(a != adIn);
609 continue;
612 case KindOfKeyset: {
613 auto* adIn = tv->m_data.parr;
614 assert(adIn->isKeyset());
615 a = SetArray::ToPHPArray(adIn, adIn->cowCheck());
616 if (a != adIn) tvDecRefArr(tv);
617 continue;
620 case KindOfPersistentArray:
621 case KindOfArray:
622 assert(tv->m_data.parr->isPHPArray());
623 return;
625 case KindOfObject:
626 // For objects, we fall back on the Variant machinery
627 tvAsVariant(tv) = tv->m_data.pobj->toArray();
628 return;
630 case KindOfResource:
631 a = ArrayData::Create(tvAsVariant(tv));
632 tvDecRefRes(tv);
633 continue;
635 case KindOfRef:
636 case KindOfClass:
637 break;
639 not_reached();
640 } while (0);
642 assert(!a->isRefCounted() || a->hasExactlyOneRef());
644 tv->m_data.parr = a;
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));
658 tvUnboxIfNeeded(tv);
659 ArrayData* a;
661 do {
662 switch (tv->m_type) {
663 case KindOfUninit:
664 case KindOfNull:
665 raise_warning("Null to vec conversion");
666 a = staticEmptyVecArray();
667 continue;
669 case KindOfBoolean:
670 raise_warning("Bool to vec conversion");
671 a = staticEmptyVecArray();
672 continue;
674 case KindOfInt64:
675 raise_warning("Int to vec conversion");
676 a = staticEmptyVecArray();
677 continue;
679 case KindOfDouble:
680 raise_warning("Double to vec conversion");
681 a = staticEmptyVecArray();
682 continue;
684 case KindOfPersistentString:
685 case KindOfString:
686 raise_warning("String to vec conversion");
687 a = staticEmptyVecArray();
688 decRefStr(tv->m_data.pstr);
689 continue;
691 case KindOfResource:
692 raise_warning("Resource to vec conversion");
693 a = staticEmptyVecArray();
694 decRefRes(tv->m_data.pres);
695 continue;
697 case KindOfPersistentDict:
698 case KindOfDict: {
699 auto* adIn = tv->m_data.parr;
700 assert(adIn->isDict());
701 a = MixedArray::ToVecDict(adIn, adIn->cowCheck());
702 assert(a != adIn);
703 decRefArr(adIn);
704 continue;
707 case KindOfPersistentKeyset:
708 case KindOfKeyset: {
709 auto* adIn = tv->m_data.parr;
710 assert(adIn->isKeyset());
711 a = SetArray::ToVec(adIn, adIn->cowCheck());
712 assert(a != adIn);
713 decRefArr(adIn);
714 continue;
717 case KindOfPersistentArray:
718 case KindOfArray: {
719 auto* adIn = tv->m_data.parr;
720 assert(adIn->isPHPArray());
721 a = adIn->toVec(adIn->cowCheck());
722 if (a != adIn) decRefArr(adIn);
723 continue;
726 case KindOfPersistentVec:
727 case KindOfVec:
728 assert(tv->m_data.parr->isVecArray());
729 return;
731 case KindOfObject: {
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());
740 a = arr.detach();
741 } else {
742 raise_warning("Non-iterable object conversion to vec");
743 a = staticEmptyVecArray();
745 decRefObj(obj);
746 continue;
749 case KindOfRef:
750 case KindOfClass:
751 break;
753 not_reached();
754 } while (0);
756 assert(!a->isRefCounted() || a->hasExactlyOneRef());
758 tv->m_data.parr = a;
759 tv->m_type = KindOfVec;
760 assert(cellIsPlausible(*tv));
763 void tvCastToDictInPlace(TypedValue* tv) {
764 assert(tvIsPlausible(*tv));
765 tvUnboxIfNeeded(tv);
766 ArrayData* a;
768 do {
769 switch (tv->m_type) {
770 case KindOfUninit:
771 case KindOfNull:
772 raise_warning("Null to dict conversion");
773 a = staticEmptyDictArray();
774 continue;
776 case KindOfBoolean:
777 raise_warning("Bool to dict conversion");
778 a = staticEmptyDictArray();
779 continue;
781 case KindOfInt64:
782 raise_warning("Int to dict conversion");
783 a = staticEmptyDictArray();
784 continue;
786 case KindOfDouble:
787 raise_warning("Double to dict conversion");
788 a = staticEmptyDictArray();
789 continue;
791 case KindOfPersistentString:
792 case KindOfString:
793 raise_warning("String to dict conversion");
794 a = staticEmptyDictArray();
795 decRefStr(tv->m_data.pstr);
796 continue;
798 case KindOfResource:
799 raise_warning("Resource to dict conversion");
800 a = staticEmptyDictArray();
801 decRefRes(tv->m_data.pres);
802 continue;
804 case KindOfPersistentVec:
805 case KindOfVec: {
806 auto* adIn = tv->m_data.parr;
807 assert(adIn->isVecArray());
808 a = PackedArray::ToDictVec(adIn, adIn->cowCheck());
809 assert(a != adIn);
810 decRefArr(adIn);
811 continue;
814 case KindOfPersistentKeyset:
815 case KindOfKeyset: {
816 auto* adIn = tv->m_data.parr;
817 assert(adIn->isKeyset());
818 a = SetArray::ToDict(adIn, adIn->cowCheck());
819 if (a != adIn) decRefArr(adIn);
820 continue;
823 case KindOfPersistentArray:
824 case KindOfArray: {
825 auto* adIn = tv->m_data.parr;
826 assert(adIn->isPHPArray());
827 a = adIn->toDict(adIn->cowCheck());
828 if (a != adIn) decRefArr(adIn);
829 continue;
832 case KindOfPersistentDict:
833 case KindOfDict:
834 assert(tv->m_data.parr->isDict());
835 return;
837 case KindOfObject: {
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());
846 a = arr.detach();
847 } else {
848 raise_warning("Non-iterable object conversion to dict");
849 a = staticEmptyDictArray();
851 decRefObj(obj);
852 continue;
855 case KindOfRef:
856 case KindOfClass:
857 break;
859 not_reached();
860 } while (0);
862 assert(!a->isRefCounted() || a->hasExactlyOneRef());
864 tv->m_data.parr = a;
865 tv->m_type = KindOfDict;
866 assert(cellIsPlausible(*tv));
869 void tvCastToKeysetInPlace(TypedValue* tv) {
870 assert(tvIsPlausible(*tv));
871 tvUnboxIfNeeded(tv);
872 ArrayData* a;
874 do {
875 switch (tv->m_type) {
876 case KindOfUninit:
877 case KindOfNull:
878 raise_warning("Null to keyset conversion");
879 a = staticEmptyKeysetArray();
880 continue;
882 case KindOfBoolean:
883 raise_warning("Bool to keyset conversion");
884 a = staticEmptyKeysetArray();
885 continue;
887 case KindOfInt64:
888 raise_warning("Int to keyset conversion");
889 a = staticEmptyKeysetArray();
890 continue;
892 case KindOfDouble:
893 raise_warning("Double to keyset conversion");
894 a = staticEmptyKeysetArray();
895 continue;
897 case KindOfPersistentString:
898 case KindOfString:
899 raise_warning("String to keyset conversion");
900 a = staticEmptyKeysetArray();
901 decRefStr(tv->m_data.pstr);
902 continue;
904 case KindOfResource:
905 raise_warning("Resource to keyset conversion");
906 a = staticEmptyKeysetArray();
907 decRefRes(tv->m_data.pres);
908 continue;
910 case KindOfPersistentVec:
911 case KindOfVec: {
912 auto* adIn = tv->m_data.parr;
913 assert(adIn->isVecArray());
914 a = PackedArray::ToKeysetVec(adIn, adIn->cowCheck());
915 assert(a != adIn);
916 decRefArr(adIn);
917 continue;
920 case KindOfPersistentDict:
921 case KindOfDict: {
922 auto* adIn = tv->m_data.parr;
923 assert(adIn->isDict());
924 a = MixedArray::ToKeysetDict(adIn, adIn->cowCheck());
925 if (a != adIn) decRefArr(adIn);
926 continue;
929 case KindOfPersistentArray:
930 case KindOfArray: {
931 auto* adIn = tv->m_data.parr;
932 assert(adIn->isPHPArray());
933 a = adIn->toKeyset(adIn->cowCheck());
934 if (a != adIn) decRefArr(adIn);
935 continue;
938 case KindOfPersistentKeyset:
939 case KindOfKeyset:
940 assert(tv->m_data.parr->isKeyset());
941 return;
943 case KindOfObject: {
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());
952 a = arr.detach();
953 } else {
954 raise_warning("Non-iterable object conversion to keyset");
955 a = staticEmptyKeysetArray();
957 decRefObj(obj);
958 continue;
961 case KindOfRef:
962 case KindOfClass:
963 break;
965 not_reached();
966 } while (0);
968 assert(!a->isRefCounted() || a->hasExactlyOneRef());
970 tv->m_data.parr = a;
971 tv->m_type = KindOfKeyset;
972 assert(cellIsPlausible(*tv));
975 void tvCastToObjectInPlace(TypedValue* tv) {
976 assert(tvIsPlausible(*tv));
977 tvUnboxIfNeeded(tv);
978 ObjectData* o;
980 do {
981 switch (tv->m_type) {
982 case KindOfUninit:
983 case KindOfNull:
984 o = SystemLib::AllocStdClassObject().detach();
985 continue;
987 case KindOfBoolean:
988 case KindOfInt64:
989 case KindOfDouble:
990 case KindOfPersistentString:
991 case KindOfResource:
992 o = SystemLib::AllocStdClassObject().detach();
993 o->o_set(s_scalar, tvAsVariant(tv));
994 continue;
996 case KindOfString:
997 o = SystemLib::AllocStdClassObject().detach();
998 o->o_set(s_scalar, tvAsVariant(tv));
999 tvDecRefStr(tv);
1000 continue;
1002 case KindOfPersistentVec:
1003 case KindOfVec:
1004 case KindOfPersistentDict:
1005 case KindOfDict:
1006 case KindOfPersistentKeyset:
1007 case KindOfKeyset:
1008 tvCastToArrayInPlace(tv);
1009 // Fall-through to array case
1010 case KindOfPersistentArray:
1011 case KindOfArray:
1012 // For arrays, we fall back on the Variant machinery
1013 tvAsVariant(tv) = ObjectData::FromArray(tv->m_data.parr);
1014 return;
1016 case KindOfObject:
1017 return;
1019 case KindOfRef:
1020 case KindOfClass:
1021 break;
1023 not_reached();
1024 } while (0);
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;
1040 } else {
1041 tvCastToObjectInPlace(tv);
1045 void tvCastToResourceInPlace(TypedValue* tv) {
1046 assert(tvIsPlausible(*tv));
1047 tvUnboxIfNeeded(tv);
1049 do {
1050 switch (tv->m_type) {
1051 DT_UNCOUNTED_CASE:
1052 continue;
1053 case KindOfString:
1054 case KindOfVec:
1055 case KindOfDict:
1056 case KindOfKeyset:
1057 case KindOfArray:
1058 case KindOfObject:
1059 tvDecRef(tv);
1060 continue;
1061 case KindOfResource:
1062 // no op, return
1063 return;
1064 case KindOfRef:
1065 case KindOfClass:
1066 break;
1068 not_reached();
1069 } while (0);
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) {
1081 case KindOfUninit:
1082 case KindOfNull:
1083 case KindOfBoolean:
1084 case KindOfInt64:
1085 case KindOfDouble:
1086 case KindOfPersistentString:
1087 case KindOfString:
1088 // In PHP 7 mode handling of null types is stricter
1089 if (tv->m_type == KindOfNull && RuntimeOption::PHP7_ScalarTypes) {
1090 return false;
1092 tvCastToBooleanInPlace(tv);
1093 return true;
1095 case KindOfPersistentVec:
1096 case KindOfVec:
1097 case KindOfPersistentDict:
1098 case KindOfDict:
1099 case KindOfPersistentKeyset:
1100 case KindOfKeyset:
1101 case KindOfPersistentArray:
1102 case KindOfArray:
1103 case KindOfObject:
1104 case KindOfResource:
1105 return false;
1107 case KindOfRef:
1108 case KindOfClass:
1109 break;
1111 not_reached();
1114 bool tvCanBeCoercedToNumber(TypedValue* tv) {
1115 switch (tv->m_type) {
1116 case KindOfUninit:
1117 case KindOfBoolean:
1118 case KindOfInt64:
1119 case KindOfDouble:
1120 return true;
1122 case KindOfNull:
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
1130 // Because PHP
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");
1142 return okay;
1145 case KindOfPersistentVec:
1146 case KindOfVec:
1147 case KindOfPersistentDict:
1148 case KindOfDict:
1149 case KindOfPersistentKeyset:
1150 case KindOfKeyset:
1151 case KindOfPersistentArray:
1152 case KindOfArray:
1153 case KindOfObject:
1154 case KindOfResource:
1155 return false;
1157 case KindOfRef:
1158 case KindOfClass:
1159 break;
1161 not_reached();
1164 bool tvCoerceParamToInt64InPlace(TypedValue* tv) {
1165 assert(tvIsPlausible(*tv));
1166 tvUnboxIfNeeded(tv);
1167 if (!tvCanBeCoercedToNumber(tv)) {
1168 return false;
1170 // In PHP 7 mode doubles only convert to integers when the conversion is non-
1171 // narrowing
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);
1178 return true;
1181 bool tvCoerceParamToDoubleInPlace(TypedValue* tv) {
1182 assert(tvIsPlausible(*tv));
1183 tvUnboxIfNeeded(tv);
1184 if (!tvCanBeCoercedToNumber(tv)) {
1185 return false;
1187 tvCastToDoubleInPlace(tv);
1188 return true;
1191 bool tvCoerceParamToStringInPlace(TypedValue* tv) {
1192 assert(tvIsPlausible(*tv));
1193 tvUnboxIfNeeded(tv);
1195 switch (tv->m_type) {
1196 case KindOfUninit:
1197 case KindOfNull:
1198 case KindOfBoolean:
1199 case KindOfInt64:
1200 case KindOfDouble:
1201 case KindOfPersistentString:
1202 case KindOfString:
1203 // In PHP 7 mode handling of null types is stricter
1204 if (tv->m_type == KindOfNull && RuntimeOption::PHP7_ScalarTypes) {
1205 return false;
1207 tvCastToStringInPlace(tv);
1208 return true;
1210 case KindOfPersistentVec:
1211 case KindOfVec:
1212 case KindOfPersistentDict:
1213 case KindOfDict:
1214 case KindOfPersistentKeyset:
1215 case KindOfKeyset:
1216 case KindOfPersistentArray:
1217 case KindOfArray:
1218 return false;
1220 case KindOfObject:
1221 if (tv->m_data.pobj->hasToString()) {
1222 tvAsVariant(tv) = tv->m_data.pobj->invokeToString();
1223 return true;
1225 return false;
1227 case KindOfResource:
1228 return false;
1230 case KindOfRef:
1231 case KindOfClass:
1232 break;
1234 not_reached();
1237 bool tvCoerceParamToArrayInPlace(TypedValue* tv) {
1238 assert(tvIsPlausible(*tv));
1239 tvUnboxIfNeeded(tv);
1241 switch (tv->m_type) {
1242 case KindOfUninit:
1243 case KindOfNull:
1244 case KindOfBoolean:
1245 case KindOfInt64:
1246 case KindOfDouble:
1247 case KindOfPersistentString:
1248 case KindOfString:
1249 case KindOfPersistentVec:
1250 case KindOfVec:
1251 case KindOfPersistentDict:
1252 case KindOfDict:
1253 case KindOfPersistentKeyset:
1254 case KindOfKeyset:
1255 return false;
1257 case KindOfPersistentArray:
1258 case KindOfArray:
1259 return true;
1261 case KindOfObject:
1262 if (LIKELY(tv->m_data.pobj->isCollection())) {
1263 tvAsVariant(tv) = tv->m_data.pobj->toArray();
1264 return true;
1266 return false;
1267 case KindOfResource:
1268 return false;
1270 case KindOfRef:
1271 case KindOfClass:
1272 break;
1274 not_reached();
1277 bool tvCoerceParamToVecInPlace(TypedValue* tv) {
1278 assert(tvIsPlausible(*tv));
1279 tvUnboxIfNeeded(tv);
1281 switch (tv->m_type) {
1282 case KindOfUninit:
1283 case KindOfNull:
1284 case KindOfBoolean:
1285 case KindOfInt64:
1286 case KindOfDouble:
1287 case KindOfPersistentString:
1288 case KindOfString:
1289 case KindOfObject:
1290 case KindOfResource:
1291 case KindOfPersistentDict:
1292 case KindOfDict:
1293 case KindOfPersistentKeyset:
1294 case KindOfKeyset:
1295 case KindOfPersistentArray:
1296 case KindOfArray:
1297 return false;
1299 case KindOfPersistentVec:
1300 case KindOfVec:
1301 return true;
1303 case KindOfRef:
1304 case KindOfClass:
1305 break;
1307 not_reached();
1310 bool tvCoerceParamToDictInPlace(TypedValue* tv) {
1311 assert(tvIsPlausible(*tv));
1312 tvUnboxIfNeeded(tv);
1314 switch (tv->m_type) {
1315 case KindOfUninit:
1316 case KindOfNull:
1317 case KindOfBoolean:
1318 case KindOfInt64:
1319 case KindOfDouble:
1320 case KindOfPersistentString:
1321 case KindOfString:
1322 case KindOfObject:
1323 case KindOfResource:
1324 case KindOfPersistentVec:
1325 case KindOfVec:
1326 case KindOfPersistentKeyset:
1327 case KindOfKeyset:
1328 case KindOfPersistentArray:
1329 case KindOfArray:
1330 return false;
1332 case KindOfPersistentDict:
1333 case KindOfDict:
1334 return true;
1336 case KindOfRef:
1337 case KindOfClass:
1338 break;
1340 not_reached();
1343 bool tvCoerceParamToKeysetInPlace(TypedValue* tv) {
1344 assert(tvIsPlausible(*tv));
1345 tvUnboxIfNeeded(tv);
1347 switch (tv->m_type) {
1348 case KindOfUninit:
1349 case KindOfNull:
1350 case KindOfBoolean:
1351 case KindOfInt64:
1352 case KindOfDouble:
1353 case KindOfPersistentString:
1354 case KindOfString:
1355 case KindOfObject:
1356 case KindOfResource:
1357 case KindOfPersistentVec:
1358 case KindOfVec:
1359 case KindOfPersistentDict:
1360 case KindOfDict:
1361 case KindOfPersistentArray:
1362 case KindOfArray:
1363 return false;
1365 case KindOfPersistentKeyset:
1366 case KindOfKeyset:
1367 return true;
1369 case KindOfRef:
1370 case KindOfClass:
1371 break;
1373 not_reached();
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;
1388 return true;
1390 return tv->m_type == KindOfObject;
1393 bool tvCoerceParamToResourceInPlace(TypedValue* tv) {
1394 assert(tvIsPlausible(*tv));
1395 tvUnboxIfNeeded(tv);
1396 return tv->m_type == KindOfResource;
1399 namespace {
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)) {
1419 return;
1422 VMRegAnchor _;
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))) { \
1435 return; \
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, \
1440 KindOf##expkind); \
1442 #define X(kind) XX(kind, kind)
1443 X(Boolean)
1444 X(Int64)
1445 X(Double)
1446 X(String)
1447 X(Vec)
1448 X(Dict)
1449 X(Keyset)
1450 X(Array)
1451 X(Object)
1452 XX(NullableObject, Object)
1453 X(Resource)
1454 #undef X
1455 #undef XX
1457 TVCoercionException::TVCoercionException(const Func* func,
1458 int arg_num,
1459 DataType actual,
1460 DataType expected)
1461 : std::runtime_error(
1462 folly::format("Unable to coerce param {} to {}() "
1463 "from {} to {}",
1464 arg_num,
1465 func->name(),
1466 actual,
1467 expected).str())
1469 if (func->attrs() & AttrParamCoerceModeFalse) {
1470 m_tv = make_tv<KindOfBoolean>(false);
1471 } else {
1472 m_tv = make_tv<KindOfNull>();
1476 ///////////////////////////////////////////////////////////////////////////////