Skip to content

Commit

Permalink
[JSC] Implement ArrayBuffer#transfer
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=263518
rdar://117337535

Reviewed by Mark Lam and Ross Kirsling.

This patch implements ArrayBuffer#transfer proposal[1], which is stage-3 and implemented in the other engines too.
It implements two functions and one getter.

1. ArrayBuffer#transfer. It transfers internal ArrayBuffer to a new ArrayBuffer and detach the original buffer.
   This only works with normal ArrayBuffer and it throws an error against SharedArrayBuffer. This preserves resizability
   of the original ArrayBuffer, it means that if we transfer resizable ArrayBuffer, we get resizable transferred ArrayBuffer.
2. ArrayBuffer#transferToFixedLength is similar to ArrayBuffer#transfer. But this always creates non-resizable ArrayBuffer.
   It means, internally, we create a new ArrayBuffer if resizability is different and copy the underlying content.

In both cases, if ArrayBuffer is not resizable and result size is the same, then it just changes underlying buffer's ownership and
does not allocate and copy contents. If it is ArrayBuffer#transfer and the input ArrayBuffer is resizable, then we also just changes
the ownership and resize the resulted ArrayBuffer's size.

3. ArrayBuffer#detach getter returns boolean whether ArrayBuffer is detached or not.

[1]: https://tc39.es/proposal-arraybuffer-transfer/

* JSTests/test262/config.yaml:
* JSTests/test262/expectations.yaml:
* Source/JavaScriptCore/runtime/CommonIdentifiers.h:
* Source/JavaScriptCore/runtime/JSArrayBufferPrototype.cpp:
(JSC::arrayBufferCopyAndDetach):
(JSC::JSC_DEFINE_HOST_FUNCTION):
(JSC::JSArrayBufferPrototype::finishCreation):

Canonical link: https://commits.webkit.org/269674@main
  • Loading branch information
