Example #1
0
void CmdNext::stepCurrentLine(CmdInterrupt& interrupt, ActRec* fp, PC pc) {
  // Special handling for yields from generators. The destination of these
  // instructions is somewhat counter intuitive so we take care to ensure that
  // we step to the most appropriate place. For yeilds, we want to land on the
  // next statement when driven from a C++ iterator like ASIO. If the generator
  // is driven directly from PHP (i.e., a loop calling send($foo)) then we'll
  // land back at the callsite of send(). For returns from generators, we follow
  // the execution stack for now, and end up at the caller of ASIO or send().
  auto op = toOp(*pc);
  if (fp->m_func->isGenerator() &&
      (op == OpContSuspend || op == OpContSuspendK || op == OpContRetC)) {
    TRACE(2, "CmdNext: encountered yield or return from generator\n");
    // Patch the projected return point(s) in both cases, to catch if we exit
    // the the asio iterator or if we are being iterated directly by PHP.
    setupStepOuts();
    op = toOp(*pc);
    if (op == OpContSuspend || op == OpContSuspendK) {
      // Patch the next normal execution point so we can pickup the stepping
      // from there if the caller is C++.
      setupStepCont(fp, pc);
    }
    removeLocationFilter();
    return;
  }

  installLocationFilterForLine(interrupt.getSite());
  m_needsVMInterrupt = true;
}
Example #2
0
void CmdNext::stepCurrentLine(CmdInterrupt& interrupt, ActRec* fp, PC pc) {
  // Special handling for yields from generators and awaits from
  // async. The destination of these instructions is somewhat counter
  // intuitive so we take care to ensure that we step to the most
  // appropriate place. For yields, we want to land on the next
  // statement when driven from a C++ iterator like ASIO. If the
  // generator is driven directly from PHP (i.e., a loop calling
  // send($foo)) then we'll land back at the callsite of send(). For
  // returns from generators, we follow the execution stack for now,
  // and end up at the caller of ASIO or send(). For async functions
  // stepping over an await, we land on the next statement.
  auto op = *reinterpret_cast<const Op*>(pc);
  if (fp->resumed() &&
      (op == OpYield || op == OpYieldK ||
       op == OpAsyncSuspend || op == OpRetC)) {
    TRACE(2, "CmdNext: encountered yield, await or return from generator\n");
    // Patch the projected return point(s) in both cases for
    // generators, to catch if we exit the the asio iterator or if we
    // are being iterated directly by PHP.
    if ((op == OpRetC) || !fp->m_func->isAsync()) setupStepOuts();
    op = *reinterpret_cast<const Op*>(pc);
    if (op == OpAsyncSuspend || op == OpYield || op == OpYieldK) {
      // Patch the next normal execution point so we can pickup the stepping
      // from there if the caller is C++.
      setupStepCont(fp, pc);
    }
    removeLocationFilter();
    return;
  } else if (op == OpAsyncSuspend) {
    // We need to step over this opcode, then grab the continuation
    // and setup continuation stepping like we do for OpYield.
    TRACE(2, "CmdNext: encountered create async\n");
    m_skippingAsyncSuspend = true;
    m_needsVMInterrupt = true;
    removeLocationFilter();
    return;
  }
  installLocationFilterForLine(interrupt.getSite());
  m_needsVMInterrupt = true;
}