PromiseOperations.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  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(JsPromise promise, ObjectInstance thenable, ICallable thenMethod)
  76. {
  77. return () =>
  78. {
  79. var (resolve, reject) = promise.CreateResolvingFunctions();
  80. try
  81. {
  82. thenMethod.Call(thenable, new[] { resolve as JsValue, reject });
  83. }
  84. catch (JavaScriptException e)
  85. {
  86. reject.Call(JsValue.Undefined, new[] { e.Error });
  87. }
  88. };
  89. }
  90. // https://tc39.es/ecma262/#sec-triggerpromisereactions
  91. //
  92. // 1. For each element reaction of reactions, do
  93. // a. Let job be NewPromiseReactionJob(reaction, argument).
  94. // b. Perform HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]]).
  95. // 2. Return undefined.
  96. internal static JsValue TriggerPromiseReactions(Engine engine, List<PromiseReaction> reactions, JsValue result)
  97. {
  98. foreach (var reaction in reactions)
  99. {
  100. engine.AddToEventLoop(NewPromiseReactionJob(reaction, result));
  101. }
  102. return JsValue.Undefined;
  103. }
  104. // https://tc39.es/ecma262/#sec-performpromisethen
  105. internal static JsValue PerformPromiseThen(
  106. Engine engine,
  107. JsPromise promise,
  108. JsValue onFulfilled,
  109. JsValue onRejected,
  110. PromiseCapability resultCapability)
  111. {
  112. var fulfilReaction = new PromiseReaction(ReactionType.Fulfill, resultCapability, onFulfilled);
  113. var rejectReaction = new PromiseReaction(ReactionType.Reject, resultCapability, onRejected);
  114. switch (promise.State)
  115. {
  116. case PromiseState.Pending:
  117. promise.PromiseFulfillReactions.Add(fulfilReaction);
  118. promise.PromiseRejectReactions.Add(rejectReaction);
  119. break;
  120. case PromiseState.Fulfilled:
  121. engine.AddToEventLoop(NewPromiseReactionJob(fulfilReaction, promise.Value));
  122. break;
  123. case PromiseState.Rejected:
  124. engine.AddToEventLoop(NewPromiseReactionJob(rejectReaction, promise.Value));
  125. break;
  126. default:
  127. ExceptionHelper.ThrowArgumentOutOfRangeException();
  128. break;
  129. }
  130. //https://tc39.es/ecma262/#sec-performpromisethen
  131. //...
  132. //13. If resultCapability is undefined, then
  133. // a. Return undefined
  134. //14. Else
  135. // a. Return resultCapability.[[Promise]]
  136. if (resultCapability is null)
  137. {
  138. return JsValue.Undefined;
  139. }
  140. return resultCapability.PromiseInstance;
  141. }
  142. }
  143. }