Skip to content

runtime: construct EvalError and URIError as native Error subclasses #2885

Description

@andrewtdiz

Summary

EvalError and URIError are exposed as global Error constructors in Node and in Perry's global builtin lists, but Perry's direct new lowering does not allocate them as Error objects. They appear to fall through the generic built-in placeholder path instead of producing native Error-subclass instances.

Node behavior

Observed with local Node v25.9.0:

for (const C of [EvalError, URIError, Error, TypeError]) {
  const e = new C("msg");
  console.log(C.name, e.name, e.message, e instanceof C, e instanceof Error);
}
// EvalError EvalError msg true true
// URIError URIError msg true true
// Error Error msg true true
// TypeError TypeError msg true true

Both constructors also expose .prototype objects and should be usable through globalThis.EvalError / globalThis.URIError.

Perry evidence

  • crates/perry-runtime/src/object/global_this.rs::GLOBAL_THIS_BUILTIN_CONSTRUCTORS lists EvalError and URIError, and install_builtin_prototype_methods() groups them with the other Error prototypes.
  • crates/perry-codegen/src/expr/helpers.rs::is_global_this_builtin_name() also lists EvalError and URIError as function-valued globals.
  • crates/perry-hir/src/lower/expr_new.rs only routes direct Error construction for Error, TypeError, RangeError, ReferenceError, SyntaxError, and BugIndicatingError. It omits EvalError and URIError.
  • Source search found no js_evalerror_new, js_urierror_new, or matching ERROR_KIND_EVAL_ERROR / ERROR_KIND_URI_ERROR runtime allocation path in crates/perry-runtime/src/error.rs.
  • crates/perry-codegen/src/lower_call/new.rs::lower_new() allocates an empty object placeholder for built-in/native names with no dedicated constructor lowering and no user class, so direct new EvalError("msg") / new URIError("msg") cannot match Node's Error object shape.

Expected fix direction

Add native Error-kind support and new lowering for both constructors so:

  • new EvalError("msg") has .name === "EvalError", .message === "msg", and instanceof EvalError / instanceof Error are true.
  • new URIError("msg") has the same shape for URIError.
  • Rebound/global constructor forms continue to use the real constructor/prototype surface.

Related but not duplicate: #2836 tracks { cause } option handling across Error subclasses; this issue is about constructing EvalError / URIError as native Error-subclass instances at all.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions