IteratorInstance.cs 6.8 KB

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