2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_VM_MEMBER_OPERATIONS_H_
18 #define incl_HPHP_VM_MEMBER_OPERATIONS_H_
20 #include <type_traits>
22 #include "hphp/runtime/base/array-data-defs.h"
23 #include "hphp/runtime/base/builtin-functions.h"
24 #include "hphp/runtime/base/collections.h"
25 #include "hphp/runtime/base/req-root.h"
26 #include "hphp/runtime/base/strings.h"
27 #include "hphp/runtime/base/tv-conversions.h"
28 #include "hphp/runtime/base/tv-refcount.h"
29 #include "hphp/runtime/base/tv-type.h"
30 #include "hphp/runtime/base/type-array.h"
31 #include "hphp/runtime/base/type-string.h"
32 #include "hphp/runtime/base/mixed-array.h"
33 #include "hphp/runtime/base/packed-array.h"
34 #include "hphp/runtime/base/set-array.h"
35 #include "hphp/runtime/vm/runtime.h"
36 #include "hphp/system/systemlib.h"
40 const StaticString
s_storage("storage");
42 struct InvalidSetMException
: std::runtime_error
{
43 InvalidSetMException()
44 : std::runtime_error("Empty InvalidSetMException")
45 , m_tv(make_tv
<KindOfNull
>())
48 explicit InvalidSetMException(const TypedValue
& value
)
49 : std::runtime_error(folly::format("InvalidSetMException containing {}",
50 value
.pretty()).str())
54 ~InvalidSetMException() noexcept override
{}
56 const TypedValue
& tv() const { return m_tv
; };
59 /* m_tv will contain a TypedValue with a reference destined for the
61 req::root
<TypedValue
> m_tv
;
64 // When MoreWarnings is set to true, the VM will raise more warnings
65 // on SetOpM, IncDecM and CGetG, intended to match Zend.
66 const bool MoreWarnings
=
67 #ifdef HHVM_MORE_WARNINGS
75 * KeyType and the associated functions below are used to generate member
76 * operation functions specialized for certain key types. Many functions take a
77 * KeyType template parameter, then use key_type<keyType> as the type of their
78 * key parameter. Depending on which KeyType is used, the parameter will be a
79 * TypedValue, int64_t, or StringData*.
82 Any
, // Key is passed as a TypedValue and could be any type
83 Int
, // Key is passed as an int64_t
84 Str
, // Key is passed as a StringData*
87 /* KeyTypeTraits maps from KeyType to the C++ type holding they key. */
88 template<KeyType
> struct KeyTypeTraits
;
89 template<> struct KeyTypeTraits
<KeyType::Any
> { typedef TypedValue type
; };
90 template<> struct KeyTypeTraits
<KeyType::Int
> { typedef int64_t type
; };
91 template<> struct KeyTypeTraits
<KeyType::Str
> { typedef StringData
* type
; };
93 /* key_type is the type used in the signatures of functions taking a member
95 template<KeyType kt
> using key_type
= typename KeyTypeTraits
<kt
>::type
;
97 /* initScratchKey is used in scenarios where we want a TypedValue key
98 * regardless of what the current function was given. */
99 inline TypedValue
initScratchKey(TypedValue tv
) {
100 assertx(tv
.m_type
!= KindOfRef
);
104 inline TypedValue
initScratchKey(int64_t key
) {
105 return make_tv
<KindOfInt64
>(key
);
108 inline TypedValue
initScratchKey(StringData
* key
) {
109 return make_tv
<KindOfString
>(key
);
112 /* keyAsValue transforms a key into a value suitable for indexing into an
114 inline const Variant
& keyAsValue(TypedValue
& key
) {
115 return tvAsCVarRef(&key
);
117 inline int64_t keyAsValue(int64_t key
) { return key
; }
118 inline StrNR
keyAsValue(StringData
* key
) { return StrNR(key
); }
120 /* prepareKey is used by operations that need to cast their key to a
121 * string. For generic keys, the returned value must be decreffed after use. */
122 StringData
* prepareAnyKey(TypedValue
* tv
);
123 inline StringData
* prepareKey(TypedValue tv
) { return prepareAnyKey(&tv
); }
124 inline StringData
* prepareKey(StringData
* sd
) { return sd
; }
126 /* releaseKey helps with decreffing a StringData* returned from
127 * prepareKey. When used with KeyType::Any, corresponding to
128 * prepareKey(TypedValue), it will consume the reference produced by
129 * prepareKey. When used with KeyType::Str, corresponding to
130 * prepareKey(StringData*), it is a nop. */
131 template<KeyType keyType
>
132 inline void releaseKey(StringData
* sd
) {
133 static_assert(keyType
== KeyType::Any
, "bad KeyType");
138 inline void releaseKey
<KeyType::Str
>(StringData
*) {
139 // do nothing. we don't own a reference to this string.
142 void objArrayAccess(ObjectData
* base
);
144 TypedValue
objOffsetGet(
150 bool objOffsetIsset(ObjectData
* base
, TypedValue offset
, bool validate
= true);
151 bool objOffsetEmpty(ObjectData
* base
, TypedValue offset
, bool validate
= true);
160 void objOffsetAppend(ObjectData
* base
, TypedValue
* val
, bool validate
= true);
161 void objOffsetUnset(ObjectData
* base
, TypedValue offset
);
163 [[noreturn
]] void throw_cannot_use_newelem_for_lval_read_col();
164 [[noreturn
]] void throw_cannot_use_newelem_for_lval_read_vec();
165 [[noreturn
]] void throw_cannot_use_newelem_for_lval_read_dict();
166 [[noreturn
]] void throw_cannot_use_newelem_for_lval_read_keyset();
168 [[noreturn
]] void unknownBaseType(const TypedValue
*);
172 ALWAYS_INLINE
void checkPromotion(const TypedValue
* base
) {
173 if (LIKELY(!RuntimeOption::EvalHackArrCompatNotices
)) return;
175 if (base
->m_type
== KindOfNull
) {
176 raise_hackarr_compat_notice("Promoting null to array");
177 } else if (base
->m_type
== KindOfBoolean
) {
178 raise_hackarr_compat_notice("Promoting false to array");
185 * Elem when base is Null
187 inline member_rval
ElemEmptyish() {
188 return member_rval
{ nullptr, &immutable_null_base
};
191 template<MOpMode mode
, bool intishWarn
>
192 inline member_rval
ElemArrayPre(ArrayData
* base
, int64_t key
) {
193 return mode
== MOpMode::Warn
? base
->rvalStrict(key
) : base
->rval(key
);
196 template<MOpMode mode
, bool intishWarn
>
197 inline member_rval
ElemArrayPre(ArrayData
* base
, StringData
* key
) {
198 auto constexpr warn
= mode
== MOpMode::Warn
;
200 assert(base
->isPHPArray());
201 if (key
->isStrictlyInteger(n
)) {
202 if (intishWarn
) raise_intish_index_cast();
203 return warn
? base
->rvalStrict(n
) : base
->rval(n
);
205 return warn
? base
->rvalStrict(key
) : base
->rval(key
);
209 template<MOpMode mode
, bool intishWarn
>
210 inline member_rval
ElemArrayPre(ArrayData
* base
, TypedValue key
) {
211 auto const dt
= key
.m_type
;
213 return ElemArrayPre
<mode
, false>(base
, key
.m_data
.num
);
215 if (isStringType(dt
)) {
216 return ElemArrayPre
<mode
, intishWarn
>(base
, key
.m_data
.pstr
);
219 // TODO(#3888164): Array elements can never be KindOfUninit. This API should
221 auto const rval
= ArrNR(base
).asArray().rvalAt(cellAsCVarRef(key
));
222 return rval
.type() != KindOfUninit
? rval
: member_rval
{ base
, nullptr };
226 * Elem when base is an Array
228 template<MOpMode mode
, KeyType keyType
, bool intishWarn
>
229 inline member_rval
ElemArray(ArrayData
* base
, key_type
<keyType
> key
) {
230 assert(base
->isPHPArray());
232 auto result
= ElemArrayPre
<mode
, intishWarn
>(base
, key
);
234 if (UNLIKELY(!result
)) {
235 if (mode
== MOpMode::Warn
) {
236 auto const scratch
= initScratchKey(key
);
237 raise_notice(Strings::UNDEFINED_INDEX
,
238 tvAsCVarRef(&scratch
).toString().data());
240 return ElemEmptyish();
243 assertx(result
.type() != KindOfUninit
);
248 * Elem when base is a Vec
250 template<MOpMode mode
>
251 inline member_rval
ElemVecPre(ArrayData
* base
, int64_t key
) {
252 return mode
== MOpMode::Warn
253 ? PackedArray::RvalIntStrictVec(base
, key
)
254 : PackedArray::RvalIntVec(base
, key
);
257 template<MOpMode mode
>
258 inline member_rval
ElemVecPre(ArrayData
* base
, StringData
* key
) {
259 if (mode
== MOpMode::Warn
) throwInvalidArrayKeyException(key
, base
);
260 return member_rval
{};
263 template<MOpMode mode
>
264 inline member_rval
ElemVecPre(ArrayData
* base
, TypedValue key
) {
265 auto const dt
= key
.m_type
;
266 if (LIKELY(isIntType(dt
))) return ElemVecPre
<mode
>(base
, key
.m_data
.num
);
267 if (isStringType(dt
)) return ElemVecPre
<mode
>(base
, key
.m_data
.pstr
);
268 throwInvalidArrayKeyException(&key
, base
);
271 template<MOpMode mode
, KeyType keyType
>
272 inline member_rval
ElemVec(ArrayData
* base
, key_type
<keyType
> key
) {
273 assertx(base
->isVecArray());
274 auto result
= ElemVecPre
<mode
>(base
, key
);
275 if (mode
!= MOpMode::Warn
) {
276 if (UNLIKELY(!result
)) return ElemEmptyish();
278 assertx(result
.type() != KindOfUninit
);
283 * Elem when base is a Dict
285 template<MOpMode mode
>
286 inline member_rval
ElemDictPre(ArrayData
* base
, int64_t key
) {
287 return mode
== MOpMode::Warn
288 ? MixedArray::RvalIntStrictDict(base
, key
)
289 : MixedArray::RvalIntDict(base
, key
);
292 template<MOpMode mode
>
293 inline member_rval
ElemDictPre(ArrayData
* base
, StringData
* key
) {
294 return mode
== MOpMode::Warn
295 ? MixedArray::RvalStrStrictDict(base
, key
)
296 : MixedArray::RvalStrDict(base
, key
);
299 template<MOpMode mode
>
300 inline member_rval
ElemDictPre(ArrayData
* base
, TypedValue key
) {
301 auto const dt
= key
.m_type
;
302 if (isIntType(dt
)) return ElemDictPre
<mode
>(base
, key
.m_data
.num
);
303 if (isStringType(dt
)) return ElemDictPre
<mode
>(base
, key
.m_data
.pstr
);
304 throwInvalidArrayKeyException(&key
, base
);
307 template<MOpMode mode
, KeyType keyType
>
308 inline member_rval
ElemDict(ArrayData
* base
, key_type
<keyType
> key
) {
309 assertx(base
->isDict());
310 auto result
= ElemDictPre
<mode
>(base
, key
);
311 if (mode
!= MOpMode::Warn
) {
312 if (UNLIKELY(!result
)) return ElemEmptyish();
314 assertx(result
.type() != KindOfUninit
);
319 * Elem when base is a Keyset
321 template<MOpMode mode
>
322 inline member_rval
ElemKeysetPre(ArrayData
* base
, int64_t key
) {
323 return mode
== MOpMode::Warn
324 ? SetArray::RvalIntStrict(base
, key
)
325 : SetArray::RvalInt(base
, key
);
328 template<MOpMode mode
>
329 inline member_rval
ElemKeysetPre(ArrayData
* base
, StringData
* key
) {
330 return mode
== MOpMode::Warn
331 ? SetArray::RvalStrStrict(base
, key
)
332 : SetArray::RvalStr(base
, key
);
335 template<MOpMode mode
>
336 inline member_rval
ElemKeysetPre(ArrayData
* base
, TypedValue key
) {
337 auto const dt
= key
.m_type
;
338 if (isIntType(dt
)) return ElemKeysetPre
<mode
>(base
, key
.m_data
.num
);
339 if (isStringType(dt
)) return ElemKeysetPre
<mode
>(base
, key
.m_data
.pstr
);
340 throwInvalidArrayKeyException(&key
, base
);
343 template<MOpMode mode
, KeyType keyType
>
344 inline member_rval
ElemKeyset(ArrayData
* base
, key_type
<keyType
> key
) {
345 assertx(base
->isKeyset());
346 auto result
= ElemKeysetPre
<mode
>(base
, key
);
347 if (mode
!= MOpMode::Warn
) {
348 if (UNLIKELY(!result
)) return ElemEmptyish();
350 assertx(isIntType(result
.type()) || isStringType(result
.type()));
355 * Elem when base is an Int64, Double, or Resource.
357 inline member_rval
ElemScalar() {
358 if (RuntimeOption::EnableHipHopSyntax
) {
359 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
361 return ElemEmptyish();
365 * Elem when base is a Boolean
367 inline member_rval
ElemBoolean(TypedValue
* base
) {
368 if (base
->m_data
.num
) {
371 return ElemEmptyish();
374 inline int64_t ElemStringPre(int64_t key
) {
378 inline int64_t ElemStringPre(StringData
* key
) {
379 return key
->toInt64(10);
382 inline int64_t ElemStringPre(TypedValue key
) {
383 if (LIKELY(isIntType(key
.m_type
))) {
384 return key
.m_data
.num
;
385 } else if (LIKELY(isStringType(key
.m_type
))) {
386 return key
.m_data
.pstr
->toInt64(10);
388 raise_notice("String offset cast occurred");
389 return cellAsCVarRef(key
).toInt64();
394 * Elem when base is a String
396 template<MOpMode mode
, KeyType keyType
>
397 inline member_rval
ElemString(TypedValue
& tvRef
,
399 key_type
<keyType
> key
) {
400 auto offset
= ElemStringPre(key
);
402 if (offset
< 0 || offset
>= base
->m_data
.pstr
->size()) {
403 if (mode
== MOpMode::Warn
) {
404 raise_notice("Uninitialized string offset: %" PRId64
, offset
);
406 tvRef
= make_tv
<KindOfPersistentString
>(staticEmptyString());
408 tvRef
= make_tv
<KindOfPersistentString
>(base
->m_data
.pstr
->getChar(offset
));
409 assert(tvRef
.m_data
.pstr
->isStatic());
411 return member_rval
{ base
->m_data
.pstr
, &tvRef
};
415 * Elem when base is an Object
417 template<MOpMode mode
, KeyType keyType
>
418 inline member_rval
ElemObject(TypedValue
& tvRef
,
420 key_type
<keyType
> key
) {
421 auto scratch
= initScratchKey(key
);
423 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
424 if (mode
== MOpMode::Warn
) {
427 collections::at(base
->m_data
.pobj
, &scratch
)
430 auto res
= collections::get(base
->m_data
.pobj
, &scratch
);
435 return member_rval
{ base
->m_data
.pobj
, res
};
438 tvRef
= objOffsetGet(instanceFromTv(base
), scratch
);
439 return member_rval
{ base
->m_data
.pobj
, &tvRef
};
443 * $result = $base[$key];
445 template<MOpMode mode
, KeyType keyType
, bool intishWarn
>
446 NEVER_INLINE member_rval
ElemSlow(TypedValue
& tvRef
,
448 key_type
<keyType
> key
) {
449 base
= tvToCell(base
);
450 assertx(cellIsPlausible(*base
));
452 switch (base
->m_type
) {
455 if (mode
!= MOpMode::None
&& RuntimeOption::EvalHackArrCompatNotices
) {
456 raise_hackarr_compat_notice("Cannot index into null");
458 return ElemEmptyish();
460 if (mode
!= MOpMode::None
&& RuntimeOption::EvalHackArrCompatNotices
) {
461 raise_hackarr_compat_notice("Cannot index into a boolean");
463 return ElemBoolean(base
);
468 case KindOfPersistentString
:
470 return ElemString
<mode
, keyType
>(tvRef
, base
, key
);
471 case KindOfPersistentVec
:
473 return ElemVec
<mode
, keyType
>(base
->m_data
.parr
, key
);
474 case KindOfPersistentDict
:
476 return ElemDict
<mode
, keyType
>(base
->m_data
.parr
, key
);
477 case KindOfPersistentKeyset
:
479 return ElemKeyset
<mode
, keyType
>(base
->m_data
.parr
, key
);
480 case KindOfPersistentArray
:
482 return ElemArray
<mode
, keyType
, intishWarn
>(base
->m_data
.parr
, key
);
484 return ElemObject
<mode
, keyType
>(tvRef
, base
, key
);
488 unknownBaseType(base
);
492 * Fast path for Elem assuming base is an Array. Does not unbox the returned
495 template<MOpMode mode
, bool intishWarn
, KeyType keyType
= KeyType::Any
>
496 inline const TypedValue
* Elem(TypedValue
& tvRef
,
498 key_type
<keyType
> key
) {
499 assertx(mode
!= MOpMode::Define
&& mode
!= MOpMode::Unset
);
500 assertx(tvIsPlausible(*base
));
502 if (LIKELY(tvIsArray(base
))) {
503 return ElemArray
<mode
, keyType
, intishWarn
>(
508 if (LIKELY(tvIsVecArray(base
))) {
509 return ElemVec
<mode
, keyType
>(base
->m_data
.parr
, key
).tv_ptr();
511 if (LIKELY(tvIsDict(base
))) {
512 return ElemDict
<mode
, keyType
>(base
->m_data
.parr
, key
).tv_ptr();
514 if (LIKELY(tvIsKeyset(base
))) {
515 return ElemKeyset
<mode
, keyType
>(base
->m_data
.parr
, key
).tv_ptr();
518 if (mode
== MOpMode::InOut
) throw_invalid_inout_base();
520 return ElemSlow
<mode
, keyType
, intishWarn
>(tvRef
, base
, key
).tv_ptr();
523 template<MOpMode mode
, bool reffy
, bool intishWarn
>
524 inline member_lval
ElemDArrayPre(TypedValue
* base
, int64_t key
, bool& defined
) {
525 auto oldArr
= base
->m_data
.parr
;
527 defined
= (mode
!= MOpMode::Warn
) || oldArr
->exists(key
);
528 auto const lval
= reffy
529 ? oldArr
->lvalRef(key
, oldArr
->cowCheck())
530 : oldArr
->lval(key
, oldArr
->cowCheck());
532 if (lval
.arr_base() != oldArr
) {
533 base
->m_type
= KindOfArray
;
534 base
->m_data
.parr
= lval
.arr_base();
535 assertx(cellIsPlausible(*base
));
542 template<MOpMode mode
, bool reffy
, bool intishWarn
>
543 inline member_lval
ElemDArrayPre(TypedValue
* base
, StringData
* key
,
545 auto oldArr
= base
->m_data
.parr
;
547 auto const lval
= [&]{
548 auto const cow
= oldArr
->cowCheck();
550 if (oldArr
->convertKey(key
, n
, intishWarn
)) {
551 defined
= (mode
!= MOpMode::Warn
) || oldArr
->exists(n
);
552 return reffy
? oldArr
->lvalRef(n
, cow
) : oldArr
->lval(n
, cow
);
554 defined
= (mode
!= MOpMode::Warn
) || oldArr
->exists(key
);
555 return reffy
? oldArr
->lvalRef(key
, cow
) : oldArr
->lval(key
, cow
);
559 if (lval
.arr_base() != oldArr
) {
560 base
->m_type
= KindOfArray
;
561 base
->m_data
.parr
= lval
.arr_base();
562 assertx(cellIsPlausible(*base
));
568 template<MOpMode mode
, bool reffy
, bool intishWarn
>
569 inline member_lval
ElemDArrayPre(TypedValue
* base
, TypedValue key
,
571 auto const dt
= key
.m_type
;
573 return ElemDArrayPre
<mode
, reffy
, false>(base
, key
.m_data
.num
, defined
);
575 if (isStringType(dt
)) {
576 return ElemDArrayPre
<mode
, reffy
, intishWarn
>(
577 base
, key
.m_data
.pstr
, defined
580 auto& arr
= tvAsVariant(base
).asArrRef();
581 defined
= (mode
!= MOpMode::Warn
) || arr
.exists(tvAsCVarRef(&key
));
582 return reffy
? arr
.lvalAtRef(key
) : arr
.lvalAt(key
);
586 * ElemD when base is an Array
588 template<MOpMode mode
, bool reffy
, bool intishWarn
, KeyType keyType
>
589 inline TypedValue
* ElemDArray(TypedValue
* base
, key_type
<keyType
> key
) {
590 assertx(tvIsArray(base
));
591 assertx(tvIsPlausible(*base
));
594 auto lval
= ElemDArrayPre
<mode
, reffy
, intishWarn
>(base
, key
, defined
);
596 assertx(tvIsArray(base
));
597 assertx(tvIsPlausible(*base
));
598 assertx(lval
.type() != KindOfUninit
);
601 auto scratchKey
= initScratchKey(key
);
602 raise_notice(Strings::UNDEFINED_INDEX
,
603 tvAsCVarRef(&scratchKey
).toString().data());
606 return lval
.tv_ptr();
610 * ElemD when base is a Vec
612 template <bool reffy
>
613 inline TypedValue
* ElemDVecPre(TypedValue
* base
, int64_t key
) {
614 ArrayData
* oldArr
= base
->m_data
.parr
;
616 if (reffy
) throwRefInvalidArrayValueException(oldArr
);
618 auto const lval
= PackedArray::LvalIntVec(oldArr
, key
, oldArr
->cowCheck());
619 if (lval
.arr_base() != oldArr
) {
620 base
->m_type
= KindOfVec
;
621 base
->m_data
.parr
= lval
.arr_base();
622 assertx(cellIsPlausible(*base
));
625 return lval
.tv_ptr();
628 template <bool reffy
>
629 inline TypedValue
* ElemDVecPre(TypedValue
* base
, StringData
* key
) {
630 throwInvalidArrayKeyException(key
, base
->m_data
.parr
);
633 template <bool reffy
>
634 inline TypedValue
* ElemDVecPre(TypedValue
* base
, TypedValue key
) {
635 auto const dt
= key
.m_type
;
636 if (LIKELY(isIntType(dt
))) return ElemDVecPre
<reffy
>(base
, key
.m_data
.num
);
637 if (isStringType(dt
)) return ElemDVecPre
<reffy
>(base
, key
.m_data
.pstr
);
638 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
641 template <bool reffy
, KeyType keyType
>
642 inline TypedValue
* ElemDVec(TypedValue
* base
, key_type
<keyType
> key
) {
643 assertx(tvIsVecArray(base
));
644 assertx(tvIsPlausible(*base
));
645 auto* result
= ElemDVecPre
<reffy
>(base
, key
);
646 assertx(tvIsVecArray(base
));
647 assertx(tvIsPlausible(*base
));
648 assertx(result
->m_type
!= KindOfUninit
);
653 * ElemD when base is a Dict
655 template <bool reffy
>
656 inline TypedValue
* ElemDDictPre(TypedValue
* base
, int64_t key
) {
657 ArrayData
* oldArr
= base
->m_data
.parr
;
659 if (reffy
) throwRefInvalidArrayValueException(oldArr
);
662 MixedArray::LvalSilentIntDict(oldArr
, key
, oldArr
->cowCheck());
664 if (UNLIKELY(!lval
)) {
665 assertx(oldArr
== lval
.arr_base());
666 throwOOBArrayKeyException(key
, oldArr
);
669 if (lval
.arr_base() != oldArr
) {
670 base
->m_type
= KindOfDict
;
671 base
->m_data
.parr
= lval
.arr_base();
672 assertx(cellIsPlausible(*base
));
676 return lval
.tv_ptr();
679 template <bool reffy
>
680 inline TypedValue
* ElemDDictPre(TypedValue
* base
, StringData
* key
) {
681 ArrayData
* oldArr
= base
->m_data
.parr
;
683 if (reffy
) throwRefInvalidArrayValueException(oldArr
);
686 MixedArray::LvalSilentStrDict(oldArr
, key
, oldArr
->cowCheck());
688 if (UNLIKELY(!lval
)) {
689 assertx(oldArr
== lval
.arr_base());
690 throwOOBArrayKeyException(key
, oldArr
);
693 if (lval
.arr_base() != oldArr
) {
694 base
->m_type
= KindOfDict
;
695 base
->m_data
.parr
= lval
.arr_base();
696 assertx(cellIsPlausible(*base
));
700 return lval
.tv_ptr();
703 template <bool reffy
>
704 inline TypedValue
* ElemDDictPre(TypedValue
* base
, TypedValue key
) {
705 auto const dt
= key
.m_type
;
706 if (isIntType(dt
)) return ElemDDictPre
<reffy
>(base
, key
.m_data
.num
);
707 if (isStringType(dt
)) return ElemDDictPre
<reffy
>(base
, key
.m_data
.pstr
);
708 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
711 template <bool reffy
, KeyType keyType
>
712 inline TypedValue
* ElemDDict(TypedValue
* base
, key_type
<keyType
> key
) {
713 assertx(tvIsDict(base
));
714 assertx(tvIsPlausible(*base
));
715 auto* result
= ElemDDictPre
<reffy
>(base
, key
);
716 assertx(tvIsDict(base
));
717 assertx(tvIsPlausible(*base
));
718 assertx(result
->m_type
!= KindOfUninit
);
723 * ElemD when base is a Keyset
725 template <bool reffy
>
726 [[noreturn
]] inline TypedValue
*
727 ElemDKeysetPre(TypedValue
* base
, int64_t /*key*/) {
728 if (reffy
) throwRefInvalidArrayValueException(base
->m_data
.parr
);
729 throwInvalidKeysetOperation();
732 template <bool reffy
>
733 [[noreturn
]] inline TypedValue
*
734 ElemDKeysetPre(TypedValue
* base
, StringData
* /*key*/) {
735 if (reffy
) throwRefInvalidArrayValueException(base
->m_data
.parr
);
736 throwInvalidKeysetOperation();
739 template <bool reffy
>
741 inline TypedValue
* ElemDKeysetPre(TypedValue
* base
, TypedValue key
) {
742 auto const dt
= key
.m_type
;
743 if (isIntType(dt
)) ElemDKeysetPre
<reffy
>(base
, key
.m_data
.num
);
744 if (isStringType(dt
)) ElemDKeysetPre
<reffy
>(base
, key
.m_data
.pstr
);
745 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
748 template <bool reffy
, KeyType keyType
>
750 inline TypedValue
* ElemDKeyset(TypedValue
* base
, key_type
<keyType
> key
) {
751 assertx(tvIsKeyset(base
));
752 assertx(tvIsPlausible(*base
));
753 ElemDKeysetPre
<reffy
>(base
, key
);
757 * ElemD when base is Null
759 template<MOpMode mode
, KeyType keyType
>
760 inline TypedValue
* ElemDEmptyish(TypedValue
* base
, key_type
<keyType
> key
) {
761 detail::checkPromotion(base
);
762 auto scratchKey
= initScratchKey(key
);
763 tvAsVariant(base
) = Array::Create();
764 auto const result
= tvAsVariant(base
).asArrRef().lvalAt(
765 cellAsCVarRef(scratchKey
)
767 if (mode
== MOpMode::Warn
) {
768 raise_notice(Strings::UNDEFINED_INDEX
,
769 tvAsCVarRef(&scratchKey
).toString().data());
775 * ElemD when base is an Int64, Double, or Resource.
777 inline TypedValue
* ElemDScalar(TypedValue
& tvRef
) {
778 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
784 * ElemD when base is a Boolean
786 template<MOpMode mode
, KeyType keyType
>
787 inline TypedValue
* ElemDBoolean(TypedValue
& tvRef
,
789 key_type
<keyType
> key
) {
790 if (base
->m_data
.num
) {
791 return ElemDScalar(tvRef
);
793 return ElemDEmptyish
<mode
, keyType
>(base
, key
);
797 * ElemD when base is a String
799 template<MOpMode mode
, KeyType keyType
>
800 inline TypedValue
* ElemDString(TypedValue
* base
, key_type
<keyType
> key
) {
801 if (base
->m_data
.pstr
->size() == 0) {
802 return ElemDEmptyish
<mode
, keyType
>(base
, key
);
804 raise_error("Operator not supported for strings");
809 * ElemD when base is an Object
811 template<MOpMode mode
, bool reffy
, KeyType keyType
>
812 inline TypedValue
* ElemDObject(TypedValue
& tvRef
, TypedValue
* base
,
813 key_type
<keyType
> key
) {
814 auto scratchKey
= initScratchKey(key
);
815 auto obj
= base
->m_data
.pobj
;
817 if (LIKELY(obj
->isCollection())) {
819 raise_error("Collection elements cannot be taken by reference");
822 return collections::atLval(obj
, &scratchKey
);
823 } else if (obj
->getVMClass()->classof(SystemLib::s_ArrayObjectClass
)) {
824 auto storage
= obj
->getPropLval(SystemLib::s_ArrayObjectClass
,
826 // ArrayObject should always have the 'storage' property...
827 assert(storage
.has_ref());
828 return UNLIKELY(RuntimeOption::EvalHackArrCompatNotices
)
829 ? ElemDArray
<mode
, reffy
, true, keyType
>(storage
.tv_ptr(), key
)
830 : ElemDArray
<mode
, reffy
, false, keyType
>(storage
.tv_ptr(), key
);
834 tvRef
= objOffsetGet(instanceFromTv(base
), scratchKey
);
839 * Intermediate elem operation for defining member instructions.
841 * Returned pointer is not yet unboxed. (I.e. it cannot point into a RefData.)
843 template<MOpMode mode
, bool reffy
, bool intishWarn
,
844 KeyType keyType
= KeyType::Any
>
845 TypedValue
* ElemD(TypedValue
& tvRef
, TypedValue
* base
, key_type
<keyType
> key
) {
846 assertx(mode
== MOpMode::Define
);
848 base
= tvToCell(base
);
849 assertx(cellIsPlausible(*base
));
851 switch (base
->m_type
) {
854 return ElemDEmptyish
<mode
, keyType
>(base
, key
);
856 return ElemDBoolean
<mode
, keyType
>(tvRef
, base
, key
);
860 return ElemDScalar(tvRef
);
861 case KindOfPersistentString
:
863 return ElemDString
<mode
, keyType
>(base
, key
);
864 case KindOfPersistentVec
:
866 return ElemDVec
<reffy
, keyType
>(base
, key
);
867 case KindOfPersistentDict
:
869 return ElemDDict
<reffy
, keyType
>(base
, key
);
870 case KindOfPersistentKeyset
:
872 return ElemDKeyset
<reffy
, keyType
>(base
, key
);
873 case KindOfPersistentArray
:
875 return ElemDArray
<mode
, reffy
, intishWarn
, keyType
>(base
, key
);
877 return ElemDObject
<mode
, reffy
, keyType
>(tvRef
, base
, key
);
881 unknownBaseType(base
);
885 * Implementation for SetWithRef*ML bytecodes.
887 template<MOpMode mode
, bool reffy
, bool intishWarn
,
888 KeyType keyType
= KeyType::Any
>
889 void SetWithRefMLElem(TypedValue
& tvRef
, TypedValue
* base
,
890 key_type
<keyType
> key
, TypedValue val
) {
891 assertx(mode
== MOpMode::Define
);
893 base
= tvToCell(base
);
894 assertx(cellIsPlausible(*base
));
896 auto const elem
= [&] {
897 switch (base
->m_type
) {
900 return ElemDEmptyish
<mode
, keyType
>(base
, key
);
902 return ElemDBoolean
<mode
, keyType
>(tvRef
, base
, key
);
906 return ElemDScalar(tvRef
);
907 case KindOfPersistentString
:
909 return ElemDString
<mode
, keyType
>(base
, key
);
910 case KindOfPersistentVec
:
912 return ElemDVec
<reffy
, keyType
>(base
, key
);
913 case KindOfPersistentDict
:
915 return ElemDDict
<reffy
, keyType
>(base
, key
);
916 case KindOfPersistentKeyset
:
918 return ElemDKeyset
<reffy
, keyType
>(base
, key
);
919 case KindOfPersistentArray
:
921 // We want to notice for binding assignments here, but not for missing
922 // index, since we're about to initialize the value in that case.
923 // Rather than fork the Lval API to have warn and no-warn flavors, we
924 // instead lift the binding assignment warning here, and then disable
925 // Hack array notices.
926 if (reffy
&& RuntimeOption::EvalHackArrCompatNotices
&&
927 !base
->m_data
.parr
->isGlobalsArray()) {
928 raiseHackArrCompatRefBind(key
);
930 SuppressHackArrCompatNotices shacn
;
931 return ElemDArray
<mode
, reffy
, intishWarn
, keyType
>(base
, key
);
934 return ElemDObject
<mode
, reffy
, keyType
>(tvRef
, base
, key
);
938 unknownBaseType(base
);
941 // Intentionally leak the old value pointed to by elem, including from magic
947 * ElemU when base is Null
949 inline member_lval
ElemUEmptyish() {
950 return member_lval
{ nullptr, const_cast<TypedValue
*>(&immutable_null_base
) };
953 template <bool intishWarn
>
954 inline member_lval
ElemUArrayImpl(TypedValue
* base
, int64_t key
) {
955 auto oldArr
= base
->m_data
.parr
;
956 if (!oldArr
->exists(key
)) return ElemUEmptyish();
957 auto const lval
= oldArr
->lval(key
, oldArr
->cowCheck());
958 if (lval
.arr_base() != oldArr
) {
959 base
->m_type
= KindOfArray
;
960 base
->m_data
.parr
= lval
.arr_base();
961 assertx(cellIsPlausible(*base
));
967 template <bool intishWarn
>
968 inline member_lval
ElemUArrayImpl(TypedValue
* base
, StringData
* key
) {
969 auto oldArr
= base
->m_data
.parr
;
971 if (oldArr
->convertKey(key
, n
, intishWarn
)) {
972 if (!oldArr
->exists(n
)) return ElemUEmptyish();
973 auto const lval
= oldArr
->lval(n
, oldArr
->cowCheck());
974 if (lval
.arr_base() != oldArr
) {
975 base
->m_type
= KindOfArray
;
976 base
->m_data
.parr
= lval
.arr_base();
977 assertx(cellIsPlausible(*base
));
982 if (!oldArr
->exists(key
)) return ElemUEmptyish();
983 auto const lval
= oldArr
->lval(key
, oldArr
->cowCheck());
984 if (lval
.arr_base() != oldArr
) {
985 base
->m_type
= KindOfArray
;
986 base
->m_data
.parr
= lval
.arr_base();
987 assertx(cellIsPlausible(*base
));
994 template <bool intishWarn
>
995 inline member_lval
ElemUArrayImpl(TypedValue
* base
, TypedValue key
) {
996 auto const dt
= key
.m_type
;
998 return ElemUArrayImpl
<false>(base
, key
.m_data
.num
);
1000 if (isStringType(dt
)) {
1001 return ElemUArrayImpl
<intishWarn
>(base
, key
.m_data
.pstr
);
1003 auto& arr
= tvAsVariant(base
).asArrRef();
1004 if (!arr
.exists(keyAsValue(key
))) {
1005 return ElemUEmptyish();
1007 return arr
.lvalAt(tvAsCVarRef(&key
));
1011 * ElemU when base is an Array
1013 template <bool intishWarn
, KeyType keyType
>
1014 inline TypedValue
* ElemUArray(TypedValue
* base
, key_type
<keyType
> key
) {
1015 assertx(tvIsArray(base
));
1016 assertx(tvIsPlausible(*base
));
1017 auto lval
= ElemUArrayImpl
<intishWarn
>(base
, key
);
1018 assertx(tvIsArray(base
));
1019 assertx(tvIsPlausible(*base
));
1020 assertx(lval
.type() != KindOfUninit
);
1021 return lval
.tv_ptr();
1025 * ElemU when base is a Vec
1027 inline TypedValue
* ElemUVecPre(TypedValue
* base
, int64_t key
) {
1028 ArrayData
* oldArr
= base
->m_data
.parr
;
1030 PackedArray::LvalSilentIntVec(oldArr
, key
, oldArr
->cowCheck());
1032 if (UNLIKELY(!lval
)) {
1033 return ElemUEmptyish().tv_ptr();
1035 if (lval
.arr_base() != oldArr
) {
1036 base
->m_type
= KindOfVec
;
1037 base
->m_data
.parr
= lval
.arr_base();
1038 assertx(cellIsPlausible(*base
));
1041 return lval
.tv_ptr();
1044 inline TypedValue
* ElemUVecPre(TypedValue
* /*base*/, StringData
* /*key*/) {
1045 return ElemUEmptyish().tv_ptr();
1048 inline TypedValue
* ElemUVecPre(TypedValue
* base
, TypedValue key
) {
1049 auto const dt
= key
.m_type
;
1050 if (LIKELY(isIntType(dt
))) return ElemUVecPre(base
, key
.m_data
.num
);
1051 if (isStringType(dt
)) return ElemUVecPre(base
, key
.m_data
.pstr
);
1052 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
1055 template <KeyType keyType
>
1056 inline TypedValue
* ElemUVec(TypedValue
* base
, key_type
<keyType
> key
) {
1057 assertx(tvIsVecArray(base
));
1058 assertx(tvIsPlausible(*base
));
1059 auto* result
= ElemUVecPre(base
, key
);
1060 assertx(tvIsVecArray(base
));
1061 assertx(tvIsPlausible(*base
));
1062 assertx(result
->m_type
!= KindOfUninit
);
1067 * ElemU when base is a Dict
1069 inline TypedValue
* ElemUDictPre(TypedValue
* base
, int64_t key
) {
1070 ArrayData
* oldArr
= base
->m_data
.parr
;
1072 MixedArray::LvalSilentIntDict(oldArr
, key
, oldArr
->cowCheck());
1074 if (UNLIKELY(!lval
)) {
1075 return ElemUEmptyish().tv_ptr();
1077 if (lval
.arr_base() != oldArr
) {
1078 base
->m_type
= KindOfDict
;
1079 base
->m_data
.parr
= lval
.arr_base();
1080 assertx(cellIsPlausible(*base
));
1083 return lval
.tv_ptr();
1086 inline TypedValue
* ElemUDictPre(TypedValue
* base
, StringData
* key
) {
1087 ArrayData
* oldArr
= base
->m_data
.parr
;
1089 MixedArray::LvalSilentStrDict(oldArr
, key
, oldArr
->cowCheck());
1091 if (UNLIKELY(!lval
)) {
1092 return ElemUEmptyish().tv_ptr();
1094 if (lval
.arr_base() != oldArr
) {
1095 base
->m_type
= KindOfDict
;
1096 base
->m_data
.parr
= lval
.arr_base();
1097 assertx(cellIsPlausible(*base
));
1100 return lval
.tv_ptr();
1103 inline TypedValue
* ElemUDictPre(TypedValue
* base
, TypedValue key
) {
1104 auto const dt
= key
.m_type
;
1105 if (isIntType(dt
)) return ElemUDictPre(base
, key
.m_data
.num
);
1106 if (isStringType(dt
)) return ElemUDictPre(base
, key
.m_data
.pstr
);
1107 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
1110 template <KeyType keyType
>
1111 inline TypedValue
* ElemUDict(TypedValue
* base
, key_type
<keyType
> key
) {
1112 assertx(tvIsDict(base
));
1113 assertx(tvIsPlausible(*base
));
1114 auto* result
= ElemUDictPre(base
, key
);
1115 assertx(tvIsDict(base
));
1116 assertx(tvIsPlausible(*base
));
1117 assertx(result
->m_type
!= KindOfUninit
);
1122 * ElemU when base is a Keyset
1124 [[noreturn
]] inline TypedValue
*
1125 ElemUKeysetPre(TypedValue
* /*base*/, int64_t /*key*/) {
1126 throwInvalidKeysetOperation();
1129 [[noreturn
]] inline TypedValue
*
1130 ElemUKeysetPre(TypedValue
* /*base*/, StringData
* /*key*/) {
1131 throwInvalidKeysetOperation();
1135 inline TypedValue
* ElemUKeysetPre(TypedValue
* base
, TypedValue key
) {
1136 auto const dt
= key
.m_type
;
1137 if (isIntType(dt
)) ElemUKeysetPre(base
, key
.m_data
.num
);
1138 if (isStringType(dt
)) ElemUKeysetPre(base
, key
.m_data
.pstr
);
1139 throwInvalidArrayKeyException(&key
, base
->m_data
.parr
);
1142 template <KeyType keyType
>
1144 inline TypedValue
* ElemUKeyset(TypedValue
* base
, key_type
<keyType
> key
) {
1145 assertx(tvIsKeyset(base
));
1146 assertx(tvIsPlausible(*base
));
1147 ElemUKeysetPre(base
, key
);
1151 * ElemU when base is an Object
1153 template <KeyType keyType
>
1154 inline TypedValue
* ElemUObject(TypedValue
& tvRef
, TypedValue
* base
,
1155 key_type
<keyType
> key
) {
1156 auto const scratchKey
= initScratchKey(key
);
1157 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1158 return collections::atLval(base
->m_data
.pobj
, &scratchKey
);
1160 tvRef
= objOffsetGet(instanceFromTv(base
), scratchKey
);
1165 * Intermediate Elem operation for an unsetting member instruction.
1167 * Returned pointer is not yet unboxed. (I.e. it cannot point into a RefData.)
1169 template <bool intishWarn
, KeyType keyType
= KeyType::Any
>
1170 TypedValue
* ElemU(TypedValue
& tvRef
, TypedValue
* base
, key_type
<keyType
> key
) {
1171 base
= tvToCell(base
);
1172 assertx(cellIsPlausible(*base
));
1174 switch (base
->m_type
) {
1180 case KindOfResource
:
1181 // Unset on scalar base never modifies the base, but the const_cast is
1182 // necessary to placate the type system.
1183 return const_cast<TypedValue
*>(&immutable_uninit_base
);
1184 case KindOfPersistentString
:
1186 raise_error(Strings::OP_NOT_SUPPORTED_STRING
);
1188 case KindOfPersistentVec
:
1190 return ElemUVec
<keyType
>(base
, key
);
1191 case KindOfPersistentDict
:
1193 return ElemUDict
<keyType
>(base
, key
);
1194 case KindOfPersistentKeyset
:
1196 return ElemUKeyset
<keyType
>(base
, key
);
1197 case KindOfPersistentArray
:
1199 return ElemUArray
<intishWarn
, keyType
>(base
, key
);
1201 return ElemUObject
<keyType
>(tvRef
, base
, key
);
1205 unknownBaseType(base
);
1209 * NewElem when base is Null
1211 inline TypedValue
* NewElemEmptyish(TypedValue
* base
) {
1212 detail::checkPromotion(base
);
1213 Array a
= Array::Create();
1214 TypedValue
* result
= a
.lvalAt().tv_ptr();
1215 tvAsVariant(base
) = a
;
1220 * NewElem when base is not a valid type (a number, true boolean,
1221 * non-empty string, etc.)
1223 inline TypedValue
* NewElemInvalid(TypedValue
& tvRef
) {
1224 raise_warning("Cannot use a scalar value as an array");
1230 * NewElem when base is a Boolean
1232 inline TypedValue
* NewElemBoolean(TypedValue
& tvRef
, TypedValue
* base
) {
1233 if (base
->m_data
.num
) {
1234 return NewElemInvalid(tvRef
);
1236 return NewElemEmptyish(base
);
1240 * NewElem when base is a String
1242 inline TypedValue
* NewElemString(TypedValue
& tvRef
, TypedValue
* base
) {
1243 if (base
->m_data
.pstr
->size() == 0) {
1244 return NewElemEmptyish(base
);
1246 return NewElemInvalid(tvRef
);
1250 * NewElem when base is an Array
1252 template <bool reffy
>
1253 inline TypedValue
* NewElemArray(TypedValue
* base
) {
1254 assertx(tvIsArray(base
));
1255 assertx(tvIsPlausible(*base
));
1257 tvAsVariant(base
).asArrRef().lvalAtRef().tv_ptr() :
1258 tvAsVariant(base
).asArrRef().lvalAt().tv_ptr();
1262 * NewElem when base is an Object
1264 inline TypedValue
* NewElemObject(TypedValue
& tvRef
, TypedValue
* base
) {
1265 if (base
->m_data
.pobj
->isCollection()) {
1266 throw_cannot_use_newelem_for_lval_read_col();
1269 tvRef
= objOffsetGet(instanceFromTv(base
), make_tv
<KindOfNull
>());
1274 * $result = ($base[] = ...);
1276 template <bool reffy
>
1277 inline TypedValue
* NewElem(TypedValue
& tvRef
,
1279 base
= tvToCell(base
);
1280 assertx(cellIsPlausible(*base
));
1282 switch (base
->m_type
) {
1285 return NewElemEmptyish(base
);
1287 return NewElemBoolean(tvRef
, base
);
1290 case KindOfResource
:
1291 return NewElemInvalid(tvRef
);
1292 case KindOfPersistentString
:
1294 return NewElemString(tvRef
, base
);
1295 case KindOfPersistentVec
:
1297 throw_cannot_use_newelem_for_lval_read_vec();
1298 case KindOfPersistentDict
:
1300 throw_cannot_use_newelem_for_lval_read_dict();
1301 case KindOfPersistentKeyset
:
1303 throw_cannot_use_newelem_for_lval_read_keyset();
1304 case KindOfPersistentArray
:
1306 return NewElemArray
<reffy
>(base
);
1308 return NewElemObject(tvRef
, base
);
1312 unknownBaseType(base
);
1316 * SetElem when base is Null
1318 template <KeyType keyType
>
1319 inline void SetElemEmptyish(TypedValue
* base
, key_type
<keyType
> key
,
1321 detail::checkPromotion(base
);
1322 auto const& scratchKey
= initScratchKey(key
);
1323 tvAsVariant(base
) = Array::Create();
1324 tvAsVariant(base
).asArrRef().set(tvAsCVarRef(&scratchKey
),
1325 tvAsCVarRef(value
));
1329 * SetElem when base is an Int64, Double, or Resource.
1331 template <bool setResult
>
1332 inline void SetElemScalar(Cell
* value
) {
1333 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
1335 throw InvalidSetMException(make_tv
<KindOfNull
>());
1337 tvDecRefGen((TypedValue
*)value
);
1338 tvWriteNull(*(TypedValue
*)value
);
1342 * SetElem when base is a Boolean
1344 template <bool setResult
, KeyType keyType
>
1345 inline void SetElemBoolean(TypedValue
* base
, key_type
<keyType
> key
,
1347 if (base
->m_data
.num
) {
1348 SetElemScalar
<setResult
>(value
);
1350 SetElemEmptyish
<keyType
>(base
, key
, value
);
1355 * Convert a key to integer for SetElem
1357 template<KeyType keyType
>
1358 inline int64_t castKeyToInt(key_type
<keyType
> key
) {
1359 return cellToInt(initScratchKey(key
));
1363 inline int64_t castKeyToInt
<KeyType::Int
>(int64_t key
) {
1368 * SetElem when base is a String
1370 template <bool setResult
, KeyType keyType
>
1371 inline StringData
* SetElemString(TypedValue
* base
, key_type
<keyType
> key
,
1373 int baseLen
= base
->m_data
.pstr
->size();
1375 SetElemEmptyish
<keyType
>(base
, key
, value
);
1377 tvIncRefGen(*value
);
1378 throw InvalidSetMException(*value
);
1383 // Convert key to string offset.
1384 int64_t x
= castKeyToInt
<keyType
>(key
);
1385 if (UNLIKELY(x
< 0 || x
>= StringData::MaxSize
)) {
1386 // Can't use PRId64 here because of order of inclusion issues
1387 raise_warning("Illegal string offset: %lld", (long long)x
);
1389 throw InvalidSetMException(make_tv
<KindOfNull
>());
1392 tvWriteNull(*value
);
1396 // Compute how long the resulting string will be. Type needs
1405 // Extract the first character of (string)value.
1409 if (LIKELY(isStringType(value
->m_type
))) {
1410 valStr
= value
->m_data
.pstr
;
1411 valStr
->incRefCount();
1413 valStr
= tvCastToStringData(*value
);
1416 if (valStr
->size() > 0) {
1417 y
[0] = valStr
->data()[0];
1425 // Create and save the result.
1426 if (x
>= 0 && x
< baseLen
&& !base
->m_data
.pstr
->cowCheck()) {
1427 // Modify base in place. This is safe because the LHS owns the
1429 auto const oldp
= base
->m_data
.pstr
;
1430 auto const newp
= oldp
->modifyChar(x
, y
[0]);
1431 if (UNLIKELY(newp
!= oldp
)) {
1433 base
->m_data
.pstr
= newp
;
1434 base
->m_type
= KindOfString
;
1437 StringData
* sd
= StringData::Make(slen
);
1438 char* s
= sd
->mutableData();
1439 memcpy(s
, base
->m_data
.pstr
->data(), baseLen
);
1441 memset(&s
[baseLen
], ' ', slen
- baseLen
- 1);
1445 decRefStr(base
->m_data
.pstr
);
1446 base
->m_data
.pstr
= sd
;
1447 base
->m_type
= KindOfString
;
1450 return StringData::Make(y
, strlen(y
), CopyString
);
1454 * SetElem when base is an Object
1456 template <KeyType keyType
>
1457 inline void SetElemObject(TypedValue
* base
, key_type
<keyType
> key
,
1459 auto const scratchKey
= initScratchKey(key
);
1460 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1461 collections::set(base
->m_data
.pobj
, &scratchKey
, value
);
1463 objOffsetSet(instanceFromTv(base
), scratchKey
, value
);
1468 * arrayRefShuffle is used by SetElemArray and by helpers for translated code
1469 * to do the necessary bookkeeping after mutating an array. The helpers return
1470 * an ArrayData* if and only if the base array was not in a php reference. If
1471 * the base array was in a reference, that reference may no longer refer to an
1472 * array after the set operation, so the helpers don't return anything.
1474 template<bool setRef
> struct ShuffleReturn
{};
1476 template<> struct ShuffleReturn
<true> {
1477 typedef void return_type
;
1478 static void do_return(ArrayData
* /*a*/) {}
1481 template<> struct ShuffleReturn
<false> {
1482 typedef ArrayData
* return_type
;
1483 static ArrayData
* do_return(ArrayData
* a
) { return a
; }
1486 template<bool setRef
, DataType dt
> inline
1487 typename ShuffleReturn
<setRef
>::return_type
1488 arrayRefShuffle(ArrayData
* oldData
, ArrayData
* newData
, TypedValue
* base
) {
1489 if (newData
== oldData
) {
1490 return ShuffleReturn
<setRef
>::do_return(oldData
);
1494 if (isArrayLikeType(base
->m_type
) && base
->m_data
.parr
== oldData
) {
1496 base
->m_data
.parr
= newData
;
1497 assertx(cellIsPlausible(*base
));
1499 // The base was in a reference that was overwritten by the set operation,
1500 // so we don't want to store the new ArrayData to it. oldData has already
1501 // been decrefed and there's nobody left to care about newData, so decref
1502 // newData instead of oldData.
1507 return ShuffleReturn
<setRef
>::do_return(newData
);
1512 * SetElem helper with Array base and Int64 key
1514 template<bool setResult
, bool intishWarn
>
1515 inline ArrayData
* SetElemArrayPre(ArrayData
* a
,
1519 return a
->set(key
, *value
, copy
);
1523 * SetElem helper with Array base and String key
1525 template<bool setResult
, bool intishWarn
>
1526 inline ArrayData
* SetElemArrayPre(ArrayData
* a
,
1531 assert(a
->isPHPArray());
1532 if (key
->isStrictlyInteger(n
)) {
1533 if (intishWarn
) raise_intish_index_cast();
1534 return a
->set(n
, *value
, copy
);
1536 return a
->set(key
, *value
, copy
);
1540 template<bool setResult
, bool intishWarn
>
1541 inline ArrayData
* SetElemArrayPre(ArrayData
* a
,
1545 if (isStringType(key
.m_type
)) {
1546 return SetElemArrayPre
<setResult
, intishWarn
>(
1547 a
, key
.m_data
.pstr
, value
, copy
1550 if (key
.m_type
== KindOfInt64
) {
1551 return SetElemArrayPre
<setResult
, false>(
1552 a
, key
.m_data
.num
, value
, copy
1555 if (RuntimeOption::EvalHackArrCompatNotices
) {
1556 raiseHackArrCompatImplicitArrayKey(&key
);
1558 if (isNullType(key
.m_type
)) {
1559 return a
->set(staticEmptyString(), *value
, copy
);
1561 if (!isArrayLikeType(key
.m_type
) && key
.m_type
!= KindOfObject
) {
1562 return SetElemArrayPre
<setResult
, false>(a
, tvAsCVarRef(&key
).toInt64(),
1566 raise_warning("Illegal offset type");
1567 // Assignment failed, so the result is null rather than the RHS.
1570 tvWriteNull(*value
);
1572 throw InvalidSetMException(make_tv
<KindOfNull
>());
1578 * SetElem when base is an Array
1580 template <bool setResult
, KeyType keyType
, bool intishWarn
>
1581 inline void SetElemArray(TypedValue
* base
, key_type
<keyType
> key
,
1583 assertx(tvIsArray(base
));
1584 assertx(tvIsPlausible(*base
));
1586 ArrayData
* a
= base
->m_data
.parr
;
1587 bool copy
= a
->cowCheck() ||
1588 (tvIsArray(value
) && value
->m_data
.parr
== a
);
1590 auto* newData
= SetElemArrayPre
<setResult
, intishWarn
>(a
, key
, value
, copy
);
1591 assertx(newData
->isPHPArray());
1593 arrayRefShuffle
<true, KindOfArray
>(a
, newData
, base
);
1597 * SetElem when base is a Vec
1599 template<bool setResult
>
1600 inline ArrayData
* SetElemVecPre(ArrayData
* a
,
1604 return PackedArray::SetIntVec(a
, key
, *value
, copy
);
1607 template <bool setResult
>
1609 SetElemVecPre(ArrayData
* a
, StringData
* key
, Cell
* /*value*/, bool /*copy*/) {
1610 throwInvalidArrayKeyException(key
, a
);
1613 template<bool setResult
>
1614 inline ArrayData
* SetElemVecPre(ArrayData
* a
,
1618 auto const dt
= key
.m_type
;
1619 if (LIKELY(isIntType(dt
))) return SetElemVecPre
<setResult
>(a
, key
.m_data
.num
,
1621 if (isStringType(dt
)) return SetElemVecPre
<setResult
>(a
, key
.m_data
.pstr
,
1623 throwInvalidArrayKeyException(&key
, a
);
1626 template <bool setResult
, KeyType keyType
>
1627 inline void SetElemVec(TypedValue
* base
, key_type
<keyType
> key
,
1629 assertx(tvIsVecArray(base
));
1630 assertx(tvIsPlausible(*base
));
1632 ArrayData
* a
= base
->m_data
.parr
;
1633 bool copy
= a
->cowCheck() ||
1634 (tvIsVecArray(value
) && value
->m_data
.parr
== a
);
1636 auto* newData
= SetElemVecPre
<setResult
>(a
, key
, value
, copy
);
1637 assertx(newData
->isVecArray());
1639 arrayRefShuffle
<true, KindOfVec
>(a
, newData
, base
);
1643 * SetElem when base is a Dict
1645 template<bool setResult
>
1646 inline ArrayData
* SetElemDictPre(ArrayData
* a
,
1650 return MixedArray::SetIntDict(a
, key
, *value
, copy
);
1653 template<bool setResult
>
1654 inline ArrayData
* SetElemDictPre(ArrayData
* a
,
1658 return MixedArray::SetStrDict(a
, key
, *value
, copy
);
1661 template<bool setResult
>
1662 inline ArrayData
* SetElemDictPre(ArrayData
* a
,
1666 auto const dt
= key
.m_type
;
1667 if (isIntType(dt
)) return SetElemDictPre
<setResult
>(a
, key
.m_data
.num
,
1669 if (isStringType(dt
)) return SetElemDictPre
<setResult
>(a
, key
.m_data
.pstr
,
1671 throwInvalidArrayKeyException(&key
, a
);
1674 template <bool setResult
, KeyType keyType
>
1675 inline void SetElemDict(TypedValue
* base
, key_type
<keyType
> key
,
1677 assertx(tvIsDict(base
));
1678 assertx(tvIsPlausible(*base
));
1680 ArrayData
* a
= base
->m_data
.parr
;
1681 bool copy
= a
->cowCheck() ||
1682 (tvIsDict(value
) && value
->m_data
.parr
== a
);
1684 auto* newData
= SetElemDictPre
<setResult
>(a
, key
, value
, copy
);
1685 assertx(newData
->isDict());
1687 arrayRefShuffle
<true, KindOfDict
>(a
, newData
, base
);
1691 * SetElem() leaves the result in 'value', rather than returning it as in
1692 * SetOpElem(), because doing so avoids a dup operation that SetOpElem() can't
1695 template <bool setResult
, KeyType keyType
, bool intishWarn
>
1697 StringData
* SetElemSlow(TypedValue
* base
, key_type
<keyType
> key
, Cell
* value
) {
1698 base
= tvToCell(base
);
1699 assertx(cellIsPlausible(*base
));
1701 switch (base
->m_type
) {
1704 SetElemEmptyish
<keyType
>(base
, key
, value
);
1707 SetElemBoolean
<setResult
, keyType
>(base
, key
, value
);
1711 case KindOfResource
:
1712 SetElemScalar
<setResult
>(value
);
1714 case KindOfPersistentString
:
1716 return SetElemString
<setResult
, keyType
>(base
, key
, value
);
1717 case KindOfPersistentVec
:
1719 SetElemVec
<setResult
, keyType
>(base
, key
, value
);
1721 case KindOfPersistentDict
:
1723 SetElemDict
<setResult
, keyType
>(base
, key
, value
);
1725 case KindOfPersistentKeyset
:
1727 throwInvalidKeysetOperation();
1728 case KindOfPersistentArray
:
1730 SetElemArray
<setResult
, keyType
, intishWarn
>(base
, key
, value
);
1733 SetElemObject
<keyType
>(base
, key
, value
);
1738 unknownBaseType(base
);
1742 * Fast path for SetElem assuming base is an Array
1744 template <bool setResult
, bool intishWarn
, KeyType keyType
= KeyType::Any
>
1745 inline StringData
* SetElem(TypedValue
* base
, key_type
<keyType
> key
,
1747 assertx(tvIsPlausible(*base
));
1749 if (LIKELY(tvIsArray(base
))) {
1750 SetElemArray
<setResult
, keyType
, intishWarn
>(base
, key
, value
);
1753 if (LIKELY(tvIsVecArray(base
))) {
1754 SetElemVec
<setResult
, keyType
>(base
, key
, value
);
1757 if (LIKELY(tvIsDict(base
))) {
1758 SetElemDict
<setResult
, keyType
>(base
, key
, value
);
1761 return SetElemSlow
<setResult
, keyType
, intishWarn
>(base
, key
, value
);
1765 * SetNewElem when base is Null
1767 inline void SetNewElemEmptyish(TypedValue
* base
, Cell
* value
) {
1768 detail::checkPromotion(base
);
1769 Array a
= Array::Create();
1770 a
.append(cellAsCVarRef(*value
));
1771 tvAsVariant(base
) = a
;
1775 * SetNewElem when base is Int64 or Double
1777 template <bool setResult
>
1778 inline void SetNewElemScalar(Cell
* value
) {
1779 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
1781 throw InvalidSetMException(make_tv
<KindOfNull
>());
1783 tvDecRefGen((TypedValue
*)value
);
1784 tvWriteNull(*(TypedValue
*)value
);
1788 * SetNewElem when base is a Boolean
1790 template <bool setResult
>
1791 inline void SetNewElemBoolean(TypedValue
* base
, Cell
* value
) {
1792 if (base
->m_data
.num
) {
1793 SetNewElemScalar
<setResult
>(value
);
1795 SetNewElemEmptyish(base
, value
);
1800 * SetNewElem when base is a String
1802 inline void SetNewElemString(TypedValue
* base
, Cell
* value
) {
1803 int baseLen
= base
->m_data
.pstr
->size();
1805 SetNewElemEmptyish(base
, value
);
1807 raise_error("[] operator not supported for strings");
1812 * SetNewElem when base is an Array
1814 inline void SetNewElemArray(TypedValue
* base
, Cell
* value
) {
1815 base
= tvToCell(base
);
1816 assertx(tvIsArray(base
));
1817 assertx(tvIsPlausible(*base
));
1818 auto a
= base
->m_data
.parr
;
1819 auto const copy
= a
->cowCheck() ||
1820 (tvIsArray(value
) && value
->m_data
.parr
== a
);
1821 auto a2
= a
->append(*value
, copy
);
1823 base
->m_type
= KindOfArray
;
1824 base
->m_data
.parr
= a2
;
1825 a
->decRefAndRelease();
1830 * SetNewElem when base is a Vec
1832 inline void SetNewElemVec(TypedValue
* base
, Cell
* value
) {
1833 base
= tvToCell(base
);
1834 assertx(tvIsVecArray(base
));
1835 assertx(tvIsPlausible(*base
));
1836 auto a
= base
->m_data
.parr
;
1837 auto const copy
= a
->cowCheck() ||
1838 (tvIsVecArray(value
) && value
->m_data
.parr
== a
);
1839 auto a2
= PackedArray::AppendVec(a
, *value
, copy
);
1841 base
->m_type
= KindOfVec
;
1842 base
->m_data
.parr
= a2
;
1843 assertx(cellIsPlausible(*base
));
1844 a
->decRefAndRelease();
1849 * SetNewElem when base is a Dict
1851 inline void SetNewElemDict(TypedValue
* base
, Cell
* value
) {
1852 base
= tvToCell(base
);
1853 assertx(tvIsDict(base
));
1854 assertx(tvIsPlausible(*base
));
1855 auto a
= base
->m_data
.parr
;
1856 auto const copy
= a
->cowCheck() ||
1857 (tvIsDict(value
) && value
->m_data
.parr
== a
);
1858 auto a2
= MixedArray::AppendDict(a
, *value
, copy
);
1860 base
->m_type
= KindOfDict
;
1861 base
->m_data
.parr
= a2
;
1862 assertx(cellIsPlausible(*base
));
1863 a
->decRefAndRelease();
1868 * SetNewElem when base is a Keyset
1870 inline void SetNewElemKeyset(TypedValue
* base
, Cell
* value
) {
1871 base
= tvToCell(base
);
1872 assertx(tvIsKeyset(base
));
1873 assertx(tvIsPlausible(*base
));
1874 auto a
= base
->m_data
.parr
;
1875 auto const copy
= a
->cowCheck() ||
1876 (tvIsKeyset(value
) && value
->m_data
.parr
== a
);
1877 auto a2
= SetArray::Append(a
, *value
, copy
);
1879 base
->m_type
= KindOfKeyset
;
1880 base
->m_data
.parr
= a2
;
1881 assertx(cellIsPlausible(*base
));
1882 a
->decRefAndRelease();
1887 * SetNewElem when base is an Object
1889 inline void SetNewElemObject(TypedValue
* base
, Cell
* value
) {
1890 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1891 collections::append(base
->m_data
.pobj
, (TypedValue
*)value
);
1893 objOffsetAppend(instanceFromTv(base
), (TypedValue
*)value
);
1900 template <bool setResult
>
1901 inline void SetNewElem(TypedValue
* base
, Cell
* value
) {
1902 base
= tvToCell(base
);
1903 assertx(cellIsPlausible(*base
));
1905 switch (base
->m_type
) {
1908 return SetNewElemEmptyish(base
, value
);
1910 return SetNewElemBoolean
<setResult
>(base
, value
);
1913 case KindOfResource
:
1914 return SetNewElemScalar
<setResult
>(value
);
1915 case KindOfPersistentString
:
1917 return SetNewElemString(base
, value
);
1918 case KindOfPersistentVec
:
1920 return SetNewElemVec(base
, value
);
1921 case KindOfPersistentDict
:
1923 return SetNewElemDict(base
, value
);
1924 case KindOfPersistentKeyset
:
1926 return SetNewElemKeyset(base
, value
);
1927 case KindOfPersistentArray
:
1929 return SetNewElemArray(base
, value
);
1931 return SetNewElemObject(base
, value
);
1935 unknownBaseType(base
);
1939 * SetOpElem when base is Null
1941 inline TypedValue
* SetOpElemEmptyish(SetOpOp op
, Cell
* base
,
1942 TypedValue key
, Cell
* rhs
) {
1943 assert(cellIsPlausible(*base
));
1945 detail::checkPromotion(base
);
1947 Array a
= Array::Create();
1948 auto const lval
= a
.lvalAt(tvAsCVarRef(&key
));
1949 tvAsVariant(base
) = a
;
1951 raise_notice(Strings::UNDEFINED_INDEX
,
1952 tvAsCVarRef(&key
).toString().data());
1954 setopBody(lval
.tv_ptr(), op
, rhs
);
1955 return lval
.tv_ptr();
1959 * SetOpElem when base is Int64 or Double
1961 inline TypedValue
* SetOpElemScalar(TypedValue
& tvRef
) {
1962 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
1968 * $result = ($base[$x] <op>= $y)
1970 template <bool intishWarn
>
1971 inline TypedValue
* SetOpElem(TypedValue
& tvRef
,
1972 SetOpOp op
, TypedValue
* base
,
1973 TypedValue key
, Cell
* rhs
) {
1974 base
= tvToCell(base
);
1975 assertx(cellIsPlausible(*base
));
1977 switch (base
->m_type
) {
1980 return SetOpElemEmptyish(op
, base
, key
, rhs
);
1983 if (base
->m_data
.num
) {
1984 return SetOpElemScalar(tvRef
);
1986 return SetOpElemEmptyish(op
, base
, key
, rhs
);
1990 case KindOfResource
:
1991 return SetOpElemScalar(tvRef
);
1993 case KindOfPersistentString
:
1995 if (base
->m_data
.pstr
->size() != 0) {
1996 raise_error("Cannot use assign-op operators with overloaded "
1997 "objects nor string offsets");
1999 return SetOpElemEmptyish(op
, base
, key
, rhs
);
2001 case KindOfPersistentVec
:
2004 result
= ElemDVec
<false, KeyType::Any
>(base
, key
);
2005 result
= tvAssertCell(result
);
2006 setopBody(result
, op
, rhs
);
2010 case KindOfPersistentDict
:
2013 result
= ElemDDict
<false, KeyType::Any
>(base
, key
);
2014 result
= tvAssertCell(result
);
2015 setopBody(result
, op
, rhs
);
2019 case KindOfPersistentKeyset
:
2021 throwInvalidKeysetOperation();
2023 case KindOfPersistentArray
:
2026 RuntimeOption::EvalHackArrCompatNotices
&&
2027 !ArrNR
{base
->m_data
.parr
}.asArray().exists(tvAsCVarRef(&key
))
2029 raiseHackArrCompatMissingSetOp();
2032 auto constexpr mode
= MoreWarnings
? MOpMode::Warn
: MOpMode::None
;
2033 result
= ElemDArray
<mode
, false, intishWarn
, KeyType::Any
>(base
, key
);
2034 result
= tvToCell(result
);
2035 setopBody(result
, op
, rhs
);
2039 case KindOfObject
: {
2041 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
2042 result
= collections::atRw(base
->m_data
.pobj
, &key
);
2043 setopBody(tvToCell(result
), op
, rhs
);
2045 tvRef
= objOffsetGet(instanceFromTv(base
), key
);
2047 setopBody(tvToCell(result
), op
, rhs
);
2048 objOffsetSet(instanceFromTv(base
), key
, result
, false);
2056 unknownBaseType(base
);
2059 inline TypedValue
* SetOpNewElemEmptyish(SetOpOp op
,
2060 TypedValue
* base
, Cell
* rhs
) {
2061 detail::checkPromotion(base
);
2062 Array a
= Array::Create();
2063 TypedValue
* result
= a
.lvalAt().tv_ptr();
2064 tvAsVariant(base
) = a
;
2065 setopBody(tvToCell(result
), op
, rhs
);
2068 inline TypedValue
* SetOpNewElemScalar(TypedValue
& tvRef
) {
2069 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
2073 inline TypedValue
* SetOpNewElem(TypedValue
& tvRef
,
2074 SetOpOp op
, TypedValue
* base
,
2076 base
= tvToCell(base
);
2077 assertx(cellIsPlausible(*base
));
2079 switch (base
->m_type
) {
2082 return SetOpNewElemEmptyish(op
, base
, rhs
);
2085 if (base
->m_data
.num
) {
2086 return SetOpNewElemScalar(tvRef
);
2088 return SetOpNewElemEmptyish(op
, base
, rhs
);
2092 case KindOfResource
:
2093 return SetOpNewElemScalar(tvRef
);
2095 case KindOfPersistentString
:
2097 if (base
->m_data
.pstr
->size() != 0) {
2098 raise_error("[] operator not supported for strings");
2100 return SetOpNewElemEmptyish(op
, base
, rhs
);
2102 case KindOfPersistentVec
:
2104 throw_cannot_use_newelem_for_lval_read_vec();
2105 case KindOfPersistentDict
:
2107 throw_cannot_use_newelem_for_lval_read_dict();
2108 case KindOfPersistentKeyset
:
2110 throw_cannot_use_newelem_for_lval_read_keyset();
2112 case KindOfPersistentArray
:
2115 result
= tvAsVariant(base
).asArrRef().lvalAt().tv_ptr();
2116 setopBody(tvToCell(result
), op
, rhs
);
2120 case KindOfObject
: {
2122 if (base
->m_data
.pobj
->isCollection()) {
2123 throw_cannot_use_newelem_for_lval_read_col();
2126 tvRef
= objOffsetGet(instanceFromTv(base
), make_tv
<KindOfNull
>());
2128 setopBody(tvToCell(result
), op
, rhs
);
2129 objOffsetAppend(instanceFromTv(base
), result
, false);
2137 unknownBaseType(base
);
2140 Cell
incDecBodySlow(IncDecOp op
, Cell
* fr
);
2142 inline Cell
IncDecBody(IncDecOp op
, Cell
* fr
) {
2143 assert(cellIsPlausible(*fr
));
2145 if (UNLIKELY(fr
->m_type
!= KindOfInt64
)) {
2146 return incDecBodySlow(op
, fr
);
2150 assert(cellIsPlausible(*fr
));
2154 // fast cases, assuming integers overflow to ints
2156 case IncDecOp::PreInc
:
2159 case IncDecOp::PostInc
: {
2160 auto const tmp
= copy();
2164 case IncDecOp::PreDec
:
2167 case IncDecOp::PostDec
: {
2168 auto const tmp
= copy();
2175 // slow case, where integers can overflow to floats
2177 case IncDecOp::PreIncO
:
2180 case IncDecOp::PostIncO
: {
2181 auto const tmp
= copy();
2185 case IncDecOp::PreDecO
:
2188 case IncDecOp::PostDecO
: {
2189 auto const tmp
= copy();
2198 inline Cell
IncDecElemEmptyish(
2203 detail::checkPromotion(base
);
2205 auto a
= Array::Create();
2206 auto const lval
= a
.lvalAt(tvAsCVarRef(&key
));
2207 tvAsVariant(base
) = a
;
2209 raise_notice(Strings::UNDEFINED_INDEX
,
2210 tvAsCVarRef(&key
).toString().data());
2212 assert(lval
.type() == KindOfNull
);
2213 return IncDecBody(op
, lval
.tv_ptr());
2216 inline Cell
IncDecElemScalar() {
2217 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
2218 return make_tv
<KindOfNull
>();
2221 template <bool intishWarn
>
2222 inline Cell
IncDecElem(
2227 base
= tvToCell(base
);
2228 assertx(cellIsPlausible(*base
));
2230 switch (base
->m_type
) {
2233 return IncDecElemEmptyish(op
, base
, key
);
2236 if (base
->m_data
.num
) {
2237 return IncDecElemScalar();
2239 return IncDecElemEmptyish(op
, base
, key
);
2243 case KindOfResource
:
2244 return IncDecElemScalar();
2246 case KindOfPersistentString
:
2248 if (base
->m_data
.pstr
->size() != 0) {
2249 raise_error("Cannot increment/decrement overloaded objects "
2250 "nor string offsets");
2252 return IncDecElemEmptyish(op
, base
, key
);
2254 case KindOfPersistentVec
:
2256 auto result
= ElemDVec
<false, KeyType::Any
>(base
, key
);
2257 return IncDecBody(op
, tvAssertCell(result
));
2260 case KindOfPersistentDict
:
2262 auto result
= ElemDDict
<false, KeyType::Any
>(base
, key
);
2263 return IncDecBody(op
, tvAssertCell(result
));
2266 case KindOfPersistentKeyset
:
2268 throwInvalidKeysetOperation();
2270 case KindOfPersistentArray
:
2273 RuntimeOption::EvalHackArrCompatNotices
&&
2274 !ArrNR
{base
->m_data
.parr
}.asArray().exists(tvAsCVarRef(&key
))
2276 raiseHackArrCompatMissingIncDec();
2278 auto constexpr mode
= MoreWarnings
? MOpMode::Warn
: MOpMode::None
;
2280 ElemDArray
<mode
, false, intishWarn
, KeyType::Any
>(base
, key
);
2281 return IncDecBody(op
, tvToCell(result
));
2284 case KindOfObject
: {
2286 auto localTvRef
= make_tv
<KindOfUninit
>();
2288 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
2289 result
= collections::atRw(base
->m_data
.pobj
, &key
);
2290 assert(cellIsPlausible(*result
));
2292 localTvRef
= objOffsetGet(instanceFromTv(base
), key
);
2293 result
= tvToCell(&localTvRef
);
2296 auto const dest
= IncDecBody(op
, result
);
2297 tvDecRefGen(localTvRef
);
2304 unknownBaseType(base
);
2307 inline Cell
IncDecNewElemEmptyish(
2311 detail::checkPromotion(base
);
2312 auto a
= Array::Create();
2313 auto result
= a
.lvalAt().tv_ptr();
2314 tvAsVariant(base
) = a
;
2315 assert(result
->m_type
== KindOfNull
);
2316 return IncDecBody(op
, result
);
2319 inline Cell
IncDecNewElemScalar() {
2320 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
2321 return make_tv
<KindOfNull
>();
2324 inline Cell
IncDecNewElem(
2329 base
= tvToCell(base
);
2330 assertx(cellIsPlausible(*base
));
2332 switch (base
->m_type
) {
2335 return IncDecNewElemEmptyish(op
, base
);
2338 if (base
->m_data
.num
) {
2339 return IncDecNewElemScalar();
2341 return IncDecNewElemEmptyish(op
, base
);
2345 case KindOfResource
:
2346 return IncDecNewElemScalar();
2348 case KindOfPersistentString
:
2350 if (base
->m_data
.pstr
->size() != 0) {
2351 raise_error("[] operator not supported for strings");
2353 return IncDecNewElemEmptyish(op
, base
);
2355 case KindOfPersistentVec
:
2357 throw_cannot_use_newelem_for_lval_read_vec();
2358 case KindOfPersistentDict
:
2360 throw_cannot_use_newelem_for_lval_read_dict();
2361 case KindOfPersistentKeyset
:
2363 throw_cannot_use_newelem_for_lval_read_keyset();
2365 case KindOfPersistentArray
:
2367 TypedValue
* result
= tvAsVariant(base
).asArrRef().lvalAt().tv_ptr();
2368 assert(result
->m_type
== KindOfNull
);
2369 return IncDecBody(op
, tvToCell(result
));
2372 case KindOfObject
: {
2374 if (base
->m_data
.pobj
->isCollection()) {
2375 throw_cannot_use_newelem_for_lval_read_col();
2377 tvRef
= objOffsetGet(instanceFromTv(base
), make_tv
<KindOfNull
>());
2378 result
= tvToCell(&tvRef
);
2379 return IncDecBody(op
, result
);
2385 unknownBaseType(base
);
2389 * UnsetElemArray when key is an Int64
2391 template <bool intishWarn
>
2392 inline ArrayData
* UnsetElemArrayPre(ArrayData
* a
, int64_t key
,
2394 return a
->remove(key
, copy
);
2398 * UnsetElemArray when key is a String
2400 template <bool intishWarn
>
2401 inline ArrayData
* UnsetElemArrayPre(ArrayData
* a
, StringData
* key
,
2404 assert(a
->isPHPArray());
2405 if (key
->isStrictlyInteger(n
)) {
2406 if (intishWarn
) raise_intish_index_cast();
2407 return a
->remove(n
, copy
);
2409 return a
->remove(key
, copy
);
2413 template <bool intishWarn
>
2414 inline ArrayData
* UnsetElemArrayPre(ArrayData
* a
, TypedValue key
,
2416 if (isStringType(key
.m_type
)) {
2417 return UnsetElemArrayPre
<intishWarn
>(a
, key
.m_data
.pstr
, copy
);
2419 if (key
.m_type
== KindOfInt64
) {
2420 return UnsetElemArrayPre
<false>(a
, key
.m_data
.num
, copy
);
2422 auto const k
= tvToKey(key
, a
);
2423 if (isNullType(k
.m_type
)) return a
;
2424 return a
->remove(k
, copy
);
2428 * UnsetElem when base is an Array
2430 template <KeyType keyType
, bool intishWarn
>
2431 inline void UnsetElemArray(TypedValue
* base
, key_type
<keyType
> key
) {
2432 assertx(tvIsArray(base
));
2433 assertx(tvIsPlausible(*base
));
2434 ArrayData
* a
= base
->m_data
.parr
;
2435 ArrayData
* a2
= UnsetElemArrayPre
<intishWarn
>(a
, key
, a
->cowCheck());
2438 base
->m_type
= KindOfArray
;
2439 base
->m_data
.parr
= a2
;
2440 assertx(cellIsPlausible(*base
));
2441 a
->decRefAndRelease();
2446 * UnsetElem when base is a Vec
2449 inline ArrayData
* UnsetElemVecPre(ArrayData
* a
, int64_t key
,
2451 return PackedArray::RemoveIntVec(a
, key
, copy
);
2455 UnsetElemVecPre(ArrayData
* a
, StringData
* /*key*/, bool /*copy*/) {
2456 /* Never contains strings, so a no-op. */
2460 inline ArrayData
* UnsetElemVecPre(ArrayData
* a
, TypedValue key
,
2462 auto const dt
= key
.m_type
;
2463 if (LIKELY(isIntType(dt
))) return UnsetElemVecPre(a
, key
.m_data
.num
, copy
);
2464 if (isStringType(dt
)) return UnsetElemVecPre(a
, key
.m_data
.pstr
, copy
);
2465 throwInvalidArrayKeyException(&key
, a
);
2468 template <KeyType keyType
>
2469 inline void UnsetElemVec(TypedValue
* base
, key_type
<keyType
> key
) {
2470 assertx(tvIsVecArray(base
));
2471 assertx(tvIsPlausible(*base
));
2472 ArrayData
* a
= base
->m_data
.parr
;
2473 ArrayData
* a2
= UnsetElemVecPre(a
, key
, a
->cowCheck());
2474 assertx(a2
->isVecArray() || a2
->isDict());
2477 base
->m_type
= a2
->toDataType();
2478 base
->m_data
.parr
= a2
;
2479 assertx(cellIsPlausible(*base
));
2480 a
->decRefAndRelease();
2485 * UnsetElem when base is a Dict
2488 inline ArrayData
* UnsetElemDictPre(ArrayData
* a
, int64_t key
,
2490 return MixedArray::RemoveIntDict(a
, key
, copy
);
2493 inline ArrayData
* UnsetElemDictPre(ArrayData
* a
, StringData
* key
,
2495 return MixedArray::RemoveStrDict(a
, key
, copy
);
2498 inline ArrayData
* UnsetElemDictPre(ArrayData
* a
, TypedValue key
,
2500 auto const dt
= key
.m_type
;
2501 if (isIntType(dt
)) return UnsetElemDictPre(a
, key
.m_data
.num
, copy
);
2502 if (isStringType(dt
)) return UnsetElemDictPre(a
, key
.m_data
.pstr
, copy
);
2503 throwInvalidArrayKeyException(&key
, a
);
2506 template <KeyType keyType
>
2507 inline void UnsetElemDict(TypedValue
* base
, key_type
<keyType
> key
) {
2508 assertx(tvIsDict(base
));
2509 assertx(tvIsPlausible(*base
));
2510 ArrayData
* a
= base
->m_data
.parr
;
2511 ArrayData
* a2
= UnsetElemDictPre(a
, key
, a
->cowCheck());
2514 base
->m_type
= KindOfDict
;
2515 base
->m_data
.parr
= a2
;
2516 assertx(cellIsPlausible(*base
));
2517 a
->decRefAndRelease();
2522 * UnsetElem when base is a Keyset
2525 inline ArrayData
* UnsetElemKeysetPre(ArrayData
* a
, int64_t key
,
2527 return SetArray::RemoveInt(a
, key
, copy
);
2530 inline ArrayData
* UnsetElemKeysetPre(ArrayData
* a
, StringData
* key
,
2532 return SetArray::RemoveStr(a
, key
, copy
);
2535 inline ArrayData
* UnsetElemKeysetPre(ArrayData
* a
, TypedValue key
,
2537 auto const dt
= key
.m_type
;
2538 if (isIntType(dt
)) return UnsetElemKeysetPre(a
, key
.m_data
.num
, copy
);
2539 if (isStringType(dt
)) return UnsetElemKeysetPre(a
, key
.m_data
.pstr
, copy
);
2540 throwInvalidArrayKeyException(&key
, a
);
2543 template <KeyType keyType
>
2544 inline void UnsetElemKeyset(TypedValue
* base
, key_type
<keyType
> key
) {
2545 assertx(tvIsKeyset(base
));
2546 assertx(tvIsPlausible(*base
));
2547 ArrayData
* a
= base
->m_data
.parr
;
2548 ArrayData
* a2
= UnsetElemKeysetPre(a
, key
, a
->cowCheck());
2551 base
->m_type
= KindOfKeyset
;
2552 base
->m_data
.parr
= a2
;
2553 assertx(cellIsPlausible(*base
));
2554 a
->decRefAndRelease();
2559 * unset($base[$member])
2561 template <KeyType keyType
, bool intishWarn
>
2563 void UnsetElemSlow(TypedValue
* base
, key_type
<keyType
> key
) {
2564 base
= tvToCell(base
);
2565 assertx(cellIsPlausible(*base
));
2567 switch (base
->m_type
) {
2573 case KindOfResource
:
2574 return; // Do nothing.
2576 case KindOfPersistentString
:
2578 raise_error(Strings::CANT_UNSET_STRING
);
2581 case KindOfPersistentVec
:
2583 UnsetElemVec
<keyType
>(base
, key
);
2586 case KindOfPersistentDict
:
2588 UnsetElemDict
<keyType
>(base
, key
);
2591 case KindOfPersistentKeyset
:
2593 UnsetElemKeyset
<keyType
>(base
, key
);
2596 case KindOfPersistentArray
:
2598 UnsetElemArray
<keyType
, intishWarn
>(base
, key
);
2601 case KindOfObject
: {
2602 auto const& scratchKey
= initScratchKey(key
);
2603 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
2604 collections::unset(base
->m_data
.pobj
, &scratchKey
);
2606 objOffsetUnset(instanceFromTv(base
), scratchKey
);
2614 unknownBaseType(base
);
2618 * Fast path for UnsetElem assuming base is an Array
2620 template <bool intishWarn
, KeyType keyType
= KeyType::Any
>
2621 inline void UnsetElem(TypedValue
* base
, key_type
<keyType
> key
) {
2622 assertx(tvIsPlausible(*base
));
2624 if (LIKELY(tvIsArray(base
))) {
2625 return UnsetElemArray
<keyType
, intishWarn
>(base
, key
);
2627 if (LIKELY(tvIsVecArray(base
))) {
2628 return UnsetElemVec
<keyType
>(base
, key
);
2630 if (LIKELY(tvIsDict(base
))) {
2631 return UnsetElemDict
<keyType
>(base
, key
);
2633 if (LIKELY(tvIsKeyset(base
))) {
2634 return UnsetElemKeyset
<keyType
>(base
, key
);
2636 return UnsetElemSlow
<keyType
, intishWarn
>(base
, key
);
2640 * IssetEmptyElem when base is an Object
2642 template<bool useEmpty
, KeyType keyType
>
2643 bool IssetEmptyElemObj(ObjectData
* instance
, key_type
<keyType
> key
) {
2644 auto scratchKey
= initScratchKey(key
);
2645 if (LIKELY(instance
->isCollection())) {
2647 ? collections::empty(instance
, &scratchKey
)
2648 : collections::isset(instance
, &scratchKey
);
2652 ? objOffsetEmpty(instance
, scratchKey
)
2653 : objOffsetIsset(instance
, scratchKey
);
2657 * IssetEmptyElem when base is a String
2659 template <bool useEmpty
, KeyType keyType
>
2660 bool IssetEmptyElemString(TypedValue
* base
, key_type
<keyType
> key
) {
2661 // TODO Task #2716479: Fix this so that the warnings raised match
2663 auto scratchKey
= initScratchKey(key
);
2665 if (LIKELY(scratchKey
.m_type
== KindOfInt64
)) {
2666 x
= scratchKey
.m_data
.num
;
2669 cellDup(scratchKey
, tv
);
2670 bool badKey
= false;
2671 if (isStringType(tv
.m_type
)) {
2672 const char* str
= tv
.m_data
.pstr
->data();
2673 size_t len
= tv
.m_data
.pstr
->size();
2675 (*str
== ' ' || *str
== '\t' || *str
== '\r' || *str
== '\n')) {
2680 badKey
= !is_strictly_integer(str
, len
, n
);
2681 } else if (isArrayLikeType(tv
.m_type
) || tv
.m_type
== KindOfObject
||
2682 tv
.m_type
== KindOfResource
) {
2685 // Even if badKey == true, we still perform the cast so that we
2686 // raise the appropriate warnings.
2687 tvCastToInt64InPlace(&tv
);
2693 if (x
< 0 || x
>= base
->m_data
.pstr
->size()) {
2700 auto str
= base
->m_data
.pstr
->getChar(x
);
2701 assert(str
->isStatic());
2702 return !str
->toBoolean();
2706 * IssetEmptyElem when base is an Array
2708 template <bool useEmpty
, KeyType keyType
, bool intishWarn
>
2709 bool IssetEmptyElemArray(ArrayData
* a
, key_type
<keyType
> key
) {
2710 assertx(a
->isPHPArray());
2711 auto const result
= ElemArray
<MOpMode::None
, keyType
, intishWarn
>(a
, key
);
2713 return !cellToBool(tvToCell(result
.tv()));
2715 return !cellIsNull(tvToCell(result
.tv()));
2719 * IssetEmptyElem when base is a Vec
2721 template <bool useEmpty
, KeyType keyType
>
2722 bool IssetEmptyElemVec(ArrayData
* a
, key_type
<keyType
> key
) {
2723 assertx(a
->isVecArray());
2724 auto const result
= ElemVec
<MOpMode::None
, keyType
>(a
, key
);
2726 return !cellToBool(tvAssertCell(result
.tv()));
2728 return !cellIsNull(tvAssertCell(result
.tv()));
2732 * IssetEmptyElem when base is a Dict
2734 template <bool useEmpty
, KeyType keyType
>
2735 bool IssetEmptyElemDict(ArrayData
* a
, key_type
<keyType
> key
) {
2736 assertx(a
->isDict());
2737 auto const result
= ElemDict
<MOpMode::None
, keyType
>(a
, key
);
2739 return !cellToBool(tvAssertCell(result
.tv()));
2741 return !cellIsNull(tvAssertCell(result
.tv()));
2745 * IssetEmptyElem when base is a Keyset
2747 template <bool useEmpty
, KeyType keyType
>
2748 bool IssetEmptyElemKeyset(ArrayData
* a
, key_type
<keyType
> key
) {
2749 assertx(a
->isKeyset());
2750 auto const result
= ElemKeyset
<MOpMode::None
, keyType
>(a
, key
);
2752 return !cellToBool(tvAssertCell(result
.tv()));
2754 return !cellIsNull(tvAssertCell(result
.tv()));
2758 * isset/empty($base[$key])
2760 template <bool useEmpty
, KeyType keyType
, bool intishWarn
>
2761 NEVER_INLINE
bool IssetEmptyElemSlow(TypedValue
* base
, key_type
<keyType
> key
) {
2762 base
= tvToCell(base
);
2763 assertx(cellIsPlausible(*base
));
2765 switch (base
->m_type
) {
2771 case KindOfResource
:
2774 case KindOfPersistentString
:
2776 return IssetEmptyElemString
<useEmpty
, keyType
>(base
, key
);
2778 case KindOfPersistentVec
:
2780 return IssetEmptyElemVec
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2782 case KindOfPersistentDict
:
2784 return IssetEmptyElemDict
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2786 case KindOfPersistentKeyset
:
2788 return IssetEmptyElemKeyset
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2790 case KindOfPersistentArray
:
2792 return IssetEmptyElemArray
<useEmpty
, keyType
, intishWarn
>(
2793 base
->m_data
.parr
, key
2797 return IssetEmptyElemObj
<useEmpty
, keyType
>(base
->m_data
.pobj
, key
);
2802 unknownBaseType(base
);
2806 * Fast path for IssetEmptyElem assuming base is an Array
2808 template <bool useEmpty
, bool intishWarn
, KeyType keyType
= KeyType::Any
>
2809 bool IssetEmptyElem(TypedValue
* base
, key_type
<keyType
> key
) {
2810 assertx(tvIsPlausible(*base
));
2812 if (LIKELY(tvIsArray(base
))) {
2813 return IssetEmptyElemArray
<useEmpty
, keyType
, intishWarn
>(
2814 base
->m_data
.parr
, key
2817 if (LIKELY(tvIsVecArray(base
))) {
2818 return IssetEmptyElemVec
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2820 if (LIKELY(tvIsDict(base
))) {
2821 return IssetEmptyElemDict
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2823 if (LIKELY(tvIsKeyset(base
))) {
2824 return IssetEmptyElemKeyset
<useEmpty
, keyType
>(base
->m_data
.parr
, key
);
2826 return IssetEmptyElemSlow
<useEmpty
, keyType
, intishWarn
>(base
, key
);
2829 template<MOpMode mode
>
2830 inline TypedValue
* propPreNull(TypedValue
& tvRef
) {
2832 if (mode
== MOpMode::Warn
) {
2833 raise_notice("Cannot access property on non-object");
2839 inline void promoteToStdClass(TypedValue
* base
, bool warn
, F fun
) {
2840 if (!RuntimeOption::EvalPromoteEmptyObject
) {
2841 // note that the whole point here is to guarantee that the property
2842 // never auto updates to a stdclass - so we must do this before
2843 // calling promote, and we don't want the try catch below around
2845 if (RuntimeOption::PHP7_EngineExceptions
) {
2846 SystemLib::throwErrorObject(Strings::SET_PROP_NON_OBJECT
);
2848 SystemLib::throwExceptionObject(Strings::SET_PROP_NON_OBJECT
);
2853 Object obj
{ ObjectData::newInstance(SystemLib::s_stdclassClass
) };
2854 if (base
->m_type
== KindOfString
) {
2855 decRefStr(base
->m_data
.pstr
);
2857 assert(!isRefcountedType(base
->m_type
));
2859 base
->m_type
= KindOfObject
;
2860 base
->m_data
.pobj
= obj
.get();
2863 // Behavior here is observable.
2864 // In PHP 5.6, raise_warning is called before updating base, so
2865 // the error_handler sees the original base; but if an exception
2866 // is thrown from the error handler, any catch block will see the
2868 // In PHP 7+, raise_warning is called after updating base, but before
2869 // doing the work of fun, and again, if an exception is thrown, fun
2870 // still gets called before reaching the catch block.
2871 // We'll match PHP7, because we have no way of ensuring that base survives
2872 // across a call to the error_handler: eg $a[0][0][0]->foo = 0; if $a
2873 // started out null, and the error handler resets it to null, base is
2875 // Note that this means that the error handler can overwrite the object
2876 // so there is no guarantee that we have an object on return from
2877 // promoteToStdClass.
2879 raise_warning(Strings::CREATING_DEFAULT_OBJECT
);
2880 } catch (const Object
&) {
2889 template<MOpMode mode
>
2890 TypedValue
* propPreStdclass(TypedValue
& tvRef
, TypedValue
* base
) {
2891 if (mode
!= MOpMode::Define
) {
2892 return propPreNull
<mode
>(tvRef
);
2895 promoteToStdClass(base
, RuntimeOption::EnableHipHopSyntax
,
2896 [] (ObjectData
*) {});
2897 if (UNLIKELY(base
->m_type
!= KindOfObject
)) {
2898 // See the comments above. Although promoteToStdClass will have
2899 // either thrown an exception, or promoted base to an object, an
2900 // installed error handler might have caused it to be overwritten
2908 template<MOpMode mode
>
2909 TypedValue
* propPre(TypedValue
& tvRef
, TypedValue
* base
) {
2910 base
= tvToCell(base
);
2911 switch (base
->m_type
) {
2914 return propPreStdclass
<mode
>(tvRef
, base
);
2917 if (base
->m_data
.num
) {
2918 return propPreNull
<mode
>(tvRef
);
2920 return propPreStdclass
<mode
>(tvRef
, base
);
2924 case KindOfResource
:
2925 return propPreNull
<mode
>(tvRef
);
2927 case KindOfPersistentString
:
2929 if (base
->m_data
.pstr
->size() != 0) {
2930 return propPreNull
<mode
>(tvRef
);
2932 return propPreStdclass
<mode
>(tvRef
, base
);
2934 case KindOfPersistentVec
:
2936 case KindOfPersistentDict
:
2938 case KindOfPersistentKeyset
:
2940 case KindOfPersistentArray
:
2942 return propPreNull
<mode
>(tvRef
);
2950 unknownBaseType(base
);
2953 inline TypedValue
* nullSafeProp(TypedValue
& tvRef
,
2957 base
= tvToCell(base
);
2958 switch (base
->m_type
) {
2966 case KindOfResource
:
2967 case KindOfPersistentString
:
2969 case KindOfPersistentVec
:
2971 case KindOfPersistentDict
:
2973 case KindOfPersistentKeyset
:
2975 case KindOfPersistentArray
:
2978 raise_notice("Cannot access property on non-object");
2981 return base
->m_data
.pobj
->prop(&tvRef
, ctx
, key
);
2983 always_assert(false);
2989 * Generic property access (PropX and PropDX end up here).
2991 * Returns a pointer to a number of possible places, but does not unbox it.
2992 * (The returned pointer is never pointing into a RefData.)
2994 template<MOpMode mode
, KeyType keyType
= KeyType::Any
, bool reffy
= false>
2995 inline TypedValue
* PropObj(TypedValue
& tvRef
, const Class
* ctx
,
2996 ObjectData
* instance
, key_type
<keyType
> key
) {
2997 auto keySD
= prepareKey(key
);
2998 SCOPE_EXIT
{ releaseKey
<keyType
>(keySD
); };
3001 if (mode
== MOpMode::Define
) {
3003 return instance
->propB(&tvRef
, ctx
, keySD
);
3005 return instance
->propD(&tvRef
, ctx
, keySD
);
3009 if (mode
== MOpMode::None
) {
3010 return instance
->prop(&tvRef
, ctx
, keySD
);
3012 if (mode
== MOpMode::Warn
) {
3013 return instance
->propW(&tvRef
, ctx
, keySD
);
3015 assert(mode
== MOpMode::Unset
);
3016 return instance
->propD(&tvRef
, ctx
, keySD
);
3019 template<MOpMode mode
, KeyType keyType
= KeyType::Any
, bool reffy
= false>
3020 inline TypedValue
* Prop(TypedValue
& tvRef
,
3023 key_type
<keyType
> key
) {
3024 auto result
= propPre
<mode
>(tvRef
, base
);
3025 if (result
->m_type
== KindOfNull
) {
3028 assertx(result
->m_type
== KindOfObject
);
3029 auto instance
= instanceFromTv(result
);
3030 return PropObj
<mode
,keyType
,reffy
>(tvRef
, ctx
, instance
, key
);
3033 template <bool useEmpty
, KeyType kt
>
3034 inline bool IssetEmptyPropObj(Class
* ctx
, ObjectData
* instance
,
3036 auto keySD
= prepareKey(key
);
3037 SCOPE_EXIT
{ releaseKey
<kt
>(keySD
); };
3040 instance
->propEmpty(ctx
, keySD
) :
3041 instance
->propIsset(ctx
, keySD
);
3044 template <bool useEmpty
, KeyType kt
= KeyType::Any
>
3045 bool IssetEmptyProp(Class
* ctx
, TypedValue
* base
, key_type
<kt
> key
) {
3046 base
= tvToCell(base
);
3047 if (LIKELY(base
->m_type
== KindOfObject
)) {
3048 return IssetEmptyPropObj
<useEmpty
, kt
>(ctx
, instanceFromTv(base
), key
);
3053 template <bool setResult
>
3054 inline void SetPropNull(Cell
* val
) {
3055 raise_warning("Cannot access property on non-object");
3060 throw InvalidSetMException(make_tv
<KindOfNull
>());
3064 inline void SetPropStdclass(TypedValue
* base
, TypedValue key
, Cell
* val
) {
3068 [&] (ObjectData
* obj
) {
3069 auto const keySD
= prepareKey(key
);
3070 SCOPE_EXIT
{ decRefStr(keySD
); };
3071 obj
->setProp(nullptr, keySD
, *val
);
3075 template <KeyType keyType
>
3076 inline void SetPropObj(Class
* ctx
, ObjectData
* instance
,
3077 key_type
<keyType
> key
, Cell
* val
) {
3078 StringData
* keySD
= prepareKey(key
);
3079 SCOPE_EXIT
{ releaseKey
<keyType
>(keySD
); };
3082 instance
->setProp(ctx
, keySD
, *val
);
3085 // $base->$key = $val
3086 template <bool setResult
, KeyType keyType
= KeyType::Any
>
3087 inline void SetProp(Class
* ctx
, TypedValue
* base
, key_type
<keyType
> key
,
3089 base
= tvToCell(base
);
3090 switch (base
->m_type
) {
3093 return SetPropStdclass(base
, initScratchKey(key
), val
);
3096 if (base
->m_data
.num
) {
3097 return SetPropNull
<setResult
>(val
);
3099 return SetPropStdclass(base
, initScratchKey(key
), val
);
3103 case KindOfPersistentVec
:
3105 case KindOfPersistentDict
:
3107 case KindOfPersistentKeyset
:
3109 case KindOfPersistentArray
:
3111 case KindOfResource
:
3112 return SetPropNull
<setResult
>(val
);
3114 case KindOfPersistentString
:
3116 if (base
->m_data
.pstr
->size() != 0) {
3117 return SetPropNull
<setResult
>(val
);
3119 return SetPropStdclass(base
, initScratchKey(key
), val
);
3122 return SetPropObj
<keyType
>(ctx
, base
->m_data
.pobj
, key
, val
);
3127 unknownBaseType(base
);
3130 inline TypedValue
* SetOpPropNull(TypedValue
& tvRef
) {
3131 raise_warning("Attempt to assign property of non-object");
3136 inline TypedValue
* SetOpPropStdclass(TypedValue
& tvRef
, SetOpOp op
,
3137 TypedValue
* base
, TypedValue key
,
3142 [&] (ObjectData
* obj
) {
3143 StringData
* keySD
= prepareKey(key
);
3144 SCOPE_EXIT
{ decRefStr(keySD
); };
3146 setopBody(tvAssertCell(&tvRef
), op
, rhs
);
3147 obj
->setProp(nullptr, keySD
, tvAssertCell(tvRef
));
3153 inline TypedValue
* SetOpPropObj(TypedValue
& tvRef
, Class
* ctx
,
3154 SetOpOp op
, ObjectData
* instance
,
3155 TypedValue key
, Cell
* rhs
) {
3156 StringData
* keySD
= prepareKey(key
);
3157 SCOPE_EXIT
{ decRefStr(keySD
); };
3158 TypedValue
* result
= instance
->setOpProp(tvRef
, ctx
, op
, keySD
, rhs
);
3162 // $base->$key <op>= $rhs
3163 inline TypedValue
* SetOpProp(TypedValue
& tvRef
,
3164 Class
* ctx
, SetOpOp op
,
3165 TypedValue
* base
, TypedValue key
,
3167 base
= tvToCell(base
);
3168 switch (base
->m_type
) {
3171 return SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
3174 if (base
->m_data
.num
) {
3175 return SetOpPropNull(tvRef
);
3177 return SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
3181 case KindOfPersistentVec
:
3183 case KindOfPersistentDict
:
3185 case KindOfPersistentKeyset
:
3187 case KindOfPersistentArray
:
3189 case KindOfResource
:
3190 return SetOpPropNull(tvRef
);
3192 case KindOfPersistentString
:
3194 if (base
->m_data
.pstr
->size() != 0) {
3195 return SetOpPropNull(tvRef
);
3197 return SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
3200 return SetOpPropObj(tvRef
, ctx
, op
, instanceFromTv(base
), key
, rhs
);
3205 unknownBaseType(base
);
3208 inline Cell
IncDecPropNull() {
3209 raise_warning("Attempt to increment/decrement property of non-object");
3210 return make_tv
<KindOfNull
>();
3213 inline Cell
IncDecPropStdclass(IncDecOp op
, TypedValue
* base
,
3219 [&] (ObjectData
* obj
) {
3220 StringData
* keySD
= prepareKey(key
);
3221 SCOPE_EXIT
{ decRefStr(keySD
); };
3224 dest
= IncDecBody(op
, &tv
);
3225 obj
->setProp(nullptr, keySD
, dest
);
3226 assert(!isRefcountedType(tv
.m_type
));
3232 inline Cell
IncDecPropObj(Class
* ctx
,
3236 auto keySD
= prepareKey(key
);
3237 SCOPE_EXIT
{ decRefStr(keySD
); };
3238 return base
->incDecProp(ctx
, op
, keySD
);
3241 inline Cell
IncDecProp(
3247 base
= tvToCell(base
);
3248 switch (base
->m_type
) {
3251 return IncDecPropStdclass(op
, base
, key
);
3254 if (base
->m_data
.num
) {
3255 return IncDecPropNull();
3257 return IncDecPropStdclass(op
, base
, key
);
3261 case KindOfPersistentVec
:
3263 case KindOfPersistentDict
:
3265 case KindOfPersistentKeyset
:
3267 case KindOfPersistentArray
:
3269 case KindOfResource
:
3270 return IncDecPropNull();
3272 case KindOfPersistentString
:
3274 if (base
->m_data
.pstr
->size() != 0) {
3275 return IncDecPropNull();
3277 return IncDecPropStdclass(op
, base
, key
);
3280 return IncDecPropObj(ctx
, op
, instanceFromTv(base
), key
);
3285 unknownBaseType(base
);
3288 inline void UnsetPropObj(Class
* ctx
, ObjectData
* instance
, TypedValue key
) {
3290 auto keySD
= prepareKey(key
);
3291 SCOPE_EXIT
{ decRefStr(keySD
); };
3293 instance
->unsetProp(ctx
, keySD
);
3296 inline void UnsetProp(Class
* ctx
, TypedValue
* base
, TypedValue key
) {
3298 base
= tvToCell(base
);
3299 if (LIKELY(base
->m_type
== KindOfObject
)) {
3300 UnsetPropObj(ctx
, instanceFromTv(base
), key
);
3304 ///////////////////////////////////////////////////////////////////////////////
3306 #endif // incl_HPHP_VM_MEMBER_OPERATIONS_H_