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; }
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; }