SetPrototype.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. #pragma warning disable CA1859 // Use concrete types when possible for improved performance -- most of prototype methods return JsValue
  2. using Jint.Collections;
  3. using Jint.Native.Object;
  4. using Jint.Native.Symbol;
  5. using Jint.Runtime;
  6. using Jint.Runtime.Descriptors;
  7. using Jint.Runtime.Interop;
  8. namespace Jint.Native.Set;
  9. /// <summary>
  10. /// https://www.ecma-international.org/ecma-262/6.0/#sec-set-objects
  11. /// </summary>
  12. internal sealed class SetPrototype : Prototype
  13. {
  14. private readonly SetConstructor _constructor;
  15. internal SetPrototype(
  16. Engine engine,
  17. Realm realm,
  18. SetConstructor setConstructor,
  19. ObjectPrototype objectPrototype) : base(engine, realm)
  20. {
  21. _prototype = objectPrototype;
  22. _constructor = setConstructor;
  23. }
  24. protected override void Initialize()
  25. {
  26. var properties = new PropertyDictionary(12, checkExistingKeys: false)
  27. {
  28. ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
  29. ["constructor"] = new PropertyDescriptor(_constructor, PropertyFlag.NonEnumerable),
  30. ["add"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  31. ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  32. ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  33. ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  34. ["forEach"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "forEach", ForEach, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  35. ["has"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "has", Has, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  36. ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  37. ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable),
  38. ["size"] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get size", Size, 0, PropertyFlag.Configurable), set: null, PropertyFlag.Configurable),
  39. ["union"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "union", Union, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)
  40. };
  41. SetProperties(properties);
  42. var symbols = new SymbolDictionary(2)
  43. {
  44. [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
  45. [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Set", false, false, true)
  46. };
  47. SetSymbols(symbols);
  48. }
  49. private JsValue Size(JsValue thisObject, JsValue[] arguments)
  50. {
  51. AssertSetInstance(thisObject);
  52. return JsNumber.Create(0);
  53. }
  54. private JsValue Add(JsValue thisObject, JsValue[] arguments)
  55. {
  56. var set = AssertSetInstance(thisObject);
  57. var value = arguments.At(0);
  58. if (value is JsNumber number && number.IsNegativeZero())
  59. {
  60. value = JsNumber.PositiveZero;
  61. }
  62. set.Add(value);
  63. return thisObject;
  64. }
  65. private JsValue Clear(JsValue thisObject, JsValue[] arguments)
  66. {
  67. var set = AssertSetInstance(thisObject);
  68. set.Clear();
  69. return Undefined;
  70. }
  71. private JsValue Delete(JsValue thisObject, JsValue[] arguments)
  72. {
  73. var set = AssertSetInstance(thisObject);
  74. return set.SetDelete(arguments.At(0))
  75. ? JsBoolean.True
  76. : JsBoolean.False;
  77. }
  78. private JsValue Has(JsValue thisObject, JsValue[] arguments)
  79. {
  80. var set = AssertSetInstance(thisObject);
  81. return set.Has(arguments.At(0))
  82. ? JsBoolean.True
  83. : JsBoolean.False;
  84. }
  85. private JsValue Entries(JsValue thisObject, JsValue[] arguments)
  86. {
  87. var set = AssertSetInstance(thisObject);
  88. return set.Entries();
  89. }
  90. private JsValue ForEach(JsValue thisObject, JsValue[] arguments)
  91. {
  92. var callbackfn = arguments.At(0);
  93. var thisArg = arguments.At(1);
  94. var set = AssertSetInstance(thisObject);
  95. var callable = GetCallable(callbackfn);
  96. set.ForEach(callable, thisArg);
  97. return Undefined;
  98. }
  99. private JsValue Union(JsValue thisObject, JsValue[] arguments)
  100. {
  101. var set = AssertSetInstance(thisObject);
  102. var other = arguments.At(0);
  103. var otherRec = GetSetRecord(other);
  104. var keysIter = otherRec.Set.GetIteratorFromMethod(_realm, otherRec.Keys);
  105. var resultSetData = set._set.Clone();
  106. while (keysIter.TryIteratorStep(out var next))
  107. {
  108. var nextValue = next.Get(CommonProperties.Value);
  109. if (nextValue == JsNumber.NegativeZero)
  110. {
  111. nextValue = JsNumber.PositiveZero;
  112. }
  113. resultSetData.Add(nextValue);
  114. }
  115. var result = new JsSet(_engine, resultSetData)
  116. {
  117. _prototype = _engine.Realm.Intrinsics.Set.PrototypeObject
  118. };
  119. return result;
  120. }
  121. private readonly record struct SetRecord(JsValue Set, double Size, ICallable Has, ICallable Keys);
  122. private SetRecord GetSetRecord(JsValue obj)
  123. {
  124. if (obj is not ObjectInstance)
  125. {
  126. ExceptionHelper.ThrowTypeError(_realm);
  127. }
  128. var rawSize = obj.Get("size");
  129. var numSize = TypeConverter.ToNumber(rawSize);
  130. if (double.IsNaN(numSize))
  131. {
  132. ExceptionHelper.ThrowTypeError(_realm);
  133. }
  134. var intSize = TypeConverter.ToIntegerOrInfinity(numSize);
  135. if (intSize < 0)
  136. {
  137. ExceptionHelper.ThrowRangeError(_realm);
  138. }
  139. var has = obj.Get(CommonProperties.Has);
  140. if (!has.IsCallable)
  141. {
  142. ExceptionHelper.ThrowTypeError(_realm);
  143. }
  144. var keys = obj.Get(CommonProperties.Keys);
  145. if (!keys.IsCallable)
  146. {
  147. ExceptionHelper.ThrowTypeError(_realm);
  148. }
  149. return new SetRecord(Set: obj, Size: intSize, Has: (ICallable) has, Keys: (ICallable) keys);
  150. }
  151. private ObjectInstance Values(JsValue thisObject, JsValue[] arguments)
  152. {
  153. var set = AssertSetInstance(thisObject);
  154. return set.Values();
  155. }
  156. private JsSet AssertSetInstance(JsValue thisObject)
  157. {
  158. if (thisObject is JsSet set)
  159. {
  160. return set;
  161. }
  162. ExceptionHelper.ThrowTypeError(_realm, "object must be a Set");
  163. return default;
  164. }
  165. }