IteratorInstance.cs 7.5 KB

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