Skip to content

Commit

Permalink
Add custom strategy for array read access (#1729)
Browse files Browse the repository at this point in the history
  • Loading branch information
lahma committed Jan 6, 2024
1 parent 4b23f03 commit 53ab42a
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 46 deletions.
6 changes: 3 additions & 3 deletions Jint/Native/Array/ArrayConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ private JsValue From(JsValue thisObject, JsValue[] arguments)
? _engine._jsValueArrayPool.RentArray(2)
: null;

var target = ArrayOperations.For(a);
var target = ArrayOperations.For(a, forWrite: true);
uint n = 0;
for (uint i = 0; i < length; i++)
{
Expand Down Expand Up @@ -157,7 +157,7 @@ private sealed class ArrayProtocol : IteratorProtocol
ICallable? callable) : base(engine, iterator, 2)
{
_thisArg = thisArg;
_instance = ArrayOperations.For(instance);
_instance = ArrayOperations.For(instance, forWrite: true);
_callable = callable;
}

Expand Down Expand Up @@ -310,7 +310,7 @@ private JsArray Construct(JsValue[] arguments, ulong capacity, ObjectInstance pr
break;
case JsArray array:
// direct copy
instance = (JsArray) ConstructArrayFromArrayLike(Undefined, ArrayOperations.For(array), callable: null, this);
instance = (JsArray) ConstructArrayFromArrayLike(Undefined, ArrayOperations.For(array, forWrite: false), callable: null, this);
break;
default:
instance = ArrayCreate(capacity, prototypeObject);
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Array/ArrayIteratorPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public ArrayLikeIterator(Engine engine, ObjectInstance objectInstance, ArrayIter
_typedArray = objectInstance as JsTypedArray;
if (_typedArray is null)
{
_operations = ArrayOperations.For(objectInstance);
_operations = ArrayOperations.For(objectInstance, forWrite: false);
}

_position = 0;
Expand Down
61 changes: 58 additions & 3 deletions Jint/Native/Array/ArrayOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,17 @@ public static ArrayOperations For(Realm realm, JsValue value, bool forWrite)
{
return new JsStringOperations(realm, stringInstance);
}

if (value is JsArray { CanUseFastAccess: true } array && array.Length <= (array._dense?.Length ?? -1))
{
return new ArrayReadOperations(array);
}
}

return For(TypeConverter.ToObject(realm, value));
return For(TypeConverter.ToObject(realm, value), forWrite);
}

public static ArrayOperations For(ObjectInstance instance)
public static ArrayOperations For(ObjectInstance instance, bool forWrite)
{
if (instance is JsArray { CanUseFastAccess: true } arrayInstance)
{
Expand Down Expand Up @@ -352,7 +357,7 @@ public override void DeletePropertyOrThrow(ulong index)
public override bool HasProperty(ulong index) => _target.HasProperty(index);
}

private sealed class JsStringOperations : ArrayOperations
private sealed class JsStringOperations : ArrayOperations
{
private readonly Realm _realm;
private readonly JsString _target;
Expand Down Expand Up @@ -405,6 +410,56 @@ public override bool TryGetValue(ulong index, out JsValue value)

public override void DeletePropertyOrThrow(ulong index) => throw new NotSupportedException();
}

private sealed class ArrayReadOperations : ArrayOperations
{
private readonly JsArray _target;
private readonly JsValue?[] _data;
private readonly uint _length;

public ArrayReadOperations(JsArray target)
{
_target = target;
_data = target._dense ?? System.Array.Empty<JsValue>();
_length = target.Length;
}

public override ObjectInstance Target => _target;

public override ulong GetSmallestIndex(ulong length) => 0;

public override uint GetLength() => _length;

public override ulong GetLongLength() => _length;

public override void SetLength(ulong length) => throw new NotSupportedException();

public override void EnsureCapacity(ulong capacity)
{
}

public override JsValue Get(ulong index) => (index < (ulong) _data.Length ? _data[(int) index] : JsValue.Undefined) ?? JsValue.Undefined;

public override bool TryGetValue(ulong index, out JsValue value)
{
if (index < _length)
{
value = _data[(int) index]!;
return value is not null;
}

value = JsValue.Undefined;
return false;
}

public override bool HasProperty(ulong index) => index < _length && _data[index] is not null;

public override void CreateDataPropertyOrThrow(ulong index, JsValue value) => throw new NotSupportedException();

public override void Set(ulong index, JsValue value, bool updateLength = false, bool throwOnError = true) => throw new NotSupportedException();

public override void DeletePropertyOrThrow(ulong index) => throw new NotSupportedException();
}
}

/// <summary>
Expand Down
61 changes: 29 additions & 32 deletions Jint/Native/Array/ArrayPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ internal ObjectInstance Values(JsValue thisObject, JsValue[] arguments)

private ObjectInstance With(JsValue thisObject, JsValue[] arguments)
{
var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject));
var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject), forWrite: false);
var len = o.GetLongLength();
var relativeIndex = TypeConverter.ToIntegerOrInfinity(arguments.At(0));
var value = arguments.At(1);
Expand Down Expand Up @@ -193,7 +193,7 @@ private JsValue Fill(JsValue thisObject, JsValue[] arguments)

