IteratorInstance.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. using System.Globalization;
  2. using Jint.Native.Object;
  3. using Jint.Native.RegExp;
  4. using Jint.Runtime;
  5. using Jint.Runtime.Descriptors;
  6. namespace Jint.Native.Iterator
  7. {
  8. internal class IteratorInstance : ObjectInstance
  9. {
  10. private readonly IEnumerator<JsValue> _enumerable;
  11. protected IteratorInstance(Engine engine)
  12. : this(engine, Enumerable.Empty<JsValue>())
  13. {
  14. }
  15. public IteratorInstance(
  16. Engine engine,
  17. IEnumerable<JsValue> enumerable) : base(engine)
  18. {
  19. _enumerable = enumerable.GetEnumerator();
  20. _prototype = engine.Realm.Intrinsics.ArrayIteratorPrototype;
  21. }
  22. public override object ToObject()
  23. {
  24. ExceptionHelper.ThrowNotImplementedException();
  25. return null;
  26. }
  27. public virtual bool TryIteratorStep(out ObjectInstance nextItem)
  28. {
  29. if (_enumerable.MoveNext())
  30. {
  31. nextItem = new ValueIteratorPosition(_engine, _enumerable.Current);
  32. return true;
  33. }
  34. nextItem = ValueIteratorPosition.Done(_engine);
  35. return false;
  36. }
  37. public virtual void Close(CompletionType completion)
  38. {
  39. }
  40. /// <summary>
  41. /// https://tc39.es/ecma262/#sec-createiterresultobject
  42. /// </summary>
  43. private ObjectInstance CreateIterResultObject(JsValue value, bool done)
  44. {
  45. return new IteratorResult(_engine, value, done ? JsBoolean.True : JsBoolean.False);
  46. }
  47. internal sealed class KeyValueIteratorPosition : ObjectInstance
  48. {
  49. internal static ObjectInstance Done(Engine engine) => new KeyValueIteratorPosition(engine, null, null);
  50. public KeyValueIteratorPosition(Engine engine, JsValue? key, JsValue? value) : base(engine)
  51. {
  52. var done = ReferenceEquals(null, key) && ReferenceEquals(null, value);
  53. if (!done)
  54. {
  55. var arrayInstance = engine.Realm.Intrinsics.Array.ArrayCreate(2);
  56. arrayInstance.SetIndexValue(0, key!, false);
  57. arrayInstance.SetIndexValue(1, value!, false);
  58. SetProperty("value", new PropertyDescriptor(arrayInstance, PropertyFlag.AllForbidden));
  59. }
  60. SetProperty("done", done ? PropertyDescriptor.AllForbiddenDescriptor.BooleanTrue : PropertyDescriptor.AllForbiddenDescriptor.BooleanFalse);
  61. }
  62. }
  63. internal sealed class ValueIteratorPosition : ObjectInstance
  64. {
  65. internal static ObjectInstance Done(Engine engine, JsValue? value = null)
  66. => new ValueIteratorPosition(engine, value ?? Undefined, true);
  67. public ValueIteratorPosition(Engine engine, JsValue value, bool? done = null) : base(engine)
  68. {
  69. if (value is not null)
  70. {
  71. SetProperty("value", new PropertyDescriptor(value, PropertyFlag.AllForbidden));
  72. }
  73. SetProperty("done", new PropertyDescriptor(done ?? value is null, PropertyFlag.AllForbidden));
  74. }
  75. }
  76. internal sealed class ObjectIterator : IteratorInstance
  77. {
  78. private readonly ObjectInstance _target;
  79. private readonly ICallable _nextMethod;
  80. public ObjectIterator(ObjectInstance target) : base(target.Engine)
  81. {
  82. _target = target;
  83. if (target.Get(CommonProperties.Next, target) is not ICallable callable)
  84. {
  85. ExceptionHelper.ThrowTypeError(target.Engine.Realm);
  86. return;
  87. }
  88. _nextMethod = callable;
  89. }
  90. public override bool TryIteratorStep(out ObjectInstance result)
  91. {
  92. result = IteratorNext();
  93. var done = result.Get(CommonProperties.Done);
  94. if (!done.IsUndefined() && TypeConverter.ToBoolean(done))
  95. {
  96. return false;
  97. }
  98. return true;
  99. }
  100. private ObjectInstance IteratorNext()
  101. {
  102. var jsValue = _nextMethod.Call(_target, Arguments.Empty);
  103. var instance = jsValue as ObjectInstance;
  104. if (instance is null)
  105. {
  106. ExceptionHelper.ThrowTypeError(_target.Engine.Realm, "Iterator result " + jsValue + " is not an object");
  107. }
  108. return instance;
  109. }
  110. public override void Close(CompletionType completion)
  111. {
  112. if (!_target.TryGetValue(CommonProperties.Return, out var func)
  113. || func.IsNullOrUndefined())
  114. {
  115. return;
  116. }
  117. var callable = func as ICallable;
  118. if (callable is null)
  119. {
  120. ExceptionHelper.ThrowTypeError(_target.Engine.Realm, func + " is not a function");
  121. }
  122. var innerResult = Undefined;
  123. try
  124. {
  125. innerResult = callable.Call(_target, Arguments.Empty);
  126. }
  127. catch
  128. {
  129. if (completion != CompletionType.Throw)
  130. {
  131. throw;
  132. }
  133. }
  134. if (completion != CompletionType.Throw && !innerResult.IsObject())
  135. {
  136. ExceptionHelper.ThrowTypeError(_target.Engine.Realm, "Iterator returned non-object");
  137. }
  138. }
  139. }
  140. internal sealed class StringIterator : IteratorInstance
  141. {
  142. private readonly TextElementEnumerator _iterator;
  143. public StringIterator(Engine engine, string str) : base(engine)
  144. {
  145. _iterator = StringInfo.GetTextElementEnumerator(str);
  146. }
  147. public override bool TryIteratorStep(out ObjectInstance nextItem)
  148. {
  149. if (_iterator.MoveNext())
  150. {
  151. nextItem = new ValueIteratorPosition(_engine, (string) _iterator.Current);
  152. return true;
  153. }
  154. nextItem = KeyValueIteratorPosition.Done(_engine);
  155. return false;
  156. }
  157. }
  158. internal sealed class RegExpStringIterator : IteratorInstance
  159. {
  160. private readonly RegExpInstance _iteratingRegExp;
  161. private readonly string _s;
  162. private readonly bool _global;
  163. private readonly bool _unicode;
  164. private bool _done;
  165. public RegExpStringIterator(Engine engine, ObjectInstance iteratingRegExp, string iteratedString, bool global, bool unicode) : base(engine)
  166. {
  167. var r = iteratingRegExp as RegExpInstance;
  168. if (r is null)
  169. {
  170. ExceptionHelper.ThrowTypeError(engine.Realm);
  171. }
  172. _iteratingRegExp = r;
  173. _s = iteratedString;
  174. _global = global;
  175. _unicode = unicode;
  176. }
  177. public override bool TryIteratorStep(out ObjectInstance nextItem)
  178. {
  179. if (_done)
  180. {
  181. nextItem = CreateIterResultObject(Undefined, true);
  182. return false;
  183. }
  184. var match = RegExpPrototype.RegExpExec(_iteratingRegExp, _s);
  185. if (match.IsNull())
  186. {
  187. _done = true;
  188. nextItem = CreateIterResultObject(Undefined, true);
  189. return false;
  190. }
  191. if (_global)
  192. {
  193. var macthStr = TypeConverter.ToString(match.Get(JsString.NumberZeroString));
  194. if (macthStr == "")
  195. {
  196. var thisIndex = TypeConverter.ToLength(_iteratingRegExp.Get(RegExpInstance.PropertyLastIndex));
  197. var nextIndex = thisIndex + 1;
  198. _iteratingRegExp.Set(RegExpInstance.PropertyLastIndex, nextIndex, true);
  199. }
  200. }
  201. else
  202. {
  203. _done = true;
  204. }
  205. nextItem = CreateIterResultObject(match, false);
  206. return true;
  207. }
  208. }
  209. }
  210. }