Exemplo n.º 1
0
// Use the address of the c_Continuation object as a tag for this stepping
// operation, to ensure we only stop once we're back to the same continuation.
// Since we'll either stop when we get out of whatever is driving this
// continuation, or we'll stop when we get back into it, we know the object
// will remain alive.
void* CmdNext::getContinuationTag(ActRec* fp) {
  c_Continuation* cont = frame_continuation(fp);
  TRACE(2, "CmdNext: continuation tag %p for %s\n", cont,
        cont->t_getorigfuncname().data());
  assert(cont->actRec() == fp);
  return cont->actRec();
}
Exemplo n.º 2
0
// A ContSuspend marks a return point from a generator or async
// function. Execution will resume at this function later, and the
// Continuation associated with this function can predict where.
void CmdNext::setupStepCont(ActRec* fp, PC pc) {
  // ContSuspend is followed by the label where execution will continue.
  DEBUG_ONLY auto ops = reinterpret_cast<const Op*>(pc);
  assert(ops[0] == OpContSuspend || ops[0] == OpContSuspendK);
  ++pc;
  int32_t label = decodeVariableSizeImm(&pc);
  c_Continuation* cont = frame_continuation(fp);
  Offset nextInst = cont->getExecutionOffset(label);
  assert(nextInst != InvalidAbsoluteOffset);
  m_stepContTag = cont->actRec();
  TRACE(2, "CmdNext: patch for cont step at '%s' offset %d\n",
        fp->m_func->fullName()->data(), nextInst);
  m_stepCont = StepDestination(fp->m_func->unit(), nextInst);
}
Exemplo n.º 3
0
RefData* closureStaticLocInit(StringData* name, ActRec* fp, TypedValue val) {
  auto const func = fp->m_func;
  assert(func->isClosureBody() || func->isGeneratorFromClosure());
  auto const closureLoc =
    LIKELY(func->isClosureBody())
      ? frame_local(fp, func->numParams())
      : frame_local(fp, frame_continuation(fp)->m_origFunc->numParams());

  bool inited;
  auto const refData = lookupStaticFromClosure(
    closureLoc->m_data.pobj, name, inited);
  if (!inited) {
    cellCopy(val, *refData->tv());
  }
  refData->incRefCount();
  return refData;
}
Exemplo n.º 4
0
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;
}