tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

AsyncDisposableStackObject.cpp (14564B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "builtin/AsyncDisposableStackObject.h"
      8 
      9 #include "vm/UsingHint.h"
     10 
     11 #include "vm/JSObject-inl.h"
     12 #include "vm/NativeObject-inl.h"
     13 
     14 using namespace js;
     15 
     16 /* static */ AsyncDisposableStackObject* AsyncDisposableStackObject::create(
     17    JSContext* cx, JS::Handle<JSObject*> proto,
     18    JS::Handle<JS::Value>
     19        initialDisposeCapability /* = JS::UndefinedHandleValue */) {
     20  AsyncDisposableStackObject* obj =
     21      NewObjectWithClassProto<AsyncDisposableStackObject>(cx, proto);
     22  if (!obj) {
     23    return nullptr;
     24  }
     25 
     26  MOZ_ASSERT(initialDisposeCapability.isUndefined() ||
     27             initialDisposeCapability.isObject());
     28  MOZ_ASSERT_IF(initialDisposeCapability.isObject(),
     29                initialDisposeCapability.toObject().is<ArrayObject>());
     30 
     31  obj->initReservedSlot(
     32      AsyncDisposableStackObject::DISPOSABLE_RESOURCE_STACK_SLOT,
     33      initialDisposeCapability);
     34  obj->initReservedSlot(
     35      AsyncDisposableStackObject::STATE_SLOT,
     36      JS::Int32Value(
     37          int32_t(AsyncDisposableStackObject::DisposableState::Pending)));
     38 
     39  return obj;
     40 }
     41 
     42 /**
     43 * Explicit Resource Management Proposal
     44 *
     45 * 27.4.1.1 AsyncDisposableStack ( )
     46 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asyncdisposablestack
     47 */
     48 /* static */ bool AsyncDisposableStackObject::construct(JSContext* cx,
     49                                                        unsigned argc,
     50                                                        JS::Value* vp) {
     51  JS::CallArgs args = CallArgsFromVp(argc, vp);
     52 
     53  // Step 1. If NewTarget is undefined, throw a TypeError exception.
     54  if (!ThrowIfNotConstructing(cx, args, "AsyncDisposableStack")) {
     55    return false;
     56  }
     57 
     58  // Step 2. Let asyncDisposableStack be ?
     59  // OrdinaryCreateFromConstructor(NewTarget,
     60  // "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
     61  // [[DisposeCapability]] »).
     62  // Step 3. Set asyncDisposableStack.[[AsyncDisposableState]] to pending.
     63  // Step 4. Set asyncDisposableStack.[[DisposeCapability]] to
     64  // NewDisposeCapability().
     65  JS::Rooted<JSObject*> proto(cx);
     66  if (!GetPrototypeFromBuiltinConstructor(
     67          cx, args, JSProto_AsyncDisposableStack, &proto)) {
     68    return false;
     69  }
     70 
     71  AsyncDisposableStackObject* obj =
     72      AsyncDisposableStackObject::create(cx, proto);
     73  if (!obj) {
     74    return false;
     75  }
     76 
     77  // Step 5. Return asyncDisposableStack.
     78  args.rval().setObject(*obj);
     79  return true;
     80 }
     81 
     82 /* static */ bool AsyncDisposableStackObject::is(JS::Handle<JS::Value> val) {
     83  return val.isObject() && val.toObject().is<AsyncDisposableStackObject>();
     84 }
     85 
     86 /**
     87 * Explicit Resource Management Proposal
     88 *
     89 * 27.4.3.6 AsyncDisposableStack.prototype.use ( value )
     90 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asyncdisposablestack.prototype.use
     91 */
     92 /* static */ bool AsyncDisposableStackObject::use_impl(
     93    JSContext* cx, const JS::CallArgs& args) {
     94  // Step 1. Let asyncDisposableStack be the this value.
     95  JS::Rooted<AsyncDisposableStackObject*> asyncDisposableStack(
     96      cx, &args.thisv().toObject().as<AsyncDisposableStackObject>());
     97 
     98  // Step 2. Perform ? RequireInternalSlot(asyncDisposableStack,
     99  // [[AsyncDisposableState]]).
    100  // (done by caller)
    101  // Step 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw
    102  // a ReferenceError exception.
    103  if (asyncDisposableStack->state() == DisposableState::Disposed) {
    104    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    105                              JSMSG_DISPOSABLE_STACK_DISPOSED);
    106    return false;
    107  }
    108 
    109  // Step 4. Perform ?
    110  // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]], value,
    111  // async-dispose).
    112  JS::Rooted<ArrayObject*> disposeCapability(
    113      cx, GetOrCreateDisposeCapability(cx, asyncDisposableStack));
    114  if (!disposeCapability) {
    115    return false;
    116  }
    117 
    118  JS::Rooted<JS::Value> val(cx, args.get(0));
    119  if (!AddDisposableResource(cx, disposeCapability, val, UsingHint::Async)) {
    120    return false;
    121  }
    122 
    123  // Step 5. Return value.
    124  args.rval().set(val);
    125  return true;
    126 }
    127 
    128 /* static */ bool AsyncDisposableStackObject::use(JSContext* cx, unsigned argc,
    129                                                  JS::Value* vp) {
    130  JS::CallArgs args = CallArgsFromVp(argc, vp);
    131  return JS::CallNonGenericMethod<is, use_impl>(cx, args);
    132 }
    133 
    134 /**
    135 * Explicit Resource Management Proposal
    136 *
    137 * 27.4.3.4 get AsyncDisposableStack.prototype.disposed
    138 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-get-asyncdisposablestack.prototype.disposed
    139 */
    140 /* static */ bool AsyncDisposableStackObject::disposed_impl(
    141    JSContext* cx, const JS::CallArgs& args) {
    142  // Step 1. Let disposableStack be the this value.
    143  auto* disposableStack =
    144      &args.thisv().toObject().as<AsyncDisposableStackObject>();
    145 
    146  // Step 2. Perform ? RequireInternalSlot(disposableStack,
    147  // [[DisposableState]]).
    148  // (done by caller)
    149  // Step 3. If disposableStack.[[DisposableState]] is disposed, return true.
    150  // Step 4. Otherwise, return false.
    151  args.rval().setBoolean(disposableStack->state() == DisposableState::Disposed);
    152  return true;
    153 }
    154 
    155 /* static */ bool AsyncDisposableStackObject::disposed(JSContext* cx,
    156                                                       unsigned argc,
    157                                                       JS::Value* vp) {
    158  JS::CallArgs args = CallArgsFromVp(argc, vp);
    159  return JS::CallNonGenericMethod<is, disposed_impl>(cx, args);
    160 }
    161 
    162 /**
    163 * Explicit Resource Management Proposal
    164 *
    165 * 27.4.3.5 AsyncDisposableStack.prototype.move ( )
    166 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asyncdisposablestack.prototype.move
    167 */
    168 /* static */ bool AsyncDisposableStackObject::move_impl(
    169    JSContext* cx, const JS::CallArgs& args) {
    170  // Step 1. Let asyncDisposableStack be the this value.
    171  JS::Rooted<AsyncDisposableStackObject*> asyncDisposableStack(
    172      cx, &args.thisv().toObject().as<AsyncDisposableStackObject>());
    173 
    174  // Step 2. Perform ? RequireInternalSlot(asyncDisposableStack,
    175  // [[AsyncDisposableState]]).
    176  // (done by caller)
    177  // Step 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw
    178  // a ReferenceError exception.
    179  if (asyncDisposableStack->state() == DisposableState::Disposed) {
    180    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    181                              JSMSG_DISPOSABLE_STACK_DISPOSED);
    182    return false;
    183  }
    184 
    185  // Step 4. Let newAsyncDisposableStack be ?
    186  // OrdinaryCreateFromConstructor(%AsyncDisposableStack%,
    187  // "%AsyncDisposableStack.prototype%", « [[AsyncDisposableState]],
    188  // [[DisposeCapability]] »).
    189  // Step 5. Set newAsyncDisposableStack.[[AsyncDisposableState]] to pending.
    190  // Step 6. Set newAsyncDisposableStack.[[DisposeCapability]] to
    191  // asyncDisposableStack.[[DisposeCapability]].
    192  JS::Rooted<JS::Value> existingDisposeCapability(
    193      cx, asyncDisposableStack->getReservedSlot(
    194              AsyncDisposableStackObject::DISPOSABLE_RESOURCE_STACK_SLOT));
    195  AsyncDisposableStackObject* newAsyncDisposableStack =
    196      AsyncDisposableStackObject::create(cx, nullptr,
    197                                         existingDisposeCapability);
    198  if (!newAsyncDisposableStack) {
    199    return false;
    200  }
    201 
    202  // Step 7. Set asyncDisposableStack.[[DisposeCapability]] to
    203  // NewDisposeCapability().
    204  asyncDisposableStack->clearDisposableResourceStack();
    205 
    206  // Step 8. Set asyncDisposableStack.[[AsyncDisposableState]] to disposed.
    207  asyncDisposableStack->setState(DisposableState::Disposed);
    208 
    209  // Step 9. Return newAsyncDisposableStack.
    210  args.rval().setObject(*newAsyncDisposableStack);
    211  return true;
    212 }
    213 
    214 /* static */ bool AsyncDisposableStackObject::move(JSContext* cx, unsigned argc,
    215                                                   JS::Value* vp) {
    216  JS::CallArgs args = CallArgsFromVp(argc, vp);
    217  return JS::CallNonGenericMethod<is, move_impl>(cx, args);
    218 }
    219 
    220 /**
    221 * Explicit Resource Management Proposal
    222 *
    223 * 27.4.3.2 AsyncDisposableStack.prototype.defer ( onDisposeAsync )
    224 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asyncdisposablestack.prototype.defer
    225 */
    226 /* static */ bool AsyncDisposableStackObject::defer_impl(
    227    JSContext* cx, const JS::CallArgs& args) {
    228  // Step 1. Let asyncDisposableStack be the this value.
    229  JS::Rooted<AsyncDisposableStackObject*> asyncDisposableStack(
    230      cx, &args.thisv().toObject().as<AsyncDisposableStackObject>());
    231 
    232  // Step 2. Perform ? RequireInternalSlot(asyncDisposableStack,
    233  // [[AsyncDisposableState]]).
    234  // (done by caller)
    235  // Step 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed,
    236  // throw a ReferenceError exception.
    237  if (asyncDisposableStack->state() == DisposableState::Disposed) {
    238    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    239                              JSMSG_DISPOSABLE_STACK_DISPOSED);
    240    return false;
    241  }
    242 
    243  // Step 4. If IsCallable(onDisposeAsync) is false, throw a TypeError
    244  // exception.
    245  JS::Handle<JS::Value> onDisposeAsync = args.get(0);
    246  if (!ThrowIfOnDisposeNotCallable(cx, onDisposeAsync)) {
    247    return false;
    248  }
    249 
    250  // Step 5. Perform ?
    251  // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
    252  // undefined, async-dispose, onDisposeAsync).
    253  JS::Rooted<ArrayObject*> disposeCapability(
    254      cx, GetOrCreateDisposeCapability(cx, asyncDisposableStack));
    255  if (!disposeCapability) {
    256    return false;
    257  }
    258 
    259  if (!AddDisposableResource(cx, disposeCapability, JS::UndefinedHandleValue,
    260                             UsingHint::Async, onDisposeAsync)) {
    261    return false;
    262  }
    263 
    264  // Step 6. Return undefined.
    265  args.rval().setUndefined();
    266  return true;
    267 }
    268 
    269 /* static */ bool AsyncDisposableStackObject::defer(JSContext* cx,
    270                                                    unsigned argc,
    271                                                    JS::Value* vp) {
    272  JS::CallArgs args = CallArgsFromVp(argc, vp);
    273  return JS::CallNonGenericMethod<is, defer_impl>(cx, args);
    274 }
    275 
    276 /**
    277 * Explicit Resource Management Proposal
    278 *
    279 * 27.4.3.1 AsyncDisposableStack.prototype.adopt ( value, onDisposeAsync )
    280 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-asyncdisposablestack.prototype.adopt
    281 */
    282 /* static */ bool AsyncDisposableStackObject::adopt_impl(
    283    JSContext* cx, const JS::CallArgs& args) {
    284  // Step 1. Let asyncDisposableStack be the this value.
    285  JS::Rooted<AsyncDisposableStackObject*> asyncDisposableStack(
    286      cx, &args.thisv().toObject().as<AsyncDisposableStackObject>());
    287 
    288  // Step 2. Perform ? RequireInternalSlot(asyncDisposableStack,
    289  // [[AsyncDisposableState]]).
    290  // (done by caller)
    291  // Step 3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw
    292  // a ReferenceError exception.
    293  if (asyncDisposableStack->state() == DisposableState::Disposed) {
    294    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    295                              JSMSG_DISPOSABLE_STACK_DISPOSED);
    296    return false;
    297  }
    298 
    299  // Step 4. If IsCallable(onDisposeAsync) is false, throw a TypeError
    300  // exception.
    301  JS::Handle<JS::Value> onDisposeAsync = args.get(1);
    302  if (!ThrowIfOnDisposeNotCallable(cx, onDisposeAsync)) {
    303    return false;
    304  }
    305 
    306  // Step 5. Let closure be a new Abstract Closure with no parameters that
    307  // captures value and onDisposeAsync and performs the following steps when
    308  // called:
    309  // Step 5.a. (see AdoptClosure)
    310  // Step 6. Let F be CreateBuiltinFunction(closure, 0, "", « »).
    311  JS::Handle<PropertyName*> funName = cx->names().empty_;
    312  JS::Rooted<JSFunction*> F(
    313      cx, NewNativeFunction(cx, AdoptClosure, 0, funName,
    314                            gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
    315  if (!F) {
    316    return false;
    317  }
    318  JS::Handle<JS::Value> value = args.get(0);
    319  F->initExtendedSlot(AdoptClosureSlot_ValueSlot, value);
    320  F->initExtendedSlot(AdoptClosureSlot_OnDisposeSlot, onDisposeAsync);
    321 
    322  // Step 7. Perform ?
    323  // AddDisposableResource(asyncDisposableStack.[[DisposeCapability]],
    324  // undefined, async-dispose, F).
    325  JS::Rooted<ArrayObject*> disposeCapability(
    326      cx, GetOrCreateDisposeCapability(cx, asyncDisposableStack));
    327  if (!disposeCapability) {
    328    return false;
    329  }
    330 
    331  JS::Rooted<JS::Value> FVal(cx, ObjectValue(*F));
    332  if (!AddDisposableResource(cx, disposeCapability, JS::UndefinedHandleValue,
    333                             UsingHint::Async, FVal)) {
    334    return false;
    335  }
    336 
    337  // Step 8. Return value.
    338  args.rval().set(value);
    339  return true;
    340 }
    341 
    342 /* static */ bool AsyncDisposableStackObject::adopt(JSContext* cx,
    343                                                    unsigned argc,
    344                                                    JS::Value* vp) {
    345  JS::CallArgs args = CallArgsFromVp(argc, vp);
    346  return JS::CallNonGenericMethod<is, adopt_impl>(cx, args);
    347 }
    348 
    349 const JSPropertySpec AsyncDisposableStackObject::properties[] = {
    350    JS_STRING_SYM_PS(toStringTag, "AsyncDisposableStack", JSPROP_READONLY),
    351    JS_PSG("disposed", disposed, 0),
    352    JS_PS_END,
    353 };
    354 
    355 const JSFunctionSpec AsyncDisposableStackObject::methods[] = {
    356    JS_FN("adopt", AsyncDisposableStackObject::adopt, 2, 0),
    357    JS_FN("defer", AsyncDisposableStackObject::defer, 1, 0),
    358    JS_SELF_HOSTED_FN("disposeAsync", "$AsyncDisposableStackDisposeAsync", 0,
    359                      0),
    360    JS_FN("move", AsyncDisposableStackObject::move, 0, 0),
    361    JS_FN("use", AsyncDisposableStackObject::use, 1, 0),
    362    JS_SELF_HOSTED_SYM_FN(asyncDispose, "$AsyncDisposableStackDisposeAsync", 0,
    363                          0),
    364    JS_FS_END,
    365 };
    366 
    367 const ClassSpec AsyncDisposableStackObject::classSpec_ = {
    368    GenericCreateConstructor<AsyncDisposableStackObject::construct, 0,
    369                             gc::AllocKind::FUNCTION>,
    370    GenericCreatePrototype<AsyncDisposableStackObject>,
    371    nullptr,
    372    nullptr,
    373    AsyncDisposableStackObject::methods,
    374    AsyncDisposableStackObject::properties,
    375    nullptr,
    376 };
    377 
    378 const JSClass AsyncDisposableStackObject::class_ = {
    379    "AsyncDisposableStack",
    380    JSCLASS_HAS_RESERVED_SLOTS(AsyncDisposableStackObject::RESERVED_SLOTS) |
    381        JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncDisposableStack),
    382    JS_NULL_CLASS_OPS,
    383    &AsyncDisposableStackObject::classSpec_,
    384 };
    385 
    386 const JSClass AsyncDisposableStackObject::protoClass_ = {
    387    "AsyncDisposableStack.prototype",
    388    JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncDisposableStack),
    389    JS_NULL_CLASS_OPS,
    390    &AsyncDisposableStackObject::classSpec_,
    391 };