123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- using Jint.Native;
- using Jint.Native.Generator;
- using Jint.Native.Iterator;
- using Jint.Native.Object;
- namespace Jint.Runtime.Interpreter.Expressions;
- internal sealed class JintYieldExpression : JintExpression
- {
- public JintYieldExpression(YieldExpression expression) : base(expression)
- {
- }
- protected override object EvaluateInternal(EvaluationContext context)
- {
- if (!context.Engine.Options.ExperimentalFeatures.HasFlag(ExperimentalFeature.Generators))
- {
- ExceptionHelper.ThrowJavaScriptException(
- context.Engine.Intrinsics.Error,
- "Yield expressions are not supported in the engine, you can enable the experimental feature 'Generators' in engine options to use them.");
- }
- var expression = (YieldExpression) _expression;
- JsValue value;
- if (context.Engine.ExecutionContext.Generator?._nextValue is not null)
- {
- value = context.Engine.ExecutionContext.Generator._nextValue;
- }
- else if (expression.Argument is not null)
- {
- value = Build(expression.Argument).GetValue(context);
- }
- else
- {
- value = JsValue.Undefined;
- }
- if (expression.Delegate)
- {
- value = YieldDelegate(context, value);
- }
- return Yield(context, value);
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
- /// </summary>
- private JsValue YieldDelegate(EvaluationContext context, JsValue value)
- {
- var engine = context.Engine;
- var generatorKind = engine.ExecutionContext.GetGeneratorKind();
- var iterator = value.GetIterator(engine.Realm, generatorKind);
- var iteratorRecord = iterator;
- var received = new Completion(CompletionType.Normal, JsValue.Undefined, _expression);
- while (true)
- {
- if (received.Type == CompletionType.Normal)
- {
- iterator.TryIteratorStep(out var innerResult);
- if (generatorKind == GeneratorKind.Async)
- {
- innerResult = Await(innerResult);
- }
- if (innerResult is not IteratorResult oi)
- {
- ExceptionHelper.ThrowTypeError(engine.Realm);
- }
- var done = IteratorComplete(innerResult);
- if (done)
- {
- return IteratorValue(innerResult);
- }
- if (generatorKind == GeneratorKind.Async)
- {
- received = AsyncGeneratorYield(IteratorValue(innerResult));
- }
- else
- {
- received = GeneratorYield(innerResult);
- }
- }
- else if (received.Type == CompletionType.Throw)
- {
- var throwMethod = iterator.GetMethod("throw");
- if (throwMethod is not null)
- {
- var innerResult = throwMethod.Call(iterator, new[]{ received.Value });
- if (generatorKind == GeneratorKind.Async)
- {
- innerResult = Await(innerResult);
- }
- // NOTE: Exceptions from the inner iterator throw method are propagated.
- // Normal completions from an inner throw method are processed similarly to an inner next.
- if (innerResult is not ObjectInstance oi)
- {
- ExceptionHelper.ThrowTypeError(engine.Realm);
- }
- var done = IteratorComplete(innerResult);
- if (done)
- {
- IteratorValue(innerResult);
- }
- if (generatorKind == GeneratorKind.Async)
- {
- received = AsyncGeneratorYield(IteratorValue(innerResult));
- }
- else
- {
- received = GeneratorYield(innerResult);
- }
- }
- else
- {
- // NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop.
- // But first we need to give iterator a chance to clean up.
- var closeCompletion = new Completion(CompletionType.Normal, null!, _expression);
- if (generatorKind == GeneratorKind.Async)
- {
- AsyncIteratorClose(iteratorRecord, CompletionType.Normal);
- }
- else
- {
- iteratorRecord.Close(CompletionType.Normal);
- }
- ExceptionHelper.ThrowTypeError(engine.Realm, "Iterator does not have close method");
- }
- }
- else
- {
- var returnMethod = iterator.GetMethod("return");
- if (returnMethod is null)
- {
- var temp = received.Value;
- if (generatorKind == GeneratorKind.Async)
- {
- temp = Await(received.Value);
- }
- return temp;
- }
- var innerReturnResult = returnMethod.Call(iterator, new[] { received.Value });
- if (generatorKind == GeneratorKind.Async)
- {
- innerReturnResult = Await(innerReturnResult);
- }
- if (innerReturnResult is not ObjectInstance oi)
- {
- ExceptionHelper.ThrowTypeError(engine.Realm);
- }
- var done = IteratorComplete(innerReturnResult);
- if (done)
- {
- var val = IteratorValue(innerReturnResult);
- return val;
- }
- if (generatorKind == GeneratorKind.Async)
- {
- received = AsyncGeneratorYield(IteratorValue(innerReturnResult));
- }
- else
- {
- received = GeneratorYield(innerReturnResult);
- }
- }
- }
- }
- private Completion GeneratorYield(JsValue innerResult)
- {
- throw new System.NotImplementedException();
- }
- private static bool IteratorComplete(JsValue iterResult)
- {
- return TypeConverter.ToBoolean(iterResult.Get(CommonProperties.Done));
- }
- private static JsValue IteratorValue(JsValue iterResult)
- {
- return iterResult.Get(CommonProperties.Value);
- }
- private static void AsyncIteratorClose(object iteratorRecord, CompletionType closeCompletion)
- {
- ExceptionHelper.ThrowNotImplementedException("async");
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-asyncgeneratoryield
- /// </summary>
- private static Completion AsyncGeneratorYield(object iteratorValue)
- {
- ExceptionHelper.ThrowNotImplementedException("async");
- return default;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#await
- /// </summary>
- private static ObjectInstance Await(JsValue innerResult)
- {
- ExceptionHelper.ThrowNotImplementedException("await");
- return null;
- }
- /// <summary>
- /// https://tc39.es/ecma262/#sec-yield
- /// </summary>
- private static JsValue Yield(EvaluationContext context, JsValue iterNextObj)
- {
- var engine = context.Engine;
- var generatorKind = engine.ExecutionContext.GetGeneratorKind();
- if (generatorKind == GeneratorKind.Async)
- {
- // TODO return ? AsyncGeneratorYield(undefined);
- ExceptionHelper.ThrowNotImplementedException("async not implemented");
- }
- // https://tc39.es/ecma262/#sec-generatoryield
- var genContext = engine.ExecutionContext;
- var generator = genContext.Generator;
- generator!._generatorState = GeneratorState.SuspendedYield;
- //_engine.LeaveExecutionContext();
- return iterNextObj;
- }
- }
|