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 };