void DataWalker::traverseData(ArrayData* data, DataFeature& features, PointerSet& visited) const { for (ArrayIter iter(data); iter; ++iter) { const Variant& var = iter.secondRef(); if (var.isReferenced()) { Variant *pvar = var.getRefData(); if (markVisited(pvar, features, visited)) { if (canStopWalk(features)) return; continue; // don't recurse forever; we already went down this path } // Right now consider it circular even if the referenced variant only // showed up in one spot. This could be revisted later. features.isCircular = true; if (canStopWalk(features)) return; } auto const type = var.getType(); // cheap enough, do it always features.hasRefCountReference = isRefcountedType(type); if (type == KindOfObject) { features.hasObjectOrResource = true; traverseData(var.getObjectData(), features, visited); } else if (isArrayType(type)) { traverseData(var.getArrayData(), features, visited); } else if (type == KindOfResource) { features.hasObjectOrResource = true; } if (canStopWalk(features)) return; } }
bool tvDecRefWillRelease(TypedValue* tv) { if (!isRefcountedType(tv->m_type)) { return false; } if (tv->m_type == KindOfRef) { return tv->m_data.pref->getRealCount() <= 1; } return TV_GENERIC_DISPATCH(*tv, decWillRelease); }
ArrayData* deepCopyDict(ArrayData* arr) { assert(arr->isDict()); Array ar(arr); MixedArray::IterateKV( MixedArray::asMixed(arr), [&](const TypedValue* k, const TypedValue* v) { if (!isRefcountedType(v->m_type)) return false; Variant value{tvAsCVarRef(v)}; deepCopy(value.asTypedValue()); if (value.asTypedValue()->m_data.num != v->m_data.num) { ar.set(tvAsCVarRef(k), value); } return false; } ); return ar.detach(); }
ArrayData* deepCopyVecArray(ArrayData* arr) { assert(arr->isVecArray()); Array ar(arr); PackedArray::IterateKV( arr, [&](const TypedValue* k, const TypedValue* v) { if (!isRefcountedType(v->m_type)) return false; Variant value{tvAsCVarRef(v)}; deepCopy(value.asTypedValue()); if (value.asTypedValue()->m_data.num != v->m_data.num) { assert(k->m_type == KindOfInt64); ar.set(k->m_data.num, value); } return false; } ); return ar.detach(); }
bool ConcurrentTableSharedStore::constructPrime(const Variant& v, KeyValuePair& item) { if (s_apc_file_storage.getState() != APCFileStorage::StorageState::Invalid && (isRefcountedType(v.getType()))) { // Only do the storage for ref-counted type String s = apc_serialize(v); char *sAddr = s_apc_file_storage.put(s.data(), s.size()); if (sAddr) { item.sAddr = sAddr; item.sSize = s.size(); return false; } } auto pair = APCHandle::Create(v, false, APCHandleLevel::Outer, false); item.value = pair.handle; item.sSize = pair.size; return true; }
void RepoQuery::getTypedValue(int iCol, TypedValue& tv) { const void* blob; size_t size; getBlob(iCol, blob, size); tvWriteUninit(&tv); if (size > 0) { String s = String((const char*)blob, size, CopyString); Variant v = unserialize_from_string(s); if (v.isString()) { v = String(makeStaticString(v.asCStrRef().get())); } else if (v.isArray()) { v = Array(ArrayData::GetScalarArray(v.asCArrRef().get())); } else { // Serialized variants and objects shouldn't ever make it into the repo. assert(!isRefcountedType(v.getType())); } tvAsVariant(&tv) = v; } }
ssize_t APCArray::indexOf(const StringData* key) const { strhash_t h = key->hash(); ssize_t bucket = hash()[h & m.m_capacity_mask]; Bucket* b = buckets(); while (bucket != -1) { if (!isRefcountedType(b[bucket].key->type())) { auto const k = APCTypedValue::fromHandle(b[bucket].key); if (b[bucket].key->type() != KindOfInt64 && key->same(k->getStringData())) { return bucket; } } else { assert(b[bucket].key->type() == KindOfString); auto const k = APCString::fromHandle(b[bucket].key); if (key->same(k->getStringData())) { return bucket; } } bucket = b[bucket].next; } return -1; }
void APCArray::add(APCHandle *key, APCHandle *val) { int pos = m.m_num; // NOTE: no check on duplication because we assume the original array has no // duplication Bucket* bucket = buckets() + pos; bucket->key = key; bucket->val = val; m.m_num++; int hash_pos; if (!isRefcountedType(key->type())) { auto const k = APCTypedValue::fromHandle(key); hash_pos = (key->type() == KindOfInt64 ? k->getInt64() : k->getStringData()->hash()) & m.m_capacity_mask; } else { assert(key->type() == KindOfString); auto const k = APCString::fromHandle(key); hash_pos = k->getStringData()->hash() & m.m_capacity_mask; } int& hp = hash()[hash_pos]; bucket->next = hp; hp = pos; }
int Variant::getRefCount() const noexcept { return isRefcountedType(m_type) ? tvGetCount(asTypedValue()) : 1; }
Variant& Variant::setWithRef(const Variant& v) noexcept { setWithRefHelper(v, isRefcountedType(m_type)); return *this; }
bool tvMatchesRepoAuthType(TypedValue tv, RepoAuthType ty) { assert(tvIsPlausible(tv)); bool const initNull = tv.m_type == KindOfNull; using T = RepoAuthType::Tag; switch (ty.tag()) { case T::Uninit: return tv.m_type == KindOfUninit; case T::InitNull: return initNull; case T::OptBool: if (initNull) return true; // fallthrough case T::Bool: return tv.m_type == KindOfBoolean; case T::OptInt: if (initNull) return true; // fallthrough case T::Int: return tv.m_type == KindOfInt64; case T::OptDbl: if (initNull) return true; // fallthrough case T::Dbl: return tv.m_type == KindOfDouble; case T::OptRes: if (initNull) return true; // fallthrough case T::Res: return tv.m_type == KindOfResource; case T::OptObj: if (initNull) return true; // fallthrough case T::Obj: return tv.m_type == KindOfObject; case T::OptSStr: if (initNull) return true; // fallthrough case T::SStr: return isStringType(tv.m_type) && tv.m_data.pstr->isStatic(); case T::OptStr: if (initNull) return true; // fallthrough case T::Str: return isStringType(tv.m_type); case T::OptSArr: if (initNull) return true; // fallthrough case T::SArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptArr: if (initNull) return true; // fallthrough case T::Arr: if (!isArrayType(tv.m_type)) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVArr: if (initNull) return true; // fallthrough case T::SVArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isVArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptVArr: if (initNull) return true; // fallthrough case T::VArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isVArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSDArr: if (initNull) return true; // fallthrough case T::SDArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isStatic() || !tv.m_data.parr->isDArray()) { return false; } if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptDArr: if (initNull) return true; // fallthrough case T::DArr: if (!isArrayType(tv.m_type) || !tv.m_data.parr->isDArray()) return false; if (auto const arr = ty.array()) { if (!tvMatchesArrayType(tv, arr)) return false; } return true; case T::OptSVec: if (initNull) return true; // fallthrough case T::SVec: return isVecType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptVec: if (initNull) return true; // fallthrough case T::Vec: return isVecType(tv.m_type); case T::OptSDict: if (initNull) return true; // fallthrough case T::SDict: return isDictType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptDict: if (initNull) return true; // fallthrough case T::Dict: return isDictType(tv.m_type); case T::OptSKeyset: if (initNull) return true; // fallthrough case T::SKeyset: return isKeysetType(tv.m_type) && tv.m_data.parr->isStatic(); case T::OptKeyset: if (initNull) return true; // fallthrough case T::Keyset: return isKeysetType(tv.m_type); case T::Null: return initNull || tv.m_type == KindOfUninit; case T::OptSubObj: if (initNull) return true; // fallthrough case T::SubObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass()->classof(cls); } case T::OptExactObj: if (initNull) return true; // fallthrough case T::ExactObj: { auto const cls = Unit::lookupClass(ty.clsName()); if (!cls) return false; return tv.m_type == KindOfObject && tv.m_data.pobj->getVMClass() == cls; } case T::InitUnc: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Unc: return !isRefcountedType(tv.m_type) || (tv.m_type == KindOfString && tv.m_data.pstr->isStatic()) || (isArrayLikeType(tv.m_type) && tv.m_data.parr->isStatic()); case T::OptArrKey: if (initNull) return true; // fallthrough case T::ArrKey: return isStringType(tv.m_type) || tv.m_type == KindOfInt64; case T::OptUncArrKey: if (initNull) return true; // fallthrough case T::UncArrKey: return (isStringType(tv.m_type) && !tv.m_data.pstr->isRefCounted()) || tv.m_type == KindOfInt64; case T::InitCell: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Cell: return tv.m_type != KindOfRef; case T::Ref: return tv.m_type == KindOfRef; case T::InitGen: if (tv.m_type == KindOfUninit) return false; // fallthrough case T::Gen: return true; } not_reached(); }