PromisePrototype.cs 6.4 KB

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