PromisePrototype.cs 6.9 KB

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