Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 13 additions & 67 deletions src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@
{
private const long MaxArrayLength = 9007199254740991L; // 2^53 - 1

private static double ToLengthValue(object? candidate)
{
var num = JsOps.ToNumber(candidate);
if (double.IsNaN(num) || double.IsInfinity(num) || num <= 0)
{
return 0;
}

var truncated = Math.Floor(num);
return Math.Min(truncated, (double)MaxArrayLength); // 2^53 - 1
}

public static void AddArrayMethods(IJsPropertyAccessor array, RealmState? realm = null,
JsObject? prototypeOverride = null)
{
Expand Down Expand Up @@ -109,72 +121,6 @@
});
return;

static double ToLengthValue(object? candidate)
{
var num = JsOps.ToNumber(candidate);
if (double.IsNaN(num) || double.IsInfinity(num) || num <= 0)
{
return 0;
}

var truncated = Math.Floor(num);
return Math.Min(truncated, (double)MaxArrayLength); // 2^53 - 1
}

static object CreateArrayIterator(object? thisValue, IJsPropertyAccessor accessor,
Func<uint, object?> projector)
{
var iterator = new JsObject();
var iteratorSymbol = TypedAstSymbol.For("Symbol.iterator");
var iteratorKey = $"@@symbol:{iteratorSymbol.GetHashCode()}";

uint index = 0;
var exhausted = false;

iterator.SetHostedProperty("next", Next);

iterator.SetHostedProperty(iteratorKey, ReturnIterator);
return iterator;

object? Next(object? _, IReadOnlyList<object?> __)
{
if (exhausted)
{
var doneResult = new JsObject();
doneResult.SetProperty("value", Symbol.Undefined);
doneResult.SetProperty("done", true);
return doneResult;
}

uint length = 0;
if (accessor.TryGetProperty("length", out var lengthValue))
{
length = (uint)ToLengthValue(lengthValue);
}

var result = new JsObject();
if (index < length)
{
result.SetProperty("value", projector(index));
result.SetProperty("done", false);
index++;
}
else
{
result.SetProperty("value", Symbol.Undefined);
result.SetProperty("done", true);
exhausted = true;
}

return result;
}

object? ReturnIterator(object? _, IReadOnlyList<object?> __)
{
return iterator;
}
}

HostFunction DefineArrayIteratorFunction(string name,
Func<IJsPropertyAccessor, object?, Func<uint, object?>> projectorFactory)
{
Expand All @@ -197,7 +143,7 @@
}

var projector = projectorFactory(accessor, thisValue);
return CreateArrayIterator(thisValue, accessor, projector);
return CreateArrayIterator(accessor, projector);
}) { IsConstructor = false };

fn.DefineProperty("name",
Expand Down Expand Up @@ -873,7 +819,7 @@
var toKey = ToIndexString(resultIndex);
if (TryGetExistingElement(spreadAccessor, fromKey, out var value))
{
result.SetProperty(toKey, value);

Check warning on line 822 in src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.
}
else if (result is not null)
{
Expand All @@ -890,11 +836,11 @@
throw ThrowTypeError("Array length exceeds 2^53 - 1", realm: realm);
}

result.SetProperty(ToIndexString(resultIndex++), sourceValue);

Check warning on line 839 in src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Dereference of a possibly null reference.
}
}

SetArrayLikeLength(result, resultIndex);

Check warning on line 843 in src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference argument for parameter 'target' in 'void StandardLibrary.SetArrayLikeLength(IJsPropertyAccessor target, long length)'.
return result;
}

Expand Down Expand Up @@ -1262,7 +1208,7 @@
result?.Delete(key);
}

SetArrayLikeLength(result, length);

Check warning on line 1211 in src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference argument for parameter 'target' in 'void StandardLibrary.SetArrayLikeLength(IJsPropertyAccessor target, long length)'.
return result;
}

Expand Down Expand Up @@ -2498,7 +2444,7 @@
private static IJsObjectLike CreateArrayLikeReceiverForConstructor(IJsCallable constructor, RealmState? realm,
long length)
{
var proto = ResolveConstructPrototype(constructor, constructor, realm);

Check warning on line 2447 in src/Asynkron.JsEngine/StdLib/StandardLibrary.Array.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference argument for parameter 'realmState' in 'JsObject? StandardLibrary.ResolveConstructPrototype(IJsCallable newTarget, IJsCallable target, RealmState realmState)'.
IJsObjectLike receiver;

if (constructor is HostFunction hostFunction && realm?.ArrayConstructor is not null &&
Expand Down
77 changes: 52 additions & 25 deletions src/Asynkron.JsEngine/StdLib/StandardLibrary.IteratorHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static HostFunction CreateGetAsyncIteratorHelper(JsEngine engine)

if (iterable is JsArray jsArray)
{
var iteratorObj = CreateArrayIterator(jsArray);
var iteratorObj = CreateArrayIterator(jsArray, idx => jsArray.GetElement((int)idx));
engine.WriteAsyncIteratorTrace(
$"getAsyncIterator: branch=array length={jsArray.Length}");
return iteratorObj;
Expand Down Expand Up @@ -104,37 +104,64 @@ static JsObject CreateStringIterator(string str)
}
}

static JsObject CreateArrayIterator(JsArray array)
static bool HasCallableNext(object? candidate)
{
var iteratorObj = new JsObject();
var index = 0;
iteratorObj.SetHostedProperty("next", Next);
return iteratorObj;
return candidate is JsObject obj &&
obj.TryGetProperty("next", out var nextProp) &&
nextProp is IJsCallable;
}
}
}

object? Next(IReadOnlyList<object?> _)
{
var result = new JsObject();
if (index < array.Length)
{
result.SetProperty("value", array.GetElement(index));
result.SetProperty("done", false);
index++;
}
else
{
result.SetProperty("done", true);
}
private static JsObject CreateArrayIterator(IJsPropertyAccessor accessor, Func<uint, object?> projector)
{
var iterator = new JsObject();
var iteratorSymbol = TypedAstSymbol.For("Symbol.iterator");
var iteratorKey = $"@@symbol:{iteratorSymbol.GetHashCode()}";

return result;
}
uint index = 0;
var exhausted = false;

iterator.SetHostedProperty("next", Next);
iterator.SetHostedProperty(iteratorKey, ReturnIterator);
return iterator;

object? Next(object? _, IReadOnlyList<object?> __)
{
if (exhausted)
{
var doneResult = new JsObject();
doneResult.SetProperty("value", Symbol.Undefined);
doneResult.SetProperty("done", true);
return doneResult;
}

static bool HasCallableNext(object? candidate)
uint length = 0;
if (accessor.TryGetProperty("length", out var lengthValue))
{
return candidate is JsObject obj &&
obj.TryGetProperty("next", out var nextProp) &&
nextProp is IJsCallable;
length = (uint)ToLengthValue(lengthValue);
}

var result = new JsObject();
if (index < length)
{
result.SetProperty("value", projector(index));
result.SetProperty("done", false);
index++;
}
else
{
result.SetProperty("value", Symbol.Undefined);
result.SetProperty("done", true);
exhausted = true;
}

return result;
}

object? ReturnIterator(object? _, IReadOnlyList<object?> __)
{
return iterator;
}
}

Expand Down
Loading