using Jint.Native.Iterator;
using Jint.Native.Object;
using Jint.Runtime;
using Jint.Runtime.Environments;
using Jint.Runtime.Interpreter;
namespace Jint.Native.Generator;
///
/// https://tc39.es/ecma262/#sec-properties-of-generator-instances
///
internal sealed class GeneratorInstance : ObjectInstance
{
internal GeneratorState _generatorState;
private ExecutionContext _generatorContext;
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
private readonly JsValue? _generatorBrand;
#pragma warning restore CS0649 // Field is never assigned to, and will always have its default value
private JintStatementList _generatorBody = null!;
public JsValue? _nextValue;
public JsValue? _error;
public GeneratorInstance(Engine engine) : base(engine)
{
}
///
/// https://tc39.es/ecma262/#sec-generatorstart
///
public JsValue GeneratorStart(JintStatementList generatorBody)
{
var genContext = _engine.UpdateGenerator(this);
_generatorBody = generatorBody;
_generatorContext = genContext;
_generatorState = GeneratorState.SuspendedStart;
return Undefined;
}
///
/// https://tc39.es/ecma262/#sec-generatorresume
///
public ObjectInstance GeneratorResume(JsValue? value, JsValue? generatorBrand)
{
var state = GeneratorValidate(generatorBrand);
if (state == GeneratorState.Completed)
{
return new IteratorResult(_engine, Undefined, JsBoolean.True);
}
var genContext = _generatorContext;
var methodContext = _engine.ExecutionContext;
// 6. Suspend methodContext.
_nextValue = value;
var context = _engine._activeEvaluationContext;
return ResumeExecution(genContext, context!);
}
///
/// https://tc39.es/ecma262/#sec-generatorresumeabrupt
///
public JsValue GeneratorResumeAbrupt(in Completion abruptCompletion, JsValue? generatorBrand)
{
var state = GeneratorValidate(generatorBrand);
if (state == GeneratorState.SuspendedStart)
{
_generatorState = GeneratorState.Completed;
state = GeneratorState.Completed;
}
if (state == GeneratorState.Completed)
{
if (abruptCompletion.Type == CompletionType.Return)
{
return new IteratorResult(_engine, abruptCompletion.Value, JsBoolean.True);
}
Throw.JavaScriptException(_engine, abruptCompletion.Value, AstExtensions.DefaultLocation);
}
var genContext = _generatorContext;
var methodContext = _engine.ExecutionContext;
// Suspend methodContext
_nextValue = abruptCompletion.Type == CompletionType.Return
? abruptCompletion.Value
: null;
_error = abruptCompletion.Type == CompletionType.Throw
? abruptCompletion.Value
: null;
if (_error is not null)
{
Throw.JavaScriptException(_engine, _error, AstExtensions.DefaultLocation);
}
return ResumeExecution(genContext, new EvaluationContext(_engine));
}
private ObjectInstance ResumeExecution(in ExecutionContext genContext, EvaluationContext context)
{
_generatorState = GeneratorState.Executing;
_engine.EnterExecutionContext(genContext);
var result = _generatorBody.Execute(context);
_engine.LeaveExecutionContext();
ObjectInstance? resultValue = null;
if (result.Type == CompletionType.Normal)
{
_generatorState = GeneratorState.Completed;
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.True);
}
else if (result.Type == CompletionType.Return)
{
if (_generatorState == GeneratorState.SuspendedYield)
{
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.False);
}
else
{
_generatorState = GeneratorState.Completed;
resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.True);
}
}
if (result.Type == CompletionType.Throw)
{
_generatorState = GeneratorState.Completed;
Throw.JavaScriptException(_engine, result.Value, result);
}
return resultValue!;
}
private GeneratorState GeneratorValidate(JsValue? generatorBrand)
{
if (!ReferenceEquals(generatorBrand, _generatorBrand))
{
Throw.TypeError(_engine.Realm, "Generator brand differs from attached brand");
}
if (_generatorState == GeneratorState.Executing)
{
Throw.TypeError(_engine.Realm, "Generator state was unexpectedly executing");
}
return _generatorState;
}
}