ALWAYS_INLINE void BaseMap::setImpl(int64_t h, const TypedValue* val) { if (!raw) { mutate(); } assert(val->m_type != KindOfRef); assert(canMutateBuffer()); retry: auto p = findForInsert(h); assert(p); if (validPos(*p)) { auto& e = data()[*p]; TypedValue old = e.data; cellDup(*val, e.data); tvRefcountedDecRef(old); return; } if (UNLIKELY(isFull())) { makeRoom(); goto retry; } if (!raw) { ++m_version; } auto& e = allocElm(p); cellDup(*val, e.data); e.setIntKey(h); updateNextKI(h); }
ALWAYS_INLINE void BaseMap::setImpl(StringData* key, const TypedValue* val) { if (!raw) { mutate(); } assert(val->m_type != KindOfRef); assert(canMutateBuffer()); retry: strhash_t h = key->hash(); auto* p = findForInsert(key, h); assert(p); if (validPos(*p)) { auto& e = data()[*p]; TypedValue old = e.data; cellDup(*val, e.data); tvRefcountedDecRef(old); return; } if (UNLIKELY(isFull())) { makeRoom(); goto retry; } if (!raw) { ++m_version; } auto& e = allocElm(p); cellDup(*val, e.data); e.setStrKey(key, h); updateIntLikeStrKeys(key); }
ALWAYS_INLINE void BaseSet::addImpl(StringData *key) { if (!raw) { mutate(); } strhash_t h = key->hash(); auto p = findForInsert(key, h); assert(p); if (validPos(*p)) { return; } if (UNLIKELY(isFull())) { makeRoom(); p = findForInsert(key, h); } auto& e = allocElm(p); // This increments the string's refcount twice, once for // the key and once for the value e.setStrKey(key, h); cellDup(make_tv<KindOfString>(key), e.data); updateIntLikeStrKeys(key); if (!raw) { ++m_version; } }
Cell lookupClassConstantTv(TypedValue* cache, const NamedEntity* ne, const StringData* cls, const StringData* cns) { Cell clsCns = g_vmContext->lookupClsCns(ne, cls, cns); assert(isUncounted(clsCns)); cellDup(clsCns, *cache); return clsCns; }
void c_ConditionWaitHandle::t_succeed(const Variant& result) { if (isFinished()) { failAlreadyFinished(); } assert(getState() == STATE_BLOCKED); auto parentChain = getParentChain(); setState(STATE_SUCCEEDED); cellDup(*result.asCell(), m_resultOrException); parentChain.unblock(); }
void HHVM_METHOD(ConditionWaitHandle, succeed, const Variant& result) { auto obj = wait_handle<c_ConditionWaitHandle>(this_); if (obj->isFinished()) { failAlreadyFinished(); } assert(obj->getState() == c_ConditionWaitHandle::STATE_BLOCKED); auto parentChain = obj->getParentChain(); obj->setState(c_ConditionWaitHandle::STATE_SUCCEEDED); cellDup(*result.asCell(), obj->m_resultOrException); parentChain.unblock(); }
void throwable_init_file_and_line_from_builtin(ObjectData* throwable) { assertx(vmfp_is_builtin()); assertx(is_throwable(throwable)); assertx(throwable_has_expected_props()); assertx(throwable->propVec()[s_fileIdx].m_type == KindOfNull); assertx(throwable->propVec()[s_lineIdx].m_type == KindOfNull); assertx(throwable->propVec()[s_traceIdx].m_type == KindOfArray); auto const trace = throwable->propVec()[s_traceIdx].m_data.parr; for (ArrayIter iter(trace); iter; ++iter) { assertx(iter.second().asTypedValue()->m_type == KindOfArray); auto const frame = iter.second().asTypedValue()->m_data.parr; auto const file = frame->nvGet(s_file.get()); auto const line = frame->nvGet(s_line.get()); if (file || line) { if (file) cellDup(*tvAssertCell(file), throwable->propVec()[s_fileIdx]); if (line) cellDup(*tvAssertCell(line), throwable->propVec()[s_lineIdx]); return; } } }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->inGenerator()) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; JIT::VMRegAnchor _; PC savePc = g_context->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(ar->m_func->fullNameRef()) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_context->getStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); g_context->m_fp = outer; g_context->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr; return false; } g_context->m_fp = ar; g_context->m_pc = savePc; return true; }
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; }
void unserialize(Cell& c) override { if (!m_exception.empty()) { throw mcr_getException(m_exception, m_op, m_replyCode, m_key); } if ((m_result.m_type == KindOfString) && !m_result.m_data.pstr) { // Deferred string init, see below m_result.m_data.pstr = StringData::Make( m_stringResult.c_str(), m_stringResult.size(), CopyString); m_result.m_data.pstr->setRefCount(1); m_stringResult.clear(); } cellDup(m_result, c); }
void BaseVector::cow() { TypedValue* newData = (TypedValue*)smart_malloc(m_capacity * sizeof(TypedValue)); assert(newData); for (uint i = 0; i < m_size; i++) { cellDup(m_data[i], newData[i]); } m_data = newData; m_frozenCopy.reset(); }
void throwable_init(ObjectData* throwable) { assertx(is_throwable(throwable)); assertx(throwable_has_expected_props()); auto trace = HHVM_FN(debug_backtrace)(exception_get_trace_options()); cellMove( make_tv<KindOfArray>(trace.detach()), throwable->propVec()[s_traceIdx]); VMRegAnchor _; auto const fp = vmfp(); if (UNLIKELY(!fp)) return; if (UNLIKELY(fp->func()->isBuiltin())) { throwable_init_file_and_line_from_builtin(throwable); } else { assertx(throwable->propVec()[s_fileIdx].m_type == KindOfNull); assertx(throwable->propVec()[s_lineIdx].m_type == KindOfNull); auto const unit = fp->func()->unit(); auto const file = const_cast<StringData*>(unit->filepath()); auto const line = unit->getLineNumber(unit->offsetOf(vmpc())); cellDup(make_tv<KindOfString>(file), throwable->propVec()[s_fileIdx]); cellDup(make_tv<KindOfInt64>(line), throwable->propVec()[s_lineIdx]); } }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->m_func; if (LIKELY(func->maybeIntercepted() == 0)) return true; Variant *h = get_intercept_handler(func->fullNameRef(), &func->maybeIntercepted()); if (!h) return true; Transl::VMRegAnchor _; PC savePc = g_vmContext->m_pc; Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Array intArgs = CREATE_VECTOR5(ar->m_func->fullNameRef(), called_on, get_frame_args_with_ref(ar), h->asCArrRef()[1], ref(doneFlag)); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_vmContext->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->m_func->numLocals()); Stack& stack = g_vmContext->getStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); g_vmContext->m_fp = outer; g_vmContext->m_pc = outer ? outer->m_func->unit()->at(pcOff) : nullptr; return false; } g_vmContext->m_fp = ar; g_vmContext->m_pc = savePc; 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(); }
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 HHVM_METHOD(ConditionWaitHandle, fail, const Object& exception) { if (!exception->instanceof(SystemLib::s_ThrowableClass)) { SystemLib::throwInvalidArgumentExceptionObject( "Expected exception to be an instance of Throwable"); } auto obj = wait_handle<c_ConditionWaitHandle>(this_); if (obj->isFinished()) { failAlreadyFinished(); } assert(obj->getState() == c_ConditionWaitHandle::STATE_BLOCKED); auto parentChain = obj->getParentChain(); obj->setState(c_ConditionWaitHandle::STATE_FAILED); cellDup(make_tv<KindOfObject>(exception.get()), obj->m_resultOrException); parentChain.unblock(); }
Cell lookupCnsHelperPersistent(rds::Handle tv_handle, StringData* nm, bool error) { assertx(rds::isPersistentHandle(tv_handle)); auto const tv = &rds::handleToRef<TypedValue>(tv_handle); assertx(tv->m_type == KindOfUninit); // Deferred system constants. if (UNLIKELY(tv->m_data.pref != nullptr)) { auto callback = (Native::ConstantCallback)(tv->m_data.pref); const Cell* cns = callback().asTypedValue(); if (LIKELY(cns->m_type != KindOfUninit)) { Cell c1; cellDup(*cns, c1); return c1; } } return lookupCnsHelper(nm, error); }
ArrayData* PackedArray::Append(ArrayData* adIn, const Variant& v, bool copy) { assert(checkInvariants(adIn)); auto const ad = copy ? CopyAndResizeIfNeeded(adIn) : ResizeIfNeeded(adIn); if (UNLIKELY(!ad)) { auto const mixed = copy ? ToMixedCopy(adIn) : ToMixed(adIn); return MixedArray::Append(mixed, v, copy); } if (ad->m_pos == ArrayData::invalid_index) { ad->m_pos = ad->m_size; } auto& dst = packedData(ad)[ad->m_size++]; cellDup(*v.asCell(), dst); // TODO(#3888164): restructure this so we don't need KindOfUninit checks. if (dst.m_type == KindOfUninit) dst.m_type = KindOfNull; return ad; }
Cell lookupCnsHelper(const TypedValue* tv, StringData* nm, bool error) { assert(tv->m_type == KindOfUninit); // Deferred constants such as SID if (UNLIKELY(tv->m_data.pref != nullptr)) { ClassInfo::ConstantInfo* ci = (ClassInfo::ConstantInfo*)(void*)tv->m_data.pref; Cell *cns = const_cast<Variant&>(ci->getDeferredValue()).asTypedValue(); if (LIKELY(cns->m_type != KindOfUninit)) { Cell c1; cellDup(*cns, c1); return c1; } } Cell *cns = nullptr; if (UNLIKELY(TargetCache::s_constants().get() != nullptr)) { cns = TargetCache::s_constants()->nvGet(nm); } if (!cns) { cns = Unit::loadCns(const_cast<StringData*>(nm)); } if (LIKELY(cns != nullptr)) { Cell c1; c1.m_type = cns->m_type; c1.m_data = cns->m_data; return c1; } // Undefined constants if (error) { raise_error("Undefined constant '%s'", nm->data()); } else { raise_notice(Strings::UNDEFINED_CONSTANT, nm->data(), nm->data()); Cell c1; c1.m_data.pstr = const_cast<StringData*>(nm); c1.m_type = KindOfStaticString; return c1; } not_reached(); }
Cell lookupCnsHelper(StringData* nm, bool error) { auto const cns = lookupCnsImpl(nm); if (LIKELY(cns != nullptr)) { Cell c1; cellDup(*cns, c1); return c1; } // Undefined constants. if (error) { raise_error("Undefined constant '%s'", nm->data()); } else { raise_notice(Strings::UNDEFINED_CONSTANT, nm->data(), nm->data()); Cell c1; c1.m_data.pstr = const_cast<StringData*>(nm); c1.m_type = KindOfPersistentString; return c1; } not_reached(); }
void c_ConditionWaitHandle::t_fail(const Variant& exception) { auto const cell = exception.asCell(); if (UNLIKELY( cell->m_type != KindOfObject || !cell->m_data.pobj->instanceof(SystemLib::s_ExceptionClass) )) { SystemLib::throwInvalidArgumentExceptionObject( "Expected exception to be an instance of Exception"); } if (isFinished()) { failAlreadyFinished(); } assert(getState() == STATE_BLOCKED); auto parentChain = getParentChain(); setState(STATE_FAILED); cellDup(make_tv<KindOfObject>(cell->m_data.pobj), m_resultOrException); parentChain.unblock(); }
/* Unserialize happens in the request thread where we can allocate smart pointers * Use this opportunity to marshal the saved data from persistent data structures * into per-request data. */ void unserialize(Cell& c) override { if (!m_exception.empty()) { mcr_throwException(m_exception, m_op, m_replyCode, m_key); } if ((m_result.m_type == KindOfString) && !m_result.m_data.pstr) { // Deferred string init, see below m_result.m_data.pstr = StringData::Make( m_stringResult.c_str(), m_stringResult.size(), CopyString); m_stringResult.clear(); } else if ((m_result.m_type == KindOfArray) && !m_result.m_data.parr) { // Deferred string value and cas, see below Array ret = Array::Create(); ret.set(s_value, String(m_stringResult.c_str(), m_stringResult.size(), CopyString)); ret.set(s_cas, (int64_t)m_cas); ret.set(s_flags, (int64_t)m_flags); m_result.m_data.parr = ret.detach(); m_stringResult.clear(); } cellDup(m_result, c); }
Cell lookupCnsUHelperPersistent(rds::Handle tv_handle, StringData* nm, StringData* fallback) { assertx(rds::isPersistentHandle(tv_handle)); // Lookup qualified name in thread-local constants. auto cns = lookupCnsImpl(nm); // Try cache handle for unqualified name. auto const tv = &rds::handleToRef<TypedValue>(tv_handle); if (UNLIKELY(!cns && tv->m_type != KindOfUninit)) { cns = tv; } if (LIKELY(cns != nullptr)) { Cell c1; cellDup(*cns, c1); return c1; } return lookupCnsHelper(fallback, false); }
Cell lookupCnsUHelperNormal(rds::Handle tv_handle, StringData* nm, StringData* fallback) { assertx(rds::isNormalHandle(tv_handle)); // Lookup qualified name in thread-local constants. auto cns = lookupCnsImpl(nm); // Try cache handle for unqualified name. if (UNLIKELY(!cns && rds::isHandleInit(tv_handle, rds::NormalTag{}))) { cns = &rds::handleToRef<TypedValue>(tv_handle); assertx(cns->m_type != KindOfUninit); } if (LIKELY(cns != nullptr)) { Cell c1; cellDup(*cns, c1); return c1; } // Lookup unqualified name in thread-local constants. return lookupCnsHelper(fallback, false); }
typename std::enable_if< std::is_base_of<BaseVector, TVector>::value, Object>::type BaseSet::php_concat(const Variant& iterable) { size_t itSize; ArrayIter iter = getArrayIterHelper(iterable, itSize); auto vec = req::make<TVector>(); uint32_t sz = m_size; vec->reserve((size_t)sz + itSize); assert(vec->canMutateBuffer()); vec->setSize(sz); uint32_t used = posLimit(); for (uint32_t i = 0, j = 0; i < used; ++i) { if (isTombstone(i)) { continue; } cellDup(data()[i].data, vec->data()[j]); ++j; } for (; iter; ++iter) { vec->addRaw(iter.second()); } return Object{std::move(vec)}; }
p_StaticResultWaitHandle c_StaticResultWaitHandle::Create(const Cell& result) { p_StaticResultWaitHandle wh = NEWOBJ(c_StaticResultWaitHandle)(); cellDup(result, wh->m_resultOrException); return wh; }
bool EventHook::RunInterceptHandler(ActRec* ar) { const Func* func = ar->func(); if (LIKELY(func->maybeIntercepted() == 0)) return true; // Intercept only original generator / async function calls, not resumption. if (ar->resumed()) return true; Variant* h = get_intercept_handler(func->fullNameStr(), &func->maybeIntercepted()); if (!h) return true; /* * In production mode, only functions that we have assumed can be * intercepted during static analysis should actually be * intercepted. */ if (RuntimeOption::RepoAuthoritative && !RuntimeOption::EvalJitEnableRenameFunction) { if (!(func->attrs() & AttrInterceptable)) { raise_error("fb_intercept was used on a non-interceptable function (%s) " "in RepoAuthoritative mode", func->fullName()->data()); } } VMRegAnchor _; PC savePc = vmpc(); Variant doneFlag = true; Variant called_on; if (ar->hasThis()) { called_on = Variant(ar->getThis()); } else if (ar->hasClass()) { // For static methods, give handler the name of called class called_on = Variant(const_cast<StringData*>(ar->getClass()->name())); } Variant intArgs = PackedArrayInit(5) .append(VarNR(ar->func()->fullName())) .append(called_on) .append(get_frame_args_with_ref(ar)) .append(h->asCArrRef()[1]) .appendRef(doneFlag) .toArray(); Variant ret = vm_call_user_func(h->asCArrRef()[0], intArgs); if (doneFlag.toBoolean()) { Offset pcOff; ActRec* outer = g_context->getPrevVMState(ar, &pcOff); frame_free_locals_inl_no_hook<true>(ar, ar->func()->numLocals()); Stack& stack = vmStack(); stack.top() = (Cell*)(ar + 1); cellDup(*ret.asCell(), *stack.allocTV()); vmfp() = outer; vmpc() = outer ? outer->func()->unit()->at(pcOff) : nullptr; return false; } vmfp() = ar; vmpc() = savePc; return true; }
Cell lookupClsCnsHelper(TypedValue* cache, const NamedEntity* ne, const StringData* cls, const StringData* cns) { auto const clsCns = g_context->lookupClsCns(ne, cls, cns); cellDup(clsCns, *cache); return clsCns; }