JintYieldExpression.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. using Esprima.Ast;
  2. using Jint.Native;
  3. using Jint.Native.Generator;
  4. using Jint.Native.Iterator;
  5. using Jint.Native.Object;
  6. namespace Jint.Runtime.Interpreter.Expressions;
  7. internal sealed class JintYieldExpression : JintExpression
  8. {
  9. public JintYieldExpression(YieldExpression expression) : base(expression)
  10. {
  11. }
  12. protected override object EvaluateInternal(EvaluationContext context)
  13. {
  14. if (!context.Engine.Options.ExperimentalFeatures.HasFlag(ExperimentalFeature.Generators))
  15. {
  16. ExceptionHelper.ThrowJavaScriptException(
  17. context.Engine.Intrinsics.Error,
  18. "Yield expressions are not supported in the engine, you can enable the experimental feature 'Generators' in engine options to use them.");
  19. }
  20. var expression = (YieldExpression) _expression;
  21. JsValue value;
  22. if (context.Engine.ExecutionContext.Generator?._nextValue is not null)
  23. {
  24. value = context.Engine.ExecutionContext.Generator._nextValue;
  25. }
  26. else if (expression.Argument is not null)
  27. {
  28. value = Build(expression.Argument).GetValue(context);
  29. }
  30. else
  31. {
  32. value = JsValue.Undefined;
  33. }
  34. if (expression.Delegate)
  35. {
  36. value = YieldDelegate(context, value);
  37. }
  38. return Yield(context, value);
  39. }
  40. /// <summary>
  41. /// https://tc39.es/ecma262/#sec-generator-function-definitions-runtime-semantics-evaluation
  42. /// </summary>
  43. private JsValue YieldDelegate(EvaluationContext context, JsValue value)
  44. {
  45. var engine = context.Engine;
  46. var generatorKind = engine.ExecutionContext.GetGeneratorKind();
  47. var iterator = value.GetIterator(engine.Realm, generatorKind);
  48. var iteratorRecord = iterator;
  49. var received = new Completion(CompletionType.Normal, JsValue.Undefined, _expression);
  50. while (true)
  51. {
  52. if (received.Type == CompletionType.Normal)
  53. {
  54. iterator.TryIteratorStep(out var innerResult);
  55. if (generatorKind == GeneratorKind.Async)
  56. {
  57. innerResult = Await(innerResult);
  58. }
  59. if (innerResult is not IteratorResult oi)
  60. {
  61. ExceptionHelper.ThrowTypeError(engine.Realm);
  62. }
  63. var done = IteratorComplete(innerResult);
  64. if (done)
  65. {
  66. return IteratorValue(innerResult);
  67. }
  68. if (generatorKind == GeneratorKind.Async)
  69. {
  70. received = AsyncGeneratorYield(IteratorValue(innerResult));
  71. }
  72. else
  73. {
  74. received = GeneratorYield(innerResult);
  75. }
  76. }
  77. else if (received.Type == CompletionType.Throw)
  78. {
  79. var throwMethod = iterator.GetMethod("throw");
  80. if (throwMethod is not null)
  81. {
  82. var innerResult = throwMethod.Call(iterator, new[]{ received.Value });
  83. if (generatorKind == GeneratorKind.Async)
  84. {
  85. innerResult = Await(innerResult);
  86. }
  87. // NOTE: Exceptions from the inner iterator throw method are propagated.
  88. // Normal completions from an inner throw method are processed similarly to an inner next.
  89. if (innerResult is not ObjectInstance oi)
  90. {
  91. ExceptionHelper.ThrowTypeError(engine.Realm);
  92. }
  93. var done = IteratorComplete(innerResult);
  94. if (done)
  95. {
  96. IteratorValue(innerResult);
  97. }
  98. if (generatorKind == GeneratorKind.Async)
  99. {
  100. received = AsyncGeneratorYield(IteratorValue(innerResult));
  101. }
  102. else
  103. {
  104. received = GeneratorYield(innerResult);
  105. }
  106. }
  107. else
  108. {
  109. // NOTE: If iterator does not have a throw method, this throw is going to terminate the yield* loop.
  110. // But first we need to give iterator a chance to clean up.
  111. var closeCompletion = new Completion(CompletionType.Normal, null!, _expression);
  112. if (generatorKind == GeneratorKind.Async)
  113. {
  114. AsyncIteratorClose(iteratorRecord, CompletionType.Normal);
  115. }
  116. else
  117. {
  118. iteratorRecord.Close(CompletionType.Normal);
  119. }
  120. ExceptionHelper.ThrowTypeError(engine.Realm, "Iterator does not have close method");
  121. }
  122. }
  123. else
  124. {
  125. var returnMethod = iterator.GetMethod("return");
  126. if (returnMethod is null)
  127. {
  128. var temp = received.Value;
  129. if (generatorKind == GeneratorKind.Async)
  130. {
  131. temp = Await(received.Value);
  132. }
  133. return temp;
  134. }
  135. var innerReturnResult = returnMethod.Call(iterator, new[] { received.Value });
  136. if (generatorKind == GeneratorKind.Async)
  137. {
  138. innerReturnResult = Await(innerReturnResult);
  139. }
  140. if (innerReturnResult is not ObjectInstance oi)
  141. {
  142. ExceptionHelper.ThrowTypeError(engine.Realm);
  143. }
  144. var done = IteratorComplete(innerReturnResult);
  145. if (done)
  146. {
  147. var val = IteratorValue(innerReturnResult);
  148. return val;
  149. }
  150. if (generatorKind == GeneratorKind.Async)
  151. {
  152. received = AsyncGeneratorYield(IteratorValue(innerReturnResult));
  153. }
  154. else
  155. {
  156. received = GeneratorYield(innerReturnResult);
  157. }
  158. }
  159. }
  160. }
  161. private Completion GeneratorYield(JsValue innerResult)
  162. {
  163. throw new System.NotImplementedException();
  164. }
  165. private static bool IteratorComplete(JsValue iterResult)
  166. {
  167. return TypeConverter.ToBoolean(iterResult.Get(CommonProperties.Done));
  168. }
  169. private static JsValue IteratorValue(JsValue iterResult)
  170. {
  171. return iterResult.Get(CommonProperties.Value);
  172. }
  173. private static void AsyncIteratorClose(object iteratorRecord, CompletionType closeCompletion)
  174. {
  175. ExceptionHelper.ThrowNotImplementedException("async");
  176. }
  177. /// <summary>
  178. /// https://tc39.es/ecma262/#sec-asyncgeneratoryield
  179. /// </summary>
  180. private static Completion AsyncGeneratorYield(object iteratorValue)
  181. {
  182. ExceptionHelper.ThrowNotImplementedException("async");
  183. return default;
  184. }
  185. /// <summary>
  186. /// https://tc39.es/ecma262/#await
  187. /// </summary>
  188. private static ObjectInstance Await(JsValue innerResult)
  189. {
  190. ExceptionHelper.ThrowNotImplementedException("await");
  191. return null;
  192. }
  193. /// <summary>
  194. /// https://tc39.es/ecma262/#sec-yield
  195. /// </summary>
  196. private static JsValue Yield(EvaluationContext context, JsValue iterNextObj)
  197. {
  198. var engine = context.Engine;
  199. var generatorKind = engine.ExecutionContext.GetGeneratorKind();
  200. if (generatorKind == GeneratorKind.Async)
  201. {
  202. // TODO return ? AsyncGeneratorYield(undefined);
  203. ExceptionHelper.ThrowNotImplementedException("async not implemented");
  204. }
  205. // https://tc39.es/ecma262/#sec-generatoryield
  206. var genContext = engine.ExecutionContext;
  207. var generator = genContext.Generator;
  208. generator!._generatorState = GeneratorState.SuspendedYield;
  209. //_engine.LeaveExecutionContext();
  210. return iterNextObj;
  211. }
  212. }