Skip to content

Spec-mandated TypeErrors / argument-validation throws (Test262 'no exception was thrown') #3146

Description

@proggeramlug

Summary

Perry silently succeeds in many places where the spec mandates a throw. Test262's recurring "Expected a <X> to be thrown but no exception was thrown at all" failures across built-ins/* and language/* come from this. Real code also depends on these throws for control flow / validation.

Concrete gaps (Perry vs Node)

// 1. Member access / method call on null|undefined must throw TypeError
var u;
u.toString();        // Perry: NO THROW (silently returns)   Node: TypeError
u.x;                 // (property GET already throws — OK)   — but method CALL does not
(null).foo();        // Node: TypeError

// 2. Non-callable callback must throw TypeError
[1,2,3].map(5);              // Node: TypeError "5 is not a function"
[1,2,3].forEach(undefined);  // Node: TypeError

// 3. TypedArray spec throws
new Int32Array(-1);          // Node: RangeError
// detached-buffer / out-of-range / non-callable comparator → TypeError/RangeError

// 4. Numeric/range validation
(123).toString(40);          // Node: RangeError (radix out of range)

The "Expected a TypeError/RangeError to be thrown" Test262 messages map directly to these missing throws.

Where the code lives

  • Method-call-on-undefined: the js_native_call_method / member-call dispatch path (crates/perry-runtime/src/object/native_call_method.rs) and the codegen call site — undefined.m() must raise TypeError instead of no-op. (Property get on undefined already throws; mirror that for calls.)
  • Non-callable callback: the js_array_* / js_typed_array_* iterators (array/iter_methods.rs, typedarray.rs) should validate the callback is a closure before iterating.
  • TypedArray range/detached: crates/perry-runtime/src/typedarray.rs constructors + element ops.
  • Error construction helpers already exist (TypeErrorNew/RangeErrorNew, see expr_new.rs).

Recommended approach

Add the spec-required validation throws at the dispatch/runtime entry points. Prefer one shared helper per error shape. Keep the happy path branch-light. Scope conservatively to avoid throwing where Perry intentionally tolerates (the --experimental-strip-types differential is the oracle).

Acceptance criteria

  • undefined.toString() / null.foo() throw TypeError; arr/TA iterators throw TypeError on a non-callable callback.
  • new Int32Array(-1) throws RangeError; (123).toString(40) throws RangeError.
  • Measure: "no exception was thrown" count drops in scripts/test262_subset.py --dir built-ins/TypedArray built-ins/Number built-ins/String --sample-cap 99999.
  • No new false throws (regular happy-path code unaffected).

Refs: Test262 radar #799, Node+TS roadmap #793.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions