Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make JsSet public #1987

Merged
merged 4 commits into from
Oct 26, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

<ItemGroup>
<PackageReference Include="Acornima.Extras" />
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Flurl.Http.Signed" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MongoDB.Bson.signed" />
Expand Down
40 changes: 40 additions & 0 deletions Jint.Tests.PublicInterface/SetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using FluentAssertions;
using Jint.Native;

namespace Jint.Tests.Runtime;

public class SetTests
{
[Fact]
public void ConConstructSet()
{
var engine = new Engine();

var set = engine.Intrinsics.Set.Construct();
set.Add(42);
set.Add("foo");
set.Size.Should().Be(2);

set.Should().ContainInOrder(42, "foo");

set.Has(42).Should().BeTrue();
set.Has("foo").Should().BeTrue();
set.Has(24).Should().BeFalse();

engine.SetValue("s", set);
engine.Evaluate("s.size").Should().Be((JsNumber) 2);
engine.Evaluate("s.has(42)").Should().Be(JsBoolean.True);
engine.Evaluate("s.has('foo')").Should().Be(JsBoolean.True);
engine.Evaluate("s.has(24)").Should().Be(JsBoolean.False);

set.Remove(42).Should().BeTrue();
set.Has(42).Should().BeFalse();
engine.Evaluate("s.has(42)").Should().Be(JsBoolean.False);
engine.Evaluate("s.size").Should().Be((JsNumber) 1);

set.Clear();
set.Should().BeEmpty();
set.Size.Should().Be(0);
engine.Evaluate("s.size").Should().Be((JsNumber) 0);
}
}
21 changes: 12 additions & 9 deletions Jint/Native/JsSet.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Descriptors;

namespace Jint.Native;

internal sealed class JsSet : ObjectInstance
public sealed class JsSet : ObjectInstance, IEnumerable<JsValue>
{
internal readonly OrderedSet<JsValue> _set;

public JsSet(Engine engine) : this(engine, new OrderedSet<JsValue>(SameValueZeroComparer.Instance))
internal JsSet(Engine engine) : this(engine, new OrderedSet<JsValue>(SameValueZeroComparer.Instance))
{
}

public JsSet(Engine engine, OrderedSet<JsValue> set) : base(engine)
internal JsSet(Engine engine, OrderedSet<JsValue> set) : base(engine)
{
_set = set;
_prototype = _engine.Realm.Intrinsics.Set.PrototypeObject;
Expand Down Expand Up @@ -47,15 +48,13 @@ protected override bool TryGetProperty(JsValue property, [NotNullWhen(true)] out
return base.TryGetProperty(property, out descriptor);
}

internal void Add(JsValue value) => _set.Add(value);
public void Add(JsValue value) => _set.Add(value);

internal void Remove(JsValue value) => _set.Remove(value);
public void Clear() => _set.Clear();

internal void Clear() => _set.Clear();
public bool Has(JsValue key) => _set.Contains(key);

internal bool Has(JsValue key) => _set.Contains(key);

internal bool SetDelete(JsValue key) => _set.Remove(key);
public bool Remove(JsValue key) => _set.Remove(key);

internal void ForEach(ICallable callable, JsValue thisArg)
{
Expand All @@ -76,4 +75,8 @@ internal void ForEach(ICallable callable, JsValue thisArg)
internal ObjectInstance Entries() => _engine.Realm.Intrinsics.SetIteratorPrototype.ConstructEntryIterator(this);

internal ObjectInstance Values() => _engine.Realm.Intrinsics.SetIteratorPrototype.ConstructValueIterator(this);

public IEnumerator<JsValue> GetEnumerator() => _set.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
36 changes: 22 additions & 14 deletions Jint/Native/Set/SetConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Jint.Native.Set;

internal sealed class SetConstructor : Constructor
public sealed class SetConstructor : Constructor
{
private static readonly JsString _functionName = new("Set");

Expand Down Expand Up @@ -37,25 +37,14 @@ protected override void Initialize()
SetSymbols(symbols);
}

private static JsValue Species(JsValue thisObject, JsValue[] arguments)
{
return thisObject;
}
public JsSet Construct() => ConstructSet(this);

/// <summary>
/// https://tc39.es/ecma262/#sec-set-iterable
/// </summary>
public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_engine.Realm);
}

var set = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Set.PrototypeObject,
static (Engine engine, Realm _, object? _) => new JsSet(engine));
var set = ConstructSet(newTarget);

if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
{
Expand Down Expand Up @@ -92,4 +81,23 @@ public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)

return set;
}

private JsSet ConstructSet(JsValue newTarget)
{
if (newTarget.IsUndefined())
{
ExceptionHelper.ThrowTypeError(_engine.Realm);
}

var set = OrdinaryCreateFromConstructor(
newTarget,
static intrinsics => intrinsics.Set.PrototypeObject,
static (Engine engine, Realm _, object? _) => new JsSet(engine));
return set;
}

private static JsValue Species(JsValue thisObject, JsValue[] arguments)
{
return thisObject;
}
}
2 changes: 1 addition & 1 deletion Jint/Native/Set/SetPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private JsValue Clear(JsValue thisObject, JsValue[] arguments)
private JsBoolean Delete(JsValue thisObject, JsValue[] arguments)
{
var set = AssertSetInstance(thisObject);
return set.SetDelete(arguments.At(0))
return set.Remove(arguments.At(0))
? JsBoolean.True
: JsBoolean.False;
}
Expand Down
2 changes: 1 addition & 1 deletion Jint/Runtime/Intrinsics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ internal Intrinsics(Engine engine, Realm realm)
internal MapIteratorPrototype MapIteratorPrototype =>
_mapIteratorPrototype ??= new MapIteratorPrototype(_engine, _realm, IteratorPrototype);

internal SetConstructor Set =>
public SetConstructor Set =>
_set ??= new SetConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);

internal SetIteratorPrototype SetIteratorPrototype =>
Expand Down
8 changes: 7 additions & 1 deletion Jint/Runtime/OrderedSet.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections;

namespace Jint.Runtime;

internal sealed class OrderedSet<T>
internal sealed class OrderedSet<T> : IEnumerable<T>
{
internal List<T> _list;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generic version ( https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ordereddictionary-2 ) has been added NET 9, maybe not yet worth to add another TFM just for that..

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what was confusing me then. I knew the generic one was added to 9, but on the docs it said 2,3....9. I was pointing to the wrong one. It all makes sense and I agree not worth it if just for 9.

internal HashSet<T> _set;
Expand Down Expand Up @@ -61,4 +63,8 @@ public bool Remove(T item)
_set.Remove(item);
return _list.Remove(item);
}

public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}