From 926b948d60a8b523dd539d117657a663fafe2f63 Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Fri, 16 Jun 2017 16:15:44 +0300 Subject: [PATCH] N-API: Implement Promise for ChakraCore Re https://github.com/nodejs/abi-stable-node/issues/242 --- src/node_api.h | 10 +++- src/node_api_jsrt.cc | 45 +++++++++++++++ test/addons-napi/test_promise/binding.gyp | 8 +++ test/addons-napi/test_promise/test.js | 61 ++++++++++++++++++++ test/addons-napi/test_promise/test_promise.c | 50 ++++++++++++++++ 5 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 test/addons-napi/test_promise/binding.gyp create mode 100644 test/addons-napi/test_promise/test.js create mode 100644 test/addons-napi/test_promise/test_promise.c diff --git a/src/node_api.h b/src/node_api.h index c27cf937137..2ec10f7cbaa 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -480,10 +480,18 @@ NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, napi_async_work work); - // version management NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result); +// Methods to manage promises +NAPI_EXTERN napi_status napi_create_promise(napi_env env, + napi_value* result, + napi_value* resolve_callback, + napi_value* reject_callback); +NAPI_EXTERN napi_status napi_is_promise(napi_env env, + napi_value value, + bool* result); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/src/node_api_jsrt.cc b/src/node_api_jsrt.cc index f6361d83f9b..9ea43122692 100644 --- a/src/node_api_jsrt.cc +++ b/src/node_api_jsrt.cc @@ -2010,6 +2010,51 @@ napi_status napi_get_version(napi_env env, uint32_t* result) { return napi_ok; } +NAPI_EXTERN napi_status napi_create_promise(napi_env env, + napi_value* result, + napi_value* resolve_callback, + napi_value* reject_callback) { + CHECK_ARG(env); + CHECK_ARG(result); + CHECK_ARG(resolve_callback); + CHECK_ARG(reject_callback); + + JsValueRef promise, reject, resolve; + + CHECK_JSRT(JsCreatePromise(&promise, &reject, &resolve)); + + (*result) = reinterpret_cast(promise); + (*resolve_callback) = reinterpret_cast(resolve); + (*reject_callback) = reinterpret_cast(reject); + + return napi_ok; +} + +NAPI_EXTERN napi_status napi_is_promise(napi_env env, + napi_value value, + bool* result) { + CHECK_ARG(env); + CHECK_ARG(value); + CHECK_ARG(result); + + napi_status status; + napi_value js_promise_ctor, global; + bool is_instance; + + status = napi_get_global(env, &global); + if (status != napi_ok) return status; + + status = napi_get_named_property(env, global, "Promise", &js_promise_ctor); + if (status != napi_ok) return status; + + status = napi_instanceof(env, value, js_promise_ctor, &is_instance); + if (status != napi_ok) return status; + + (*result) = is_instance; + + return napi_ok; +} + namespace uvimpl { napi_status ConvertUVErrorCode(int code) { diff --git a/test/addons-napi/test_promise/binding.gyp b/test/addons-napi/test_promise/binding.gyp new file mode 100644 index 00000000000..bf266f93db7 --- /dev/null +++ b/test/addons-napi/test_promise/binding.gyp @@ -0,0 +1,8 @@ +{ + "targets": [ + { + "target_name": "test_promise", + "sources": [ "test_promise.c" ] + } + ] +} diff --git a/test/addons-napi/test_promise/test.js b/test/addons-napi/test_promise/test.js new file mode 100644 index 00000000000..8db5b267998 --- /dev/null +++ b/test/addons-napi/test_promise/test.js @@ -0,0 +1,61 @@ +'use strict'; + +const common = require('../../common'); +const test_promise = require(`./build/${common.buildType}/test_promise`); +const assert = require('assert'); + +var native_promise, conclude_value; + +// Create a native promise and resolve it. +native_promise = test_promise.napi_create_promise(); +conclude_value = "Promise delivered"; +native_promise.promise + .then( + function( result ) { + assert.strictEqual( result, conclude_value, "Promise was resolved as expected" ); + }, + function( error ) { + assert.ok( false, "Promise was rejected when it should have been resolved: " + + error + ": " + JSON.stringify( error, null, 4 ) ); + } ); +native_promise.resolve( conclude_value ); + +// Make sure the native promise is recognized as a promise. +assert.strictEqual( test_promise.napi_is_promise( native_promise.promise ), true, + "N-API correctly identifies a promise it created as a promise" ); + +// Create a native promise and reject it. +native_promise = test_promise.napi_create_promise(); +conclude_value = new Error( "I broke my promise" ); +native_promise.promise + .then( + function( result ) { + assert.ok( false, "Promise was resolved when it should have been rejected: " + + result + ": " + JSON.stringify( result, null, 4 ) ); + }, + function( error ) { + assert.strictEqual( error, conclude_value, "Promise was rejected as expected" ); + } ); + +// N-API correctly identifies a JS promise as a promise +assert.strictEqual( + test_promise.napi_is_promise( Promise.reject( new Error( "No reason, just sayin'" ) ) ), true, + "N-API correctly identifies a JS promise as a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( 42 ), false, + "N-API correctly reports that a number is not a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( "Gumball" ), false, + "N-API correctly reports that a string is not a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( undefined ), false, + "N-API correctly reports that undefined is not a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( null ), false, + "N-API correctly reports that null is not a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( {} ), false, + "N-API correctly reports that an object is not a promise" ); + +assert.strictEqual( test_promise.napi_is_promise( Symbol( "Espoo" ) ), false, + "N-API correctly reports that a symbol is not a promise" ); diff --git a/test/addons-napi/test_promise/test_promise.c b/test/addons-napi/test_promise/test_promise.c new file mode 100644 index 00000000000..f283a9b3787 --- /dev/null +++ b/test/addons-napi/test_promise/test_promise.c @@ -0,0 +1,50 @@ +#include +#include +#include "../common.h" + +napi_value bind_napi_create_promise(napi_env env, napi_callback_info info) { + napi_value promise, resolve, reject, result; + + NAPI_CALL(env, napi_create_promise(env, &promise, &resolve, &reject)); + NAPI_CALL(env, napi_create_object(env, &result)); + + fprintf(stderr, "resolve: %p\n", resolve); + + napi_valuetype value_type; + NAPI_CALL(env, napi_typeof(env, resolve, &value_type)); + fprintf(stderr, "resolve: value_type: %d\n", value_type); + + napi_property_descriptor properties[] = { + { "promise", NULL, NULL, NULL, NULL, promise, napi_default, NULL }, + { "resolve", NULL, NULL, NULL, NULL, resolve, napi_default, NULL }, + { "reject", NULL, NULL, NULL, NULL, reject, napi_default, NULL }, + }; + + NAPI_CALL(env, napi_define_properties(env, result, 3, properties)); + + return result; +} + +napi_value bind_napi_is_promise(napi_env env, napi_callback_info info) { + napi_value promise, result; + size_t argc = 1; + bool is_promise; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &promise, NULL, NULL)); + NAPI_CALL(env, napi_is_promise(env, promise, &is_promise)); + NAPI_CALL(env, napi_get_boolean(env, is_promise, &result)); + + return result; +} + +void Init(napi_env env, napi_value exports, napi_value module, void* priv) { + napi_property_descriptor descriptors[] = { + DECLARE_NAPI_PROPERTY("napi_create_promise", bind_napi_create_promise), + DECLARE_NAPI_PROPERTY("napi_is_promise", bind_napi_is_promise) + }; + + NAPI_CALL_RETURN_VOID(env, napi_define_properties( + env, exports, sizeof(descriptors) / sizeof(*descriptors), descriptors)); +} + +NAPI_MODULE(addon, Init)