JavascriptAsyncFromSyncIterator.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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. namespace Js
  7. {
  8. Var JavascriptAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(RecyclableObject* result, ScriptContext* scriptContext)
  9. {
  10. JavascriptLibrary* library = scriptContext->GetLibrary();
  11. // 1. Let done be IteratorComplete(result).
  12. // 2. IfAbruptRejectPromise(done, promiseCapability).
  13. bool done;
  14. try
  15. {
  16. done = JavascriptOperators::IteratorComplete(result, scriptContext);
  17. }
  18. catch (const JavascriptException& err)
  19. {
  20. JavascriptExceptionObject* exception = err.GetAndClear();
  21. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  22. }
  23. // 3. Let value be IteratorValue(result).
  24. // 4. IfAbruptRejectPromise(value, promiseCapability).
  25. Var value = nullptr;
  26. try
  27. {
  28. value = JavascriptOperators::IteratorValue(result, scriptContext);
  29. }
  30. catch (const JavascriptException& err)
  31. {
  32. JavascriptExceptionObject* exception = err.GetAndClear();
  33. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  34. }
  35. // 5. Let valueWrapper be ? PromiseResolve(%Promise%, <<value>>).
  36. JavascriptPromise* valueWrapper = JavascriptPromise::InternalPromiseResolve(value, scriptContext);
  37. // 6. Let steps be the algorithm steps defined in Async-from-Sync Iterator Value Unwrap Functions.
  38. // 7. Let onFulfilled be CreateBuiltinFunction(steps, <<[[Done]]>>).
  39. // 8. Set onFulfilled.[[Done]] to done.
  40. RecyclableObject* onFulfilled;
  41. if (done)
  42. {
  43. onFulfilled = library->EnsureAsyncFromSyncIteratorValueUnwrapTrueFunction();
  44. }
  45. else
  46. {
  47. onFulfilled = library->EnsureAsyncFromSyncIteratorValueUnwrapFalseFunction();
  48. }
  49. // 9. Perform ! PerformPromiseThen(valueWrapper, onFulfilled, undefined, promiseCapability).
  50. // 10. Return promiseCapability.[[Promise]].
  51. return JavascriptPromise::CreateThenPromise(valueWrapper, onFulfilled, library->GetThrowerFunction(), scriptContext);
  52. }
  53. Var JavascriptAsyncFromSyncIterator::EntryAsyncFromSyncIteratorValueUnwrapTrueFunction(RecyclableObject* function, CallInfo callInfo, ...)
  54. {
  55. ScriptContext* scriptContext = function->GetScriptContext();
  56. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  57. ARGUMENTS(args, callInfo);
  58. Assert(!(callInfo.Flags & CallFlags_New));
  59. AssertOrFailFastMsg(args.Info.Count > 1, "AsyncFromSyncIteratorValueUnwrap should never be called without an argument");
  60. JavascriptLibrary* library = scriptContext->GetLibrary();
  61. return library->CreateIteratorResultObject(args[1], library->GetTrue());
  62. }
  63. Var JavascriptAsyncFromSyncIterator::EntryAsyncFromSyncIteratorValueUnwrapFalseFunction(RecyclableObject* function, CallInfo callInfo, ...)
  64. {
  65. ScriptContext* scriptContext = function->GetScriptContext();
  66. PROBE_STACK(scriptContext, Js::Constants::MinStackDefault);
  67. ARGUMENTS(args, callInfo);
  68. Assert(!(callInfo.Flags & CallFlags_New));
  69. AssertOrFailFastMsg(args.Info.Count > 1, "AsyncFromSyncIteratorValueUnwrap should never be called without an argument");
  70. JavascriptLibrary* library = scriptContext->GetLibrary();
  71. return library->CreateIteratorResultObject(args[1], library->GetFalse());
  72. }
  73. Var JavascriptAsyncFromSyncIterator::EntryAsyncFromSyncIteratorNext(RecyclableObject* function, CallInfo callInfo, ...)
  74. {
  75. ScriptContext* scriptContext = function->GetScriptContext();
  76. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  77. ARGUMENTS(args, callInfo);
  78. Assert(!(callInfo.Flags & CallFlags_New));
  79. AssertMsg(args.Info.Count > 0, "AsyncFromSyncIteratorNext should have implicit this");
  80. // 1. Let O be the this value.
  81. // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  82. // promise creation deferred to the end to simplify logic
  83. // this step skipped as no codepath exists that will enable reaching here with wrong object type
  84. // 3. If Type(O) is not Object, or if O does not have a [[SyncIteratorRecord]] internal slot, then
  85. // a. Let invalidIteratorError be a newly created TypeError object.
  86. // b. Perform ! Call(promiseCapability.[[Reject]], undefined, << invalidIteratorError >>).
  87. // c. Return promiseCapability.[[Promise]].
  88. // this will failfast if the logic that makes steps 3 irrelevant changes
  89. JavascriptAsyncFromSyncIterator* thisValue = VarTo<JavascriptAsyncFromSyncIterator>(args[0]);
  90. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
  91. RecyclableObject* syncIteratorRecord = thisValue->GetSyncIterator();
  92. RecyclableObject* result = nullptr;
  93. // 5. Let result be IteratorNext(syncIteratorRecord, value).
  94. try
  95. {
  96. result = JavascriptOperators::IteratorNext(syncIteratorRecord, scriptContext, thisValue->EnsureSyncNextFunction(scriptContext), args.Info.Count > 1 ? args[1] : nullptr);
  97. }
  98. catch (const JavascriptException& err)
  99. {
  100. // 6. IfAbruptRejectPromise(result, promiseCapability).
  101. JavascriptExceptionObject* exception = err.GetAndClear();
  102. if (exception != nullptr)
  103. {
  104. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  105. }
  106. }
  107. // 7. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
  108. return JavascriptAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(result, scriptContext);
  109. }
  110. Var JavascriptAsyncFromSyncIterator::EntryAsyncFromSyncIteratorReturn(RecyclableObject* function, CallInfo callInfo, ...)
  111. {
  112. ScriptContext* scriptContext = function->GetScriptContext();
  113. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  114. ARGUMENTS(args, callInfo);
  115. Assert(!(callInfo.Flags & CallFlags_New));
  116. AssertMsg(args.Info.Count > 0, "AsyncFromSyncIteratorReturn should have implicit this");
  117. JavascriptLibrary* library = scriptContext->GetLibrary();
  118. // 1. Let O be the this value.
  119. // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  120. // promise creation deferred to the end to simplify logic
  121. // this step skipped as no codepath exists that will enable reaching here with wrong object type
  122. // 3. If Type(O) is not Object, or if O does not have a [[SyncIteratorRecord]] internal slot, then
  123. // a. Let invalidIteratorError be a newly created TypeError object.
  124. // b. Perform ! Call(promiseCapability.[[Reject]], undefined, << invalidIteratorError >>).
  125. // c. Return promiseCapability.[[Promise]].
  126. // this will failfast if the logic that makes steps 3-6 irrelevant changes
  127. JavascriptAsyncFromSyncIterator* thisValue = VarTo<JavascriptAsyncFromSyncIterator>(args[0]);
  128. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
  129. RecyclableObject* syncIteratorRecord = thisValue->GetSyncIterator();
  130. Var returnMethod = nullptr;
  131. Var result = nullptr;
  132. // 5. Let return be GetMethod(syncIterator, "return").
  133. // 6. IfAbruptRejectPromise(return, promiseCapability).
  134. // 7. If return is undefined, then
  135. // a. Perform ! Call(promiseCapability.[[Reject]], undefined, << value >>).
  136. // b. Return promiseCapability.[[Promise]].
  137. try
  138. {
  139. returnMethod = JavascriptOperators::GetProperty(syncIteratorRecord, PropertyIds::return_, scriptContext);
  140. }
  141. catch (const JavascriptException& err)
  142. {
  143. JavascriptExceptionObject* exception = err.GetAndClear();
  144. if (exception != nullptr)
  145. {
  146. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  147. }
  148. }
  149. if (returnMethod == library->GetUndefined())
  150. {
  151. result = library->CreateIteratorResultObject(args.Info.Count > 1 ? args[1] : library->GetUndefined(), library->GetTrue());
  152. return JavascriptPromise::CreateResolvedPromise(result, scriptContext);
  153. }
  154. if (!JavascriptConversion::IsCallable(returnMethod))
  155. {
  156. JavascriptError* typeError = library->CreateTypeError();
  157. JavascriptError::SetErrorMessage(typeError, JSERR_NeedFunction, _u("AsyncFromSyncIteratorThrow.prototype.return"), scriptContext);
  158. return JavascriptPromise::CreateRejectedPromise(typeError, scriptContext);
  159. }
  160. // 8. Let result be Call(return, syncIterator, << value >>).
  161. try
  162. {
  163. RecyclableObject* callable = VarTo<RecyclableObject>(returnMethod);
  164. Var value = args.Info.Count > 1 ? args[1] : nullptr;
  165. result = scriptContext->GetThreadContext()->ExecuteImplicitCall(callable, ImplicitCall_Accessor, [=]() -> Var
  166. {
  167. Js::Var args[] = { syncIteratorRecord, value };
  168. Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args) + (value == nullptr ? -1 : 0));
  169. return JavascriptFunction::CallFunction<true>(callable, callable->GetEntryPoint(), Arguments(callInfo, args));
  170. });
  171. }
  172. catch (const JavascriptException& err)
  173. {
  174. // 9. IfAbruptRejectPromise(result, promiseCapability).
  175. JavascriptExceptionObject* exception = err.GetAndClear();
  176. if (exception != nullptr)
  177. {
  178. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  179. }
  180. }
  181. // 10. If Type(result) is not Object, then
  182. // a. Perform ! Call(promiseCapability.[[Reject]], undefined, << a newly created TypeError object >>).
  183. // b. Return promiseCapability.[[Promise]].
  184. if (!JavascriptOperators::IsObject(result))
  185. {
  186. JavascriptError* typeError = library->CreateTypeError();
  187. JavascriptError::SetErrorMessage(typeError, JSERR_NonObjectFromIterable, _u("AsyncFromSyncIteratorThrow.prototype.return"), scriptContext);
  188. return JavascriptPromise::CreateRejectedPromise(typeError, scriptContext);
  189. }
  190. // 11. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
  191. return JavascriptAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(UnsafeVarTo<RecyclableObject>(result), scriptContext);
  192. }
  193. Var JavascriptAsyncFromSyncIterator::EntryAsyncFromSyncIteratorThrow(RecyclableObject* function, CallInfo callInfo, ...)
  194. {
  195. ScriptContext* scriptContext = function->GetScriptContext();
  196. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  197. ARGUMENTS(args, callInfo);
  198. Assert(!(callInfo.Flags & CallFlags_New));
  199. AssertMsg(args.Info.Count > 0, "AsyncFromSyncIteratorThrow should have implicit this");
  200. JavascriptLibrary* library = scriptContext->GetLibrary();
  201. // 1. Let O be the this value.
  202. // 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  203. // promise creation deferred to the end to simplify logic
  204. // this step skipped as no code path exists that will enable reaching here with wrong object type
  205. // 3. If Type(O) is not Object, or if O does not have a [[SyncIteratorRecord]] internal slot, then
  206. // a. Let invalidIteratorError be a newly created TypeError object.
  207. // b. Perform ! Call(promiseCapability.[[Reject]], undefined, << invalidIteratorError >>).
  208. // c. Return promiseCapability.[[Promise]].
  209. // this will failfast if the logic that makes step 3 irrelevant changes
  210. JavascriptAsyncFromSyncIterator* thisValue = VarTo<JavascriptAsyncFromSyncIterator>(args[0]);
  211. // 4. Let syncIteratorRecord be O.[[SyncIteratorRecord]].
  212. RecyclableObject* syncIteratorRecord = thisValue->GetSyncIterator();
  213. Var throwMethod = nullptr;
  214. // 5. Let throw be GetMethod(syncIterator, "throw").
  215. // 6. IfAbruptRejectPromise(throw, promiseCapability).
  216. // 7. If throw is undefined, then
  217. // a. Perform ! Call(promiseCapability.[[Reject]], undefined, << value >>).
  218. // b. Return promiseCapability.[[Promise]].
  219. try
  220. {
  221. throwMethod = JavascriptOperators::GetProperty(syncIteratorRecord, PropertyIds::throw_, scriptContext);
  222. }
  223. catch (const JavascriptException& err)
  224. {
  225. JavascriptExceptionObject* exception = err.GetAndClear();
  226. if (exception != nullptr)
  227. {
  228. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  229. }
  230. }
  231. if (throwMethod == library->GetUndefined())
  232. {
  233. return JavascriptPromise::CreateRejectedPromise(library->GetUndefined(), scriptContext);
  234. }
  235. if (!JavascriptConversion::IsCallable(throwMethod))
  236. {
  237. JavascriptError* typeError = library->CreateTypeError();
  238. JavascriptError::SetErrorMessage(typeError, JSERR_NeedFunction, _u("AsyncFromSyncIteratorThrow.prototype.throw"), scriptContext);
  239. return JavascriptPromise::CreateRejectedPromise(typeError, scriptContext);
  240. }
  241. // 8. Let result be Call(throw, syncIterator, << value >>).
  242. Var result = nullptr;
  243. try
  244. {
  245. RecyclableObject* callable = VarTo<RecyclableObject>(throwMethod);
  246. Var value = args.Info.Count > 1 ? args[1] : nullptr;
  247. result = scriptContext->GetThreadContext()->ExecuteImplicitCall(callable, ImplicitCall_Accessor, [=]() -> Var
  248. {
  249. Js::Var args[] = { syncIteratorRecord, value };
  250. Js::CallInfo callInfo(Js::CallFlags_Value, _countof(args) + (value == nullptr ? -1 : 0));
  251. return JavascriptFunction::CallFunction<true>(callable, callable->GetEntryPoint(), Arguments(callInfo, args));
  252. });
  253. }
  254. catch (const JavascriptException& err)
  255. {
  256. // 9. IfAbruptRejectPromise(result, promiseCapability).
  257. JavascriptExceptionObject* exception = err.GetAndClear();
  258. if (exception != nullptr)
  259. {
  260. return JavascriptPromise::CreateRejectedPromise(exception->GetThrownObject(scriptContext), scriptContext);
  261. }
  262. }
  263. // 10. If Type(result) is not Object, then
  264. // a. Perform ! Call(promiseCapability.[[Reject]], undefined, << a newly created TypeError object >>).
  265. // b. Return promiseCapability.[[Promise]].
  266. if (!JavascriptOperators::IsObject(result))
  267. {
  268. JavascriptError* typeError = library->CreateTypeError();
  269. JavascriptError::SetErrorMessage(typeError, JSERR_NonObjectFromIterable, _u("AsyncFromSyncIteratorThrow.prototype.throw"), scriptContext);
  270. return JavascriptPromise::CreateRejectedPromise(typeError, scriptContext);
  271. }
  272. // 11. Return ! AsyncFromSyncIteratorContinuation(result, promiseCapability).
  273. return JavascriptAsyncFromSyncIterator::AsyncFromSyncIteratorContinuation(UnsafeVarTo<RecyclableObject>(result), scriptContext);
  274. }
  275. RecyclableObject* JavascriptAsyncFromSyncIterator::EnsureSyncNextFunction(ScriptContext* scriptContext)
  276. {
  277. if (syncNextFunction == nullptr)
  278. {
  279. syncNextFunction = JavascriptOperators::CacheIteratorNext(syncIterator, scriptContext);
  280. }
  281. return syncNextFunction;
  282. }
  283. template <> bool VarIsImpl<JavascriptAsyncFromSyncIterator>(RecyclableObject* obj)
  284. {
  285. return JavascriptOperators::GetTypeId(obj) == TypeIds_AsyncFromSyncIterator;
  286. }
  287. }