2
0

JintYieldExpression.cs 8.1 KB

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