ArrayData* StructArray::SetStr( ArrayData* ad, StringData* k, Cell v, bool copy ) { auto structArray = asStructArray(ad); auto shape = structArray->shape(); auto result = structArray; auto offset = shape->offsetFor(k); bool isNewProperty = offset == PropertyTable::kInvalidOffset; auto convertToMixedAndAdd = [&]() { auto mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixed->addValNoAsserts(k, v); }; if (isNewProperty) { StringData* staticKey; // We don't support adding non-static strings yet. if (k->isStatic()) { staticKey = k; } else { staticKey = lookupStaticString(k); if (!staticKey) return convertToMixedAndAdd(); } auto newShape = shape->transition(staticKey); if (!newShape) return convertToMixedAndAdd(); result = copy ? CopyAndResizeIfNeeded(structArray, newShape) : ResizeIfNeeded(structArray, newShape); offset = result->shape()->offsetFor(staticKey); assert(offset != PropertyTable::kInvalidOffset); TypedValue* dst = &result->data()[offset]; // TODO(#3888164): we should restructure things so we don't have to // check KindOfUninit here. if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>(); cellDup(v, *dst); return result; } if (copy) { result = asStructArray(Copy(structArray)); } assert(offset != PropertyTable::kInvalidOffset); TypedValue* dst = &result->data()[offset]; if (UNLIKELY(v.m_type == KindOfUninit)) v = make_tv<KindOfNull>(); cellSet(v, *tvToCell(dst)); return result; }
ArrayData* StructArray::LvalStr( ArrayData* ad, StringData* property, Variant*& ret, bool copy ) { auto structArray = asStructArray(ad); auto shape = structArray->shape(); auto offset = shape->offsetFor(property); if (offset != PropertyTable::kInvalidOffset) { auto const result = asStructArray( copy ? Copy(structArray) : structArray); ret = &tvAsVariant(&result->data()[offset]); return result; } auto convertToMixedAndAdd = [&]() { auto mixed = copy ? ToMixedCopy(structArray) : ToMixed(structArray); return mixed->addLvalImpl(property, ret); }; // We don't support adding non-static strings yet. StringData* staticKey; if (property->isStatic()) { staticKey = property; } else { staticKey = lookupStaticString(property); if (!staticKey) return convertToMixedAndAdd(); } auto newShape = shape->transition(staticKey); if (!newShape) return convertToMixedAndAdd(); auto result = copy ? CopyAndResizeIfNeeded(structArray, newShape) : ResizeIfNeeded(structArray, newShape); assert(newShape->hasOffsetFor(staticKey)); offset = newShape->offsetFor(staticKey); tvWriteNull(&result->data()[offset]); ret = &tvAsVariant(&result->data()[offset]); return result; }
APCHandle::Pair APCHandle::Create(const Variant& source, bool serialized, APCHandleLevel level, bool unserializeObj) { auto type = source.getType(); // this gets rid of the ref, if it was one switch (type) { case KindOfUninit: { auto value = APCTypedValue::tvUninit(); return {value->getHandle(), sizeof(APCTypedValue)}; } case KindOfNull: { auto value = APCTypedValue::tvNull(); return {value->getHandle(), sizeof(APCTypedValue)}; } case KindOfBoolean: { auto value = source.getBoolean() ? APCTypedValue::tvTrue() : APCTypedValue::tvFalse(); return {value->getHandle(), sizeof(APCTypedValue)}; } case KindOfInt64: { auto value = new APCTypedValue(source.getInt64()); return {value->getHandle(), sizeof(APCTypedValue)}; } case KindOfDouble: { auto value = new APCTypedValue(source.getDouble()); return {value->getHandle(), sizeof(APCTypedValue)}; } case KindOfPersistentString: case KindOfString: { StringData* s = source.getStringData(); if (serialized) { // It is priming, and there might not be the right class definitions // for unserialization. return APCString::MakeSerializedObject(apc_reserialize(String{s})); } if (s->isStatic()) { auto value = new APCTypedValue(APCTypedValue::StaticStr{}, s); return APCHandle::Pair{value->getHandle(), sizeof(APCTypedValue)}; } auto const st = lookupStaticString(s); if (st) { auto value = new APCTypedValue(APCTypedValue::StaticStr{}, st); return {value->getHandle(), sizeof(APCTypedValue)}; } if (apcExtension::UseUncounted) { auto st = StringData::MakeUncounted(s->slice()); auto value = new APCTypedValue(APCTypedValue::UncountedStr{}, st); return {value->getHandle(), st->size() + sizeof(APCTypedValue)}; } return APCString::MakeSharedString(s); } case KindOfPersistentVec: case KindOfVec: { auto ad = source.getArrayData(); assert(ad->isVecArray()); if (ad->isStatic()) { auto value = new APCTypedValue(APCTypedValue::StaticVec{}, ad); return {value->getHandle(), sizeof(APCTypedValue)}; } return APCArray::MakeSharedVec(ad, level, unserializeObj); } case KindOfPersistentDict: case KindOfDict: { auto ad = source.getArrayData(); assert(ad->isDict()); if (ad->isStatic()) { auto value = new APCTypedValue(APCTypedValue::StaticDict{}, ad); return {value->getHandle(), sizeof(APCTypedValue)}; } return APCArray::MakeSharedDict(ad, level, unserializeObj); } case KindOfPersistentKeyset: case KindOfKeyset: { auto ad = source.getArrayData(); assert(ad->isKeyset()); if (ad->isStatic()) { auto value = new APCTypedValue(APCTypedValue::StaticKeyset{}, ad); return {value->getHandle(), sizeof(APCTypedValue)}; } return APCArray::MakeSharedKeyset(ad, level, unserializeObj); } case KindOfPersistentArray: case KindOfArray: { auto ad = source.getArrayData(); assert(ad->isPHPArray()); if (ad->isStatic()) { auto value = new APCTypedValue(APCTypedValue::StaticArr{}, ad); return {value->getHandle(), sizeof(APCTypedValue)}; } return APCArray::MakeSharedArray(ad, level, unserializeObj); } case KindOfObject: if (source.getObjectData()->isCollection()) { return APCCollection::Make(source.getObjectData(), level, unserializeObj); } return unserializeObj ? APCObject::Construct(source.getObjectData()) : APCString::MakeSerializedObject(apc_serialize(source)); case KindOfResource: // TODO Task #2661075: Here and elsewhere in the runtime, we convert // Resources to the empty array during various serialization operations, // which does not match Zend behavior. We should fix this. return APCArray::MakeSharedEmptyArray(); case KindOfRef: case KindOfClass: return {nullptr, 0}; } not_reached(); }
APCHandle* APCHandle::Create(const Variant& source, size_t& size, bool serialized, bool inner /* = false */, bool unserializeObj /* = false */) { auto type = source.getType(); // this gets rid of the ref, if it was one switch (type) { case KindOfBoolean: { auto value = new APCTypedValue(type, static_cast<int64_t>(source.getBoolean())); size = sizeof(APCTypedValue); return value->getHandle(); } case KindOfInt64: { auto value = new APCTypedValue(type, source.getInt64()); size = sizeof(APCTypedValue); return value->getHandle(); } case KindOfDouble: { auto value = new APCTypedValue(type, source.getDouble()); size = sizeof(APCTypedValue); return value->getHandle(); } case KindOfUninit: case KindOfNull: { auto value = new APCTypedValue(type); size = sizeof(APCTypedValue); return value->getHandle(); } case KindOfStaticString: { if (serialized) goto StringCase; auto value = new APCTypedValue(type, source.getStringData()); size = sizeof(APCTypedValue); return value->getHandle(); } StringCase: case KindOfString: { StringData* s = source.getStringData(); if (serialized) { // It is priming, and there might not be the right class definitions // for unserialization. return APCObject::MakeShared(apc_reserialize(s), size); } auto const st = lookupStaticString(s); if (st) { APCTypedValue* value = new APCTypedValue(KindOfStaticString, st); size = sizeof(APCTypedValue); return value->getHandle(); } assert(!s->isStatic()); // would've been handled above if (!inner && apcExtension::UseUncounted) { StringData* st = StringData::MakeUncounted(s->slice()); APCTypedValue* value = new APCTypedValue(st); size = sizeof(APCTypedValue) + st->size(); return value->getHandle(); } return APCString::MakeShared(type, s, size); } case KindOfArray: return APCArray::MakeShared(source.getArrayData(), size, inner, unserializeObj); case KindOfResource: // TODO Task #2661075: Here and elsewhere in the runtime, we convert // Resources to the empty array during various serialization operations, // which does not match Zend behavior. We should fix this. size = sizeof(APCArray); return APCArray::MakeShared(); case KindOfObject: return unserializeObj ? APCObject::Construct(source.getObjectData(), size) : APCObject::MakeShared(apc_serialize(source), size); default: return nullptr; } }