diff --git a/core/core.js b/core/core.js index 729ca4faa4..f0933d0344 100644 --- a/core/core.js +++ b/core/core.js @@ -6,15 +6,15 @@ const { opcall } = window.Deno.core; let opsCache = {}; - const errorMap = { - // Builtin v8 / JS errors - Error, - RangeError, - ReferenceError, - SyntaxError, - TypeError, - URIError, - }; + const errorMap = {}; + // Builtin v8 / JS errors + registerErrorClass("Error", Error); + registerErrorClass("RangeError", RangeError); + registerErrorClass("ReferenceError", ReferenceError); + registerErrorClass("SyntaxError", SyntaxError); + registerErrorClass("TypeError", TypeError); + registerErrorClass("URIError", URIError); + let nextPromiseId = 1; const promiseMap = new Map(); const RING_SIZE = 4 * 1024; @@ -83,23 +83,27 @@ } function registerErrorClass(className, errorClass) { + registerErrorBuilder(className, (msg) => new errorClass(msg)); + } + + function registerErrorBuilder(className, errorBuilder) { if (typeof errorMap[className] !== "undefined") { throw new TypeError(`Error class for "${className}" already registered`); } - errorMap[className] = errorClass; + errorMap[className] = errorBuilder; } function unwrapOpResult(res) { // .$err_class_name is a special key that should only exist on errors if (res?.$err_class_name) { const className = res.$err_class_name; - const ErrorClass = errorMap[className]; - if (!ErrorClass) { + const errorBuilder = errorMap[className]; + if (!errorBuilder) { throw new Error( `Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, ); } - throw new ErrorClass(res.message); + throw errorBuilder(res.message); } return res; } @@ -138,6 +142,7 @@ close, print, resources, + registerErrorBuilder, registerErrorClass, handleAsyncMsgFromRust, syncOpsCache, diff --git a/core/error_builder_test.js b/core/error_builder_test.js new file mode 100644 index 0000000000..aae47c6cd7 --- /dev/null +++ b/core/error_builder_test.js @@ -0,0 +1,30 @@ +const { core } = Deno; + +class DOMException { + constructor(message, code) { + this.msg = message; + this.code = code; + } +} + +core.registerErrorBuilder( + "DOMExceptionOperationError", + function DOMExceptionOperationError(msg) { + return new DOMException(msg, "OperationError"); + }, +); + +try { + core.opSync("op_err", undefined, null); + throw new Error("op_err didn't throw!"); +} catch (err) { + if (!(err instanceof DOMException)) { + throw new Error("err not DOMException"); + } + if (err.msg !== "abc") { + throw new Error("err.message is incorrect"); + } + if (err.code !== "OperationError") { + throw new Error("err.code is incorrect"); + } +} diff --git a/core/runtime.rs b/core/runtime.rs index 547f6aa23f..af12803737 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -1521,7 +1521,9 @@ impl JsRuntime { #[cfg(test)] pub mod tests { use super::*; + use crate::error::custom_error; use crate::modules::ModuleSourceFuture; + use crate::op_sync; use futures::future::lazy; use futures::FutureExt; use std::io; @@ -1768,6 +1770,39 @@ pub mod tests { }); } + #[test] + fn test_error_builder() { + fn op_err( + _: &mut OpState, + _: (), + _: Option, + ) -> Result<(), AnyError> { + Err(custom_error("DOMExceptionOperationError", "abc")) + } + + pub fn get_error_class_name(_: &AnyError) -> &'static str { + "DOMExceptionOperationError" + } + + run_in_task(|mut cx| { + let mut runtime = JsRuntime::new(RuntimeOptions { + get_error_class_fn: Some(&get_error_class_name), + ..Default::default() + }); + runtime.register_op("op_err", op_sync(op_err)); + runtime.sync_ops_cache(); + runtime + .execute( + "error_builder_test.js", + include_str!("error_builder_test.js"), + ) + .unwrap(); + if let Poll::Ready(Err(_)) = runtime.poll_event_loop(&mut cx) { + unreachable!(); + } + }); + } + #[test] fn will_snapshot() { let snapshot = { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index d2626a07d8..8d75ea739c 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -184,10 +184,10 @@ delete Object.prototype.__proto__; core.registerErrorClass("Http", errors.Http); core.registerErrorClass("Busy", errors.Busy); core.registerErrorClass("NotSupported", errors.NotSupported); - core.registerErrorClass( + core.registerErrorBuilder( "DOMExceptionOperationError", function DOMExceptionOperationError(msg) { - DOMException.prototype.constructor.call(this, msg, "OperationError"); + return new DOMException(msg, "OperationError"); }, ); }