Skip to content
Merged
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
21 changes: 21 additions & 0 deletions Jint.Tests/Runtime/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3868,4 +3868,25 @@ public void Tick()
}
}

[Fact]
public void ShouldBeAbleToWriteLengthOfListLike()
{
var list = new List<string> { "a", "b", "c" };
_engine.SetValue("list", list);

_engine.Evaluate("list.length = 2;");
list.Should().HaveCount(2);
list[0].Should().Be("a");
list[1].Should().Be("b");

_engine.Evaluate("list.length = 0;");
list.Should().BeEmpty();

var act = () => _engine.Evaluate("list.length = -1;");
act.Should().Throw<JavaScriptException>().WithMessage("Invalid array length");

_engine.Evaluate("list.length = 1;");
list.Should().HaveCount(1);
list[0].Should().Be(null);
}
}
56 changes: 51 additions & 5 deletions Jint/Runtime/Interop/ObjectWrapper.Specialized.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ protected ArrayLikeWrapper(

public abstract int Length { get; }

public override JsValue Get(JsValue property, JsValue receiver)
public sealed override JsValue Get(JsValue property, JsValue receiver)
{
if (property.IsInteger())
{
Expand All @@ -36,7 +36,7 @@ public override JsValue Get(JsValue property, JsValue receiver)
return base.Get(property, receiver);
}

public override bool HasProperty(JsValue property)
public sealed override bool HasProperty(JsValue property)
{
if (property.IsNumber())
{
Expand All @@ -54,7 +54,7 @@ public override bool HasProperty(JsValue property)
return base.HasProperty(property);
}

public override bool Delete(JsValue property)
public sealed override bool Delete(JsValue property)
{
if (!_engine.Options.Interop.AllowWrite)
{
Expand Down Expand Up @@ -84,9 +84,53 @@ public override bool Delete(JsValue property)
return base.Delete(property);
}


public abstract object? GetAt(int index);

public sealed override bool Set(JsValue property, JsValue value, JsValue receiver)
{
if (ReferenceEquals(receiver, this) && CommonProperties.Length.Equals(property))
{
if (!CanWrite)
{
return false;
}

if (value.IsInteger())
{
var length = value.AsInteger();
if (length < 0)
{
ExceptionHelper.ThrowRangeError(_engine.Realm, "Invalid array length");
}

if (length == Length)
{
return true;
}

if (length > Length)
{
EnsureCapacity(length);
}
else
{
// decrease the length, remove items
for (var i = Length - 1; i >= length; i--)
{
RemoveAt(i);
}
}
return true;
}

ExceptionHelper.ThrowTypeError(_engine.Realm, "Invalid array length");
}

return base.Set(property, value, receiver);
}

protected virtual bool CanWrite => _engine.Options.Interop.AllowWrite;

public void SetAt(int index, JsValue value)
{
if (_engine.Options.Interop.AllowWrite)
Expand Down Expand Up @@ -133,7 +177,7 @@ public virtual void EnsureCapacity(int capacity)
}
}

internal class ListWrapper : ArrayLikeWrapper
internal sealed class ListWrapper : ArrayLikeWrapper
{
private readonly IList? _list;

Expand Down Expand Up @@ -226,6 +270,8 @@ public ReadOnlyListWrapper(Engine engine, IReadOnlyList<T> target, Type type) :
return null;
}

protected override bool CanWrite => false;

public override void AddDefault() => ExceptionHelper.ThrowNotSupportedException();

protected override void DoSetAt(int index, object? value) => ExceptionHelper.ThrowNotSupportedException();
Expand Down