fix(core): error registration could pollute constructors (#10422)

Co-authored-by: Luca Casonato <lucacasonato@yahoo.com>
This commit is contained in:
Aaron O'Mullan 2021-05-03 17:30:41 +02:00 committed by GitHub
parent 7bc03523d0
commit d21380728f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 85 additions and 15 deletions

View file

@ -6,15 +6,15 @@
const { opcall } = window.Deno.core; const { opcall } = window.Deno.core;
let opsCache = {}; let opsCache = {};
const errorMap = { const errorMap = {};
// Builtin v8 / JS errors // Builtin v8 / JS errors
Error, registerErrorClass("Error", Error);
RangeError, registerErrorClass("RangeError", RangeError);
ReferenceError, registerErrorClass("ReferenceError", ReferenceError);
SyntaxError, registerErrorClass("SyntaxError", SyntaxError);
TypeError, registerErrorClass("TypeError", TypeError);
URIError, registerErrorClass("URIError", URIError);
};
let nextPromiseId = 1; let nextPromiseId = 1;
const promiseMap = new Map(); const promiseMap = new Map();
const RING_SIZE = 4 * 1024; const RING_SIZE = 4 * 1024;
@ -83,23 +83,27 @@
} }
function registerErrorClass(className, errorClass) { function registerErrorClass(className, errorClass) {
registerErrorBuilder(className, (msg) => new errorClass(msg));
}
function registerErrorBuilder(className, errorBuilder) {
if (typeof errorMap[className] !== "undefined") { if (typeof errorMap[className] !== "undefined") {
throw new TypeError(`Error class for "${className}" already registered`); throw new TypeError(`Error class for "${className}" already registered`);
} }
errorMap[className] = errorClass; errorMap[className] = errorBuilder;
} }
function unwrapOpResult(res) { function unwrapOpResult(res) {
// .$err_class_name is a special key that should only exist on errors // .$err_class_name is a special key that should only exist on errors
if (res?.$err_class_name) { if (res?.$err_class_name) {
const className = res.$err_class_name; const className = res.$err_class_name;
const ErrorClass = errorMap[className]; const errorBuilder = errorMap[className];
if (!ErrorClass) { if (!errorBuilder) {
throw new Error( throw new Error(
`Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, `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; return res;
} }
@ -138,6 +142,7 @@
close, close,
print, print,
resources, resources,
registerErrorBuilder,
registerErrorClass, registerErrorClass,
handleAsyncMsgFromRust, handleAsyncMsgFromRust,
syncOpsCache, syncOpsCache,

View file

@ -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");
}
}

View file

@ -1521,7 +1521,9 @@ impl JsRuntime {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::error::custom_error;
use crate::modules::ModuleSourceFuture; use crate::modules::ModuleSourceFuture;
use crate::op_sync;
use futures::future::lazy; use futures::future::lazy;
use futures::FutureExt; use futures::FutureExt;
use std::io; use std::io;
@ -1768,6 +1770,39 @@ pub mod tests {
}); });
} }
#[test]
fn test_error_builder() {
fn op_err(
_: &mut OpState,
_: (),
_: Option<ZeroCopyBuf>,
) -> 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] #[test]
fn will_snapshot() { fn will_snapshot() {
let snapshot = { let snapshot = {

View file

@ -184,10 +184,10 @@ delete Object.prototype.__proto__;
core.registerErrorClass("Http", errors.Http); core.registerErrorClass("Http", errors.Http);
core.registerErrorClass("Busy", errors.Busy); core.registerErrorClass("Busy", errors.Busy);
core.registerErrorClass("NotSupported", errors.NotSupported); core.registerErrorClass("NotSupported", errors.NotSupported);
core.registerErrorClass( core.registerErrorBuilder(
"DOMExceptionOperationError", "DOMExceptionOperationError",
function DOMExceptionOperationError(msg) { function DOMExceptionOperationError(msg) {
DOMException.prototype.constructor.call(this, msg, "OperationError"); return new DOMException(msg, "OperationError");
}, },
); );
} }