Implement Promise.try
Categories
(Core :: JavaScript Engine, enhancement, P3)
Tracking
()
Tracking | Status | |
---|---|---|
firefox132 | --- | fixed |
People
(Reporter: dminor, Assigned: martin, Mentored)
References
(Blocks 1 open bug)
Details
(Keywords: dev-doc-complete, good-first-bug)
Attachments
(1 file)
Implementing the Promise.try
proposal can likely be done completely in self-hosted JavaScript, making this a good project for someone new to contributing to SpiderMonkey.
Updated•5 months ago
|
Updated•5 months ago
|
Assignee | ||
Comment 1•5 months ago
|
||
Hi [:dminor],
I'm a first time contributor, and I'd love to pick this up if that's OK?
I've worked through Building Firefox on Linux and Building and testing spider monkey. I'm now familarising with the proposal, if you have any suggestions for the next step after that it would be really appreciated.
Thanks,
Martin
Reporter | ||
Comment 2•5 months ago
|
||
Hi Martin,
Great, thanks for your interest :) For the implementation, I would take a look at this function that was added for the Promise.withResolvers proposal: https://searchfox.org/mozilla-central/rev/0b1d02b2cb5736511139cf0e40b318273e825899/js/src/builtin/Promise.cpp#4923, and the work that Tom did on this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1845586. That should give you an idea of where to add the code and hopefully how to approach the implementation.
Please let me know if you have any questions, or get need some extra help getting things working :)
Daniel
Assignee | ||
Comment 3•5 months ago
|
||
Amazing, thanks for the pointers
Assignee | ||
Comment 4•5 months ago
|
||
Hi Daniel,
I wondered if you'd mind giving a few more points. So far I've added a JS_SELF_HOSTED_FN
to promise_static_methods
in js/src/builtin/Promise.cpp
, and a new function:
/**
* https://tc39.es/proposal-promise-try/
*/
function Promise_try(f, ...args) {
//1. Let C be the this value.
var C = this;
//2. If C is not an Object, throw a TypeError exception.
if (!IsObject(C)) {
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "Promise", "try", "value");
}
/*
3. Let promiseCapability be ? NewPromiseCapability(C).
4. Let status be Completion(Call(callbackfn, undefined, args)).
5. If status is an abrupt completion, then
a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).
6. Else,
a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »).
7. Return promiseCapability.[[Promise]].
*/
}
to js/src/builtin/Promise.js
.
For
3. Let promiseCapability be ? NewPromiseCapability(C).
I was thinking that I'd need to expose NewPromiseCapability
as an instrinsic function in js/src/vm/SelfHosting.cpp
Am I along the right lines so far, or way off piste?
Thanks,
Martin
Reporter | ||
Comment 5•5 months ago
|
||
Sorry, I have been away and am just catching up on things. I'll try to have a look at this tomorrow.
Reporter | ||
Comment 6•4 months ago
|
||
Hi Martin,
I'm very sorry about the delay here, this week was the TC39 plenary, and that's taken up a lot of my time.
I think your best bet is to add things directly to js/src/builtin/Promise.cpp
because you will have direct access to NewPromiseCapability
instead of having to expose it to self-hosted JavaScript. Here's a start of an implementation for you:
/**
* https://tc39.es/proposal-promise-try/
*/
static bool Promise_static_try(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
// 1. Let C be the this value.
RootedValue cVal(cx, args.thisv());
// 2. If C is not an Object, throw a TypeError exception.
if (!cVal.isObject()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_OBJECT_REQUIRED,
"Receiver of Promise.try call");
return false;
}
// 3. Let promiseCapability be ? NewPromiseCapability(C).
RootedObject c(cx, &cVal.toObject());
Rooted<PromiseCapability> promiseCapability(cx);
if (!NewPromiseCapability(cx, c, &promiseCapability, false)) {
return false;
}
// 4. Let status be Completion(Call(callbackfn, undefined, args)).
// 5. If status is an abrupt completion, then
// 5.a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).
// 6. Else,
// 6.a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »).
}
The next steps would be to figure out how to use Call()
to call callbackfn
with the appropriate arguments. I think after that you can use CallPromiseRejectFunction
and CallPromiseResolveFunction
to handle the reject
and resolve
cases. You should be able to find lots of examples in Promise.cpp
. If you're not aware of it, we have a code search engine that can help find existing uses of these functions, e.g. here's a search for CallPromiseResolveFunction
.
I hope this helps! Again, I'm really sorry about the delay here. Please let me know if you need any more help :)
Assignee | ||
Comment 7•4 months ago
|
||
No problem whatsoever. Thanks a lot for the help, I think that will set me on the right track. I'll give it a shot and let you know!
Thanks
Assignee | ||
Comment 8•4 months ago
|
||
Reporter | ||
Comment 9•3 months ago
|
||
Hi Martin, how are things going on this? If you're stuck, please let us know, and we'll do our best to unblock you :)
Assignee | ||
Comment 10•3 months ago
|
||
hey Dan,
Thanks for the message and sorry it's dragging a bit!
I just replied to the phabricator request. As I am a bit stuck trying to get my head around request for two extra requests. Any pointers would be appreciated!
Thanks,
Martin
Reporter | ||
Comment 11•3 months ago
|
||
It looks like Arai is helping out over in phabricator, please let me know if there's anything I can do.
Comment 12•3 months ago
|
||
Hey, Martin,
I don't have the permission to comment on the reversion.
So I comment here.
https://tannal.github.io/comment.png
Thanks,
tannal
Assignee | ||
Comment 13•3 months ago
|
||
Thank you tannal,
I just gave that a go but couldn't get it to work.
I think the issue is that it is calling the callback function with a single argument that is an array, rather than packing the original arguments into separate InvokeArgs
arguments?
Some more context below in case I've missed something obvious
...
// 4. Let status be Completion(Call(callbackfn, undefined, args)).
size_t argCount = args.length();
if (argCount > 0) {
argCount--;
}
HandleValueArray iargs = HandleValueArray::subarray(args, 1, argCount);
HandleValue callbackfn = args.get(0);
RootedValue rval(cx);
bool ok = Call(cx, callbackfn, UndefinedHandleValue, iargs, &rval);
...
An example of one of the failing tests:
## test262/built-ins/Promise/try/args.js: rc = 0, run time = 0.019557
FAILED! TypeError: Promise is not a function
REGRESSION - test262/built-ins/Promise/try/args.js
Comment 14•3 months ago
|
||
I think the issue is that it is calling the callback function with a single argument that is an array, rather than packing the original arguments into separate
InvokeArgs
arguments?
Yes, we should copy them.
HandleValueArray is not a good idea.XD
Assignee | ||
Comment 15•3 months ago
|
||
Was good to try though, thank you
Assignee | ||
Comment 16•3 months ago
|
||
Hi Dan,
Thanks for the review and help :)
I tried to land it but get
You have insufficient permissions to land. Level 3 Commit Access is required. See the FAQ for help. Has an open ancestor revision that is blocked. The requested set of revisions are not landable.
What are the next steps now?
Thanks,
Martin
Reporter | ||
Comment 17•3 months ago
|
||
Hi Martin,
I'm trying the changes on CI right now: https://treeherder.mozilla.org/jobs?repo=try&selectedTaskRun=a2jHKcpHSbq7KWlMGRtRLA.0&revision=ad9985f7c143beb7c1b35b7f170fb473a63d407e.
I'll land the changes for you once that is complete, it looks fine.
Reporter | ||
Comment 18•3 months ago
|
||
Hi Martin, unfortunately, your changes need to be rebased before I can land them: https://lando.services.mozilla.com/D218494/.
Assignee | ||
Comment 19•3 months ago
|
||
Thanks Dan, I've done that and resubmitted to phabricator. Was that the right thing to do?
Cheers
Comment 20•3 months ago
|
||
Comment 21•3 months ago
|
||
bugherder |
Description
•