PromiseOperations.cs 6.2 KB

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