Object c_GenVectorWaitHandle::ti_create(const Variant& dependencies) { if (UNLIKELY(!dependencies.isObject() || dependencies.getObjectData()->getCollectionType() != Collection::VectorType)) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be an instance of Vector")); throw e; } assert(dependencies.getObjectData()->instanceof(c_Vector::classof())); auto deps = p_Vector::attach(c_Vector::Clone(dependencies.getObjectData())); for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { Cell* current = deps->at(iter_pos); if (UNLIKELY(!c_WaitHandle::fromCell(current))) { Object e(SystemLib::AllocInvalidArgumentExceptionObject( "Expected dependencies to be a vector of WaitHandle instances")); throw e; } } Object exception; for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { Cell* current = tvAssertCell(deps->at(iter_pos)); assert(current->m_type == KindOfObject); assert(current->m_data.pobj->instanceof(c_WaitHandle::classof())); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { cellSet(child->getResult(), *current); } else if (child->isFailed()) { putException(exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); p_GenVectorWaitHandle my_wh = NEWOBJ(c_GenVectorWaitHandle)(); my_wh->initialize(exception, deps.get(), iter_pos, child_wh); AsioSession* session = AsioSession::Get(); if (UNLIKELY(session->hasOnGenVectorCreateCallback())) { session->onGenVectorCreate(my_wh.get(), dependencies); } return my_wh; } } if (exception.isNull()) { return c_StaticWaitHandle::CreateSucceeded( make_tv<KindOfObject>(deps.get())); } else { return c_StaticWaitHandle::CreateFailed(exception.get()); } }
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 c_GenVectorWaitHandle::onUnblocked() { assert(getState() == STATE_BLOCKED); for (; m_iterPos < m_deps->size(); ++m_iterPos) { Cell* current = tvAssertCell(m_deps->at(m_iterPos)); assert(current->m_type == KindOfObject); assert(current->m_data.pobj->instanceof(c_WaitHandle::classof())); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { cellSet(child->getResult(), *current); } else if (child->isFailed()) { putException(m_exception, child->getException()); } else { assert(child->instanceof(c_WaitableWaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); try { if (isInContext()) { child_wh->enterContext(getContextIdx()); } detectCycle(child_wh); blockOn(child_wh); return; } catch (const Object& cycle_exception) { putException(m_exception, cycle_exception.get()); } } } auto const parentChain = getFirstParent(); if (m_exception.isNull()) { setState(STATE_SUCCEEDED); tvWriteObject(m_deps.get(), &m_resultOrException); } else { setState(STATE_FAILED); tvWriteObject(m_exception.get(), &m_resultOrException); m_exception = nullptr; } m_deps = nullptr; UnblockChain(parentChain); }
void HHVM_FUNCTION(set_frame_metadata, const Variant& metadata) { VMRegAnchor _; auto fp = vmfp(); if (fp && fp->skipFrame()) fp = g_context->getPrevVMState(fp); if (UNLIKELY(!fp)) return; if (LIKELY(!(fp->func()->attrs() & AttrMayUseVV)) || LIKELY(!fp->hasVarEnv())) { auto const local = fp->func()->lookupVarId(s_86metadata.get()); if (LIKELY(local != kInvalidId)) { cellSet(*metadata.asCell(), *tvAssertCell(frame_local(fp, local))); } else { SystemLib::throwInvalidArgumentExceptionObject( "Unsupported dynamic call of set_frame_metadata()"); } } else { fp->getVarEnv()->set(s_86metadata.get(), metadata.asTypedValue()); } }
void c_GenMapWaitHandle::onUnblocked() { for (; m_deps->iter_valid(m_iterPos); m_iterPos = m_deps->iter_next(m_iterPos)) { Cell* current = tvAssertCell(m_deps->iter_value(m_iterPos)); assert(current->m_type == KindOfObject); assert(current->m_data.pobj->instanceof(c_WaitHandle::classof())); auto child = static_cast<c_WaitHandle*>(current->m_data.pobj); if (child->isSucceeded()) { cellSet(child->getResult(), *current); } else if (child->isFailed()) { putException(m_exception, child->getException()); } else { assert(child->instanceof(c_WaitHandle::classof())); auto child_wh = static_cast<c_WaitableWaitHandle*>(child); try { if (isInContext()) { child_wh->enterContext(getContextIdx()); } detectCycle(child_wh); blockOn(child_wh); return; } catch (const Object& cycle_exception) { putException(m_exception, cycle_exception.get()); } } } if (m_exception.isNull()) { setResult(make_tv<KindOfObject>(m_deps.get())); m_deps = nullptr; } else { setException(m_exception.get()); m_exception = nullptr; m_deps = nullptr; } }
void Generator::done(TypedValue tv) { assert(getState() == State::Running); cellSetNull(m_key); cellSet(*tvToCell(&tv), m_value); setState(State::Done); }
ArrayData* GlobalsArray::SetStr(ArrayData* ad, StringData* k, Cell v, bool /*copy*/) { auto a = asGlobals(ad); cellSet(v, *tvToCell(a->m_tab->lookupAdd(k))); return a; }
void tearDownFrame(ActRec*& fp, Stack& stack, PC& pc) { auto const func = fp->m_func; auto const curOp = *reinterpret_cast<const Op*>(pc); auto const unwindingReturningFrame = curOp == OpRetC || curOp == OpRetV || curOp == OpCreateCont || curOp == OpAsyncSuspend; auto const prevFp = fp->arGetSfp(); auto const soff = fp->m_soff; FTRACE(1, "tearDownFrame: {} ({})\n fp {} prevFp {}\n", func->fullName()->data(), func->unit()->filepath()->data(), implicit_cast<void*>(fp), implicit_cast<void*>(prevFp)); // When throwing from a constructor, we normally want to avoid running the // destructor on an object that hasn't been fully constructed yet. But if // we're unwinding through the constructor's RetC, the constructor has // logically finished and we're unwinding for some internal reason (timeout // or user profiler, most likely). More importantly, fp->m_this may have // already been destructed and/or overwritten due to sharing space with // fp->m_r. if (!unwindingReturningFrame && fp->isFromFPushCtor() && fp->hasThis()) { fp->getThis()->setNoDestruct(); } /* * If we're unwinding through a frame that's returning, it's only * possible that its locals have already been decref'd. * * Here's why: * * - If a destructor for any of these things throws a php * exception, it's swallowed at the dtor boundary and we keep * running php. * * - If the destructor for any of these things throws a fatal, * it's swallowed, and we set surprise flags to throw a fatal * from now on. * * - If the second case happened and we have to run another * destructor, its enter hook will throw, but it will be * swallowed again. * * - Finally, the exit hook for the returning function can * throw, but this happens last so everything is destructed. * */ if (!unwindingReturningFrame) { try { // Note that we must convert locals and the $this to // uninit/zero during unwind. This is because a backtrace // from another destructing object during this unwind may try // to read them. frame_free_locals_unwind(fp, func->numLocals()); } catch (...) {} } if (LIKELY(!fp->resumed())) { // Free ActRec. stack.ndiscard(func->numSlotsInFrame()); stack.discardAR(); } else if (fp->func()->isAsync()) { // Do nothing. AsyncFunctionWaitHandle will handle the exception. } else if (fp->func()->isGenerator()) { // Mark the generator as finished and clear its m_value. auto cont = frame_continuation(fp); cont->setDone(); cellSet(make_tv<KindOfNull>(), cont->m_value); } else { not_reached(); } /* * At the final ActRec in this nesting level. We don't need to set * pc and fp since we're about to re-throw the exception. And we * don't want to dereference prefFp since we just popped it. */ if (prevFp == fp) return; assert(stack.isValidAddress(reinterpret_cast<uintptr_t>(prevFp)) || prevFp->resumed()); auto const prevOff = soff + prevFp->m_func->base(); pc = prevFp->m_func->unit()->at(prevOff); fp = prevFp; }
void c_Continuation::suspend(Offset offset, const Cell& value) { assert(actRec()->func()->contains(offset)); m_offset = offset; cellSet(make_tv<KindOfInt64>(++m_index), m_key); cellSet(value, m_value); }