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
24 changes: 24 additions & 0 deletions crates/perry-codegen/src/codegen/artifacts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,29 @@ pub(super) fn emit_module_artifacts(c: ModuleArtifactsCtx<'_>) -> Result<()> {
}
}
}
// Strict-mode user functions (file-level `"use strict"` or body
// directive), for OrdinaryCallBindThis in `call`/`apply`/`bind`: a
// strict callee must observe the raw primitive `thisArg`, a sloppy one
// gets it boxed. Same two symbol forms as the generator registries.
let mut user_fn_wrapper_strict: std::collections::HashSet<String> = hir
.functions
.iter()
.filter(|f| f.is_strict)
.filter_map(|f| {
func_names
.get(&f.id)
.map(|name| format!("__perry_wrap_{}", name))
})
.collect();
for (func_id, expr) in closures {
if let perry_hir::Expr::Closure { is_strict, .. } = expr {
if *is_strict {
user_fn_wrapper_strict
.insert(format!("perry_closure_{}__{}", module_prefix, func_id));
}
}
}

// #3664: async-generator wrapper symbols, identified by the func_ids the
// generator transform recorded (it cleared `is_async` before we get here,
// so the body shape alone can't tell async generators from sync ones).
Expand Down Expand Up @@ -1541,6 +1564,7 @@ pub(super) fn emit_module_artifacts(c: ModuleArtifactsCtx<'_>) -> Result<()> {
&user_fn_wrapper_async,
&user_fn_wrapper_generator,
&user_fn_wrapper_async_generator,
&user_fn_wrapper_strict,
&user_fn_display_names,
&user_fn_source,
);
Expand Down
11 changes: 11 additions & 0 deletions crates/perry-codegen/src/codegen/string_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ pub(super) fn emit_string_pool(
// `%AsyncGeneratorFunction%`/`%AsyncGenerator%` intrinsic chain (and
// `util.types.isAsyncFunction`) resolve correctly for them.
user_fn_wrapper_async_generator: &std::collections::HashSet<String>,
// Strict-mode user functions (wrapper or inline-closure symbols).
// Each entry produces one `js_register_closure_strict_function` call so
// call/apply/bind can apply spec OrdinaryCallBindThis (#4850).
user_fn_wrapper_strict: &std::collections::HashSet<String>,
// `(wrapper_symbol, display_name)` for every top-level user function
// we want `console.log` / `util.inspect` to label with the original
// JS name. Each entry produces one `js_register_function_name` call
Expand Down Expand Up @@ -992,5 +996,12 @@ pub(super) fn emit_string_pool(
);
}

let mut sorted_strict_wrappers: Vec<String> = user_fn_wrapper_strict.iter().cloned().collect();
sorted_strict_wrappers.sort();
for wrap_sym in sorted_strict_wrappers {
let func_ref = format!("@{}", wrap_sym);
blk.call_void("js_register_closure_strict_function", &[(PTR, &func_ref)]);
}

blk.ret_void();
}
5 changes: 5 additions & 0 deletions crates/perry-codegen/src/expr/env_clones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
// this same singleton for `globalThis.process.env` etc.
Ok(ctx.block().call(DOUBLE, "js_get_global_this", &[]))
}
Expr::ModuleTopThis => {
// CJS-style module top-level `this`: a lazily-allocated plain
// object (the module's `exports` stand-in), NOT `globalThis`.
Ok(ctx.block().call(DOUBLE, "js_module_top_this", &[]))
}
Expr::DateToISOString(d) => {
let v = lower_expr(ctx, d)?;
let blk = ctx.block();
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen/src/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1864,6 +1864,7 @@ pub(crate) fn lower_expr(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
| Expr::EnvGetDynamic(..)
| Expr::ProcessEnv => array_methods::lower(ctx, expr),
Expr::GlobalThisExpr
| Expr::ModuleTopThis
| Expr::DateToISOString(..)
| Expr::DateToLocaleString(..)
| Expr::FetchGetWithAuth { .. }
Expand Down
2 changes: 2 additions & 0 deletions crates/perry-codegen/src/runtime_decls/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ pub fn declare_phase_b_strings(module: &mut LlModule) {
module.declare_function("js_register_closure_arity", VOID, &[PTR, I32]);
module.declare_function("js_register_closure_length", VOID, &[PTR, I32]);
module.declare_function("js_register_closure_arrow_function", VOID, &[PTR]);
module.declare_function("js_register_closure_strict_function", VOID, &[PTR]);
module.declare_function("js_register_closure_async_function", VOID, &[PTR]);
module.declare_function("js_register_closure_generator_function", VOID, &[PTR]);
module.declare_function("js_register_closure_async_generator_function", VOID, &[PTR]);
Expand Down Expand Up @@ -1198,6 +1199,7 @@ pub fn declare_phase_b_strings(module: &mut LlModule) {
module.declare_function("js_with_implicit_read", DOUBLE, &[DOUBLE, DOUBLE]);
// Iterator-protocol result validation (for-of lazy loop).
module.declare_function("js_iterator_result_validate", DOUBLE, &[DOUBLE]);
module.declare_function("js_global_get_or_throw_unresolved", DOUBLE, &[DOUBLE]);
module.declare_function("js_throw_reference_error_this_before_super", DOUBLE, &[]);
module.declare_function("js_throw_reference_error_super_delete", DOUBLE, &[]);
module.declare_function(
Expand Down
1 change: 1 addition & 0 deletions crates/perry-codegen/src/runtime_decls/strings_part2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ pub(crate) fn declare_phase_b_strings_part2(module: &mut LlModule) {
// The codegen IndexGet/IndexSet paths on `Expr::GlobalGet` route
// through this helper.
module.declare_function("js_get_global_this", DOUBLE, &[]);
module.declare_function("js_module_top_this", DOUBLE, &[]);
module.declare_function("js_global_or_console_property_by_name", DOUBLE, &[I64]);
// Refs #420: register a static computed-key Symbol field on a class.
// Called from `init_static_fields` for each `static [Symbol.X] = init`.
Expand Down
5 changes: 5 additions & 0 deletions crates/perry-hir/src/ir/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,11 @@ pub enum Expr {
// value is not a function` at module init and the import
// resolves to undefined. Followup to #957 / PR #959.
GlobalThisExpr,
/// `this` in module top-level code. Node runs the assembled test files
/// as CJS, where top-level `this` is `module.exports` — a fresh plain
/// object distinct from `globalThis`. Lowered separately from
/// `Expr::This` so function-body `this` semantics are untouched.
ModuleTopThis,
// Process uptime: process.uptime() -> number (seconds)
ProcessUptime,
// Process current working directory: process.cwd() -> string
Expand Down
Loading