GeneratorObject.cpp (17983B)
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 "vm/GeneratorObject.h" 8 9 #include "frontend/ParserAtom.h" 10 #ifdef DEBUG 11 # include "js/friend/DumpFunctions.h" // js::DumpObject, js::DumpValue 12 #endif 13 #include "js/PropertySpec.h" 14 #include "vm/AsyncFunction.h" 15 #include "vm/AsyncIteration.h" 16 #include "vm/FunctionFlags.h" // js::FunctionFlags 17 #include "vm/GlobalObject.h" 18 #include "vm/JSObject.h" 19 #include "vm/PlainObject.h" // js::PlainObject 20 21 #include "debugger/DebugAPI-inl.h" 22 #include "vm/Stack-inl.h" 23 24 using namespace js; 25 26 AbstractGeneratorObject* AbstractGeneratorObject::create( 27 JSContext* cx, HandleFunction callee, HandleScript script, 28 HandleObject environmentChain, Handle<ArgumentsObject*> argsObject) { 29 Rooted<AbstractGeneratorObject*> genObj(cx); 30 if (!callee->isAsync()) { 31 genObj = GeneratorObject::create(cx, callee); 32 } else if (callee->isGenerator()) { 33 genObj = AsyncGeneratorObject::create(cx, callee); 34 } else { 35 genObj = AsyncFunctionGeneratorObject::create(cx, callee); 36 } 37 if (!genObj) { 38 return nullptr; 39 } 40 41 genObj->setCallee(*callee); 42 genObj->setEnvironmentChain(*environmentChain); 43 if (argsObject) { 44 genObj->setArgsObj(*argsObject.get()); 45 } 46 47 ArrayObject* stack = NewDenseFullyAllocatedArray(cx, script->nslots()); 48 if (!stack) { 49 return nullptr; 50 } 51 52 genObj->setStackStorage(*stack); 53 54 // Note: This assumes that a Warp frame cannot be the target of 55 // the debugger, as we do not call OnNewGenerator. 56 return genObj; 57 } 58 59 JSObject* AbstractGeneratorObject::createFromFrame(JSContext* cx, 60 AbstractFramePtr frame) { 61 MOZ_ASSERT(frame.isGeneratorFrame()); 62 MOZ_ASSERT(!frame.isConstructing()); 63 64 if (frame.isModuleFrame()) { 65 return createModuleGenerator(cx, frame); 66 } 67 68 RootedFunction fun(cx, frame.callee()); 69 Rooted<ArgumentsObject*> maybeArgs( 70 cx, frame.script()->needsArgsObj() ? &frame.argsObj() : nullptr); 71 RootedObject environmentChain(cx, frame.environmentChain()); 72 73 RootedScript script(cx, frame.script()); 74 Rooted<AbstractGeneratorObject*> genObj( 75 cx, AbstractGeneratorObject::create(cx, fun, script, environmentChain, 76 maybeArgs)); 77 if (!genObj) { 78 return nullptr; 79 } 80 81 if (!DebugAPI::onNewGenerator(cx, frame, genObj)) { 82 return nullptr; 83 } 84 85 return genObj; 86 } 87 88 JSObject* AbstractGeneratorObject::createModuleGenerator( 89 JSContext* cx, AbstractFramePtr frame) { 90 Rooted<ModuleObject*> module(cx, frame.script()->module()); 91 Rooted<AbstractGeneratorObject*> genObj(cx); 92 genObj = AsyncFunctionGeneratorObject::create(cx, module); 93 if (!genObj) { 94 return nullptr; 95 } 96 97 // Create a handler function to wrap the module's script. This way 98 // we can access it later and restore the state. 99 Handle<PropertyName*> funName = cx->names().empty_; 100 RootedFunction handlerFun( 101 cx, NewFunctionWithProto(cx, nullptr, 0, 102 FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC, 103 nullptr, funName, nullptr, 104 gc::AllocKind::FUNCTION, GenericObject)); 105 if (!handlerFun) { 106 return nullptr; 107 } 108 handlerFun->initScript(module->script()); 109 110 genObj->setCallee(*handlerFun); 111 genObj->setEnvironmentChain(*frame.environmentChain()); 112 113 ArrayObject* stack = 114 NewDenseFullyAllocatedArray(cx, module->script()->nslots()); 115 if (!stack) { 116 return nullptr; 117 } 118 119 genObj->setStackStorage(*stack); 120 121 if (!DebugAPI::onNewGenerator(cx, frame, genObj)) { 122 return nullptr; 123 } 124 125 return genObj; 126 } 127 128 void AbstractGeneratorObject::trace(JSTracer* trc) { 129 DebugAPI::traceGeneratorFrame(trc, this); 130 } 131 132 bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj, 133 AbstractFramePtr frame, 134 const jsbytecode* pc, unsigned nvalues) { 135 MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield || 136 JSOp(*pc) == JSOp::Await); 137 138 auto genObj = obj.as<AbstractGeneratorObject>(); 139 MOZ_ASSERT(!genObj->hasStackStorage() || genObj->isStackStorageEmpty()); 140 MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Await, genObj->callee().isAsync()); 141 MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Yield, genObj->callee().isGenerator()); 142 143 if (nvalues > 0) { 144 ArrayObject* stack = nullptr; 145 MOZ_ASSERT(genObj->hasStackStorage()); 146 stack = &genObj->stackStorage(); 147 MOZ_ASSERT(stack->getDenseCapacity() >= nvalues); 148 if (!frame.saveGeneratorSlots(cx, nvalues, stack)) { 149 return false; 150 } 151 } 152 153 genObj->setResumeIndex(pc); 154 genObj->setEnvironmentChain(*frame.environmentChain()); 155 return true; 156 } 157 158 #ifdef DEBUG 159 void AbstractGeneratorObject::dump() const { 160 fprintf(stderr, "(AbstractGeneratorObject*) %p {\n", (void*)this); 161 fprintf(stderr, " callee: (JSFunction*) %p,\n", (void*)&callee()); 162 fprintf(stderr, " environmentChain: (JSObject*) %p,\n", 163 (void*)&environmentChain()); 164 if (hasArgsObj()) { 165 fprintf(stderr, " argsObj: Some((ArgumentsObject*) %p),\n", 166 (void*)&argsObj()); 167 } else { 168 fprintf(stderr, " argsObj: None,\n"); 169 } 170 if (hasStackStorage()) { 171 fprintf(stderr, " stackStorage: Some(ArrayObject {\n"); 172 ArrayObject& stack = stackStorage(); 173 uint32_t denseLen = uint32_t(stack.getDenseInitializedLength()); 174 fprintf(stderr, " denseInitializedLength: %u\n,", denseLen); 175 uint32_t len = stack.length(); 176 fprintf(stderr, " length: %u\n,", len); 177 fprintf(stderr, " data: [\n"); 178 const Value* elements = getDenseElements(); 179 for (uint32_t i = 0; i < std::max(len, denseLen); i++) { 180 fprintf(stderr, " [%u]: ", i); 181 js::DumpValue(elements[i]); 182 } 183 fprintf(stderr, " ],\n"); 184 fprintf(stderr, " }),\n"); 185 } else { 186 fprintf(stderr, " stackStorage: None\n"); 187 } 188 if (isSuspended()) { 189 fprintf(stderr, " resumeIndex: Some(%u),\n", resumeIndex()); 190 } else { 191 fprintf(stderr, " resumeIndex: None, /* (not suspended) */\n"); 192 } 193 fprintf(stderr, "}\n"); 194 } 195 #endif 196 197 void AbstractGeneratorObject::finalSuspend(JSContext* cx, HandleObject obj) { 198 auto* genObj = &obj->as<AbstractGeneratorObject>(); 199 MOZ_ASSERT(genObj->isRunning()); 200 genObj->setClosed(cx); 201 } 202 203 static AbstractGeneratorObject* GetGeneratorObjectForCall(JSContext* cx, 204 CallObject& callObj) { 205 // The ".generator" binding is always present and always "aliased". 206 mozilla::Maybe<PropertyInfo> prop = 207 callObj.lookup(cx, cx->names().dot_generator_); 208 if (prop.isNothing()) { 209 return nullptr; 210 } 211 Value genValue = callObj.getSlot(prop->slot()); 212 213 // If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode 214 // sequence has not run yet, genValue is undefined. 215 return genValue.isObject() 216 ? &genValue.toObject().as<AbstractGeneratorObject>() 217 : nullptr; 218 } 219 220 AbstractGeneratorObject* js::GetGeneratorObjectForFrame( 221 JSContext* cx, AbstractFramePtr frame) { 222 cx->check(frame); 223 MOZ_ASSERT(frame.isGeneratorFrame()); 224 225 if (frame.isModuleFrame()) { 226 ModuleEnvironmentObject* moduleEnv = 227 frame.script()->module()->environment(); 228 mozilla::Maybe<PropertyInfo> prop = 229 moduleEnv->lookup(cx, cx->names().dot_generator_); 230 Value genValue = moduleEnv->getSlot(prop->slot()); 231 return genValue.isObject() 232 ? &genValue.toObject().as<AbstractGeneratorObject>() 233 : nullptr; 234 } 235 if (!frame.hasInitialEnvironment()) { 236 return nullptr; 237 } 238 239 return GetGeneratorObjectForCall(cx, frame.callObj()); 240 } 241 242 AbstractGeneratorObject* js::GetGeneratorObjectForEnvironment( 243 JSContext* cx, HandleObject env) { 244 auto* call = CallObject::find(env); 245 return call ? GetGeneratorObjectForCall(cx, *call) : nullptr; 246 } 247 248 bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame, 249 Handle<AbstractGeneratorObject*> genObj, 250 HandleValue arg, 251 GeneratorResumeKind resumeKind) { 252 MOZ_ASSERT(genObj->isRunning()); 253 if (resumeKind == GeneratorResumeKind::Throw) { 254 cx->setPendingException(arg, ShouldCaptureStack::Maybe); 255 } else { 256 MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return); 257 258 MOZ_ASSERT_IF(genObj->is<GeneratorObject>(), arg.isObject()); 259 frame.setReturnValue(arg); 260 261 RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING)); 262 cx->setPendingException(closing, nullptr); 263 } 264 return false; 265 } 266 267 bool AbstractGeneratorObject::resume(JSContext* cx, 268 InterpreterActivation& activation, 269 Handle<AbstractGeneratorObject*> genObj, 270 HandleValue arg, HandleValue resumeKind) { 271 MOZ_ASSERT(genObj->isSuspended()); 272 273 RootedFunction callee(cx, &genObj->callee()); 274 RootedObject envChain(cx, &genObj->environmentChain()); 275 if (!activation.resumeGeneratorFrame(callee, envChain)) { 276 return false; 277 } 278 activation.regs().fp()->setResumedGenerator(); 279 280 if (genObj->hasArgsObj()) { 281 activation.regs().fp()->initArgsObj(genObj->argsObj()); 282 } 283 284 if (genObj->hasStackStorage() && !genObj->isStackStorageEmpty()) { 285 JSScript* script = activation.regs().fp()->script(); 286 ArrayObject* storage = &genObj->stackStorage(); 287 uint32_t len = storage->getDenseInitializedLength(); 288 activation.regs().fp()->restoreGeneratorSlots(storage); 289 activation.regs().sp += len - script->nfixed(); 290 storage->setDenseInitializedLength(0); 291 } 292 293 JSScript* script = callee->nonLazyScript(); 294 uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()]; 295 activation.regs().pc = script->offsetToPC(offset); 296 297 // Push arg, generator, resumeKind Values on the generator's stack. 298 activation.regs().sp += 3; 299 MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth())); 300 activation.regs().sp[-3] = arg; 301 activation.regs().sp[-2] = ObjectValue(*genObj); 302 activation.regs().sp[-1] = resumeKind; 303 304 genObj->setRunning(); 305 return true; 306 } 307 308 GeneratorObject* GeneratorObject::create(JSContext* cx, HandleFunction fun) { 309 MOZ_ASSERT(fun->isGenerator() && !fun->isAsync()); 310 311 // FIXME: This would be faster if we could avoid doing a lookup to get 312 // the prototype for the instance. Bug 906600. 313 RootedValue pval(cx); 314 if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) { 315 return nullptr; 316 } 317 RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr); 318 if (!proto) { 319 proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global()); 320 if (!proto) { 321 return nullptr; 322 } 323 } 324 return NewObjectWithGivenProto<GeneratorObject>(cx, proto); 325 } 326 327 const JSClass GeneratorObject::class_ = { 328 "Generator", 329 JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS), 330 &classOps_, 331 }; 332 333 const JSClassOps GeneratorObject::classOps_ = { 334 nullptr, // addProperty 335 nullptr, // delProperty 336 nullptr, // enumerate 337 nullptr, // newEnumerate 338 nullptr, // resolve 339 nullptr, // mayResolve 340 nullptr, // finalize 341 nullptr, // call 342 nullptr, // construct 343 CallTraceMethod<AbstractGeneratorObject>, // trace 344 }; 345 346 static const JSFunctionSpec generator_methods[] = { 347 JS_SELF_HOSTED_FN("next", "GeneratorNext", 1, 0), 348 JS_SELF_HOSTED_FN("throw", "GeneratorThrow", 1, 0), 349 JS_SELF_HOSTED_FN("return", "GeneratorReturn", 1, 0), 350 JS_FS_END, 351 }; 352 353 JSObject* js::NewTenuredObjectWithFunctionPrototype( 354 JSContext* cx, Handle<GlobalObject*> global) { 355 RootedObject proto(cx, &cx->global()->getFunctionPrototype()); 356 return NewPlainObjectWithProto(cx, proto, TenuredObject); 357 } 358 359 static JSObject* CreateGeneratorFunction(JSContext* cx, JSProtoKey key) { 360 RootedObject proto(cx, &cx->global()->getFunctionConstructor()); 361 Handle<PropertyName*> name = cx->names().GeneratorFunction; 362 return NewFunctionWithProto(cx, Generator, 1, FunctionFlags::NATIVE_CTOR, 363 nullptr, name, proto, gc::AllocKind::FUNCTION, 364 TenuredObject); 365 } 366 367 static JSObject* CreateGeneratorFunctionPrototype(JSContext* cx, 368 JSProtoKey key) { 369 return NewTenuredObjectWithFunctionPrototype(cx, cx->global()); 370 } 371 372 static bool GeneratorFunctionClassFinish(JSContext* cx, 373 HandleObject genFunction, 374 HandleObject genFunctionProto) { 375 Handle<GlobalObject*> global = cx->global(); 376 377 // Change the "constructor" property to non-writable before adding any other 378 // properties, so it's still the last property and can be modified without a 379 // dictionary-mode transition. 380 MOZ_ASSERT(genFunctionProto->as<NativeObject>().getLastProperty().key() == 381 NameToId(cx->names().constructor)); 382 MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode()); 383 384 RootedValue genFunctionVal(cx, ObjectValue(*genFunction)); 385 if (!DefineDataProperty(cx, genFunctionProto, cx->names().constructor, 386 genFunctionVal, JSPROP_READONLY)) { 387 return false; 388 } 389 MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode()); 390 391 RootedObject iteratorProto( 392 cx, GlobalObject::getOrCreateIteratorPrototype(cx, global)); 393 if (!iteratorProto) { 394 return false; 395 } 396 397 RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting( 398 cx, &PlainObject::class_, iteratorProto)); 399 if (!genObjectProto) { 400 return false; 401 } 402 if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, 403 generator_methods) || 404 !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) { 405 return false; 406 } 407 408 if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, 409 JSPROP_READONLY, JSPROP_READONLY) || 410 !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) { 411 return false; 412 } 413 414 global->setGeneratorObjectPrototype(genObjectProto); 415 416 return true; 417 } 418 419 static const ClassSpec GeneratorFunctionClassSpec = { 420 CreateGeneratorFunction, 421 CreateGeneratorFunctionPrototype, 422 nullptr, 423 nullptr, 424 nullptr, 425 nullptr, 426 GeneratorFunctionClassFinish, 427 ClassSpec::DontDefineConstructor, 428 }; 429 430 const JSClass js::GeneratorFunctionClass = { 431 "GeneratorFunction", 432 0, 433 JS_NULL_CLASS_OPS, 434 &GeneratorFunctionClassSpec, 435 }; 436 437 const Value& AbstractGeneratorObject::getUnaliasedLocal(uint32_t slot) const { 438 MOZ_ASSERT(isSuspended()); 439 MOZ_ASSERT(hasStackStorage()); 440 MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed()); 441 return stackStorage().getDenseElement(slot); 442 } 443 444 void AbstractGeneratorObject::setUnaliasedLocal(uint32_t slot, 445 const Value& value) { 446 MOZ_ASSERT(isSuspended()); 447 MOZ_ASSERT(hasStackStorage()); 448 MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed()); 449 return stackStorage().setDenseElement(slot, value); 450 } 451 452 void AbstractGeneratorObject::setClosed(JSContext* cx) { 453 setFixedSlot(CALLEE_SLOT, NullValue()); 454 setFixedSlot(ENV_CHAIN_SLOT, NullValue()); 455 setFixedSlot(ARGS_OBJ_SLOT, NullValue()); 456 setFixedSlot(STACK_STORAGE_SLOT, NullValue()); 457 setFixedSlot(RESUME_INDEX_SLOT, NullValue()); 458 459 DebugAPI::onGeneratorClosed(cx, this); 460 } 461 462 bool AbstractGeneratorObject::isAfterYield() { 463 return isAfterYieldOrAwait(JSOp::Yield); 464 } 465 466 bool AbstractGeneratorObject::isAfterAwait() { 467 return isAfterYieldOrAwait(JSOp::Await); 468 } 469 470 bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op) { 471 if (isClosed() || isRunning()) { 472 return false; 473 } 474 475 JSScript* script = callee().nonLazyScript(); 476 jsbytecode* code = script->code(); 477 uint32_t nextOffset = script->resumeOffsets()[resumeIndex()]; 478 if (JSOp(code[nextOffset]) != JSOp::AfterYield) { 479 return false; 480 } 481 482 static_assert(JSOpLength_Yield == JSOpLength_InitialYield, 483 "JSOp::Yield and JSOp::InitialYield must have the same length"); 484 static_assert(JSOpLength_Yield == JSOpLength_Await, 485 "JSOp::Yield and JSOp::Await must have the same length"); 486 487 uint32_t offset = nextOffset - JSOpLength_Yield; 488 JSOp prevOp = JSOp(code[offset]); 489 MOZ_ASSERT(prevOp == JSOp::InitialYield || prevOp == JSOp::Yield || 490 prevOp == JSOp::Await); 491 492 return prevOp == op; 493 } 494 495 template <> 496 bool JSObject::is<js::AbstractGeneratorObject>() const { 497 return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() || 498 is<AsyncGeneratorObject>(); 499 } 500 501 GeneratorResumeKind js::ParserAtomToResumeKind( 502 frontend::TaggedParserAtomIndex atom) { 503 if (atom == frontend::TaggedParserAtomIndex::WellKnown::next()) { 504 return GeneratorResumeKind::Next; 505 } 506 if (atom == frontend::TaggedParserAtomIndex::WellKnown::throw_()) { 507 return GeneratorResumeKind::Throw; 508 } 509 MOZ_ASSERT(atom == frontend::TaggedParserAtomIndex::WellKnown::return_()); 510 return GeneratorResumeKind::Return; 511 } 512 513 JSAtom* js::ResumeKindToAtom(JSContext* cx, GeneratorResumeKind kind) { 514 switch (kind) { 515 case GeneratorResumeKind::Next: 516 return cx->names().next; 517 518 case GeneratorResumeKind::Throw: 519 return cx->names().throw_; 520 521 case GeneratorResumeKind::Return: 522 return cx->names().return_; 523 } 524 MOZ_CRASH("Invalid resume kind"); 525 }