Skip to content

runtime: expose standard global function values on globalThis #2905

Closed

Description

@andrewtdiz

Summary

Node exposes the standard scalar/global helper APIs as function-valued properties on globalThis, and those properties are the same callable values as the bare bindings. Perry currently implements/lower-cases several of the bare calls, but does not populate the corresponding globalThis properties, so rebinding or property-based access observes undefined / non-callable behavior instead of Node-compatible functions.

Node behavior

On Node v25.9.0:

const names = [
  'parseInt',
  'parseFloat',
  'isNaN',
  'isFinite',
  'encodeURI',
  'decodeURI',
  'encodeURIComponent',
  'decodeURIComponent',
  'escape',
  'unescape',
];

for (const name of names) {
  console.log(name, typeof globalThis[name], globalThis[name] === eval(name));
}

console.log(encodeURIComponent('a b?é'), decodeURIComponent('a%20b%3F%C3%A9'));
console.log(escape('a b?é'), unescape('a%20b%3F%E9'));

prints function true for every listed global, followed by successful URI / legacy escape round-trips.

Perry evidence

Perry already has direct-call lowering/runtime helpers for several of these names:

  • crates/perry-hir/src/lower/expr_call/globals.rs lowers bare parseInt, parseFloat, isNaN, isFinite, encodeURI, decodeURI, encodeURIComponent, and decodeURIComponent calls.
  • crates/perry-runtime/src/builtins/numbers.rs implements js_parse_int, js_parse_float, js_is_nan, and js_is_finite.
  • crates/perry-runtime/src/builtins/globals.rs implements js_encode_uri, js_decode_uri, js_encode_uri_component, and js_decode_uri_component.

But the globalThis population path only exposes a smaller callable set:

  • crates/perry-runtime/src/object/global_this.rs GLOBAL_THIS_BUILTIN_FUNCTIONS lists only structuredClone, atob, btoa, timers, immediates, and queueMicrotask.
  • The matching thunk table in populate_global_this_builtins() only dispatches those same names.
  • The codegen mirror in crates/perry-codegen/src/expr/helpers.rs::is_global_this_builtin_name() omits these standard helper names, so property reads / typeof globalThis.parseInt do not take the populated-global path either.
  • escape / unescape are also standard global function values in Node, but no global helper surface appears to be populated for them; source hits are querystring/lodash-specific helpers rather than globalThis.escape / globalThis.unescape.

That means code such as:

const parse = globalThis.parseInt;
const enc = globalThis.encodeURIComponent;
console.log(typeof parse, parse('10', 2));
console.log(typeof enc, enc('a b'));
console.log(globalThis.parseInt === parseInt);

should work like Node but currently falls outside Perry's populated globalThis callable surface.

Acceptance criteria

  • globalThis.parseInt, parseFloat, isNaN, isFinite, encodeURI, decodeURI, encodeURIComponent, decodeURIComponent, escape, and unescape are function-valued properties.
  • Rebound calls through those properties dispatch the same semantics as the bare built-ins.
  • globalThis.<name> === <name> matches Node for the listed helpers where Perry exposes bare globals.
  • typeof globalThis.<name> matches Node's "function" result.

Related but separate: #2889 tracks rebound constructor values such as globalThis.Array / globalThis.Object; this issue is only for standard global helper functions.

Activity

  1. added a commit that references this issue on May 30, 2026
    08ec46b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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