Browse Source

[MERGE #5131 @kfarnung] New APIs for getting Promise state and result

Merge pull request #5131 from kfarnung:promises
Kyle Farnung 7 years ago
parent
commit
1c76177a11
4 changed files with 176 additions and 2 deletions
  1. 2 2
      bin/ChakraCore/ChakraCore.def
  2. 71 0
      bin/NativeTests/JsRTApiTest.cpp
  3. 42 0
      lib/Jsrt/ChakraCore.h
  4. 61 0
      lib/Jsrt/Jsrt.cpp

+ 2 - 2
bin/ChakraCore/ChakraCore.def

@@ -62,7 +62,7 @@ JsLessThan
 JsLessThanOrEqual
 
 JsCreateEnhancedFunction
-
 JsSetHostPromiseRejectionTracker
-
 JsGetProxyProperties
+JsGetPromiseState
+JsGetPromiseResult

+ 71 - 0
bin/NativeTests/JsRTApiTest.cpp

@@ -4,6 +4,7 @@
 //-------------------------------------------------------------------------------------------------------
 #include "stdafx.h"
 #include "catch.hpp"
+#include <array>
 #include <process.h>
 
 #pragma warning(disable:4100) // unreferenced formal parameter
@@ -2663,4 +2664,74 @@ namespace JsRTApiTest
     {
         JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCreateStringTest);
     }
+
+    void JsCreatePromiseTest(JsRuntimeAttributes attributes, JsRuntimeHandle runtime)
+    {
+        JsValueRef result = JS_INVALID_REFERENCE;
+
+        JsValueRef promise = JS_INVALID_REFERENCE;
+        JsValueRef resolve = JS_INVALID_REFERENCE;
+        JsValueRef reject = JS_INVALID_REFERENCE;
+
+        // Create resolvable promise
+        REQUIRE(JsCreatePromise(&promise, &resolve, &reject) == JsNoError);
+
+        JsPromiseState state = JsPromiseState_Pending;
+        REQUIRE(JsGetPromiseState(promise, &state) == JsNoError);
+        CHECK(state == JsPromiseState_Pending);
+
+        result = JS_INVALID_REFERENCE;
+        CHECK(JsGetPromiseResult(promise, &result) == JsErrorInvalidArgument);
+        CHECK(result == JS_INVALID_REFERENCE);
+
+        JsValueRef num = JS_INVALID_REFERENCE;
+        REQUIRE(JsIntToNumber(42, &num) == JsNoError);
+
+        std::array<JsValueRef, 2> args{ GetUndefined(), num };
+        REQUIRE(JsCallFunction(resolve, args.data(), static_cast<unsigned short>(args.size()), &result) == JsNoError);
+
+        state = JsPromiseState_Pending;
+        REQUIRE(JsGetPromiseState(promise, &state) == JsNoError);
+        CHECK(state == JsPromiseState_Fulfilled);
+
+        result = JS_INVALID_REFERENCE;
+        REQUIRE(JsGetPromiseResult(promise, &result) == JsNoError);
+
+        int resultNum = 0;
+        REQUIRE(JsNumberToInt(result, &resultNum) == JsNoError);
+        CHECK(resultNum == 42);
+
+        // Create rejectable promise
+        REQUIRE(JsCreatePromise(&promise, &resolve, &reject) == JsNoError);
+
+        state = JsPromiseState_Pending;
+        REQUIRE(JsGetPromiseState(promise, &state) == JsNoError);
+        CHECK(state == JsPromiseState_Pending);
+
+        result = JS_INVALID_REFERENCE;
+        CHECK(JsGetPromiseResult(promise, &result) == JsErrorInvalidArgument);
+        CHECK(result == JS_INVALID_REFERENCE);
+
+        num = JS_INVALID_REFERENCE;
+        REQUIRE(JsIntToNumber(43, &num) == JsNoError);
+
+        args = { GetUndefined(), num };
+        REQUIRE(JsCallFunction(reject, args.data(), static_cast<unsigned short>(args.size()), &result) == JsNoError);
+
+        state = JsPromiseState_Pending;
+        REQUIRE(JsGetPromiseState(promise, &state) == JsNoError);
+        CHECK(state == JsPromiseState_Rejected);
+
+        result = JS_INVALID_REFERENCE;
+        REQUIRE(JsGetPromiseResult(promise, &result) == JsNoError);
+
+        resultNum = 0;
+        REQUIRE(JsNumberToInt(result, &resultNum) == JsNoError);
+        CHECK(resultNum == 43);
+    }
+
+    TEST_CASE("ApiTest_JsCreatePromiseTest", "[ApiTest]")
+    {
+        JsRTApiTest::RunWithAttributes(JsRTApiTest::JsCreatePromiseTest);
+    }
 }

