Skip to content

runtime: make String.prototype.normalize throw RangeError for invalid forms #2782

Description

@andrewtdiz

Summary

Perry implements Unicode normalization for the valid String.prototype.normalize() forms, but invalid form values silently fall back to NFC. Node throws a RangeError when the supplied form is anything other than "NFC", "NFD", "NFKC", or "NFKD".

Node behavior

Local Node v25.9.0 probe:

undefined="é":e9
NFC="é":e9
NFD="é":65+301
NFKC="é":e9
NFKD="é":65+301
BAD=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.
null=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.
=RangeError:The normalization form should be one of NFC, NFD, NFKC, NFKD.

Probe shape:

"e\u0301".normalize()
"e\u0301".normalize("NFC")
"e\u0301".normalize("NFD")
"e\u0301".normalize("NFKC")
"e\u0301".normalize("NFKD")
"e\u0301".normalize("BAD")
"e\u0301".normalize(null)
"e\u0301".normalize("")

The no-argument call defaults to NFC. Supplying null is not the same as omitting the argument: Node applies ToString(null), gets "null", and throws because it is not a valid normalization form.

Perry evidence

  • crates/perry-codegen/src/lower_string_method.rs lowers normalize with zero or one argument and passes the provided argument through to js_string_normalize.
  • crates/perry-runtime/src/string/compare.rs::js_string_normalize() handles the four valid forms, but its wildcard match arm returns str_data.nfc().collect() for everything else.
  • The runtime comment currently says Pass null/empty for default NFC, which contradicts Node behavior for explicit null and empty string arguments.
  • test-files/test_gap_string_methods.ts only covers the valid forms and the no-argument NFC default, so the invalid-form error path is currently untested.

Duplicate check

Searched issues and PRs for:

  • String.normalize
  • normalize RangeError
  • String.prototype.normalize invalid
  • PR search for String.normalize OR String.prototype.normalize OR normalize RangeError

No existing focused issue showed up. The broad docs mention normalize() as a parity gap, but there is no open tracker for this concrete invalid-form behavior.

Expected fix shape

  • Keep the current valid-form behavior for omitted / "NFC" / "NFD" / "NFKC" / "NFKD".
  • Convert explicit form arguments with JS ToString semantics where needed.
  • Throw a Node-compatible RangeError for invalid forms, including "BAD", null, and "".
  • Add a regression test covering both valid forms and invalid-form errors.

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