Constellation committed Oct 23, 2023
1 parent 454ec9e commit fe0aa79
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 191 deletions.
14 changes: 9 additions & 5 deletions JSTests/stress/v8-harmony-arraybuffer-transfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ function AssertBufferContainsTestData(ab) {

function TestSameLength(len, opts) {
let ab = new ArrayBuffer(len, opts);
let resizable = ab.resizable;
WriteTestData(ab);
const xfer = ab.transfer();
assertEquals(len, xfer.byteLength);
assertFalse(xfer.resizable);
assertEquals(resizable, xfer.resizable);
AssertBufferContainsTestData(xfer);
AssertDetached(ab);
}
Expand Down Expand Up @@ -73,11 +74,12 @@ function TestNonGrow(len, opts) {
for (let newLen of [len / 2, // shrink
0 // 0 special case
]) {
let ab = new ArrayBuffer(len, opts);
let ab = new ArrayBuffer(len, opts);
let resizable = ab.resizable;
WriteTestData(ab);
const xfer = ab.transfer(newLen);
assertEquals(newLen, xfer.byteLength);
assertFalse(xfer.resizable);
assertEquals(resizable, xfer.resizable);
if (len > 0) AssertBufferContainsTestData(xfer);
AssertDetached(ab);
}
Expand All @@ -97,17 +99,19 @@ TestNonGrow(0, { maxByteLength: 2048 });

{
let ab = new ArrayBuffer(len, { maxByteLength: len * 4 });
let resizable = ab.resizable;
const shrink = { valueOf() { ab.resize(len / 2); return len; } };
const xfer = ab.transfer(shrink);
assertFalse(xfer.resizable);
assertEquals(resizable, xfer.resizable);
assertEquals(len, xfer.byteLength);
}

{
let ab = new ArrayBuffer(len, { maxByteLength: len * 4 });
let resizable = ab.resizable;
const grow = { valueOf() { ab.resize(len * 2); return len; } };
const xfer = ab.transfer(grow);
assertFalse(xfer.resizable);
assertEquals(resizable, xfer.resizable);
assertEquals(len, xfer.byteLength);
}
})();
Expand Down
3 changes: 2 additions & 1 deletion JSTests/test262/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ flags:
Temporal: useTemporal
array-grouping: useArrayGroupMethod
ShadowRealm: useShadowRealm
resizable-arraybuffer: useArrayBufferTransfer
resizable-arraybuffer: useResizableArrayBuffer
arraybuffer-transfer: useArrayBufferTransfer
import-assertions: useImportAttributes
json-modules: useImportAttributes
promise-with-resolvers: usePromiseWithResolversMethod
Expand Down
153 changes: 0 additions & 153 deletions JSTests/test262/expectations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,159 +7,6 @@ test/built-ins/Array/fromAsync/this-constructor-operations.js:
test/built-ins/Array/fromAsync/this-constructor.js:
default: 'Test262:AsyncTestFailure:Test262Error: Test262Error: constructor is called once Expected SameValue(«2», «1») to be true'
strict mode: 'Test262:AsyncTestFailure:Test262Error: Test262Error: constructor is called once Expected SameValue(«2», «1») to be true'
test/built-ins/ArrayBuffer/prototype/detached/detached-buffer-resizable.js:
default: 'Test262Error: Resizable ArrayBuffer with maxByteLength of 0 is not detached Expected SameValue(«undefined», «false») to be true'
strict mode: 'Test262Error: Resizable ArrayBuffer with maxByteLength of 0 is not detached Expected SameValue(«undefined», «false») to be true'
test/built-ins/ArrayBuffer/prototype/detached/detached-buffer.js:
default: 'Test262Error: Expected SameValue(«undefined», «false») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «false») to be true'
test/built-ins/ArrayBuffer/prototype/detached/invoked-as-accessor.js:
default: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
strict mode: 'Test262Error: Expected a TypeError to be thrown but no exception was thrown at all'
test/built-ins/ArrayBuffer/prototype/detached/invoked-as-func.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
test/built-ins/ArrayBuffer/prototype/detached/length.js:
default: "TypeError: undefined is not an object (evaluating 'desc.get')"
strict mode: "TypeError: undefined is not an object (evaluating 'desc.get')"
test/built-ins/ArrayBuffer/prototype/detached/name.js:
default: "TypeError: undefined is not an object (evaluating 'desc.get')"
strict mode: "TypeError: undefined is not an object (evaluating 'desc.get')"
test/built-ins/ArrayBuffer/prototype/detached/prop-desc.js:
default: "TypeError: undefined is not an object (evaluating 'desc.set')"
strict mode: "TypeError: undefined is not an object (evaluating 'desc.set')"
test/built-ins/ArrayBuffer/prototype/detached/this-has-no-arraybufferdata-internal.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
test/built-ins/ArrayBuffer/prototype/detached/this-is-not-object.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor("
test/built-ins/ArrayBuffer/prototype/detached/this-is-sharedarraybuffer-resizable.js:
default: "TypeError: undefined is not an object (evaluating 'detached.get')"
strict mode: "TypeError: undefined is not an object (evaluating 'detached.get')"
test/built-ins/ArrayBuffer/prototype/detached/this-is-sharedarraybuffer.js:
default: "TypeError: undefined is not an object (evaluating 'detached.get')"
strict mode: "TypeError: undefined is not an object (evaluating 'detached.get')"
test/built-ins/ArrayBuffer/prototype/transfer/descriptor.js:
default: 'Test262Error: obj should have an own property transfer'
strict mode: 'Test262Error: obj should have an own property transfer'
test/built-ins/ArrayBuffer/prototype/transfer/extensible.js:
default: 'Test262Error: Expected true but got false'
strict mode: 'Test262Error: Expected true but got false'
test/built-ins/ArrayBuffer/prototype/transfer/from-fixed-to-larger-no-resizable.js:
default: "TypeError: source.transfer is not a function. (In 'source.transfer(5)', 'source.transfer' is undefined)"
strict mode: "TypeError: source.transfer is not a function. (In 'source.transfer(5)', 'source.transfer' is undefined)"
test/built-ins/ArrayBuffer/prototype/transfer/from-fixed-to-same-no-resizable.js:
default: "TypeError: source.transfer is not a function. (In 'source.transfer()', 'source.transfer' is undefined)"
strict mode: "TypeError: source.transfer is not a function. (In 'source.transfer()', 'source.transfer' is undefined)"
test/built-ins/ArrayBuffer/prototype/transfer/from-fixed-to-smaller-no-resizable.js:
default: "TypeError: source.transfer is not a function. (In 'source.transfer(3)', 'source.transfer' is undefined)"
strict mode: "TypeError: source.transfer is not a function. (In 'source.transfer(3)', 'source.transfer' is undefined)"
test/built-ins/ArrayBuffer/prototype/transfer/from-fixed-to-zero-no-resizable.js:
default: "TypeError: source.transfer is not a function. (In 'source.transfer(0)', 'source.transfer' is undefined)"
strict mode: "TypeError: source.transfer is not a function. (In 'source.transfer(0)', 'source.transfer' is undefined)"
test/built-ins/ArrayBuffer/prototype/transfer/from-resizable-to-larger.js:
default: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
strict mode: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/from-resizable-to-same.js:
default: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
strict mode: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/from-resizable-to-smaller.js:
default: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
strict mode: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/from-resizable-to-zero.js:
default: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
strict mode: 'Test262Error: dest.resizable Expected SameValue(«false», «true») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/length.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
test/built-ins/ArrayBuffer/prototype/transfer/name.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
test/built-ins/ArrayBuffer/prototype/transfer/new-length-excessive.js:
default: 'Test262Error: Expected a RangeError but got a TypeError'
strict mode: 'Test262Error: Expected a RangeError but got a TypeError'
test/built-ins/ArrayBuffer/prototype/transfer/new-length-non-number.js:
default: 'Test262Error: Expected SameValue(«0», «2») to be true'
strict mode: 'Test262Error: Expected SameValue(«0», «2») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/nonconstructor.js:
default: 'Test262Error: isConstructor invoked with a non-function value'
strict mode: 'Test262Error: isConstructor invoked with a non-function value'
test/built-ins/ArrayBuffer/prototype/transfer/this-is-detached.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/this-is-not-arraybuffer-object.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/ArrayBuffer/prototype/transfer/this-is-not-object.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/descriptor.js:
default: 'Test262Error: obj should have an own property transferToFixedLength'
strict mode: 'Test262Error: obj should have an own property transferToFixedLength'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/extensible.js:
default: 'Test262Error: Expected true but got false'
strict mode: 'Test262Error: Expected true but got false'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-larger-no-resizable.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-larger.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-same-no-resizable.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-same.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-smaller-no-resizable.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-smaller.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-zero-no-resizable.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-fixed-to-zero.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-resizable-to-larger.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(5)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-resizable-to-same.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength()', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-resizable-to-smaller.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(3)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/from-resizable-to-zero.js:
default: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
strict mode: "TypeError: source.transferToFixedLength is not a function. (In 'source.transferToFixedLength(0)', 'source.transferToFixedLength' is undefined)"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/length.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/name.js:
default: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
strict mode: "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertyDescriptor(obj, name)')"
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/new-length-excessive.js:
default: 'Test262Error: Expected a RangeError but got a TypeError'
strict mode: 'Test262Error: Expected a RangeError but got a TypeError'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/new-length-non-number.js:
default: 'Test262Error: Expected SameValue(«0», «2») to be true'
strict mode: 'Test262Error: Expected SameValue(«0», «2») to be true'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/nonconstructor.js:
default: 'Test262Error: isConstructor invoked with a non-function value'
strict mode: 'Test262Error: isConstructor invoked with a non-function value'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/this-is-detached.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/this-is-not-arraybuffer-object.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/ArrayBuffer/prototype/transferToFixedLength/this-is-not-object.js:
default: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
strict mode: 'Test262Error: Expected SameValue(«undefined», «function») to be true'
test/built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-broken-promise.js:
default: 'Error: broken promise'
strict mode: 'Error: broken promise'
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/CommonIdentifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
macro(deref) \
macro(description) \
macro(descriptions) \
macro(detached) \
macro(detail) \
macro(displayName) \
macro(done) \
Expand Down Expand Up @@ -278,6 +279,7 @@
macro(toWellFormed) \
macro(trailingZeroDisplay) \
macro(transfer) \
macro(transferToFixedLength) \
macro(type) \
macro(uid) \
macro(unicode) \
Expand Down
Loading

0 comments on commit fe0aa79

Please sign in to comment.