2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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_VM_MEMBER_OPERATIONS_H_
18 #define incl_VM_MEMBER_OPERATIONS_H_
20 #include "runtime/base/types.h"
21 #include "runtime/base/strings.h"
22 #include "runtime/base/tv_macros.h"
23 #include "system/lib/systemlib.h"
24 #include "runtime/base/builtin_functions.h"
25 #include "runtime/vm/core_types.h"
26 #include "runtime/vm/runtime.h"
27 #include "runtime/ext/ext_collection.h"
32 // When MoreWarnings is set to true, the VM will raise more warnings
33 // on SetOpM, IncDecM and CGetG, intended to match Zend.
34 const bool MoreWarnings
=
35 #ifdef HHVM_MORE_WARNINGS
49 struct KeyTypeTraits
{};
50 template<> struct KeyTypeTraits
<AnyKey
> {
51 typedef CVarRef valType
;
52 typedef int64 rawType
; // This is never actually used but it's
53 // needed to keep the compiler happy
55 template<> struct KeyTypeTraits
<StrKey
> {
56 typedef StrNR valType
;
57 typedef StringData
* rawType
;
59 template<> struct KeyTypeTraits
<IntKey
> {
60 typedef int64 valType
;
61 typedef int64 rawType
;
64 static inline DataType
keyDataType(KeyType t
) {
67 } else if (t
== IntKey
) {
75 inline typename KeyTypeTraits
<kt
>::valType
keyAsValue(TypedValue
* key
);
77 inline int64 keyAsValue
<IntKey
>(TypedValue
* key
) {
78 return reinterpret_cast<int64
>(key
);
81 inline CVarRef keyAsValue
<AnyKey
>(TypedValue
* key
) {
82 return tvAsCVarRef(key
);
85 inline StrNR keyAsValue
<StrKey
>(TypedValue
* key
) {
86 return StrNR(reinterpret_cast<StringData
*>(key
));
90 inline typename KeyTypeTraits
<kt
>::rawType
keyAsRaw(TypedValue
* key
) {
94 return reinterpret_cast<typename KeyTypeTraits
<kt
>::rawType
>(key
);
97 // This is used for helpers that are not type specialized for the key
98 // and therefore need the key in a TypedValue
100 inline void initScratchKey(TypedValue
& tv
, TypedValue
*& key
) {
102 tv
.m_type
= keyDataType(kt
);
103 tv
.m_data
.num
= reinterpret_cast<int64
>(key
);
108 void objArrayAccess(Instance
* base
);
109 TypedValue
* objOffsetGet(TypedValue
& tvRef
, Instance
* base
,
110 CVarRef offset
, bool validate
=true);
111 bool objOffsetIsset(TypedValue
& tvRef
, Instance
* base
, CVarRef offset
,
113 bool objOffsetEmpty(TypedValue
& tvRef
, Instance
* base
, CVarRef offset
,
115 void objOffsetSet(Instance
* base
, CVarRef offset
, TypedValue
* val
,
117 void objOffsetAppend(Instance
* base
, TypedValue
* val
, bool validate
=true);
118 void objOffsetUnset(Instance
* base
, CVarRef offset
);
120 StringData
* prepareAnyKey(TypedValue
* tv
);
122 template <KeyType keyType
= AnyKey
>
123 static inline StringData
* prepareKey(TypedValue
* tv
) {
124 if (keyType
== StrKey
) {
125 return reinterpret_cast<StringData
*>(tv
);
126 } else if (keyType
== AnyKey
) {
127 return prepareAnyKey(tv
);
133 template <KeyType keyType
>
134 static inline void releaseKey(StringData
* keySD
) {
135 if (keyType
== AnyKey
) {
136 LITSTR_DECREF(keySD
);
138 ASSERT(keyType
== StrKey
);
142 static inline void opPre(TypedValue
*& base
, DataType
& type
) {
143 // Get inner variant if necessary.
145 if (type
== KindOfRef
) {
146 base
= base
->m_data
.pref
->tv();
151 static inline TypedValue
* ElemArrayRawKey(ArrayData
* base
,
154 if (LIKELY(IsHphpArray(base
))) {
155 result
= static_cast<HphpArray
*>(base
)->nvGet(key
);
156 if (result
== NULL
) {
157 result
= (TypedValue
*)&null_variant
;
160 result
= (TypedValue
*)&base
->get(key
);
165 static inline TypedValue
* ElemArrayRawKey(ArrayData
* base
,
168 if (LIKELY(IsHphpArray(base
))) {
170 if (!key
->isStrictlyInteger(n
)) {
171 result
= static_cast<HphpArray
*>(base
)->nvGet(key
);
173 result
= static_cast<HphpArray
*>(base
)->nvGet(n
);
175 if (result
== NULL
) {
176 result
= (TypedValue
*)&null_variant
;
179 result
= (TypedValue
*)&ArrNR(base
).asArray().rvalAtRef(StrNR(key
));
184 template <bool warn
, KeyType keyType
>
185 static inline TypedValue
* ElemArray(ArrayData
* base
,
188 if (keyType
== AnyKey
) {
189 DataType rtt
= key
->m_type
;
190 if (rtt
== KindOfInt64
) {
191 result
= ElemArrayRawKey(base
, key
->m_data
.num
);
192 } else if (IS_STRING_TYPE(rtt
)) {
193 result
= ElemArrayRawKey(base
, key
->m_data
.pstr
);
195 result
= (TypedValue
*)&ArrNR(base
).asArray()
196 .rvalAtRef(tvCellAsCVarRef(key
));
199 result
= ElemArrayRawKey(base
, keyAsRaw
<keyType
>(key
));
202 if (UNLIKELY(result
->m_type
== KindOfUninit
)) {
203 result
= (TypedValue
*)&init_null_variant
;
206 initScratchKey
<keyType
>(scratch
, key
);
207 raise_notice(Strings::UNDEFINED_INDEX
,
208 tvAsCVarRef(key
).toString().data());
214 // $result = $base[$key];
215 template <bool warn
, KeyType keyType
= AnyKey
>
216 static inline TypedValue
* Elem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
217 TypedValue
* base
, bool& baseStrOff
,
228 result
= (TypedValue
*)&init_null_variant
;
231 case KindOfStaticString
:
234 raise_error("Cannot use string offset as an array");
237 if (keyType
== IntKey
) {
238 x
= reinterpret_cast<int64
>(key
);
239 } else if (keyType
== StrKey
) {
240 x
= reinterpret_cast<StringData
*>(key
)->toInt64(10);
242 x
= IS_INT_TYPE(key
->m_type
) ? key
->m_data
.num
243 : int64(tvCellAsCVarRef(key
));
245 if (x
< 0 || x
>= base
->m_data
.pstr
->size()) {
247 raise_warning("Out of bounds");
249 static StringData
* sd
= StringData::GetStaticString("");
250 tvScratch
.m_data
.pstr
= sd
;
251 tvScratch
._count
= 0;
252 tvScratch
.m_type
= KindOfString
;
254 TV_WRITE_UNINIT(&tvScratch
);
255 tvAsVariant(&tvScratch
) = base
->m_data
.pstr
->getChar(x
);
262 result
= ElemArray
<warn
, keyType
>(base
->m_data
.parr
, key
);
267 initScratchKey
<keyType
>(scratch
, key
);
268 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
269 result
= collectionGet(base
->m_data
.pobj
, key
);
271 result
= objOffsetGet(tvRef
, instanceFromTv(base
), tvCellAsCVarRef(key
));
283 template <bool warn
, KeyType keyType
>
284 static inline TypedValue
* ElemDArray(TypedValue
* base
, TypedValue
* key
) {
286 bool defined
= !warn
||
287 tvAsCVarRef(base
).asCArrRef().exists(keyAsValue
<keyType
>(key
));
289 if (keyType
== AnyKey
&& key
->m_type
== KindOfInt64
) {
290 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef()
291 .lvalAt(key
->m_data
.num
);
293 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef()
294 .lvalAt(keyAsValue
<keyType
>(key
));
300 initScratchKey
<keyType
>(scratch
, key
);
301 raise_notice(Strings::UNDEFINED_INDEX
,
302 tvAsCVarRef(key
).toString().data());
313 template <bool warn
, bool reffy
, KeyType keyType
= AnyKey
>
314 static inline TypedValue
* ElemD(TypedValue
& tvScratch
, TypedValue
& tvRef
,
315 TypedValue
* base
, TypedValue
* key
) {
323 initScratchKey
<keyType
>(scratch
, key
);
324 Array a
= Array::Create();
325 result
= (TypedValue
*)&a
.lvalAt(tvCellAsCVarRef(key
));
327 raise_notice(Strings::UNDEFINED_INDEX
,
328 tvAsCVarRef(key
).toString().data());
330 tvAsVariant(base
) = a
;
333 case KindOfBoolean
: {
334 if (base
->m_data
.num
) {
335 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
336 tvWriteUninit(&tvScratch
);
339 initScratchKey
<keyType
>(scratch
, key
);
340 Array a
= Array::Create();
341 result
= (TypedValue
*)&a
.lvalAt(tvCellAsCVarRef(key
));
343 raise_notice(Strings::UNDEFINED_INDEX
,
344 tvAsCVarRef(key
).toString().data());
346 tvAsVariant(base
) = a
;
352 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
353 tvWriteUninit(&tvScratch
);
357 case KindOfStaticString
:
359 if (base
->m_data
.pstr
->size() == 0) {
360 initScratchKey
<keyType
>(scratch
, key
);
361 Array a
= Array::Create();
362 result
= (TypedValue
*)&a
.lvalAt(tvCellAsCVarRef(key
));
364 raise_notice(Strings::UNDEFINED_INDEX
,
365 tvAsCVarRef(key
).toString().data());
367 tvAsVariant(base
) = a
;
369 raise_error("Operator not supported for strings");
370 result
= NULL
; // Silence compiler warning.
375 result
= ElemDArray
<warn
, keyType
>(base
, key
);
379 initScratchKey
<keyType
>(scratch
, key
);
380 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
382 raise_error("Collection elements cannot be taken by reference");
385 result
= collectionGet(base
->m_data
.pobj
, key
);
388 result
= objOffsetGet(tvRef
, instanceFromTv(base
), tvCellAsCVarRef(key
));
394 result
= NULL
; // Silence compiler warning.
404 template <KeyType keyType
= AnyKey
>
405 static inline TypedValue
* ElemU(TypedValue
& tvScratch
, TypedValue
& tvRef
,
406 TypedValue
* base
, TypedValue
* key
) {
407 TypedValue
* result
= NULL
;
416 tvWriteUninit(&tvScratch
);
420 case KindOfStaticString
:
422 raise_error("Operator not supported for strings");
427 tvAsCVarRef(base
).asCArrRef().exists(keyAsValue
<keyType
>(key
));
429 if (keyType
== AnyKey
&& key
->m_type
== KindOfInt64
) {
430 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef()
431 .lvalAt(key
->m_data
.num
);
433 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef().lvalAt(
434 keyAsValue
<keyType
>(key
));
437 tvWriteUninit(&tvScratch
);
444 initScratchKey
<keyType
>(scratch
, key
);
445 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
446 result
= collectionGet(base
->m_data
.pobj
, key
);
448 result
= objOffsetGet(tvRef
, instanceFromTv(base
), tvCellAsCVarRef(key
));
460 // $result = ($base[] = ...);
461 static inline TypedValue
* NewElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
469 Array a
= Array::Create();
470 result
= (TypedValue
*)&a
.lvalAt();
471 tvAsVariant(base
) = a
;
474 case KindOfBoolean
: {
475 if (base
->m_data
.num
) {
476 raise_warning("Invalid NewElem operand");
477 tvWriteUninit(&tvScratch
);
480 Array a
= Array::Create();
481 result
= (TypedValue
*)&a
.lvalAt();
482 tvAsVariant(base
) = a
;
486 case KindOfStaticString
:
488 if (base
->m_data
.pstr
->size() == 0) {
489 Array a
= Array::Create();
490 result
= (TypedValue
*)&a
.lvalAt();
491 tvAsVariant(base
) = a
;
493 raise_warning("Invalid NewElem operand");
494 tvWriteUninit(&tvScratch
);
500 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef().lvalAt();
504 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
505 raise_error("Cannot use [] for reading");
508 result
= objOffsetGet(tvRef
, instanceFromTv(base
), init_null_variant
);
513 raise_warning("Invalid NewElem operand");
514 tvWriteUninit(&tvScratch
);
522 static inline void SetElemEmptyish(TypedValue
* base
, TypedValue
* key
,
524 Array a
= Array::Create();
525 a
.set(tvAsCVarRef(key
), tvAsCVarRef(value
));
526 tvAsVariant(base
) = a
;
528 template <bool setResult
>
529 static inline void SetElemNumberish(Cell
* value
) {
530 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
532 tvRefcountedDecRefCell((TypedValue
*)value
);
533 tvWriteNull((TypedValue
*)value
);
537 static inline ArrayData
* SetElemArrayRawKey(ArrayData
* a
,
541 return a
->set(key
, tvCellAsCVarRef(value
), copy
);
544 static inline ArrayData
* SetElemArrayRawKey(ArrayData
* a
,
549 if (key
->isStrictlyInteger(n
)) {
550 return a
->set(n
, tvCellAsCVarRef(value
), copy
);
552 return a
->set(StrNR(key
), tvCellAsCVarRef(value
), copy
);
556 template <bool setResult
, KeyType keyType
>
557 static inline void SetElemArray(TypedValue
* base
, TypedValue
* key
,
559 ArrayData
* a
= base
->m_data
.parr
;
560 ArrayData
* newData
= NULL
;
561 bool copy
= (a
->getCount() > 1)
562 || (value
->m_type
== KindOfArray
&& value
->m_data
.parr
== a
);
564 if (keyType
!= AnyKey
) {
565 newData
= SetElemArrayRawKey(a
, keyAsRaw
<keyType
>(key
), value
, copy
);
566 } else if (key
->m_type
<= KindOfNull
) {
567 newData
= a
->set(empty_string
, tvCellAsCVarRef(value
), copy
);
568 } else if (IS_STRING_TYPE(key
->m_type
)) {
569 newData
= SetElemArrayRawKey(a
, key
->m_data
.pstr
, value
, copy
);
570 } else if (key
->m_type
!= KindOfArray
&& key
->m_type
!= KindOfObject
) {
571 newData
= SetElemArrayRawKey(a
, tvAsCVarRef(key
).toInt64(), value
, copy
);
573 raise_warning("Illegal offset type");
574 // Assignment failed, so the result is null rather than the RHS.
575 // XXX This does not match bytecode.specification, but it does
576 // roughly match Zend behavior.
578 if (IS_REFCOUNTED_TYPE(value
->m_type
)) {
585 if (newData
!= NULL
&& newData
!= a
) {
586 newData
->incRefCount();
587 if (a
->decRefCount() == 0) {
590 base
->m_data
.parr
= newData
;
593 // SetElem() leaves the result in 'value', rather than returning it as in
594 // SetOpElem(), because doing so avoids a dup operation that SetOpElem() can't
596 template <bool setResult
, KeyType keyType
= AnyKey
>
597 static inline void SetElem(TypedValue
* base
, TypedValue
* key
, Cell
* value
) {
604 initScratchKey
<keyType
>(scratch
, key
);
605 SetElemEmptyish(base
, key
, value
);
608 case KindOfBoolean
: {
609 if (base
->m_data
.num
) {
610 SetElemNumberish
<setResult
>(value
);
612 initScratchKey
<keyType
>(scratch
, key
);
613 SetElemEmptyish(base
, key
, value
);
619 SetElemNumberish
<setResult
>(value
);
622 case KindOfStaticString
:
624 initScratchKey
<keyType
>(scratch
, key
);
625 int baseLen
= base
->m_data
.pstr
->size();
627 SetElemEmptyish(base
, key
, value
);
629 // Convert key to string offset.
634 tvCastToInt64InPlace(&tv
);
637 if (x
< 0 || x
>= StringData::MaxSize
) {
638 raise_warning("Illegal string offset: %lld", x
);
641 // Compute how long the resulting string will be. Type needs
649 // Extract the first character of (string)value.
654 tvCastToStringInPlace(&tv
);
655 if (tv
.m_data
.pstr
->size() > 0) {
656 y
[0] = tv
.m_data
.pstr
->data()[0];
661 tvRefcountedDecRef(&tv
);
663 // Create and save the result.
664 if (x
>= 0 && x
< baseLen
&& base
->m_data
.pstr
->getCount() <= 1) {
665 // Modify base in place. This is safe because the LHS owns the only
667 base
->m_data
.pstr
->setChar(x
, y
[0]);
669 StringData
* sd
= NEW(StringData
)(slen
);
670 char* s
= sd
->mutableSlice().ptr
;
671 memcpy(s
, base
->m_data
.pstr
->data(), baseLen
);
673 memset(&s
[baseLen
], ' ', slen
- baseLen
- 1);
678 if (base
->m_data
.pstr
->decRefCount() == 0) base
->m_data
.pstr
->release();
679 base
->m_data
.pstr
= sd
;
680 base
->m_type
= KindOfString
;
683 // Push y onto the stack.
684 tvRefcountedDecRef(value
);
685 StringData
* sd
= NEW(StringData
)(y
, strlen(y
), CopyString
);
687 value
->m_data
.pstr
= sd
;
689 value
->m_type
= KindOfString
;
695 SetElemArray
<setResult
, keyType
>(base
, key
, value
);
699 initScratchKey
<keyType
>(scratch
, key
);
700 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
701 collectionSet(base
->m_data
.pobj
, key
, (TypedValue
*)value
);
703 objOffsetSet(instanceFromTv(base
), tvAsCVarRef(key
), (TypedValue
*)value
);
707 default: not_reached();
711 static inline void SetNewElemEmptyish(TypedValue
* base
, Cell
* value
) {
712 Array a
= Array::Create();
713 a
.append(tvCellAsCVarRef(value
));
714 tvAsVariant(base
) = a
;
716 template <bool setResult
>
717 static inline void SetNewElemNumberish(Cell
* value
) {
718 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
720 tvRefcountedDecRefCell((TypedValue
*)value
);
721 tvWriteNull((TypedValue
*)value
);
724 template <bool setResult
>
725 static inline void SetNewElem(TypedValue
* base
, Cell
* value
) {
731 SetNewElemEmptyish(base
, value
);
734 case KindOfBoolean
: {
735 if (base
->m_data
.num
) {
736 SetNewElemNumberish
<setResult
>(value
);
738 SetNewElemEmptyish(base
, value
);
744 SetNewElemNumberish
<setResult
>(value
);
747 case KindOfStaticString
:
749 int baseLen
= base
->m_data
.pstr
->size();
751 SetNewElemEmptyish(base
, value
);
753 raise_error("[] operator not supported for strings");
758 ArrayData
* a
= base
->m_data
.parr
;
759 bool copy
= (a
->getCount() > 1)
760 || (value
->m_type
== KindOfArray
&& value
->m_data
.parr
== a
);
761 a
= a
->append(tvCellAsCVarRef(value
), copy
);
764 base
->m_data
.parr
->decRefCount();
765 base
->m_data
.parr
= a
;
770 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
771 collectionAppend(base
->m_data
.pobj
, (TypedValue
*)value
);
773 objOffsetAppend(instanceFromTv(base
), (TypedValue
*)value
);
777 default: ASSERT(false);
781 static inline TypedValue
* SetOpElemEmptyish(unsigned char op
, TypedValue
* base
,
782 TypedValue
* key
, Cell
* rhs
) {
783 Array a
= Array::Create();
784 TypedValue
* result
= (TypedValue
*)&a
.lvalAt(tvAsCVarRef(key
));
785 tvAsVariant(base
) = a
;
787 raise_notice(Strings::UNDEFINED_INDEX
,
788 tvAsCVarRef(key
).toString().data());
790 SETOP_BODY(result
, op
, rhs
);
793 static inline TypedValue
* SetOpElemNumberish(TypedValue
& tvScratch
) {
794 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
795 tvWriteNull(&tvScratch
);
799 template <KeyType keyType
= AnyKey
>
800 static inline TypedValue
* SetOpElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
801 unsigned char op
, TypedValue
* base
,
802 TypedValue
* key
, Cell
* rhs
) {
810 initScratchKey
<keyType
>(scratch
, key
);
811 result
= SetOpElemEmptyish(op
, base
, key
, rhs
);
814 case KindOfBoolean
: {
815 if (base
->m_data
.num
) {
816 result
= SetOpElemNumberish(tvScratch
);
818 initScratchKey
<keyType
>(scratch
, key
);
819 result
= SetOpElemEmptyish(op
, base
, key
, rhs
);
825 result
= SetOpElemNumberish(tvScratch
);
828 case KindOfStaticString
:
830 if (base
->m_data
.pstr
->size() != 0) {
831 raise_error("Invalid SetOpElem operand");
833 initScratchKey
<keyType
>(scratch
, key
);
834 result
= SetOpElemEmptyish(op
, base
, key
, rhs
);
838 result
= ElemDArray
<MoreWarnings
, keyType
>(base
, key
);
839 SETOP_BODY(result
, op
, rhs
);
843 initScratchKey
<keyType
>(scratch
, key
);
844 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
845 result
= collectionGet(base
->m_data
.pobj
, key
);
846 SETOP_BODY(result
, op
, rhs
);
848 result
= objOffsetGet(tvRef
, instanceFromTv(base
), tvCellAsCVarRef(key
));
849 SETOP_BODY(result
, op
, rhs
);
850 objOffsetSet(instanceFromTv(base
), tvAsCVarRef(key
), result
, false);
856 result
= NULL
; // Silence compiler warning.
862 static inline TypedValue
* SetOpNewElemEmptyish(unsigned char op
,
863 TypedValue
* base
, Cell
* rhs
) {
864 Array a
= Array::Create();
865 TypedValue
* result
= (TypedValue
*)&a
.lvalAt();
866 tvAsVariant(base
) = a
;
867 SETOP_BODY(result
, op
, rhs
);
870 static inline TypedValue
* SetOpNewElemNumberish(TypedValue
& tvScratch
) {
871 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
872 tvWriteNull(&tvScratch
);
875 static inline TypedValue
* SetOpNewElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
876 unsigned char op
, TypedValue
* base
,
884 result
= SetOpNewElemEmptyish(op
, base
, rhs
);
887 case KindOfBoolean
: {
888 if (base
->m_data
.num
) {
889 result
= SetOpNewElemNumberish(tvScratch
);
891 result
= SetOpNewElemEmptyish(op
, base
, rhs
);
897 result
= SetOpNewElemNumberish(tvScratch
);
900 case KindOfStaticString
:
902 if (base
->m_data
.pstr
->size() != 0) {
903 raise_error("[] operator not supported for strings");
905 result
= SetOpNewElemEmptyish(op
, base
, rhs
);
909 result
= (TypedValue
*)&tvAsVariant(base
).asArrRef().lvalAt();
910 SETOP_BODY(result
, op
, rhs
);
914 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
915 raise_error("Cannot use [] for reading");
918 result
= objOffsetGet(tvRef
, instanceFromTv(base
), init_null_variant
);
919 SETOP_BODY(result
, op
, rhs
);
920 objOffsetAppend(instanceFromTv(base
), result
, false);
926 result
= NULL
; // Silence compiler warning.
932 template <bool setResult
>
933 static inline void IncDecBody(unsigned char op
, TypedValue
* fr
,
935 if (fr
->m_type
== KindOfInt64
) {
936 switch ((IncDecOp
)op
) {
965 default: ASSERT(false);
969 if (fr
->m_type
== KindOfUninit
) {
970 ActRec
* fp
= g_vmContext
->m_fp
;
971 size_t pind
= ((uintptr_t(fp
) - uintptr_t(fr
)) / sizeof(TypedValue
)) - 1;
972 if (pind
< size_t(fp
->m_func
->numNamedLocals())) {
973 // Only raise a warning if fr points to a local variable
974 raise_notice(Strings::UNDEFINED_VARIABLE
,
975 fp
->m_func
->localVarName(pind
)->data());
977 // Convert uninit null to null so that we don't write out an uninit null
978 // to the eval stack for PostInc and PostDec.
979 fr
->m_type
= KindOfNull
;
981 switch ((IncDecOp
)op
) {
1007 --(tvAsVariant(fr
));
1010 default: ASSERT(false);
1014 template <bool setResult
>
1015 static inline void IncDecElemEmptyish(unsigned char op
, TypedValue
* base
,
1016 TypedValue
* key
, TypedValue
& dest
) {
1017 Array a
= Array::Create();
1018 TypedValue
* result
= (TypedValue
*)&a
.lvalAt(tvAsCVarRef(key
));
1019 tvAsVariant(base
) = a
;
1021 raise_notice(Strings::UNDEFINED_INDEX
,
1022 tvAsCVarRef(key
).toString().data());
1024 IncDecBody
<setResult
>(op
, result
, &dest
);
1026 template <bool setResult
>
1027 static inline void IncDecElemNumberish(TypedValue
& dest
) {
1028 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
1033 template <bool setResult
, KeyType keyType
= AnyKey
>
1034 static inline void IncDecElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1035 unsigned char op
, TypedValue
* base
,
1036 TypedValue
* key
, TypedValue
& dest
) {
1043 initScratchKey
<keyType
>(scratch
, key
);
1044 IncDecElemEmptyish
<setResult
>(op
, base
, key
, dest
);
1047 case KindOfBoolean
: {
1048 if (base
->m_data
.num
) {
1049 IncDecElemNumberish
<setResult
>(dest
);
1051 initScratchKey
<keyType
>(scratch
, key
);
1052 IncDecElemEmptyish
<setResult
>(op
, base
, key
, dest
);
1057 case KindOfDouble
: {
1058 IncDecElemNumberish
<setResult
>(dest
);
1061 case KindOfStaticString
:
1062 case KindOfString
: {
1063 if (base
->m_data
.pstr
->size() != 0) {
1064 raise_error("Invalid IncDecElem operand");
1066 initScratchKey
<keyType
>(scratch
, key
);
1067 IncDecElemEmptyish
<setResult
>(op
, base
, key
, dest
);
1071 TypedValue
* result
= ElemDArray
<MoreWarnings
, keyType
>(base
, key
);
1072 IncDecBody
<setResult
>(op
, result
, &dest
);
1075 case KindOfObject
: {
1077 initScratchKey
<keyType
>(scratch
, key
);
1078 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1079 result
= collectionGet(base
->m_data
.pobj
, key
);
1081 result
= objOffsetGet(tvRef
, instanceFromTv(base
), tvCellAsCVarRef(key
));
1083 IncDecBody
<setResult
>(op
, result
, &dest
);
1086 default: ASSERT(false);
1090 template <bool setResult
>
1091 static inline void IncDecNewElemEmptyish(unsigned char op
, TypedValue
* base
,
1093 Array a
= Array::Create();
1094 TypedValue
* result
= (TypedValue
*)&a
.lvalAt();
1095 tvAsVariant(base
) = a
;
1096 IncDecBody
<setResult
>(op
, result
, &dest
);
1098 template <bool setResult
>
1099 static inline void IncDecNewElemNumberish(TypedValue
& dest
) {
1100 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY
);
1105 template <bool setResult
>
1106 static inline void IncDecNewElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1107 unsigned char op
, TypedValue
* base
,
1114 IncDecNewElemEmptyish
<setResult
>(op
, base
, dest
);
1117 case KindOfBoolean
: {
1118 if (base
->m_data
.num
) {
1119 IncDecNewElemNumberish
<setResult
>(dest
);
1121 IncDecNewElemEmptyish
<setResult
>(op
, base
, dest
);
1126 case KindOfDouble
: {
1127 IncDecNewElemNumberish
<setResult
>(dest
);
1130 case KindOfStaticString
:
1131 case KindOfString
: {
1132 if (base
->m_data
.pstr
->size() != 0) {
1133 raise_error("Invalid IncDecNewElem operand");
1135 IncDecNewElemEmptyish
<setResult
>(op
, base
, dest
);
1139 TypedValue
* result
= (TypedValue
*)&tvAsVariant(base
).asArrRef().lvalAt();
1140 IncDecBody
<setResult
>(op
, result
, &dest
);
1143 case KindOfObject
: {
1145 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1146 raise_error("Cannot use [] for reading");
1149 result
= objOffsetGet(tvRef
, instanceFromTv(base
), init_null_variant
);
1150 IncDecBody
<setResult
>(op
, result
, &dest
);
1154 default: ASSERT(false);
1158 static inline ArrayData
* UnsetElemArrayRawKey(ArrayData
* a
, int64 key
,
1160 return a
->remove(key
, copy
);
1163 static inline ArrayData
* UnsetElemArrayRawKey(ArrayData
* a
, StringData
* key
,
1166 if (!key
->isStrictlyInteger(n
)) {
1167 return a
->remove(StrNR(key
), copy
);
1169 return a
->remove(n
, copy
);
1173 template <KeyType keyType
>
1174 static inline void UnsetElemArray(TypedValue
* base
, TypedValue
* key
) {
1175 ArrayData
* a
= base
->m_data
.parr
;
1176 bool copy
= a
->getCount() > 1;
1177 if (keyType
== AnyKey
) {
1178 if (IS_STRING_TYPE(key
->m_type
)) {
1179 a
= UnsetElemArrayRawKey(a
, key
->m_data
.pstr
, copy
);
1180 } else if (key
->m_type
== KindOfInt64
) {
1181 a
= UnsetElemArrayRawKey(a
, key
->m_data
.num
, copy
);
1183 VarNR varKey
= tvAsCVarRef(key
).toKey();
1184 if (varKey
.isNull()) {
1187 a
= a
->remove(varKey
, copy
);
1190 a
= UnsetElemArrayRawKey(a
, keyAsRaw
<keyType
>(key
), copy
);
1194 base
->m_data
.parr
->decRefCount();
1195 base
->m_data
.parr
= a
;
1199 template <KeyType keyType
= AnyKey
>
1200 static inline void UnsetElem(TypedValue
* base
, TypedValue
* member
) {
1205 case KindOfStaticString
:
1206 case KindOfString
: {
1207 raise_error("Cannot unset string offsets");
1210 UnsetElemArray
<keyType
>(base
, member
);
1213 case KindOfObject
: {
1214 initScratchKey
<keyType
>(scratch
, member
);
1215 if (LIKELY(base
->m_data
.pobj
->isCollection())) {
1216 collectionUnset(base
->m_data
.pobj
, member
);
1218 objOffsetUnset(instanceFromTv(base
), tvAsCVarRef(member
));
1222 default: break; // Do nothing.
1226 template <bool warn
>
1227 static inline DataType
propPreNull(TypedValue
& tvScratch
, TypedValue
*& result
) {
1228 TV_WRITE_NULL(&tvScratch
);
1229 result
= &tvScratch
;
1231 raise_warning("Cannot access property on non-object");
1235 static inline Instance
* createDefaultObject() {
1236 raise_warning(Strings::CREATING_DEFAULT_OBJECT
);
1237 Instance
* obj
= newInstance(SystemLib::s_stdclassClass
);
1240 template <bool warn
, bool define
>
1241 static inline DataType
propPreStdclass(TypedValue
& tvScratch
,
1242 TypedValue
*& result
, TypedValue
* base
) {
1244 return propPreNull
<warn
>(tvScratch
, result
);
1246 // TODO(#1124706): We don't want to do this anymore.
1247 Instance
* obj
= createDefaultObject();
1248 tvRefcountedDecRef(base
);
1249 base
->m_type
= KindOfObject
;
1250 base
->m_data
.pobj
= obj
;
1253 return KindOfObject
;
1256 template <bool warn
, bool define
, bool issetEmpty
>
1257 static inline DataType
propPre(TypedValue
& tvScratch
, TypedValue
*& result
,
1258 TypedValue
*& base
) {
1264 return propPreStdclass
<warn
, define
>(tvScratch
, result
, base
);
1266 case KindOfBoolean
: {
1267 if (base
->m_data
.num
) {
1268 return propPreNull
<warn
>(tvScratch
, result
);
1270 return propPreStdclass
<warn
, define
>(tvScratch
, result
, base
);
1273 case KindOfStaticString
:
1274 case KindOfString
: {
1275 if (base
->m_data
.pstr
->size() != 0) {
1276 return propPreNull
<warn
>(tvScratch
, result
);
1278 return propPreStdclass
<warn
, define
>(tvScratch
, result
, base
);
1282 return issetEmpty
? KindOfArray
: propPreNull
<warn
>(tvScratch
, result
);
1284 case KindOfObject
: {
1285 return KindOfObject
;
1288 return propPreNull
<warn
>(tvScratch
, result
);
1294 // $result = $base->$key;
1297 // $base->$key = ...
1301 template <bool warn
, bool define
, bool unset
, bool baseIsObj
= false,
1302 KeyType keyType
= AnyKey
>
1303 static inline TypedValue
* Prop(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1304 Class
* ctx
, TypedValue
* base
, TypedValue
* key
) {
1305 static_assert(keyType
!= IntKey
, "Integer property keys are not supported");
1306 ASSERT(!warn
|| !unset
);
1307 TypedValue
* result
= NULL
;
1308 StringData
* keySD
= NULL
;
1311 instance
= reinterpret_cast<Instance
*>(base
);
1313 DataType t
= propPre
<warn
, define
, false>(tvScratch
, result
, base
);
1314 if (t
== KindOfNull
) {
1317 ASSERT(t
== KindOfObject
);
1318 instance
= instanceFromTv(base
);
1321 keySD
= prepareKey
<keyType
>(key
);
1323 result
= &tvScratch
;
1324 #define ARGS result, tvRef, ctx, keySD
1325 if (!warn
&& !(define
|| unset
)) instance
->prop (ARGS
);
1326 if (!warn
&& (define
|| unset
)) instance
->propD (ARGS
);
1327 if ( warn
&& !define
) instance
->propW (ARGS
);
1328 if ( warn
&& define
) instance
->propWD(ARGS
);
1330 releaseKey
<keyType
>(keySD
);
1334 template<bool useEmpty
>
1335 static inline bool IssetEmptyElemObj(TypedValue
& tvRef
, Instance
* instance
,
1336 bool baseStrOff
, TypedValue
* key
) {
1338 if (LIKELY(instance
->isCollection())) {
1339 return collectionEmpty(instance
, key
);
1341 return objOffsetEmpty(tvRef
, instance
, tvCellAsCVarRef(key
));
1344 if (LIKELY(instance
->isCollection())) {
1345 return collectionIsset(instance
, key
);
1347 return objOffsetIsset(tvRef
, instance
, tvCellAsCVarRef(key
));
1352 template <bool useEmpty
, bool isObj
= false, KeyType keyType
= AnyKey
>
1353 static inline bool IssetEmptyElem(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1354 TypedValue
* base
, bool baseStrOff
,
1358 initScratchKey
<keyType
>(scratch
, key
);
1359 return IssetEmptyElemObj
<useEmpty
>(
1360 tvRef
, reinterpret_cast<Instance
*>(base
), baseStrOff
, key
);
1367 case KindOfStaticString
:
1368 case KindOfString
: {
1373 initScratchKey
<keyType
>(scratch
, key
);
1375 tvCastToInt64InPlace(&tv
);
1376 int64 x
= tv
.m_data
.num
;
1377 if (x
< 0 || x
>= base
->m_data
.pstr
->size()) {
1383 tvAsVariant(&tvScratch
) = base
->m_data
.pstr
->getChar(x
);
1384 result
= &tvScratch
;
1388 result
= ElemArray
<false, keyType
>(base
->m_data
.parr
, key
);
1391 case KindOfObject
: {
1392 initScratchKey
<keyType
>(scratch
, key
);
1393 return IssetEmptyElemObj
<useEmpty
>(
1394 tvRef
, static_cast<Instance
*>(base
->m_data
.pobj
), baseStrOff
, key
);
1403 return empty(tvAsCVarRef(result
));
1405 return isset(tvAsCVarRef(result
));
1409 template <bool useEmpty
, KeyType keyType
>
1410 static inline bool IssetEmptyPropObj(Class
* ctx
, Instance
* instance
,
1413 bool issetEmptyResult
;
1414 keySD
= prepareKey
<keyType
>(key
);
1415 issetEmptyResult
= useEmpty
?
1416 instance
->propEmpty(ctx
, keySD
) :
1417 instance
->propIsset(ctx
, keySD
);
1418 releaseKey
<keyType
>(keySD
);
1419 return issetEmptyResult
;
1422 template <bool useEmpty
, bool isObj
= false, KeyType keyType
= AnyKey
>
1423 static inline bool IssetEmptyProp(Class
* ctx
, TypedValue
* base
,
1426 return IssetEmptyPropObj
<useEmpty
, keyType
>(
1427 ctx
, reinterpret_cast<Instance
*>(base
), key
);
1430 TypedValue tvScratch
;
1431 TypedValue
* result
= NULL
;
1432 DataType t
= propPre
<false, false, true>(tvScratch
, result
, base
);
1433 if (t
== KindOfNull
) {
1436 if (t
== KindOfObject
) {
1437 return IssetEmptyPropObj
<useEmpty
, keyType
>(ctx
, instanceFromTv(base
), key
);
1439 ASSERT(t
== KindOfArray
);
1444 template <bool setResult
>
1445 static inline void SetPropNull(Cell
* val
) {
1447 tvRefcountedDecRefCell(val
);
1450 raise_warning("Cannot access property on non-object");
1452 static inline void SetPropStdclass(TypedValue
* base
, TypedValue
* key
,
1454 Instance
* obj
= createDefaultObject();
1456 StringData
* keySD
= prepareKey(key
);
1457 obj
->setProp(NULL
, keySD
, (TypedValue
*)val
);
1458 if (keySD
->decRefCount() == 0) {
1461 tvRefcountedDecRef(base
);
1462 base
->m_type
= KindOfObject
;
1463 /* dont set _count; base could be an inner variant */
1464 base
->m_data
.pobj
= obj
;
1467 template <KeyType keyType
>
1468 static inline void SetPropObj(Class
* ctx
, Instance
* instance
,
1469 TypedValue
* key
, Cell
* val
) {
1470 StringData
* keySD
= prepareKey
<keyType
>(key
);
1472 instance
->setProp(ctx
, keySD
, val
);
1473 releaseKey
<keyType
>(keySD
);
1476 // $base->$key = $val
1477 template <bool setResult
, bool isObj
= false, KeyType keyType
= AnyKey
>
1478 static inline void SetProp(Class
* ctx
, TypedValue
* base
, TypedValue
* key
,
1481 SetPropObj
<keyType
>(ctx
, reinterpret_cast<Instance
*>(base
),
1492 initScratchKey
<keyType
>(scratch
, key
);
1493 SetPropStdclass(base
, key
, val
);
1496 case KindOfBoolean
: {
1497 if (base
->m_data
.num
) {
1498 SetPropNull
<setResult
>(val
);
1500 initScratchKey
<keyType
>(scratch
, key
);
1501 SetPropStdclass(base
, key
, val
);
1505 case KindOfStaticString
:
1506 case KindOfString
: {
1507 if (base
->m_data
.pstr
->size() != 0) {
1508 SetPropNull
<setResult
>(val
);
1510 initScratchKey
<keyType
>(scratch
, key
);
1511 SetPropStdclass(base
, key
, val
);
1515 case KindOfObject
: {
1516 SetPropObj
<keyType
>(ctx
, static_cast<Instance
*>(base
->m_data
.pobj
),
1521 SetPropNull
<setResult
>(val
);
1527 static inline TypedValue
* SetOpPropNull(TypedValue
& tvScratch
) {
1528 raise_warning("Attempt to assign property of non-object");
1529 tvWriteNull(&tvScratch
);
1532 static inline TypedValue
* SetOpPropStdclass(TypedValue
& tvRef
, unsigned char op
,
1533 TypedValue
* base
, TypedValue
* key
,
1535 Instance
* obj
= createDefaultObject();
1537 tvRefcountedDecRef(base
);
1538 base
->m_type
= KindOfObject
;
1540 base
->m_data
.pobj
= obj
;
1542 StringData
* keySD
= prepareKey(key
);
1543 tvWriteNull(&tvRef
);
1544 SETOP_BODY(&tvRef
, op
, rhs
);
1545 obj
->setProp(NULL
, keySD
, &tvRef
);
1546 LITSTR_DECREF(keySD
);
1550 template <KeyType keyType
>
1551 static inline TypedValue
* SetOpPropObj(TypedValue
& tvRef
, Class
* ctx
,
1552 unsigned char op
, Instance
* instance
,
1553 TypedValue
* key
, Cell
* rhs
) {
1554 StringData
* keySD
= prepareKey
<keyType
>(key
);
1555 TypedValue
* result
= instance
->setOpProp(tvRef
, ctx
, op
, keySD
, rhs
);
1556 releaseKey
<keyType
>(keySD
);
1560 // $base->$key <op>= $rhs
1561 template<bool isObj
= false, KeyType keyType
= AnyKey
>
1562 static inline TypedValue
* SetOpProp(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1563 Class
* ctx
, unsigned char op
,
1564 TypedValue
* base
, TypedValue
* key
,
1568 return SetOpPropObj
<keyType
>(tvRef
, ctx
, op
,
1569 reinterpret_cast<Instance
*>(base
), key
, rhs
);
1578 initScratchKey
<keyType
>(scratch
, key
);
1579 result
= SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
1582 case KindOfBoolean
: {
1583 if (base
->m_data
.num
) {
1584 result
= SetOpPropNull(tvScratch
);
1586 initScratchKey
<keyType
>(scratch
, key
);
1587 result
= SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
1591 case KindOfStaticString
:
1592 case KindOfString
: {
1593 if (base
->m_data
.pstr
->size() != 0) {
1594 result
= SetOpPropNull(tvScratch
);
1596 initScratchKey
<keyType
>(scratch
, key
);
1597 result
= SetOpPropStdclass(tvRef
, op
, base
, key
, rhs
);
1601 case KindOfObject
: {
1602 result
= SetOpPropObj
<keyType
>(tvRef
, ctx
, op
, instanceFromTv(base
), key
,
1607 result
= SetOpPropNull(tvScratch
);
1614 template <bool setResult
>
1615 static inline void IncDecPropNull(TypedValue
& dest
) {
1616 raise_warning("Attempt to increment/decrement property of non-object");
1621 template <bool setResult
>
1622 static inline void IncDecPropStdclass(unsigned char op
, TypedValue
* base
,
1623 TypedValue
* key
, TypedValue
& dest
) {
1624 Instance
* obj
= createDefaultObject();
1626 tvRefcountedDecRef(base
);
1627 base
->m_type
= KindOfObject
;
1629 base
->m_data
.pobj
= obj
;
1631 StringData
* keySD
= prepareKey(key
);
1635 IncDecBody
<true>(op
, (&tv
), &dest
);
1636 obj
->setProp(NULL
, keySD
, &dest
);
1638 // The caller doesn't actually want the result set, but we have to do so in
1639 // order to call obj->setProp().
1641 tvWriteUninit(&tDest
);
1642 IncDecBody
<true>(op
, (&tv
), &tDest
);
1643 obj
->setProp(NULL
, keySD
, &tDest
);
1644 ASSERT(!IS_REFCOUNTED_TYPE(tDest
.m_type
));
1646 ASSERT(!IS_REFCOUNTED_TYPE(tv
.m_type
));
1647 LITSTR_DECREF(keySD
);
1650 template <bool setResult
, KeyType keyType
>
1651 static inline void IncDecPropObj(TypedValue
& tvRef
, Class
* ctx
,
1652 unsigned char op
, Instance
* base
,
1653 TypedValue
* key
, TypedValue
& dest
) {
1654 StringData
* keySD
= prepareKey
<keyType
>(key
);
1655 base
->incDecProp
<setResult
>(tvRef
, ctx
, op
, keySD
, dest
);
1656 releaseKey
<keyType
>(keySD
);
1659 template <bool setResult
, bool isObj
= false, KeyType keyType
= AnyKey
>
1660 static inline void IncDecProp(TypedValue
& tvScratch
, TypedValue
& tvRef
,
1661 Class
* ctx
, unsigned char op
,
1662 TypedValue
* base
, TypedValue
* key
,
1666 IncDecPropObj
<setResult
, keyType
>(tvRef
, ctx
, op
,
1667 reinterpret_cast<Instance
*>(base
),
1677 initScratchKey
<keyType
>(scratch
, key
);
1678 IncDecPropStdclass
<setResult
>(op
, base
, key
, dest
);
1681 case KindOfBoolean
: {
1682 if (base
->m_data
.num
) {
1683 IncDecPropNull
<setResult
>(dest
);
1685 initScratchKey
<keyType
>(scratch
, key
);
1686 IncDecPropStdclass
<setResult
>(op
, base
, key
, dest
);
1690 case KindOfStaticString
:
1691 case KindOfString
: {
1692 if (base
->m_data
.pstr
->size() != 0) {
1693 IncDecPropNull
<setResult
>(dest
);
1695 initScratchKey
<keyType
>(scratch
, key
);
1696 IncDecPropStdclass
<setResult
>(op
, base
, key
, dest
);
1700 case KindOfObject
: {
1701 IncDecPropObj
<setResult
, keyType
>(tvRef
, ctx
, op
, instanceFromTv(base
),
1706 IncDecPropNull
<setResult
>(dest
);
1712 template<bool isObj
= false, KeyType keyType
= AnyKey
>
1713 static inline void UnsetProp(Class
* ctx
, TypedValue
* base
,
1720 if (UNLIKELY(type
!= KindOfObject
)) {
1724 instance
= instanceFromTv(base
);
1726 instance
= reinterpret_cast<Instance
*>(base
);
1729 StringData
* keySD
= prepareKey
<keyType
>(key
);
1731 instance
->unsetProp(ctx
, keySD
);
1732 releaseKey
<keyType
>(keySD
);
1735 ///////////////////////////////////////////////////////////////////////////////
1738 #endif // incl_VM_MEMBER_OPERATIONS_H_