Skip to content
Closed
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
18 changes: 18 additions & 0 deletions changelog.d/10617-field-init-collection-receiver-guard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### Fixed

A class whose direct parent is a built-in (`Error`/`TypeError`/other non-user base) and that
declares its own `super()`-calling constructor now runs its own field initializers. Every other
non-user-parent `super()` arm already did this after the base constructor returns; the
`Error`-family arm was the one that skipped it, so `class E extends Error { labels =
new Set(); constructor(m) { super(m); } }` left `labels` `undefined`.

Calling a method (`.add`/`.set`/`.get`/`.has`/`.delete`/`.clear`/`.forEach`/...) on a
statically-typed `Set<T>`/`Map<K, V>` value that holds `undefined`, `null`, or another
primitive at runtime now throws a catchable `TypeError` instead of segfaulting. The static-type
fast path unboxed the receiver's declared-type payload with no tag check; a receiver guard now
runs first, on the fast/common path costing one compare of the receiver's tag bits.

Together these fixed a crash in the mongodb 7.5.0 driver: `MongoError`'s
`errorLabelSet: Set<string>` field was left `undefined` by the first bug, and
`addErrorLabel()` calling `.add()` on it segfaulted via the second, about 100ms after
`client.connect()`.
2 changes: 1 addition & 1 deletion crates/perry-codegen/src/expr/arrays_finds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,8 @@ pub(crate) fn lower(
// -------- Map.clear --------
Expr::MapClear(map) => {
let m_box = lower_expr(ctx, map)?;
let m_handle = super::unbox_collection_receiver(ctx, &m_box, "clear");
let blk = ctx.block();
let m_handle = unbox_to_i64(blk, &m_box);
blk.call_void("js_map_clear", &[(I64, &m_handle)]);
// Map.prototype.clear() returns undefined, not 0.
Ok(double_literal(f64::from_bits(crate::nanbox::TAG_UNDEFINED)))
Expand Down
81 changes: 34 additions & 47 deletions crates/perry-codegen/src/expr/bigint_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use super::{
nanbox_pointer_inline, record_collection_number_key_fallback,
record_collection_number_key_selected, record_collection_string_key_fallback,
record_collection_string_key_selected, record_collection_typed_value_fallback,
record_collection_typed_value_selected, unbox_to_i64, FnCtx,
record_collection_typed_value_selected, unbox_collection_receiver, unbox_to_i64, FnCtx,
};

fn number_coerce_operand_is_already_primitive_number(ctx: &FnCtx<'_>, operand: &Expr) -> bool {
Expand Down Expand Up @@ -153,13 +153,16 @@ fn guarded_set_number_add(ctx: &mut FnCtx<'_>, set_handle: &str, value_box: &str
/// is exactly what it was before this change. On the protected path the
/// handle has to come from the *re-read* box, below the value's lowering, so
/// it is derived in [`reread_set_receiver`] instead.
fn eager_set_handle(ctx: &mut FnCtx<'_>, group: &RootedGroup<'_>) -> Result<Option<String>> {
fn eager_set_handle(
ctx: &mut FnCtx<'_>,
group: &RootedGroup<'_>,
method: &str,
) -> Result<Option<String>> {
if group.is_rooted() {
return Ok(None);
}
let s_box = group.reread(ctx, 0)?;
let blk = ctx.block();
Ok(Some(unbox_to_i64(blk, &s_box)))
Ok(Some(unbox_collection_receiver(ctx, &s_box, method)))
}

/// Re-derive the `Set` receiver handle AFTER `value` has been lowered (#9523).
Expand All @@ -174,13 +177,13 @@ fn reread_set_receiver(
ctx: &mut FnCtx<'_>,
group: &RootedGroup<'_>,
s_handle_unrooted: &Option<String>,
method: &str,
) -> Result<String> {
if let Some(handle) = s_handle_unrooted {
return Ok(handle.clone());
}
let s_box = group.reread(ctx, 0)?;
let blk = ctx.block();
Ok(unbox_to_i64(blk, &s_box))
Ok(unbox_collection_receiver(ctx, &s_box, method))
}

fn guarded_set_number_has(ctx: &mut FnCtx<'_>, set_handle: &str, value_box: &str) -> String {
Expand Down Expand Up @@ -648,10 +651,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_i32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I32)?;
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
let new_handle = {
let blk = ctx.block();
blk.call(
Expand All @@ -675,10 +675,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_u32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::U32)?;
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
let new_handle = {
let blk = ctx.block();
blk.call(
Expand All @@ -702,10 +699,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_f32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::F32)?;
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
let new_handle = {
let blk = ctx.block();
blk.call(
Expand All @@ -729,10 +723,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_i1 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I1)?;
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
let new_handle = {
let blk = ctx.block();
let value_i32 = blk.zext(I1, &value_i1.value, I32);
Expand All @@ -756,17 +747,11 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_number_set {
let v = lower_expr(ctx, value)?;
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
guarded_set_number_add(ctx, &set_handle, &v)
} else {
let set_box = lower_expr(ctx, &set_expr)?;
let set_handle = {
let blk = ctx.block();
unbox_to_i64(blk, &set_box)
};
let set_handle = unbox_collection_receiver(ctx, &set_box, "add");
if use_string_set {
let value_ref = lower_expr_native(
ctx,
Expand Down Expand Up @@ -931,11 +916,11 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_collects = operand_may_collect(ctx, value);
let i32_v = with_rooted_group(ctx, 1, |ctx, group| {
group.lower(ctx, set, value_collects)?;
let s_handle_unrooted = eager_set_handle(ctx, group)?;
let s_handle_unrooted = eager_set_handle(ctx, group, "has")?;
let i32_v = if use_i32_set {
let value_i32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -958,7 +943,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_u32_set {
let value_u32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::U32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -981,7 +966,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_f32_set {
let value_f32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::F32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -1004,7 +989,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_boolean_set {
let value_i1 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I1)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
let value_i32 = blk.zext(I1, &value_i1.value, I32);
Expand All @@ -1027,7 +1012,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
i32_v
} else if use_number_set {
let v_box = lower_expr(ctx, value)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
guarded_set_number_has(ctx, &s_handle, &v_box)
} else {
if use_string_set {
Expand All @@ -1036,7 +1021,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
value,
crate::native_value::ExpectedNativeRep::StringRef,
)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
let i32_v = blk.call(
Expand Down Expand Up @@ -1067,7 +1052,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
i32_v
} else {
let v_box = lower_expr(ctx, value)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "has")?;
let i32_v = {
let blk = ctx.block();
blk.call(I32, "js_set_has", &[(I64, &s_handle), (DOUBLE, &v_box)])
Expand Down Expand Up @@ -1187,11 +1172,11 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
let value_collects = operand_may_collect(ctx, value);
let i32_v = with_rooted_group(ctx, 1, |ctx, group| {
group.lower(ctx, set, value_collects)?;
let s_handle_unrooted = eager_set_handle(ctx, group)?;
let s_handle_unrooted = eager_set_handle(ctx, group, "delete")?;
let i32_v = if use_i32_set {
let value_i32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -1214,7 +1199,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_u32_set {
let value_u32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::U32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -1237,7 +1222,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_f32_set {
let value_f32 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::F32)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
blk.call(
Expand All @@ -1260,7 +1245,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
} else if use_boolean_set {
let value_i1 =
lower_expr_native(ctx, value, crate::native_value::ExpectedNativeRep::I1)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
let value_i32 = blk.zext(I1, &value_i1.value, I32);
Expand All @@ -1283,7 +1268,7 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
i32_v
} else if use_number_set {
let v_box = lower_expr(ctx, value)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
guarded_set_number_delete(ctx, &s_handle, &v_box)
} else {
if use_string_set {
Expand All @@ -1292,7 +1277,8 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
value,
crate::native_value::ExpectedNativeRep::StringRef,
)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle =
reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
let i32_v = blk.call(
Expand Down Expand Up @@ -1323,7 +1309,8 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
i32_v
} else {
let v_box = lower_expr(ctx, value)?;
let s_handle = reread_set_receiver(ctx, group, &s_handle_unrooted)?;
let s_handle =
reread_set_receiver(ctx, group, &s_handle_unrooted, "delete")?;
let i32_v = {
let blk = ctx.block();
blk.call(I32, "js_set_delete", &[(I64, &s_handle), (DOUBLE, &v_box)])
Expand Down Expand Up @@ -1415,8 +1402,8 @@ pub(crate) fn lower(ctx: &mut FnCtx<'_>, expr: &Expr) -> Result<String> {
// -------- set.size -> number --------
Expr::SetSize(set) => {
let s_box = lower_expr(ctx, set)?;
let s_handle = unbox_collection_receiver(ctx, &s_box, "size");
let blk = ctx.block();
let s_handle = unbox_to_i64(blk, &s_box);
let i32_v = blk.call(I32, "js_set_size", &[(I64, &s_handle)]);
Ok(blk.sitofp(I32, &i32_v, DOUBLE))
}
Expand Down
Loading
Loading