Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/ChakraCore/ChakraCore.def
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ JsLessThan
JsLessThanOrEqual

JsCreateEnhancedFunction

JsSetHostPromiseRejectionTracker

JsGetProxyProperties
JsGetPromiseState
JsGetPromiseResult
71 changes: 71 additions & 0 deletions bin/NativeTests/JsRTApiTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//-------------------------------------------------------------------------------------------------------
#include "stdafx.h"
#include "catch.hpp"
#include <array>
#include <process.h>

#pragma warning(disable:4100) // unreferenced formal parameter
Expand Down Expand Up @@ -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 changes: 42 additions & 0 deletions lib/Jsrt/ChakraCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
61 changes: 61 additions & 0 deletions lib/Jsrt/Jsrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the status be undefined if jsPromise->GetResult() is null here? Just so that we don't return a good status code and then have GetPromiseResult return InvalidArgument- that could be confusing to callers

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A Promise only has a result once it's resolved or rejected, so GetResult() will return nullptr even in the Unresolved case.

{
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)
Expand Down