diff --git a/hphp/runtime/ext/ext_continuation.cpp b/hphp/runtime/ext/ext_continuation.cpp index 199955507..09cbc2d46 100644 --- a/hphp/runtime/ext/ext_continuation.cpp +++ b/hphp/runtime/ext/ext_continuation.cpp @@ -149,10 +149,84 @@ String c_Continuation::t_getcalledclass() { return called_class; } +void c_Continuation::dupContVar(const StringData* name, TypedValue* src) { + ActRec *fp = actRec(); + Id destId = fp->m_func->lookupVarId(name); + if (destId != kInvalidId) { + // Copy the value of the local to the cont object. + tvDupFlattenVars(src, frame_local(fp, destId), nullptr); + } else { + ActRec *contFP = fp; + if (!fp->hasVarEnv()) { + // This VarEnv may potentially outlive the most recently stack-allocated + // VarEnv, so we need to heap allocate it. + fp->setVarEnv(VarEnv::createLocalOnHeap(fp)); + } + fp->getVarEnv()->setWithRef(name, src); + } +} + +static const StaticString s_this("this"); + +void c_Continuation::copyContinuationVars(ActRec* fp) { + // For functions that contain only named locals, we can copy TVs + // right to the local space. + static const StringData* thisStr = s_this.get(); + bool skipThis; + if (fp->hasVarEnv()) { + Stats::inc(Stats::Cont_CreateVerySlow); + Array definedVariables = fp->getVarEnv()->getDefinedVariables(); + skipThis = definedVariables.exists(s_this, true); + + for (ArrayIter iter(definedVariables); !iter.end(); iter.next()) { + if (iter.first().getStringData()->same(s___cont__.get())) { + continue; + } + dupContVar(iter.first().getStringData(), + const_cast(iter.secondRef().asTypedValue())); + } + } else { + const Func *genFunc = actRec()->m_func; + skipThis = genFunc->lookupVarId(thisStr) != kInvalidId; + // skip local 0 because that's the old continuation + for (Id i = 1; i < genFunc->numNamedLocals(); ++i) { + dupContVar(genFunc->localVarName(i), frame_local(fp, i)); + } + } + + // If $this is used as a local inside the body and is not provided + // by our containing environment, just prefill it here instead of + // using InitThisLoc inside the body + if (!skipThis && fp->hasThis()) { + Id id = actRec()->m_func->lookupVarId(thisStr); + if (id != kInvalidId) { + tvAsVariant(frame_local(actRec(), id)) = fp->getThis(); + } + } +} + +// unused Variant c_Continuation::t___clone() { - throw_fatal( - "Trying to clone an uncloneable object of class Continuation"); - return uninit_null(); + not_reached(); +} + +c_Continuation *c_Continuation::clone() { + const Func *origFunc = m_origFunc; + const Func *genFunc = actRec()->m_func; + + ActRec *fp = g_vmContext->getFP(); + c_Continuation* cont = origFunc->isMethod() + ? g_vmContext->createContMeth(origFunc, genFunc, fp->getThisOrClass()) + : g_vmContext->createContFunc(origFunc, genFunc); + + cont->copyContinuationVars(actRec()); + + cont->o_subclassData.u16 = o_subclassData.u16; + cont->m_label = m_label; + cont->m_index = m_index; + cont->m_value = m_value; + + return cont; } namespace { diff --git a/hphp/runtime/ext/ext_continuation.h b/hphp/runtime/ext/ext_continuation.h index fd6f2e07f..85b44f86d 100644 --- a/hphp/runtime/ext/ext_continuation.h +++ b/hphp/runtime/ext/ext_continuation.h @@ -71,6 +71,8 @@ public: String t_getcalledclass(); Variant t___clone(); + c_Continuation* clone(); + static c_Continuation* alloc(const Func* origFunc, const Func* genFunc) { assert(origFunc); assert(genFunc); @@ -128,6 +130,9 @@ private: return (char*)(m_arPtr + 1) - (char*)this; } + void dupContVar(const StringData *name, TypedValue *src); + void copyContinuationVars(ActRec *fp); + public: /* 32-bit o_id from ObjectData */ int32_t m_label; diff --git a/hphp/test/slow/yield/clone/g1.php b/hphp/test/slow/yield/clone/g1.php new file mode 100644 index 000000000..ec7870e6e --- /dev/null +++ b/hphp/test/slow/yield/clone/g1.php @@ -0,0 +1,44 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($x as $v) { + echo $v . "\n"; +} +echo "========\n"; +foreach ($y1 as $v) { + echo $v . "\n"; +} +echo "========\n"; +foreach ($y2 as $v) { + echo $v . "\n"; +} diff --git a/hphp/test/slow/yield/clone/g10.php.expect b/hphp/test/slow/yield/clone/g10.php.expect new file mode 100644 index 000000000..e5cd761c5 --- /dev/null +++ b/hphp/test/slow/yield/clone/g10.php.expect @@ -0,0 +1,8 @@ +111 +1111 +======== +1211 +2211 +======== +2311 +3311 diff --git a/hphp/test/slow/yield/clone/g11.php b/hphp/test/slow/yield/clone/g11.php new file mode 100644 index 000000000..83fe69129 --- /dev/null +++ b/hphp/test/slow/yield/clone/g11.php @@ -0,0 +1,23 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($y1 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($y2 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($x as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); diff --git a/hphp/test/slow/yield/clone/g12.php.expect b/hphp/test/slow/yield/clone/g12.php.expect new file mode 100644 index 000000000..639b809a6 --- /dev/null +++ b/hphp/test/slow/yield/clone/g12.php.expect @@ -0,0 +1,17 @@ +31 302 +61 603 +-------- +int(1) +int(604) +======== +31 804 +61 1105 +-------- +int(1) +int(1106) +======== +31 1306 +61 1607 +-------- +int(1) +int(1608) diff --git a/hphp/test/slow/yield/clone/g2.php b/hphp/test/slow/yield/clone/g2.php new file mode 100644 index 000000000..ab51b8e9b --- /dev/null +++ b/hphp/test/slow/yield/clone/g2.php @@ -0,0 +1,45 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($y1 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($y2 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($x as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); diff --git a/hphp/test/slow/yield/clone/g2.php.expect b/hphp/test/slow/yield/clone/g2.php.expect new file mode 100644 index 000000000..639b809a6 --- /dev/null +++ b/hphp/test/slow/yield/clone/g2.php.expect @@ -0,0 +1,17 @@ +31 302 +61 603 +-------- +int(1) +int(604) +======== +31 804 +61 1105 +-------- +int(1) +int(1106) +======== +31 1306 +61 1607 +-------- +int(1) +int(1608) diff --git a/hphp/test/slow/yield/clone/g3.php b/hphp/test/slow/yield/clone/g3.php new file mode 100644 index 000000000..6a9794700 --- /dev/null +++ b/hphp/test/slow/yield/clone/g3.php @@ -0,0 +1,44 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($y1 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($y2 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($x as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); diff --git a/hphp/test/slow/yield/clone/g4.php.expect b/hphp/test/slow/yield/clone/g4.php.expect new file mode 100644 index 000000000..639b809a6 --- /dev/null +++ b/hphp/test/slow/yield/clone/g4.php.expect @@ -0,0 +1,17 @@ +31 302 +61 603 +-------- +int(1) +int(604) +======== +31 804 +61 1105 +-------- +int(1) +int(1106) +======== +31 1306 +61 1607 +-------- +int(1) +int(1608) diff --git a/hphp/test/slow/yield/clone/g5.php b/hphp/test/slow/yield/clone/g5.php new file mode 100644 index 000000000..e17e43af0 --- /dev/null +++ b/hphp/test/slow/yield/clone/g5.php @@ -0,0 +1,44 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($y1 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($y2 as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); +echo "========\n"; +foreach ($x as $v) { + $v1 = (int)($v / 10000); + $v2 = $v % 10000; + echo $v1 . " " . $v2 . "\n"; + ++$a2; +} +echo "--------\n"; +var_dump($a1, $a2); diff --git a/hphp/test/slow/yield/clone/g6.php.expect b/hphp/test/slow/yield/clone/g6.php.expect new file mode 100644 index 000000000..639b809a6 --- /dev/null +++ b/hphp/test/slow/yield/clone/g6.php.expect @@ -0,0 +1,17 @@ +31 302 +61 603 +-------- +int(1) +int(604) +======== +31 804 +61 1105 +-------- +int(1) +int(1106) +======== +31 1306 +61 1607 +-------- +int(1) +int(1608) diff --git a/hphp/test/slow/yield/clone/g7.php b/hphp/test/slow/yield/clone/g7.php new file mode 100644 index 000000000..59066ae21 --- /dev/null +++ b/hphp/test/slow/yield/clone/g7.php @@ -0,0 +1,24 @@ +next(); +$y1 = clone $x; +$y2 = clone $x; +foreach ($x as $v) { + echo $v . "\n"; +} +echo "========\n"; +foreach ($y1 as $v) { + echo $v . "\n"; +} +echo "========\n"; +foreach ($y2 as $v) { + echo $v . "\n"; +} diff --git a/hphp/test/slow/yield/clone/g8.php.expect b/hphp/test/slow/yield/clone/g8.php.expect new file mode 100644 index 000000000..e5cd761c5 --- /dev/null +++ b/hphp/test/slow/yield/clone/g8.php.expect @@ -0,0 +1,8 @@ +111 +1111 +======== +1211 +2211 +======== +2311 +3311 diff --git a/hphp/test/slow/yield/clone/g9.php b/hphp/test/slow/yield/clone/g9.php new file mode 100644 index 000000000..f705a6963 --- /dev/null +++ b/hphp/test/slow/yield/clone/g9.php @@ -0,0 +1,24 @@ +