PromisePrototype.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /*
  2. * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Function.h>
  7. #include <LibJS/Runtime/AbstractOperations.h>
  8. #include <LibJS/Runtime/Error.h>
  9. #include <LibJS/Runtime/GlobalObject.h>
  10. #include <LibJS/Runtime/Promise.h>
  11. #include <LibJS/Runtime/PromiseCapability.h>
  12. #include <LibJS/Runtime/PromiseConstructor.h>
  13. #include <LibJS/Runtime/PromisePrototype.h>
  14. namespace JS {
  15. GC_DEFINE_ALLOCATOR(PromisePrototype);
  16. PromisePrototype::PromisePrototype(Realm& realm)
  17. : PrototypeObject(realm.intrinsics().object_prototype())
  18. {
  19. }
  20. void PromisePrototype::initialize(Realm& realm)
  21. {
  22. auto& vm = this->vm();
  23. Base::initialize(realm);
  24. u8 attr = Attribute::Writable | Attribute::Configurable;
  25. define_native_function(realm, vm.names.then, then, 2, attr);
  26. define_native_function(realm, vm.names.catch_, catch_, 1, attr);
  27. define_native_function(realm, vm.names.finally, finally, 1, attr);
  28. // 27.2.5.5 Promise.prototype [ @@toStringTag ], https://tc39.es/ecma262/#sec-promise.prototype-@@tostringtag
  29. define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.Promise.as_string()), Attribute::Configurable);
  30. }
  31. // 27.2.5.4 Promise.prototype.then ( onFulfilled, onRejected ), https://tc39.es/ecma262/#sec-promise.prototype.then
  32. JS_DEFINE_NATIVE_FUNCTION(PromisePrototype::then)
  33. {
  34. auto& realm = *vm.current_realm();
  35. auto on_fulfilled = vm.argument(0);
  36. auto on_rejected = vm.argument(1);
  37. // 1. Let promise be the this value.
  38. // 2. If IsPromise(promise) is false, throw a TypeError exception.
  39. auto promise = TRY(typed_this_object(vm));
  40. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  41. auto* constructor = TRY(species_constructor(vm, promise, realm.intrinsics().promise_constructor()));
  42. // 4. Let resultCapability be ? NewPromiseCapability(C).
  43. auto result_capability = TRY(new_promise_capability(vm, constructor));
  44. // 5. Return PerformPromiseThen(promise, onFulfilled, onRejected, resultCapability).
  45. return promise->perform_then(on_fulfilled, on_rejected, result_capability);
  46. }
  47. // 27.2.5.1 Promise.prototype.catch ( onRejected ), https://tc39.es/ecma262/#sec-promise.prototype.catch
  48. JS_DEFINE_NATIVE_FUNCTION(PromisePrototype::catch_)
  49. {
  50. auto on_rejected = vm.argument(0);
  51. // 1. Let promise be the this value.
  52. auto this_value = vm.this_value();
  53. // 2. Return ? Invoke(promise, "then", « undefined, onRejected »).
  54. return TRY(this_value.invoke(vm, vm.names.then, js_undefined(), on_rejected));
  55. }
  56. // 27.2.5.3 Promise.prototype.finally ( onFinally ), https://tc39.es/ecma262/#sec-promise.prototype.finally
  57. JS_DEFINE_NATIVE_FUNCTION(PromisePrototype::finally)
  58. {
  59. auto& realm = *vm.current_realm();
  60. auto on_finally = vm.argument(0);
  61. // 1. Let promise be the this value.
  62. auto promise = vm.this_value();
  63. // 2. If Type(promise) is not Object, throw a TypeError exception.
  64. if (!promise.is_object())
  65. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, promise.to_string_without_side_effects());
  66. // 3. Let C be ? SpeciesConstructor(promise, %Promise%).
  67. auto* constructor = TRY(species_constructor(vm, promise.as_object(), realm.intrinsics().promise_constructor()));
  68. // 4. Assert: IsConstructor(C) is true.
  69. VERIFY(constructor);
  70. Value then_finally;
  71. Value catch_finally;
  72. // 5. If IsCallable(onFinally) is false, then
  73. if (!on_finally.is_function()) {
  74. // a. Let thenFinally be onFinally.
  75. then_finally = on_finally;
  76. // b. Let catchFinally be onFinally.
  77. catch_finally = on_finally;
  78. }
  79. // 6. Else,
  80. else {
  81. // a. Let thenFinallyClosure be a new Abstract Closure with parameters (value) that captures onFinally and C and performs the following steps when called:
  82. auto then_finally_closure = [constructor, on_finally](auto& vm) -> ThrowCompletionOr<Value> {
  83. auto& realm = *vm.current_realm();
  84. auto value = vm.argument(0);
  85. // i. Let result be ? Call(onFinally, undefined).
  86. auto result = TRY(call(vm, on_finally, js_undefined()));
  87. // ii. Let promise be ? PromiseResolve(C, result).
  88. auto promise = TRY(promise_resolve(vm, *constructor, result));
  89. // iii. Let returnValue be a new Abstract Closure with no parameters that captures value and performs the following steps when called:
  90. auto return_value = [value](auto&) -> ThrowCompletionOr<Value> {
  91. // 1. Return value.
  92. return value;
  93. };
  94. // iv. Let valueThunk be CreateBuiltinFunction(returnValue, 0, "", « »).
  95. auto value_thunk = NativeFunction::create(realm, move(return_value), 0, "");
  96. // v. Return ? Invoke(promise, "then", « valueThunk »).
  97. return TRY(Value(promise).invoke(vm, vm.names.then, value_thunk));
  98. };
  99. // b. Let thenFinally be CreateBuiltinFunction(thenFinallyClosure, 1, "", « »).
  100. then_finally = NativeFunction::create(realm, move(then_finally_closure), 1, "");
  101. // c. Let catchFinallyClosure be a new Abstract Closure with parameters (reason) that captures onFinally and C and performs the following steps when called:
  102. auto catch_finally_closure = [constructor, on_finally](auto& vm) -> ThrowCompletionOr<Value> {
  103. auto& realm = *vm.current_realm();
  104. auto reason = vm.argument(0);
  105. // i. Let result be ? Call(onFinally, undefined).
  106. auto result = TRY(call(vm, on_finally, js_undefined()));
  107. // ii. Let promise be ? PromiseResolve(C, result).
  108. auto promise = TRY(promise_resolve(vm, *constructor, result));
  109. // iii. Let throwReason be a new Abstract Closure with no parameters that captures reason and performs the following steps when called:
  110. auto throw_reason = [reason](auto&) -> ThrowCompletionOr<Value> {
  111. // 1. Return ThrowCompletion(reason).
  112. return throw_completion(reason);
  113. };
  114. // iv. Let thrower be CreateBuiltinFunction(throwReason, 0, "", « »).
  115. auto thrower = NativeFunction::create(realm, move(throw_reason), 0, "");
  116. // v. Return ? Invoke(promise, "then", « thrower »).
  117. return TRY(Value(promise).invoke(vm, vm.names.then, thrower));
  118. };
  119. // d. Let catchFinally be CreateBuiltinFunction(catchFinallyClosure, 1, "", « »).
  120. catch_finally = NativeFunction::create(realm, move(catch_finally_closure), 1, "");
  121. }
  122. // 7. Return ? Invoke(promise, "then", « thenFinally, catchFinally »).
  123. return TRY(promise.invoke(vm, vm.names.then, then_finally, catch_finally));
  124. }
  125. }