From 3b4708b025c4a4ad4e99d980a64a7c49a84cc9bf Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof Date: Wed, 6 Sep 2017 13:00:43 +0300 Subject: [PATCH] n-api: implement napi_run_script Fixes: https://github.com/nodejs/abi-stable-node/issues/51 Backport-PR-URL: https://github.com/nodejs/node/pull/19447 PR-URL: https://github.com/nodejs/node/pull/15216 Reviewed-By: Anna Henningsen Reviewed-By: Benjamin Gruenbaum Reviewed-By: Colin Ihrig Reviewed-By: Michael Dawson Reviewed-By: Timothy Gu --- doc/api/n-api.md | 21 +++++++++++++++ src/node_api.cc | 27 ++++++++++++++++++++ src/node_api.h | 5 ++++ test/addons-napi/test_general/testNapiRun.js | 12 +++++++++ test/addons-napi/test_general/test_general.c | 13 ++++++++++ 5 files changed, 78 insertions(+) create mode 100644 test/addons-napi/test_general/testNapiRun.js diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 9c56e50e83f299..80d83612509ca1 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -43,6 +43,7 @@ The documentation for N-API is structured as follows: * [Object Wrap][] * [Asynchronous Operations][] * [Promises][] +* [Script Execution][] The N-API is a C API that ensures ABI stability across Node.js versions and different compiler levels. However, we also understand that a C++ @@ -3556,6 +3557,25 @@ NAPI_EXTERN napi_status napi_is_promise(napi_env env, - `[out] is_promise`: Flag indicating whether `promise` is a native promise object - that is, a promise object created by the underlying engine. +## Script execution + +N-API provides an API for executing a string containing JavaScript using the +underlying JavaScript engine. + +### napi_run_script + +```C +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result); +``` + +- `[in] env`: The environment that the API is invoked under. +- `[in] script`: A JavaScript string containing the script to execute. +- `[out] result`: The value resulting from having executed the script. + [Promises]: #n_api_promises [Asynchronous Operations]: #n_api_asynchronous_operations [Basic N-API Data Types]: #n_api_basic_n_api_data_types @@ -3565,6 +3585,7 @@ object - that is, a promise object created by the underlying engine. [Native Abstractions for Node.js]: https://github.com/nodejs/nan [Object Lifetime Management]: #n_api_object_lifetime_management [Object Wrap]: #n_api_object_wrap +[Script Execution]: #n_api_script_execution [Section 9.1.6]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc [Section 12.5.5]: https://tc39.github.io/ecma262/#sec-typeof-operator [Section 24.3]: https://tc39.github.io/ecma262/#sec-dataview-objects diff --git a/src/node_api.cc b/src/node_api.cc index a2322295fccbaa..9c8e0edec94d40 100644 --- a/src/node_api.cc +++ b/src/node_api.cc @@ -3426,3 +3426,30 @@ NAPI_EXTERN napi_status napi_is_promise(napi_env env, return napi_clear_last_error(env); } + +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result) { + NAPI_PREAMBLE(env); + CHECK_ARG(env, script); + CHECK_ARG(env, result); + + v8::Local v8_script = v8impl::V8LocalValueFromJsValue(script); + + if (!v8_script->IsString()) { + return napi_set_last_error(env, napi_string_expected); + } + + v8::Local context = env->isolate->GetCurrentContext(); + + auto maybe_script = v8::Script::Compile(context, + v8::Local::Cast(v8_script)); + CHECK_MAYBE_EMPTY(env, maybe_script, napi_generic_failure); + + auto script_result = + maybe_script.ToLocalChecked()->Run(context); + CHECK_MAYBE_EMPTY(env, script_result, napi_generic_failure); + + *result = v8impl::JsValueFromV8LocalValue(script_result.ToLocalChecked()); + return GET_RETURN_STATUS(env); +} diff --git a/src/node_api.h b/src/node_api.h index 702ddf2d9e6a0e..807595777dd947 100644 --- a/src/node_api.h +++ b/src/node_api.h @@ -562,6 +562,11 @@ NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, int64_t change_in_bytes, int64_t* adjusted_value); +// Runnig a script +NAPI_EXTERN napi_status napi_run_script(napi_env env, + napi_value script, + napi_value* result); + EXTERN_C_END #endif // SRC_NODE_API_H_ diff --git a/test/addons-napi/test_general/testNapiRun.js b/test/addons-napi/test_general/testNapiRun.js new file mode 100644 index 00000000000000..d7534ecf9c3912 --- /dev/null +++ b/test/addons-napi/test_general/testNapiRun.js @@ -0,0 +1,12 @@ +'use strict'; + +const common = require('../../common'); +const assert = require('assert'); + +// addon is referenced through the eval expression in testFile +// eslint-disable-next-line no-unused-vars +const addon = require(`./build/${common.buildType}/test_general`); + +assert.strictEqual(addon.testNapiRun('(41.92 + 0.08);'), 42, + 'napi_run_script() works correctly'); +assert.throws(() => addon.testNapiRun({ abc: 'def' }), /string was expected/); diff --git a/test/addons-napi/test_general/test_general.c b/test/addons-napi/test_general/test_general.c index ea1f2ece0a5d30..287c1d7cb8cb34 100644 --- a/test/addons-napi/test_general/test_general.c +++ b/test/addons-napi/test_general/test_general.c @@ -1,4 +1,5 @@ #include +#include #include "../common.h" napi_value testStrictEquals(napi_env env, napi_callback_info info) { @@ -215,12 +216,24 @@ napi_value testAdjustExternalMemory(napi_env env, napi_callback_info info) { return result; } +napi_value testNapiRun(napi_env env, napi_callback_info info) { + napi_value script, result; + size_t argc = 1; + + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &script, NULL, NULL)); + + NAPI_CALL(env, napi_run_script(env, script, &result)); + + return result; +} + void Init(napi_env env, napi_value exports, napi_value module, void* priv) { napi_property_descriptor descriptors[] = { DECLARE_NAPI_PROPERTY("testStrictEquals", testStrictEquals), DECLARE_NAPI_PROPERTY("testGetPrototype", testGetPrototype), DECLARE_NAPI_PROPERTY("testGetVersion", testGetVersion), DECLARE_NAPI_PROPERTY("testGetNodeVersion", testGetNodeVersion), + DECLARE_NAPI_PROPERTY("testNapiRun", testNapiRun), DECLARE_NAPI_PROPERTY("doInstanceOf", doInstanceOf), DECLARE_NAPI_PROPERTY("getUndefined", getUndefined), DECLARE_NAPI_PROPERTY("getNull", getNull),