Supported enregistered string and int keys in the vector translator
[hiphop-php.git] / src / runtime / vm / member_operations.h
blobf1ab85b043dc77f087770d1e2dc98f5897ab52b2
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
29 namespace HPHP {
30 namespace VM {
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
36 true
37 #else
38 false
39 #endif
42 enum KeyType {
43 AnyKey = 0,
44 StrKey,
45 IntKey,
48 template<KeyType kt>
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) {
65 if (t == StrKey) {
66 return KindOfString;
67 } else if (t == IntKey) {
68 return KindOfInt64;
69 } else {
70 not_reached();
74 template<KeyType kt>
75 inline typename KeyTypeTraits<kt>::valType keyAsValue(TypedValue* key);
76 template<>
77 inline int64 keyAsValue<IntKey>(TypedValue* key) {
78 return reinterpret_cast<int64>(key);
80 template<>
81 inline CVarRef keyAsValue<AnyKey>(TypedValue* key) {
82 return tvAsCVarRef(key);
84 template<>
85 inline StrNR keyAsValue<StrKey>(TypedValue* key) {
86 return StrNR(reinterpret_cast<StringData*>(key));
89 template<KeyType kt>
90 inline typename KeyTypeTraits<kt>::rawType keyAsRaw(TypedValue* key) {
91 if (kt == AnyKey) {
92 not_reached();
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
99 template<KeyType kt>
100 inline void initScratchKey(TypedValue& tv, TypedValue*& key) {
101 if (kt != AnyKey) {
102 tv.m_type = keyDataType(kt);
103 tv.m_data.num = reinterpret_cast<int64>(key);
104 key = &tv;
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,
112 bool validate=true);
113 bool objOffsetEmpty(TypedValue& tvRef, Instance* base, CVarRef offset,
114 bool validate=true);
115 void objOffsetSet(Instance* base, CVarRef offset, TypedValue* val,
116 bool validate=true);
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);
128 } else {
129 not_reached();
133 template <KeyType keyType>
134 static inline void releaseKey(StringData* keySD) {
135 if (keyType == AnyKey) {
136 LITSTR_DECREF(keySD);
137 } else {
138 ASSERT(keyType == StrKey);
142 static inline void opPre(TypedValue*& base, DataType& type) {
143 // Get inner variant if necessary.
144 type = base->m_type;
145 if (type == KindOfRef) {
146 base = base->m_data.pref->tv();
147 type = base->m_type;
151 static inline TypedValue* ElemArrayRawKey(ArrayData* base,
152 int64 key) {
153 TypedValue* result;
154 if (LIKELY(IsHphpArray(base))) {
155 result = static_cast<HphpArray*>(base)->nvGet(key);
156 if (result == NULL) {
157 result = (TypedValue*)&null_variant;
159 } else {
160 result = (TypedValue*)&base->get(key);
162 return result;
165 static inline TypedValue* ElemArrayRawKey(ArrayData* base,
166 StringData* key) {
167 TypedValue* result;
168 if (LIKELY(IsHphpArray(base))) {
169 int64 n;
170 if (!key->isStrictlyInteger(n)) {
171 result = static_cast<HphpArray*>(base)->nvGet(key);
172 } else {
173 result = static_cast<HphpArray*>(base)->nvGet(n);
175 if (result == NULL) {
176 result = (TypedValue*)&null_variant;
178 } else {
179 result = (TypedValue*)&ArrNR(base).asArray().rvalAtRef(StrNR(key));
181 return result;
184 template <bool warn, KeyType keyType>
185 static inline TypedValue* ElemArray(ArrayData* base,
186 TypedValue* key) {
187 TypedValue* result;
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);
194 } else {
195 result = (TypedValue*)&ArrNR(base).asArray()
196 .rvalAtRef(tvCellAsCVarRef(key));
198 } else {
199 result = ElemArrayRawKey(base, keyAsRaw<keyType>(key));
202 if (UNLIKELY(result->m_type == KindOfUninit)) {
203 result = (TypedValue*)&init_null_variant;
204 if (warn) {
205 TypedValue scratch;
206 initScratchKey<keyType>(scratch, key);
207 raise_notice(Strings::UNDEFINED_INDEX,
208 tvAsCVarRef(key).toString().data());
211 return result;
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,
218 TypedValue* key) {
219 TypedValue* result;
220 DataType type;
221 opPre(base, type);
222 switch (type) {
223 case KindOfUninit:
224 case KindOfNull:
225 case KindOfBoolean:
226 case KindOfInt64:
227 case KindOfDouble: {
228 result = (TypedValue*)&init_null_variant;
229 break;
231 case KindOfStaticString:
232 case KindOfString: {
233 if (baseStrOff) {
234 raise_error("Cannot use string offset as an array");
236 int64 x;
237 if (keyType == IntKey) {
238 x = reinterpret_cast<int64>(key);
239 } else if (keyType == StrKey) {
240 x = reinterpret_cast<StringData*>(key)->toInt64(10);
241 } else {
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()) {
246 if (warn) {
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;
253 } else {
254 TV_WRITE_UNINIT(&tvScratch);
255 tvAsVariant(&tvScratch) = base->m_data.pstr->getChar(x);
257 result = &tvScratch;
258 baseStrOff = true;
259 break;
261 case KindOfArray: {
262 result = ElemArray<warn, keyType>(base->m_data.parr, key);
263 break;
265 case KindOfObject: {
266 TypedValue scratch;
267 initScratchKey<keyType>(scratch, key);
268 if (LIKELY(base->m_data.pobj->isCollection())) {
269 result = collectionGet(base->m_data.pobj, key);
270 } else {
271 result = objOffsetGet(tvRef, instanceFromTv(base), tvCellAsCVarRef(key));
273 break;
275 default: {
276 ASSERT(false);
277 result = NULL;
280 return result;
283 template <bool warn, KeyType keyType>
284 static inline TypedValue* ElemDArray(TypedValue* base, TypedValue* key) {
285 TypedValue* result;
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);
292 } else {
293 result = (TypedValue*)&tvAsVariant(base).asArrRef()
294 .lvalAt(keyAsValue<keyType>(key));
297 if (warn) {
298 if (!defined) {
299 TypedValue scratch;
300 initScratchKey<keyType>(scratch, key);
301 raise_notice(Strings::UNDEFINED_INDEX,
302 tvAsCVarRef(key).toString().data());
306 return result;
309 // $base[$key] = ...
310 // \____ ____/
311 // v
312 // $result
313 template <bool warn, bool reffy, KeyType keyType = AnyKey>
314 static inline TypedValue* ElemD(TypedValue& tvScratch, TypedValue& tvRef,
315 TypedValue* base, TypedValue* key) {
316 TypedValue scratch;
317 TypedValue* result;
318 DataType type;
319 opPre(base, type);
320 switch (type) {
321 case KindOfUninit:
322 case KindOfNull: {
323 initScratchKey<keyType>(scratch, key);
324 Array a = Array::Create();
325 result = (TypedValue*)&a.lvalAt(tvCellAsCVarRef(key));
326 if (warn) {
327 raise_notice(Strings::UNDEFINED_INDEX,
328 tvAsCVarRef(key).toString().data());
330 tvAsVariant(base) = a;
331 break;
333 case KindOfBoolean: {
334 if (base->m_data.num) {
335 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY);
336 tvWriteUninit(&tvScratch);
337 result = &tvScratch;
338 } else {
339 initScratchKey<keyType>(scratch, key);
340 Array a = Array::Create();
341 result = (TypedValue*)&a.lvalAt(tvCellAsCVarRef(key));
342 if (warn) {
343 raise_notice(Strings::UNDEFINED_INDEX,
344 tvAsCVarRef(key).toString().data());
346 tvAsVariant(base) = a;
348 break;
350 case KindOfInt64:
351 case KindOfDouble: {
352 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY);
353 tvWriteUninit(&tvScratch);
354 result = &tvScratch;
355 break;
357 case KindOfStaticString:
358 case KindOfString: {
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));
363 if (warn) {
364 raise_notice(Strings::UNDEFINED_INDEX,
365 tvAsCVarRef(key).toString().data());
367 tvAsVariant(base) = a;
368 } else {
369 raise_error("Operator not supported for strings");
370 result = NULL; // Silence compiler warning.
372 break;
374 case KindOfArray: {
375 result = ElemDArray<warn, keyType>(base, key);
376 break;
378 case KindOfObject: {
379 initScratchKey<keyType>(scratch, key);
380 if (LIKELY(base->m_data.pobj->isCollection())) {
381 if (reffy) {
382 raise_error("Collection elements cannot be taken by reference");
383 result = NULL;
384 } else {
385 result = collectionGet(base->m_data.pobj, key);
387 } else {
388 result = objOffsetGet(tvRef, instanceFromTv(base), tvCellAsCVarRef(key));
390 break;
392 default: {
393 ASSERT(false);
394 result = NULL; // Silence compiler warning.
397 return result;
400 // $base[$key] = ...
401 // \____ ____/
402 // v
403 // $result
404 template <KeyType keyType = AnyKey>
405 static inline TypedValue* ElemU(TypedValue& tvScratch, TypedValue& tvRef,
406 TypedValue* base, TypedValue* key) {
407 TypedValue* result = NULL;
408 DataType type;
409 opPre(base, type);
410 switch (type) {
411 case KindOfUninit:
412 case KindOfNull:
413 case KindOfBoolean:
414 case KindOfInt64:
415 case KindOfDouble: {
416 tvWriteUninit(&tvScratch);
417 result = &tvScratch;
418 break;
420 case KindOfStaticString:
421 case KindOfString: {
422 raise_error("Operator not supported for strings");
423 break;
425 case KindOfArray: {
426 bool defined =
427 tvAsCVarRef(base).asCArrRef().exists(keyAsValue<keyType>(key));
428 if (defined) {
429 if (keyType == AnyKey && key->m_type == KindOfInt64) {
430 result = (TypedValue*)&tvAsVariant(base).asArrRef()
431 .lvalAt(key->m_data.num);
432 } else {
433 result = (TypedValue*)&tvAsVariant(base).asArrRef().lvalAt(
434 keyAsValue<keyType>(key));
436 } else {
437 tvWriteUninit(&tvScratch);
438 result = &tvScratch;
440 break;
442 case KindOfObject: {
443 TypedValue scratch;
444 initScratchKey<keyType>(scratch, key);
445 if (LIKELY(base->m_data.pobj->isCollection())) {
446 result = collectionGet(base->m_data.pobj, key);
447 } else {
448 result = objOffsetGet(tvRef, instanceFromTv(base), tvCellAsCVarRef(key));
450 break;
452 default: {
453 ASSERT(false);
454 result = NULL;
457 return result;
460 // $result = ($base[] = ...);
461 static inline TypedValue* NewElem(TypedValue& tvScratch, TypedValue& tvRef,
462 TypedValue* base) {
463 TypedValue* result;
464 DataType type;
465 opPre(base, type);
466 switch (type) {
467 case KindOfUninit:
468 case KindOfNull: {
469 Array a = Array::Create();
470 result = (TypedValue*)&a.lvalAt();
471 tvAsVariant(base) = a;
472 break;
474 case KindOfBoolean: {
475 if (base->m_data.num) {
476 raise_warning("Invalid NewElem operand");
477 tvWriteUninit(&tvScratch);
478 result = &tvScratch;
479 } else {
480 Array a = Array::Create();
481 result = (TypedValue*)&a.lvalAt();
482 tvAsVariant(base) = a;
484 break;
486 case KindOfStaticString:
487 case KindOfString: {
488 if (base->m_data.pstr->size() == 0) {
489 Array a = Array::Create();
490 result = (TypedValue*)&a.lvalAt();
491 tvAsVariant(base) = a;
492 } else {
493 raise_warning("Invalid NewElem operand");
494 tvWriteUninit(&tvScratch);
495 result = &tvScratch;
497 break;
499 case KindOfArray: {
500 result = (TypedValue*)&tvAsVariant(base).asArrRef().lvalAt();
501 break;
503 case KindOfObject: {
504 if (LIKELY(base->m_data.pobj->isCollection())) {
505 raise_error("Cannot use [] for reading");
506 result = NULL;
507 } else {
508 result = objOffsetGet(tvRef, instanceFromTv(base), init_null_variant);
510 break;
512 default: {
513 raise_warning("Invalid NewElem operand");
514 tvWriteUninit(&tvScratch);
515 result = &tvScratch;
516 break;
519 return result;
522 static inline void SetElemEmptyish(TypedValue* base, TypedValue* key,
523 Cell* value) {
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);
531 if (setResult) {
532 tvRefcountedDecRefCell((TypedValue*)value);
533 tvWriteNull((TypedValue*)value);
537 static inline ArrayData* SetElemArrayRawKey(ArrayData* a,
538 int64 key,
539 Cell* value,
540 bool copy) {
541 return a->set(key, tvCellAsCVarRef(value), copy);
544 static inline ArrayData* SetElemArrayRawKey(ArrayData* a,
545 StringData* key,
546 Cell* value,
547 bool copy) {
548 int64 n;
549 if (key->isStrictlyInteger(n)) {
550 return a->set(n, tvCellAsCVarRef(value), copy);
551 } else {
552 return a->set(StrNR(key), tvCellAsCVarRef(value), copy);
556 template <bool setResult, KeyType keyType>
557 static inline void SetElemArray(TypedValue* base, TypedValue* key,
558 Cell* value) {
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);
572 } else {
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.
577 if (setResult) {
578 if (IS_REFCOUNTED_TYPE(value->m_type)) {
579 tvDecRef(value);
581 tvWriteNull(value);
585 if (newData != NULL && newData != a) {
586 newData->incRefCount();
587 if (a->decRefCount() == 0) {
588 a->release();
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
595 // get around.
596 template <bool setResult, KeyType keyType = AnyKey>
597 static inline void SetElem(TypedValue* base, TypedValue* key, Cell* value) {
598 TypedValue scratch;
599 DataType type;
600 opPre(base, type);
601 switch (type) {
602 case KindOfUninit:
603 case KindOfNull: {
604 initScratchKey<keyType>(scratch, key);
605 SetElemEmptyish(base, key, value);
606 break;
608 case KindOfBoolean: {
609 if (base->m_data.num) {
610 SetElemNumberish<setResult>(value);
611 } else {
612 initScratchKey<keyType>(scratch, key);
613 SetElemEmptyish(base, key, value);
615 break;
617 case KindOfInt64:
618 case KindOfDouble: {
619 SetElemNumberish<setResult>(value);
620 break;
622 case KindOfStaticString:
623 case KindOfString: {
624 initScratchKey<keyType>(scratch, key);
625 int baseLen = base->m_data.pstr->size();
626 if (baseLen == 0) {
627 SetElemEmptyish(base, key, value);
628 } else {
629 // Convert key to string offset.
630 int64 x;
632 TypedValue tv;
633 tvDup(key, &tv);
634 tvCastToInt64InPlace(&tv);
635 x = tv.m_data.num;
637 if (x < 0 || x >= StringData::MaxSize) {
638 raise_warning("Illegal string offset: %lld", x);
639 break;
641 // Compute how long the resulting string will be. Type needs
642 // to agree with x.
643 int64 slen;
644 if (x >= baseLen) {
645 slen = x + 1;
646 } else {
647 slen = baseLen;
649 // Extract the first character of (string)value.
650 char y[2];
652 TypedValue tv;
653 tvDup(value, &tv);
654 tvCastToStringInPlace(&tv);
655 if (tv.m_data.pstr->size() > 0) {
656 y[0] = tv.m_data.pstr->data()[0];
657 y[1] = '\0';
658 } else {
659 y[0] = '\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
666 // reference.
667 base->m_data.pstr->setChar(x, y[0]);
668 } else {
669 StringData* sd = NEW(StringData)(slen);
670 char* s = sd->mutableSlice().ptr;
671 memcpy(s, base->m_data.pstr->data(), baseLen);
672 if (x > baseLen) {
673 memset(&s[baseLen], ' ', slen - baseLen - 1);
675 s[x] = y[0];
676 sd->setSize(slen);
677 sd->incRefCount();
678 if (base->m_data.pstr->decRefCount() == 0) base->m_data.pstr->release();
679 base->m_data.pstr = sd;
680 base->m_type = KindOfString;
682 if (setResult) {
683 // Push y onto the stack.
684 tvRefcountedDecRef(value);
685 StringData* sd = NEW(StringData)(y, strlen(y), CopyString);
686 sd->incRefCount();
687 value->m_data.pstr = sd;
688 value->_count = 0;
689 value->m_type = KindOfString;
692 break;
694 case KindOfArray: {
695 SetElemArray<setResult, keyType>(base, key, value);
696 break;
698 case KindOfObject: {
699 initScratchKey<keyType>(scratch, key);
700 if (LIKELY(base->m_data.pobj->isCollection())) {
701 collectionSet(base->m_data.pobj, key, (TypedValue*)value);
702 } else {
703 objOffsetSet(instanceFromTv(base), tvAsCVarRef(key), (TypedValue*)value);
705 break;
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);
719 if (setResult) {
720 tvRefcountedDecRefCell((TypedValue*)value);
721 tvWriteNull((TypedValue*)value);
724 template <bool setResult>
725 static inline void SetNewElem(TypedValue* base, Cell* value) {
726 DataType type;
727 opPre(base, type);
728 switch (type) {
729 case KindOfUninit:
730 case KindOfNull: {
731 SetNewElemEmptyish(base, value);
732 break;
734 case KindOfBoolean: {
735 if (base->m_data.num) {
736 SetNewElemNumberish<setResult>(value);
737 } else {
738 SetNewElemEmptyish(base, value);
740 break;
742 case KindOfInt64:
743 case KindOfDouble: {
744 SetNewElemNumberish<setResult>(value);
745 break;
747 case KindOfStaticString:
748 case KindOfString: {
749 int baseLen = base->m_data.pstr->size();
750 if (baseLen == 0) {
751 SetNewElemEmptyish(base, value);
752 } else {
753 raise_error("[] operator not supported for strings");
755 break;
757 case KindOfArray: {
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);
762 if (a) {
763 a->incRefCount();
764 base->m_data.parr->decRefCount();
765 base->m_data.parr = a;
767 break;
769 case KindOfObject: {
770 if (LIKELY(base->m_data.pobj->isCollection())) {
771 collectionAppend(base->m_data.pobj, (TypedValue*)value);
772 } else {
773 objOffsetAppend(instanceFromTv(base), (TypedValue*)value);
775 break;
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;
786 if (MoreWarnings) {
787 raise_notice(Strings::UNDEFINED_INDEX,
788 tvAsCVarRef(key).toString().data());
790 SETOP_BODY(result, op, rhs);
791 return result;
793 static inline TypedValue* SetOpElemNumberish(TypedValue& tvScratch) {
794 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY);
795 tvWriteNull(&tvScratch);
796 return &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) {
803 TypedValue scratch;
804 TypedValue* result;
805 DataType type;
806 opPre(base, type);
807 switch (type) {
808 case KindOfUninit:
809 case KindOfNull: {
810 initScratchKey<keyType>(scratch, key);
811 result = SetOpElemEmptyish(op, base, key, rhs);
812 break;
814 case KindOfBoolean: {
815 if (base->m_data.num) {
816 result = SetOpElemNumberish(tvScratch);
817 } else {
818 initScratchKey<keyType>(scratch, key);
819 result = SetOpElemEmptyish(op, base, key, rhs);
821 break;
823 case KindOfInt64:
824 case KindOfDouble: {
825 result = SetOpElemNumberish(tvScratch);
826 break;
828 case KindOfStaticString:
829 case KindOfString: {
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);
835 break;
837 case KindOfArray: {
838 result = ElemDArray<MoreWarnings, keyType>(base, key);
839 SETOP_BODY(result, op, rhs);
840 break;
842 case KindOfObject: {
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);
847 } else {
848 result = objOffsetGet(tvRef, instanceFromTv(base), tvCellAsCVarRef(key));
849 SETOP_BODY(result, op, rhs);
850 objOffsetSet(instanceFromTv(base), tvAsCVarRef(key), result, false);
852 break;
854 default: {
855 ASSERT(false);
856 result = NULL; // Silence compiler warning.
859 return result;
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);
868 return result;
870 static inline TypedValue* SetOpNewElemNumberish(TypedValue& tvScratch) {
871 raise_warning(Strings::CANNOT_USE_SCALAR_AS_ARRAY);
872 tvWriteNull(&tvScratch);
873 return &tvScratch;
875 static inline TypedValue* SetOpNewElem(TypedValue& tvScratch, TypedValue& tvRef,
876 unsigned char op, TypedValue* base,
877 Cell* rhs) {
878 TypedValue* result;
879 DataType type;
880 opPre(base, type);
881 switch (type) {
882 case KindOfUninit:
883 case KindOfNull: {
884 result = SetOpNewElemEmptyish(op, base, rhs);
885 break;
887 case KindOfBoolean: {
888 if (base->m_data.num) {
889 result = SetOpNewElemNumberish(tvScratch);
890 } else {
891 result = SetOpNewElemEmptyish(op, base, rhs);
893 break;
895 case KindOfInt64:
896 case KindOfDouble: {
897 result = SetOpNewElemNumberish(tvScratch);
898 break;
900 case KindOfStaticString:
901 case KindOfString: {
902 if (base->m_data.pstr->size() != 0) {
903 raise_error("[] operator not supported for strings");
905 result = SetOpNewElemEmptyish(op, base, rhs);
906 break;
908 case KindOfArray: {
909 result = (TypedValue*)&tvAsVariant(base).asArrRef().lvalAt();
910 SETOP_BODY(result, op, rhs);
911 break;
913 case KindOfObject: {
914 if (LIKELY(base->m_data.pobj->isCollection())) {
915 raise_error("Cannot use [] for reading");
916 result = NULL;
917 } else {
918 result = objOffsetGet(tvRef, instanceFromTv(base), init_null_variant);
919 SETOP_BODY(result, op, rhs);
920 objOffsetAppend(instanceFromTv(base), result, false);
922 break;
924 default: {
925 ASSERT(false);
926 result = NULL; // Silence compiler warning.
929 return result;
932 template <bool setResult>
933 static inline void IncDecBody(unsigned char op, TypedValue* fr,
934 TypedValue* to) {
935 if (fr->m_type == KindOfInt64) {
936 switch ((IncDecOp)op) {
937 case PreInc: {
938 ++(fr->m_data.num);
939 if (setResult) {
940 tvDupCell(fr, to);
942 break;
944 case PostInc: {
945 if (setResult) {
946 tvDupCell(fr, to);
948 ++(fr->m_data.num);
949 break;
951 case PreDec: {
952 --(fr->m_data.num);
953 if (setResult) {
954 tvDupCell(fr, to);
956 break;
958 case PostDec: {
959 if (setResult) {
960 tvDupCell(fr, to);
962 --(fr->m_data.num);
963 break;
965 default: ASSERT(false);
967 return;
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) {
982 case PreInc: {
983 ++(tvAsVariant(fr));
984 if (setResult) {
985 tvReadCell(fr, to);
987 break;
989 case PostInc: {
990 if (setResult) {
991 tvReadCell(fr, to);
993 ++(tvAsVariant(fr));
994 break;
996 case PreDec: {
997 --(tvAsVariant(fr));
998 if (setResult) {
999 tvReadCell(fr, to);
1001 break;
1003 case PostDec: {
1004 if (setResult) {
1005 tvReadCell(fr, to);
1007 --(tvAsVariant(fr));
1008 break;
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;
1020 if (MoreWarnings) {
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);
1029 if (setResult) {
1030 tvWriteNull(&dest);
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) {
1037 TypedValue scratch;
1038 DataType type;
1039 opPre(base, type);
1040 switch (type) {
1041 case KindOfUninit:
1042 case KindOfNull: {
1043 initScratchKey<keyType>(scratch, key);
1044 IncDecElemEmptyish<setResult>(op, base, key, dest);
1045 break;
1047 case KindOfBoolean: {
1048 if (base->m_data.num) {
1049 IncDecElemNumberish<setResult>(dest);
1050 } else {
1051 initScratchKey<keyType>(scratch, key);
1052 IncDecElemEmptyish<setResult>(op, base, key, dest);
1054 break;
1056 case KindOfInt64:
1057 case KindOfDouble: {
1058 IncDecElemNumberish<setResult>(dest);
1059 break;
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);
1068 break;
1070 case KindOfArray: {
1071 TypedValue* result = ElemDArray<MoreWarnings, keyType>(base, key);
1072 IncDecBody<setResult>(op, result, &dest);
1073 break;
1075 case KindOfObject: {
1076 TypedValue* result;
1077 initScratchKey<keyType>(scratch, key);
1078 if (LIKELY(base->m_data.pobj->isCollection())) {
1079 result = collectionGet(base->m_data.pobj, key);
1080 } else {
1081 result = objOffsetGet(tvRef, instanceFromTv(base), tvCellAsCVarRef(key));
1083 IncDecBody<setResult>(op, result, &dest);
1084 break;
1086 default: ASSERT(false);
1090 template <bool setResult>
1091 static inline void IncDecNewElemEmptyish(unsigned char op, TypedValue* base,
1092 TypedValue& dest) {
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);
1101 if (setResult) {
1102 tvWriteNull(&dest);
1105 template <bool setResult>
1106 static inline void IncDecNewElem(TypedValue& tvScratch, TypedValue& tvRef,
1107 unsigned char op, TypedValue* base,
1108 TypedValue& dest) {
1109 DataType type;
1110 opPre(base, type);
1111 switch (type) {
1112 case KindOfUninit:
1113 case KindOfNull: {
1114 IncDecNewElemEmptyish<setResult>(op, base, dest);
1115 break;
1117 case KindOfBoolean: {
1118 if (base->m_data.num) {
1119 IncDecNewElemNumberish<setResult>(dest);
1120 } else {
1121 IncDecNewElemEmptyish<setResult>(op, base, dest);
1123 break;
1125 case KindOfInt64:
1126 case KindOfDouble: {
1127 IncDecNewElemNumberish<setResult>(dest);
1128 break;
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);
1136 break;
1138 case KindOfArray: {
1139 TypedValue* result = (TypedValue*)&tvAsVariant(base).asArrRef().lvalAt();
1140 IncDecBody<setResult>(op, result, &dest);
1141 break;
1143 case KindOfObject: {
1144 TypedValue* result;
1145 if (LIKELY(base->m_data.pobj->isCollection())) {
1146 raise_error("Cannot use [] for reading");
1147 result = NULL;
1148 } else {
1149 result = objOffsetGet(tvRef, instanceFromTv(base), init_null_variant);
1150 IncDecBody<setResult>(op, result, &dest);
1152 break;
1154 default: ASSERT(false);
1158 static inline ArrayData* UnsetElemArrayRawKey(ArrayData* a, int64 key,
1159 bool copy) {
1160 return a->remove(key, copy);
1163 static inline ArrayData* UnsetElemArrayRawKey(ArrayData* a, StringData* key,
1164 bool copy) {
1165 int64 n;
1166 if (!key->isStrictlyInteger(n)) {
1167 return a->remove(StrNR(key), copy);
1168 } else {
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);
1182 } else {
1183 VarNR varKey = tvAsCVarRef(key).toKey();
1184 if (varKey.isNull()) {
1185 return;
1187 a = a->remove(varKey, copy);
1189 } else {
1190 a = UnsetElemArrayRawKey(a, keyAsRaw<keyType>(key), copy);
1192 if (a) {
1193 a->incRefCount();
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) {
1201 TypedValue scratch;
1202 DataType type;
1203 opPre(base, type);
1204 switch (type) {
1205 case KindOfStaticString:
1206 case KindOfString: {
1207 raise_error("Cannot unset string offsets");
1209 case KindOfArray: {
1210 UnsetElemArray<keyType>(base, member);
1211 break;
1213 case KindOfObject: {
1214 initScratchKey<keyType>(scratch, member);
1215 if (LIKELY(base->m_data.pobj->isCollection())) {
1216 collectionUnset(base->m_data.pobj, member);
1217 } else {
1218 objOffsetUnset(instanceFromTv(base), tvAsCVarRef(member));
1220 break;
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;
1230 if (warn) {
1231 raise_warning("Cannot access property on non-object");
1233 return KindOfNull;
1235 static inline Instance* createDefaultObject() {
1236 raise_warning(Strings::CREATING_DEFAULT_OBJECT);
1237 Instance* obj = newInstance(SystemLib::s_stdclassClass);
1238 return obj;
1240 template <bool warn, bool define>
1241 static inline DataType propPreStdclass(TypedValue& tvScratch,
1242 TypedValue*& result, TypedValue* base) {
1243 if (!define) {
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;
1251 obj->incRefCount();
1252 result = base;
1253 return KindOfObject;
1256 template <bool warn, bool define, bool issetEmpty>
1257 static inline DataType propPre(TypedValue& tvScratch, TypedValue*& result,
1258 TypedValue*& base) {
1259 DataType type;
1260 opPre(base, type);
1261 switch (type) {
1262 case KindOfUninit:
1263 case KindOfNull: {
1264 return propPreStdclass<warn, define>(tvScratch, result, base);
1266 case KindOfBoolean: {
1267 if (base->m_data.num) {
1268 return propPreNull<warn>(tvScratch, result);
1269 } else {
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);
1277 } else {
1278 return propPreStdclass<warn, define>(tvScratch, result, base);
1281 case KindOfArray: {
1282 return issetEmpty ? KindOfArray : propPreNull<warn>(tvScratch, result);
1284 case KindOfObject: {
1285 return KindOfObject;
1287 default: {
1288 return propPreNull<warn>(tvScratch, result);
1293 // define == false:
1294 // $result = $base->$key;
1296 // define == true:
1297 // $base->$key = ...
1298 // \____ ____/
1299 // v
1300 // $result
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;
1309 Instance* instance;
1310 if (baseIsObj) {
1311 instance = reinterpret_cast<Instance*>(base);
1312 } else {
1313 DataType t = propPre<warn, define, false>(tvScratch, result, base);
1314 if (t == KindOfNull) {
1315 return result;
1317 ASSERT(t == KindOfObject);
1318 instance = instanceFromTv(base);
1321 keySD = prepareKey<keyType>(key);
1322 // Get property.
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);
1329 #undef ARGS
1330 releaseKey<keyType>(keySD);
1331 return result;
1334 template<bool useEmpty>
1335 static inline bool IssetEmptyElemObj(TypedValue& tvRef, Instance* instance,
1336 bool baseStrOff, TypedValue* key) {
1337 if (useEmpty) {
1338 if (LIKELY(instance->isCollection())) {
1339 return collectionEmpty(instance, key);
1340 } else {
1341 return objOffsetEmpty(tvRef, instance, tvCellAsCVarRef(key));
1343 } else {
1344 if (LIKELY(instance->isCollection())) {
1345 return collectionIsset(instance, key);
1346 } else {
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,
1355 TypedValue* key) {
1356 TypedValue scratch;
1357 if (isObj) {
1358 initScratchKey<keyType>(scratch, key);
1359 return IssetEmptyElemObj<useEmpty>(
1360 tvRef, reinterpret_cast<Instance*>(base), baseStrOff, key);
1363 TypedValue* result;
1364 DataType type;
1365 opPre(base, type);
1366 switch (type) {
1367 case KindOfStaticString:
1368 case KindOfString: {
1369 if (baseStrOff) {
1370 return useEmpty;
1372 TypedValue tv;
1373 initScratchKey<keyType>(scratch, key);
1374 tvDup(key, &tv);
1375 tvCastToInt64InPlace(&tv);
1376 int64 x = tv.m_data.num;
1377 if (x < 0 || x >= base->m_data.pstr->size()) {
1378 return useEmpty;
1380 if (!useEmpty) {
1381 return true;
1383 tvAsVariant(&tvScratch) = base->m_data.pstr->getChar(x);
1384 result = &tvScratch;
1385 break;
1387 case KindOfArray: {
1388 result = ElemArray<false, keyType>(base->m_data.parr, key);
1389 break;
1391 case KindOfObject: {
1392 initScratchKey<keyType>(scratch, key);
1393 return IssetEmptyElemObj<useEmpty>(
1394 tvRef, static_cast<Instance*>(base->m_data.pobj), baseStrOff, key);
1395 break;
1397 default: {
1398 return useEmpty;
1402 if (useEmpty) {
1403 return empty(tvAsCVarRef(result));
1404 } else {
1405 return isset(tvAsCVarRef(result));
1409 template <bool useEmpty, KeyType keyType>
1410 static inline bool IssetEmptyPropObj(Class* ctx, Instance* instance,
1411 TypedValue* key) {
1412 StringData* keySD;
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,
1424 TypedValue* key) {
1425 if (isObj) {
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) {
1434 return useEmpty;
1436 if (t == KindOfObject) {
1437 return IssetEmptyPropObj<useEmpty, keyType>(ctx, instanceFromTv(base), key);
1438 } else {
1439 ASSERT(t == KindOfArray);
1440 return useEmpty;
1444 template <bool setResult>
1445 static inline void SetPropNull(Cell* val) {
1446 if (setResult) {
1447 tvRefcountedDecRefCell(val);
1448 tvWriteNull(val);
1450 raise_warning("Cannot access property on non-object");
1452 static inline void SetPropStdclass(TypedValue* base, TypedValue* key,
1453 Cell* val) {
1454 Instance* obj = createDefaultObject();
1455 obj->incRefCount();
1456 StringData* keySD = prepareKey(key);
1457 obj->setProp(NULL, keySD, (TypedValue*)val);
1458 if (keySD->decRefCount() == 0) {
1459 keySD->release();
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);
1471 // Set property.
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,
1479 Cell* val) {
1480 if (isObj) {
1481 SetPropObj<keyType>(ctx, reinterpret_cast<Instance*>(base),
1482 key, val);
1483 return;
1486 TypedValue scratch;
1487 DataType type;
1488 opPre(base, type);
1489 switch (type) {
1490 case KindOfUninit:
1491 case KindOfNull: {
1492 initScratchKey<keyType>(scratch, key);
1493 SetPropStdclass(base, key, val);
1494 break;
1496 case KindOfBoolean: {
1497 if (base->m_data.num) {
1498 SetPropNull<setResult>(val);
1499 } else {
1500 initScratchKey<keyType>(scratch, key);
1501 SetPropStdclass(base, key, val);
1503 break;
1505 case KindOfStaticString:
1506 case KindOfString: {
1507 if (base->m_data.pstr->size() != 0) {
1508 SetPropNull<setResult>(val);
1509 } else {
1510 initScratchKey<keyType>(scratch, key);
1511 SetPropStdclass(base, key, val);
1513 break;
1515 case KindOfObject: {
1516 SetPropObj<keyType>(ctx, static_cast<Instance*>(base->m_data.pobj),
1517 key, val);
1518 break;
1520 default: {
1521 SetPropNull<setResult>(val);
1522 break;
1527 static inline TypedValue* SetOpPropNull(TypedValue& tvScratch) {
1528 raise_warning("Attempt to assign property of non-object");
1529 tvWriteNull(&tvScratch);
1530 return &tvScratch;
1532 static inline TypedValue* SetOpPropStdclass(TypedValue& tvRef, unsigned char op,
1533 TypedValue* base, TypedValue* key,
1534 Cell* rhs) {
1535 Instance* obj = createDefaultObject();
1536 obj->incRefCount();
1537 tvRefcountedDecRef(base);
1538 base->m_type = KindOfObject;
1539 base->_count = 0;
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);
1547 return &tvRef;
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);
1557 return result;
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,
1565 Cell* rhs) {
1566 TypedValue scratch;
1567 if (isObj) {
1568 return SetOpPropObj<keyType>(tvRef, ctx, op,
1569 reinterpret_cast<Instance*>(base), key, rhs);
1572 TypedValue* result;
1573 DataType type;
1574 opPre(base, type);
1575 switch (type) {
1576 case KindOfUninit:
1577 case KindOfNull: {
1578 initScratchKey<keyType>(scratch, key);
1579 result = SetOpPropStdclass(tvRef, op, base, key, rhs);
1580 break;
1582 case KindOfBoolean: {
1583 if (base->m_data.num) {
1584 result = SetOpPropNull(tvScratch);
1585 } else {
1586 initScratchKey<keyType>(scratch, key);
1587 result = SetOpPropStdclass(tvRef, op, base, key, rhs);
1589 break;
1591 case KindOfStaticString:
1592 case KindOfString: {
1593 if (base->m_data.pstr->size() != 0) {
1594 result = SetOpPropNull(tvScratch);
1595 } else {
1596 initScratchKey<keyType>(scratch, key);
1597 result = SetOpPropStdclass(tvRef, op, base, key, rhs);
1599 break;
1601 case KindOfObject: {
1602 result = SetOpPropObj<keyType>(tvRef, ctx, op, instanceFromTv(base), key,
1603 rhs);
1604 break;
1606 default: {
1607 result = SetOpPropNull(tvScratch);
1608 break;
1611 return result;
1614 template <bool setResult>
1615 static inline void IncDecPropNull(TypedValue& dest) {
1616 raise_warning("Attempt to increment/decrement property of non-object");
1617 if (setResult) {
1618 tvWriteNull(&dest);
1621 template <bool setResult>
1622 static inline void IncDecPropStdclass(unsigned char op, TypedValue* base,
1623 TypedValue* key, TypedValue& dest) {
1624 Instance* obj = createDefaultObject();
1625 obj->incRefCount();
1626 tvRefcountedDecRef(base);
1627 base->m_type = KindOfObject;
1628 base->_count = 0;
1629 base->m_data.pobj = obj;
1631 StringData* keySD = prepareKey(key);
1632 TypedValue tv;
1633 tvWriteNull(&tv);
1634 if (setResult) {
1635 IncDecBody<true>(op, (&tv), &dest);
1636 obj->setProp(NULL, keySD, &dest);
1637 } else {
1638 // The caller doesn't actually want the result set, but we have to do so in
1639 // order to call obj->setProp().
1640 TypedValue tDest;
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,
1663 TypedValue& dest) {
1664 TypedValue scratch;
1665 if (isObj) {
1666 IncDecPropObj<setResult, keyType>(tvRef, ctx, op,
1667 reinterpret_cast<Instance*>(base),
1668 key, dest);
1669 return;
1672 DataType type;
1673 opPre(base, type);
1674 switch (type) {
1675 case KindOfUninit:
1676 case KindOfNull: {
1677 initScratchKey<keyType>(scratch, key);
1678 IncDecPropStdclass<setResult>(op, base, key, dest);
1679 break;
1681 case KindOfBoolean: {
1682 if (base->m_data.num) {
1683 IncDecPropNull<setResult>(dest);
1684 } else {
1685 initScratchKey<keyType>(scratch, key);
1686 IncDecPropStdclass<setResult>(op, base, key, dest);
1688 break;
1690 case KindOfStaticString:
1691 case KindOfString: {
1692 if (base->m_data.pstr->size() != 0) {
1693 IncDecPropNull<setResult>(dest);
1694 } else {
1695 initScratchKey<keyType>(scratch, key);
1696 IncDecPropStdclass<setResult>(op, base, key, dest);
1698 break;
1700 case KindOfObject: {
1701 IncDecPropObj<setResult, keyType>(tvRef, ctx, op, instanceFromTv(base),
1702 key, dest);
1703 break;
1705 default: {
1706 IncDecPropNull<setResult>(dest);
1707 break;
1712 template<bool isObj = false, KeyType keyType = AnyKey>
1713 static inline void UnsetProp(Class* ctx, TypedValue* base,
1714 TypedValue* key) {
1715 Instance* instance;
1716 if (!isObj) {
1717 DataType type;
1718 opPre(base, type);
1719 // Validate base.
1720 if (UNLIKELY(type != KindOfObject)) {
1721 // Do nothing.
1722 return;
1724 instance = instanceFromTv(base);
1725 } else {
1726 instance = reinterpret_cast<Instance*>(base);
1728 // Prepare key.
1729 StringData* keySD = prepareKey<keyType>(key);
1730 // Unset property.
1731 instance->unsetProp(ctx, keySD);
1732 releaseKey<keyType>(keySD);
1735 ///////////////////////////////////////////////////////////////////////////////
1738 #endif // incl_VM_MEMBER_OPERATIONS_H_