2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/array-iterator.h"
20 #include "folly/Likely.h"
22 #include "hphp/runtime/base/array-data.h"
23 #include "hphp/runtime/base/mixed-array.h"
24 #include "hphp/runtime/base/packed-array.h"
25 #include "hphp/runtime/base/apc-local-array.h"
26 #include "hphp/runtime/base/complex-types.h"
27 #include "hphp/runtime/base/builtin-functions.h"
28 #include "hphp/runtime/ext/ext_collections.h"
30 #include "hphp/runtime/base/mixed-array-defs.h"
31 #include "hphp/runtime/base/packed-array-defs.h"
32 #include "hphp/runtime/base/array-iterator-defs.h"
33 #include "hphp/runtime/base/apc-local-array-defs.h"
37 TRACE_SET_MOD(runtime
);
39 //////////////////////////////////////////////////////////////////////
48 __thread MIterTable tl_miter_table
;
50 //////////////////////////////////////////////////////////////////////
52 ArrayIter::ArrayIter(const ArrayData
* data
) {
56 ArrayIter::ArrayIter(const Array
& array
) {
60 ArrayIter::ArrayIter(ObjectData
* obj
)
61 : m_pos(ArrayData::invalid_index
) {
65 ArrayIter::ArrayIter(ObjectData
* obj
, NoInc
)
66 : m_pos(ArrayData::invalid_index
) {
70 ArrayIter::ArrayIter(const Object
& obj
)
71 : m_pos(ArrayData::invalid_index
) {
72 objInit
<true>(obj
.get());
75 ArrayIter::ArrayIter(const Cell
& c
) {
79 ArrayIter::ArrayIter(const Variant
& v
) {
80 cellInit(*v
.asCell());
83 ArrayIter::ArrayIter(const ArrayIter
& iter
) {
86 m_version
= iter
.m_version
;
87 m_itype
= iter
.m_itype
;
88 m_nextHelperIdx
= iter
.m_nextHelperIdx
;
90 const ArrayData
* ad
= getArrayData();
91 if (ad
) const_cast<ArrayData
*>(ad
)->incRefCount();
93 ObjectData
* obj
= getObject();
99 void ArrayIter::arrInit(const ArrayData
* arr
) {
103 m_pos
= arr
->iter_begin();
105 m_pos
= ArrayData::invalid_index
;
109 void ArrayIter::VectorInit(ArrayIter
* iter
, ObjectData
* obj
) {
110 auto vec
= static_cast<c_Vector
*>(obj
);
111 iter
->m_version
= vec
->getVersion();
115 void ArrayIter::MapInit(ArrayIter
* iter
, ObjectData
* obj
) {
116 auto mp
= static_cast<c_Map
*>(obj
);
117 iter
->m_version
= mp
->getVersion();
118 iter
->m_pos
= mp
->iter_begin();
121 void ArrayIter::ImmMapInit(ArrayIter
* iter
, ObjectData
* obj
) {
122 auto smp
= static_cast<c_ImmMap
*>(obj
);
123 iter
->m_version
= smp
->getVersion();
124 iter
->m_pos
= smp
->iter_begin();
127 void ArrayIter::SetInit(ArrayIter
* iter
, ObjectData
* obj
) {
128 auto st
= static_cast<c_Set
*>(obj
);
129 iter
->m_version
= st
->getVersion();
130 iter
->m_pos
= st
->iter_begin();
133 void ArrayIter::PairInit(ArrayIter
* iter
, ObjectData
* obj
) {
137 void ArrayIter::ImmVectorInit(ArrayIter
* iter
, ObjectData
* obj
) {
138 auto vec
= static_cast<c_ImmVector
*>(obj
);
139 iter
->m_version
= vec
->getVersion();
142 void ArrayIter::ImmSetInit(ArrayIter
* iter
, ObjectData
* obj
) {
143 auto st
= static_cast<c_ImmSet
*>(obj
);
144 iter
->m_version
= st
->getVersion();
145 iter
->m_pos
= st
->iter_begin();
148 IterNextIndex
ArrayIter::getNextHelperIdx(ObjectData
* obj
) {
149 Class
* cls
= obj
->getVMClass();
150 if (cls
== c_Vector::classof()) {
151 return IterNextIndex::Vector
;
152 } else if (cls
== c_Map::classof()) {
153 return IterNextIndex::Map
;
154 } else if (cls
== c_Set::classof()) {
155 return IterNextIndex::Set
;
156 } else if (cls
== c_ImmVector::classof()) {
157 return IterNextIndex::ImmVector
;
158 } else if (cls
== c_ImmMap::classof()) {
159 return IterNextIndex::ImmMap
;
160 } else if (cls
== c_ImmSet::classof()) {
161 return IterNextIndex::ImmSet
;
162 } else if (cls
== c_Pair::classof()) {
163 return IterNextIndex::Pair
;
165 return IterNextIndex::Object
;
169 void ArrayIter::IteratorObjInit(ArrayIter
* iter
, ObjectData
* obj
) {
170 assert(obj
->instanceof(SystemLib::s_IteratorClass
));
172 obj
->o_invoke_few_args(s_rewind
, 0);
174 // Regardless of whether the incRef template parameter is true or
175 // false, at this point this ArrayIter "owns" a reference to the
176 // object and is responsible for decreffing the object when the
177 // ArrayIter's lifetime ends. Normally ArrayIter's destructor takes
178 // care of this, but the destructor will not get invoked if an
179 // exception is thrown before the constructor finishes so we have
180 // to manually handle decreffing the object here.
181 iter
->m_data
= nullptr;
182 if (debug
) iter
->m_itype
= TypeUndefined
;
188 const ArrayIter::InitFuncPtr
189 ArrayIter::initFuncTable
[Collection::MaxNumTypes
] = {
190 &ArrayIter::IteratorObjInit
,
191 &ArrayIter::VectorInit
,
194 &ArrayIter::PairInit
,
195 &ArrayIter::ImmVectorInit
,
196 &ArrayIter::ImmMapInit
,
197 &ArrayIter::ImmSetInit
,
200 template <bool incRef
>
201 void ArrayIter::objInit(ObjectData
* obj
) {
207 initFuncTable
[getCollectionType()](this, obj
);
210 void ArrayIter::cellInit(const Cell
& c
) {
211 assert(cellIsPlausible(c
));
212 if (LIKELY(c
.m_type
== KindOfArray
)) {
213 arrInit(c
.m_data
.parr
);
214 } else if (LIKELY(c
.m_type
== KindOfObject
)) {
215 objInit
<true>(c
.m_data
.pobj
);
221 void ArrayIter::destruct() {
222 if (hasArrayData()) {
223 const ArrayData
* ad
= getArrayData();
224 if (debug
) m_itype
= TypeUndefined
;
225 if (ad
) decRefArr(const_cast<ArrayData
*>(ad
));
228 ObjectData
* obj
= getObject();
229 if (debug
) m_itype
= TypeUndefined
;
234 ArrayIter
& ArrayIter::operator=(const ArrayIter
& iter
) {
236 m_data
= iter
.m_data
;
238 m_version
= iter
.m_version
;
239 m_itype
= iter
.m_itype
;
240 m_nextHelperIdx
= iter
.m_nextHelperIdx
;
241 if (hasArrayData()) {
242 const ArrayData
* ad
= getArrayData();
243 if (ad
) const_cast<ArrayData
*>(ad
)->incRefCount();
245 ObjectData
* obj
= getObject();
252 ArrayIter
& ArrayIter::operator=(ArrayIter
&& iter
) {
254 m_data
= iter
.m_data
;
256 m_version
= iter
.m_version
;
257 m_itype
= iter
.m_itype
;
258 m_nextHelperIdx
= iter
.m_nextHelperIdx
;
259 iter
.m_data
= nullptr;
263 bool ArrayIter::endHelper() {
264 switch (getCollectionType()) {
265 case Collection::VectorType
: {
266 return m_pos
>= getVector()->size();
268 case Collection::MapType
:
269 case Collection::ImmMapType
: {
270 return !getMap()->iter_valid(m_pos
);
272 case Collection::SetType
:
273 case Collection::ImmSetType
: {
274 return !getSet()->iter_valid(m_pos
);
276 case Collection::PairType
: {
277 return m_pos
>= getPair()->size();
279 case Collection::ImmVectorType
: {
280 return m_pos
>= getImmVector()->size();
282 case Collection::InvalidType
: {
283 ObjectData
* obj
= getIteratorObj();
284 return !obj
->o_invoke_few_args(s_valid
, 0).toBoolean();
290 void ArrayIter::nextHelper() {
291 switch (getCollectionType()) {
292 case Collection::VectorType
: {
296 case Collection::MapType
:
297 case Collection::ImmMapType
: {
298 BaseMap
* mp
= getMap();
299 if (UNLIKELY(m_version
!= mp
->getVersion())) {
300 throw_collection_modified();
302 m_pos
= mp
->iter_next(m_pos
);
305 case Collection::SetType
: {
306 BaseSet
* st
= getSet();
307 if (UNLIKELY(m_version
!= st
->getVersion())) {
308 throw_collection_modified();
310 m_pos
= st
->iter_next(m_pos
);
313 case Collection::PairType
: {
317 case Collection::ImmVectorType
: {
321 case Collection::ImmSetType
: {
322 c_ImmSet
* st
= getImmSet();
323 assert(m_version
== st
->getVersion());
324 m_pos
= st
->iter_next(m_pos
);
327 case Collection::InvalidType
: {
328 ObjectData
* obj
= getIteratorObj();
329 obj
->o_invoke_few_args(s_next
, 0);
334 Variant
ArrayIter::firstHelper() {
335 switch (getCollectionType()) {
336 case Collection::VectorType
: {
339 case Collection::MapType
:
340 case Collection::ImmMapType
: {
341 BaseMap
* mp
= getMap();
342 if (UNLIKELY(m_version
!= mp
->getVersion())) {
343 throw_collection_modified();
345 return mp
->iter_key(m_pos
);
347 case Collection::SetType
: {
348 BaseSet
* st
= getSet();
349 if (UNLIKELY(m_version
!= st
->getVersion())) {
350 throw_collection_modified();
352 return st
->iter_key(m_pos
);
354 case Collection::PairType
: {
357 case Collection::ImmVectorType
: {
360 case Collection::ImmSetType
: {
361 c_ImmSet
* st
= getImmSet();
362 if (UNLIKELY(m_version
!= st
->getVersion())) {
363 throw_collection_modified();
365 return st
->iter_key(m_pos
);
367 case Collection::InvalidType
:
370 ObjectData
* obj
= getIteratorObj();
371 return obj
->o_invoke_few_args(s_key
, 0);
374 Variant
ArrayIter::second() {
375 if (LIKELY(hasArrayData())) {
376 assert(m_pos
!= ArrayData::invalid_index
);
377 const ArrayData
* ad
= getArrayData();
379 return ad
->getValue(m_pos
);
381 switch (getCollectionType()) {
382 case Collection::VectorType
: {
383 auto* vec
= getVector();
384 if (UNLIKELY(m_version
!= vec
->getVersion())) {
385 throw_collection_modified();
387 return tvAsCVarRef(vec
->at(m_pos
));
389 case Collection::MapType
:
390 case Collection::ImmMapType
: {
391 BaseMap
* mp
= getMap();
392 if (UNLIKELY(m_version
!= mp
->getVersion())) {
393 throw_collection_modified();
395 return tvAsCVarRef(mp
->iter_value(m_pos
));
397 case Collection::SetType
: {
398 BaseSet
* st
= getSet();
399 if (UNLIKELY(m_version
!= st
->getVersion())) {
400 throw_collection_modified();
402 return tvAsCVarRef(st
->iter_value(m_pos
));
404 case Collection::PairType
: {
405 return tvAsCVarRef(getPair()->at(m_pos
));
407 case Collection::ImmVectorType
: {
408 c_ImmVector
* fvec
= getImmVector();
409 if (UNLIKELY(m_version
!= fvec
->getVersion())) {
410 throw_collection_modified();
412 return tvAsCVarRef(fvec
->at(m_pos
));
414 case Collection::ImmSetType
: {
415 c_ImmSet
* st
= getImmSet();
416 assert(m_version
== st
->getVersion());
417 return tvAsCVarRef(st
->iter_value(m_pos
));
419 case Collection::InvalidType
:
423 ObjectData
* obj
= getIteratorObj();
424 return obj
->o_invoke_few_args(s_current
, 0);
427 const Variant
& ArrayIter::secondRef() {
428 if (!hasArrayData()) {
429 throw FatalErrorException("taking reference on iterator objects");
431 assert(hasArrayData());
432 assert(m_pos
!= ArrayData::invalid_index
);
433 const ArrayData
* ad
= getArrayData();
435 return ad
->getValueRef(m_pos
);
438 const Variant
& ArrayIter::secondRefPlus() {
439 if (LIKELY(hasArrayData())) {
440 assert(m_pos
!= ArrayData::invalid_index
);
441 const ArrayData
* ad
= getArrayData();
443 return ad
->getValueRef(m_pos
);
445 switch (getCollectionType()) {
446 case Collection::VectorType
: {
447 auto* vec
= getVector();
448 if (UNLIKELY(m_version
!= vec
->getVersion())) {
449 throw_collection_modified();
451 return tvAsCVarRef(vec
->at(m_pos
));
453 case Collection::MapType
:
454 case Collection::ImmMapType
: {
455 BaseMap
* mp
= getMap();
456 if (UNLIKELY(m_version
!= mp
->getVersion())) {
457 throw_collection_modified();
459 return tvAsCVarRef(mp
->iter_value(m_pos
));
461 case Collection::SetType
: {
462 BaseSet
* st
= getSet();
463 if (UNLIKELY(m_version
!= st
->getVersion())) {
464 throw_collection_modified();
466 return tvAsCVarRef(st
->iter_value(m_pos
));
468 case Collection::PairType
: {
469 return tvAsCVarRef(getPair()->at(m_pos
));
471 case Collection::ImmVectorType
: {
472 c_ImmVector
* fvec
= getImmVector();
473 if (UNLIKELY(m_version
!= fvec
->getVersion())) {
474 throw_collection_modified();
476 return tvAsCVarRef(fvec
->at(m_pos
));
478 case Collection::ImmSetType
: {
479 c_ImmSet
* st
= getImmSet();
480 assert(m_version
== st
->getVersion());
481 return tvAsCVarRef(st
->iter_value(m_pos
));
483 case Collection::InvalidType
: {
484 throw_param_is_not_container();
491 // Collection iterator specialized functions.
494 template<class Tuplish
>
495 ArrayIter::ArrayIter(Tuplish
* coll
, Fixed
)
496 : m_pos(0), m_itype(ArrayIter::TypeIterator
) {
499 // TODO Task #4204598: In theory, we might be able to squeeze out a win
500 // here by not checking the version for immutable collections, but we'd
501 // to make sure all iteration implementations are consistent about this.
502 m_version
= coll
->getVersion();
505 template<class Vectorish
>
506 ArrayIter::ArrayIter(Vectorish
* coll
, Versionable
)
507 : m_pos(0), m_itype(ArrayIter::TypeIterator
) {
508 assert(coll
&& coll
->size() > 0);
510 m_version
= coll
->getVersion();
513 template<class Mappish
>
514 ArrayIter::ArrayIter(Mappish
* coll
, VersionableSparse
)
515 : m_itype(ArrayIter::TypeIterator
) {
516 assert(coll
&& coll
->size() > 0);
518 m_version
= coll
->getVersion();
519 m_pos
= coll
->iter_begin();
522 template<class Tuplish
>
524 bool ArrayIter::iterNext(Fixed
) {
525 return ++m_pos
< static_cast<Tuplish
*>(getObject())->size();
528 template<class Vectorish
>
530 bool ArrayIter::iterNext(Versionable
) {
531 Vectorish
* vec
= static_cast<Vectorish
*>(getObject());
532 if (UNLIKELY(m_version
!= vec
->getVersion())) {
533 throw_collection_modified();
535 return ++m_pos
< vec
->size();
538 template<class Mappish
>
540 bool ArrayIter::iterNext(VersionableSparse
) {
541 Mappish
* coll
= static_cast<Mappish
*>(getObject());
542 if (UNLIKELY(m_version
!= coll
->getVersion())) {
543 throw_collection_modified();
545 m_pos
= coll
->iter_next(m_pos
);
546 return coll
->iter_valid(m_pos
);
549 template<class Tuplish
>
551 Variant
ArrayIter::iterKey(Fixed
) {
555 template<class Vectorish
>
557 Variant
ArrayIter::iterKey(Versionable
) {
561 template<class Mappish
>
563 Variant
ArrayIter::iterKey(VersionableSparse
) {
564 return static_cast<Mappish
*>(getObject())->iter_key(m_pos
);
567 template<class Tuplish
>
569 Variant
ArrayIter::iterValue(Fixed
) {
570 return tvAsCVarRef(static_cast<Tuplish
*>(getObject())->get(m_pos
));
573 template<class Vectorish
>
575 Variant
ArrayIter::iterValue(Versionable
) {
576 return tvAsCVarRef(static_cast<Vectorish
*>(getObject())->get(m_pos
));
579 template<class Mappish
>
581 Variant
ArrayIter::iterValue(VersionableSparse
) {
582 return tvAsCVarRef(static_cast<Mappish
*>(getObject())->iter_value(m_pos
));
585 RefData
* ArrayIter::zSecond() {
586 auto tv
= nvSecond();
587 if (tv
->m_type
!= KindOfRef
) {
588 // FIXME: this is not allowed.
589 tvBox(const_cast<TypedValue
*>(tv
));
591 return tv
->m_data
.pref
;
594 //////////////////////////////////////////////////////////////////////
598 // Handle the cases where we didn't have enough preallocated Ents in
599 // tl_miter_table, and we need to allocate from `extras'.
601 MIterTable::Ent
* find_empty_strong_iter_slower() {
602 return tl_miter_table
.extras
.find_unpopulated();
605 // Handle finding an empty strong iterator slot when the first slot
606 // was already in use.
608 MIterTable::Ent
* find_empty_strong_iter_slow() {
610 if (LIKELY(!tl_miter_table.ents[i].array)) return &tl_miter_table.ents[i];
617 static_assert(tl_miter_table
.ents
.size() == 7, "");
619 return find_empty_strong_iter_slower();
622 // Find a strong iterator slot that is empty. Almost always the first
623 // one will be empty, so that path is inlined---everything else
624 // delegates to slow.
626 MIterTable::Ent
* find_empty_strong_iter() {
627 if (LIKELY(!tl_miter_table
.ents
[0].array
)) {
628 return &tl_miter_table
.ents
[0];
630 return find_empty_strong_iter_slow();
633 void newMArrayIter(MArrayIter
* marr
, ArrayData
* ad
) {
634 assert(!marr
->getContainer());
635 auto const slot
= find_empty_strong_iter();
636 assert(!slot
->array
);
639 marr
->setContainer(ad
);
640 marr
->m_pos
= ad
->getPosition();
641 assert(strong_iterators_exist());
645 void free_strong_iterator_impl(Cond cond
) {
646 assert(strong_iterators_exist());
648 // We need to maintain the invariant that if there are any strong
649 // iterators bound to arrays, one of the bindings is in slot zero.
650 // This pvalid will point to something we can move into the first
651 // slot if alreadyValid is false. If when we're done alreadyValid
652 // is false, and pvalid is also nullptr, it means this function
653 // freed the last strong iterator.
654 MIterTable::Ent
* pvalid
= nullptr;
655 bool alreadyValid
= true; // because strong_iterators_exist()
657 auto rm
= [&] (MIterTable::Ent
& ent
) {
659 ent
.iter
->setContainer(nullptr);
662 } else if (!alreadyValid
&& ent
.array
) {
667 if (cond(tl_miter_table
.ents
[0])) {
668 tl_miter_table
.ents
[0].iter
->setContainer(nullptr);
669 tl_miter_table
.ents
[0].array
= nullptr;
670 tl_miter_table
.ents
[0].iter
= nullptr;
671 alreadyValid
= false;
673 rm(tl_miter_table
.ents
[1]);
674 rm(tl_miter_table
.ents
[2]);
675 rm(tl_miter_table
.ents
[3]);
676 rm(tl_miter_table
.ents
[4]);
677 rm(tl_miter_table
.ents
[5]);
678 rm(tl_miter_table
.ents
[6]);
679 static_assert(tl_miter_table
.ents
.size() == 7, "");
681 if (UNLIKELY(pvalid
!= nullptr)) {
682 std::swap(*pvalid
, tl_miter_table
.ents
[0]);
685 if (LIKELY(tl_miter_table
.extras
.empty())) return;
687 tl_miter_table
.extras
.release_if([&] (const MIterTable::Ent
& e
) {
689 e
.iter
->setContainer(nullptr);
695 // If we didn't manage to keep something in the first non-extra
696 // slot, scan extras again to swap something over.
697 if (LIKELY(alreadyValid
)) return;
698 if (!tl_miter_table
.extras
.empty()) {
699 tl_miter_table
.extras
.visit_to_remove(
700 [&] (const MIterTable::Ent
& ent
) {
701 tl_miter_table
.ents
[0] = ent
;
707 void freeMArrayIter(MArrayIter
* marr
) {
708 assert(strong_iterators_exist());
709 free_strong_iterator_impl(
710 [marr
] (const MIterTable::Ent
& e
) {
711 return e
.iter
== marr
;
718 void free_strong_iterators(ArrayData
* ad
) {
719 free_strong_iterator_impl([ad
] (const MIterTable::Ent
& e
) {
720 return e
.array
== ad
;
725 * This function returns its first argument so that in some cases we
726 * can do tails calls (or maybe avoid spills).
728 * Note that in some cases reusing the return value can be (very
729 * slightly) worse. The compiler won't know that the return value is
730 * going to be the same as the argument, so if it didn't already have
731 * to spill to make the call, or it can't tail call for some other
732 * reason, you can cause an extra move after the return.
734 ArrayData
* move_strong_iterators(ArrayData
* dst
, ArrayData
* src
) {
735 for_each_strong_iterator([&] (MIterTable::Ent
& ent
) {
736 if (ent
.array
== src
) {
738 ent
.iter
->setContainer(dst
);
744 //////////////////////////////////////////////////////////////////////
746 MArrayIter::MArrayIter(RefData
* ref
)
748 , m_container(nullptr)
755 auto const data
= cowCheck();
758 newMArrayIter(this, data
);
761 assert(getContainer() == data
);
764 MArrayIter::MArrayIter(ArrayData
* data
)
767 , m_container(nullptr)
771 assert(!data
->isStatic());
776 newMArrayIter(this, data
);
779 assert(getContainer() == data
);
782 MArrayIter::~MArrayIter() {
783 auto const container
= getContainer();
785 freeMArrayIter(this);
786 assert(getContainer() == nullptr);
790 } else if (hasAd()) {
795 bool MArrayIter::end() const {
796 return !const_cast<MArrayIter
*>(this)->prepare();
799 bool MArrayIter::advance() {
800 ArrayData
* data
= getArray();
801 ArrayData
* container
= getContainer();
804 freeMArrayIter(this);
809 if (container
== data
) {
810 return cowCheck()->advanceMArrayIter(*this);
813 assert(data
&& data
== getContainer());
814 assert(!getResetFlag());
815 if (!data
->validMArrayIter(*this)) return false;
816 // To conform to PHP behavior, we need to set the internal
817 // cursor to point to the next element.
822 bool MArrayIter::prepare() {
823 ArrayData
* data
= getArray();
824 ArrayData
* container
= getContainer();
827 freeMArrayIter(this);
832 if (container
!= data
) {
835 return data
->validMArrayIter(*this);
838 void MArrayIter::escalateCheck() {
840 auto const data
= getData();
842 auto const esc
= data
->escalate();
844 cellSet(make_tv
<KindOfArray
>(esc
), *getRef()->tv());
850 auto const data
= getAd();
851 auto const esc
= data
->escalate();
859 ArrayData
* MArrayIter::cowCheck() {
861 auto data
= getData();
862 if (!data
) return nullptr;
863 if (data
->hasMultipleRefs() && !data
->noCopyOnWrite()) {
864 data
= data
->copyWithStrongIterators();
865 cellSet(make_tv
<KindOfArray
>(data
), *getRef()->tv());
871 auto const data
= getAd();
872 if (data
->hasMultipleRefs() && !data
->noCopyOnWrite()) {
873 ArrayData
* copied
= data
->copyWithStrongIterators();
874 copied
->incRefCount();
882 ArrayData
* MArrayIter::reregister() {
883 ArrayData
* container
= getContainer();
884 assert(getArray() != nullptr && container
!= getArray());
885 if (container
!= nullptr) {
886 freeMArrayIter(this);
889 assert(getContainer() == nullptr);
891 ArrayData
* data
= cowCheck();
892 newMArrayIter(this, data
);
896 //////////////////////////////////////////////////////////////////////
898 CufIter::~CufIter() {
899 if (m_ctx
&& !(uintptr_t(m_ctx
) & 1)) {
900 decRefObj((ObjectData
*)m_ctx
);
902 if (m_name
) decRefStr(m_name
);
905 bool Iter::init(TypedValue
* c1
) {
906 assert(c1
->m_type
!= KindOfRef
);
907 bool hasElems
= true;
908 if (c1
->m_type
== KindOfArray
) {
909 if (!c1
->m_data
.parr
->empty()) {
910 (void) new (&arr()) ArrayIter(c1
->m_data
.parr
);
911 arr().setIterType(ArrayIter::TypeArray
);
915 } else if (c1
->m_type
== KindOfObject
) {
917 if (c1
->m_data
.pobj
->isCollection()) {
919 (void) new (&arr()) ArrayIter(c1
->m_data
.pobj
);
921 Object obj
= c1
->m_data
.pobj
->iterableObject(isIterator
);
923 (void) new (&arr()) ArrayIter(obj
.detach(), ArrayIter::noInc
);
925 Class
* ctx
= arGetContextClass(g_context
->getFP());
926 auto ctxStr
= ctx
? ctx
->nameStr() : StrNR();
927 Array
iterArray(obj
->o_toIterArray(ctxStr
));
928 ArrayData
* ad
= iterArray
.get();
929 (void) new (&arr()) ArrayIter(ad
);
934 // Iterator was empty; call the destructor on the iterator we
935 // just constructed and branch to done case
940 isIterator
? ArrayIter::TypeIterator
: ArrayIter::TypeArray
);
947 raise_warning("Invalid argument supplied for foreach()");
954 assert(arr().getIterType() == ArrayIter::TypeArray
||
955 arr().getIterType() == ArrayIter::TypeIterator
);
956 // The emitter should never generate bytecode where the iterator
957 // is at the end before IterNext is executed. However, even if
958 // the iterator is at the end, it is safe to call next().
959 ArrayIter
* ai
= &arr();
962 // If after advancing the iterator we have reached the end, free
963 // the iterator and fall through to the next instruction.
964 // The ArrayIter destructor will decRef the array.
968 // If after advancing the iterator we have not reached the end,
969 // jump to the location specified by the second immediate argument.
974 assert(arr().getIterType() == ArrayIter::TypeArray
||
975 arr().getIterType() == ArrayIter::TypeIterator
);
980 marr().~MArrayIter();
988 * Helper functions for collection style iterators.
989 * Iterators over collections are never by-ref so there is no reason to
991 * Templates are instantiated over the collection class and the iterator
992 * style. See the definition of Fixed, Versionable and VersionableSparse
993 * in the header for details.
994 * IterInit and IterNext can be called directly from the JIT for specialized
997 template<class Coll
, class Style
>
998 static void iterValue(ArrayIter
* iter
, TypedValue
* out
) {
999 Variant val
= iter
->iterValue
<Coll
>(Style());
1000 assert(val
.getRawType() != KindOfRef
);
1001 cellDup(*val
.asTypedValue(), *out
);
1004 template<class Coll
, class Style
>
1005 static void iterKey(ArrayIter
* iter
, TypedValue
* out
) {
1006 Variant key
= iter
->iterKey
<Coll
>(Style());
1007 cellDup(*key
.asTypedValue(), *out
);
1010 template<class Coll
, class Style
>
1011 static int64_t iterInit(Iter
* dest
, Coll
* coll
,
1012 TypedValue
* valOut
, TypedValue
* keyOut
) {
1013 int64_t size
= coll
->size();
1014 if (UNLIKELY(size
== 0)) {
1018 (void) new (&dest
->arr()) ArrayIter(coll
, Style());
1020 DataType vType
= valOut
->m_type
;
1021 assert(vType
!= KindOfRef
);
1022 uint64_t vDatum
= valOut
->m_data
.num
;
1023 iterValue
<Coll
, Style
>(&dest
->arr(), valOut
);
1024 tvRefcountedDecRefHelper(vType
, vDatum
);
1027 DataType kType
= keyOut
->m_type
;
1028 uint64_t kDatum
= keyOut
->m_data
.num
;
1029 iterKey
<Coll
, Style
>(&dest
->arr(), keyOut
);
1030 tvRefcountedDecRefHelper(kType
, kDatum
);
1035 template<class Coll
, class Style
>
1037 int64_t iterNext(ArrayIter
* iter
, TypedValue
* valOut
, TypedValue
* keyOut
) {
1038 if (!iter
->iterNext
<Coll
>(Style())) {
1043 DataType vType
= valOut
->m_type
;
1044 assert(vType
!= KindOfRef
);
1045 uint64_t vDatum
= valOut
->m_data
.num
;
1046 iterValue
<Coll
, Style
>(iter
, valOut
);
1047 tvRefcountedDecRefHelper(vType
, vDatum
);
1050 DataType kType
= keyOut
->m_type
;
1051 uint64_t kDatum
= keyOut
->m_data
.num
;
1052 iterKey
<Coll
, Style
>(iter
, keyOut
);
1053 tvRefcountedDecRefHelper(kType
, kDatum
);
1059 * iter_value_cell* will store a copy of the current value at the address
1060 * given by 'out'. iter_value_cell* will increment the refcount of the current
1061 * value if appropriate.
1064 template <bool typeArray
, bool withRef
>
1065 static inline void iter_value_cell_local_impl(Iter
* iter
, TypedValue
* out
) {
1066 DataType oldType
= out
->m_type
;
1067 assert(withRef
|| oldType
!= KindOfRef
);
1068 uint64_t oldDatum
= out
->m_data
.num
;
1069 TRACE(2, "%s: typeArray: %s, I %p, out %p\n",
1070 __func__
, typeArray
? "true" : "false", iter
, out
);
1071 assert((typeArray
&& iter
->arr().getIterType() == ArrayIter::TypeArray
) ||
1072 (!typeArray
&& iter
->arr().getIterType() == ArrayIter::TypeIterator
));
1073 ArrayIter
& arrIter
= iter
->arr();
1075 auto const cur
= arrIter
.nvSecond();
1076 if (cur
->m_type
== KindOfRef
) {
1077 if (!withRef
|| !cur
->m_data
.pref
->isReferenced()) {
1078 cellDup(*(cur
->m_data
.pref
->tv()), *out
);
1083 cellDup(*cur
, *out
);
1086 Variant val
= arrIter
.second();
1087 assert(val
.getRawType() != KindOfRef
);
1088 cellDup(*val
.asTypedValue(), *out
);
1090 tvRefcountedDecRefHelper(oldType
, oldDatum
);
1093 template <bool typeArray
, bool withRef
>
1094 static inline void iter_key_cell_local_impl(Iter
* iter
, TypedValue
* out
) {
1095 DataType oldType
= out
->m_type
;
1096 assert(withRef
|| oldType
!= KindOfRef
);
1097 uint64_t oldDatum
= out
->m_data
.num
;
1098 TRACE(2, "%s: I %p, out %p\n", __func__
, iter
, out
);
1099 assert((typeArray
&& iter
->arr().getIterType() == ArrayIter::TypeArray
) ||
1100 (!typeArray
&& iter
->arr().getIterType() == ArrayIter::TypeIterator
));
1101 ArrayIter
& arr
= iter
->arr();
1105 Variant key
= arr
.first();
1106 cellDup(*key
.asTypedValue(), *out
);
1108 tvRefcountedDecRefHelper(oldType
, oldDatum
);
1112 int64_t iter_next_free_packed(Iter
* iter
, ArrayData
* arr
) {
1113 assert(arr
->hasExactlyOneRef());
1114 assert(arr
->isPacked());
1115 PackedArray::Release(arr
);
1117 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1123 int64_t iter_next_free_mixed(Iter
* iter
, ArrayData
* arr
) {
1124 assert(arr
->isMixed());
1125 assert(arr
->hasExactlyOneRef());
1126 MixedArray::Release(arr
);
1128 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1134 static int64_t iter_next_free_apc(Iter
* iter
, APCLocalArray
* arr
) {
1135 assert(arr
->hasExactlyOneRef());
1136 APCLocalArray::Release(arr
->asArrayData());
1138 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1144 * new_iter_array creates an iterator for the specified array iff the
1145 * array is not empty. If new_iter_array creates an iterator, it does
1146 * not increment the refcount of the specified array. If
1147 * new_iter_array does not create an iterator, it decRefs the array.
1149 template <bool withRef
>
1151 int64_t new_iter_array_cold(Iter
* dest
, ArrayData
* arr
, TypedValue
* valOut
,
1152 TypedValue
* keyOut
) {
1153 TRACE(2, "%s: I %p, arr %p\n", __func__
, dest
, arr
);
1155 valOut
= tvToCell(valOut
);
1156 if (keyOut
) keyOut
= tvToCell(keyOut
);
1158 if (!arr
->empty()) {
1159 // We are transferring ownership of the array to the iterator, therefore
1160 // we do not need to adjust the refcount.
1161 (void) new (&dest
->arr()) ArrayIter(arr
, ArrayIter::noInc
);
1162 dest
->arr().setIterType(ArrayIter::TypeArray
);
1163 iter_value_cell_local_impl
<true, withRef
>(dest
, valOut
);
1165 iter_key_cell_local_impl
<true, withRef
>(dest
, keyOut
);
1169 // We did not transfer ownership of the array to an iterator, so we need
1170 // to decRef the array.
1175 int64_t new_iter_array(Iter
* dest
, ArrayData
* ad
, TypedValue
* valOut
) {
1176 TRACE(2, "%s: I %p, ad %p\n", __func__
, dest
, ad
);
1177 if (UNLIKELY(ad
->getSize() == 0)) {
1178 if (UNLIKELY(ad
->hasExactlyOneRef())) {
1179 if (ad
->isPacked()) return iter_next_free_packed(dest
, ad
);
1180 if (ad
->isMixed()) return iter_next_free_mixed(dest
, ad
);
1185 if (UNLIKELY(IS_REFCOUNTED_TYPE(valOut
->m_type
))) {
1186 return new_iter_array_cold
<false>(dest
, ad
, valOut
, nullptr);
1189 // We are transferring ownership of the array to the iterator, therefore
1190 // we do not need to adjust the refcount.
1191 auto& aiter
= dest
->arr();
1193 auto const itypeU32
= static_cast<uint32_t>(ArrayIter::TypeArray
);
1195 if (LIKELY(ad
->isPacked())) {
1197 aiter
.m_itypeAndNextHelperIdx
=
1198 static_cast<uint32_t>(IterNextIndex::ArrayPacked
) << 16 | itypeU32
;
1199 assert(aiter
.m_itype
== ArrayIter::TypeArray
);
1200 assert(aiter
.m_nextHelperIdx
== IterNextIndex::ArrayPacked
);;
1201 cellDup(*tvToCell(packedData(ad
)), *valOut
);
1205 if (LIKELY(ad
->isMixed())) {
1206 auto const mixed
= MixedArray::asMixed(ad
);
1207 aiter
.m_pos
= mixed
->getIterBegin();
1208 aiter
.m_itypeAndNextHelperIdx
=
1209 static_cast<uint32_t>(IterNextIndex::ArrayMixed
) << 16 | itypeU32
;
1210 assert(aiter
.m_itype
== ArrayIter::TypeArray
);
1211 assert(aiter
.m_nextHelperIdx
== IterNextIndex::ArrayMixed
);
1212 mixed
->getArrayElm(aiter
.m_pos
, valOut
);
1216 return new_iter_array_cold
<false>(dest
, ad
, valOut
, nullptr);
1219 template<bool WithRef
>
1220 int64_t new_iter_array_key(Iter
* dest
,
1223 TypedValue
* keyOut
) {
1224 if (UNLIKELY(ad
->getSize() == 0)) {
1225 if (UNLIKELY(ad
->hasExactlyOneRef())) {
1226 if (ad
->isPacked()) return iter_next_free_packed(dest
, ad
);
1227 if (ad
->isMixed()) return iter_next_free_mixed(dest
, ad
);
1232 if (UNLIKELY(IS_REFCOUNTED_TYPE(valOut
->m_type
))) {
1233 return new_iter_array_cold
<false>(dest
, ad
, valOut
, keyOut
);
1235 if (UNLIKELY(IS_REFCOUNTED_TYPE(keyOut
->m_type
))) {
1236 return new_iter_array_cold
<false>(dest
, ad
, valOut
, keyOut
);
1239 // We are transferring ownership of the array to the iterator, therefore
1240 // we do not need to adjust the refcount.
1241 auto& aiter
= dest
->arr();
1243 auto const itypeU32
= static_cast<uint32_t>(ArrayIter::TypeArray
);
1245 if (ad
->isPacked()) {
1247 aiter
.m_itypeAndNextHelperIdx
=
1248 static_cast<uint32_t>(IterNextIndex::ArrayPacked
) << 16 | itypeU32
;
1249 assert(aiter
.m_itype
== ArrayIter::TypeArray
);
1250 assert(aiter
.m_nextHelperIdx
== IterNextIndex::ArrayPacked
);
1252 tvDupWithRef(*packedData(ad
), *valOut
);
1254 cellDup(*tvToCell(packedData(ad
)), *valOut
);
1256 keyOut
->m_type
= KindOfInt64
;
1257 keyOut
->m_data
.num
= 0;
1261 if (ad
->isMixed()) {
1262 auto const mixed
= MixedArray::asMixed(ad
);
1263 aiter
.m_pos
= mixed
->getIterBegin();
1264 aiter
.m_itypeAndNextHelperIdx
=
1265 static_cast<uint32_t>(IterNextIndex::ArrayMixed
) << 16 | itypeU32
;
1266 assert(aiter
.m_itype
== ArrayIter::TypeArray
);
1267 assert(aiter
.m_nextHelperIdx
== IterNextIndex::ArrayMixed
);
1269 mixed
->dupArrayElmWithRef(aiter
.m_pos
, valOut
, keyOut
);
1271 mixed
->getArrayElm(aiter
.m_pos
, valOut
, keyOut
);
1276 return new_iter_array_cold
<false>(dest
, ad
, valOut
, keyOut
);
1279 template int64_t new_iter_array_key
<false>(Iter
* dest
, ArrayData
* ad
,
1281 TypedValue
* keyOut
);
1282 template int64_t new_iter_array_key
<true>(Iter
* dest
, ArrayData
* ad
,
1284 TypedValue
* keyOut
);
1288 FreeObj() : m_obj(0) {}
1289 void operator=(ObjectData
* obj
) { m_obj
= obj
; }
1290 ~FreeObj() { if (UNLIKELY(m_obj
!= nullptr)) decRefObj(m_obj
); }
1296 * new_iter_object_any creates an iterator for the specified object if the
1297 * object is iterable and it is non-empty (has properties). If
1298 * new_iter_object_any creates an iterator, it does not increment the refcount
1299 * of the specified object. If new_iter_object does not create an iterator,
1300 * it decRefs the object.
1302 * If exceptions are thrown, new_iter_object_any takes care of decRefing the
1305 static int64_t new_iter_object_any(Iter
* dest
, ObjectData
* obj
, Class
* ctx
,
1306 TypedValue
* valOut
, TypedValue
* keyOut
) {
1307 valOut
= tvToCell(valOut
);
1309 keyOut
= tvToCell(keyOut
);
1311 ArrayIter::Type itType
;
1314 if (obj
->implementsIterator()) {
1315 TRACE(2, "%s: I %p, obj %p, ctx %p, collection or Iterator\n",
1316 __func__
, dest
, obj
, ctx
);
1317 (void) new (&dest
->arr()) ArrayIter(obj
, ArrayIter::noInc
);
1318 itType
= ArrayIter::TypeIterator
;
1320 bool isIteratorAggregate
;
1322 * We are not going to transfer ownership of obj to the iterator,
1323 * so arrange to decRef it later. The actual decRef has to happen
1324 * after the call to arr().end() below, because both can have visible side
1325 * effects (calls to __destruct() and valid()). Similarly it has to
1326 * happen before the iter_*_cell_local_impl calls below, because they call
1327 * current() and key() (hence the explicit scope around FreeObj fo;)
1331 Object itObj
= obj
->iterableObject(isIteratorAggregate
, false);
1332 if (isIteratorAggregate
) {
1333 TRACE(2, "%s: I %p, obj %p, ctx %p, IteratorAggregate\n",
1334 __func__
, dest
, obj
, ctx
);
1335 (void) new (&dest
->arr()) ArrayIter(itObj
.detach(), ArrayIter::noInc
);
1336 itType
= ArrayIter::TypeIterator
;
1338 TRACE(2, "%s: I %p, obj %p, ctx %p, iterate as array\n",
1339 __func__
, dest
, obj
, ctx
);
1340 auto ctxStr
= ctx
? ctx
->nameStr() : StrNR();
1341 Array
iterArray(itObj
->o_toIterArray(ctxStr
));
1342 ArrayData
* ad
= iterArray
.get();
1343 (void) new (&dest
->arr()) ArrayIter(ad
);
1344 itType
= ArrayIter::TypeArray
;
1348 if (dest
->arr().end()) {
1349 // Iterator was empty; call the destructor on the iterator we just
1351 dest
->arr().~ArrayIter();
1355 dest
->arr().~ArrayIter();
1360 dest
->arr().setIterType(itType
);
1361 if (itType
== ArrayIter::TypeIterator
) {
1362 iter_value_cell_local_impl
<false, false>(dest
, valOut
);
1364 iter_key_cell_local_impl
<false, false>(dest
, keyOut
);
1367 iter_value_cell_local_impl
<true, false>(dest
, valOut
);
1369 iter_key_cell_local_impl
<true, false>(dest
, keyOut
);
1375 int64_t new_iter_object(Iter
* dest
, ObjectData
* obj
, Class
* ctx
,
1376 TypedValue
* valOut
, TypedValue
* keyOut
) {
1377 TRACE(2, "%s: I %p, obj %p, ctx %p, collection or Iterator or Object\n",
1378 __func__
, dest
, obj
, ctx
);
1379 Collection::Type type
= obj
->getCollectionType();
1381 case Collection::VectorType
:
1382 return iterInit
<c_Vector
, ArrayIter::Versionable
>(
1383 dest
, static_cast<c_Vector
*>(obj
),
1385 case Collection::MapType
:
1386 case Collection::ImmMapType
:
1387 return iterInit
<BaseMap
, ArrayIter::VersionableSparse
>(
1389 static_cast<BaseMap
*>(obj
),
1391 case Collection::SetType
:
1392 return iterInit
<c_Set
, ArrayIter::VersionableSparse
>(
1394 static_cast<c_Set
*>(obj
),
1396 case Collection::PairType
:
1397 return iterInit
<c_Pair
, ArrayIter::Fixed
>(
1399 static_cast<c_Pair
*>(obj
),
1401 case Collection::ImmVectorType
:
1402 return iterInit
<c_ImmVector
, ArrayIter::Fixed
>(
1403 dest
, static_cast<c_ImmVector
*>(obj
),
1405 case Collection::ImmSetType
:
1406 return iterInit
<c_ImmSet
, ArrayIter::VersionableSparse
>(
1408 static_cast<c_ImmSet
*>(obj
),
1410 case Collection::InvalidType
:
1411 return new_iter_object_any(dest
, obj
, ctx
, valOut
, keyOut
);
1416 template <bool withRef
>
1418 static int64_t iter_next_collection(ArrayIter
* ai
,
1421 Collection::Type type
) {
1422 assert(!ai
->hasArrayData());
1423 assert(type
!= Collection::InvalidType
);
1426 case Collection::VectorType
:
1427 return iterNext
<c_Vector
, ArrayIter::Versionable
>(
1428 ai
, valOut
, keyOut
);
1429 case Collection::MapType
:
1430 case Collection::ImmMapType
:
1431 return iterNext
<BaseMap
, ArrayIter::VersionableSparse
>(
1432 ai
, valOut
, keyOut
);
1433 case Collection::SetType
:
1434 return iterNext
<c_Set
, ArrayIter::VersionableSparse
>(
1435 ai
, valOut
, keyOut
);
1436 case Collection::PairType
:
1437 return iterNext
<c_Pair
, ArrayIter::Fixed
>(
1438 ai
, valOut
, keyOut
);
1439 case Collection::ImmVectorType
:
1440 return iterNext
<c_ImmVector
, ArrayIter::Fixed
>(
1441 ai
, valOut
, keyOut
);
1442 case Collection::ImmSetType
:
1443 return iterNext
<c_ImmSet
, ArrayIter::VersionableSparse
>(
1444 ai
, valOut
, keyOut
);
1445 case Collection::InvalidType
:
1451 template <bool withRef
>
1453 int64_t iter_next_cold(Iter
* iter
, TypedValue
* valOut
, TypedValue
* keyOut
) {
1454 auto const ai
= &iter
->arr();
1455 assert(ai
->getIterType() == ArrayIter::TypeArray
||
1456 ai
->getIterType() == ArrayIter::TypeIterator
);
1457 if (UNLIKELY(!ai
->hasArrayData())) {
1458 auto const coll
= ai
->getObject()->getCollectionType();
1459 if (UNLIKELY(coll
!= Collection::InvalidType
)) {
1460 return iter_next_collection
<withRef
>(ai
, valOut
, keyOut
, coll
);
1466 // The ArrayIter destructor will decRef the array
1470 if (iter
->arr().getIterType() == ArrayIter::TypeArray
) {
1471 iter_value_cell_local_impl
<true, withRef
>(iter
, valOut
);
1473 iter_key_cell_local_impl
<true, withRef
>(iter
, keyOut
);
1476 iter_value_cell_local_impl
<false, withRef
>(iter
, valOut
);
1478 iter_key_cell_local_impl
<false, withRef
>(iter
, keyOut
);
1485 static int64_t iter_next_apc_array(Iter
* iter
,
1489 assert(ad
->kind() == ArrayData::kSharedKind
);
1491 auto const arrIter
= &iter
->arr();
1492 auto const arr
= APCLocalArray::asSharedArray(ad
);
1493 ssize_t
const pos
= arr
->iterAdvanceImpl(arrIter
->getPos());
1494 if (UNLIKELY(pos
== ArrayData::invalid_index
)) {
1495 if (UNLIKELY(arr
->hasExactlyOneRef())) {
1496 return iter_next_free_apc(iter
, arr
);
1500 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1504 arrIter
->setPos(pos
);
1506 // Note that APCLocalArray can never return KindOfRefs.
1507 const Variant
& var
= APCLocalArray::GetValueRef(arr
->asArrayData(), pos
);
1508 assert(var
.asTypedValue()->m_type
!= KindOfRef
);
1509 cellSet(*var
.asTypedValue(), *valOut
);
1510 if (LIKELY(!keyOut
)) return 1;
1513 APCLocalArray::NvGetKey(ad
, &key
, pos
);
1514 auto const keyType
= keyOut
->m_type
;
1515 auto const keyDatum
= keyOut
->m_data
.num
;
1516 cellCopy(key
, *keyOut
);
1517 tvRefcountedDecRefHelper(keyType
, keyDatum
);
1522 int64_t witer_next_key(Iter
* iter
, TypedValue
* valOut
, TypedValue
* keyOut
) {
1523 TRACE(2, "iter_next_key: I %p\n", iter
);
1524 assert(iter
->arr().getIterType() == ArrayIter::TypeArray
||
1525 iter
->arr().getIterType() == ArrayIter::TypeIterator
);
1526 auto const arrIter
= &iter
->arr();
1527 if (UNLIKELY(!arrIter
->hasArrayData())) {
1532 auto const ad
= const_cast<ArrayData
*>(arrIter
->getArrayData());
1533 auto const isPacked
= ad
->isPacked();
1534 auto const isMixed
= ad
->isMixed();
1536 if (UNLIKELY(!isMixed
&& !isPacked
)) {
1537 if (ad
->isSharedArray()) {
1538 // TODO(#4055855): what if a local value in an apc array has
1539 // been turned into a ref? Is this actually ok to do?
1540 return iter_next_apc_array(iter
, valOut
, keyOut
, ad
);
1545 if (LIKELY(isPacked
)) {
1546 ssize_t pos
= arrIter
->getPos() + 1;
1547 if (size_t(pos
) >= size_t(ad
->getSize())) {
1548 if (UNLIKELY(ad
->hasExactlyOneRef())) {
1549 return iter_next_free_packed(iter
, ad
);
1553 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1558 if (UNLIKELY(tvDecRefWillCallHelper(valOut
)) ||
1559 UNLIKELY(tvDecRefWillCallHelper(keyOut
))) {
1562 tvDecRefOnly(valOut
);
1563 tvDecRefOnly(keyOut
);
1565 arrIter
->setPos(pos
);
1566 tvDupWithRef(packedData(ad
)[pos
], *valOut
);
1567 keyOut
->m_type
= KindOfInt64
;
1568 keyOut
->m_data
.num
= pos
;
1572 auto const mixed
= MixedArray::asMixed(ad
);
1573 ssize_t pos
= arrIter
->getPos();
1576 if (size_t(pos
) >= size_t(mixed
->iterLimit())) {
1577 if (UNLIKELY(mixed
->hasExactlyOneRef())) {
1578 return iter_next_free_mixed(iter
, mixed
->asArrayData());
1580 mixed
->decRefCount();
1582 iter
->arr().setIterType(ArrayIter::TypeUndefined
);
1586 } while (UNLIKELY(mixed
->isTombstone(pos
)));
1588 if (UNLIKELY(tvDecRefWillCallHelper(valOut
)) ||
1589 UNLIKELY(tvDecRefWillCallHelper(keyOut
))) {
1592 tvDecRefOnly(valOut
);
1593 tvDecRefOnly(keyOut
);
1595 arrIter
->setPos(pos
);
1596 mixed
->dupArrayElmWithRef(pos
, valOut
, keyOut
);
1601 return iter_next_cold
<true>(iter
, valOut
, keyOut
);
1604 ///////////////////////////////////////////////////////////////////////////////
1607 int64_t new_miter_array_key(Iter
* dest
, RefData
* v1
,
1608 TypedValue
* valOut
, TypedValue
* keyOut
) {
1609 TRACE(2, "%s: I %p, ad %p\n", __func__
, dest
, v1
);
1611 TypedValue
* rtv
= v1
->tv();
1612 ArrayData
* ad
= rtv
->m_data
.parr
;
1614 if (UNLIKELY(ad
->empty())) {
1618 (void) new (&dest
->marr()) MArrayIter(v1
);
1619 dest
->marr().advance();
1621 tvAsVariant(valOut
).assignRef(dest
->marr().val());
1623 tvAsVariant(keyOut
).assign(dest
->marr().key());
1629 int64_t new_miter_object(Iter
* dest
, RefData
* ref
, Class
* ctx
,
1630 TypedValue
* valOut
, TypedValue
* keyOut
) {
1631 ObjectData
*obj
= ref
->tv()->m_data
.pobj
;
1632 if (obj
->isCollection()) {
1633 raise_error("Collection elements cannot be taken by reference");
1637 Object itObj
= obj
->iterableObject(isIterator
);
1639 raise_error("An iterator cannot be used with foreach by reference");
1642 TRACE(2, "%s: I %p, obj %p, ctx %p, iterate as array\n",
1643 __func__
, dest
, obj
, ctx
);
1644 auto ctxStr
= ctx
? ctx
->nameStr() : StrNR();
1645 Array
iterArray(itObj
->o_toIterArray(ctxStr
, true));
1646 ArrayData
* ad
= iterArray
.detach();
1647 (void) new (&dest
->marr()) MArrayIter(ad
);
1648 if (UNLIKELY(!dest
->marr().advance())) {
1649 // Iterator was empty; call the destructor on the iterator we just
1651 dest
->marr().~MArrayIter();
1655 tvAsVariant(valOut
).assignRef(dest
->marr().val());
1657 tvAsVariant(keyOut
).assign(dest
->marr().key());
1662 int64_t new_miter_other(Iter
* dest
, RefData
* data
) {
1663 TRACE(2, "%s: I %p, data %p, invalid type\n",
1664 __func__
, dest
, data
);
1666 // TODO(#2570852): we should really issue a warning here
1670 int64_t miter_next_key(Iter
* iter
, TypedValue
* valOut
, TypedValue
* keyOut
) {
1671 TRACE(2, "miter_next_key: I %p\n", iter
);
1672 MArrayIter
& marr
= iter
->marr();
1674 if (UNLIKELY(!marr
.advance())) {
1679 tvAsVariant(valOut
).assignRef(marr
.val());
1681 tvAsVariant(keyOut
).assign(marr
.key());
1687 ///////////////////////////////////////////////////////////////////////////////
1688 // IterNext/IterNextK helpers
1692 template<bool HasKey
>
1694 int64_t iter_next_mixed_impl(Iter
* it
,
1696 TypedValue
* keyOut
) {
1697 ArrayIter
& iter
= it
->arr();
1698 auto const arrData
= const_cast<ArrayData
*>(iter
.getArrayData());
1699 auto const arr
= MixedArray::asMixed(arrData
);
1700 ssize_t pos
= iter
.getPos();
1703 if (size_t(++pos
) >= size_t(arr
->iterLimit())) {
1704 if (UNLIKELY(arr
->hasExactlyOneRef())) {
1705 return iter_next_free_mixed(it
, arr
->asArrayData());
1709 iter
.setIterType(ArrayIter::TypeUndefined
);
1713 } while (UNLIKELY(arr
->isTombstone(pos
)));
1715 if (UNLIKELY(tvDecRefWillCallHelper(valOut
))) {
1716 return iter_next_cold
<false>(it
, valOut
, keyOut
);
1718 if (HasKey
&& UNLIKELY(tvDecRefWillCallHelper(keyOut
))) {
1719 return iter_next_cold
<false>(it
, valOut
, keyOut
);
1721 tvDecRefOnly(valOut
);
1723 tvDecRefOnly(keyOut
);
1727 arr
->getArrayElm(pos
, valOut
, keyOut
);
1729 arr
->getArrayElm(pos
, valOut
);
1734 template<bool HasKey
>
1735 int64_t iter_next_packed_impl(Iter
* it
,
1737 TypedValue
* keyOut
) {
1738 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1739 it
->arr().hasArrayData() &&
1740 it
->arr().getArrayData()->isPacked());
1741 auto& iter
= it
->arr();
1742 auto const ad
= const_cast<ArrayData
*>(iter
.getArrayData());
1743 assert(PackedArray::checkInvariants(ad
));
1745 ssize_t pos
= iter
.getPos() + 1;
1746 if (LIKELY(pos
< ad
->getSize())) {
1747 if (UNLIKELY(tvDecRefWillCallHelper(valOut
))) {
1748 return iter_next_cold
<false>(it
, valOut
, keyOut
);
1750 if (HasKey
&& UNLIKELY(tvDecRefWillCallHelper(keyOut
))) {
1751 return iter_next_cold
<false>(it
, valOut
, keyOut
);
1753 tvDecRefOnly(valOut
);
1755 cellDup(*tvToCell(packedData(ad
) + pos
), *valOut
);
1757 tvDecRefOnly(keyOut
);
1758 keyOut
->m_data
.num
= pos
;
1759 keyOut
->m_type
= KindOfInt64
;
1764 // Finished iterating---we need to free the array.
1765 if (UNLIKELY(ad
->hasExactlyOneRef())) {
1766 return iter_next_free_packed(it
, ad
);
1770 iter
.setIterType(ArrayIter::TypeUndefined
);
1777 int64_t iterNextArrayPacked(Iter
* it
, TypedValue
* valOut
) {
1778 TRACE(2, "iterNextArrayPacked: I %p\n", it
);
1779 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1780 it
->arr().hasArrayData() &&
1781 it
->arr().getArrayData()->isPacked());
1782 return iter_next_packed_impl
<false>(it
, valOut
, nullptr);
1785 int64_t iterNextKArrayPacked(Iter
* it
,
1787 TypedValue
* keyOut
) {
1788 TRACE(2, "iterNextKArrayPacked: I %p\n", it
);
1789 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1790 it
->arr().hasArrayData() &&
1791 it
->arr().getArrayData()->isPacked());
1792 return iter_next_packed_impl
<true>(it
, valOut
, keyOut
);
1795 int64_t iterNextArrayMixed(Iter
* it
, TypedValue
* valOut
) {
1796 TRACE(2, "iterNextArrayMixed: I %p\n", it
);
1797 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1798 it
->arr().hasArrayData() &&
1799 it
->arr().getArrayData()->isMixed());
1800 return iter_next_mixed_impl
<false>(it
, valOut
, nullptr);
1803 int64_t iterNextKArrayMixed(Iter
* it
,
1805 TypedValue
* keyOut
) {
1806 TRACE(2, "iterNextKArrayMixed: I %p\n", it
);
1807 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1808 it
->arr().hasArrayData() &&
1809 it
->arr().getArrayData()->isMixed());
1810 return iter_next_mixed_impl
<true>(it
, valOut
, keyOut
);
1813 int64_t iterNextArray(Iter
* it
, TypedValue
* valOut
) {
1814 TRACE(2, "iterNextArray: I %p\n", it
);
1815 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1816 it
->arr().hasArrayData());
1817 assert(!it
->arr().getArrayData()->isPacked());
1818 assert(!it
->arr().getArrayData()->isMixed());
1820 ArrayIter
& iter
= it
->arr();
1821 auto const ad
= const_cast<ArrayData
*>(iter
.getArrayData());
1822 if (ad
->isSharedArray()) {
1823 return iter_next_apc_array(it
, valOut
, nullptr, ad
);
1825 return iter_next_cold
<false>(it
, valOut
, nullptr);
1828 int64_t iterNextKArray(Iter
* it
,
1830 TypedValue
* keyOut
) {
1831 TRACE(2, "iterNextKArray: I %p\n", it
);
1832 assert(it
->arr().getIterType() == ArrayIter::TypeArray
&&
1833 it
->arr().hasArrayData());
1834 assert(!it
->arr().getArrayData()->isMixed());
1835 assert(!it
->arr().getArrayData()->isPacked());
1837 ArrayIter
& iter
= it
->arr();
1838 auto const ad
= const_cast<ArrayData
*>(iter
.getArrayData());
1839 if (ad
->isSharedArray()) {
1840 return iter_next_apc_array(it
, valOut
, keyOut
, ad
);
1842 return iter_next_cold
<false>(it
, valOut
, keyOut
);
1845 int64_t iterNextVector(Iter
* it
, TypedValue
* valOut
) {
1846 TRACE(2, "iterNextVector: I %p\n", it
);
1847 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1848 it
->arr().hasCollection());
1850 auto const iter
= &it
->arr();
1851 return iterNext
<c_Vector
, ArrayIter::Versionable
>(iter
, valOut
, nullptr);
1854 int64_t iterNextKVector(Iter
* it
,
1856 TypedValue
* keyOut
) {
1857 TRACE(2, "iterNextKVector: I %p\n", it
);
1858 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1859 it
->arr().hasCollection());
1861 auto const iter
= &it
->arr();
1862 return iterNext
<c_Vector
, ArrayIter::Versionable
>(iter
, valOut
, keyOut
);
1865 int64_t iterNextImmVector(Iter
* it
, TypedValue
* valOut
) {
1866 TRACE(2, "iterFrozenNextVector: I %p\n", it
);
1867 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1868 it
->arr().hasCollection());
1870 auto const iter
= &it
->arr();
1871 return iterNext
<c_ImmVector
, ArrayIter::Fixed
>(iter
, valOut
, nullptr);
1874 int64_t iterNextKImmVector(Iter
* it
,
1876 TypedValue
* keyOut
) {
1877 TRACE(2, "iterFrozenNextKVector: I %p\n", it
);
1878 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1879 it
->arr().hasCollection());
1881 auto const iter
= &it
->arr();
1882 return iterNext
<c_ImmVector
, ArrayIter::Fixed
>(iter
, valOut
, keyOut
);
1885 int64_t iterNextMap(Iter
* it
, TypedValue
* valOut
) {
1886 TRACE(2, "iterNextMap: I %p\n", it
);
1887 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1888 it
->arr().hasCollection());
1890 auto const iter
= &it
->arr();
1891 return iterNext
<c_Map
, ArrayIter::VersionableSparse
>(iter
, valOut
, nullptr);
1894 int64_t iterNextImmMap(Iter
* it
, TypedValue
* valOut
) {
1895 TRACE(2, "iterNextImmMap: I %p\n", it
);
1896 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1897 it
->arr().hasCollection());
1899 auto const iter
= &it
->arr();
1901 iterNext
<c_ImmMap
, ArrayIter::VersionableSparse
>(iter
, valOut
, nullptr);
1904 int64_t iterNextKMap(Iter
* it
,
1906 TypedValue
* keyOut
) {
1907 TRACE(2, "iterNextKMap: I %p\n", it
);
1908 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1909 it
->arr().hasCollection());
1911 auto const iter
= &it
->arr();
1912 return iterNext
<c_Map
, ArrayIter::VersionableSparse
>(iter
, valOut
, keyOut
);
1915 int64_t iterNextKImmMap(Iter
* it
,
1917 TypedValue
* keyOut
) {
1918 TRACE(2, "iterNextKImmMap: I %p\n", it
);
1919 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1920 it
->arr().hasCollection());
1922 auto const iter
= &it
->arr();
1923 return iterNext
<c_ImmMap
, ArrayIter::VersionableSparse
>(iter
, valOut
, keyOut
);
1926 int64_t iterNextSet(Iter
* it
, TypedValue
* valOut
) {
1927 TRACE(2, "iterNextSet: I %p\n", it
);
1928 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1929 it
->arr().hasCollection());
1931 auto const iter
= &it
->arr();
1932 return iterNext
<c_Set
, ArrayIter::VersionableSparse
>(iter
, valOut
, nullptr);
1935 int64_t iterNextImmSet(Iter
* it
, TypedValue
* valOut
) {
1936 TRACE(2, "iterNextImmSet: I %p\n", it
);
1937 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1938 it
->arr().hasCollection());
1940 auto const iter
= &it
->arr();
1942 iterNext
<c_ImmSet
, ArrayIter::VersionableSparse
>(iter
, valOut
, nullptr);
1945 int64_t iterNextKSet(Iter
* it
,
1947 TypedValue
* keyOut
) {
1948 TRACE(2, "iterNextKSet: I %p\n", it
);
1949 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1950 it
->arr().hasCollection());
1952 auto const iter
= &it
->arr();
1953 return iterNext
<c_Set
, ArrayIter::VersionableSparse
>(iter
, valOut
, keyOut
);
1956 int64_t iterNextKImmSet(Iter
* it
,
1958 TypedValue
* keyOut
) {
1959 TRACE(2, "iterNextKImmSet: I %p\n", it
);
1960 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1961 it
->arr().hasCollection());
1963 auto const iter
= &it
->arr();
1964 return iterNext
<c_ImmSet
, ArrayIter::VersionableSparse
>(iter
, valOut
, keyOut
);
1967 int64_t iterNextPair(Iter
* it
, TypedValue
* valOut
) {
1968 TRACE(2, "iterNextPair: I %p\n", it
);
1969 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1970 it
->arr().hasCollection());
1972 auto const iter
= &it
->arr();
1973 return iterNext
<c_Pair
, ArrayIter::Fixed
>(iter
, valOut
, nullptr);
1976 int64_t iterNextKPair(Iter
* it
,
1978 TypedValue
* keyOut
) {
1979 TRACE(2, "iterNextKPair: I %p\n", it
);
1980 assert(it
->arr().getIterType() == ArrayIter::TypeIterator
&&
1981 it
->arr().hasCollection());
1983 auto const iter
= &it
->arr();
1984 return iterNext
<c_Pair
, ArrayIter::Fixed
>(iter
, valOut
, keyOut
);
1987 int64_t iterNextObject(Iter
* it
, TypedValue
* valOut
) {
1988 TRACE(2, "iterNextObject: I %p\n", it
);
1989 // We can't just put the address of iter_next_cold in the table
1990 // below right now because we need to get a nullptr into the third
1991 // argument register for it.
1992 return iter_next_cold
<false>(it
, valOut
, nullptr);
1995 using IterNextHelper
= int64_t (*)(Iter
*, TypedValue
*);
1996 using IterNextKHelper
= int64_t (*)(Iter
*, TypedValue
*, TypedValue
*);
1998 const IterNextHelper g_iterNextHelpers
[] = {
1999 &iterNextArrayPacked
,
2000 &iterNextArrayMixed
,
2012 const IterNextKHelper g_iterNextKHelpers
[] = {
2013 &iterNextKArrayPacked
,
2014 &iterNextKArrayMixed
,
2017 &iterNextKImmVector
,
2023 &iter_next_cold
<false>, // iterNextKObject
2026 int64_t iter_next_ind(Iter
* iter
, TypedValue
* valOut
) {
2027 TRACE(2, "iter_next_ind: I %p\n", iter
);
2028 assert(iter
->arr().getIterType() == ArrayIter::TypeArray
||
2029 iter
->arr().getIterType() == ArrayIter::TypeIterator
);
2030 auto const arrIter
= &iter
->arr();
2031 valOut
= tvToCell(valOut
);
2032 IterNextHelper iterNext
=
2033 g_iterNextHelpers
[static_cast<uint32_t>(arrIter
->getHelperIndex())];
2034 return iterNext(iter
, valOut
);
2037 int64_t iter_next_key_ind(Iter
* iter
, TypedValue
* valOut
, TypedValue
* keyOut
) {
2038 TRACE(2, "iter_next_key_ind: I %p\n", iter
);
2039 assert(iter
->arr().getIterType() == ArrayIter::TypeArray
||
2040 iter
->arr().getIterType() == ArrayIter::TypeIterator
);
2041 auto const arrIter
= &iter
->arr();
2042 valOut
= tvToCell(valOut
);
2043 keyOut
= tvToCell(keyOut
);
2044 IterNextKHelper iterNextK
=
2045 g_iterNextKHelpers
[static_cast<uint32_t>(arrIter
->getHelperIndex())];
2046 return iterNextK(iter
, valOut
, keyOut
);
2049 ///////////////////////////////////////////////////////////////////////////////