Skip to content

Commit 8beca2f

Browse files
proggeramlugRalph Küpper
andauthored
fix: method-scoped prototype guards, IPC transports, value profiles, Intl worklist (#8723)
Lands #8672, #8718, #8720 and #8659. #8672's blocker is resolved the way the evidence pointed. Its own `is_bound_native_method_closure_value` is gone; only main's `is_bound_native_constructor_closure_value` remains, and the branch that called it in `parent_static.rs` is deleted. That branch was unreachable under either predicate -- the `if let Some(..) = bound_native_callable_ module_and_method(..)` block directly above returns unconditionally, and both predicates require that same query to be `Some` -- so removing it is behaviour-preserving rather than a choice between two semantics. #8718 (closes #6620) routes `server.listen(path)`, `net.connect(path)` and the `{ path }` overloads through real Windows named pipes and Unix-domain sockets instead of falling back to TCP. #8720 stabilizes native value profile boundaries; #8659 completes the Intl 402 test262 worklist. One fix on top: a changelog fragment for #8718, which had neither one nor a skip-changelog label. #8719 is NOT in this batch -- it conflicts with #8672 on `lower_call/method_override.rs`, which both touch. No version bump. Co-authored-by: Ralph Küpper <ralph@skelpo.com>
1 parent 3f65530 commit 8beca2f

74 files changed

Lines changed: 4785 additions & 1122 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Completed the #5896 Test262 Intl402 worklist. Locale canonicalization now applies ICU4X CLDR aliases and likely-subtag data, Intl constructors consistently handle proxy-backed locale and option objects, Collator/PluralRules/RelativeTimeFormat/Segmenter behavior matches the listed ECMA-402 cases, derived Intl classes preserve their native prototypes, and maximum-length arrays stay logically sparse. All 101 pinned worklist tests now pass.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
category: Performance
3+
title: Restore method-scoped prototype guards
4+
---
5+
6+
Prototype mutation now invalidates direct-call guards by method-name slot
7+
instead of permanently disabling every method guard in the process. Hash
8+
collisions remain conservative, and dynamic prototype replacement retains a
9+
global fail-closed escape hatch.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added local IPC transports to `node:net` (closes #6620). `server.listen(path)`, `net.connect(path)` and the `{ path }` overloads now route through a real Windows named pipe or Unix-domain socket rather than falling back to TCP, reusing the existing socket lifecycle. Connection ordering, connection limits and drop events, close cleanup, `server.address()` and deferred `Socket.connect()` behaviour are preserved, with platform round-trip coverage added.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
### Added
2+
3+
- Stabilized the native value profile with checked exact-width scalars,
4+
source-linked and nested POD layouts, and value-copy semantics across local
5+
assignments and ordinary function boundaries. Invalid or imprecise native
6+
crossings now fail explicitly instead of truncating or losing precision.

crates/perry-api-manifest/src/native_abi.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,20 +226,32 @@ pub enum NativeAbiType {
226226
Json,
227227
/// JavaScript truthiness lowered to a C `i32` boolean slot.
228228
Bool,
229+
/// Signed 8-bit integer slot.
230+
I8,
231+
/// Signed 16-bit integer slot.
232+
I16,
229233
/// Signed 32-bit integer slot.
230234
I32,
231235
/// Signed 64-bit integer slot.
232236
I64,
233237
/// Legacy string return where the native function returns the string
234238
/// pointer as an `i64` instead of a C pointer.
235239
I64String,
240+
/// Unsigned 8-bit integer slot. The manifest spelling `byte` is accepted
241+
/// as an alias and canonicalizes to `u8`.
242+
U8,
243+
/// Unsigned 16-bit integer slot.
244+
U16,
236245
/// Unsigned 32-bit integer slot.
237246
U32,
238247
/// Unsigned 64-bit integer slot.
239248
U64,
240249
/// Pointer-sized unsigned integer slot. Perry's native runtime targets are
241250
/// currently 64-bit, so this lowers as an LLVM `i64`.
242251
USize,
252+
/// Pointer-sized signed integer slot. Perry's native runtime targets are
253+
/// currently 64-bit, so this lowers as an LLVM `i64`.
254+
ISize,
243255
/// 32-bit float slot.
244256
F32,
245257
/// 64-bit float slot. The legacy manifest spelling `"number"` is accepted
@@ -280,12 +292,17 @@ impl NativeAbiType {
280292
"string" => Ok(Self::String),
281293
"json" => Ok(Self::Json),
282294
"bool" | "boolean" => Ok(Self::Bool),
295+
"i8" => Ok(Self::I8),
296+
"i16" => Ok(Self::I16),
283297
"i32" => Ok(Self::I32),
284298
"i64" => Ok(Self::I64),
285299
"i64_str" => Ok(Self::I64String),
300+
"u8" | "byte" => Ok(Self::U8),
301+
"u16" => Ok(Self::U16),
286302
"u32" => Ok(Self::U32),
287303
"u64" => Ok(Self::U64),
288304
"usize" => Ok(Self::USize),
305+
"isize" => Ok(Self::ISize),
289306
"f32" => Ok(Self::F32),
290307
"f64" | "number" => Ok(Self::F64),
291308
"ptr" => Ok(Self::Ptr),
@@ -338,12 +355,17 @@ impl NativeAbiType {
338355
Self::String => "string",
339356
Self::Json => "json",
340357
Self::Bool => "bool",
358+
Self::I8 => "i8",
359+
Self::I16 => "i16",
341360
Self::I32 => "i32",
342361
Self::I64 => "i64",
343362
Self::I64String => "i64_str",
363+
Self::U8 => "u8",
364+
Self::U16 => "u16",
344365
Self::U32 => "u32",
345366
Self::U64 => "u64",
346367
Self::USize => "usize",
368+
Self::ISize => "isize",
347369
Self::F32 => "f32",
348370
Self::F64 => "f64",
349371
Self::Ptr => "ptr",
@@ -419,11 +441,16 @@ impl NativeAbiType {
419441
pub fn is_valid_pod_field(&self) -> bool {
420442
matches!(
421443
self,
422-
Self::I32
444+
Self::I8
445+
| Self::I16
446+
| Self::I32
423447
| Self::I64
448+
| Self::U8
449+
| Self::U16
424450
| Self::U32
425451
| Self::U64
426452
| Self::USize
453+
| Self::ISize
427454
| Self::F32
428455
| Self::F64
429456
| Self::BufferLen
@@ -464,11 +491,16 @@ impl NativeAbiType {
464491
Self::Pod(_) => "object",
465492
Self::PodAndCount(_) => "PerryPodView<any>",
466493
Self::BufferAndLen => "Buffer",
467-
Self::I32
494+
Self::I8
495+
| Self::I16
496+
| Self::I32
468497
| Self::I64
498+
| Self::U8
499+
| Self::U16
469500
| Self::U32
470501
| Self::U64
471502
| Self::USize
503+
| Self::ISize
472504
| Self::F32
473505
| Self::F64
474506
| Self::BufferLen
@@ -576,4 +608,24 @@ mod tests {
576608
// Not a scalar POD field.
577609
assert!(!json.is_valid_pod_field());
578610
}
611+
612+
#[test]
613+
fn exact_width_scalar_spellings_are_canonical_and_pod_safe() {
614+
for (spelling, expected, canonical) in [
615+
("i8", NativeAbiType::I8, "i8"),
616+
("i16", NativeAbiType::I16, "i16"),
617+
("u8", NativeAbiType::U8, "u8"),
618+
("byte", NativeAbiType::U8, "u8"),
619+
("u16", NativeAbiType::U16, "u16"),
620+
("isize", NativeAbiType::ISize, "isize"),
621+
] {
622+
let parsed = NativeAbiType::parse_str(spelling).expect("exact-width descriptor");
623+
assert_eq!(parsed, expected);
624+
assert_eq!(parsed.canonical_kind(), canonical);
625+
assert!(parsed.is_valid_param());
626+
assert!(parsed.is_valid_return());
627+
assert!(parsed.is_valid_pod_field());
628+
assert_eq!(parsed.js_type_name(), "number");
629+
}
630+
}
579631
}

crates/perry-codegen/src/collectors/proven_this_routing_tests.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -692,11 +692,11 @@ fn guarded_pshape_call_site_is_preceded_by_a_shape_id_guard() {
692692
}
693693

694694
/// The single-pair shape-only arm is small enough to inline at the call site.
695-
/// Pin the complete safety gate: acquire the prototype-mutation latch, accept
696-
/// both the boxed-pointer and internal raw-pointer ABIs, reject addresses
697-
/// outside the target heap range before dereference, reject own descriptors,
698-
/// then compare the exact class/ShapeId pair. The out-of-line guard must be
699-
/// absent from this caller.
695+
/// Pin the complete safety gate: acquire both the all-method escape latch and
696+
/// the FNV-indexed method-name latch, accept both the boxed-pointer and
697+
/// internal raw-pointer ABIs, reject addresses outside the target heap range
698+
/// before dereference, reject own descriptors, then compare the exact
699+
/// class/ShapeId pair. The out-of-line guard must be absent from this caller.
700700
#[test]
701701
fn single_arm_method_shape_guard_is_inlined_with_the_runtime_contract() {
702702
let ir = emit(&guarded_site_module(), false);
@@ -707,6 +707,12 @@ fn single_arm_method_shape_guard_is_inlined_with_the_runtime_contract() {
707707
),
708708
"the inline guard must acquire the runtime's release-published sticky latch:\n{probe}"
709709
);
710+
assert!(
711+
probe.contains(
712+
"getelementptr i8, ptr @PERRY_CLASS_PROTOTYPE_FAST_GUARDS_INVALIDATED_BY_METHOD",
713+
) && probe.matches("load atomic i8").count() >= 2,
714+
"the inline guard must acquire its method-name invalidation byte:\n{probe}"
715+
);
710716
assert!(
711717
!probe.contains("call i32 @js_method_direct_shape_guard("),
712718
"a monomorphic shape-only site must not retain the out-of-line guard call:\n{probe}"

crates/perry-codegen/src/expr/literals_vars.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,14 @@ use crate::lower_string_concat::{
1313
lower_string_self_append_chain,
1414
};
1515
use crate::nanbox::double_literal;
16-
use crate::native_value::MaterializationReason;
1716
use crate::type_analysis::{is_map_expr, is_set_expr, receiver_class_name};
1817
use crate::types::{DOUBLE, I32, I64};
1918

2019
use super::{
2120
can_lower_expr_as_i32_in_current_region, emit_root_nanbox_store_on_block,
2221
emit_shadow_slot_clear, emit_shadow_slot_update_for_expr, emit_write_barrier,
2322
is_global_this_builtin_function_name, lower_expr, lower_expr_as_i32,
24-
lower_pod_local_reassignment, materialize_pod_local, nanbox_string_inline, FnCtx,
23+
lower_pod_local_reassignment, materialize_pod_value_copy, nanbox_string_inline, FnCtx,
2524
TrustedBoxCapturePtr,
2625
};
2726

@@ -439,7 +438,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
439438
// module-scope `let`s (the ones in `hir.init` at top level).
440439
Expr::LocalGet(id) => {
441440
if ctx.pod_records.contains_key(id) {
442-
return materialize_pod_local(ctx, *id, MaterializationReason::PodMaterialization);
441+
return materialize_pod_value_copy(ctx, *id);
443442
}
444443
// Captured by closure (from outer scope):
445444
if let Some(&capture_idx) = ctx.closure_captures.get(id) {

crates/perry-codegen/src/expr/mod.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ pub(crate) use nanbox_inline::{
9999
pub(crate) use native_record::{array_kind_fact, effect_fact, raw_f64_layout_fact};
100100
pub(crate) use object_literal::lower_object_literal;
101101
pub(crate) use pod_record::{
102-
lower_and_store_initial_pod_field, lower_pod_local_reassignment, materialize_pod_local,
103-
try_lower_pod_field_get, try_lower_pod_field_set,
102+
copy_pod_local, lower_and_store_initial_pod_field, lower_pod_local_reassignment,
103+
materialize_pod_local, materialize_pod_value_copy, try_lower_pod_field_get,
104+
try_lower_pod_field_set,
104105
};
105106
pub(crate) use proven_view_access::{
106107
index_is_exact_i32_shape, local_is_proven_int_store_view,
@@ -2873,10 +2874,16 @@ fn native_number_to_f64(ctx: &mut FnCtx<'_>, lowered: &LoweredValue) -> Option<S
28732874
NativeRep::U32 | NativeRep::BufferLen => {
28742875
Some(ctx.block().uitofp(I32, &lowered.value, DOUBLE))
28752876
}
2876-
NativeRep::I64 | NativeRep::ISize => Some(ctx.block().sitofp(I64, &lowered.value, DOUBLE)),
2877-
NativeRep::U64 | NativeRep::USize | NativeRep::HandleId => {
2878-
Some(ctx.block().uitofp(I64, &lowered.value, DOUBLE))
2879-
}
2877+
NativeRep::I64 | NativeRep::ISize => Some(ctx.block().call(
2878+
DOUBLE,
2879+
"js_native_abi_materialize_i64",
2880+
&[(I64, &lowered.value)],
2881+
)),
2882+
NativeRep::U64 | NativeRep::USize | NativeRep::HandleId => Some(ctx.block().call(
2883+
DOUBLE,
2884+
"js_native_abi_materialize_u64",
2885+
&[(I64, &lowered.value)],
2886+
)),
28802887
_ => None,
28812888
}
28822889
}

0 commit comments

Comments
 (0)