Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions crates/perry-hir/src/lower/expr_member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1899,13 +1899,42 @@ fn lower_member_inner(ctx: &mut LoweringContext, member: &ast::MemberExpr) -> Re
matches!(member, "fromCharCode" | "fromCodePoint" | "raw")
})
.unwrap_or(false);
// #4521: `Promise.resolve` / `reject` / `all` / `race` /
// `allSettled` / `any` / `withResolvers` / `try` read as
// VALUES need the reified Promise constructor receiver so
// they resolve to the real native function objects (correct
// `.name` / `.length`, callable via reference / `.call`).
// They are installed with metadata via
// `install_constructor_static` (global_this.rs); the
// reroute-undo otherwise collapses them to
// `GlobalGet(0).<name>` (undefined). Direct calls
// (`Promise.all([...])`) take the codegen fast path via the
// `!member_is_call_callee` gate.
let outer_is_reified_promise_static_value = !member_is_call_callee
&& property == "Promise"
&& outer_static_member
.map(|member| {
matches!(
member,
"resolve"
| "reject"
| "all"
| "race"
| "allSettled"
| "any"
| "withResolvers"
| "try"
)
})
.unwrap_or(false);
if !outer_is_prototype_or_proto
&& !receiver_is_namespace_value
&& !outer_is_websocket_static
&& !outer_is_reified_object_static_value
&& !outer_is_reified_builtin_static_value
&& !outer_is_reified_date_static_value
&& !outer_is_reified_string_static_value
&& !outer_is_reified_promise_static_value
&& !receiver_is_detached_console_read
{
object_expr = Expr::GlobalGet(0);
Expand Down
120 changes: 120 additions & 0 deletions crates/perry-runtime/src/object/global_this.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3245,6 +3245,79 @@ extern "C" fn string_from_code_point_static(
crate::value::js_nanbox_string(s as i64)
}

// #4521: reified `Promise` statics so `Promise.all` / `Promise.resolve` / etc.
// are first-class function values (correct `.name` / `.length`, usable via
// reference, `.call`, `.apply`, spread). Direct calls (`Promise.all([...])`)
// still take the codegen fast path in `lower_call/console_promise.rs`; these
// thunks back value reads and rebound/`.call` usage by delegating to the same
// runtime entry points the direct-call path emits. Spec-internal observable
// semantics (per-iteration `this.resolve`, real resolve-element closures with
// `[[AlreadyCalled]]`, `NewPromiseCapability(this)`) are a follow-up — these
// thunks intentionally use the native Promise machinery regardless of `this`.
extern "C" fn promise_resolve_static(
_closure: *const crate::closure::ClosureHeader,
value: f64,
) -> f64 {
let p = crate::promise::js_promise_resolved(value);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_reject_static(
_closure: *const crate::closure::ClosureHeader,
reason: f64,
) -> f64 {
let p = crate::promise::js_promise_rejected(reason);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_all_static(
_closure: *const crate::closure::ClosureHeader,
iterable: f64,
) -> f64 {
let p = crate::promise::combinators::js_promise_all_iterable(iterable);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_race_static(
_closure: *const crate::closure::ClosureHeader,
iterable: f64,
) -> f64 {
let p = crate::promise::combinators::js_promise_race_iterable(iterable);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_all_settled_static(
_closure: *const crate::closure::ClosureHeader,
iterable: f64,
) -> f64 {
let p = crate::promise::combinators::js_promise_all_settled_iterable(iterable);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_any_static(
_closure: *const crate::closure::ClosureHeader,
iterable: f64,
) -> f64 {
let p = crate::promise::combinators::js_promise_any_iterable(iterable);
crate::value::js_nanbox_pointer(p as i64)
}

extern "C" fn promise_with_resolvers_static(_closure: *const crate::closure::ClosureHeader) -> f64 {
let obj = crate::promise::js_promise_with_resolvers();
crate::value::js_nanbox_pointer(obj as i64)
}

// `Promise.try(fn, ...args)`: call-arity 1 (callback) + rest (forwarded args).
extern "C" fn promise_try_static(
_closure: *const crate::closure::ClosureHeader,
callback: f64,
rest: f64,
) -> f64 {
let rest_ptr = crate::value::js_nanbox_get_pointer(rest) as *const crate::array::ArrayHeader;
let p = crate::promise::js_promise_try(callback, rest_ptr);
crate::value::js_nanbox_pointer(p as i64)
}

// #4627: reified `String.raw(callSite, ...substitutions)` tag function. One
// fixed param (the template/cooked object) then a rest of substitutions, which
// `js_string_raw` reads by numeric index — so `rest` (the collected array) is
Expand Down Expand Up @@ -3696,6 +3769,53 @@ fn install_builtin_constructor_statics(name: &str, ctor: *mut crate::closure::Cl
true,
);
}
"Promise" => {
// #4521: reify the `Promise` statics as first-class function values
// (correct `.name` / `.length`, usable via reference / `.call` /
// `.apply` / spread). Direct calls (`Promise.all([...])`) keep the
// codegen fast path; these back value reads and rebound usage.
install_constructor_static(
ctor,
"resolve",
promise_resolve_static as *const u8,
1,
false,
);
install_constructor_static(
ctor,
"reject",
promise_reject_static as *const u8,
1,
false,
);
install_constructor_static(ctor, "all", promise_all_static as *const u8, 1, false);
install_constructor_static(ctor, "race", promise_race_static as *const u8, 1, false);
install_constructor_static(
ctor,
"allSettled",
promise_all_settled_static as *const u8,
1,
false,
);
install_constructor_static(ctor, "any", promise_any_static as *const u8, 1, false);
// `withResolvers` takes no arguments → spec `.length` 0.
install_constructor_static(
ctor,
"withResolvers",
promise_with_resolvers_static as *const u8,
0,
false,
);
// `try(fn, ...args)`: 1 fixed param (callback) + rest; spec `.length` 1.
install_constructor_static_with_call_arity(
ctor,
"try",
promise_try_static as *const u8,
1,
1,
true,
);
}
"ArrayBuffer" => {
install_constructor_static(
ctor,
Expand Down