Skip to content

Commit 8f5d2d1

Browse files
authored
feat(runtime): expose gc() as a callable globalThis property (#5712)
Follow-up to the `typeof gc` capability-guard fix. That change makes the bare-identifier guard `if (typeof gc === "function") gc()` work, but the other idiomatic capability-guard forms read `gc` as a VALUE: if (globalThis.gc) globalThis.gc(); // Node CLI docs' own example global.gc?.(); // Bun's documented form const g = globalThis.gc; g?.(); These all read `globalThis.gc`, which Perry never installed, so they were `undefined` / no-ops even though `gc()` is callable. Install `gc` as a real callable property on the globalThis singleton (a ClosureHeader-backed value, like `setTimeout`/`queueMicrotask`), routed to the same `js_gc_collect` the bare `gc()` intrinsic uses. Non-enumerable; the optional Node `force` argument is accepted but ignored (Perry's gc is a full collection). Now `typeof globalThis.gc === "function"`, `globalThis.gc` is truthy, and `globalThis.gc()` / `globalThis.gc?.()` / `gc?.()` all run a real collection.
1 parent 9ac7abc commit 8f5d2d1

4 files changed

Lines changed: 24 additions & 1 deletion

File tree

crates/perry-runtime/src/object/global_this.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub(crate) use builtin_thunks::{
6161
global_this_btoa_thunk, global_this_decode_uri_component_thunk, global_this_decode_uri_thunk,
6262
global_this_encode_uri_component_thunk, global_this_encode_uri_thunk,
6363
global_this_error_capture_stack_trace_thunk, global_this_error_is_error_thunk,
64-
global_this_error_prepare_stack_trace_thunk, global_this_escape_thunk,
64+
global_this_error_prepare_stack_trace_thunk, global_this_escape_thunk, global_this_gc_thunk,
6565
global_this_is_finite_thunk, global_this_is_nan_thunk, global_this_number_thunk,
6666
global_this_object_thunk, global_this_parse_float_thunk, global_this_parse_int_thunk,
6767
global_this_string_thunk, global_this_structured_clone_thunk, global_this_unescape_thunk,

crates/perry-runtime/src/object/global_this/builtin_thunks.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,20 @@ pub(crate) extern "C" fn global_this_is_nan_thunk(
276276
crate::builtins::js_is_nan(value)
277277
}
278278

279+
/// `globalThis.gc([force])` — request a garbage collection. This is the value
280+
/// form of the same builtin the bare `gc()` call-intrinsic routes to, so
281+
/// `globalThis.gc()`, `global.gc?.()`, `if (globalThis.gc) gc()`, and
282+
/// `const f = gc; f()` all run a real collection. Perry's collector treats
283+
/// `gc()` as a full collection, so Node's optional `force` argument is accepted
284+
/// but ignored. Returns `undefined`.
285+
pub(crate) extern "C" fn global_this_gc_thunk(
286+
_closure: *const crate::closure::ClosureHeader,
287+
_force: f64,
288+
) -> f64 {
289+
crate::gc::js_gc_collect();
290+
f64::from_bits(crate::value::JSValue::undefined().bits())
291+
}
292+
279293
pub(crate) extern "C" fn global_this_is_finite_thunk(
280294
_closure: *const crate::closure::ClosureHeader,
281295
value: f64,

crates/perry-runtime/src/object/global_this/populate.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ pub(crate) fn populate_global_this_builtins(singleton: *mut ObjectHeader) {
425425
false,
426426
true,
427427
),
428+
// `gc([force])` — value form of the bare `gc()` call-intrinsic.
429+
// Non-enumerable (a debug/diagnostic global). The optional `force`
430+
// arg is accepted (arity 1) but ignored — Perry's gc is full.
431+
"gc" => (global_this_gc_thunk as *const u8, 1, false, false),
428432
// #2905: standard global helper functions.
429433
"parseInt" => (global_this_parse_int_thunk as *const u8, 2, false, false),
430434
"parseFloat" => (global_this_parse_float_thunk as *const u8, 1, false, false),

crates/perry-runtime/src/object/global_this_tables.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,11 @@ pub(crate) const GLOBAL_THIS_BUILTIN_FUNCTIONS: &[&str] = &[
196196
"setImmediate",
197197
"clearImmediate",
198198
"queueMicrotask",
199+
// The `gc()` builtin, exposed as a real callable global value so the
200+
// capability-guard idioms `if (globalThis.gc) gc()` / `global.gc?.()` /
201+
// `const f = gc; f()` work (Perry's `gc()` is always available, unlike
202+
// Node's `--expose-gc`-gated one).
203+
"gc",
199204
// #2905: standard global helper functions. These route through Perry's
200205
// real direct-call runtime helpers, so `const p = parseInt; p("42px")`
201206
// and `globalThis.encodeURIComponent("a b")` match Node.

0 commit comments

Comments
 (0)