[jsdbg2] Debugger.Objects for promises should provide reaction records
Categories
(Core :: JavaScript Engine, enhancement, P3)
Tracking
()
People
(Reporter: jimb, Assigned: jimb)
References
Details
Attachments
(7 files)
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review | |
47 bytes,
text/x-phabricator-request
|
Details | Review |
Given a Debugger.Object
for a promise, the Debugger API should provide a way to obtain Debugger.Object
s for chained promises, and Debugger.Frame
s for any async calls awaiting the resolution of that promise.
Together with bug 1592415, this would give the devtools server the primitives it needs to include asynchronous callers in stack traces.
When we evaluate an expression like the await
call in f
:
async function f() {
await g();
}
where g
is an asynchronous function, this bug challenges us to find a path from the promise returned by the call g()
, representing g
's resolution, to the Debugger.Frame
for the call to f
.
That path is not trivial:
-
Let
G
be the internal generator object representing the call tof
. -
Let
P1
be the promise returned byg()
. -
The
await
expression creates a fresh internal promise,P2
, and resolves it toP1
. That is, resolvingP1
resolvesP2
as well. -
The
await
expression then attaches a promise reaction record toP2
(i.e., it callsP2.then
) to awakenG
.
Concretely, we should see the following arrangement:
-
Bug 1592415 lets us obtain a
Debugger.Object
forP1
, given aDebugger.Frame
for the call tog
. -
P1
is aPromiseObject
. APromiseObject
has a reserved slot,PromiseSlot_ReactionsOrResult
, that holds its reaction jobs, represented asPromiseReactionRecord
s. (There are separate representations for zero, one, and more than one reaction jobs, but those are details.) -
A
PromiseReactionRecord
has flags indicating what sort of reaction it represents.-
I believe the
REACTION_FLAG_DEFAULT_RESOLVING_HANDLER
flag indicates that the reaction record represents another promise resolved to this one, soP1
should have aPromiseReactionRecord
with this flag set that points toP2
. -
I believe the
REACTION_FLAG_ASYNC_FUNCTION
flag indicates that the reaction record represents an asynchronous function awaiting this promise, soP2
should have aPromiseReactionRecord
with this flag set that points toG
.
-
-
Given
G
, we should be able to find theDebugger.Frame
representing the suspended call tof
.
Assignee | ||
Updated•5 years ago
|
Assignee | ||
Updated•5 years ago
|
Updated•5 years ago
|
Assignee | ||
Updated•5 years ago
|
Assignee | ||
Comment 1•5 years ago
|
||
Comment 2•5 years ago
|
||
are you going to post more patches later, or is the problem already solved in other bugs? (I'll check the related code, but asking just to make sure)
I'm wondering the relation between the patch and the goal of this bug.
Pushed by jblandy@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/4c8c82641737 Correct comment for ReactionRecordSlots::ReactionRecordSlot_Promise. DONTBUILD r=arai
Updated•5 years ago
|
Assignee | ||
Comment 4•5 years ago
|
||
(In reply to Tooru Fujisawa [:arai] from comment #2)
are you going to post more patches later, or is the problem already solved in other bugs? (I'll check the related code, but asking just to make sure)
I'm wondering the relation between the patch and the goal of this bug.
I'm going to post more patches later, sorry for the confusion.
Comment 5•5 years ago
|
||
bugherder |
Assignee | ||
Comment 6•5 years ago
|
||
js/src/builtin/Promise.cpp's InternalAwait function takes onFulfilled and
onRejected handlers, but in all uses these are PromiseHandler enum values, not
actual JavaScript function objects.
It's helpful to readers to know that InternalAwait reaction records always use
one of the stock handlers from PromiseHandler, and never arbitrary code supplied
by the user. It's also easier on the callers to not have to pass Value handles.
Assignee | ||
Comment 7•5 years ago
|
||
Here's what I'm planning to implement:
promiseReactions
If the referent is a [Promise
][promise], this is an array of objects
describing the reaction records added to the promise. There are several
different sorts of reaction records:
-
The array entry for a reaction record added with
then
orcatch
has the
form{ then: F, catch: F, result: P }
, where eachF
is aDebugger.Object
referring to a function object, andP
is the promise that will be resolved
with the result of calling them.The
then
andcatch
properties may be absent in some cases. A call to
then
can omit the rejection handler, and a call tocatch
omits the
fulfillment handler. Furthermore, various promise facilities create records
like this as internal implementation details, creating handlers that are not
representable as JavaScript functions. -
When a promise
P1
is resolved to another promiseP2
(that is, whenP2
is resolved,P1
will be resolved the same way), that adds a reaction
record toP2
. The array entry for that reaction record is simply the
Debugger.Object
representingP1
. -
An
await
expression creates an internal promiseA
, resolves it to its
operand, and then adds a reaction record toA
that resumes the suspended
call appropriately. The array entry for that reaction record is a
Debugger.Frame
representing the suspended call.If an
await
's operand is a promiseP
, this means we can obtain the
Debugger.Frame
for the suspended call by looking throughP
's reaction
records for the simple promiseA
, and then look throughA
's reaction
records (there should be only one) for theDebugger.Frame
for the call.
Pushed by jblandy@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/f16fced2b955 Let InternalAwait arg types show which handlers are actually used. r=arai
Comment 9•5 years ago
|
||
bugherder |
Assignee | ||
Comment 10•5 years ago
|
||
SpiderMonkey Promises use a hybrid representation for the list of reaction
records attached to a promise: an empty list is undefined
; a list of one
element is the reaction record itself, and a list of two or more records is an
array.
TriggerPromiseReactions and PromiseObject::dependentPromises duplicate the code
for walking over a reaction record list. Later patches in the series will
introduce a third function that does it, so it seemed like a good time to
abstract out the iteration.
This patch defines the function 'ForEachReaction', which applies a closure to
each reaction in the list, and changes existing code to use it.
Assignee | ||
Comment 11•5 years ago
|
||
Subsequent patches introduce Debugger.Object.prototype.promiseReactions, which
will be the first case in which we've ever needed to create a Debugger.Frame for
a suspended generator call. All prior generator support creates Debugger.Frames
only for frames on the stack.
This patch changes DebuggerFrame::create to take both a FrameIter and a
generator object, both optional (one or the other must be present), and create a
frame with whatever data is provided. The sole caller is adjusted accordingly.
Depends on D56835
Assignee | ||
Comment 12•5 years ago
|
||
Responsibility for creating entries in Debugger::generatorFrames was moved to
DebuggerFrame::setGenerator, so Debugger::getFrame is only doing lookups now,
and can use the more restricted Ptr
type for the results of its lookups.
Depends on D56836
Assignee | ||
Comment 13•5 years ago
|
||
Debugger::generatorFrames and Debugger::frames used to have looser types, and
the code working with them needed to downcast in order to work with more
specific object types. But now the key and value types in those maps are
well-typed, so the downcasts do nothing and can be removed.
Depends on D56837
Assignee | ||
Comment 14•5 years ago
|
||
See documentation in js/src/doc/Debugger/Debugger.Object.md.
Depends on D56838
Comment 15•5 years ago
|
||
Pushed by jblandy@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/0c8f04b2a6dc Introduce ForEachReaction helper function. r=arai https://hg.mozilla.org/integration/autoland/rev/35be3ee203a8 Let DebuggerFrame::create build both on-stack and suspended frames. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/cb76becb05c3 Don't use an AddPtr to access generatorFrames in Debugger::getFrame. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/b3a05fee765f Remove as<...> casts from Debugger frame handling. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/fb6cac74e451 Implement Debugger.Object.prototype.promiseReactions. r=arai,loganfsmyth
Comment 16•5 years ago
|
||
Backed out 5 changesets (bug 1592427) for causing build bustages on debugger/Object.cpp CLOSED TREE
Backout revision https://hg.mozilla.org/integration/autoland/rev/a9fe5d6acd655181d94ca4e68c0aed7f34c5053b
Failure log https://treeherder.mozilla.org/logviewer.html#?job_id=280987337&repo=autoland
Jim can you please take a look ?
Assignee | ||
Comment 18•5 years ago
|
||
The static analysis found a GC rooting problem.
Comment 19•5 years ago
|
||
Pushed by jblandy@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/f0ca73c538e7 Introduce ForEachReaction helper function. r=arai https://hg.mozilla.org/integration/autoland/rev/cf8aa85ab3da Let DebuggerFrame::create build both on-stack and suspended frames. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/595e5d95e650 Don't use an AddPtr to access generatorFrames in Debugger::getFrame. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/2cdf0ee12aaf Remove as<...> casts from Debugger frame handling. r=loganfsmyth https://hg.mozilla.org/integration/autoland/rev/496e56e95295 Implement Debugger.Object.prototype.promiseReactions. r=arai,loganfsmyth
Comment 20•5 years ago
|
||
bugherder |
Assignee | ||
Updated•5 years ago
|
Description
•