using System.Globalization; using Jint.Native.Generator; using Jint.Native.Object; using Jint.Native.RegExp; using Jint.Runtime; namespace Jint.Native.Iterator { internal abstract class IteratorInstance : ObjectInstance { protected IteratorInstance(Engine engine) : base(engine) { _prototype = engine.Realm.Intrinsics.ArrayIteratorPrototype; } public override object ToObject() { ExceptionHelper.ThrowNotImplementedException(); return null; } public abstract bool TryIteratorStep(out ObjectInstance nextItem); public virtual void Close(CompletionType completion) { } /// /// https://tc39.es/ecma262/#sec-createiterresultobject /// private IteratorResult CreateIterResultObject(JsValue value, bool done) { return new IteratorResult(_engine, value, JsBoolean.Create(done)); } internal sealed class ObjectIterator : IteratorInstance { private readonly ObjectInstance _target; private readonly ICallable _nextMethod; public ObjectIterator(ObjectInstance target) : base(target.Engine) { _target = target; if (target.Get(CommonProperties.Next) is not ICallable callable) { ExceptionHelper.ThrowTypeError(target.Engine.Realm); return; } _nextMethod = callable; } public override bool TryIteratorStep(out ObjectInstance result) { result = IteratorNext(); var done = result.Get(CommonProperties.Done); if (!done.IsUndefined() && TypeConverter.ToBoolean(done)) { return false; } return true; } private ObjectInstance IteratorNext() { var jsValue = _nextMethod.Call(_target, Arguments.Empty); var instance = jsValue as ObjectInstance; if (instance is null) { ExceptionHelper.ThrowTypeError(_target.Engine.Realm, $"Iterator result {jsValue} is not an object"); } return instance; } public override void Close(CompletionType completion) { if (!_target.TryGetValue(CommonProperties.Return, out var func) || func.IsNullOrUndefined()) { return; } var callable = func as ICallable; if (callable is null) { ExceptionHelper.ThrowTypeError(_target.Engine.Realm, func + " is not a function"); } var innerResult = Undefined; try { innerResult = callable.Call(_target, Arguments.Empty); } catch { if (completion != CompletionType.Throw) { throw; } } if (completion != CompletionType.Throw && !innerResult.IsObject()) { ExceptionHelper.ThrowTypeError(_target.Engine.Realm, "Iterator returned non-object"); } } } internal sealed class StringIterator : IteratorInstance { private readonly TextElementEnumerator _iterator; public StringIterator(Engine engine, string str) : base(engine) { _iterator = StringInfo.GetTextElementEnumerator(str); } public override bool TryIteratorStep(out ObjectInstance nextItem) { if (_iterator.MoveNext()) { nextItem = IteratorResult.CreateValueIteratorPosition(_engine, (string) _iterator.Current); return true; } nextItem = IteratorResult.CreateKeyValueIteratorPosition(_engine); return false; } } internal sealed class RegExpStringIterator : IteratorInstance { private readonly JsRegExp _iteratingRegExp; private readonly string _s; private readonly bool _global; private readonly bool _unicode; private bool _done; public RegExpStringIterator(Engine engine, ObjectInstance iteratingRegExp, string iteratedString, bool global, bool unicode) : base(engine) { var r = iteratingRegExp as JsRegExp; if (r is null) { ExceptionHelper.ThrowTypeError(engine.Realm); } _iteratingRegExp = r; _s = iteratedString; _global = global; _unicode = unicode; } public override bool TryIteratorStep(out ObjectInstance nextItem) { if (_done) { nextItem = CreateIterResultObject(Undefined, true); return false; } var match = RegExpPrototype.RegExpExec(_iteratingRegExp, _s); if (match.IsNull()) { _done = true; nextItem = CreateIterResultObject(Undefined, true); return false; } if (_global) { var macthStr = TypeConverter.ToString(match.Get(JsString.NumberZeroString)); if (macthStr == "") { var thisIndex = TypeConverter.ToLength(_iteratingRegExp.Get(JsRegExp.PropertyLastIndex)); var nextIndex = thisIndex + 1; _iteratingRegExp.Set(JsRegExp.PropertyLastIndex, nextIndex, true); } } else { _done = true; } nextItem = CreateIterResultObject(match, false); return true; } } internal sealed class EnumerableIterator : IteratorInstance { private readonly IEnumerator _enumerable; public EnumerableIterator(Engine engine, IEnumerable obj) : base(engine) { _enumerable = obj.GetEnumerator(); } public override bool TryIteratorStep(out ObjectInstance nextItem) { if (_enumerable.MoveNext()) { nextItem = IteratorResult.CreateValueIteratorPosition(_engine, _enumerable.Current); return true; } nextItem = IteratorResult.CreateValueIteratorPosition(_engine, done: JsBoolean.True); return false; } } internal sealed class GeneratorIterator : IteratorInstance { private readonly GeneratorInstance _generator; public GeneratorIterator(Engine engine, GeneratorInstance generator) : base(engine) { _generator = generator; } public override bool TryIteratorStep(out ObjectInstance nextItem) { nextItem = IteratorResult.CreateValueIteratorPosition(_engine, done: JsBoolean.True); return false; } } } }