-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement HostPromiseRejectionTracker (#2530) #4608
Conversation
6d98ad6
to
f2df324
Compare
So all the windows test/debug builds are failing on the test I've written. Please could someone tell me how to make a baseline file that will pass on windows? (Reading the output shows that it's printing the right text) My guess is that this is something to do with line endings but I've tried setting my text editor to do CRLF line endings and it's still not working. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a nice addition. Thanks a lot for taking the time to implement this support.
I gave it a look over and have a couple of cursory questions.
We only call SetIsHandled
in JavascriptPromise::CreateThenPromise
right now. Is that the only time we know that a promise is definitely handled?
I'm a little confused about the third argument (rejected
) to the promise rejection tracker function. It looks like this indicates handled? All of the promises we pass to this function were rejected, yes?
Otherwise, seems sound. Don't worry too much about ch test hook quality - as long as it's on-par with other stuff in there shouldn't be a problem.
Your line-endings may be a problem. Ideally, try to make the baselines and commit them on windows. 😁 We have some settings in our gitconfig which may be messing up your files. I guess you could run unix2dos
on them.
As for where to check-in, you retarget onto master. We won't be able to take this in release/1.8 and it's unlikely we'll cut more Node-ChakraCore releases using release/1.9.
I will give this a closer look and provide some more specific feedbacks but thanks again for digging in and contributing here.
087e3fe
to
4142600
Compare
@boingoing thanks for the comments:
|
cfbd914
to
dcdaf1f
Compare
To answer that question, yes, all promises passed to the callback are rejected. The third parameter indicates whether the promise has just been rejected without a handler, or if it was rejected previously and is only now having a handler attached. It corresponds to the |
4769f67
to
3a1dc62
Compare
(only here to comment on the API surface change, not implementation) Thanks for this very welcomed API addition @rhuanjl ! It looks nice and I appreciate it directly mirroring concepts in the spec. Couple of comments -
|
@liminzhu thanks for the comments: |
c1af310
to
e7c64ab
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of naming/semantic issues and a copy/paste mishap. Other than that the change looks good, nice and clean. Works great in miniSphere, too. 🐖
lib/Jsrt/ChakraCore.h
Outdated
/// A Promise Rejection Tracker callback may be set - which will then be called when this occurs. | ||
/// </remarks> | ||
/// <param name="promise">The promise object, represented as a JsValueRef.</param> | ||
/// <param name="resolution">The value/cause of the rejection, represented as a JsValueRef.</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolution
should be renamed, to either reason
--or if you want something more generic, value
. As far as promises go, "resolve" usually only refers to fulfillment (e.g. Promise.resolve()
is never a rejection unless you specifically pass it a rejected promise) so the current name is maybe a bit confusing.
lib/Jsrt/ChakraCore.h
Outdated
/// </remarks> | ||
/// <param name="promise">The promise object, represented as a JsValueRef.</param> | ||
/// <param name="resolution">The value/cause of the rejection, represented as a JsValueRef.</param> | ||
/// <param name="reject">Boolean - true if the promise has just been rejected with no handler, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline I'm still of the opinion that if we keep this as a boolean, the meaning should be reversed, i.e. it should be false for "rejected" and true for "handler attached".
lib/Jsrt/ChakraCore.h
Outdated
/// Requires an active script context. | ||
/// </para> | ||
/// </remarks> | ||
/// <param name="callbackState"> The callback function being set.</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be
/// <param name="promiseRejectionTrackerCallback"> ... </param>
Correct?
8a15dc3
to
abf54d1
Compare
@liminzhu I've adjusted for my friend @fatcerberus 's comments which seemed sensible to me which included:
I have also moved the API to chakracore.h @akroshg do you know when you will have a chance to review this? CI failure appears to be unrelated (I can repro the fail with a clean download of master or 1.9 without applying this PR) I think CI on both master and 1.9 has been blocked since this PR: #4625 |
#4632 should fix it |
abf54d1
to
fe5a34d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some minor nitpicks. @boingoing one benefit of putting this in release/1.9 would be that we could fix Node-ChakraCore's feature gap around this sooner? Not sure if that matters.
void JavascriptLibrary::SetNativeHostPromiseRejectionTrackerCallback(HostPromiseRejectionTrackerCallback function) | ||
{ | ||
this->nativeHostPromiseRejectionTracker = function; | ||
this->hostShouldTrackPromiseRejections = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: set nativeHostPromiseRejectionTracker
to nullptr upfront so that you can avoid this bool and just check if the function pointer is nullptr or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - though I've now added a callbackState in the place of this.
@@ -461,6 +461,8 @@ namespace Js | |||
PromiseStatusCode_HasRejection | |||
}; | |||
|
|||
bool GetIsHandled() {return isHandled;} | |||
void SetIsHandled() {isHandled = true;} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: spacing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
lib/Jsrt/ChakraCore.h
Outdated
@@ -993,5 +1008,26 @@ CHAKRA_API | |||
_In_ JsValueRef object, | |||
_In_ JsValueRef key, | |||
_Out_ bool *hasOwnProperty); | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra newline #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
@@ -949,6 +953,9 @@ namespace Js | |||
JavascriptFunction* GetThrowerFunction() const { return throwerFunction; } | |||
|
|||
void SetNativeHostPromiseContinuationFunction(PromiseContinuationCallback function, void *state); | |||
void SetNativeHostPromiseRejectionTrackerCallback(HostPromiseRejectionTrackerCallback function); | |||
void CallNativeHostPromiseRejectionTracker(Var promise, Var reason, bool handled); | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extra newline #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
bin/ch/ch.cpp
Outdated
if(HostConfigFlags::flags.TrackRejectedPromises) | ||
{ | ||
ChakraRTInterface::JsSetHostPromiseRejectionTracker(WScriptJsrt::PromiseRejectionTrackerCallback); | ||
} | ||
len = strlen(fullPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add a newline here #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Between the closing curly and the next line #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
bin/ch/WScriptJsrt.cpp
Outdated
Assert(reason != JS_INVALID_REFERENCE); | ||
if(!handled) | ||
{ | ||
wprintf(_u("Uncaught promise rejection\n")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also print out the message to ensure that the reason
is being set correctly? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I may add further commentary to this, it might make more sense to say "Promise rejected with no handler attached" rather than "Uncaught promise rejection". We don't know whether or not it will remain uncaught at this time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated to print the reason.
I've left it as "Uncaught promise rejection" - as at the time of notification it is uncaught - it may be handled later but it is not at the moment of notification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with minor fixes
@@ -60,3 +60,5 @@ JsLessThan | |||
JsLessThanOrEqual | |||
|
|||
JsCreateEnhancedFunction | |||
|
|||
JsSetHostPromiseRejectionTracker |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be a newline at end of file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
bin/ch/ChakraRtInterface.h
Outdated
@@ -356,6 +358,7 @@ class ChakraRTInterface | |||
static JsErrorCode WINAPI JsGetValueType(JsValueRef value, JsValueType *type) { return HOOK_JS_API(GetValueType(value, type)); } | |||
static JsErrorCode WINAPI JsSetIndexedProperty(JsValueRef object, JsValueRef index, JsValueRef value) { return HOOK_JS_API(SetIndexedProperty(object, index, value)); } | |||
static JsErrorCode WINAPI JsSetPromiseContinuationCallback(JsPromiseContinuationCallback callback, void *callbackState) { return HOOK_JS_API(SetPromiseContinuationCallback(callback, callbackState)); } | |||
static JsErrorCode WINAPI JsSetHostPromiseRejectionTracker(JsHostPromiseRejectionTrackerCallback callback) { return HOOK_JS_API(SetHostPromiseRejectionTracker(callback)); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the diff and seeing that literally all the other callbacks surrounding this get a callbackState
argument, I think we should have it here for consistency too, even if I question its real-world utility in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
bin/ch/WScriptJsrt.cpp
Outdated
Assert(reason != JS_INVALID_REFERENCE); | ||
if(!handled) | ||
{ | ||
wprintf(_u("Uncaught promise rejection\n")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I may add further commentary to this, it might make more sense to say "Promise rejected with no handler attached" rather than "Uncaught promise rejection". We don't know whether or not it will remain uncaught at this time.
Thanks for the reviews @kfarnung @jackhorton @fatcerberus |
@curtisman @akroshg Can one of you take a look? #Closed |
3e050b8
to
87dc5ef
Compare
@kfarnung sorry I didn't respond on Friday, I was away without a computer. I've updated to latest master and squashed including your commit and adding some extra tests as requested by @MSLaguana |
Thanks for adding those additional tests, looks good to me! |
test/es7/PromiseRejectionTracking.js
Outdated
|
||
let tests = [ | ||
{ | ||
name: "Reject promise with no reactions.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super nit: looks like you used tabs here, can you switch over to spaces to match the majority of the files in the same directory? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry about that, fixed.
87dc5ef
to
812e237
Compare
@dotnet-bot test Windows arm_test please |
812e237
to
3b29e2f
Compare
@kfarnung any update on this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few more nit fixes
bin/ch/WScriptJsrt.cpp
Outdated
JsValueRef strValue; | ||
JsErrorCode error = ChakraRTInterface::JsConvertValueToString(reason, &strValue); | ||
|
||
if(!handled) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: spacing between if
and condition #Closed
bin/ch/ch.cpp
Outdated
@@ -757,6 +757,11 @@ HRESULT ExecuteTest(const char* fileName) | |||
IfFailGo(E_FAIL); | |||
} | |||
|
|||
if(HostConfigFlags::flags.TrackRejectedPromises) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space between if
and condition #Closed
|
||
void JavascriptLibrary::CallNativeHostPromiseRejectionTracker(Var promise, Var reason, bool handled) | ||
{ | ||
if(nativeHostPromiseRejectionTracker != nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space between if
and condition #Closed
|
||
void JavascriptLibrary::CallNativeHostPromiseRejectionTracker(Var promise, Var reason, bool handled) | ||
{ | ||
if(nativeHostPromiseRejectionTracker != nullptr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this->nativeHostPromiseRejectionTracker
#Closed
BEGIN_LEAVE_SCRIPT(scriptContext); | ||
try | ||
{ | ||
nativeHostPromiseRejectionTracker(promise, reason, handled, this->nativeHostPromiseContinuationFunctionState); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this->nativeHostPromiseRejectionTracker
#Closed
@@ -660,6 +661,10 @@ namespace Js | |||
{ | |||
reactions = this->GetRejectReactions(); | |||
newStatus = PromiseStatusCode_HasRejection; | |||
if(!GetIsHandled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space between if
and condition #Closed
@@ -838,13 +843,19 @@ namespace Js | |||
EnqueuePromiseReactionTask(resolveReaction, sourcePromise->result, scriptContext); | |||
break; | |||
case PromiseStatusCode_HasRejection: | |||
if(!sourcePromise->GetIsHandled()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: space between if
and condition #Closed
Hey @rhuanjl, thanks for your patience. I'm still doing some local testing. I pushed a minor commit to exclude the new test from one of our suites. #Closed |
@kfarnung I fixed the new nits, I'll hold off on squashing for now as I assume there's more testing and at least one more review to come? |
@liminzhu @bterlson Any concerns with the API surface for this change? I didn't see any follow up on the previous question:
@rhuanjl I don't see any other issues with this change, I'm just trying to make sure I cover all the bases. #Closed |
d93de9e
to
dfff5a9
Compare
@kfarnung thanks very much for the help with this. Do you know if there is any chance of this getting landed soon. I was hoping to prepare a PR for Promise.prototype.finally this weekend but it will be affecting several of the same files and nervous I'll end up with merge conflicts |
@@ -470,6 +472,7 @@ namespace Js | |||
protected: | |||
Field(PromiseStatus) status; | |||
Field(Var) result; | |||
Field(bool) isHandled; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move this up above var just to get better packing? instead of 40 bytes it can be in 32 bytes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
<files>PromiseRejectionTracking.js</files> | ||
<compile-flags>-TrackRejectedPromises -args summary -endargs -nodeferparse</compile-flags> | ||
<baseline>PromiseRejectionTracking.baseline</baseline> | ||
<tags>exclude_jshost</tags> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exclude_jshost [](start = 12, length = 14)
Great. Thanks for covering with detailed test cases. @[email protected] should we be implementing this in jshost just to get consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was the one who added the tag. I think this is something we should do in the near future, it's just a matter of scheduling.
/cc @boingoing
@rhuanjl thanks for PR. Looks good to me. (apologize for adding a late comment). |
Looks good to me too! |
1. Internal CC machinary for unhandled promise rejection. 2. Simplisitic implimentation in ch 3. Test case
dfff5a9
to
0b61d16
Compare
Fixed last point and updated to latest. Thanks for reviews everyone; sorry for the number of style issues, I'll try and be more consistent in future. |
Merge pull request #4608 from rhuanjl:HostPromiseRejection Responding to #2530 This PR contains: 1. Internal CC machinery for HostPromiseRejectionTracker [ecmaspec 25.4.1.9](https://tc39.github.io/ecma262/#sec-host-promise-rejection-tracker) - this should enable any host to implement a handler for uncaught promise rejections e.g. the method defined here [WhatW spec 8.1.3.12](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections) 2. A very simplistic implementation in ch (behind a command line flag) 3. Test case I'm expecting feedback/changes before this is merged but wanted to get other people's thoughts on it in this state. Please give me your thoughts/comments. Cross ref: [Edge uservoice request for this feature](https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/16665397-detect-unhandled-promise-rejection?tracking_code=4f4e218df62afae7db37fb5be5f4ff1c) - note this PR will not put the feature in edge - it just creates the hooks the edge development team would have to track them through. **cc** @liminzhu @saschanaz @fatcerberus @MSLaguana **Notes:** **1. Not spec compliant for "await" EDIT: snip - re-read spec this comment was wrong what I've done in this PR is correct** **2. The ch implementation I've done is pretty bad,** it simply prints to the terminal whenever a notification is made, either handled or rejected - reasonable for testing that the interface is operational but certainly not what the WhatW spec would have a Host do. **3. Parameters passed to the callback function** - per spec HostPromiseRejectionTracker should be passed the Promise and rejected/handled. I've gone for: a) the promise b) the promise's result - I know this could be retrieved from the promise object but I've added it for implementer's convenience c) rejected/handled boolean - true for handled false for rejected - possibly should be an int or an enum of some kind
@rhuanjl Thanks for your contribution! |
Thanks @rhuanjl ! Now that this has been merged, can I ask you to send a quick PR to https://github.com/Microsoft/ChakraCore-wiki to update the docs as well (this should be an example for where to make the changes). I'm happy to do that as well so let me know. |
Responding to #2530
This PR contains:
I'm expecting feedback/changes before this is merged but wanted to get other people's thoughts on it in this state. Please give me your thoughts/comments.
Cross ref: Edge uservoice request for this feature - note this PR will not put the feature in edge - it just creates the hooks the edge development team would have to track them through.
cc @liminzhu @saschanaz @fatcerberus @MSLaguana
Notes:
1. Not spec compliant for "await" EDIT: snip - re-read spec this comment was wrong what I've done in this PR is correct
2. The ch implementation I've done is pretty bad, it simply prints to the terminal whenever a notification is made, either handled or rejected - reasonable for testing that the interface is operational but certainly not what the WhatW spec would have a Host do.
3. Parameters passed to the callback function - per spec HostPromiseRejectionTracker should be passed the Promise and rejected/handled. I've gone for:
a) the promise
b) the promise's result - I know this could be retrieved from the promise object but I've added it for implementer's convenience
c) rejected/handled boolean - true for handled false for rejected - possibly should be an int or an enum of some kind