DisposableStackPrototype.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. * Copyright (c) 2022, David Tuin <davidot@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/AbstractOperations.h>
  7. #include <LibJS/Runtime/DisposableStack.h>
  8. #include <LibJS/Runtime/DisposableStackConstructor.h>
  9. #include <LibJS/Runtime/DisposableStackPrototype.h>
  10. #include <LibJS/Runtime/NativeFunction.h>
  11. namespace JS {
  12. GC_DEFINE_ALLOCATOR(DisposableStackPrototype);
  13. DisposableStackPrototype::DisposableStackPrototype(Realm& realm)
  14. : PrototypeObject(realm.intrinsics().object_prototype())
  15. {
  16. }
  17. void DisposableStackPrototype::initialize(Realm& realm)
  18. {
  19. auto& vm = this->vm();
  20. Base::initialize(realm);
  21. u8 attr = Attribute::Writable | Attribute::Configurable;
  22. define_native_accessor(realm, vm.names.disposed, disposed_getter, {}, attr);
  23. define_native_function(realm, vm.names.dispose, dispose, 0, attr);
  24. define_native_function(realm, vm.names.use, use, 1, attr);
  25. define_native_function(realm, vm.names.adopt, adopt, 2, attr);
  26. define_native_function(realm, vm.names.defer, defer, 1, attr);
  27. define_native_function(realm, vm.names.move, move_, 0, attr);
  28. // 11.3.3.7 DisposableStack.prototype [ @@dispose ] (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@dispose
  29. define_direct_property(vm.well_known_symbol_dispose(), get_without_side_effects(vm.names.dispose), attr);
  30. // 11.3.3.8 DisposableStack.prototype [ @@toStringTag ], https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype-@@toStringTag
  31. define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.DisposableStack.as_string()), Attribute::Configurable);
  32. }
  33. // 11.3.3.1 get DisposableStack.prototype.disposed, https://tc39.es/proposal-explicit-resource-management/#sec-get-disposablestack.prototype.disposed
  34. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::disposed_getter)
  35. {
  36. // 1. Let disposableStack be the this value.
  37. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  38. auto disposable_stack = TRY(typed_this_object(vm));
  39. // 3. If disposableStack.[[DisposableState]] is disposed, return true.
  40. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  41. return Value(true);
  42. // 4. Otherwise, return false.
  43. return Value(false);
  44. }
  45. // 11.3.3.2 DisposableStack.prototype.dispose (), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.dispose
  46. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::dispose)
  47. {
  48. // 1. Let disposableStack be the this value.
  49. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  50. auto disposable_stack = TRY(typed_this_object(vm));
  51. // 3. If disposableStack.[[DisposableState]] is disposed, return undefined.
  52. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  53. return js_undefined();
  54. // 4. Set disposableStack.[[DisposableState]] to disposed.
  55. disposable_stack->set_disposed();
  56. // 5. Return DisposeResources(disposableStack, NormalCompletion(undefined)).
  57. return *TRY(dispose_resources(vm, disposable_stack->disposable_resource_stack(), Completion { js_undefined() }));
  58. }
  59. // 11.3.3.3 DisposableStack.prototype.use( value ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.use
  60. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::use)
  61. {
  62. auto value = vm.argument(0);
  63. // 1. Let disposableStack be the this value.
  64. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  65. auto disposable_stack = TRY(typed_this_object(vm));
  66. // 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
  67. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  68. return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
  69. // 4. If value is neither null nor undefined, then
  70. if (!value.is_nullish()) {
  71. // a. If Type(value) is not Object, throw a TypeError exception.
  72. if (!value.is_object())
  73. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
  74. // FIXME: This should be TRY in the spec
  75. // b. Let method be GetDisposeMethod(value, sync-dispose).
  76. auto method = TRY(get_dispose_method(vm, value, Environment::InitializeBindingHint::SyncDispose));
  77. // c. If method is undefined, then
  78. if (!method.ptr()) {
  79. // i. Throw a TypeError exception.
  80. return vm.throw_completion<TypeError>(ErrorType::NoDisposeMethod, value.to_string_without_side_effects());
  81. }
  82. // d. Else,
  83. // i. Perform ? AddDisposableResource(disposableStack, value, sync-dispose, method).
  84. // FIXME: Fairly sure this can't fail, see https://github.com/tc39/proposal-explicit-resource-management/pull/142
  85. MUST(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), value, Environment::InitializeBindingHint::SyncDispose, method));
  86. }
  87. // 5. Return value.
  88. return value;
  89. }
  90. // 11.3.3.4 DisposableStack.prototype.adopt( value, onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.adopt
  91. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::adopt)
  92. {
  93. auto& realm = *vm.current_realm();
  94. auto value = vm.argument(0);
  95. auto on_dispose = vm.argument(1);
  96. // 1. Let disposableStack be the this value.
  97. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  98. auto disposable_stack = TRY(typed_this_object(vm));
  99. // 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
  100. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  101. return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
  102. // 4. If IsCallable(onDispose) is false, throw a TypeError exception.
  103. if (!on_dispose.is_function())
  104. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose.to_string_without_side_effects());
  105. // 5. Let F be a new built-in function object as defined in 11.3.3.4.1.
  106. // 6. Set F.[[Argument]] to value.
  107. // 7. Set F.[[OnDisposeCallback]] to onDispose.
  108. // 11.3.3.4.1 DisposableStack Adopt Callback Functions, https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack-adopt-callback-functions
  109. // A DisposableStack adopt callback function is an anonymous built-in function object that has [[Argument]] and [[OnDisposeCallback]] internal slots.
  110. auto function = NativeFunction::create(
  111. realm, [argument = make_root(value), callback = make_root(on_dispose)](VM& vm) {
  112. // When a DisposableStack adopt callback function is called, the following steps are taken:
  113. // 1. Let F be the active function object.
  114. // 2. Assert: IsCallable(F.[[OnDisposeCallback]]) is true.
  115. VERIFY(callback.value().is_function());
  116. // 3. Return Call(F.[[OnDisposeCallback]], undefined, « F.[[Argument]] »).
  117. return call(vm, callback.value(), js_undefined(), argument.value());
  118. },
  119. 0, "");
  120. // 8. Perform ? AddDisposableResource(disposableStack, undefined, sync-dispose, F).
  121. TRY(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, function));
  122. // 9. Return value.
  123. return value;
  124. }
  125. // 11.3.3.5 DisposableStack.prototype.defer( onDispose ), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.defer
  126. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::defer)
  127. {
  128. auto on_dispose = vm.argument(0);
  129. // 1. Let disposableStack be the this value.
  130. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  131. auto disposable_stack = TRY(typed_this_object(vm));
  132. // 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
  133. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  134. return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
  135. // 4. If IsCallable(onDispose) is false, throw a TypeError exception.
  136. if (!on_dispose.is_function())
  137. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, on_dispose.to_string_without_side_effects());
  138. // 5. Perform ? AddDisposableResource(disposableStack, undefined, sync-dispose, onDispose).
  139. TRY(add_disposable_resource(vm, disposable_stack->disposable_resource_stack(), js_undefined(), Environment::InitializeBindingHint::SyncDispose, &on_dispose.as_function()));
  140. // 6. Return undefined.
  141. return js_undefined();
  142. }
  143. // 11.3.3.6 DisposableStack.prototype.move(), https://tc39.es/proposal-explicit-resource-management/#sec-disposablestack.prototype.move
  144. JS_DEFINE_NATIVE_FUNCTION(DisposableStackPrototype::move_)
  145. {
  146. // 1. Let disposableStack be the this value.
  147. // 2. Perform ? RequireInternalSlot(disposableStack, [[DisposableState]]).
  148. auto disposable_stack = TRY(typed_this_object(vm));
  149. // 3. If disposableStack.[[DisposableState]] is disposed, throw a ReferenceError exception.
  150. if (disposable_stack->disposable_state() == DisposableStack::DisposableState::Disposed)
  151. return vm.throw_completion<ReferenceError>(ErrorType::DisposableStackAlreadyDisposed);
  152. // 4. Let newDisposableStack be ? OrdinaryCreateFromConstructor(%DisposableStack%, "%DisposableStack.prototype%", « [[DisposableState]], [[DisposableResourceStack]] »).
  153. auto new_disposable_stack = TRY(ordinary_create_from_constructor<DisposableStack>(vm, *vm.current_realm()->intrinsics().disposable_stack_constructor(), &Intrinsics::disposable_stack_prototype, disposable_stack->disposable_resource_stack()));
  154. // 5. Set newDisposableStack.[[DisposableState]] to pending.
  155. // 6. Set newDisposableStack.[[DisposableResourceStack]] to disposableStack.[[DisposableResourceStack]].
  156. // NOTE: Already done in the constructor
  157. // 7. Set disposableStack.[[DisposableResourceStack]] to a new empty List.
  158. disposable_stack->disposable_resource_stack().clear();
  159. // 8. Set disposableStack.[[DisposableState]] to disposed.
  160. disposable_stack->set_disposed();
  161. // 9. Return newDisposableStack.
  162. return new_disposable_stack;
  163. }
  164. }