Skip to content

Commit 34f7a35

Browse files
committed
vm.internalCompileFunction can now optionally return errors instead of throwing them
1 parent 3a37bc3 commit 34f7a35

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

doc/api/vm.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -1027,7 +1027,9 @@ changes:
10271027
* Returns: {Module Namespace Object|vm.Module} Returning a `vm.Module` is
10281028
recommended in order to take advantage of error tracking, and to avoid
10291029
issues with namespaces that contain `then` function exports.
1030-
* Returns: {Function}
1030+
* `shouldThrowOnError` {boolean} Whether compilation errors will be thrown or
1031+
returned. **Default:** `true`.
1032+
* Returns: {Function | Error}
10311033

10321034
Compiles the given code into the provided context (if no context is
10331035
supplied, the current context is used), and returns it wrapped inside a

lib/internal/vm.js

+34
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const {
44
ArrayPrototypeForEach,
5+
ObjectGetPrototypeOf,
6+
SyntaxErrorPrototype,
57
} = primordials;
68

79
const {
@@ -24,12 +26,36 @@ const {
2426
ERR_INVALID_ARG_TYPE,
2527
} = require('internal/errors').codes;
2628

29+
/**
30+
* Checks if the given object is a context object.
31+
* @param {object} object - The object to check.
32+
* @returns {boolean} - Returns true if the object is a context object, else false.
33+
*/
2734
function isContext(object) {
2835
validateObject(object, 'object', kValidateObjectAllowArray);
2936

3037
return _isContext(object);
3138
}
3239

40+
/**
41+
* Compiles a function from the given code string.
42+
* @param {string} code - The code string to compile.
43+
* @param {string[]} [params] - An optional array of parameter names for the compiled function.
44+
* @param {object} [options] - An optional object containing compilation options.
45+
* @param {string} [options.filename=''] - The filename to use for the compiled function.
46+
* @param {number} [options.columnOffset=0] - The column offset to use for the compiled function.
47+
* @param {number} [options.lineOffset=0] - The line offset to use for the compiled function.
48+
* @param {Buffer} [options.cachedData=undefined] - The cached data to use for the compiled function.
49+
* @param {boolean} [options.produceCachedData=false] - Whether to produce cached data for the compiled function.
50+
* @param {ReturnType<import('vm').createContext} [options.parsingContext=undefined] - The parsing context to use for the compiled function.
51+
* @param {object[]} [options.contextExtensions=[]] - An array of context extensions to use for the compiled function.
52+
* @param {import('internal/modules/esm/utils').ImportModuleDynamicallyCallback} [options.importModuleDynamically] -
53+
* A function to use for dynamically importing modules.
54+
* @param {boolean} [options.shouldThrowOnError=true] - Whether to throw an error if the code contains syntax errors.
55+
* @returns {Object} An object containing the compiled function and any associated data.
56+
* @throws {TypeError} If any of the arguments are of the wrong type.
57+
* @throws {ERR_INVALID_ARG_TYPE} If the parsing context is not a valid context object.
58+
*/
3359
function internalCompileFunction(code, params, options) {
3460
validateString(code, 'code');
3561
if (params !== undefined) {
@@ -45,6 +71,7 @@ function internalCompileFunction(code, params, options) {
4571
parsingContext = undefined,
4672
contextExtensions = [],
4773
importModuleDynamically,
74+
shouldThrowOnError = true,
4875
} = options;
4976

5077
validateString(filename, 'options.filename');
@@ -71,6 +98,7 @@ function internalCompileFunction(code, params, options) {
7198
const name = `options.contextExtensions[${i}]`;
7299
validateObject(extension, name, kValidateObjectAllowNullable);
73100
});
101+
validateBoolean(shouldThrowOnError, 'options.shouldThrowOnError');
74102

75103
const result = compileFunction(
76104
code,
@@ -82,8 +110,14 @@ function internalCompileFunction(code, params, options) {
82110
parsingContext,
83111
contextExtensions,
84112
params,
113+
shouldThrowOnError,
85114
);
86115

116+
// If we're not supposed to throw on errors, and compilation errored, then return the error.
117+
if (!shouldThrowOnError && result != null && ObjectGetPrototypeOf(result) === SyntaxErrorPrototype) {
118+
return result;
119+
}
120+
87121
if (produceCachedData) {
88122
result.function.cachedDataProduced = result.cachedDataProduced;
89123
}

src/node_contextify.cc

+11-1
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,13 @@ void ContextifyContext::CompileFunction(
11991199
params_buf = args[8].As<Array>();
12001200
}
12011201

1202+
// Argument 10: Whether to throw errors or return them (optional)
1203+
bool should_throw_on_error = true;
1204+
if (!args[9]->IsUndefined()) {
1205+
CHECK(args[9]->IsBoolean());
1206+
should_throw_on_error = args[9]->BooleanValue(args.GetIsolate());
1207+
}
1208+
12021209
// Read cache from cached data buffer
12031210
ScriptCompiler::CachedData* cached_data = nullptr;
12041211
if (!cached_data_buf.IsEmpty()) {
@@ -1273,7 +1280,10 @@ void ContextifyContext::CompileFunction(
12731280
if (!maybe_fn.ToLocal(&fn)) {
12741281
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
12751282
errors::DecorateErrorStack(env, try_catch);
1276-
try_catch.ReThrow();
1283+
if (should_throw_on_error)
1284+
try_catch.ReThrow();
1285+
else
1286+
args.GetReturnValue().Set(try_catch.Exception());
12771287
}
12781288
return;
12791289
}

0 commit comments

Comments
 (0)