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

Commit 84afad6

Browse files
committed
Bug 1966355 - Part 18: Add structured clone support. r=spidermonkey-reviewers,sfink
Spec PR: <whatwg/html#11033> Differential Revision: https://phabricator.services.mozilla.com/D249411
1 parent 5ab9513 commit 84afad6

File tree

3 files changed

+168
-10
lines changed

3 files changed

+168
-10
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// |jit-test| --enable-arraybuffer-immutable; skip-if: !ArrayBuffer.prototype.transferToImmutable
2+
3+
load(libdir + "asserts.js");
4+
5+
const scopes = [
6+
"SameProcess",
7+
"DifferentProcess",
8+
"DifferentProcessForIndexedDB",
9+
];
10+
11+
function testArrayBufferTransferable(scope) {
12+
var length = 4;
13+
var byteLength = length * Uint8Array.BYTES_PER_ELEMENT;
14+
15+
var ab = new ArrayBuffer(byteLength);
16+
assertEq(ab.immutable, false);
17+
assertEq(ab.byteLength, byteLength);
18+
19+
var ta = new Uint8Array(ab);
20+
ta.set([33, 44, 55, 66]);
21+
22+
var iab = ab.transferToImmutable();
23+
var ita = new Uint8Array(iab);
24+
25+
assertEq(ab.detached, true);
26+
assertEq(iab.detached, false);
27+
assertEq(ita.toString(), "33,44,55,66");
28+
29+
assertThrowsInstanceOf(() => serialize(iab, [iab], {scope}), TypeError);
30+
31+
assertEq(ab.detached, true);
32+
assertEq(iab.detached, false);
33+
assertEq(ita.toString(), "33,44,55,66");
34+
}
35+
scopes.forEach(testArrayBufferTransferable);
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// |jit-test| --enable-arraybuffer-immutable; skip-if: !ArrayBuffer.prototype.transferToImmutable
2+
3+
const scopes = [
4+
"SameProcess",
5+
"DifferentProcess",
6+
"DifferentProcessForIndexedDB",
7+
];
8+
9+
function testInt32Array(scope) {
10+
var length = 4;
11+
var byteLength = length * Int32Array.BYTES_PER_ELEMENT;
12+
13+
var ab = new ArrayBuffer(byteLength);
14+
assertEq(ab.immutable, false);
15+
assertEq(ab.byteLength, byteLength);
16+
17+
var ta = new Int32Array(ab);
18+
ta.set([1, 87654321, -123]);
19+
20+
var ita1 = new Int32Array(ab.transferToImmutable());
21+
assertEq(ita1.byteLength, byteLength);
22+
assertEq(ita1.toString(), "1,87654321,-123,0");
23+
assertEq(ita1.buffer.immutable, true);
24+
25+
var clonebuf = serialize(ita1, undefined, {scope});
26+
var ita2 = deserialize(clonebuf);
27+
assertEq(ita2 instanceof Int32Array, true);
28+
assertEq(ita2.byteLength, byteLength);
29+
assertEq(ita2.toString(), "1,87654321,-123,0");
30+
assertEq(ita2.buffer.immutable, true);
31+
assertEq(ita2.buffer.byteLength, byteLength);
32+
}
33+
scopes.forEach(testInt32Array);
34+
35+
function testFloat64Array(scope) {
36+
var length = 4;
37+
var byteLength = length * Float64Array.BYTES_PER_ELEMENT;
38+
39+
var ab = new ArrayBuffer(byteLength);
40+
assertEq(ab.immutable, false);
41+
assertEq(ab.byteLength, byteLength);
42+
43+
var ta = new Float64Array(ab);
44+
ta.set([NaN, 3.14, 0, 0]);
45+
46+
var ita1 = new Float64Array(ab.transferToImmutable());
47+
assertEq(ita1.byteLength, byteLength);
48+
assertEq(ita1.toString(), "NaN,3.14,0,0");
49+
assertEq(ita1.buffer.immutable, true);
50+
51+
var clonebuf = serialize(ita1, undefined, {scope});
52+
var ita2 = deserialize(clonebuf);
53+
assertEq(ita2 instanceof Float64Array, true);
54+
assertEq(ita2.byteLength, byteLength);
55+
assertEq(ita2.toString(), "NaN,3.14,0,0");
56+
assertEq(ita2.buffer.immutable, true);
57+
assertEq(ita2.buffer.byteLength, byteLength);
58+
}
59+
scopes.forEach(testFloat64Array);
60+
61+
function testDataView(scope) {
62+
var length = 4;
63+
var byteLength = length * Uint8Array.BYTES_PER_ELEMENT;
64+
65+
var ab = new ArrayBuffer(byteLength);
66+
assertEq(ab.immutable, false);
67+
assertEq(ab.byteLength, byteLength);
68+
69+
var ta = new Uint8Array(ab);
70+
ta.set([5, 0, 255]);
71+
assertEq(ta.toString(), "5,0,255,0");
72+
73+
var idv1 = new DataView(ab.transferToImmutable());
74+
assertEq(idv1.byteLength, byteLength);
75+
assertEq(idv1.buffer.immutable, true);
76+
77+
var clonebuf = serialize(idv1, undefined, {scope});
78+
var idv2 = deserialize(clonebuf);
79+
assertEq(idv2 instanceof DataView, true);
80+
assertEq(idv2.byteLength, byteLength);
81+
assertEq(new Uint8Array(idv2.buffer).toString(), "5,0,255,0");
82+
assertEq(idv2.buffer.immutable, true);
83+
assertEq(idv2.buffer.byteLength, byteLength);
84+
}
85+
scopes.forEach(testDataView);
86+
87+
function testArrayBuffer(scope) {
88+
var length = 4;
89+
var byteLength = length * Uint8Array.BYTES_PER_ELEMENT;
90+
91+
var ab = new ArrayBuffer(byteLength);
92+
assertEq(ab.immutable, false);
93+
assertEq(ab.byteLength, byteLength);
94+
95+
var ta = new Uint8Array(ab);
96+
ta.set([33, 44, 55, 66]);
97+
assertEq(ta.toString(), "33,44,55,66");
98+
99+
var iab1 = ab.transferToImmutable();
100+
101+
var clonebuf = serialize(iab1, undefined, {scope});
102+
var iab2 = deserialize(clonebuf);
103+
assertEq(iab2 instanceof ArrayBuffer, true);
104+
assertEq(new Uint8Array(iab2).toString(), "33,44,55,66");
105+
assertEq(iab2.immutable, true);
106+
assertEq(iab2.byteLength, byteLength);
107+
}
108+
scopes.forEach(testArrayBuffer);