+ 42 - 0
lib/Jsrt/ChakraCore.h

@@ -58,6 +58,16 @@ typedef enum JsModuleHostInfoKind
     JsModuleHostInfo_Url = 0x6
 } JsModuleHostInfoKind;
 
+/// <summary>
+///     The possible states for a Promise object.
+/// </summary>
+typedef enum _JsPromiseState
+{
+    JsPromiseState_Pending = 0x0,
+    JsPromiseState_Fulfilled = 0x1,
+    JsPromiseState_Rejected = 0x2
+} JsPromiseState;
+
 /// <summary>
 ///     User implemented callback to fetch additional imported modules.
 /// </summary>
@@ -633,6 +643,38 @@ CHAKRA_API
         _In_ JsValueRef sourceUrl,
         _Out_ JsValueRef *result);
 
+/// <summary>
+///     Gets the state of a given Promise object.
+/// </summary>
+/// <remarks>
+///     Requires an active script context.
+/// </remarks>
+/// <param name="promise">The Promise object.</param>
+/// <param name="state">The current state of the Promise.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+    JsGetPromiseState(
+        _In_ JsValueRef promise,
+        _Out_ JsPromiseState *state);
+
+/// <summary>
+///     Gets the result of a given Promise object.
+/// </summary>
+/// <remarks>
+///     Requires an active script context.
+/// </remarks>
+/// <param name="promise">The Promise object.</param>
+/// <param name="result">The result of the Promise.</param>
+/// <returns>
+///     The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
+/// </returns>
+CHAKRA_API
+    JsGetPromiseResult(
+        _In_ JsValueRef promise,
+        _Out_ JsValueRef *result);
+
 /// <summary>
 ///     Creates a new JavaScript Promise object.
 /// </summary>

+ 61 - 0
lib/Jsrt/Jsrt.cpp

@@ -5112,6 +5112,67 @@ CHAKRA_API JsCreatePromise(_Out_ JsValueRef *promise, _Out_ JsValueRef *resolve,
     });
 }
 
+CHAKRA_API JsGetPromiseState(_In_ JsValueRef promise, _Out_ JsPromiseState *state)
+{
+    return ContextAPIWrapper<JSRT_MAYBE_TRUE>([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
+        PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(scriptContext);
+
+        VALIDATE_INCOMING_REFERENCE(promise, scriptContext);
+        PARAM_NOT_NULL(state);
+
+        *state = JsPromiseState_Pending;
+
+        if (!Js::JavascriptPromise::Is(promise))
+        {
+            return JsErrorInvalidArgument;
+        }
+
+        Js::JavascriptPromise *jsPromise = Js::JavascriptPromise::FromVar(promise);
+        Js::JavascriptPromise::PromiseStatus status = jsPromise->GetStatus();
+
+        switch (status)
+        {
+        case Js::JavascriptPromise::PromiseStatus::PromiseStatusCode_HasRejection:
+            *state = JsPromiseState_Rejected;
+            break;
+
+        case Js::JavascriptPromise::PromiseStatus::PromiseStatusCode_HasResolution:
+            *state = JsPromiseState_Fulfilled;
+            break;
+        }
+
+        return JsNoError;
+    });
+}
+
+CHAKRA_API JsGetPromiseResult(_In_ JsValueRef promise, _Out_ JsValueRef *result)
+{
+    return ContextAPIWrapper<JSRT_MAYBE_TRUE>([&](Js::ScriptContext *scriptContext, TTDRecorder& _actionEntryPopper) -> JsErrorCode {
+        PERFORM_JSRT_TTD_RECORD_ACTION_NOT_IMPLEMENTED(scriptContext);
+
+        VALIDATE_INCOMING_REFERENCE(promise, scriptContext);
+        PARAM_NOT_NULL(result);
+
+        *result = JS_INVALID_REFERENCE;
+
+        if (!Js::JavascriptPromise::Is(promise))
+        {
+            return JsErrorInvalidArgument;
+        }
+
+        Js::JavascriptPromise *jsPromise = Js::JavascriptPromise::FromVar(promise);
+        Js::Var jsResult = jsPromise->GetResult();
+
+        if (jsResult == nullptr)
+        {
+            return JsErrorInvalidArgument;
+        }
+
+        *result = (JsValueRef)jsResult;
+        return JsNoError;
+    });
+}
+
 CHAKRA_API JsCreateWeakReference(
     _In_ JsValueRef value,
     _Out_ JsWeakRef* weakRef)