diff --git a/bin/ChakraCore/ChakraCore.def b/bin/ChakraCore/ChakraCore.def
index 38df743e296..3010e888d9b 100644
--- a/bin/ChakraCore/ChakraCore.def
+++ b/bin/ChakraCore/ChakraCore.def
@@ -62,3 +62,5 @@ JsLessThan
JsLessThanOrEqual
JsCreateEnhancedFunction
+
+JsSetHostPromiseRejectionTracker
diff --git a/bin/ch/ChakraRtInterface.cpp b/bin/ch/ChakraRtInterface.cpp
index 191fa1c499f..503d6b8c025 100644
--- a/bin/ch/ChakraRtInterface.cpp
+++ b/bin/ch/ChakraRtInterface.cpp
@@ -120,6 +120,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
m_jsApiHooks.pfJsrtGetValueType = (JsAPIHooks::JsrtGetValueType)GetChakraCoreSymbol(library, "JsGetValueType");
m_jsApiHooks.pfJsrtSetIndexedProperty = (JsAPIHooks::JsrtSetIndexedPropertyPtr)GetChakraCoreSymbol(library, "JsSetIndexedProperty");
m_jsApiHooks.pfJsrtSetPromiseContinuationCallback = (JsAPIHooks::JsrtSetPromiseContinuationCallbackPtr)GetChakraCoreSymbol(library, "JsSetPromiseContinuationCallback");
+ m_jsApiHooks.pfJsrtSetHostPromiseRejectionTracker = (JsAPIHooks::JsrtSetHostPromiseRejectionTrackerPtr)GetChakraCoreSymbol(library, "JsSetHostPromiseRejectionTracker");
m_jsApiHooks.pfJsrtGetContextOfObject = (JsAPIHooks::JsrtGetContextOfObject)GetChakraCoreSymbol(library, "JsGetContextOfObject");
m_jsApiHooks.pfJsrtInitializeModuleRecord = (JsAPIHooks::JsInitializeModuleRecordPtr)GetChakraCoreSymbol(library, "JsInitializeModuleRecord");
m_jsApiHooks.pfJsrtParseModuleSource = (JsAPIHooks::JsParseModuleSourcePtr)GetChakraCoreSymbol(library, "JsParseModuleSource");
diff --git a/bin/ch/ChakraRtInterface.h b/bin/ch/ChakraRtInterface.h
index a81cd63d397..5bd00024930 100644
--- a/bin/ch/ChakraRtInterface.h
+++ b/bin/ch/ChakraRtInterface.h
@@ -54,6 +54,7 @@ struct JsAPIHooks
typedef JsErrorCode (WINAPI *JsrtGetValueType)(JsValueRef value, JsValueType *type);
typedef JsErrorCode (WINAPI *JsrtSetIndexedPropertyPtr)(JsValueRef object, JsValueRef index, JsValueRef value);
typedef JsErrorCode (WINAPI *JsrtSetPromiseContinuationCallbackPtr)(JsPromiseContinuationCallback callback, void *callbackState);
+ typedef JsErrorCode (WINAPI *JsrtSetHostPromiseRejectionTrackerPtr)(JsHostPromiseRejectionTrackerCallback callback, void *callbackState);
typedef JsErrorCode (WINAPI *JsrtGetContextOfObject)(JsValueRef object, JsContextRef *callbackState);
typedef JsErrorCode(WINAPI *JsrtDiagStartDebugging)(JsRuntimeHandle runtimeHandle, JsDiagDebugEventCallback debugEventCallback, void* callbackState);
@@ -152,6 +153,7 @@ struct JsAPIHooks
JsrtGetValueType pfJsrtGetValueType;
JsrtSetIndexedPropertyPtr pfJsrtSetIndexedProperty;
JsrtSetPromiseContinuationCallbackPtr pfJsrtSetPromiseContinuationCallback;
+ JsrtSetHostPromiseRejectionTrackerPtr pfJsrtSetHostPromiseRejectionTracker;
JsrtGetContextOfObject pfJsrtGetContextOfObject;
JsrtDiagStartDebugging pfJsrtDiagStartDebugging;
JsrtDiagStopDebugging pfJsrtDiagStopDebugging;
@@ -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, void *callbackState) { return HOOK_JS_API(SetHostPromiseRejectionTracker(callback, callbackState)); }
static JsErrorCode WINAPI JsGetContextOfObject(JsValueRef object, JsContextRef* context) { return HOOK_JS_API(GetContextOfObject(object, context)); }
static JsErrorCode WINAPI JsDiagStartDebugging(JsRuntimeHandle runtimeHandle, JsDiagDebugEventCallback debugEventCallback, void* callbackState) { return HOOK_JS_API(DiagStartDebugging(runtimeHandle, debugEventCallback, callbackState)); }
static JsErrorCode WINAPI JsDiagStopDebugging(JsRuntimeHandle runtimeHandle, void** callbackState) { return HOOK_JS_API(DiagStopDebugging(runtimeHandle, callbackState)); }
diff --git a/bin/ch/HostConfigFlagsList.h b/bin/ch/HostConfigFlagsList.h
index a2fa5bd75b1..0749efa34ec 100644
--- a/bin/ch/HostConfigFlagsList.h
+++ b/bin/ch/HostConfigFlagsList.h
@@ -15,5 +15,6 @@ FLAG(bool, IgnoreScriptErrorCode, "Don't return error code on script e
FLAG(bool, MuteHostErrorMsg, "Mute host error output, e.g. module load failures", false)
FLAG(bool, TraceHostCallback, "Output traces for host callbacks", false)
FLAG(bool, Test262, "load Test262 harness", false)
+FLAG(bool, TrackRejectedPromises, "Enable tracking of unhandled promise rejections", false)
#undef FLAG
#endif
diff --git a/bin/ch/WScriptJsrt.cpp b/bin/ch/WScriptJsrt.cpp
index 485ed60d73c..10e9b464d81 100644
--- a/bin/ch/WScriptJsrt.cpp
+++ b/bin/ch/WScriptJsrt.cpp
@@ -1871,3 +1871,32 @@ void WScriptJsrt::PromiseContinuationCallback(JsValueRef task, void *callbackSta
WScriptJsrt::CallbackMessage *msg = new WScriptJsrt::CallbackMessage(0, task);
messageQueue->InsertSorted(msg);
}
+
+void WScriptJsrt::PromiseRejectionTrackerCallback(JsValueRef promise, JsValueRef reason, bool handled, void *callbackState)
+{
+ Assert(promise != JS_INVALID_REFERENCE);
+ Assert(reason != JS_INVALID_REFERENCE);
+ JsValueRef strValue;
+ JsErrorCode error = ChakraRTInterface::JsConvertValueToString(reason, &strValue);
+
+ if(!handled)
+ {
+ wprintf(_u("Uncaught promise rejection\n"));
+ }
+ else
+ {
+ wprintf(_u("Promise rejection handled\n"));
+ }
+
+ if (error == JsNoError)
+ {
+ AutoString str(strValue);
+ if (str.GetError() == JsNoError)
+ {
+ wprintf(_u("%ls\n"), str.GetWideString());
+ }
+ }
+
+ fflush(stdout);
+}
+
diff --git a/bin/ch/WScriptJsrt.h b/bin/ch/WScriptJsrt.h
index 3ac3be5452f..580baaca933 100644
--- a/bin/ch/WScriptJsrt.h
+++ b/bin/ch/WScriptJsrt.h
@@ -58,6 +58,7 @@ class WScriptJsrt
static JsErrorCode NotifyModuleReadyCallback(_In_opt_ JsModuleRecord referencingModule, _In_opt_ JsValueRef exceptionVar);
static JsErrorCode InitializeModuleCallbacks();
static void CALLBACK PromiseContinuationCallback(JsValueRef task, void *callbackState);
+ static void CALLBACK PromiseRejectionTrackerCallback(JsValueRef promise, JsValueRef reason, bool handled, void *callbackState);
static LPCWSTR ConvertErrorCodeToMessage(JsErrorCode errorCode)
{
diff --git a/bin/ch/ch.cpp b/bin/ch/ch.cpp
index fbd39ecfe47..ac3a3ad66fc 100644
--- a/bin/ch/ch.cpp
+++ b/bin/ch/ch.cpp
@@ -757,6 +757,11 @@ HRESULT ExecuteTest(const char* fileName)
IfFailGo(E_FAIL);
}
+ if(HostConfigFlags::flags.TrackRejectedPromises)
+ {
+ ChakraRTInterface::JsSetHostPromiseRejectionTracker(WScriptJsrt::PromiseRejectionTrackerCallback, nullptr);
+ }
+
len = strlen(fullPath);
if (HostConfigFlags::flags.GenerateLibraryByteCodeHeaderIsEnabled)
{
diff --git a/lib/Jsrt/ChakraCore.h b/lib/Jsrt/ChakraCore.h
index c0c7dd2a1cb..2aefcfd9aff 100644
--- a/lib/Jsrt/ChakraCore.h
+++ b/lib/Jsrt/ChakraCore.h
@@ -130,6 +130,27 @@ typedef struct JsNativeFunctionInfo
/// The result of the call, if any.
typedef _Ret_maybenull_ JsValueRef(CHAKRA_CALLBACK * JsEnhancedNativeFunction)(_In_ JsValueRef callee, _In_ JsValueRef *arguments, _In_ unsigned short argumentCount, _In_ JsNativeFunctionInfo *info, _In_opt_ void *callbackState);
+///
+/// A Promise Rejection Tracker callback.
+///
+///
+/// The host can specify a promise rejection tracker callback in JsSetHostPromiseRejectionTracker.
+/// If a promise is rejected with no reactions or a reaction is added to a promise that was rejected
+/// before it had reactions by default nothing is done.
+/// A Promise Rejection Tracker callback may be set - which will then be called when this occurs.
+/// Note - per draft ECMASpec 2018 25.4.1.9 this function should not set or return an exception
+/// Note also the promise and reason parameters may be garbage collected after this function is called
+/// if you wish to make further use of them you will need to use JsAddRef to preserve them
+/// However if you use JsAddRef you must also call JsRelease and not hold unto them after
+/// a handled notification (both per spec and to avoid memory leaks)
+///
+/// The promise object, represented as a JsValueRef.
+/// The value/cause of the rejection, represented as a JsValueRef.
+/// Boolean - false for promiseRejected: i.e. if the promise has just been rejected with no handler,
+/// true for promiseHandled: i.e. if it was rejected before without a handler and is now being handled.
+/// The state passed to JsSetHostPromiseRejectionTracker.
+typedef void (CHAKRA_CALLBACK *JsHostPromiseRejectionTrackerCallback)(_In_ JsValueRef promise, _In_ JsValueRef reason, _In_ bool handled, _In_opt_ void *callbackState);
+
///
/// Creates a new enhanced JavaScript function.
///
@@ -993,5 +1014,27 @@ CHAKRA_API
_In_ JsValueRef object,
_In_ JsValueRef key,
_Out_ bool *hasOwnProperty);
+
+///
+/// Sets whether any action should be taken when a promise is rejected with no reactions
+/// or a reaction is added to a promise that was rejected before it had reactions.
+/// By default in either of these cases nothing occurs.
+/// This function allows you to specify if something should occur and provide a callback
+/// to implement whatever should occur.
+///
+///
+/// Requires an active script context.
+///
+/// The callback function being set.
+///
+/// User provided state that will be passed back to the callback.
+///
+///
+/// The code JsNoError if the operation succeeded, a failure code otherwise.
+///
+CHAKRA_API
+ JsSetHostPromiseRejectionTracker(
+ _In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback,
+ _In_opt_ void *callbackState);
#endif // _CHAKRACOREBUILD
#endif // _CHAKRACORE_H_
diff --git a/lib/Jsrt/Jsrt.cpp b/lib/Jsrt/Jsrt.cpp
index 2157ba062db..994d2f59cd7 100644
--- a/lib/Jsrt/Jsrt.cpp
+++ b/lib/Jsrt/Jsrt.cpp
@@ -5334,4 +5334,13 @@ CHAKRA_API JsGetDataViewInfo(
END_JSRT_NO_EXCEPTION
}
+CHAKRA_API JsSetHostPromiseRejectionTracker(_In_ JsHostPromiseRejectionTrackerCallback promiseRejectionTrackerCallback, _In_opt_ void *callbackState)
+{
+ return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
+ scriptContext->GetLibrary()->SetNativeHostPromiseRejectionTrackerCallback((Js::JavascriptLibrary::HostPromiseRejectionTrackerCallback) promiseRejectionTrackerCallback, callbackState);
+ return JsNoError;
+ },
+ /*allowInObjectBeforeCollectCallback*/true);
+}
+
#endif // _CHAKRACOREBUILD
diff --git a/lib/Runtime/Library/JavascriptLibrary.cpp b/lib/Runtime/Library/JavascriptLibrary.cpp
index e7b91ff2aff..213836ea568 100644
--- a/lib/Runtime/Library/JavascriptLibrary.cpp
+++ b/lib/Runtime/Library/JavascriptLibrary.cpp
@@ -5295,6 +5295,32 @@ namespace Js
this->nativeHostPromiseContinuationFunctionState = state;
}
+ void JavascriptLibrary::SetNativeHostPromiseRejectionTrackerCallback(HostPromiseRejectionTrackerCallback function, void *state)
+ {
+ this->nativeHostPromiseRejectionTracker = function;
+ this->nativeHostPromiseContinuationFunctionState = state;
+ }
+
+ void JavascriptLibrary::CallNativeHostPromiseRejectionTracker(Var promise, Var reason, bool handled)
+ {
+ if(nativeHostPromiseRejectionTracker != nullptr)
+ {
+ BEGIN_LEAVE_SCRIPT(scriptContext);
+ try
+ {
+ nativeHostPromiseRejectionTracker(promise, reason, handled, this->nativeHostPromiseContinuationFunctionState);
+ }
+ catch (...)
+ {
+ // Hosts are required not to pass exceptions back across the callback boundary. If
+ // this happens, it is a bug in the host, not something that we are expected to
+ // handle gracefully.
+ Js::Throw::FatalInternalError();
+ }
+ END_LEAVE_SCRIPT(scriptContext);
+ }
+ }
+
void JavascriptLibrary::SetJsrtContext(FinalizableObject* jsrtContext)
{
// With JsrtContext supporting cross context, ensure that it doesn't get GCed
diff --git a/lib/Runtime/Library/JavascriptLibrary.h b/lib/Runtime/Library/JavascriptLibrary.h
index 6d94aedaee6..2f2ecd6a256 100644
--- a/lib/Runtime/Library/JavascriptLibrary.h
+++ b/lib/Runtime/Library/JavascriptLibrary.h
@@ -242,6 +242,7 @@ namespace Js
static DWORD GetRandSeed1Offset() { return offsetof(JavascriptLibrary, randSeed1); }
static DWORD GetTypeDisplayStringsOffset() { return offsetof(JavascriptLibrary, typeDisplayStrings); }
typedef bool (CALLBACK *PromiseContinuationCallback)(Var task, void *callbackState);
+ typedef void (CALLBACK *HostPromiseRejectionTrackerCallback)(Var promise, Var reason, bool handled, void *callbackState);
Var GetUndeclBlockVar() const { return undeclBlockVarSentinel; }
bool IsUndeclBlockVar(Var var) const { return var == undeclBlockVarSentinel; }
@@ -492,6 +493,9 @@ namespace Js
FieldNoBarrier(PromiseContinuationCallback) nativeHostPromiseContinuationFunction;
Field(void *) nativeHostPromiseContinuationFunctionState;
+ FieldNoBarrier(HostPromiseRejectionTrackerCallback) nativeHostPromiseRejectionTracker = nullptr;
+ Field(void *) nativeHostPromiseRejectionTrackerState;
+
typedef SList FunctionReferenceList;
typedef JsUtil::WeakReferenceDictionary> JsrtExternalTypesCache;
@@ -949,6 +953,8 @@ namespace Js
JavascriptFunction* GetThrowerFunction() const { return throwerFunction; }
void SetNativeHostPromiseContinuationFunction(PromiseContinuationCallback function, void *state);
+ void SetNativeHostPromiseRejectionTrackerCallback(HostPromiseRejectionTrackerCallback function, void *state);
+ void CallNativeHostPromiseRejectionTracker(Var promise, Var reason, bool handled);
void SetJsrtContext(FinalizableObject* jsrtContext);
FinalizableObject* GetJsrtContext();
diff --git a/lib/Runtime/Library/JavascriptPromise.cpp b/lib/Runtime/Library/JavascriptPromise.cpp
index 27a33e62bcf..07a1424d2c8 100644
--- a/lib/Runtime/Library/JavascriptPromise.cpp
+++ b/lib/Runtime/Library/JavascriptPromise.cpp
@@ -12,6 +12,7 @@ namespace Js
Assert(type->GetTypeId() == TypeIds_Promise);
this->status = PromiseStatusCode_Undefined;
+ this->isHandled = false;
this->result = nullptr;
this->resolveReactions = nullptr;
this->rejectReactions = nullptr;
@@ -660,6 +661,10 @@ namespace Js
{
reactions = this->GetRejectReactions();
newStatus = PromiseStatusCode_HasRejection;
+ if(!GetIsHandled())
+ {
+ scriptContext->GetLibrary()->CallNativeHostPromiseRejectionTracker(this, resolution, false);
+ }
}
else
{
@@ -838,6 +843,10 @@ namespace Js
EnqueuePromiseReactionTask(resolveReaction, sourcePromise->result, scriptContext);
break;
case PromiseStatusCode_HasRejection:
+ if(!sourcePromise->GetIsHandled())
+ {
+ scriptContext->GetLibrary()->CallNativeHostPromiseRejectionTracker(sourcePromise, sourcePromise->result, true);
+ }
EnqueuePromiseReactionTask(rejectReaction, sourcePromise->result, scriptContext);
break;
default:
@@ -845,6 +854,8 @@ namespace Js
break;
}
+ sourcePromise->SetIsHandled();
+
return promiseCapability->GetPromise();
}
diff --git a/lib/Runtime/Library/JavascriptPromise.h b/lib/Runtime/Library/JavascriptPromise.h
index ae3b6791eb8..dbf0a2683eb 100644
--- a/lib/Runtime/Library/JavascriptPromise.h
+++ b/lib/Runtime/Library/JavascriptPromise.h
@@ -461,6 +461,8 @@ namespace Js
PromiseStatusCode_HasRejection
};
+ bool GetIsHandled() { return isHandled; }
+ void SetIsHandled() { isHandled = true; }
PromiseStatus GetStatus() const { return status; }
Var GetResult() const { return result; }
@@ -470,6 +472,7 @@ namespace Js
protected:
Field(PromiseStatus) status;
Field(Var) result;
+ Field(bool) isHandled;
Field(JavascriptPromiseReactionList*) resolveReactions;
Field(JavascriptPromiseReactionList*) rejectReactions;
diff --git a/test/es7/PromiseRejectionTracking.baseline b/test/es7/PromiseRejectionTracking.baseline
new file mode 100644
index 00000000000..b132c8fa267
--- /dev/null
+++ b/test/es7/PromiseRejectionTracking.baseline
@@ -0,0 +1,43 @@
+Executing test #1 - Reject promise with no reactions.
+Uncaught promise rejection
+Rejection from test 1
+Executing test #2 - Reject promise with a catch reaction only.
+Executing test #3 - Reject promise with catch and then reactions.
+Executing test #4 - Reject promise then add a catch afterwards.
+Uncaught promise rejection
+Rejection from test 4
+Promise rejection handled
+Rejection from test 4
+Executing test #5 - Reject promise then add two catches afterwards.
+Uncaught promise rejection
+Rejection from test 5
+Promise rejection handled
+Rejection from test 5
+Executing test #6 - Async function that throws.
+Uncaught promise rejection
+Rejection from test 6
+Executing test #7 - Async function that throws but is caught.
+Uncaught promise rejection
+Rejection from test 7
+Promise rejection handled
+Rejection from test 7
+Executing test #8 - Async function that awaits a function that throws.
+Uncaught promise rejection
+Rejection from test 8
+Promise rejection handled
+Rejection from test 8
+Executing test #9 - Reject a handled promise then handle one of the handles but not the other.
+Executing test #10 - Reject a handled promise and don't handle either path.
+Begin async results:
+Uncaught promise rejection
+Rejection from test 8
+Uncaught promise rejection
+Rejection from test 9
+Uncaught promise rejection
+Rejection from test 10
+Uncaught promise rejection
+Rejection from test 10
+Promise rejection handled
+Rejection from test 9
+Uncaught promise rejection
+Rejection from test 9
diff --git a/test/es7/PromiseRejectionTracking.js b/test/es7/PromiseRejectionTracking.js
new file mode 100644
index 00000000000..0b24fe906b1
--- /dev/null
+++ b/test/es7/PromiseRejectionTracking.js
@@ -0,0 +1,145 @@
+//-------------------------------------------------------------------------------------------------------
+// Copyright (C) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+//-------------------------------------------------------------------------------------------------------
+
+// Test HostPromiseRejectionTracker - see ecma262 section 25.4.1.9
+
+let tests = [
+ {
+ name: "Reject promise with no reactions.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject)=>{
+ controller = {resolve, reject};
+ });
+ controller.reject("Rejection from test " + index);//Should notify rejected
+ }
+ },
+ {
+ name: "Reject promise with a catch reaction only.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject)=>{
+ controller = {resolve, reject};
+ }).catch(()=>{});
+ controller.reject("Rejection from test " + index);//Should NOT notify
+ }
+ },
+ {
+ name: "Reject promise with catch and then reactions.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject)=>{
+ controller = {resolve, reject};
+ }).then(()=>{}).catch(()=>{});
+ controller.reject("Rejection from test " + index);//Should NOT notify
+ }
+ },
+ {
+ name: "Reject promise then add a catch afterwards.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject)=>{
+ controller = {resolve, reject};
+ });
+ controller.reject("Rejection from test " + index);//Should notify rejected
+ promise.catch(()=>{});//Should notify handled
+ }
+ },
+ {
+ name: "Reject promise then add two catches afterwards.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject)=>{
+ controller = {resolve, reject};
+ });
+ controller.reject("Rejection from test " + index);//Should notify rejected
+ promise.catch(()=>{});//Should notify handled
+ promise.catch(()=>{});//Should NOT notify
+ }
+ },
+ {
+ name: "Async function that throws.",
+ body: function(index)
+ {
+ async function aFunction()
+ {
+ throw ("Rejection from test " + index);
+ }
+ aFunction();//Should notify rejected
+ }
+ },
+ {
+ name: "Async function that throws but is caught.",
+ body: function(index)
+ {
+ async function aFunction()
+ {
+ throw ("Rejection from test " + index);
+ }
+ aFunction().catch(()=>{});//Should notify rejected AND then handled
+ }
+ },
+ {
+ name: "Async function that awaits a function that throws.",
+ body: function(index)
+ {
+ async function aFunction()
+ {
+ throw ("Rejection from test " + index);//Should notify rejected
+ }
+ async function bFunction()
+ {
+ await aFunction();//Should notify handled
+ }
+ bFunction();//Should notify rejected in the async section
+ },
+ },
+ {
+ name: "Reject a handled promise then handle one of the handles but not the other.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject) => { controller = {resolve, reject};});
+ let a = promise.then(() => {});//a is not handled
+ let b = promise.then(() => {});//b is not handled
+ controller.reject("Rejection from test " + index);//no notification as handled
+
+ let c = a.then(() => {}); //handle a
+
+ c.catch(() => {b.then(()=>{})}); // handle c
+ //b is still not handled -> notify once in async section
+ //b has an async handler -> will notify handled in async section
+ //the .then() on b is not handled so will notify in async section
+ },
+ },
+ {
+ name: "Reject a handled promise and don't handle either path.",
+ body: function(index)
+ {
+ let controller;
+ let promise = new Promise((resolve, reject) => { controller = {resolve, reject};});
+ let a = promise.then(() => {});//a is not handled
+ let b = promise.then(() => {});//b is not handled
+ controller.reject("Rejection from test " + index);//no notification as handled
+
+ let c = a.then(() => {}); //handle a
+
+ //b is not handled -> will notify in async section
+ //c is not handled -> will notify in async section
+ }
+ }
+];
+
+for(let i = 0; i < tests.length; ++i)
+{
+ WScript.Echo('Executing test #' + (i + 1) + ' - ' + tests[i].name);
+ tests[i].body(i+1);
+}
+WScript.Echo("Begin async results:");
diff --git a/test/es7/rlexe.xml b/test/es7/rlexe.xml
index 9a23c30fd1c..8c34409e4c2 100644
--- a/test/es7/rlexe.xml
+++ b/test/es7/rlexe.xml
@@ -92,4 +92,11 @@
exclude_xplat
+
+
+ PromiseRejectionTracking.js
+ -TrackRejectedPromises -args summary -endargs -nodeferparse
+ PromiseRejectionTracking.baseline
+
+