Skip to content

Commit

Permalink
Implement ArrayBuffer.transfer (#1709)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored Dec 31, 2023
1 parent 5ad54b1 commit 547f66f
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 4 deletions.
1 change: 0 additions & 1 deletion Jint.Tests.Test262/Test262Harness.settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
"Parallel": true,
"ExcludedFeatures": [
"Array.fromAsync",
"arraybuffer-transfer",
"async-iteration",
"Atomics",
"generators",
Expand Down
71 changes: 70 additions & 1 deletion Jint/Native/ArrayBuffer/ArrayBufferPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ protected override void Initialize()
["maxByteLength"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get maxByteLength", MaxByteLength, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
["resizable"] = new GetSetPropertyDescriptor(new ClrFunctionInstance(_engine, "get resizable", Resizable, 0, lengthFlags), Undefined, PropertyFlag.Configurable),
["resize"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "resize", Resize, 1, lengthFlags), PropertyFlag.NonEnumerable),
["slice"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "slice", Slice, 2, lengthFlags), PropertyFlag.NonEnumerable)
["slice"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "slice", Slice, 2, lengthFlags), PropertyFlag.NonEnumerable),
["transfer"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "transfer", Transfer, 0, lengthFlags), PropertyFlag.NonEnumerable),
["transferToFixedLength"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "transferToFixedLength", TransferToFixedLength, 0, lengthFlags), PropertyFlag.NonEnumerable),
};
SetProperties(properties);

Expand Down Expand Up @@ -215,4 +217,71 @@ private JsValue Slice(JsValue thisObject, JsValue[] arguments)
System.Array.Copy(fromBuf!, first, toBuf!, 0, newLen);
return bufferInstance;
}

/// <summary>
/// https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfer
/// </summary>
private JsValue Transfer(JsValue thisObject, JsValue[] arguments)
{
return ArrayBufferCopyAndDetach(thisObject, arguments.At(0), PreserveResizability.PreserveResizability);
}

/// <summary>
/// https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength
/// </summary>
private JsValue TransferToFixedLength(JsValue thisObject, JsValue[] arguments)
{
return ArrayBufferCopyAndDetach(thisObject, arguments.At(0), PreserveResizability.FixedLength);
}

private JsValue ArrayBufferCopyAndDetach(JsValue o, JsValue newLength, PreserveResizability preserveResizability)
{
if (o is not JsArrayBuffer arrayBuffer || arrayBuffer.IsSharedArrayBuffer)
{
ExceptionHelper.ThrowTypeError(_realm, "Method ArrayBuffer.prototype.ArrayBufferCopyAndDetach called on incompatible receiver " + o);
return Undefined;
}

uint newByteLength;
if (newLength.IsUndefined())
{
newByteLength = (uint) arrayBuffer.ArrayBufferByteLength;
}
else
{
newByteLength = TypeConverter.ToIndex(_realm, newLength);
}

arrayBuffer.AssertNotDetached();

uint? newMaxByteLength = null;
if (preserveResizability == PreserveResizability.PreserveResizability && arrayBuffer._arrayBufferMaxByteLength != null)
{
newMaxByteLength = (uint) arrayBuffer._arrayBufferMaxByteLength.Value;
}

if (!arrayBuffer._arrayBufferDetachKey.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_realm);
}

var newBuffer = _engine.Realm.Intrinsics.ArrayBuffer.AllocateArrayBuffer(_engine.Realm.Intrinsics.ArrayBuffer, newByteLength, newMaxByteLength);
var copyLength = System.Math.Min(newByteLength, arrayBuffer.ArrayBufferByteLength);
var fromBlock = arrayBuffer.ArrayBufferData!;
var toBlock = newBuffer.ArrayBufferData!;

System.Array.Copy(fromBlock, 0, toBlock, 0, copyLength);

// NOTE: Neither creation of the new Data Block nor copying from the old Data Block are observable. Implementations may implement this method as a zero-copy move or a realloc.

arrayBuffer.DetachArrayBuffer();

return newBuffer;
}

private enum PreserveResizability
{
PreserveResizability,
FixedLength
}
}
2 changes: 1 addition & 1 deletion Jint/Native/ArrayBuffer/JsArrayBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class JsArrayBuffer : ObjectInstance
internal byte[]? _arrayBufferData;
internal readonly int? _arrayBufferMaxByteLength;

private readonly JsValue _arrayBufferDetachKey = Undefined;
internal readonly JsValue _arrayBufferDetachKey = Undefined;

internal JsArrayBuffer(
Engine engine,
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,9 @@ Following features are supported in version 3.x.

#### ECMAScript Stage 3 (no version yet)

-`ArrayBuffer.prototype.transfer`
- ✔ Array Grouping - `Object.groupBy` and `Map.groupBy`
- ✔ Promise.withResolvers
-`Promise.withResolvers`
- ✔ Resizable and growable ArrayBuffers
- ✔ ShadowRealm

Expand Down

0 comments on commit 547f66f

Please sign in to comment.