Skip to content

Commit faea384

Browse files
authored
feat(ecmascript): SharedArrayBuffer backed TypedArrays and DataViews (#869)
1 parent ab81608 commit faea384

File tree

71 files changed

+13094
-9854
lines changed

Some content is hidden

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

71 files changed

+13094
-9854
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ clap = { version = "4.5.48", features = ["derive"] }
2020
cliclack = "0.3.6"
2121
console = "0.15.11"
2222
ctrlc = "3.5.0"
23+
ecmascript_atomics = "0.1.4"
2324
fast-float = "0.2.0"
2425
hashbrown = "0.16.0"
2526
lexical = { version = "7.0.5", default-features = false, features = [
@@ -43,9 +44,9 @@ oxc_syntax = "0.94.0"
4344
rand = "0.9.2"
4445
regex = "1.11.3"
4546
ryu-js = "1.0.2"
46-
sonic-rs = "0.3.17"
4747
soavec = "0.1.1"
4848
soavec_derive = "0.1.1"
49+
sonic-rs = "0.3.17"
4950
unicode-normalization = "0.1.24"
5051
usdt = { git = "https://github.com/aapoalas/usdt.git", branch = "nova-aarch64-branch" }
5152
wtf8 = "0.1"

nova_vm/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ categories.workspace = true
1313

1414
[dependencies]
1515
ahash = { workspace = true }
16+
ecmascript_atomics = { workspace = true }
1617
fast-float = { workspace = true }
1718
hashbrown = { workspace = true }
1819
lexical = { workspace = true }
@@ -31,9 +32,9 @@ rand = { workspace = true }
3132
regex = { workspace = true }
3233
ryu-js = { workspace = true }
3334
small_string = { path = "../small_string", version = "0.2.0" }
34-
sonic-rs = { workspace = true, optional = true }
3535
soavec = { workspace = true }
3636
soavec_derive = { workspace = true }
37+
sonic-rs = { workspace = true, optional = true }
3738
unicode-normalization = { workspace = true }
3839
usdt = { workspace = true }
3940
wtf8 = { workspace = true }

nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,9 +1374,8 @@ pub(crate) fn species_constructor<'a>(
13741374
}
13751375
// 3. If C is not an Object, throw a TypeError exception.
13761376
let Ok(c) = Object::try_from(c) else {
1377-
return Err(agent.throw_exception_with_static_message(
1378-
ExceptionType::TypeError,
1379-
"constructor property value is not an object",
1377+
return Err(throw_constructor_property_not_an_object(
1378+
agent,
13801379
gc.into_nogc(),
13811380
));
13821381
};
@@ -1401,13 +1400,89 @@ pub(crate) fn species_constructor<'a>(
14011400
return Ok(s.unbind());
14021401
}
14031402
// 7. Throw a TypeError exception.
1404-
Err(agent.throw_exception_with_static_message(
1405-
ExceptionType::TypeError,
1406-
"constructor species is not a constructor",
1403+
Err(throw_species_constructor_is_not_a_constructor(
1404+
agent,
14071405
gc.into_nogc(),
14081406
))
14091407
}
14101408

1409+
#[cold]
1410+
#[inline(never)]
1411+
fn throw_constructor_property_not_an_object<'gc>(
1412+
agent: &mut Agent,
1413+
gc: NoGcScope<'gc, '_>,
1414+
) -> JsError<'gc> {
1415+
agent.throw_exception_with_static_message(
1416+
ExceptionType::TypeError,
1417+
"constructor property value is not an object",
1418+
gc.into_nogc(),
1419+
)
1420+
}
1421+
1422+
#[cold]
1423+
#[inline(never)]
1424+
fn throw_species_constructor_is_not_a_constructor<'gc>(
1425+
agent: &mut Agent,
1426+
gc: NoGcScope<'gc, '_>,
1427+
) -> JsError<'gc> {
1428+
agent.throw_exception_with_static_message(
1429+
ExceptionType::TypeError,
1430+
"species constructor is not a constructor",
1431+
gc.into_nogc(),
1432+
)
1433+
}
1434+
1435+
/// ### [7.3.22 SpeciesConstructor ( O, defaultConstructor )](https://tc39.es/ecma262/multipage/abstract-operations.html#sec-speciesconstructor)
1436+
pub(crate) fn try_species_constructor<'gc>(
1437+
agent: &mut Agent,
1438+
o: Object<'gc>,
1439+
default_constructor: ProtoIntrinsics,
1440+
gc: NoGcScope<'gc, '_>,
1441+
) -> TryResult<'gc, Option<Function<'gc>>> {
1442+
// 1. Let C be ? Get(O, "constructor").
1443+
let c = try_get_result_into_value(try_get(
1444+
agent,
1445+
o,
1446+
BUILTIN_STRING_MEMORY.constructor.into(),
1447+
None,
1448+
gc,
1449+
))?;
1450+
// 2. If C is undefined, return defaultConstructor.
1451+
if c.is_undefined() {
1452+
return TryResult::Continue(None);
1453+
}
1454+
// 3. If C is not an Object, throw a TypeError exception.
1455+
let Ok(c) = Object::try_from(c) else {
1456+
return throw_constructor_property_not_an_object(agent, gc).into();
1457+
};
1458+
// 4. Let S be ? Get(C, %Symbol.species%).
1459+
let s = try_get_result_into_value(try_get(
1460+
agent,
1461+
c,
1462+
WellKnownSymbolIndexes::Species.into(),
1463+
None,
1464+
gc,
1465+
))?;
1466+
// 5. If S is either undefined or null, return defaultConstructor.
1467+
if s.is_undefined() || s.is_null() {
1468+
return TryResult::Continue(None);
1469+
}
1470+
// 6. If IsConstructor(S) is true, return S.
1471+
if let Some(s) = is_constructor(agent, s) {
1472+
let default_constructor = agent
1473+
.current_realm_record()
1474+
.intrinsics()
1475+
.get_intrinsic_default_constructor(default_constructor);
1476+
return TryResult::Continue(if s == default_constructor {
1477+
None
1478+
} else {
1479+
Some(s)
1480+
});
1481+
}
1482+
// 7. Throw a TypeError exception.
1483+
throw_species_constructor_is_not_a_constructor(agent, gc).into()
1484+
}
1485+
14111486
pub(crate) fn is_prototype_of_loop<'a>(
14121487
agent: &mut Agent,
14131488
o: Object,

