Skip to content

Commit 6cfa1c5

Browse files
authored
feat(ecmascript): Atomics.load (#882)
1 parent 26d4580 commit 6cfa1c5

File tree

3 files changed

+146
-26
lines changed

3 files changed

+146
-26
lines changed

nova_vm/src/ecmascript/builtins/structured_data/atomics_object.rs

Lines changed: 142 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
use ecmascript_atomics::Ordering;
6+
57
use crate::{
68
ecmascript::{
79
abstract_operations::type_conversion::{
@@ -10,7 +12,7 @@ use crate::{
1012
builders::ordinary_object_builder::OrdinaryObjectBuilder,
1113
builtins::{
1214
ArgumentsList, Behaviour, Builtin,
13-
array_buffer::get_modify_set_value_in_buffer,
15+
array_buffer::{get_modify_set_value_in_buffer, get_value_from_buffer},
1416
indexed_collections::typed_array_objects::abstract_operations::{
1517
TypedArrayAbstractOperations, TypedArrayWithBufferWitnessRecords,
1618
make_typed_array_with_buffer_witness_record, validate_typed_array,
@@ -219,13 +221,92 @@ impl AtomicsObject {
219221
Err(agent.todo("Atomics.isLockFree", gc.into_nogc()))
220222
}
221223

224+
/// ### [25.4.9 Atomics.load ( typedArray, index )](https://tc39.es/ecma262/#sec-atomics.load)
222225
fn load<'gc>(
223226
agent: &mut Agent,
224227
_this_value: Value,
225-
_arguments: ArgumentsList,
226-
gc: GcScope<'gc, '_>,
228+
arguments: ArgumentsList,
229+
mut gc: GcScope<'gc, '_>,
227230
) -> JsResult<'gc, Value<'gc>> {
228-
Err(agent.todo("Atomics.load", gc.into_nogc()))
231+
let arguments = arguments.bind(gc.nogc());
232+
let typed_array = arguments.get(0);
233+
let index = arguments.get(1);
234+
// 1. Let byteIndexInBuffer be ? ValidateAtomicAccessOnIntegerTypedArray(typedArray, index).
235+
let ta_record = validate_typed_array(
236+
agent,
237+
typed_array,
238+
ecmascript_atomics::Ordering::Unordered,
239+
gc.nogc(),
240+
)
241+
.unbind()?
242+
.bind(gc.nogc());
243+
// a. Let type be TypedArrayElementType(typedArray).
244+
// b. If IsUnclampedIntegerElementType(type) is false and
245+
// IsBigIntElementType(type) is false, throw a TypeError exception.
246+
if !ta_record.object.is_integer() {
247+
return Err(agent.throw_exception_with_static_message(
248+
ExceptionType::TypeError,
249+
"cannot use TypedArray in Atomics",
250+
gc.into_nogc(),
251+
));
252+
}
253+
// 1. Let length be TypedArrayLength(taRecord).
254+
let length = ta_record.typed_array_length(agent);
255+
let (byte_index_in_buffer, typed_array) = if let Value::Integer(index) = index {
256+
// 7. Let offset be typedArray.[[ByteOffset]].
257+
let typed_array = ta_record.object.bind(gc.nogc());
258+
// 2. Let accessIndex be ? ToIndex(requestIndex).
259+
let access_index = validate_index(agent, index.into_i64(), gc.nogc()).unbind()?;
260+
// 3. If accessIndex ≥ length, throw a RangeError exception.
261+
if access_index >= length as u64 {
262+
return Err(agent.throw_exception_with_static_message(
263+
ExceptionType::RangeError,
264+
"accessIndex out of bounds",
265+
gc.into_nogc(),
266+
));
267+
}
268+
let access_index = access_index as usize;
269+
// 5. Let typedArray be taRecord.[[Object]].
270+
let offset = typed_array.byte_offset(agent);
271+
let byte_index_in_buffer =
272+
offset + access_index * typed_array.typed_array_element_size();
273+
(byte_index_in_buffer, typed_array)
274+
} else {
275+
// 2. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
276+
atomic_load_slow(
277+
agent,
278+
ta_record.unbind(),
279+
index.unbind(),
280+
length,
281+
gc.reborrow(),
282+
)
283+
.unbind()?
284+
.bind(gc.nogc())
285+
};
286+
let typed_array = typed_array.unbind();
287+
let gc = gc.into_nogc();
288+
let typed_array = typed_array.bind(gc);
289+
// 3. Let buffer be typedArray.[[ViewedArrayBuffer]].
290+
let buffer = typed_array.viewed_array_buffer(agent);
291+
// 4. Let elementType be TypedArrayElementType(typedArray).
292+
// 5. Return GetValueFromBuffer(buffer, byteIndexInBuffer, elementType, true, seq-cst).
293+
Ok(for_any_typed_array!(
294+
typed_array,
295+
_t,
296+
{
297+
get_value_from_buffer::<ElementType>(
298+
agent,
299+
buffer,
300+
byte_index_in_buffer,
301+
true,
302+
Ordering::SeqCst,
303+
None,
304+
gc,
305+
)
306+
},
307+
ElementType
308+
)
309+
.into_value())
229310
}
230311

231312
fn or<'gc>(
@@ -671,3 +752,60 @@ fn atomic_read_modify_write_slow<'gc>(
671752
}
672753
Ok((byte_index_in_buffer, typed_array, v))
673754
}
755+
756+
#[inline(never)]
757+
#[cold]
758+
fn atomic_load_slow<'gc>(
759+
agent: &mut Agent,
760+
ta_record: TypedArrayWithBufferWitnessRecords,
761+
index: Value,
762+
length: usize,
763+
mut gc: GcScope<'gc, '_>,
764+
) -> JsResult<'gc, (usize, AnyTypedArray<'gc>)> {
765+
let mut ta_record = ta_record.bind(gc.nogc());
766+
let index = index.bind(gc.nogc());
767+
let mut revalidate = false;
768+
769+
// 2. Let accessIndex be ? ToIndex(requestIndex).
770+
let access_index =
771+
if let Some(index) = try_result_into_js(try_to_index(agent, index, gc.nogc())).unbind()? {
772+
index
773+
} else {
774+
let ta = ta_record.object.scope(agent, gc.nogc());
775+
let cached_buffer_byte_length = ta_record.cached_buffer_byte_length;
776+
let access_index = to_index(agent, index.unbind(), gc.reborrow()).unbind()?;
777+
revalidate = true;
778+
// SAFETY: not shared.
779+
ta_record = unsafe {
780+
TypedArrayWithBufferWitnessRecords {
781+
object: ta.take(agent),
782+
cached_buffer_byte_length,
783+
}
784+
};
785+
access_index
786+
};
787+
// 3. Assert: accessIndex ≥ 0.
788+
// 4. If accessIndex ≥ length, throw a RangeError exception.
789+
if access_index >= length as u64 {
790+
return Err(agent.throw_exception_with_static_message(
791+
ExceptionType::RangeError,
792+
"accessIndex out of bounds",
793+
gc.into_nogc(),
794+
));
795+
}
796+
let access_index = access_index as usize;
797+
// 5. Let typedArray be taRecord.[[Object]].
798+
// 6. Let elementSize be TypedArrayElementSize(typedArray).
799+
// 7. Let offset be typedArray.[[ByteOffset]].
800+
let offset = ta_record.object.byte_offset(agent);
801+
// 8. Return (accessIndex × elementSize) + offset.
802+
let byte_index_in_buffer = offset + access_index * ta_record.object.typed_array_element_size();
803+
let typed_array = ta_record.object.unbind();
804+
let gc = gc.into_nogc();
805+
let typed_array = typed_array.bind(gc);
806+
if revalidate {
807+
// 2. Perform ? RevalidateAtomicAccess(typedArray, byteIndexInBuffer).
808+
revalidate_atomic_access(agent, typed_array, byte_index_in_buffer, gc)?;
809+
}
810+
Ok((byte_index_in_buffer, typed_array))
811+
}

tests/expectations.json

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -239,13 +239,9 @@
239239
"built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/throw-return-getter.js": "FAIL",
240240
"built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/throw-return.js": "FAIL",
241241
"built-ins/Atomics/add/bigint/good-views.js": "FAIL",
242-
"built-ins/Atomics/add/bigint/non-shared-bufferdata.js": "FAIL",
243242
"built-ins/Atomics/add/good-views.js": "FAIL",
244-
"built-ins/Atomics/add/non-shared-bufferdata.js": "FAIL",
245243
"built-ins/Atomics/and/bigint/good-views.js": "FAIL",
246-
"built-ins/Atomics/and/bigint/non-shared-bufferdata.js": "FAIL",
247244
"built-ins/Atomics/and/good-views.js": "FAIL",
248-
"built-ins/Atomics/and/non-shared-bufferdata.js": "FAIL",
249245
"built-ins/Atomics/compareExchange/bad-range.js": "FAIL",
250246
"built-ins/Atomics/compareExchange/bigint/bad-range.js": "FAIL",
251247
"built-ins/Atomics/compareExchange/bigint/good-views.js": "FAIL",
@@ -259,22 +255,12 @@
259255
"built-ins/Atomics/compareExchange/validate-arraytype-before-index-coercion.js": "FAIL",
260256
"built-ins/Atomics/compareExchange/validate-arraytype-before-replacementValue-coercion.js": "FAIL",
261257
"built-ins/Atomics/exchange/bigint/good-views.js": "FAIL",
262-
"built-ins/Atomics/exchange/bigint/non-shared-bufferdata.js": "FAIL",
263258
"built-ins/Atomics/exchange/good-views.js": "FAIL",
264-
"built-ins/Atomics/exchange/non-shared-bufferdata.js": "FAIL",
265259
"built-ins/Atomics/isLockFree/bigint/expected-return-value.js": "FAIL",
266260
"built-ins/Atomics/isLockFree/corner-cases.js": "FAIL",
267261
"built-ins/Atomics/isLockFree/expected-return-value.js": "FAIL",
268-
"built-ins/Atomics/load/bad-range.js": "FAIL",
269-
"built-ins/Atomics/load/bigint/bad-range.js": "FAIL",
270262
"built-ins/Atomics/load/bigint/good-views.js": "FAIL",
271-
"built-ins/Atomics/load/bigint/non-shared-bufferdata.js": "FAIL",
272-
"built-ins/Atomics/load/expected-return-value.js": "FAIL",
273263
"built-ins/Atomics/load/good-views.js": "FAIL",
274-
"built-ins/Atomics/load/non-shared-bufferdata.js": "FAIL",
275-
"built-ins/Atomics/load/non-shared-int-views-throws.js": "FAIL",
276-
"built-ins/Atomics/load/non-views.js": "FAIL",
277-
"built-ins/Atomics/load/validate-arraytype-before-index-coercion.js": "FAIL",
278264
"built-ins/Atomics/notify/bad-range.js": "FAIL",
279265
"built-ins/Atomics/notify/bigint/bad-range.js": "FAIL",
280266
"built-ins/Atomics/notify/bigint/non-bigint64-typedarray-throws.js": "FAIL",
@@ -323,9 +309,7 @@
323309
"built-ins/Atomics/notify/validate-arraytype-before-count-coercion.js": "FAIL",
324310
"built-ins/Atomics/notify/validate-arraytype-before-index-coercion.js": "FAIL",
325311
"built-ins/Atomics/or/bigint/good-views.js": "FAIL",
326-
"built-ins/Atomics/or/bigint/non-shared-bufferdata.js": "FAIL",
327312
"built-ins/Atomics/or/good-views.js": "FAIL",
328-
"built-ins/Atomics/or/non-shared-bufferdata.js": "FAIL",
329313
"built-ins/Atomics/pause/descriptor.js": "FAIL",
330314
"built-ins/Atomics/pause/length.js": "FAIL",
331315
"built-ins/Atomics/pause/name.js": "FAIL",
@@ -519,9 +503,7 @@
519503
"built-ins/Atomics/waitAsync/waiterlist-block-indexedposition-wake.js": "FAIL",
520504
"built-ins/Atomics/waitAsync/was-woken-before-timeout.js": "FAIL",
521505
"built-ins/Atomics/xor/bigint/good-views.js": "FAIL",
522-
"built-ins/Atomics/xor/bigint/non-shared-bufferdata.js": "FAIL",
523506
"built-ins/Atomics/xor/good-views.js": "FAIL",
524-
"built-ins/Atomics/xor/non-shared-bufferdata.js": "FAIL",
525507
"built-ins/BigInt/asUintN/arithmetic.js": "CRASH",
526508
"built-ins/Boolean/proto-from-ctor-realm.js": "FAIL",
527509
"built-ins/DataView/proto-from-ctor-realm-sab.js": "FAIL",

tests/metrics.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"results": {
33
"crash": 107,
4-
"fail": 8003,
5-
"pass": 39250,
4+
"fail": 7986,
5+
"pass": 39267,
66
"skip": 3325,
7-
"timeout": 14,
7+
"timeout": 13,
88
"unresolved": 34
99
},
1010
"total": 50733
11-
}
11+
}

0 commit comments

Comments
 (0)