Skip to content

Commit 3b43a7c

Browse files
committed
typedarray: preserve observable toLocaleString semantics while keeping shared NumberFormat optimization
Signed-off-by: mrhapile <allinonegaming3456@gmail.com>
1 parent 5e5b7d7 commit 3b43a7c

File tree

3 files changed

+77
-17
lines changed

3 files changed

+77
-17
lines changed

core/engine/src/builtins/number/mod.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ pub(crate) struct Number;
4848

4949
impl IntrinsicObject for Number {
5050
fn init(realm: &Realm) {
51+
let to_locale_string_function = BuiltInBuilder::callable_with_object(
52+
realm,
53+
realm
54+
.intrinsics()
55+
.objects()
56+
.number_prototype_to_locale_string()
57+
.into(),
58+
Self::to_locale_string,
59+
)
60+
.name(js_string!("toLocaleString"))
61+
.length(0)
62+
.build();
63+
5164
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
5265

5366
BuiltInBuilder::from_standard_constructor::<Self>(realm)
@@ -87,7 +100,11 @@ impl IntrinsicObject for Number {
87100
.static_method(Self::number_is_integer, js_string!("isInteger"), 1)
88101
.method(Self::to_exponential, js_string!("toExponential"), 1)
89102
.method(Self::to_fixed, js_string!("toFixed"), 1)
90-
.method(Self::to_locale_string, js_string!("toLocaleString"), 0)
103+
.property(
104+
js_string!("toLocaleString"),
105+
to_locale_string_function,
106+
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
107+
)
91108
.method(Self::to_precision, js_string!("toPrecision"), 1)
92109
.method(Self::to_string, js_string!("toString"), 1)
93110
.method(Self::value_of, js_string!("valueOf"), 0)

core/engine/src/builtins/typed_array/builtin.rs

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,6 +2523,22 @@ impl BuiltinTypedArray {
25232523

25242524
let call_args = [locales, options];
25252525

2526+
#[cfg(feature = "intl")]
2527+
let is_unmodified = {
2528+
let number_proto = context.intrinsics().constructors().number().prototype();
2529+
2530+
let current_tls = number_proto.get(js_string!("toLocaleString"), context)?;
2531+
2532+
let builtin_tls = context
2533+
.intrinsics()
2534+
.objects()
2535+
.number_prototype_to_locale_string();
2536+
2537+
current_tls
2538+
.as_object()
2539+
.is_some_and(|o| JsObject::equals(&o, &builtin_tls.into()))
2540+
};
2541+
25262542
// 5. Let k be 0.
25272543
// 6. Repeat, while k < len,
25282544
for k in 0..len {
@@ -2537,21 +2553,35 @@ impl BuiltinTypedArray {
25372553
}
25382554

25392555
#[cfg(feature = "intl")]
2540-
if is_bigint {
2541-
let s = next_element
2542-
.invoke(js_string!("toLocaleString"), &call_args, context)?
2543-
.to_string(context)?;
2544-
r.push(s);
2545-
} else {
2546-
use crate::builtins::intl::number_format::to_intl_mathematical_value;
2547-
let mut x = to_intl_mathematical_value(&next_element, context)?;
2548-
r.push(js_string!(
2549-
number_format
2550-
.as_ref()
2551-
.expect("number_format should be initialized for numeric typed arrays")
2552-
.format(&mut x)
2553-
.to_string()
2554-
));
2556+
{
2557+
use crate::{
2558+
builtins::intl::number_format::to_intl_mathematical_value, value::JsVariant,
2559+
};
2560+
2561+
// Fast path: finite primitive numbers only
2562+
let use_fast_path = is_unmodified
2563+
&& match next_element.variant() {
2564+
JsVariant::Integer32(_) => !is_bigint,
2565+
JsVariant::Float64(f) => !is_bigint && f.is_finite(),
2566+
_ => false,
2567+
};
2568+
2569+
if use_fast_path {
2570+
let mut x = to_intl_mathematical_value(&next_element, context)?;
2571+
r.push(js_string!(
2572+
number_format
2573+
.as_ref()
2574+
.expect("number_format is Some for numeric typed arrays")
2575+
.format(&mut x)
2576+
.to_string()
2577+
));
2578+
} else {
2579+
// Slow path: delegate to element.toLocaleString()
2580+
let s = next_element
2581+
.invoke(js_string!("toLocaleString"), &call_args, context)?
2582+
.to_string(context)?;
2583+
r.push(s);
2584+
}
25552585
}
25562586

25572587
#[cfg(not(feature = "intl"))]
@@ -2566,7 +2596,7 @@ impl BuiltinTypedArray {
25662596
// 7. Return R.
25672597
let separator = js_string!(", ");
25682598
let mut result = Vec::with_capacity(
2569-
r.iter().map(boa_string::JsString::len).sum::<usize>()
2599+
r.iter().map(JsString::len).sum::<usize>()
25702600
+ separator.len() * (len.saturating_sub(1) as usize),
25712601
);
25722602
for (i, s) in r.into_iter().enumerate() {

core/engine/src/context/intrinsics.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,9 @@ pub struct IntrinsicObjects {
10861086
/// [`%Array.prototype.toString%`](https://tc39.es/ecma262/#sec-array.prototype.tostring)
10871087
array_prototype_to_string: JsFunction,
10881088

1089+
/// [`%Number.prototype.toLocaleString%`](https://tc39.es/ecma262/#sec-number.prototype.tolocalestring)
1090+
number_prototype_to_locale_string: JsFunction,
1091+
10891092
/// Cached iterator prototypes.
10901093
iterator_prototypes: IteratorPrototypes,
10911094

@@ -1158,6 +1161,7 @@ impl IntrinsicObjects {
11581161
throw_type_error: JsFunction::empty_intrinsic_function(false),
11591162
array_prototype_values: JsFunction::empty_intrinsic_function(false),
11601163
array_prototype_to_string: JsFunction::empty_intrinsic_function(false),
1164+
number_prototype_to_locale_string: JsFunction::empty_intrinsic_function(false),
11611165
iterator_prototypes: IteratorPrototypes::default(),
11621166
generator: JsObject::with_null_proto(),
11631167
async_generator: JsObject::with_null_proto(),
@@ -1210,6 +1214,15 @@ impl IntrinsicObjects {
12101214
self.array_prototype_to_string.clone()
12111215
}
12121216

1217+
/// Gets the [`%Number.prototype.toLocaleString%`][spec] intrinsic function.
1218+
///
1219+
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tolocalestring
1220+
#[inline]
1221+
#[must_use]
1222+
pub fn number_prototype_to_locale_string(&self) -> JsFunction {
1223+
self.number_prototype_to_locale_string.clone()
1224+
}
1225+
12131226
/// Gets the cached iterator prototypes.
12141227
#[inline]
12151228
#[must_use]

0 commit comments

Comments
 (0)