summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/debug/bug1644699-terminated-generator.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/debug/bug1644699-terminated-generator.js')
-rw-r--r--js/src/jit-test/tests/debug/bug1644699-terminated-generator.js79
1 files changed, 79 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/debug/bug1644699-terminated-generator.js b/js/src/jit-test/tests/debug/bug1644699-terminated-generator.js
new file mode 100644
index 0000000000..dc87ecda8c
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1644699-terminated-generator.js
@@ -0,0 +1,79 @@
+// |jit-test| exitstatus:6
+// Ensure that a frame terminated due to an interrupt in the generator
+// builtin will properly be treated as terminated.
+
+const g = newGlobal({ newCompartment: true });
+const dbg = new Debugger(g);
+g.eval(`
+var done = false;
+async function* f() {
+ await null;
+ await null;
+ await null;
+ await null;
+ done = true;
+}
+`);
+
+dbg.onEnterFrame = f => {
+ frame = f;
+ dbg.onEnterFrame = undefined;
+};
+
+setInterruptCallback(function() {
+ const stack = saveStack();
+
+ // We want to explicitly terminate inside the AsyncGeneratorNext builtin
+ // when it tries to resume execution at the 'await' in the async generator.
+ // Terminating inside AsyncGeneratorNext causes the generator to be closed,
+ // and for this test case we specifically need that to happen without
+ // entering the async generator frame because we're aiming to trigger the
+ // case where DebugAPI::onLeaveFrame does not having the opportunity to
+ // clean up the generator information associated with the Debugger.Frame.
+ if (
+ stack.parent &&
+ stack.parent.source === "self-hosted" &&
+ stack.parent.functionDisplayName === "AsyncGeneratorNext" &&
+ stack.parent.parent &&
+ stack.parent.parent.source === stack.source &&
+ stack.parent.parent.line === DRAIN_QUEUE_LINE
+ ) {
+ return false;
+ }
+
+ // Keep interrupting until we find the right place.
+ interruptIf(true);
+ return true;
+});
+
+// Run the async generator and suspend at the first await.
+const it = g.f();
+let promise = it.next();
+
+// Queue the interrupt so that it will start trying to terminate inside the
+// generator builtin.
+interruptIf(true);
+
+// Explicitly drain the queue to run the async generator to completion.
+const DRAIN_QUEUE_LINE = saveStack().line + 1;
+drainJobQueue();
+
+let threw = false;
+try {
+ // In the original testcase for this bug, this call would cause
+ // an assertion failure because the generator was closed.
+ frame.environment;
+} catch (err) {
+ threw = true;
+}
+assertEq(threw, true);
+
+// The frame here still has a GeneratorInfo datastructure because the
+// termination interrupt will cause the generator to be closed without
+// clearing that data. The frame must still be treated as terminated in
+// this case in order for the Debugger API to behave consistently.
+assertEq(frame.terminated, true);
+
+// We should never reach the end of the async generator because it will
+// have been terminated.
+assertEq(g.done, false);