var o = TypeConverter.ToObject(_realm, thisObject);

var operations = ArrayOperations.For(o);
var operations = ArrayOperations.For(o, forWrite: true);
var length = operations.GetLongLength();

var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
Expand Down Expand Up @@ -246,7 +246,7 @@ private JsValue CopyWithin(JsValue thisObject, JsValue[] arguments)
JsValue start = arguments.At(1);
JsValue end = arguments.At(2);

var operations = ArrayOperations.For(o);
var operations = ArrayOperations.For(o, forWrite: true);
var len = operations.GetLongLength();

var relativeTarget = TypeConverter.ToIntegerOrInfinity(target);
Expand Down Expand Up @@ -379,7 +379,7 @@ private JsValue Reduce(JsValue thisObject, JsValue[] arguments)
var callbackfn = arguments.At(0);
var initialValue = arguments.At(1);

var o = ArrayOperations.For(_realm, thisObject, forWrite: false);
var o = ArrayOperations.For(_realm, thisObject, forWrite: true);
var len = o.GetLength();

var callable = GetCallable(callbackfn);
Expand Down Expand Up @@ -447,7 +447,7 @@ private JsValue Filter(JsValue thisObject, JsValue[] arguments)
var callable = GetCallable(callbackfn);

var a = _realm.Intrinsics.Array.ArraySpeciesCreate(TypeConverter.ToObject(_realm, thisObject), 0);
var operations = ArrayOperations.For(a);
var operations = ArrayOperations.For(a, forWrite: true);

uint to = 0;
var args = _engine._jsValueArrayPool.RentArray(3);
Expand Down Expand Up @@ -496,7 +496,7 @@ private JsValue Map(JsValue thisObject, JsValue[] arguments)
var thisArg = arguments.At(1);
var callable = GetCallable(callbackfn);

var a = ArrayOperations.For(_realm.Intrinsics.Array.ArraySpeciesCreate(TypeConverter.ToObject(_realm, thisObject), (uint) len));
var a = ArrayOperations.For(_realm.Intrinsics.Array.ArraySpeciesCreate(TypeConverter.ToObject(_realm, thisObject), (uint) len), forWrite: true);
var args = _engine._jsValueArrayPool.RentArray(3);
args[2] = o.Target;
for (uint k = 0; k < len; k++)
Expand All @@ -518,8 +518,7 @@ private JsValue Map(JsValue thisObject, JsValue[] arguments)
/// </summary>
private JsValue Flat(JsValue thisObject, JsValue[] arguments)
{
var O = TypeConverter.ToObject(_realm, thisObject);
var operations = ArrayOperations.For(O);
var operations = ArrayOperations.For(_realm, thisObject, forWrite: false);
var sourceLen = operations.GetLength();
double depthNum = 1;
var depth = arguments.At(0);
Expand All @@ -533,8 +532,8 @@ private JsValue Flat(JsValue thisObject, JsValue[] arguments)
depthNum = 0;
}

var A = _realm.Intrinsics.Array.ArraySpeciesCreate(O, 0);
FlattenIntoArray(A, O, sourceLen, 0, depthNum);
var A = _realm.Intrinsics.Array.ArraySpeciesCreate(operations.Target, 0);
FlattenIntoArray(A, operations, sourceLen, 0, depthNum);
return A;
}

