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