Skip to content

Commit ffc71ed

Browse files
tniessenGabriel Schulhof
authored andcommitted
Add Env::RunScript
This is a thin wrapper around napi_run_script. Refs: nodejs/node#15216 PR-URL: #616 Reviewed-By: Nicola Del Gobbo <nicoladelgobbo@gmail.com> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Kevin Eady <kevin.c.eady@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
1 parent a1b1060 commit ffc71ed

File tree

8 files changed

+139
-0
lines changed

8 files changed

+139
-0
lines changed

doc/env.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,17 @@ Napi::Error Napi::Env::GetAndClearPendingException();
6161
```
6262

6363
Returns an `Napi::Error` object representing the environment's pending exception, if any.
64+
65+
### RunScript
66+
67+
```cpp
68+
Napi::Value Napi::Env::RunScript(____ script);
69+
```
70+
- `[in] script`: A string containing JavaScript code to execute.
71+
72+
Runs JavaScript code contained in a string and returns its result.
73+
74+
The `script` can be any of the following types:
75+
- [`Napi::String`](string.md)
76+
- `const char *`
77+
- `const std::string &`

napi-inl.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,22 @@ inline Error Env::GetAndClearPendingException() {
319319
return Error(_env, value);
320320
}
321321

322+
inline Value Env::RunScript(const char* utf8script) {
323+
String script = String::New(_env, utf8script);
324+
return RunScript(script);
325+
}
326+
327+
inline Value Env::RunScript(const std::string& utf8script) {
328+
return RunScript(utf8script.c_str());
329+
}
330+
331+
inline Value Env::RunScript(String script) {
332+
napi_value result;
333+
napi_status status = napi_run_script(_env, script, &result);
334+
NAPI_THROW_IF_FAILED(_env, status, Undefined());
335+
return Value(_env, result);
336+
}
337+
322338
////////////////////////////////////////////////////////////////////////////////
323339
// Value class
324340
////////////////////////////////////////////////////////////////////////////////

napi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ namespace Napi {
178178
bool IsExceptionPending() const;
179179
Error GetAndClearPendingException();
180180

181+
Value RunScript(const char* utf8script);
182+
Value RunScript(const std::string& utf8script);
183+
Value RunScript(String script);
184+
181185
private:
182186
napi_env _env;
183187
};

test/binding.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Object InitObject(Env env);
3939
Object InitObjectDeprecated(Env env);
4040
#endif // !NODE_ADDON_API_DISABLE_DEPRECATED
4141
Object InitPromise(Env env);
42+
Object InitRunScript(Env env);
4243
#if (NAPI_VERSION > 3)
4344
Object InitThreadSafeFunctionCtx(Env env);
4445
Object InitThreadSafeFunctionExistingTsfn(Env env);
@@ -92,6 +93,7 @@ Object Init(Env env, Object exports) {
9293
exports.Set("object_deprecated", InitObjectDeprecated(env));
9394
#endif // !NODE_ADDON_API_DISABLE_DEPRECATED
9495
exports.Set("promise", InitPromise(env));
96+
exports.Set("run_script", InitRunScript(env));
9597
#if (NAPI_VERSION > 3)
9698
exports.Set("threadsafe_function_ctx", InitThreadSafeFunctionCtx(env));
9799
exports.Set("threadsafe_function_existing_tsfn", InitThreadSafeFunctionExistingTsfn(env));

test/binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
'object/object.cc',
3737
'object/set_property.cc',
3838
'promise.cc',
39+
'run_script.cc',
3940
'threadsafe_function/threadsafe_function_ctx.cc',
4041
'threadsafe_function/threadsafe_function_existing_tsfn.cc',
4142
'threadsafe_function/threadsafe_function_ptr.cc',

test/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ let testModules = [
3939
'object/object_deprecated',
4040
'object/set_property',
4141
'promise',
42+
'run_script',
4243
'threadsafe_function/threadsafe_function_ctx',
4344
'threadsafe_function/threadsafe_function_existing_tsfn',
4445
'threadsafe_function/threadsafe_function_ptr',

test/run_script.cc

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "napi.h"
2+
3+
using namespace Napi;
4+
5+
namespace {
6+
7+
Value RunPlainString(const CallbackInfo& info) {
8+
Env env = info.Env();
9+
return env.RunScript("1 + 2 + 3");
10+
}
11+
12+
Value RunStdString(const CallbackInfo& info) {
13+
Env env = info.Env();
14+
std::string str = "1 + 2 + 3";
15+
return env.RunScript(str);
16+
}
17+
18+
Value RunJsString(const CallbackInfo& info) {
19+
Env env = info.Env();
20+
return env.RunScript(info[0].As<String>());
21+
}
22+
23+
Value RunWithContext(const CallbackInfo& info) {
24+
Env env = info.Env();
25+
26+
Array keys = info[1].As<Object>().GetPropertyNames();
27+
std::string code = "(";
28+
for (unsigned int i = 0; i < keys.Length(); i++) {
29+
if (i != 0) code += ",";
30+
code += keys.Get(i).As<String>().Utf8Value();
31+
}
32+
code += ") => " + info[0].As<String>().Utf8Value();
33+
34+
Value ret = env.RunScript(code);
35+
Function fn = ret.As<Function>();
36+
std::vector<napi_value> args;
37+
for (unsigned int i = 0; i < keys.Length(); i++) {
38+
Value key = keys.Get(i);
39+
args.push_back(info[1].As<Object>().Get(key));
40+
}
41+
return fn.Call(args);
42+
}
43+
44+
} // end anonymous namespace
45+
46+
Object InitRunScript(Env env) {
47+
Object exports = Object::New(env);
48+
49+
exports["plainString"] = Function::New(env, RunPlainString);
50+
exports["stdString"] = Function::New(env, RunStdString);
51+
exports["jsString"] = Function::New(env, RunJsString);
52+
exports["withContext"] = Function::New(env, RunWithContext);
53+
54+
return exports;
55+
}

test/run_script.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
const buildType = process.config.target_defaults.default_configuration;
3+
const assert = require('assert');
4+
const testUtil = require('./testUtil');
5+
6+
test(require(`./build/${buildType}/binding.node`));
7+
test(require(`./build/${buildType}/binding_noexcept.node`));
8+
9+
function test(binding) {
10+
testUtil.runGCTests([
11+
'Plain C string',
12+
() => {
13+
const sum = binding.run_script.plainString();
14+
assert.strictEqual(sum, 1 + 2 + 3);
15+
},
16+
17+
'std::string',
18+
() => {
19+
const sum = binding.run_script.stdString();
20+
assert.strictEqual(sum, 1 + 2 + 3);
21+
},
22+
23+
'JavaScript string',
24+
() => {
25+
const sum = binding.run_script.jsString("1 + 2 + 3");
26+
assert.strictEqual(sum, 1 + 2 + 3);
27+
},
28+
29+
'JavaScript, but not a string',
30+
() => {
31+
assert.throws(() => {
32+
binding.run_script.jsString(true);
33+
}, {
34+
name: 'Error',
35+
message: 'A string was expected'
36+
});
37+
},
38+
39+
'With context',
40+
() => {
41+
const a = 1, b = 2, c = 3;
42+
const sum = binding.run_script.withContext("a + b + c", { a, b, c });
43+
assert.strictEqual(sum, a + b + c);
44+
}
45+
]);
46+
}

0 commit comments

Comments
 (0)