PromiseOperations.cs 6.8 KB

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