tor-browser

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

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 }