Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 5ab9513

Browse files
committed
Bug 1966355 - Part 17: Handle immutable typed arrays in Atomics. r=sfink,jandem
Disallow immutable typed arrays for all `Atomics` methods except `Atomics.load`. Differential Revision: https://phabricator.services.mozilla.com/D249410
1 parent 314e244 commit 5ab9513

File tree

3 files changed

+83
-22
lines changed

3 files changed

+83
-22
lines changed

js/src/builtin/AtomicsObject.cpp

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ static bool ReportDetachedArrayBuffer(JSContext* cx) {
5151
return false;
5252
}
5353

54+
static bool ReportImmutableBuffer(JSContext* cx) {
55+
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
56+
JSMSG_ARRAYBUFFER_IMMUTABLE);
57+
return false;
58+
}
59+
5460
static bool ReportResizedArrayBuffer(JSContext* cx) {
5561
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5662
JSMSG_TYPED_ARRAY_RESIZED_BOUNDS);
@@ -64,15 +70,15 @@ static bool ReportOutOfRange(JSContext* cx) {
6470
return false;
6571
}
6672

67-
// ES2021 draft rev bd868f20b8c574ad6689fba014b62a1dba819e56
68-
// Plus: https://github.com/tc39/ecma262/pull/1908
69-
// 24.4.1.1 ValidateIntegerTypedArray ( typedArray [ , waitable ] )
73+
enum class AccessMode { Read, Write };
74+
75+
// ES2026 draft rev affcec07523a45d40fb668689c07657412e772ac
76+
// Plus: https://tc39.es/proposal-immutable-arraybuffer/
77+
// 25.4.3.1 ValidateIntegerTypedArray ( typedArray, waitable )
7078
static bool ValidateIntegerTypedArray(
71-
JSContext* cx, HandleValue typedArray, bool waitable,
79+
JSContext* cx, HandleValue typedArray, bool waitable, AccessMode accessMode,
7280
MutableHandle<TypedArrayObject*> unwrappedTypedArray) {
73-
// Step 1 (implicit).
74-
75-
// Step 2.
81+
// Steps 1-2.
7682
auto* unwrapped = UnwrapAndTypeCheckValue<TypedArrayObject>(
7783
cx, typedArray, [cx]() { ReportBadArrayType(cx); });
7884
if (!unwrapped) {
@@ -83,7 +89,12 @@ static bool ValidateIntegerTypedArray(
8389
return ReportDetachedArrayBuffer(cx);
8490
}
8591

86-
// Steps 3-6.
92+
if (accessMode == AccessMode::Write &&
93+
unwrapped->is<ImmutableTypedArrayObject>()) {
94+
return ReportImmutableBuffer(cx);
95+
}
96+
97+
// Steps 3-4.
8798
if (waitable) {
8899
switch (unwrapped->type()) {
89100
case Scalar::Int32:
@@ -108,7 +119,7 @@ static bool ValidateIntegerTypedArray(
108119
}
109120
}
110121

111-
// Steps 7-9 (modified to return the TypedArray).
122+
// Step 5 (modified to return the TypedArray).
112123
unwrappedTypedArray.set(unwrapped);
113124
return true;
114125
}
@@ -253,10 +264,12 @@ struct ArrayOps<uint64_t> {
253264
// 24.4.4 Atomics.compareExchange ( typedArray, index, ... ), steps 1-2.
254265
// 24.4.9 Atomics.store ( typedArray, index, value ), steps 1-2.
255266
template <typename Op>
256-
bool AtomicAccess(JSContext* cx, HandleValue obj, HandleValue index, Op op) {
267+
static bool AtomicAccess(JSContext* cx, HandleValue obj, HandleValue index,
268+
AccessMode accessMode, Op op) {
257269
// Step 1.
258270
Rooted<TypedArrayObject*> unwrappedTypedArray(cx);
259-
if (!ValidateIntegerTypedArray(cx, obj, false, &unwrappedTypedArray)) {
271+
if (!ValidateIntegerTypedArray(cx, obj, false, accessMode,
272+
&unwrappedTypedArray)) {
260273
return false;
261274
}
262275

@@ -326,7 +339,7 @@ static bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp) {
326339
HandleValue index = args.get(1);
327340

328341
return AtomicAccess(
329-
cx, typedArray, index,
342+
cx, typedArray, index, AccessMode::Write,
330343
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
331344
size_t index) {
332345
using T = typename decltype(ops)::Type;
@@ -363,7 +376,7 @@ static bool atomics_load(JSContext* cx, unsigned argc, Value* vp) {
363376
HandleValue index = args.get(1);
364377

365378
return AtomicAccess(
366-
cx, typedArray, index,
379+
cx, typedArray, index, AccessMode::Read,
367380
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
368381
size_t index) {
369382
using T = typename decltype(ops)::Type;
@@ -388,7 +401,7 @@ static bool atomics_store(JSContext* cx, unsigned argc, Value* vp) {
388401
HandleValue index = args.get(1);
389402

390403
return AtomicAccess(
391-
cx, typedArray, index,
404+
cx, typedArray, index, AccessMode::Write,
392405
[cx, &args](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
393406
size_t index) {
394407
using T = typename decltype(ops)::Type;
@@ -418,7 +431,7 @@ static bool AtomicReadModifyWrite(JSContext* cx, const CallArgs& args,
418431
HandleValue index = args.get(1);
419432

420433
return AtomicAccess(
421-
cx, typedArray, index,
434+
cx, typedArray, index, AccessMode::Write,
422435
[cx, &args, op](auto ops, Handle<TypedArrayObject*> unwrappedTypedArray,
423436
size_t index) {
424437
using T = typename decltype(ops)::Type;
@@ -1303,7 +1316,8 @@ static bool DoWait(JSContext* cx, bool isAsync, HandleValue objv,
13031316
MutableHandleValue r) {
13041317
// Steps 1-2.
13051318
Rooted<TypedArrayObject*> unwrappedTypedArray(cx);
1306-
if (!ValidateIntegerTypedArray(cx, objv, true, &unwrappedTypedArray)) {
1319+
if (!ValidateIntegerTypedArray(cx, objv, true, AccessMode::Read,
1320+
&unwrappedTypedArray)) {
13071321
return false;
13081322
}
13091323
MOZ_ASSERT(unwrappedTypedArray->type() == Scalar::Int32 ||
@@ -1472,7 +1486,8 @@ static bool atomics_notify(JSContext* cx, unsigned argc, Value* vp) {
14721486

14731487
// Step 1.
14741488
Rooted<TypedArrayObject*> unwrappedTypedArray(cx);
1475-
if (!ValidateIntegerTypedArray(cx, objv, true, &unwrappedTypedArray)) {
1489+
if (!ValidateIntegerTypedArray(cx, objv, true, AccessMode::Read,
1490+
&unwrappedTypedArray)) {
14761491
return false;
14771492
}
14781493
MOZ_ASSERT(unwrappedTypedArray->type() == Scalar::Int32 ||
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// |jit-test| --enable-arraybuffer-immutable; skip-if: !ArrayBuffer.prototype.transferToImmutable
2+
3+
const IntTypedArrays = [
4+
Int8Array,
5+
Uint8Array,
6+
Int16Array,
7+
Uint16Array,
8+
Int32Array,
9+
Uint32Array,
10+
BigInt64Array,
11+
BigUint64Array,
12+
];
13+
14+
function test(TA) {
15+
const length = 4;
16+
17+
let expected = new TA(length);
18+
let type = expected[0].constructor;
19+
20+
for (let i = 0; i < length; ++i) {
21+
expected[i] = type(i + 1);
22+
}
23+
24+
let actual = new TA(expected.buffer.sliceToImmutable());
25+
assertEq(actual.buffer.immutable, true);
26+
27+
for (let i = 0; i < 200; ++i) {
28+
let index = i % length;
29+
assertEq(Atomics.load(actual, index), expected[index]);
30+
}
31+
}
32+
33+
for (let TA of IntTypedArrays) {
34+
// Copy test function to ensure monomorphic ICs.
35+
let copy = Function(`return ${test}`)();
36+
37+
for (let i = 0; i < 2; ++i) copy(TA);
38+
}

js/src/jit/CacheIR.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9545,8 +9545,16 @@ AttachDecision InlinableNativeIRGenerator::tryAttachReflectGetPrototypeOf() {
95459545
return AttachDecision::Attach;
95469546
}
95479547

9548+
enum class AtomicAccess { Read, Write };
9549+
95489550
static bool AtomicsMeetsPreconditions(TypedArrayObject* typedArray,
9549-
const Value& index) {
9551+
const Value& index, AtomicAccess access) {
9552+
// Can't write into immutable TypedArrays.
9553+
if (access == AtomicAccess::Write &&
9554+
typedArray->is<ImmutableTypedArrayObject>()) {
9555+
return false;
9556+
}
9557+
95509558
switch (typedArray->type()) {
95519559
case Scalar::Int8:
95529560
case Scalar::Uint8:
@@ -9603,7 +9611,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsCompareExchange() {
96039611
}
96049612

96059613
auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
9606-
if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
9614+
if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
96079615
return AttachDecision::NoAction;
96089616
}
96099617

@@ -9669,7 +9677,7 @@ bool InlinableNativeIRGenerator::canAttachAtomicsReadWriteModify() {
96699677
}
96709678

96719679
auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
9672-
if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
9680+
if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
96739681
return false;
96749682
}
96759683
if (!ValueCanConvertToNumeric(typedArray->type(), args_[2])) {
@@ -9845,7 +9853,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsLoad() {
98459853
}
98469854

98479855
auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
9848-
if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
9856+
if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Read)) {
98499857
return AttachDecision::NoAction;
98509858
}
98519859

@@ -9900,7 +9908,7 @@ AttachDecision InlinableNativeIRGenerator::tryAttachAtomicsStore() {
99009908
}
99019909

99029910
auto* typedArray = &args_[0].toObject().as<TypedArrayObject>();
9903-
if (!AtomicsMeetsPreconditions(typedArray, args_[1])) {
9911+
if (!AtomicsMeetsPreconditions(typedArray, args_[1], AtomicAccess::Write)) {
99049912
return AttachDecision::NoAction;
99059913
}
99069914

0 commit comments

Comments
 (0)