Skip to content

Commit

Permalink
Implement resizable ArrayBuffer (#1707)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma authored Dec 30, 2023
1 parent 5c00d38 commit 331c1b7
Show file tree
Hide file tree
Showing 14 changed files with 805 additions and 348 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 @@ -17,7 +17,6 @@
"regexp-lookbehind",
"regexp-unicode-property-escapes",
"regexp-v-flag",
"resizable-arraybuffer",
"SharedArrayBuffer",
"tail-call-optimization",
"Temporal",
Expand Down
150 changes: 91 additions & 59 deletions Jint/Native/ArrayBuffer/ArrayBufferConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,83 +8,115 @@
using Jint.Runtime.Descriptors;
using Jint.Runtime.Interop;

namespace Jint.Native.ArrayBuffer
namespace Jint.Native.ArrayBuffer;

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-constructor
/// </summary>
internal sealed class ArrayBufferConstructor : Constructor
{
private static readonly JsString _functionName = new("ArrayBuffer");

internal ArrayBufferConstructor(
Engine engine,
Realm realm,
FunctionPrototype functionPrototype,
ObjectPrototype objectPrototype)
: base(engine, realm, _functionName)
{
_prototype = functionPrototype;
PrototypeObject = new ArrayBufferPrototype(engine, this, objectPrototype);
_length = new PropertyDescriptor(1, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}

private ArrayBufferPrototype PrototypeObject { get; }

protected override void Initialize()
{
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
var properties = new PropertyDictionary(1, checkExistingKeys: false)
{
["isView"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "isView", IsView, 1, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)),
};
SetProperties(properties);

var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, lengthFlags), set: Undefined,PropertyFlag.Configurable),
};
SetSymbols(symbols);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-constructor
/// https://tc39.es/ecma262/#sec-arraybuffer.isview
/// </summary>
internal sealed class ArrayBufferConstructor : Constructor
private static JsValue IsView(JsValue thisObject, JsValue[] arguments)
{
private static readonly JsString _functionName = new("ArrayBuffer");

internal ArrayBufferConstructor(
Engine engine,
Realm realm,
FunctionPrototype functionPrototype,
ObjectPrototype objectPrototype)
: base(engine, realm, _functionName)
{
_prototype = functionPrototype;
PrototypeObject = new ArrayBufferPrototype(engine, this, objectPrototype);
_length = new PropertyDescriptor(1, PropertyFlag.Configurable);
_prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
}
var arg = arguments.At(0);
return arg is JsDataView or JsTypedArray;
}

private ArrayBufferPrototype PrototypeObject { get; }
/// <summary>
/// https://tc39.es/ecma262/#sec-get-arraybuffer-@@species
/// </summary>
private static JsValue Species(JsValue thisObject, JsValue[] arguments)
{
return thisObject;
}

protected override void Initialize()
public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
const PropertyFlag lengthFlags = PropertyFlag.Configurable;
var properties = new PropertyDictionary(1, checkExistingKeys: false)
{
["isView"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "isView", IsView, 1, lengthFlags), PropertyFlag.Configurable | PropertyFlag.Writable)),
};
SetProperties(properties);

var symbols = new SymbolDictionary(1)
{
[GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, lengthFlags), set: Undefined,PropertyFlag.Configurable),
};
SetSymbols(symbols);
ExceptionHelper.ThrowTypeError(_realm);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-arraybuffer.isview
/// </summary>
private static JsValue IsView(JsValue thisObject, JsValue[] arguments)
var length = arguments.At(0);
var options = arguments.At(1);

var byteLength = TypeConverter.ToIndex(_realm, length);
var requestedMaxByteLength = GetArrayBufferMaxByteLengthOption(options);
return AllocateArrayBuffer(newTarget, byteLength, requestedMaxByteLength);
}

/// <summary>
/// https://tc39.es/ecma262/#sec-getarraybuffermaxbytelengthoption
/// </summary>
private uint? GetArrayBufferMaxByteLengthOption(JsValue options)
{
if (options is not ObjectInstance)
{
var arg = arguments.At(0);
return arg is JsDataView or JsTypedArray;
return null;
}

/// <summary>
/// https://tc39.es/ecma262/#sec-get-arraybuffer-@@species
/// </summary>
private static JsValue Species(JsValue thisObject, JsValue[] arguments)
var maxByteLength = options.Get("maxByteLength");
if (maxByteLength.IsUndefined())
{
return thisObject;
return null;
}

public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_realm);
}
return TypeConverter.ToIndex(_realm, maxByteLength);
}

var byteLength = TypeConverter.ToIndex(_realm, arguments.At(0));
return AllocateArrayBuffer(newTarget, byteLength);
}
/// <summary>
/// https://tc39.es/ecma262/#sec-allocatearraybuffer
/// </summary>
internal JsArrayBuffer AllocateArrayBuffer(JsValue constructor, ulong byteLength, uint? maxByteLength = null)
{
var allocatingResizableBuffer = maxByteLength != null;

internal JsArrayBuffer AllocateArrayBuffer(JsValue constructor, ulong byteLength)
if (allocatingResizableBuffer && byteLength > maxByteLength)
{
var obj = OrdinaryCreateFromConstructor(
constructor,
static intrinsics => intrinsics.ArrayBuffer.PrototypeObject,
static (engine, realm, state) => new JsArrayBuffer(engine, (ulong) state!._value),
JsNumber.Create(byteLength));

return obj;
ExceptionHelper.ThrowRangeError(_realm);
}

var obj = OrdinaryCreateFromConstructor(
constructor,
static intrinsics => intrinsics.ArrayBuffer.PrototypeObject,
static (engine, _, state) => new JsArrayBuffer(engine, state!.Item1, state.Item2),
new Tuple<ulong, uint?>(byteLength, maxByteLength));

return obj;
}
}
2 changes: 1 addition & 1 deletion Jint/Native/ArrayBuffer/ArrayBufferOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ namespace Jint.Native.ArrayBuffer;
internal enum ArrayBufferOrder
{
Init,
SecCst,
SeqCst,
Unordered
}
Loading

0 comments on commit 331c1b7

Please sign in to comment.