Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/expr/instance_misc1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
"ReferenceError" => 0xFFFF0012u32,
"SyntaxError" => 0xFFFF0013u32,
"AggregateError" => 0xFFFF0014u32,
"EvalError" | "globalThis.EvalError" => 0xFFFF0015u32,
"URIError" | "globalThis.URIError" => 0xFFFF0016u32,
// Uint8Array / Buffer — runtime detects these via a
// thread-local buffer registry (see buffer.rs). The
// TextEncoder path registers its ArrayHeader result
Expand Down
19 changes: 19 additions & 0 deletions crates/perry-codegen/src/lower_call/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ pub(super) fn lower_builtin_new(
return Ok(None);
}
match class_name {
"EvalError" | "URIError" => {
let msg_box = if let Some(message) = args.first() {
lower_expr(ctx, message)?
} else {
lower_expr(ctx, &Expr::String(String::new()))?
};
for arg in args.iter().skip(1) {
let _ = lower_expr(ctx, arg)?;
}
let blk = ctx.block();
let msg_handle = unbox_to_i64(blk, &msg_box);
let runtime = if class_name == "EvalError" {
"js_evalerror_new"
} else {
"js_urierror_new"
};
let err_handle = blk.call(I64, runtime, &[(I64, &msg_handle)]);
Ok(Some(nanbox_pointer_inline(blk, &err_handle)))
}
// `new RegExp(pattern)` / `new RegExp(pattern, flags)` — call
// js_regexp_new directly so the resulting object is a real
// RegExpHeader (registered in REGEX_POINTERS, .test/.exec/etc
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/runtime_decls/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,8 @@ pub fn declare_phase_b_strings(module: &mut LlModule) {
module.declare_function("js_rangeerror_new", I64, &[I64]);
module.declare_function("js_syntaxerror_new", I64, &[I64]);
module.declare_function("js_referenceerror_new", I64, &[I64]);
module.declare_function("js_evalerror_new", I64, &[I64]);
module.declare_function("js_urierror_new", I64, &[I64]);
// WeakMap / WeakSet / WeakRef / FinalizationRegistry — called
// via ExternFuncRef from the HIR lowering (which synthesizes
// `Call(ExternFuncRef("js_weakmap_set"), [...])`). The f64/f64
Expand Down
4 changes: 4 additions & 0 deletions crates/perry-hir/src/lower/lower_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ pub(crate) fn lower_expr(ctx: &mut LoweringContext, expr: &ast::Expr) -> Result<
&& name != "Error"
&& name != "TypeError"
&& name != "RangeError"
&& name != "SyntaxError"
&& name != "ReferenceError"
&& name != "EvalError"
&& name != "URIError"
&& name != "Promise"
&& name != "Map"
&& name != "Set"
Expand Down
33 changes: 33 additions & 0 deletions crates/perry-runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub const ERROR_KIND_RANGE_ERROR: u32 = 2;
pub const ERROR_KIND_REFERENCE_ERROR: u32 = 3;
pub const ERROR_KIND_SYNTAX_ERROR: u32 = 4;
pub const ERROR_KIND_AGGREGATE_ERROR: u32 = 5;
pub const ERROR_KIND_EVAL_ERROR: u32 = 6;
pub const ERROR_KIND_URI_ERROR: u32 = 7;

/// Special class IDs for `instanceof` checks (must match perry-codegen/src/expr.rs)
pub const CLASS_ID_ERROR: u32 = 0xFFFF0001;
Expand All @@ -32,6 +34,8 @@ pub const CLASS_ID_RANGE_ERROR: u32 = 0xFFFF0011;
pub const CLASS_ID_REFERENCE_ERROR: u32 = 0xFFFF0012;
pub const CLASS_ID_SYNTAX_ERROR: u32 = 0xFFFF0013;
pub const CLASS_ID_AGGREGATE_ERROR: u32 = 0xFFFF0014;
pub const CLASS_ID_EVAL_ERROR: u32 = 0xFFFF0015;
pub const CLASS_ID_URI_ERROR: u32 = 0xFFFF0016;
/// AssertionError is a plain ObjectHeader (so it can carry the extra
/// `actual` / `expected` / `operator` / `code` / `generatedMessage`
/// fields Node attaches), but it is registered via
Expand Down Expand Up @@ -208,6 +212,18 @@ pub extern "C" fn js_syntaxerror_new(message: *mut StringHeader) -> *mut ErrorHe
unsafe { alloc_error(ERROR_KIND_SYNTAX_ERROR, b"SyntaxError", message) }
}

/// Create a new EvalError with a message
#[no_mangle]
pub extern "C" fn js_evalerror_new(message: *mut StringHeader) -> *mut ErrorHeader {
unsafe { alloc_error(ERROR_KIND_EVAL_ERROR, b"EvalError", message) }
}

/// Create a new URIError with a message
#[no_mangle]
pub extern "C" fn js_urierror_new(message: *mut StringHeader) -> *mut ErrorHeader {
unsafe { alloc_error(ERROR_KIND_URI_ERROR, b"URIError", message) }
}

/// Create a new AggregateError with an errors array and a message
#[no_mangle]
pub extern "C" fn js_aggregateerror_new(
Expand Down Expand Up @@ -543,4 +559,21 @@ mod tostring_tests {
let out = unsafe { read_string_header_owned(js_error_to_string(e)) };
assert_eq!(out, "TypeError: bad");
}

#[test]
fn eval_and_uri_errors_have_distinct_kinds_and_names() {
let eval = js_evalerror_new(s(b"eval"));
assert_eq!(js_error_get_kind(eval), ERROR_KIND_EVAL_ERROR);
assert_eq!(
unsafe { read_string_header_owned(js_error_get_name(eval)) },
"EvalError"
);

let uri = js_urierror_new(s(b"uri"));
assert_eq!(js_error_get_kind(uri), ERROR_KIND_URI_ERROR);
assert_eq!(
unsafe { read_string_header_owned(js_error_get_name(uri)) },
"URIError"
);
}
}
6 changes: 6 additions & 0 deletions crates/perry-runtime/src/object/assert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ fn constructor_name_matches_builtin_error(thrown: f64, expected: f64) -> bool {
"ReferenceError"
} else if expected.to_bits() == global_builtin(b"SyntaxError").to_bits() {
"SyntaxError"
} else if expected.to_bits() == global_builtin(b"EvalError").to_bits() {
"EvalError"
} else if expected.to_bits() == global_builtin(b"URIError").to_bits() {
"URIError"
} else if expected.to_bits() == global_builtin(b"AggregateError").to_bits() {
"AggregateError"
} else {
Expand All @@ -157,6 +161,8 @@ fn constructor_name_matches_builtin_error(thrown: f64, expected: f64) -> bool {
| "RangeError"
| "ReferenceError"
| "SyntaxError"
| "EvalError"
| "URIError"
| "AggregateError"
) {
return false;
Expand Down
16 changes: 15 additions & 1 deletion crates/perry-runtime/src/object/class_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ pub(crate) fn report_dispatch_miss(tower: &str, recv: f64, name: &str, returning
/// `js_new_function_construct` to dispatch `new <inst.constructor>(...)`
/// shapes (date-fns `constructFrom`, lodash-style `Array` cloning, ...)
/// to the right runtime factory.
fn identify_global_builtin_constructor(func_value: f64) -> Option<&'static str> {
pub(super) fn identify_global_builtin_constructor(func_value: f64) -> Option<&'static str> {
use crate::value::JSValue;
let jv = JSValue::from_bits(func_value.to_bits());
if !jv.is_pointer() {
Expand Down Expand Up @@ -861,6 +861,20 @@ pub unsafe extern "C" fn js_new_function_construct(
let obj = js_object_alloc(0, 0);
return crate::value::js_nanbox_pointer(obj as i64);
}
"EvalError" | "URIError" => {
let message = if args.is_empty() || args[0].to_bits() == crate::value::TAG_UNDEFINED
{
crate::string::js_string_from_bytes(b"".as_ptr(), 0)
} else {
crate::builtins::js_string_coerce(args[0])
};
let error = if name == "EvalError" {
crate::error::js_evalerror_new(message)
} else {
crate::error::js_urierror_new(message)
};
return crate::value::js_nanbox_pointer(error as i64);
}
"TextEncoderStream" | "TextDecoderStream" => {
return js_text_encoding_stream_new();
}
Expand Down
2 changes: 1 addition & 1 deletion crates/perry-runtime/src/object/global_this.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ fn populate_global_this_builtins(singleton: *mut ObjectHeader) {
if name == "String" {
crate::closure::js_register_closure_arity(func_ptr, 1);
}
if name == "File" {
if matches!(name, "File" | "EvalError" | "URIError") {
super::native_module::set_bound_native_closure_name(closure_ptr, name);
}
if name == "Error" {
Expand Down
30 changes: 30 additions & 0 deletions crates/perry-runtime/src/object/instanceof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ pub extern "C" fn js_instanceof_dynamic(value: f64, type_ref: f64) -> f64 {
if is_buffer_constructor_value(type_ref) {
return js_instanceof(value, crate::buffer::BUFFER_TYPE_ID);
}
if let Some(name) = identify_global_builtin_constructor(type_ref) {
let class_id = match name {
"Error" => crate::error::CLASS_ID_ERROR,
"TypeError" => crate::error::CLASS_ID_TYPE_ERROR,
"RangeError" => crate::error::CLASS_ID_RANGE_ERROR,
"ReferenceError" => crate::error::CLASS_ID_REFERENCE_ERROR,
"SyntaxError" => crate::error::CLASS_ID_SYNTAX_ERROR,
"EvalError" => crate::error::CLASS_ID_EVAL_ERROR,
"URIError" => crate::error::CLASS_ID_URI_ERROR,
"AggregateError" => crate::error::CLASS_ID_AGGREGATE_ERROR,
_ => 0,
};
if class_id != 0 {
return js_instanceof(value, class_id);
}
}
if crate::node_submodules::is_diagnostics_channel_constructor_value(type_ref) {
return if crate::node_submodules::diagnostics_channel_is_channel_instance_value(value) {
f64::from_bits(crate::value::TAG_TRUE)
Expand Down Expand Up @@ -443,6 +459,20 @@ pub extern "C" fn js_instanceof(value: f64, class_id: u32) -> f64 {
false_val
}
}
crate::error::CLASS_ID_EVAL_ERROR => {
if kind == crate::error::ERROR_KIND_EVAL_ERROR {
true_val
} else {
false_val
}
}
crate::error::CLASS_ID_URI_ERROR => {
if kind == crate::error::ERROR_KIND_URI_ERROR {
true_val
} else {
false_val
}
}
crate::error::CLASS_ID_AGGREGATE_ERROR => {
if kind == crate::error::ERROR_KIND_AGGREGATE_ERROR {
true_val
Expand Down
32 changes: 32 additions & 0 deletions test-parity/node-suite/globals/error-eval-uri-constructors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
function show(label: string, value: any) {
console.log(label + ":", String(value));
}

function showError(label: string, error: any, ctor: any) {
show(label + " ctor name", ctor.name);
show(label + " name", error.name);
show(label + " message", error.message);
show(label + " self dynamic", error instanceof ctor);
show(label + " error base", error instanceof Error);
}

const evalDirect: any = new EvalError("msg");
showError("eval direct", evalDirect, EvalError);
show("eval direct self static", evalDirect instanceof EvalError);

const uriDirect: any = new URIError("msg");
showError("uri direct", uriDirect, URIError);
show("uri direct self static", uriDirect instanceof URIError);

const evalGlobal: any = new globalThis.EvalError("global");
showError("eval global", evalGlobal, globalThis.EvalError);
show("eval global self static", evalGlobal instanceof globalThis.EvalError);

const uriEmpty: any = new URIError();
show("uri empty name", uriEmpty.name);
show("uri empty message empty", uriEmpty.message === "");
show("uri empty self static", uriEmpty instanceof URIError);

const ReboundEval: any = EvalError;
const evalRebound: any = new ReboundEval("rebound");
showError("eval rebound", evalRebound, ReboundEval);