PromiseOperations.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using Jint.Native.Object;
  5. using Jint.Runtime;
  6. namespace Jint.Native.Promise
  7. {
  8. internal static class PromiseOperations
  9. {
  10. // https://tc39.es/ecma262/#sec-newpromisereactionjob
  11. //
  12. // 1. Let job be a new Job Abstract Closure with no parameters that captures reaction and argument and performs the following steps when called:
  13. // a. Assert: reaction is a PromiseReaction Record.
  14. // b. Let promiseCapability be reaction.[[Capability]].
  15. // c. Let type be reaction.[[Type]].
  16. // d. Let handler be reaction.[[Handler]].
  17. // e. If handler is empty, then
  18. // i. If type is Fulfill, let handlerResult be NormalCompletion(argument).
  19. // ii. Else,
  20. // 1. Assert: type is Reject.
  21. // 2. Let handlerResult be ThrowCompletion(argument).
  22. // f. Else, let handlerResult be HostCallJobCallback(handler, undefined, « argument »).
  23. // g. If promiseCapability is undefined, then
  24. // i. Assert: handlerResult is not an abrupt completion.
  25. // ii. Return NormalCompletion(empty).
  26. // h. Assert: promiseCapability is a PromiseCapability Record.
  27. // i. If handlerResult is an abrupt completion, then
  28. // i. Let status be Call(promiseCapability.[[Reject]], undefined, « handlerResult.[[Value]] »).
  29. // j. Else,
  30. // i. Let status be Call(promiseCapability.[[Resolve]], undefined, « handlerResult.[[Value]] »).
  31. // k. Return Completion(status).
  32. private static Action NewPromiseReactionJob(PromiseReaction reaction, JsValue value)
  33. {
  34. return () =>
  35. {
  36. var promiseCapability = reaction.Capability;
  37. if (reaction.Handler is ICallable handler)
  38. {
  39. try
  40. {
  41. var result = handler.Call(JsValue.Undefined, new[] {value});
  42. promiseCapability.Resolve.Call(JsValue.Undefined, new[] {result});
  43. }
  44. catch (JavaScriptException e)
  45. {
  46. promiseCapability.Reject.Call(JsValue.Undefined, new[] {e.Error});
  47. }
  48. }
  49. else
  50. {
  51. switch (reaction.Type)
  52. {
  53. case ReactionType.Fulfill:
  54. promiseCapability.Resolve.Call(JsValue.Undefined, new[] {value});
  55. break;
  56. case ReactionType.Reject:
  57. promiseCapability.Reject.Call(JsValue.Undefined, new[] {value});
  58. break;
  59. default:
  60. throw new ArgumentOutOfRangeException();
  61. }
  62. }
  63. };
  64. }
  65. // https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob
  66. // The abstract operation NewPromiseResolveThenableJob takes arguments promiseToResolve, thenable, and then. It performs the following steps when called:
  67. //
  68. // 1. Let job be a new Job Abstract Closure with no parameters that captures promiseToResolve,
  69. // thenable, and then and performs the following steps when called:
  70. // a. Let resolvingFunctions be CreateResolvingFunctions(promiseToResolve).
  71. // b. Let thenCallResult be HostCallJobCallback(then, thenable, « resolvingFunctions.[[Resolve]], resolvingFunctions.[[Reject]] »).
  72. // c. If thenCallResult is an abrupt completion, then
  73. // i. Let status be Call(resolvingFunctions.[[Reject]], undefined, « thenCallResult.[[Value]] »).
  74. // ii. Return Completion(status).
  75. // d. Return Completion(thenCallResult).
  76. // .....Realm stuff....
  77. // 6. Return the Record { [[Job]]: job, [[Realm]]: thenRealm }.
  78. internal static Action NewPromiseResolveThenableJob(PromiseInstance promise, ObjectInstance thenable,
  79. ICallable thenMethod)
  80. {
  81. return () =>
  82. {
  83. var (resolve, reject) = promise.CreateResolvingFunctions();
  84. try
  85. {
  86. thenMethod.Call(thenable, new[] {resolve as JsValue, reject});
  87. }
  88. catch (JavaScriptException e)
  89. {
  90. reject.Call(JsValue.Undefined, new[] {e.Error});
  91. }
  92. };
  93. }
  94. // https://tc39.es/ecma262/#sec-triggerpromisereactions
  95. //
  96. // 1. For each element reaction of reactions, do
  97. // a. Let job be NewPromiseReactionJob(reaction, argument).
  98. // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
  99. // 2. Return undefined.
  100. internal static JsValue TriggerPromiseReactions(Engine engine, List<PromiseReaction> reactions, JsValue result)
  101. {
  102. foreach (var reaction in reactions)
  103. {
  104. engine.AddToEventLoop(NewPromiseReactionJob(reaction, result));
  105. }
  106. return JsValue.Undefined;
  107. }
  108. // https://tc39.es/ecma262/#sec-performpromisethen
  109. internal static JsValue PerformPromiseThen(
  110. Engine engine,
  111. PromiseInstance promise,
  112. JsValue onFulfilled,
  113. JsValue onRejected,
  114. PromiseCapability resultCapability)
  115. {
  116. var fulfilReaction = new PromiseReaction(ReactionType.Fulfill, resultCapability, onFulfilled);
  117. var rejectReaction = new PromiseReaction(ReactionType.Reject, resultCapability, onRejected);
  118. switch (promise.State)
  119. {
  120. case PromiseState.Pending:
  121. promise.PromiseFulfillReactions.Add(fulfilReaction);
  122. promise.PromiseRejectReactions.Add(rejectReaction);
  123. break;
  124. case PromiseState.Fulfilled:
  125. engine.AddToEventLoop(NewPromiseReactionJob(fulfilReaction, promise.Value));
  126. break;
  127. case PromiseState.Rejected:
  128. engine.AddToEventLoop(NewPromiseReactionJob(rejectReaction, promise.Value));
  129. break;
  130. default:
  131. ExceptionHelper.ThrowArgumentOutOfRangeException();
  132. break;
  133. }
  134. //https://tc39.es/ecma262/#sec-performpromisethen
  135. //...
  136. //13. If resultCapability is undefined, then
  137. // a. Return undefined
  138. //14. Else
  139. // a. Return resultCapability.[[Promise]]
  140. if (resultCapability is null)
  141. {
  142. return JsValue.Undefined;
  143. }
  144. return resultCapability.PromiseInstance;
  145. }
  146. }
  147. }