tor-browser

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

DisposableStackObject.cpp (13771B)


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