JavascriptAsyncGenerator.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. #include "Language/InterpreterStackFrame.h"
  7. using namespace Js;
  8. JavascriptAsyncGenerator* JavascriptAsyncGenerator::New(
  9. Recycler* recycler,
  10. DynamicType* generatorType,
  11. Arguments& args,
  12. ScriptFunction* scriptFunction)
  13. {
  14. auto* requestQueue = RecyclerNew(recycler, JavascriptAsyncGenerator::RequestQueue, recycler);
  15. JavascriptAsyncGenerator* generator = RecyclerNew(
  16. recycler,
  17. JavascriptAsyncGenerator,
  18. generatorType,
  19. args,
  20. scriptFunction,
  21. requestQueue);
  22. auto* library = scriptFunction->GetLibrary();
  23. generator->onFulfilled = library->CreateAsyncGeneratorCallbackFunction(
  24. EntryAwaitFulfilledCallback,
  25. generator);
  26. generator->onRejected = library->CreateAsyncGeneratorCallbackFunction(
  27. EntryAwaitRejectedCallback,
  28. generator);
  29. return generator;
  30. }
  31. Var JavascriptAsyncGenerator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
  32. {
  33. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  34. ARGUMENTS(args, callInfo);
  35. auto* scriptContext = function->GetScriptContext();
  36. auto* library = scriptContext->GetLibrary();
  37. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("AsyncGenerator.prototype.next"));
  38. Var thisValue = args[0];
  39. Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined();
  40. return EnqueueRequest(
  41. thisValue,
  42. scriptContext,
  43. input,
  44. ResumeYieldKind::Normal,
  45. _u("AsyncGenerator.prototype.next"));
  46. }
  47. Var JavascriptAsyncGenerator::EntryReturn(RecyclableObject* function, CallInfo callInfo, ...)
  48. {
  49. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  50. ARGUMENTS(args, callInfo);
  51. auto* scriptContext = function->GetScriptContext();
  52. auto* library = scriptContext->GetLibrary();
  53. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("AsyncGenerator.prototype.return"));
  54. Var thisValue = args[0];
  55. Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined();
  56. return EnqueueRequest(
  57. thisValue,
  58. scriptContext,
  59. input,
  60. ResumeYieldKind::Return,
  61. _u("AsyncGenerator.prototype.return"));
  62. }
  63. Var JavascriptAsyncGenerator::EntryThrow(RecyclableObject* function, CallInfo callInfo, ...)
  64. {
  65. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  66. ARGUMENTS(args, callInfo);
  67. auto* scriptContext = function->GetScriptContext();
  68. auto* library = scriptContext->GetLibrary();
  69. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("AsyncGenerator.prototype.throw"));
  70. Var thisValue = args[0];
  71. Var input = args.Info.Count > 1 ? args[1] : library->GetUndefined();
  72. return EnqueueRequest(
  73. thisValue,
  74. scriptContext,
  75. input,
  76. ResumeYieldKind::Throw,
  77. _u("AsyncGenerator.prototype.throw"));
  78. }
  79. Var JavascriptAsyncGenerator::EntryAwaitFulfilledCallback(
  80. RecyclableObject* function,
  81. CallInfo callInfo, ...)
  82. {
  83. auto* scriptContext = function->GetScriptContext();
  84. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  85. ARGUMENTS(args, callInfo);
  86. AssertOrFailFast(args.Info.Count > 1);
  87. Var value = args[1];
  88. auto* callbackFn = VarTo<AsyncGeneratorCallbackFunction>(function);
  89. JavascriptAsyncGenerator* generator = callbackFn->generator;
  90. PendingState state = generator->pendingState;
  91. generator->pendingState = PendingState::None;
  92. switch (state)
  93. {
  94. case PendingState::Await:
  95. generator->ResumeCoroutine(value, ResumeYieldKind::Normal);
  96. break;
  97. case PendingState::AwaitReturn:
  98. generator->ResumeCoroutine(value, ResumeYieldKind::Return);
  99. break;
  100. case PendingState::Yield:
  101. generator->ResolveNext(value);
  102. break;
  103. default:
  104. AssertMsg(false, "Expected an async generator pending state");
  105. break;
  106. }
  107. return scriptContext->GetLibrary()->GetUndefined();
  108. }
  109. Var JavascriptAsyncGenerator::EntryAwaitRejectedCallback(
  110. RecyclableObject* function,
  111. CallInfo callInfo, ...)
  112. {
  113. auto* scriptContext = function->GetScriptContext();
  114. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  115. ARGUMENTS(args, callInfo);
  116. AssertOrFailFast(args.Info.Count > 1);
  117. Var value = args[1];
  118. auto* callbackFn = VarTo<AsyncGeneratorCallbackFunction>(function);
  119. JavascriptAsyncGenerator* generator = callbackFn->generator;
  120. PendingState state = generator->pendingState;
  121. generator->pendingState = PendingState::None;
  122. switch (state)
  123. {
  124. case PendingState::Await:
  125. case PendingState::AwaitReturn:
  126. generator->ResumeCoroutine(value, ResumeYieldKind::Throw);
  127. break;
  128. case PendingState::Yield:
  129. generator->RejectNext(value);
  130. break;
  131. default:
  132. AssertMsg(false, "Expected an async generator pending state");
  133. break;
  134. }
  135. return scriptContext->GetLibrary()->GetUndefined();
  136. }
  137. Var JavascriptAsyncGenerator::EnqueueRequest(
  138. Var thisValue,
  139. ScriptContext* scriptContext,
  140. Var input,
  141. ResumeYieldKind resumeKind,
  142. const char16* apiNameForErrorMessage)
  143. {
  144. auto* promise = JavascriptPromise::CreateEnginePromise(scriptContext);
  145. if (!VarIs<JavascriptAsyncGenerator>(thisValue))
  146. {
  147. auto* library = scriptContext->GetLibrary();
  148. auto* error = library->CreateTypeError();
  149. JavascriptError::SetErrorMessage(
  150. error,
  151. JSERR_NeedObjectOfType,
  152. scriptContext,
  153. apiNameForErrorMessage,
  154. _u("AsyncGenerator"));
  155. promise->Reject(error, scriptContext);
  156. }
  157. else
  158. {
  159. auto* request = RecyclerNew(
  160. scriptContext->GetRecycler(),
  161. AsyncGeneratorRequest,
  162. input,
  163. resumeKind,
  164. promise);
  165. auto* generator = UnsafeVarTo<JavascriptAsyncGenerator>(thisValue);
  166. generator->PushRequest(request);
  167. generator->ResumeNext();
  168. }
  169. return promise;
  170. }
  171. void JavascriptAsyncGenerator::ResumeNext()
  172. {
  173. if (IsExecuting() || this->pendingState != PendingState::None || !HasRequest())
  174. return;
  175. auto* scriptContext = GetScriptContext();
  176. auto* library = scriptContext->GetLibrary();
  177. AsyncGeneratorRequest* next = PeekRequest();
  178. if (next->kind != ResumeYieldKind::Normal)
  179. {
  180. if (IsSuspendedStart())
  181. SetCompleted();
  182. if (next->kind == ResumeYieldKind::Return)
  183. {
  184. if (IsCompleted()) UnwrapValue(next->data, PendingState::Yield);
  185. else UnwrapValue(next->data, PendingState::AwaitReturn);
  186. }
  187. else
  188. {
  189. if (IsCompleted()) RejectNext(next->data);
  190. else ResumeCoroutine(next->data, next->kind);
  191. }
  192. }
  193. else
  194. {
  195. if (IsCompleted()) ResolveNext(library->GetUndefined());
  196. else ResumeCoroutine(next->data, next->kind);
  197. }
  198. }
  199. void JavascriptAsyncGenerator::ResumeCoroutine(Var value, ResumeYieldKind resumeKind)
  200. {
  201. Assert(this->pendingState == PendingState::None);
  202. RecyclableObject* result = nullptr;
  203. try
  204. {
  205. // Call the internal (sync) generator entry point
  206. result = VarTo<RecyclableObject>(this->CallGenerator(value, resumeKind));
  207. }
  208. catch (const JavascriptException& err)
  209. {
  210. RejectNext(err.GetAndClear()->GetThrownObject(nullptr));
  211. return;
  212. }
  213. Var resultValue = JavascriptOperators::GetProperty(
  214. result,
  215. PropertyIds::value,
  216. GetScriptContext());
  217. if (JavascriptOperators::GetTypeId(result) == TypeIds_AwaitObject)
  218. {
  219. UnwrapValue(resultValue, PendingState::Await);
  220. return;
  221. }
  222. if (IsCompleted())
  223. {
  224. // If the generator is completed, then resolve immediately. Return
  225. // values are unwrapped explicitly by the code generated for the
  226. // return statement.
  227. ResolveNext(resultValue);
  228. }
  229. else
  230. {
  231. // Otherwise, await the yielded value
  232. UnwrapValue(resultValue, PendingState::Yield);
  233. }
  234. }
  235. void JavascriptAsyncGenerator::ResolveNext(Var value)
  236. {
  237. auto* scriptContext = GetScriptContext();
  238. auto* library = scriptContext->GetLibrary();
  239. Var result = library->CreateIteratorResultObject(value, IsCompleted());
  240. ShiftRequest()->promise->Resolve(result, scriptContext);
  241. ResumeNext();
  242. }
  243. void JavascriptAsyncGenerator::RejectNext(Var reason)
  244. {
  245. SetCompleted();
  246. ShiftRequest()->promise->Reject(reason, GetScriptContext());
  247. ResumeNext();
  248. }
  249. void JavascriptAsyncGenerator::UnwrapValue(Var value, PendingState pendingState)
  250. {
  251. this->pendingState = pendingState;
  252. auto* scriptContext = GetScriptContext();
  253. auto* promise = JavascriptPromise::InternalPromiseResolve(value, scriptContext);
  254. auto* unused = JavascriptPromise::UnusedPromiseCapability(scriptContext);
  255. JavascriptPromise::PerformPromiseThen(promise, unused, onFulfilled, onRejected, scriptContext);
  256. }
  257. template<>
  258. bool Js::VarIsImpl<JavascriptAsyncGenerator>(RecyclableObject* obj)
  259. {
  260. return JavascriptOperators::GetTypeId(obj) == TypeIds_AsyncGenerator;
  261. }
  262. template<>
  263. bool Js::VarIsImpl<AsyncGeneratorCallbackFunction>(RecyclableObject* obj)
  264. {
  265. return VarIs<JavascriptFunction>(obj) && (
  266. VirtualTableInfo<AsyncGeneratorCallbackFunction>::HasVirtualTable(obj) ||
  267. VirtualTableInfo<CrossSiteObject<AsyncGeneratorCallbackFunction>>::HasVirtualTable(obj)
  268. );
  269. }