js/src/vm/StructuredClone.cpp

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ enum StructuredDataType : uint32_t {
149149

150150
SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT,
151151
SCTAG_GROWABLE_SHARED_ARRAY_BUFFER_OBJECT,
152+
SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT,
152153

153154
SCTAG_TYPED_ARRAY_V1_MIN = 0xFFFF0100,
154155
SCTAG_TYPED_ARRAY_V1_INT8 = SCTAG_TYPED_ARRAY_V1_MIN + Scalar::Int8,
@@ -1237,9 +1238,13 @@ bool JSStructuredCloneWriter::parseTransferable() {
12371238

12381239
// External array buffers may be able to be transferred in the future,
12391240
// but that is not currently implemented.
1241+
//
1242+
// Immutable array buffers can't be transferred, because they can't be
1243+
// detached.
12401244

12411245
else if (unwrappedObj->is<ArrayBufferObject>()) {
1242-
if (unwrappedObj->as<ArrayBufferObject>().isExternal()) {
1246+
if (unwrappedObj->as<ArrayBufferObject>().isExternal() ||
1247+
unwrappedObj->as<ArrayBufferObject>().isImmutable()) {
12431248
return reportDataCloneError(JS_SCERR_TRANSFERABLE);
12441249
}
12451250
}
@@ -1486,9 +1491,10 @@ bool JSStructuredCloneWriter::writeArrayBuffer(HandleObject obj) {
14861491
obj->maybeUnwrapAs<ArrayBufferObject>());
14871492
JSAutoRealm ar(context(), buffer);
14881493

1489-
StructuredDataType type = !buffer->isResizable()
1490-
? SCTAG_ARRAY_BUFFER_OBJECT
1491-
: SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT;
1494+
StructuredDataType type =
1495+
buffer->isResizable() ? SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT
1496+
: buffer->isImmutable() ? SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT
1497+
: SCTAG_ARRAY_BUFFER_OBJECT;
14921498

14931499
if (!out.writePair(type, 0)) {
14941500
return false;
@@ -2343,6 +2349,9 @@ bool JSStructuredCloneWriter::transferOwnership() {
23432349
cx, obj->maybeUnwrapAs<ArrayBufferObject>());
23442350
JSAutoRealm ar(cx, arrayBuffer);
23452351

2352+
MOZ_ASSERT(!arrayBuffer->isImmutable(),
2353+
"Immutable array buffers can't be transferred");
2354+
23462355
if (arrayBuffer->isDetached()) {
23472356
reportDataCloneError(JS_SCERR_TYPED_ARRAY_DETACHED);
23482357
return false;
@@ -2837,7 +2846,8 @@ bool JSStructuredCloneReader::readArrayBuffer(StructuredDataType type,
28372846
// length separately to allow larger length values.
28382847
uint64_t nbytes = 0;
28392848
uint64_t maxbytes = 0;
2840-
if (type == SCTAG_ARRAY_BUFFER_OBJECT) {
2849+
if (type == SCTAG_ARRAY_BUFFER_OBJECT ||
2850+
type == SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
28412851
if (!in.read(&nbytes)) {
28422852
return false;
28432853
}
@@ -2863,12 +2873,15 @@ bool JSStructuredCloneReader::readArrayBuffer(StructuredDataType type,
28632873
}
28642874

28652875
JSObject* obj;
2866-
if (type != SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT) {
2867-
MOZ_ASSERT(maxbytes == 0);
2868-
obj = ArrayBufferObject::createZeroed(context(), size_t(nbytes));
2869-
} else {
2876+
if (type == SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT) {
28702877
obj = ResizableArrayBufferObject::createZeroed(context(), size_t(nbytes),
28712878
size_t(maxbytes));
2879+
} else if (type == SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
2880+
MOZ_ASSERT(maxbytes == 0);
2881+
obj = ImmutableArrayBufferObject::createZeroed(context(), size_t(nbytes));
2882+
} else {
2883+
MOZ_ASSERT(maxbytes == 0);
2884+
obj = ArrayBufferObject::createZeroed(context(), size_t(nbytes));
28722885
}
28732886
if (!obj) {
28742887
return false;
@@ -3253,6 +3266,7 @@ bool JSStructuredCloneReader::startRead(MutableHandleValue vp,
32533266
case SCTAG_ARRAY_BUFFER_OBJECT_V2:
32543267
case SCTAG_ARRAY_BUFFER_OBJECT:
32553268
case SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT:
3269+
case SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT:
32563270
if (!readArrayBuffer(StructuredDataType(tag), data, vp)) {
32573271
return false;
32583272
}
@@ -3553,7 +3567,8 @@ bool JSStructuredCloneReader::readTransferMap() {
35533567
}
35543568
if (tag != SCTAG_ARRAY_BUFFER_OBJECT_V2 &&
35553569
tag != SCTAG_ARRAY_BUFFER_OBJECT &&
3556-
tag != SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT) {
3570+
tag != SCTAG_RESIZABLE_ARRAY_BUFFER_OBJECT &&
3571+
tag != SCTAG_IMMUTABLE_ARRAY_BUFFER_OBJECT) {
35573572
ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE, closure);
35583573
return false;
35593574
}

0 commit comments

Comments
 (0)