Skip to content

Commit

Permalink
vm.internalCompileFunction can now optionally return errors instead o…
Browse files Browse the repository at this point in the history
…f throwing them
  • Loading branch information
GeoffreyBooth committed Oct 10, 2023
1 parent 3a37bc3 commit 34f7a35
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 2 deletions.
4 changes: 3 additions & 1 deletion doc/api/vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,9 @@ changes:
* Returns: {Module Namespace Object|vm.Module} Returning a `vm.Module` is
recommended in order to take advantage of error tracking, and to avoid
issues with namespaces that contain `then` function exports.
* Returns: {Function}
* `shouldThrowOnError` {boolean} Whether compilation errors will be thrown or
returned. **Default:** `true`.
* Returns: {Function | Error}

Compiles the given code into the provided context (if no context is
supplied, the current context is used), and returns it wrapped inside a
Expand Down
34 changes: 34 additions & 0 deletions lib/internal/vm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const {
ArrayPrototypeForEach,
ObjectGetPrototypeOf,
SyntaxErrorPrototype,
} = primordials;

const {
Expand All @@ -24,12 +26,36 @@ const {
ERR_INVALID_ARG_TYPE,
} = require('internal/errors').codes;

/**
* Checks if the given object is a context object.
* @param {object} object - The object to check.
* @returns {boolean} - Returns true if the object is a context object, else false.
*/
function isContext(object) {
validateObject(object, 'object', kValidateObjectAllowArray);

return _isContext(object);
}

/**
* Compiles a function from the given code string.
* @param {string} code - The code string to compile.
* @param {string[]} [params] - An optional array of parameter names for the compiled function.
* @param {object} [options] - An optional object containing compilation options.
* @param {string} [options.filename=''] - The filename to use for the compiled function.
* @param {number} [options.columnOffset=0] - The column offset to use for the compiled function.
* @param {number} [options.lineOffset=0] - The line offset to use for the compiled function.
* @param {Buffer} [options.cachedData=undefined] - The cached data to use for the compiled function.
* @param {boolean} [options.produceCachedData=false] - Whether to produce cached data for the compiled function.
* @param {ReturnType<import('vm').createContext} [options.parsingContext=undefined] - The parsing context to use for the compiled function.

Check failure on line 50 in lib/internal/vm.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

This line has a length of 140. Maximum allowed is 120
* @param {object[]} [options.contextExtensions=[]] - An array of context extensions to use for the compiled function.
* @param {import('internal/modules/esm/utils').ImportModuleDynamicallyCallback} [options.importModuleDynamically] -
* A function to use for dynamically importing modules.
* @param {boolean} [options.shouldThrowOnError=true] - Whether to throw an error if the code contains syntax errors.
* @returns {Object} An object containing the compiled function and any associated data.

Check warning on line 55 in lib/internal/vm.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Invalid JSDoc @returns type "Object"; prefer: "object"
* @throws {TypeError} If any of the arguments are of the wrong type.
* @throws {ERR_INVALID_ARG_TYPE} If the parsing context is not a valid context object.
*/
function internalCompileFunction(code, params, options) {
validateString(code, 'code');
if (params !== undefined) {
Expand All @@ -45,6 +71,7 @@ function internalCompileFunction(code, params, options) {
parsingContext = undefined,
contextExtensions = [],
importModuleDynamically,
shouldThrowOnError = true,
} = options;

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

const result = compileFunction(
code,
Expand All @@ -82,8 +110,14 @@ function internalCompileFunction(code, params, options) {
parsingContext,
contextExtensions,
params,
shouldThrowOnError,
);

// If we're not supposed to throw on errors, and compilation errored, then return the error.
if (!shouldThrowOnError && result != null && ObjectGetPrototypeOf(result) === SyntaxErrorPrototype) {
return result;
}

if (produceCachedData) {
result.function.cachedDataProduced = result.cachedDataProduced;
}
Expand Down
12 changes: 11 additions & 1 deletion src/node_contextify.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,13 @@ void ContextifyContext::CompileFunction(
params_buf = args[8].As<Array>();
}

// Argument 10: Whether to throw errors or return them (optional)
bool should_throw_on_error = true;
if (!args[9]->IsUndefined()) {
CHECK(args[9]->IsBoolean());
should_throw_on_error = args[9]->BooleanValue(args.GetIsolate());
}

// Read cache from cached data buffer
ScriptCompiler::CachedData* cached_data = nullptr;
if (!cached_data_buf.IsEmpty()) {
Expand Down Expand Up @@ -1273,7 +1280,10 @@ void ContextifyContext::CompileFunction(
if (!maybe_fn.ToLocal(&fn)) {
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
errors::DecorateErrorStack(env, try_catch);
try_catch.ReThrow();
if (should_throw_on_error)
try_catch.ReThrow();
else
args.GetReturnValue().Set(try_catch.Exception());
}
return;
}
Expand Down

0 comments on commit 34f7a35

Please sign in to comment.