nova_vm/src/ecmascript/abstract_operations/type_conversion.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,28 @@ pub(crate) fn string_to_number<'gc>(
409409
///
410410
/// If the JavaScript number was infinite, then the appropriate i64 minimum or
411411
/// maximum value is used as a sentinel.
412-
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
412+
#[derive(Clone, Copy, PartialEq, PartialOrd)]
413413
pub(crate) struct IntegerOrInfinity(i64);
414414

415+
impl core::fmt::Display for IntegerOrInfinity {
416+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417+
if self.is_finite() {
418+
self.0.fmt(f)
419+
} else if self.is_neg_infinity() {
420+
f.write_str("-Infinity")
421+
} else {
422+
f.write_str("Infinity")
423+
}
424+
}
425+
}
426+
427+
impl core::fmt::Debug for IntegerOrInfinity {
428+
#[inline(always)]
429+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
430+
<Self as core::fmt::Display>::fmt(self, f)
431+
}
432+
}
433+
415434
impl IntegerOrInfinity {
416435
pub(crate) const NEG_INFINITY: Self = Self(i64::MIN);
417436
pub(crate) const POS_INFINITY: Self = Self(i64::MAX);
@@ -596,7 +615,7 @@ pub(crate) fn to_int32<'a>(
596615
/// ### [7.1.6 ToInt32 ( argument )](https://tc39.es/ecma262/#sec-toint32)
597616
///
598617
/// Implements steps 2 to 5 of the abstract operation, callable only with Numbers.
599-
pub(crate) fn to_int32_number(agent: &mut Agent, number: Number) -> i32 {
618+
pub(crate) fn to_int32_number(agent: &Agent, number: Number) -> i32 {
600619
if let Number::Integer(int) = number {
601620
let int = int.into_i64();
602621
return int as i32;
@@ -635,7 +654,7 @@ pub(crate) fn to_uint32<'a>(
635654
/// ### [7.1.7 ToUint32 ( argument )](https://tc39.es/ecma262/#sec-touint32)
636655
///
637656
/// Implements steps 2 to 5 of the abstract operation, callable only with Numbers.
638-
pub(crate) fn to_uint32_number(agent: &mut Agent, number: Number) -> u32 {
657+
pub(crate) fn to_uint32_number(agent: &Agent, number: Number) -> u32 {
639658
if let Number::Integer(int) = number {
640659
let int = int.into_i64();
641660
return int as u32;
@@ -670,7 +689,7 @@ pub(crate) fn to_int16<'a>(
670689
Ok(to_int16_number(agent, number))
671690
}
672691

673-
pub(crate) fn to_int16_number(agent: &mut Agent, number: Number) -> i16 {
692+
pub(crate) fn to_int16_number(agent: &Agent, number: Number) -> i16 {
674693
if let Number::Integer(int) = number {
675694
// Fast path: Integer value is very nearly int16 already.
676695
let int = int.into_i64();
@@ -706,7 +725,7 @@ pub(crate) fn to_uint16<'a>(
706725
Ok(to_uint16_number(agent, number))
707726
}
708727

709-
pub(crate) fn to_uint16_number(agent: &mut Agent, number: Number) -> u16 {
728+
pub(crate) fn to_uint16_number(agent: &Agent, number: Number) -> u16 {
710729
if let Number::Integer(int) = number {
711730
// Fast path: Integer value is very nearly uin16 already.
712731
let int = int.into_i64();
@@ -742,7 +761,7 @@ pub(crate) fn to_int8<'a>(
742761
Ok(to_int8_number(agent, number))
743762
}
744763

745-
pub(crate) fn to_int8_number(agent: &mut Agent, number: Number) -> i8 {
764+
pub(crate) fn to_int8_number(agent: &Agent, number: Number) -> i8 {
746765
if let Number::Integer(int) = number {
747766
// Fast path: Integer value is very nearly uint32 already.
748767
let int = int.into_i64();
@@ -778,7 +797,7 @@ pub(crate) fn to_uint8<'a>(
778797
Ok(to_uint8_number(agent, number))
779798
}
780799

781-
pub(crate) fn to_uint8_number(agent: &mut Agent, number: Number) -> u8 {
800+
pub(crate) fn to_uint8_number(agent: &Agent, number: Number) -> u8 {
782801
if let Number::Integer(int) = number {
783802
// Fast path: Integer value is very nearly uint32 already.
784803
let int = int.into_i64();
@@ -814,7 +833,7 @@ pub(crate) fn to_uint8_clamp<'a>(
814833
Ok(to_uint8_clamp_number(agent, number))
815834
}
816835

817-
pub(crate) fn to_uint8_clamp_number(agent: &mut Agent, number: Number) -> u8 {
836+
pub(crate) fn to_uint8_clamp_number(agent: &Agent, number: Number) -> u8 {
818837
if let Number::Integer(int) = number {
819838
// Fast path: Integer value is very nearly uint8 already.
820839
return int.into_i64().clamp(0, 255) as u8;
@@ -969,7 +988,7 @@ pub(crate) fn to_big_int64<'a>(
969988
Ok(to_big_int64_big_int(agent, n))
970989
}
971990

972-
pub(crate) fn to_big_int64_big_int(agent: &mut Agent, n: BigInt) -> i64 {
991+
pub(crate) fn to_big_int64_big_int(agent: &Agent, n: BigInt) -> i64 {
973992
// 2. Let int64bit be ℝ(n) modulo 2**64.
974993
match n {
975994
BigInt::BigInt(heap_big_int) => {
@@ -1003,7 +1022,7 @@ pub(crate) fn to_big_uint64<'a>(
10031022
Ok(to_big_uint64_big_int(agent, n))
10041023
}
10051024

1006-
pub(crate) fn to_big_uint64_big_int(agent: &mut Agent, n: BigInt) -> u64 {
1025+
pub(crate) fn to_big_uint64_big_int(agent: &Agent, n: BigInt) -> u64 {
10071026
// 2. Let int64bit be ℝ(n) modulo 2**64.
10081027
match n {
10091028
BigInt::BigInt(heap_big_int) => {

nova_vm/src/ecmascript/builtins/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ use super::ordinary::{
5454
ordinary_has_property, ordinary_try_get, ordinary_try_has_property,
5555
};
5656

57-
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
57+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5858
#[repr(transparent)]
5959
pub struct Array<'a>(BaseIndex<'a, ArrayHeapData<'static>>);
6060

0 commit comments

Comments
 (0)