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
11 changes: 11 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,17 @@ jobs:
if: ${{ !cancelled() }}
run: ./scripts/check_file_size.sh

# #7846: erased TypeScript annotations and initializer-refined local
# types are hints, not runtime proofs. Every codegen read must choose the
# whole-region write-invalidating accessor or carry an inventoried
# runtime/representation justification. Count-exact entries make both a
# new consumer and a stale exemption fail.
- name: Local binding type-proof audit
if: ${{ !cancelled() }}
run: |
python3 scripts/local_binding_type_audit.py --self-test
python3 scripts/local_binding_type_audit.py

# Well-known binding provenance pins: every third-party binding in
# well_known_bindings.toml must carry an [bindings.<name>.upstream]
# pin, and the lock-step rule (ported-at == version) must hold, so a
Expand Down
3 changes: 3 additions & 0 deletions changelog.d/8033-local-binding-runtime-proofs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Bug fixes

- Code generation now requires runtime-derived evidence before treating local bindings as non-pointer or native scalar values, preserving JavaScript semantics for erased annotations and keeping BigInt and object values rooted across collection points.
49 changes: 19 additions & 30 deletions crates/perry-codegen/src/codegen/closure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fn emit_public_typed_closure_trampoline(
module_prefix: &str,
generic_body_name: &str,
kind: TypedFunctionTrampolineKind,
string_capture_count: usize,
capture_reps: &[TypedParamRep],
) -> Result<()> {
let params = match closure_expr {
perry_hir::Expr::Closure { params, .. } => params,
Expand Down Expand Up @@ -155,15 +155,11 @@ fn emit_public_typed_closure_trampoline(
None => ok,
});
}
if string_capture_count > 0 {
if let Some(capture_guard) =
emit_typed_string_capture_guard(blk, "%this_closure", string_capture_count)
{
guard = Some(match guard {
Some(prev) => blk.and(I1, &prev, &capture_guard),
None => capture_guard,
});
}
if let Some(capture_guard) = emit_typed_capture_guard(blk, "%this_closure", capture_reps) {
guard = Some(match guard {
Some(prev) => blk.and(I1, &prev, &capture_guard),
None => capture_guard,
});
}
}

Expand Down Expand Up @@ -253,26 +249,21 @@ fn load_typed_capture(
}
}