Expand All @@ -543,7 +542,7 @@ private JsValue Flat(JsValue thisObject, JsValue[] arguments)
/// </summary>
private JsValue FlatMap(JsValue thisObject, JsValue[] arguments)
{
var O = TypeConverter.ToObject(_realm, thisObject);
var O = ArrayOperations.For(_realm, thisObject, forWrite: false);
var mapperFunction = arguments.At(0);
var thisArg = arguments.At(1);

Expand All @@ -554,40 +553,39 @@ private JsValue FlatMap(JsValue thisObject, JsValue[] arguments)
ExceptionHelper.ThrowTypeError(_realm, "flatMap mapper function is not callable");
}

var A = _realm.Intrinsics.Array.ArraySpeciesCreate(O, 0);
var A = _realm.Intrinsics.Array.ArraySpeciesCreate(O.Target, 0);
FlattenIntoArray(A, O, sourceLen, 0, 1, (ICallable) mapperFunction, thisArg);
return A;
}

/// <summary>
/// https://tc39.es/ecma262/#sec-flattenintoarray
/// </summary>
private long FlattenIntoArray(
private ulong FlattenIntoArray(
ObjectInstance target,
ObjectInstance source,
ArrayOperations source,
uint sourceLen,
long start,
ulong start,
double depth,
ICallable? mapperFunction = null,
JsValue? thisArg = null)
{
var targetIndex = start;
var sourceIndex = 0;
ulong sourceIndex = 0;

var callArguments = System.Array.Empty<JsValue>();
if (mapperFunction is not null)
{
callArguments = _engine._jsValueArrayPool.RentArray(3);
callArguments[2] = source;
callArguments[2] = source.Target;
}

while (sourceIndex < sourceLen)
{
var P = TypeConverter.ToString(sourceIndex);
var exists = source.HasProperty(P);
var exists = source.HasProperty(sourceIndex);
if (exists)
{
var element = source.Get(P);
var element = source.Get(sourceIndex);
if (mapperFunction is not null)
{
callArguments[0] = element;
Expand All @@ -609,7 +607,7 @@ private JsValue FlatMap(JsValue thisObject, JsValue[] arguments)

var objectInstance = (ObjectInstance) element;
var elementLen = objectInstance.GetLength();
targetIndex = FlattenIntoArray(target, objectInstance, elementLen, targetIndex, newDepth);
targetIndex = FlattenIntoArray(target, ArrayOperations.For(objectInstance, forWrite: false), elementLen, targetIndex, newDepth);
}
else
{
Expand Down Expand Up @@ -896,7 +894,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments)
var deleteCount = arguments.At(1);

var obj = TypeConverter.ToObject(_realm, thisObject);
var o = ArrayOperations.For(_realm, obj, forWrite: false);
var o = ArrayOperations.For(_realm, obj, forWrite: true);
var len = o.GetLongLength();
var relativeStart = TypeConverter.ToInteger(start);

Expand Down Expand Up @@ -943,7 +941,7 @@ private JsValue Splice(JsValue thisObject, JsValue[] arguments)
}

var instance = _realm.Intrinsics.Array.ArraySpeciesCreate(obj, actualDeleteCount);
var a = ArrayOperations.For(instance);
var a = ArrayOperations.For(instance, forWrite: true);
for (uint k = 0; k < actualDeleteCount; k++)
{
var index = actualStart + k;
Expand Down Expand Up @@ -1051,8 +1049,7 @@ private JsValue Unshift(JsValue thisObject, JsValue[] arguments)
/// </summary>
private JsValue Sort(JsValue thisObject, JsValue[] arguments)
{
var objectInstance = TypeConverter.ToObject(_realm, thisObject);
var obj = ArrayOperations.For(objectInstance);
var obj = ArrayOperations.For(_realm, thisObject, forWrite: true);
var compareFn = GetCompareFunction(arguments.At(0));

var len = obj.GetLength();
Expand Down Expand Up @@ -1150,7 +1147,7 @@ private JsValue Slice(JsValue thisObject, JsValue[] arguments)
else
{
// slower path
var operations = ArrayOperations.For(a);
var operations = ArrayOperations.For(a, forWrite: true);
for (uint n = 0; k < final; k++, n++)
{
if (o.TryGetValue(k, out var kValue))
Expand Down Expand Up @@ -1328,7 +1325,7 @@ private JsValue Concat(JsValue thisObject, JsValue[] arguments)

uint n = 0;
var a = _realm.Intrinsics.Array.ArraySpeciesCreate(TypeConverter.ToObject(_realm, thisObject), 0);
var aOperations = ArrayOperations.For(a);
var aOperations = ArrayOperations.For(a, forWrite: true);
for (var i = 0; i < items.Count; i++)
{
var e = items[i];
Expand All @@ -1341,7 +1338,7 @@ private JsValue Concat(JsValue thisObject, JsValue[] arguments)
}
else
{
var operations = ArrayOperations.For(oi);
var operations = ArrayOperations.For(oi, forWrite: false);
var len = operations.GetLongLength();

if (n + len > ArrayOperations.MaxArrayLikeLength)
Expand Down Expand Up @@ -1390,7 +1387,7 @@ internal JsValue ToString(JsValue thisObject, JsValue[] arguments)

private JsValue ToReversed(JsValue thisObject, JsValue[] arguments)
{
var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject));
var o = ArrayOperations.For(_realm, thisObject, forWrite: false);

var len = o.GetLongLength();

Expand All @@ -1411,7 +1408,7 @@ private JsValue ToReversed(JsValue thisObject, JsValue[] arguments)

private JsValue ToSorted(JsValue thisObject, JsValue[] arguments)
{
var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject));
var o = ArrayOperations.For(_realm, thisObject, forWrite: false);
var compareFn = GetCompareFunction(arguments.At(0));

var len = o.GetLongLength();
Expand Down Expand Up @@ -1554,7 +1551,7 @@ private JsValue ReduceRight(JsValue thisObject, JsValue[] arguments)
var callbackfn = arguments.At(0);
var initialValue = arguments.At(1);

var o = ArrayOperations.For(_realm, thisObject, forWrite: false);
var o = ArrayOperations.For(_realm, thisObject, forWrite: true);
var len = o.GetLongLength();

var callable = GetCallable(callbackfn);
Expand Down Expand Up @@ -1615,7 +1612,7 @@ public JsValue Push(JsValue thisObject, JsValue[] arguments)
return arrayInstance.Push(arguments);
}

var o = ArrayOperations.For(TypeConverter.ToObject(_realm, thisObject));
var o = ArrayOperations.For(_realm, thisObject, forWrite: true);
var n = o.GetLongLength();

if (n + (ulong) arguments.Length > ArrayOperations.MaxArrayLikeLength)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/Function/FunctionPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ internal static JsValue[] CreateListFromArrayLike(Realm realm, JsValue argArray,
{
ExceptionHelper.ThrowTypeError(realm);
}
var operations = ArrayOperations.For(argArrayObj);
var operations = ArrayOperations.For(argArrayObj, forWrite: false);
var argList = elementTypes is null ? operations.GetAll() : operations.GetAll(elementTypes.Value);
return argList;
}
Expand Down
5 changes: 2 additions & 3 deletions Jint/Native/String/StringConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,8 @@ private JsValue FromCodePoint(JsValue thisObject, JsValue[] arguments)
private JsValue Raw(JsValue thisObject, JsValue[] arguments)
{
var cooked = TypeConverter.ToObject(_realm, arguments.At(0));
var raw = TypeConverter.ToObject(_realm, cooked.Get(JintTaggedTemplateExpression.PropertyRaw));

var operations = ArrayOperations.For(raw);
var raw = cooked.Get(JintTaggedTemplateExpression.PropertyRaw);
var operations = ArrayOperations.For(_realm, raw, forWrite: false);
var length = operations.GetLength();

if (length <= 0)
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ private JsTypedArray TypedArrayCreateSameType(JsTypedArray exemplar, JsValue[] a
private static JsValue[] SortArray(JsArrayBuffer buffer, ICallable? compareFn, JsTypedArray obj)
{
var comparer = TypedArrayComparer.WithFunction(buffer, compareFn);
var operations = ArrayOperations.For(obj);
var operations = ArrayOperations.For(obj, forWrite: false);
try
{
return operations.OrderBy(x => x, comparer).ToArray();
Expand Down
2 changes: 1 addition & 1 deletion Jint/Native/TypedArray/TypedArrayConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ private static void InitializeTypedArrayFromList(JsTypedArray o, List<JsValue> v
/// </summary>
private static void InitializeTypedArrayFromArrayLike(JsTypedArray o, ObjectInstance arrayLike)
{
var operations = ArrayOperations.For(arrayLike);
var operations = ArrayOperations.For(arrayLike, forWrite: false);
var len = operations.GetLongLength();
o.AllocateTypedArrayBuffer(len);
for (uint k = 0; k < len; ++k)
Expand Down

0 comments on commit 53ab42a

Please sign in to comment.