PromisePrototype.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. using Jint.Native.Object;
  2. using Jint.Native.Symbol;
  3. using Jint.Runtime;
  4. using Jint.Runtime.Descriptors;
  5. using Jint.Runtime.Interop;
  6. namespace Jint.Native.Promise;
  7. internal sealed class PromisePrototype : Prototype
  8. {
  9. private readonly PromiseConstructor _constructor;
  10. internal PromisePrototype(
  11. Engine engine,
  12. Realm realm,
  13. PromiseConstructor constructor,
  14. ObjectPrototype objectPrototype) : base(engine, realm)
  15. {
  16. _prototype = objectPrototype;
  17. _constructor = constructor;
  18. }
  19. protected override void Initialize()
  20. {
  21. const PropertyFlag lengthFlags = PropertyFlag.Configurable;
  22. const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
  23. var properties = new PropertyDictionary(5, checkExistingKeys: false)
  24. {
  25. ["constructor"] = new(_constructor, PropertyFlag.NonEnumerable),
  26. ["then"] = new(new ClrFunction(Engine, "then", Then, 2, lengthFlags), propertyFlags),
  27. ["catch"] = new(new ClrFunction(Engine, "catch", Catch, 1, lengthFlags), propertyFlags),
  28. ["finally"] = new(new ClrFunction(Engine, "finally", Finally, 1, lengthFlags), propertyFlags)
  29. };
  30. SetProperties(properties);
  31. var symbols = new SymbolDictionary(1)
  32. {
  33. [GlobalSymbolRegistry.ToStringTag] = new(new JsString("Promise"), PropertyFlag.Configurable)
  34. };
  35. SetSymbols(symbols);
  36. }
  37. // https://tc39.es/ecma262/#sec-promise.prototype.then
  38. // When the then method is called with arguments onFulfilled and onRejected,
  39. // the following steps are taken:
  40. //
  41. // 1. Let promise be the this value.
  42. // 2. If IsPromise(promise) is false, throw a TypeError exception.
  43. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  44. // 4. Let resultCapability be ? NewPromiseCapability(C).
  45. // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
  46. private JsValue Then(JsValue thisValue, JsCallArguments arguments)
  47. {
  48. // 1. Let promise be the this value.
  49. // 2. If IsPromise(promise) is false, throw a TypeError exception.
  50. var promise = thisValue as JsPromise;
  51. if (promise is null)
  52. {
  53. Throw.TypeError(_realm, "Method Promise.prototype.then called on incompatible receiver");
  54. }
  55. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  56. var ctor = SpeciesConstructor(promise, _realm.Intrinsics.Promise);
  57. // 4. Let resultCapability be ? NewPromiseCapability(C).
  58. var capability = PromiseConstructor.NewPromiseCapability(_engine, (JsValue) ctor);
  59. // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
  60. return PromiseOperations.PerformPromiseThen(_engine, promise, arguments.At(0), arguments.At(1), capability);
  61. }
  62. // https://tc39.es/ecma262/#sec-promise.prototype.catch
  63. //
  64. // When the catch method is called with argument onRejected,
  65. // the following steps are taken:
  66. //
  67. // 1. Let promise be the this value.
  68. // 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
  69. private JsValue Catch(JsValue thisValue, JsCallArguments arguments) =>
  70. _engine.Invoke(thisValue, "then", [Undefined, arguments.At(0)]);
  71. // https://tc39.es/ecma262/#sec-promise.prototype.finally
  72. private JsValue Finally(JsValue thisValue, JsCallArguments arguments)
  73. {
  74. // 1. Let promise be the this value.
  75. // 2. If Type(promise) is not Object, throw a TypeError exception.
  76. var promise = thisValue as ObjectInstance;
  77. if (promise is null)
  78. {
  79. Throw.TypeError(_realm, "this passed to Promise.prototype.finally is not an object");
  80. }
  81. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  82. // 4. Assert: IsConstructor(C) is true.
  83. var ctor = SpeciesConstructor(promise, _realm.Intrinsics.Promise);
  84. JsValue thenFinally;
  85. JsValue catchFinally;
  86. var onFinally = arguments.At(0);
  87. // 5. If IsCallable(onFinally) is false, then
  88. if (onFinally is not ICallable onFinallyFunc)
  89. {
  90. // a. Let thenFinally be onFinally.
  91. // b. Let catchFinally be onFinally.
  92. thenFinally = onFinally;
  93. catchFinally = onFinally;
  94. }
  95. else
  96. {
  97. thenFinally = ThenFinallyFunctions(onFinallyFunc, ctor);
  98. catchFinally = CatchFinallyFunctions(onFinallyFunc, ctor);
  99. }
  100. // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
  101. return _engine.Invoke(promise, "then", [thenFinally, catchFinally]);
  102. }
  103. // https://tc39.es/ecma262/#sec-thenfinallyfunctions
  104. private ClrFunction ThenFinallyFunctions(ICallable onFinally, IConstructor ctor) =>
  105. new ClrFunction(_engine, "", (_, args) =>
  106. {
  107. var value = args.At(0);
  108. //4. Let result be ? Call(onFinally, undefined).
  109. var result = onFinally.Call(Undefined, Arguments.Empty);
  110. // 7. Let promise be ? PromiseResolve(C, result).
  111. var promise = _realm.Intrinsics.Promise.Resolve((JsValue) ctor, [result]);
  112. // 8. Let valueThunk be equivalent to a function that returns value.
  113. var valueThunk = new ClrFunction(_engine, "", (_, _) => value);
  114. // 9. Return ? Invoke(promise, "then", « valueThunk »).
  115. return _engine.Invoke(promise, "then", [valueThunk]);
  116. }, 1, PropertyFlag.Configurable);
  117. // https://tc39.es/ecma262/#sec-catchfinallyfunctions
  118. private ClrFunction CatchFinallyFunctions(ICallable onFinally, IConstructor ctor) =>
  119. new ClrFunction(_engine, "", (_, args) =>
  120. {
  121. var reason = args.At(0);
  122. //4. Let result be ? Call(onFinally, undefined).
  123. var result = onFinally.Call(Undefined, Arguments.Empty);
  124. // 7. Let promise be ? PromiseResolve(C, result).
  125. var promise = _realm.Intrinsics.Promise.Resolve((JsValue) ctor, [result]);
  126. // 8. Let thrower be equivalent to a function that throws reason.
  127. var thrower = new ClrFunction(_engine, "", (_, _) => throw new JavaScriptException(reason));
  128. // 9. Return ? Invoke(promise, "then", « thrower »).
  129. return _engine.Invoke(promise, "then", [thrower]);
  130. }, 1, PropertyFlag.Configurable);
  131. }