|
@@ -1,5 +1,3 @@
|
|
|
-#pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
|
|
|
-
|
|
|
using Jint.Collections;
|
|
|
using Jint.Native.Object;
|
|
|
using Jint.Native.Symbol;
|
|
@@ -30,30 +28,36 @@ internal sealed class SetPrototype : Prototype
|
|
|
{
|
|
|
var properties = new PropertyDictionary(12, checkExistingKeys: false)
|
|
|
{
|
|
|
- ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
|
|
|
- ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
|
|
|
- ["add"] = new PropertyDescriptor(new ClrFunction(Engine, "add", Add, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["clear"] = new PropertyDescriptor(new ClrFunction(Engine, "clear", Clear, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["delete"] = new PropertyDescriptor(new ClrFunction(Engine, "delete", Delete, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["entries"] = new PropertyDescriptor(new ClrFunction(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["forEach"] = new PropertyDescriptor(new ClrFunction(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["has"] = new PropertyDescriptor(new ClrFunction(Engine, "has", Has, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["keys"] = new PropertyDescriptor(new ClrFunction(Engine, "keys", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
- ["values"] = new PropertyDescriptor(new ClrFunction(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["length"] = new(0, PropertyFlag.Configurable),
|
|
|
+ ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
|
|
|
+ ["add"] = new(new ClrFunction(Engine, "add", Add, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["clear"] = new(new ClrFunction(Engine, "clear", Clear, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["delete"] = new(new ClrFunction(Engine, "delete", Delete, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["difference"] = new(new ClrFunction(Engine, "difference", Difference, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["entries"] = new(new ClrFunction(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["forEach"] = new(new ClrFunction(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["has"] = new(new ClrFunction(Engine, "has", Has, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["intersection"] = new(new ClrFunction(Engine, "intersection", Intersection, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["isDisjointFrom"] = new(new ClrFunction(Engine, "isDisjointFrom", IsDisjointFrom, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["isSubsetOf"] = new(new ClrFunction(Engine, "isSubsetOf", IsSubsetOf, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["isSupersetOf"] = new(new ClrFunction(Engine, "isSupersetOf", IsSupersetOf, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["keys"] = new(new ClrFunction(Engine, "keys", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["values"] = new(new ClrFunction(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
["size"] = new GetSetPropertyDescriptor(get: new ClrFunction(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable),
|
|
|
- ["union"] = new PropertyDescriptor(new ClrFunction(Engine, "union", Union, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)
|
|
|
+ ["symmetricDifference"] = new(new ClrFunction(Engine, "symmetricDifference", SymmetricDifference, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
|
|
|
+ ["union"] = new(new ClrFunction(Engine, "union", Union, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)
|
|
|
};
|
|
|
SetProperties(properties);
|
|
|
|
|
|
var symbols = new SymbolDictionary(2)
|
|
|
{
|
|
|
- [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunction(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
|
|
|
- [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Set", false, false, true)
|
|
|
+ [GlobalSymbolRegistry.Iterator] = new(new ClrFunction(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
|
|
|
+ [GlobalSymbolRegistry.ToStringTag] = new("Set", false, false, true)
|
|
|
};
|
|
|
SetSymbols(symbols);
|
|
|
}
|
|
|
|
|
|
- private JsValue Size(JsValue thisObject, JsValue[] arguments)
|
|
|
+ private JsNumber Size(JsValue thisObject, JsValue[] arguments)
|
|
|
{
|
|
|
AssertSetInstance(thisObject);
|
|
|
return JsNumber.Create(0);
|
|
@@ -78,7 +82,7 @@ internal sealed class SetPrototype : Prototype
|
|
|
return Undefined;
|
|
|
}
|
|
|
|
|
|
- private JsValue Delete(JsValue thisObject, JsValue[] arguments)
|
|
|
+ private JsBoolean Delete(JsValue thisObject, JsValue[] arguments)
|
|
|
{
|
|
|
var set = AssertSetInstance(thisObject);
|
|
|
return set.SetDelete(arguments.At(0))
|
|
@@ -86,7 +90,325 @@ internal sealed class SetPrototype : Prototype
|
|
|
: JsBoolean.False;
|
|
|
}
|
|
|
|
|
|
- private JsValue Has(JsValue thisObject, JsValue[] arguments)
|
|
|
+ private JsSet Difference(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+ var resultSetData = new JsSet(_engine, new OrderedSet<JsValue>(set._set._set));
|
|
|
+
|
|
|
+ if (set.Size <= otherRec.Size)
|
|
|
+ {
|
|
|
+
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ var result = new HashSet<JsValue>(set._set._set, SameValueZeroComparer.Instance);
|
|
|
+ result.ExceptWith(otherSet._set._set);
|
|
|
+ return new JsSet(_engine, new OrderedSet<JsValue>(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ var index = 0;
|
|
|
+ var args = new JsValue[1];
|
|
|
+ while (index < set.Size)
|
|
|
+ {
|
|
|
+ var e = resultSetData[index];
|
|
|
+ if (e is not null)
|
|
|
+ {
|
|
|
+ args[0] = e;
|
|
|
+ var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args));
|
|
|
+ if (inOther)
|
|
|
+ {
|
|
|
+ resultSetData.Remove(e);
|
|
|
+ index--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultSetData;
|
|
|
+ }
|
|
|
+
|
|
|
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (!keysIter.TryIteratorStep(out var next))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var nextValue = next.Get(CommonProperties.Value);
|
|
|
+ if (nextValue == JsNumber.NegativeZero)
|
|
|
+ {
|
|
|
+ nextValue = JsNumber.PositiveZero;
|
|
|
+ }
|
|
|
+
|
|
|
+ resultSetData.Remove(nextValue);
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultSetData;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JsBoolean IsDisjointFrom(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+ var resultSetData = new JsSet(_engine, new OrderedSet<JsValue>(set._set._set));
|
|
|
+
|
|
|
+ if (set.Size <= otherRec.Size)
|
|
|
+ {
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ return set._set._set.Overlaps(otherSet._set._set) ? JsBoolean.False : JsBoolean.True;
|
|
|
+ }
|
|
|
+
|
|
|
+ var index = 0;
|
|
|
+ var args = new JsValue[1];
|
|
|
+ while (index < set.Size)
|
|
|
+ {
|
|
|
+ var e = resultSetData[index];
|
|
|
+ index++;
|
|
|
+ if (e is not null)
|
|
|
+ {
|
|
|
+ args[0] = e;
|
|
|
+ var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args));
|
|
|
+ if (inOther)
|
|
|
+ {
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return JsBoolean.True;
|
|
|
+ }
|
|
|
+
|
|
|
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (!keysIter.TryIteratorStep(out var next))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var nextValue = next.Get(CommonProperties.Value);
|
|
|
+ if (set.Has(nextValue))
|
|
|
+ {
|
|
|
+ keysIter.Close(CompletionType.Normal);
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return JsBoolean.True;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private JsSet Intersection(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+ var resultSetData = new JsSet(_engine);
|
|
|
+ var thisSize = set.Size;
|
|
|
+
|
|
|
+ if (thisSize <= otherRec.Size)
|
|
|
+ {
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ var result = new HashSet<JsValue>(set._set._set, SameValueZeroComparer.Instance);
|
|
|
+ result.IntersectWith(otherSet._set._set);
|
|
|
+ return new JsSet(_engine, new OrderedSet<JsValue>(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ var index = 0;
|
|
|
+ var args = new JsValue[1];
|
|
|
+ while (index < thisSize)
|
|
|
+ {
|
|
|
+ var e = set[index];
|
|
|
+ index++;
|
|
|
+ if (e is not null)
|
|
|
+ {
|
|
|
+ args[0] = e;
|
|
|
+ var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args));
|
|
|
+ if (inOther)
|
|
|
+ {
|
|
|
+ var alreadyInResult = resultSetData.Has(e);
|
|
|
+ if (!alreadyInResult)
|
|
|
+ {
|
|
|
+ resultSetData.Add(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ thisSize = set.Size;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultSetData;
|
|
|
+ }
|
|
|
+
|
|
|
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (!keysIter.TryIteratorStep(out var next))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var nextValue = next.Get(CommonProperties.Value);
|
|
|
+ if (nextValue == JsNumber.NegativeZero)
|
|
|
+ {
|
|
|
+ nextValue = JsNumber.PositiveZero;
|
|
|
+ }
|
|
|
+
|
|
|
+ var alreadyInResult = resultSetData.Has(nextValue);
|
|
|
+ var inThis = set.Has(nextValue);
|
|
|
+ if (!alreadyInResult && inThis)
|
|
|
+ {
|
|
|
+ resultSetData.Add(nextValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultSetData;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JsSet SymmetricDifference(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ var result = new HashSet<JsValue>(set._set._set, SameValueZeroComparer.Instance);
|
|
|
+ result.SymmetricExceptWith(otherSet._set._set);
|
|
|
+ return new JsSet(_engine, new OrderedSet<JsValue>(result));
|
|
|
+ }
|
|
|
+
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
|
|
|
+ var resultSetData = new JsSet(_engine, new OrderedSet<JsValue>(set._set._set));
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (!keysIter.TryIteratorStep(out var next))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var nextValue = next.Get(CommonProperties.Value);
|
|
|
+ if (nextValue == JsNumber.NegativeZero)
|
|
|
+ {
|
|
|
+ nextValue = JsNumber.PositiveZero;
|
|
|
+ }
|
|
|
+
|
|
|
+ var inResult = resultSetData.Has(nextValue);
|
|
|
+ if (set.Has(nextValue))
|
|
|
+ {
|
|
|
+ if (inResult)
|
|
|
+ {
|
|
|
+ resultSetData.Remove(nextValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (!inResult)
|
|
|
+ {
|
|
|
+ resultSetData.Add(nextValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return resultSetData;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JsBoolean IsSubsetOf(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ return set._set._set.IsSubsetOf(otherSet._set._set) ? JsBoolean.True : JsBoolean.False;
|
|
|
+ }
|
|
|
+
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+ var resultSetData = new JsSet(_engine, new OrderedSet<JsValue>(set._set._set));
|
|
|
+ var thisSize = set.Size;
|
|
|
+
|
|
|
+ if (thisSize > otherRec.Size)
|
|
|
+ {
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (thisSize <= otherRec.Size)
|
|
|
+ {
|
|
|
+ var index = 0;
|
|
|
+ var args = new JsValue[1];
|
|
|
+ while (index < thisSize)
|
|
|
+ {
|
|
|
+ var e = resultSetData[index];
|
|
|
+ if (e is not null)
|
|
|
+ {
|
|
|
+ args[0] = e;
|
|
|
+ var inOther = TypeConverter.ToBoolean(otherRec.Has.Call(otherRec.Set, args));
|
|
|
+ if (!inOther)
|
|
|
+ {
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ thisSize = set.Size;
|
|
|
+ index++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return JsBoolean.True;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JsBoolean IsSupersetOf(JsValue thisObject, JsValue[] arguments)
|
|
|
+ {
|
|
|
+ var set = AssertSetInstance(thisObject);
|
|
|
+ var other = arguments.At(0);
|
|
|
+
|
|
|
+ if (other is JsSet otherSet)
|
|
|
+ {
|
|
|
+ // fast path
|
|
|
+ var result = new HashSet<JsValue>(set._set._set, SameValueZeroComparer.Instance);
|
|
|
+ return result.IsSupersetOf(otherSet._set._set) ? JsBoolean.True : JsBoolean.False;
|
|
|
+ }
|
|
|
+
|
|
|
+ var thisSize = set.Size;
|
|
|
+ var otherRec = GetSetRecord(other);
|
|
|
+
|
|
|
+ if (thisSize < otherRec.Size)
|
|
|
+ {
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+
|
|
|
+ var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (!keysIter.TryIteratorStep(out var next))
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var nextValue = next.Get(CommonProperties.Value);
|
|
|
+ if (!set.Has(nextValue))
|
|
|
+ {
|
|
|
+ keysIter.Close(CompletionType.Normal);
|
|
|
+ return JsBoolean.False;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return JsBoolean.True;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private JsBoolean Has(JsValue thisObject, JsValue[] arguments)
|
|
|
{
|
|
|
var set = AssertSetInstance(thisObject);
|
|
|
return set.Has(arguments.At(0))
|
|
@@ -94,7 +416,7 @@ internal sealed class SetPrototype : Prototype
|
|
|
: JsBoolean.False;
|
|
|
}
|
|
|
|
|
|
- private JsValue Entries(JsValue thisObject, JsValue[] arguments)
|
|
|
+ private ObjectInstance Entries(JsValue thisObject, JsValue[] arguments)
|
|
|
{
|
|
|
var set = AssertSetInstance(thisObject);
|
|
|
return set.Entries();
|
|
@@ -113,7 +435,7 @@ internal sealed class SetPrototype : Prototype
|
|
|
return Undefined;
|
|
|
}
|
|
|
|
|
|
- private JsValue Union(JsValue thisObject, JsValue[] arguments)
|
|
|
+ private JsSet Union(JsValue thisObject, JsValue[] arguments)
|
|
|
{
|
|
|
var set = AssertSetInstance(thisObject);
|
|
|
var other = arguments.At(0);
|
|
@@ -130,12 +452,8 @@ internal sealed class SetPrototype : Prototype
|
|
|
resultSetData.Add(nextValue);
|
|
|
}
|
|
|
|
|
|
- var result = new JsSet(_engine, resultSetData)
|
|
|
- {
|
|
|
- _prototype = _engine.Realm.Intrinsics.Set.PrototypeObject
|
|
|
- };
|
|
|
+ var result = new JsSet(_engine, resultSetData);
|
|
|
return result;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
private readonly record struct SetRecord(JsValue Set, double Size, ICallable Has, ICallable Keys);
|
|
@@ -147,7 +465,7 @@ internal sealed class SetPrototype : Prototype
|
|
|
ExceptionHelper.ThrowTypeError(_realm);
|
|
|
}
|
|
|
|
|
|
- var rawSize = obj.Get("size");
|
|
|
+ var rawSize = obj.Get(CommonProperties.Size);
|
|
|
var numSize = TypeConverter.ToNumber(rawSize);
|
|
|
if (double.IsNaN(numSize))
|
|
|
{
|