bool cellLessOrEqual(Cell c1, Cell c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); if ((c1.m_type == KindOfArray && c2.m_type == KindOfArray) || (c1.m_type == KindOfObject && c2.m_type == KindOfObject)) { return cellLess(c1, c2) || cellEqual(c1, c2); } return !cellGreater(c1, c2); }
bool cellGreaterOrEqual(const Cell* c1, const Cell* c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); if ((c1->m_type == KindOfArray && c2->m_type == KindOfArray) || (c1->m_type == KindOfObject && c2->m_type == KindOfObject)) { return cellGreater(c1, c2) || cellEqual(c1, c2); } return !cellLess(c1, c2); }
bool cellGreaterOrEqual(Cell c1, Cell c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); if ((c1.m_type == KindOfArray && c2.m_type == KindOfArray) || (c1.m_type == KindOfObject && c2.m_type == KindOfObject) || (c1.m_type == KindOfResource && c2.m_type == KindOfResource)) { return cellGreater(c1, c2) || cellEqual(c1, c2); } if ((c1.m_type == KindOfDouble && std::isnan(c1.m_data.dbl)) || (c2.m_type == KindOfDouble && std::isnan(c2.m_data.dbl))) { return cellGreater(c1, c2) || cellEqual(c1, c2); } return !cellLess(c1, c2); }
// Helper for converting String, Array, Bool, Null or Obj to Dbl|Int. // Other types (i.e. Int and Double) must be handled outside of this. TypedNum numericConvHelper(Cell cell) { assert(cellIsPlausible(cell)); switch (cell.m_type) { case KindOfUninit: case KindOfNull: return make_int(0); case KindOfBoolean: return make_int(cell.m_data.num); case KindOfString: case KindOfStaticString: return stringToNumeric(cell.m_data.pstr); case KindOfArray: throw_bad_array_operand(); case KindOfObject: return make_int(cell.m_data.pobj->o_toInt64()); case KindOfResource: return make_int(cell.m_data.pres->o_toInt64()); case KindOfInt64: case KindOfDouble: case KindOfRef: case KindOfClass: break; } not_reached(); }
void tvCastToResourceInPlace(TypedValue* tv) { assert(tvIsPlausible(*tv)); tvUnboxIfNeeded(tv); do { switch (tv->m_type) { DT_UNCOUNTED_CASE: continue; case KindOfString: case KindOfVec: case KindOfDict: case KindOfKeyset: case KindOfArray: case KindOfObject: tvDecRef(tv); continue; case KindOfResource: // no op, return return; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); tv->m_type = KindOfResource; tv->m_data.pres = req::make<DummyResource>().detach()->hdr(); assert(cellIsPlausible(*tv)); }
void c_ExternalThreadEventWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { m_event->unserialize(result); } catch (const Object& exception) { setException(exception.get()); return; } catch (...) { setException(AsioSession::Get()->getAbruptInterruptException().get()); throw; } assert(cellIsPlausible(result)); setResult(result); tvRefcountedDecRefCell(&result); }
void cellCastToInt64InPlace(Cell* cell) { assert(cellIsPlausible(*cell)); int64_t i; do { switch (cell->m_type) { case KindOfUninit: case KindOfNull: cell->m_data.num = 0LL; // fallthru case KindOfBoolean: assert(cell->m_data.num == 0LL || cell->m_data.num == 1LL); cell->m_type = KindOfInt64; // fallthru case KindOfInt64: return; case KindOfDouble: i = toInt64(cell->m_data.dbl); continue; case KindOfPersistentString: i = cell->m_data.pstr->toInt64(); continue; case KindOfString: i = cell->m_data.pstr->toInt64(); tvDecRefStr(cell); continue; case KindOfPersistentArray: i = cell->m_data.parr->empty() ? 0 : 1; continue; case KindOfArray: i = cell->m_data.parr->empty() ? 0 : 1; tvDecRefArr(cell); continue; case KindOfObject: i = cell->m_data.pobj->toInt64(); tvDecRefObj(cell); continue; case KindOfResource: i = cell->m_data.pres->data()->o_toInt64(); tvDecRefRes(cell); continue; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); cell->m_data.num = i; cell->m_type = KindOfInt64; }
bool cellSame(Cell c1, Cell c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); bool const null1 = isNullType(c1.m_type); bool const null2 = isNullType(c2.m_type); if (null1 && null2) return true; if (null1 || null2) return false; switch (c1.m_type) { case KindOfBoolean: case KindOfInt64: if (c2.m_type != c1.m_type) return false; return c1.m_data.num == c2.m_data.num; case KindOfDouble: if (c2.m_type != c1.m_type) return false; return c1.m_data.dbl == c2.m_data.dbl; case KindOfPersistentString: case KindOfString: if (!isStringType(c2.m_type)) return false; return c1.m_data.pstr->same(c2.m_data.pstr); case KindOfPersistentArray: case KindOfArray: if (!isArrayType(c2.m_type)) return false; return c1.m_data.parr->equal(c2.m_data.parr, true); case KindOfObject: return c2.m_type == KindOfObject && c1.m_data.pobj == c2.m_data.pobj; case KindOfResource: return c2.m_type == KindOfResource && c1.m_data.pres == c2.m_data.pres; case KindOfUninit: case KindOfNull: case KindOfRef: case KindOfClass: break; } not_reached(); }
bool tvIsPlausible(TypedValue tv) { if (tv.m_type == KindOfRef) { assert(tv.m_data.pref); assert(uintptr_t(tv.m_data.pref) % sizeof(void*) == 0); assert(check_refcount(tv.m_data.pref->getRealCount())); tv = *tv.m_data.pref->tv(); } return cellIsPlausible(tv); }
bool cellLessOrEqual(Cell c1, Cell c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); if ((c1.m_type == KindOfArray && c2.m_type == KindOfArray) || (c1.m_type == KindOfObject && c2.m_type == KindOfObject) || (c1.m_type == KindOfResource && c2.m_type == KindOfResource)) { return cellLess(c1, c2) || cellEqual(c1, c2); } // We have to treat NaN specially: NAN <= NAN is false, for example, so we // can't just say !(NAN > NAN). if ((c1.m_type == KindOfDouble && std::isnan(c1.m_data.dbl)) || (c2.m_type == KindOfDouble && std::isnan(c2.m_data.dbl))) { return cellLess(c1, c2) || cellEqual(c1, c2); } return !cellGreater(c1, c2); }
void c_WaitableWaitHandle::done() { assert(isFinished()); assert(cellIsPlausible(m_resultOrException)); // unblock parents while (m_firstParent) { m_firstParent = m_firstParent->unblock(); } }
bool tvIsPlausible(const TypedValue* tv) { assert(tv); if (tv->m_type == KindOfRef) { assert(tv->m_data.pref); assert(uintptr_t(tv->m_data.pref) % sizeof(void*) == 0); assert(is_refcount_realistic(tv->m_data.pref->getCount())); tv = tv->m_data.pref->tv(); } return cellIsPlausible(tv); }
bool tvIsPlausible(TypedValue tv) { if (isRefType(tv.m_type)) { assertx(tv.m_data.pref); assertx(uintptr_t(tv.m_data.pref) % sizeof(void*) == 0); assertx(tv.m_data.pref->kindIsValid()); assertx(tv.m_data.pref->checkCountZ()); tv = *tv.m_data.pref->cell(); } return cellIsPlausible(tv); }
void tvCastToObjectInPlace(TypedValue* tv) { assert(tvIsPlausible(*tv)); tvUnboxIfNeeded(tv); ObjectData* o; do { switch (tv->m_type) { case KindOfUninit: case KindOfNull: o = SystemLib::AllocStdClassObject().detach(); continue; case KindOfBoolean: case KindOfInt64: case KindOfDouble: case KindOfPersistentString: case KindOfResource: o = SystemLib::AllocStdClassObject().detach(); o->o_set(s_scalar, tvAsVariant(tv)); continue; case KindOfString: o = SystemLib::AllocStdClassObject().detach(); o->o_set(s_scalar, tvAsVariant(tv)); tvDecRefStr(tv); continue; case KindOfPersistentVec: case KindOfVec: case KindOfPersistentDict: case KindOfDict: case KindOfPersistentKeyset: case KindOfKeyset: tvCastToArrayInPlace(tv); // Fall-through to array case case KindOfPersistentArray: case KindOfArray: // For arrays, we fall back on the Variant machinery tvAsVariant(tv) = ObjectData::FromArray(tv->m_data.parr); return; case KindOfObject: return; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); tv->m_data.pobj = o; tv->m_type = KindOfObject; assert(cellIsPlausible(*tv)); }
bool APCLocalArray::checkInvariants(const ArrayData* ad) { assert(ad->isApcArray()); assert(ad->checkCount()); DEBUG_ONLY auto const local = static_cast<const APCLocalArray*>(ad); DEBUG_ONLY auto p = local->localCache(); for (auto end = p + local->getSize(); p < end; ++p) { // Elements in the local cache must not be KindOfRef. assert(cellIsPlausible(*p)); } return true; }
bool cellSame(Cell c1, Cell c2) { assert(cellIsPlausible(c1)); assert(cellIsPlausible(c2)); bool const null1 = IS_NULL_TYPE(c1.m_type); bool const null2 = IS_NULL_TYPE(c2.m_type); if (null1 && null2) return true; if (null1 || null2) return false; switch (c1.m_type) { case KindOfInt64: case KindOfBoolean: if (c2.m_type != c1.m_type) return false; return c1.m_data.num == c2.m_data.num; case KindOfDouble: if (c2.m_type != c1.m_type) return false; return c1.m_data.dbl == c2.m_data.dbl; case KindOfStaticString: case KindOfString: if (!IS_STRING_TYPE(c2.m_type)) return false; return c1.m_data.pstr->same(c2.m_data.pstr); case KindOfArray: if (c2.m_type != KindOfArray) return false; return c1.m_data.parr->equal(c2.m_data.parr, true); case KindOfObject: return c2.m_type == KindOfObject && c1.m_data.pobj == c2.m_data.pobj; case KindOfResource: return c2.m_type == KindOfResource && c1.m_data.pres == c2.m_data.pres; default: break; } not_reached(); }
bool APCLocalArray::checkInvariants(const ArrayData* ad) { assert(ad->isApcArray()); DEBUG_ONLY auto const shared = static_cast<const APCLocalArray*>(ad); if (auto ptr = shared->m_localCache) { auto const cap = shared->m_arr->capacity(); auto const stop = ptr + cap; for (; ptr != stop; ++ptr) { // Elements in the local cache must not be KindOfRef. assert(cellIsPlausible(*ptr)); } } return true; }
void incDecBodySlow(IncDecOp op, Cell* fr, TypedValue* to) { assert(cellIsPlausible(*fr)); assert(fr->m_type != KindOfUninit); auto dup = [&]() { cellDup(*fr, *to); }; switch (op) { case IncDecOp::PreInc: cellInc(*fr); dup(); return; case IncDecOp::PostInc: dup(); cellInc(*fr); return; case IncDecOp::PreDec: cellDec(*fr); dup(); return; case IncDecOp::PostDec: dup(); cellDec(*fr); return; default: break; } switch (op) { case IncDecOp::PreIncO: cellIncO(*fr); dup(); return; case IncDecOp::PostIncO: dup(); cellIncO(*fr); return; case IncDecOp::PreDecO: cellDecO(*fr); dup(); return; case IncDecOp::PostDecO: dup(); cellDecO(*fr); return; default: break; } not_reached(); }
Cell incDecBodySlow(IncDecOp op, Cell* fr) { assert(cellIsPlausible(*fr)); assert(fr->m_type != KindOfUninit); auto dup = [&]() { tvRefcountedIncRef(fr); return *fr; }; switch (op) { case IncDecOp::PreInc: cellInc(*fr); return dup(); case IncDecOp::PostInc: { auto const tmp = dup(); cellInc(*fr); return tmp; } case IncDecOp::PreDec: cellDec(*fr); return dup(); case IncDecOp::PostDec: { auto const tmp = dup(); cellDec(*fr); return tmp; } default: break; } switch (op) { case IncDecOp::PreIncO: cellIncO(*fr); return dup(); case IncDecOp::PostIncO: { auto const tmp = dup(); cellIncO(*fr); return tmp; } case IncDecOp::PreDecO: cellDecO(*fr); return dup(); case IncDecOp::PostDecO: { auto const tmp = dup(); cellDecO(*fr); return tmp; } default: break; } not_reached(); }
void c_WaitableWaitHandle::setResult(const Cell& result) { assert(cellIsPlausible(result)); setState(STATE_SUCCEEDED); cellDup(result, m_resultOrException); // unref creator if (m_creator) { decRefObj(m_creator); m_creator = nullptr; } // unblock parents while (m_firstParent) { m_firstParent = m_firstParent->unblock(); } }
void c_ExternalThreadEventWaitHandle::process() { assert(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { m_event->unserialize(result); } catch (const Object& exception) { assert(exception->instanceof(SystemLib::s_ExceptionClass)); auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventFailCallback())) { session->onExternalThreadEventFail(this, exception); } return; } catch (...) { auto const parentChain = getFirstParent(); setState(STATE_FAILED); tvWriteObject(AsioSession::Get()->getAbruptInterruptException(), &m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); throw; } assert(cellIsPlausible(result)); auto const parentChain = getFirstParent(); setState(STATE_SUCCEEDED); cellCopy(result, m_resultOrException); c_BlockableWaitHandle::UnblockChain(parentChain); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventSuccessCallback())) { session->onExternalThreadEventSuccess(this, tvAsCVarRef(&result)); } }
void cellCastToInt64InPlace(Cell* cell) { assert(cellIsPlausible(*cell)); int64_t i; switch (cell->m_type) { case KindOfUninit: case KindOfNull: cell->m_data.num = 0LL; // Fall through case KindOfBoolean: assert(cell->m_data.num == 0LL || cell->m_data.num == 1LL); cell->m_type = KindOfInt64; // Fall through case KindOfInt64: return; case KindOfDouble: { i = toInt64(cell->m_data.dbl); break; } case KindOfStaticString: i = (cell->m_data.pstr->toInt64()); break; case KindOfString: { i = cell->m_data.pstr->toInt64(); tvDecRefStr(cell); break; } case KindOfArray: i = cell->m_data.parr->empty() ? 0 : 1; tvDecRefArr(cell); break; case KindOfObject: i = cell->m_data.pobj->o_toInt64(); tvDecRefObj(cell); break; case KindOfResource: i = cell->m_data.pres->o_toInt64(); tvDecRefRes(cell); break; default: not_reached(); } cell->m_data.num = i; cell->m_type = KindOfInt64; }
void tvCastToKeysetInPlace(TypedValue* tv) { assert(tvIsPlausible(*tv)); tvUnboxIfNeeded(tv); ArrayData* a; do { switch (tv->m_type) { case KindOfUninit: case KindOfNull: raise_warning("Null to keyset conversion"); a = staticEmptyKeysetArray(); continue; case KindOfBoolean: raise_warning("Bool to keyset conversion"); a = staticEmptyKeysetArray(); continue; case KindOfInt64: raise_warning("Int to keyset conversion"); a = staticEmptyKeysetArray(); continue; case KindOfDouble: raise_warning("Double to keyset conversion"); a = staticEmptyKeysetArray(); continue; case KindOfPersistentString: case KindOfString: raise_warning("String to keyset conversion"); a = staticEmptyKeysetArray(); decRefStr(tv->m_data.pstr); continue; case KindOfResource: raise_warning("Resource to keyset conversion"); a = staticEmptyKeysetArray(); decRefRes(tv->m_data.pres); continue; case KindOfPersistentVec: case KindOfVec: { auto* adIn = tv->m_data.parr; assert(adIn->isVecArray()); a = PackedArray::ToKeysetVec(adIn, adIn->cowCheck()); assert(a != adIn); decRefArr(adIn); continue; } case KindOfPersistentDict: case KindOfDict: { auto* adIn = tv->m_data.parr; assert(adIn->isDict()); a = MixedArray::ToKeysetDict(adIn, adIn->cowCheck()); if (a != adIn) decRefArr(adIn); continue; } case KindOfPersistentArray: case KindOfArray: { auto* adIn = tv->m_data.parr; assert(adIn->isPHPArray()); a = adIn->toKeyset(adIn->cowCheck()); if (a != adIn) decRefArr(adIn); continue; } case KindOfPersistentKeyset: case KindOfKeyset: assert(tv->m_data.parr->isKeyset()); return; case KindOfObject: { auto* obj = tv->m_data.pobj; if (!obj->isCollection()) { raise_warning("Non-collection object conversion to keyset"); a = staticEmptyKeysetArray(); } else { auto keyset = collections::toArray(obj).toKeyset(); decRefObj(obj); a = keyset.detach(); } continue; } case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); assert(!a->isRefCounted() || a->hasExactlyOneRef()); tv->m_data.parr = a; tv->m_type = KindOfKeyset; assert(cellIsPlausible(*tv)); }
void c_ExternalThreadEventWaitHandle::process() { assertx(getState() == STATE_WAITING); if (isInContext()) { unregisterFromContext(); } // Store the finish time of the underlying IO operation // So we can pass it in the finish callbacks // clean up once event is processed auto exit_guard = folly::makeGuard([&] { destroyEvent(); }); Cell result; try { try { m_event->unserialize(result); } catch (ExtendedException& exception) { exception.recomputeBacktraceFromWH(this); throw exception; } } catch (const Object& exception) { assertx(exception->instanceof(SystemLib::s_ThrowableClass)); throwable_recompute_backtrace_from_wh(exception.get(), this); auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(exception.get(), &m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventFail())) { session->onExternalThreadEventFail( this, exception, std::chrono::duration_cast<std::chrono::nanoseconds>( m_event->getFinishTime().time_since_epoch() ).count() ); } return; } catch (...) { auto parentChain = getParentChain(); setState(STATE_FAILED); tvWriteObject(AsioSession::Get()->getAbruptInterruptException(), &m_resultOrException); parentChain.unblock(); throw; } assertx(cellIsPlausible(result)); auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); cellCopy(result, m_resultOrException); parentChain.unblock(); auto session = AsioSession::Get(); if (UNLIKELY(session->hasOnExternalThreadEventSuccess())) { session->onExternalThreadEventSuccess( this, tvAsCVarRef(&result), std::chrono::duration_cast<std::chrono::nanoseconds>( m_event->getFinishTime().time_since_epoch() ).count() ); } }
void Variant::setEvalScalar() { assertx(cellIsPlausible(*this)); auto const do_array = [this]{ auto parr = m_data.parr; if (!parr->isStatic()) { auto ad = ArrayData::GetScalarArray(parr); assert(ad->isStatic()); m_data.parr = ad; decRefArr(parr); } }; switch (m_type) { case KindOfUninit: case KindOfNull: case KindOfBoolean: case KindOfInt64: case KindOfDouble: return; case KindOfString: m_type = KindOfPersistentString; case KindOfPersistentString: { auto pstr = m_data.pstr; if (!pstr->isStatic()) { StringData *sd = makeStaticString(pstr); decRefStr(pstr); m_data.pstr = sd; assert(m_data.pstr->isStatic()); } return; } case KindOfVec: m_type = KindOfPersistentVec; case KindOfPersistentVec: do_array(); return; case KindOfDict: m_type = KindOfPersistentDict; case KindOfPersistentDict: do_array(); return; case KindOfKeyset: m_type = KindOfPersistentKeyset; case KindOfPersistentKeyset: do_array(); return; case KindOfArray: m_type = KindOfPersistentArray; case KindOfPersistentArray: do_array(); return; case KindOfObject: case KindOfResource: case KindOfRef: case KindOfClass: break; } not_reached(); }
void tvCastToArrayInPlace(TypedValue* tv) { assert(tvIsPlausible(*tv)); tvUnboxIfNeeded(tv); ArrayData* a; do { switch (tv->m_type) { case KindOfUninit: case KindOfNull: a = ArrayData::Create(); continue; case KindOfBoolean: case KindOfInt64: case KindOfDouble: case KindOfPersistentString: a = ArrayData::Create(tvAsVariant(tv)); continue; case KindOfString: a = ArrayData::Create(tvAsVariant(tv)); tvDecRefStr(tv); continue; case KindOfPersistentVec: { auto* adIn = tv->m_data.parr; assert(adIn->isVecArray()); a = PackedArray::ToPHPArrayVec(adIn, true); assert(a != adIn); continue; } case KindOfVec: { auto* adIn = tv->m_data.parr; assert(adIn->isVecArray()); a = PackedArray::ToPHPArrayVec(adIn, adIn->cowCheck()); if (a != adIn) tvDecRefArr(tv); continue; } case KindOfPersistentDict: { auto* adIn = tv->m_data.parr; assert(adIn->isDict()); a = MixedArray::ToPHPArrayDict(adIn, true); assert(a != adIn); continue; } case KindOfDict: { auto* adIn = tv->m_data.parr; assert(adIn->isDict()); a = MixedArray::ToPHPArrayDict(adIn, adIn->cowCheck()); if (a != adIn) tvDecRefArr(tv); continue; } case KindOfPersistentKeyset: { auto* adIn = tv->m_data.parr; assert(adIn->isKeyset()); a = MixedArray::ToPHPArrayKeyset(adIn, true); assert(a != adIn); continue; } case KindOfKeyset: { auto* adIn = tv->m_data.parr; assert(adIn->isKeyset()); a = MixedArray::ToPHPArrayKeyset(adIn, adIn->cowCheck()); if (a != adIn) tvDecRefArr(tv); continue; } case KindOfPersistentArray: case KindOfArray: assert(tv->m_data.parr->isPHPArray()); return; case KindOfObject: // For objects, we fall back on the Variant machinery tvAsVariant(tv) = tv->m_data.pobj->toArray(); return; case KindOfResource: a = ArrayData::Create(tvAsVariant(tv)); tvDecRefRes(tv); continue; case KindOfRef: case KindOfClass: break; } not_reached(); } while (0); assert(!a->isRefCounted() || a->hasExactlyOneRef()); tv->m_data.parr = a; tv->m_type = KindOfArray; assert(cellIsPlausible(*tv)); }
std::string escaped_long(Cell cell) { assert(cellIsPlausible(cell)); auto const str = f_serialize(tvAsCVarRef(&cell)); return escaped_long(str.get()); }
std::string member_tv_initializer(Cell cell) { assert(cellIsPlausible(cell)); if (cell.m_type == KindOfUninit) return "uninit"; return escaped_long(cell); }