pub(crate) fn emit_typed_string_capture_guard(
pub(crate) fn emit_typed_capture_guard(
blk: &mut crate::block::LlBlock,
closure_handle: &str,
capture_count: usize,
capture_reps: &[TypedParamRep],
) -> Option<String> {
let mut guard: Option<String> = None;
for idx in 0..capture_count {
for (idx, rep) in capture_reps.iter().enumerate() {
let idx = idx.to_string();
let captured_bits = blk.call(
I64,
"js_closure_get_capture_bits",
&[(I64, closure_handle), (I32, &idx)],
);
let captured = blk.bitcast_i64_to_double(&captured_bits);
let raw = blk.call(
I32,
"js_typed_string_arg_guard",
&[(DOUBLE, captured.as_str())],
);
let ok = blk.icmp_ne(I32, &raw, "0");
let ok = emit_typed_arg_guard(blk, *rep, &captured);
guard = Some(match guard {
Some(prev) => blk.and(I1, &prev, &ok),
None => ok,
Expand Down Expand Up @@ -913,6 +904,8 @@ pub(super) fn compile_closure(
native_facts: &native_facts,
locals,
local_types,
proven_local_types: std::collections::HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals,
const_string_locals: std::collections::HashMap::new(),
const_number_locals: std::collections::HashMap::new(),
Expand Down Expand Up @@ -1092,7 +1085,7 @@ pub(super) fn compile_closure(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: false,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down Expand Up @@ -1168,23 +1161,19 @@ pub(super) fn compile_closure(
llmod.add_raw_global(raw.clone());
}
if let Some(kind) = typed_public_trampoline {
let string_capture_count = if matches!(kind, TypedFunctionTrampolineKind::StringRef) {
cross_module
.typed_string_closure_capture_counts
.get(&func_id)
.copied()
.unwrap_or(0)
} else {
0
};
let capture_reps = cross_module
.typed_closure_capture_reps
.get(&func_id)
.map(Vec::as_slice)
.unwrap_or(&[]);
emit_public_typed_closure_trampoline(
llmod,
func_id,
closure_expr,
module_prefix,
&llvm_name,
kind,
string_capture_count,
capture_reps,
)?;
}
Ok(())
Expand Down
8 changes: 6 additions & 2 deletions crates/perry-codegen/src/codegen/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,8 @@ pub(super) fn compile_module_entry(
native_facts: &main_native_facts,
locals: HashMap::new(),
local_types: init_local_types,
proven_local_types: HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals: crate::collectors::reassigned_locals(&hir.init),
const_string_locals: HashMap::new(),
const_number_locals: HashMap::new(),
Expand Down Expand Up @@ -912,7 +914,7 @@ pub(super) fn compile_module_entry(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: hir.init_was_unrolled,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down Expand Up @@ -1404,6 +1406,8 @@ pub(super) fn compile_module_entry(
native_facts: &init_native_facts,
locals: HashMap::new(),
local_types: HashMap::new(),
proven_local_types: HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals: crate::collectors::reassigned_locals(&hir.init),
const_string_locals: HashMap::new(),
const_number_locals: HashMap::new(),
Expand Down Expand Up @@ -1583,7 +1587,7 @@ pub(super) fn compile_module_entry(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: hir.init_was_unrolled,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down
4 changes: 3 additions & 1 deletion crates/perry-codegen/src/codegen/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ pub(super) fn compile_function(
native_facts: &native_facts,
locals,
local_types,
proven_local_types: std::collections::HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals: crate::collectors::reassigned_locals(&f.body),
const_string_locals: std::collections::HashMap::new(),
const_number_locals: std::collections::HashMap::new(),
Expand Down Expand Up @@ -888,7 +890,7 @@ pub(super) fn compile_function(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: f.was_unrolled,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down
8 changes: 6 additions & 2 deletions crates/perry-codegen/src/codegen/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ pub(super) fn compile_method(
native_facts: &native_facts,
locals,
local_types,
proven_local_types: std::collections::HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals: crate::collectors::reassigned_locals(&method.body),
const_string_locals: std::collections::HashMap::new(),
const_number_locals: std::collections::HashMap::new(),
Expand Down Expand Up @@ -599,7 +601,7 @@ pub(super) fn compile_method(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: method.was_unrolled,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down Expand Up @@ -1488,6 +1490,8 @@ pub(super) fn compile_static_method(
native_facts: &native_facts,
locals,
local_types,
proven_local_types: std::collections::HashMap::new(),
module_global_proven_types: &cross_module.module_global_proven_types,
reassigned_locals: crate::collectors::reassigned_locals(&f.body),
const_string_locals: std::collections::HashMap::new(),
const_number_locals: std::collections::HashMap::new(),
Expand Down Expand Up @@ -1664,7 +1668,7 @@ pub(super) fn compile_static_method(
typed_i1_closures: &cross_module.typed_i1_closures,
typed_i1_closure_param_reps: &cross_module.typed_i1_closure_param_reps,
typed_string_closures: &cross_module.typed_string_closures,
typed_string_closure_capture_counts: &cross_module.typed_string_closure_capture_counts,
typed_closure_capture_reps: &cross_module.typed_closure_capture_reps,
was_unrolled: f.was_unrolled,
ic_site_counter: ic_base,
ic_globals: Vec::new(),
Expand Down
35 changes: 19 additions & 16 deletions crates/perry-codegen/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ mod testing_feature_gate_tests;
mod typed_abi;
mod typed_abi_opt_report;

pub(crate) use closure::emit_typed_string_capture_guard;
pub(crate) use closure::emit_typed_capture_guard;
pub use helpers::resolve_target_triple;
pub(crate) use helpers::{
decide_codegen_units, decide_full_outline_ic, default_target_triple, full_outline_ic_enabled,
Expand All @@ -217,12 +217,12 @@ pub(crate) use opts::{CrossModuleCtx, ImportedCtor};
pub(crate) use spec_abi::{spec_abi_enabled, spec_function_name, SpecDispatch, SpecFnPlan};
pub(crate) use typed_abi::{
emit_typed_arg_guard, emit_typed_arg_to_raw, generic_closure_body_name,
generic_function_body_name, generic_method_body_name, typed_f64_closure_name,
typed_f64_function_name, typed_f64_method_name, typed_f64_receiver_method_info,
typed_f64_receiver_method_name, typed_i1_closure_name, typed_i1_function_name,
typed_i1_method_name, typed_i32_closure_name, typed_i32_function_name, typed_i32_method_name,
typed_param_reps_match_args, typed_string_closure_name, typed_string_function_name,
typed_string_method_name, TypedParamRep, TypedReceiverMethodInfo,
generic_function_body_name, generic_method_body_name, typed_arg_is_guard_candidate,
typed_f64_closure_name, typed_f64_function_name, typed_f64_method_name,
typed_f64_receiver_method_info, typed_f64_receiver_method_name, typed_i1_closure_name,
typed_i1_function_name, typed_i1_method_name, typed_i32_closure_name, typed_i32_function_name,
typed_i32_method_name, typed_param_reps_match_args, typed_string_closure_name,
typed_string_function_name, typed_string_method_name, TypedParamRep, TypedReceiverMethodInfo,
};

use artifacts::{emit_module_artifacts, ModuleArtifactsCtx};
Expand Down Expand Up @@ -1794,6 +1794,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
local_async_funcs,
local_generator_funcs,
async_step_closures: hir.async_step_closures.iter().copied().collect(),
module_global_proven_types: std::collections::HashMap::new(),
funcs_reading_dynamic_this,
type_aliases: opts.type_aliases,
imported_func_param_counts: opts.imported_func_param_counts,
Expand Down Expand Up @@ -1921,7 +1922,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
typed_i32_closures: std::collections::HashSet::new(),
typed_i1_closures: std::collections::HashSet::new(),
typed_string_closures: std::collections::HashSet::new(),
typed_string_closure_capture_counts: std::collections::HashMap::new(),
typed_closure_capture_reps: std::collections::HashMap::new(),
typed_i1_closure_param_reps: std::collections::HashMap::new(),
compiler_private_async_i32_control_locals,
compiler_private_async_i1_control_locals,
Expand Down Expand Up @@ -2076,6 +2077,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
let module_globals_emit::ModuleGlobals {
module_globals,
module_global_types,
module_global_proven_types,
static_field_globals,
} = module_globals_emit::emit_module_globals(
&mut llmod,
Expand All @@ -2084,6 +2086,7 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
&cross_module.compile_time_constants,
&module_prefix,
);
cross_module.module_global_proven_types = module_global_proven_types;

// Method registry + cross-module method/getter/setter/ctor/static
// extern declares. See `method_registry::build_method_names`.
Expand Down Expand Up @@ -2214,9 +2217,16 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
cross_module.typed_i32_closures.clear();
cross_module.typed_i1_closures.clear();
cross_module.typed_string_closures.clear();
cross_module.typed_string_closure_capture_counts.clear();
cross_module.typed_closure_capture_reps.clear();
cross_module.typed_i1_closure_param_reps.clear();
for (func_id, expr) in &closures {
if let Some(captures) =
typed_abi::typed_f64_closure_capture_reps(expr, &typed_abi_local_types)
{
cross_module
.typed_closure_capture_reps
.insert(*func_id, captures.into_iter().map(|(_, rep)| rep).collect());
}
match typed_abi::typed_f64_closure_rejection_reason_with_types(expr, &typed_abi_local_types)
{
None => {
Expand Down Expand Up @@ -2320,13 +2330,6 @@ pub fn compile_module(hir: &HirModule, opts: CompileOptions) -> Result<Vec<u8>>
.insert(*func_id, reps);
}
}
let capture_count =
typed_abi::typed_string_closure_capture_reps(expr, &typed_abi_local_types)
.map(|captures| captures.len())
.unwrap_or(0);
cross_module
.typed_string_closure_capture_counts
.insert(*func_id, capture_count);
}
Some(reason) => record_typed_clone_rejection(
&mut typed_clone_rejection_records,
Expand Down
Loading
Loading