static void unserializeProp(VariableUnserializer *uns, ObjectData *obj, const String& key, Class* ctx, const String& realKey, int nProp) { // Do a two-step look up bool visible, accessible, unset; auto t = &tvAsVariant(obj->getProp(ctx, key.get(), visible, accessible, unset)); assert(!unset); if (!t || !accessible) { // Dynamic property. If this is the first, and we're using MixedArray, // we need to pre-allocate space in the array to ensure the elements // dont move during unserialization. // // TODO(#2881866): this assumption means we can't do reallocations // when promoting kPackedKind -> kMixedKind. t = &obj->reserveProperties(nProp).lvalAt(realKey, AccessFlags::Key); } t->unserialize(uns); if (!RuntimeOption::EvalCheckRepoAuthDeserialize) return; if (!RuntimeOption::RepoAuthoritative) return; if (!Repo::get().global().HardPrivatePropInference) return; /* * We assume for performance reasons in repo authoriative mode that * we can see all the sets to private properties in a class. * * It's a hole in this if we don't check unserialization doesn't * violate what we've seen, which we handle by throwing if the repo * was built with this option. */ auto const cls = obj->getVMClass(); auto const slot = cls->lookupDeclProp(key.get()); if (UNLIKELY(slot == kInvalidSlot)) return; auto const repoTy = obj->getVMClass()->declPropRepoAuthType(slot); if (LIKELY(tvMatchesRepoAuthType(*t->asTypedValue(), repoTy))) { return; } auto msg = folly::format( "Property {} for class {} was deserialized with type ({}) that " "didn't match what we inferred in static analysis", key.data(), obj->getVMClass()->name()->data(), tname(t->asTypedValue()->m_type) ).str(); throw Exception(msg); }
std::string getObjectConnectionName( ObjectData* obj, const void* target ) { Class* cls = obj->getVMClass(); FTRACE(5, "HG: Getting connection name for type {} at {}\n", obj->getClassName().data(), obj ); if (!supportsToArray(obj)) { return ""; } auto arr = obj->toArray(); bool is_packed = arr->isPacked(); for (ArrayIter iter(arr); iter; ++iter) { auto first = iter.first(); auto key = first.toString(); auto key_tv = first.asTypedValue(); auto val_tv = iter.secondRef().asTypedValue(); if (key_tv->m_type == HPHP::KindOfString) { // If the key begins with a NUL, it's a private or protected property. // Read the class name from between the two NUL bytes. // // Note: Copied from object-data.cpp if (!key.empty() && key[0] == '\0') { int subLen = key.find('\0', 1) + 1; key = key.substr(subLen); } } FTRACE(5, "HG: ...Iterating over object key-val {}=>{}\n", key, tname(val_tv->m_type) ); // We're only interested in the porperty name that points to our target if ((void*)val_tv->m_data.pobj != target) { continue; } bool is_declared = key_tv->m_type == HPHP::KindOfString && cls->lookupDeclProp(key.get()) != kInvalidSlot; if (!is_declared && !is_packed) { return std::string("Key:" + key); } else if (is_packed) { return std::string("PropertyIndex"); } else { return std::string("Property:" + key); } } return ""; }
void ProxyArray::proxyAppend(void* data, uint32_t data_size, void** dest) { ArrayData * r; if (hasZvalValues()) { assert(data_size == sizeof(void*)); int64_t k = 0; r = innerArr(this)->zAppend(*(RefData**)data, &k); if (dest) { *dest = (void*)(&r->nvGet(k)->m_data.pref); } } else { auto v = Variant(makeElementResource(data, data_size, dest)); r = innerArr(this)->append(*v.asTypedValue(), false); } reseatable(this, r); }
ArrayData* StructArray::MakeUncounted(ArrayData* array) { auto structArray = asStructArray(array); // We don't need to copy the full capacity, since the array won't // change once it's uncounted. auto size = structArray->size(); StructArray* result = createUncounted(structArray->shape(), size); result->m_hdr.init(HeaderKind::Struct, UncountedValue); result->m_sizeAndPos = array->m_sizeAndPos; auto const srcData = structArray->data(); auto const stop = srcData + size; auto targetData = result->data(); for (auto ptr = srcData; ptr != stop; ++ptr, ++targetData) { auto srcVariant = MixedArray::CreateVarForUncountedArray(tvAsCVarRef(ptr)); tvCopy(*srcVariant.asTypedValue(), *targetData); } assert(result->m_pos == structArray->m_pos); assert(result->isUncounted()); return result; }
Object APCObject::createObject() const { Object obj; const Class* klass; if (auto const c = m_cls.left()) { klass = c; } else { klass = Unit::loadClass(m_cls.right()); if (!klass) { Logger::Error("APCObject::getObject(): Cannot find class %s", m_cls.right()->data()); return obj; } } obj = ObjectData::newInstance(const_cast<Class*>(klass)); obj.get()->clearNoDestruct(); auto prop = props(); auto const propEnd = prop + m_propCount; for (; prop != propEnd; ++prop) { auto const key = prop->name; const Class* ctx = nullptr; if (prop->ctx.isNull()) { ctx = klass; } else { if (auto const cls = prop->ctx.left()) { ctx = cls; } else { ctx = Unit::lookupClass(prop->ctx.right()); if (!ctx) continue; } } auto val = prop->val ? prop->val->toLocal() : init_null(); obj->setProp(const_cast<Class*>(ctx), key, val.asTypedValue(), false); } obj->invokeWakeup(); return obj; }
Variant::~Variant() noexcept { tvRefcountedDecRef(asTypedValue()); if (debug) { memset(this, kTVTrashFill2, sizeof(*this)); } }
int Variant::getRefCount() const noexcept { return isRefcountedType(m_type) ? tvGetCount(asTypedValue()) : 1; }