Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/vm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 17 kB image not shown  

Quelle  GeneratorObject.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "vm/GeneratorObject.h"

#include "frontend/ParserAtom.h"
#ifdef DEBUG
#  include "js/friend/DumpFunctions.h"  // js::DumpObject, js::DumpValue
#endif
#include "js/PropertySpec.h"
#include "vm/AsyncFunction.h"
#include "vm/AsyncIteration.h"
#include "vm/FunctionFlags.h"  // js::FunctionFlags
#include "vm/GlobalObject.h"
#include "vm/JSObject.h"
#include "vm/PlainObject.h"  // js::PlainObject

#include "debugger/DebugAPI-inl.h"
#include "vm/Stack-inl.h"

using namespace js;

AbstractGeneratorObject* AbstractGeneratorObject::create(
    JSContext* cx, HandleFunction callee, HandleScript script,
    HandleObject environmentChain, Handle<ArgumentsObject*> argsObject) {
  Rooted<AbstractGeneratorObject*> genObj(cx);
  if (!callee->isAsync()) {
    genObj = GeneratorObject::create(cx, callee);
  } else if (callee->isGenerator()) {
    genObj = AsyncGeneratorObject::create(cx, callee);
  } else {
    genObj = AsyncFunctionGeneratorObject::create(cx, callee);
  }
  if (!genObj) {
    return nullptr;
  }

  genObj->setCallee(*callee);
  genObj->setEnvironmentChain(*environmentChain);
  if (argsObject) {
    genObj->setArgsObj(*argsObject.get());
  }

  ArrayObject* stack = NewDenseFullyAllocatedArray(cx, script->nslots());
  if (!stack) {
    return nullptr;
  }

  genObj->setStackStorage(*stack);

  // Note: This assumes that a Warp frame cannot be the target of
  //       the debugger, as we do not call OnNewGenerator.
  return genObj;
}

JSObject* AbstractGeneratorObject::createFromFrame(JSContext* cx,
                                                   AbstractFramePtr frame) {
  MOZ_ASSERT(frame.isGeneratorFrame());
  MOZ_ASSERT(!frame.isConstructing());

  if (frame.isModuleFrame()) {
    return createModuleGenerator(cx, frame);
  }

  RootedFunction fun(cx, frame.callee());
  Rooted<ArgumentsObject*> maybeArgs(
      cx, frame.script()->needsArgsObj() ? &frame.argsObj() : nullptr);
  RootedObject environmentChain(cx, frame.environmentChain());

  RootedScript script(cx, frame.script());
  Rooted<AbstractGeneratorObject*> genObj(
      cx, AbstractGeneratorObject::create(cx, fun, script, environmentChain,
                                          maybeArgs));
  if (!genObj) {
    return nullptr;
  }

  if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
    return nullptr;
  }

  return genObj;
}

JSObject* AbstractGeneratorObject::createModuleGenerator(
    JSContext* cx, AbstractFramePtr frame) {
  Rooted<ModuleObject*> module(cx, frame.script()->module());
  Rooted<AbstractGeneratorObject*> genObj(cx);
  genObj = AsyncFunctionGeneratorObject::create(cx, module);
  if (!genObj) {
    return nullptr;
  }

  // Create a handler function to wrap the module's script. This way
  // we can access it later and restore the state.
  Handle<PropertyName*> funName = cx->names().empty_;
  RootedFunction handlerFun(
      cx, NewFunctionWithProto(cx, nullptr, 0,
                               FunctionFlags::INTERPRETED_GENERATOR_OR_ASYNC,
                               nullptr, funName, nullptr,
                               gc::AllocKind::FUNCTION, GenericObject));
  if (!handlerFun) {
    return nullptr;
  }
  handlerFun->initScript(module->script());

  genObj->setCallee(*handlerFun);
  genObj->setEnvironmentChain(*frame.environmentChain());

  ArrayObject* stack =
      NewDenseFullyAllocatedArray(cx, module->script()->nslots());
  if (!stack) {
    return nullptr;
  }

  genObj->setStackStorage(*stack);

  if (!DebugAPI::onNewGenerator(cx, frame, genObj)) {
    return nullptr;
  }

  return genObj;
}

void AbstractGeneratorObject::trace(JSTracer* trc) {
  DebugAPI::traceGeneratorFrame(trc, this);
}

bool AbstractGeneratorObject::suspend(JSContext* cx, HandleObject obj,
                                      AbstractFramePtr frame,
                                      const jsbytecode* pc, unsigned nvalues) {
  MOZ_ASSERT(JSOp(*pc) == JSOp::InitialYield || JSOp(*pc) == JSOp::Yield ||
             JSOp(*pc) == JSOp::Await);

  auto genObj = obj.as<AbstractGeneratorObject>();
  MOZ_ASSERT(!genObj->hasStackStorage() || genObj->isStackStorageEmpty());
  MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Await, genObj->callee().isAsync());
  MOZ_ASSERT_IF(JSOp(*pc) == JSOp::Yield, genObj->callee().isGenerator());

  if (nvalues > 0) {
    ArrayObject* stack = nullptr;
    MOZ_ASSERT(genObj->hasStackStorage());
    stack = &genObj->stackStorage();
    MOZ_ASSERT(stack->getDenseCapacity() >= nvalues);
    if (!frame.saveGeneratorSlots(cx, nvalues, stack)) {
      return false;
    }
  }

  genObj->setResumeIndex(pc);
  genObj->setEnvironmentChain(*frame.environmentChain());
  return true;
}

#ifdef DEBUG
void AbstractGeneratorObject::dump() const {
  fprintf(stderr, "(AbstractGeneratorObject*) %p {\n", (void*)this);
  fprintf(stderr, " callee: (JSFunction*) %p,\n", (void*)&callee());
  fprintf(stderr, " environmentChain: (JSObject*) %p,\n",
          (void*)&environmentChain());
  if (hasArgsObj()) {
    fprintf(stderr, " argsObj: Some((ArgumentsObject*) %p),\n",
            (void*)&argsObj());
  } else {
    fprintf(stderr, " argsObj: None,\n");
  }
  if (hasStackStorage()) {
    fprintf(stderr, " stackStorage: Some(ArrayObject {\n");
    ArrayObject& stack = stackStorage();
    uint32_t denseLen = uint32_t(stack.getDenseInitializedLength());
    fprintf(stderr, " denseInitializedLength: %u\n,", denseLen);
    uint32_t len = stack.length();
    fprintf(stderr, " length: %u\n,", len);
    fprintf(stderr, " data: [\n");
    const Value* elements = getDenseElements();
    for (uint32_t i = 0; i < std::max(len, denseLen); i++) {
      fprintf(stderr, " [%u]: ", i);
      js::DumpValue(elements[i]);
    }
    fprintf(stderr, " ],\n");
    fprintf(stderr, " }),\n");
  } else {
    fprintf(stderr, " stackStorage: None\n");
  }
  if (isSuspended()) {
    fprintf(stderr, " resumeIndex: Some(%u),\n", resumeIndex());
  } else {
    fprintf(stderr, " resumeIndex: None, /* (not suspended) */\n");
  }
  fprintf(stderr, "}\n");
}
#endif

void AbstractGeneratorObject::finalSuspend(JSContext* cx, HandleObject obj) {
  auto* genObj = &obj->as<AbstractGeneratorObject>();
  MOZ_ASSERT(genObj->isRunning());
  genObj->setClosed(cx);
}

static AbstractGeneratorObject* GetGeneratorObjectForCall(JSContext* cx,
                                                          CallObject& callObj) {
  // The ".generator" binding is always present and always "aliased".
  mozilla::Maybe<PropertyInfo> prop =
      callObj.lookup(cx, cx->names().dot_generator_);
  if (prop.isNothing()) {
    return nullptr;
  }
  Value genValue = callObj.getSlot(prop->slot());

  // If the `Generator; SetAliasedVar ".generator"; InitialYield` bytecode
  // sequence has not run yet, genValue is undefined.
  return genValue.isObject()
             ? &genValue.toObject().as<AbstractGeneratorObject>()
             : nullptr;
}

AbstractGeneratorObject* js::GetGeneratorObjectForFrame(
    JSContext* cx, AbstractFramePtr frame) {
  cx->check(frame);
  MOZ_ASSERT(frame.isGeneratorFrame());

  if (frame.isModuleFrame()) {
    ModuleEnvironmentObject* moduleEnv =
        frame.script()->module()->environment();
    mozilla::Maybe<PropertyInfo> prop =
        moduleEnv->lookup(cx, cx->names().dot_generator_);
    Value genValue = moduleEnv->getSlot(prop->slot());
    return genValue.isObject()
               ? &genValue.toObject().as<AbstractGeneratorObject>()
               : nullptr;
  }
  if (!frame.hasInitialEnvironment()) {
    return nullptr;
  }

  return GetGeneratorObjectForCall(cx, frame.callObj());
}

AbstractGeneratorObject* js::GetGeneratorObjectForEnvironment(
    JSContext* cx, HandleObject env) {
  auto* call = CallObject::find(env);
  return call ? GetGeneratorObjectForCall(cx, *call) : nullptr;
}

bool js::GeneratorThrowOrReturn(JSContext* cx, AbstractFramePtr frame,
                                Handle<AbstractGeneratorObject*> genObj,
                                HandleValue arg,
                                GeneratorResumeKind resumeKind) {
  MOZ_ASSERT(genObj->isRunning());
  if (resumeKind == GeneratorResumeKind::Throw) {
    cx->setPendingException(arg, ShouldCaptureStack::Maybe);
  } else {
    MOZ_ASSERT(resumeKind == GeneratorResumeKind::Return);

    MOZ_ASSERT_IF(genObj->is<GeneratorObject>(), arg.isObject());
    frame.setReturnValue(arg);

    RootedValue closing(cx, MagicValue(JS_GENERATOR_CLOSING));
    cx->setPendingException(closing, nullptr);
  }
  return false;
}

bool AbstractGeneratorObject::resume(JSContext* cx,
                                     InterpreterActivation& activation,
                                     Handle<AbstractGeneratorObject*> genObj,
                                     HandleValue arg, HandleValue resumeKind) {
  MOZ_ASSERT(genObj->isSuspended());

  RootedFunction callee(cx, &genObj->callee());
  RootedObject envChain(cx, &genObj->environmentChain());
  if (!activation.resumeGeneratorFrame(callee, envChain)) {
    return false;
  }
  activation.regs().fp()->setResumedGenerator();

  if (genObj->hasArgsObj()) {
    activation.regs().fp()->initArgsObj(genObj->argsObj());
  }

  if (genObj->hasStackStorage() && !genObj->isStackStorageEmpty()) {
    JSScript* script = activation.regs().fp()->script();
    ArrayObject* storage = &genObj->stackStorage();
    uint32_t len = storage->getDenseInitializedLength();
    activation.regs().fp()->restoreGeneratorSlots(storage);
    activation.regs().sp += len - script->nfixed();
    storage->setDenseInitializedLength(0);
  }

  JSScript* script = callee->nonLazyScript();
  uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()];
  activation.regs().pc = script->offsetToPC(offset);

  // Push arg, generator, resumeKind Values on the generator's stack.
  activation.regs().sp += 3;
  MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
  activation.regs().sp[-3] = arg;
  activation.regs().sp[-2] = ObjectValue(*genObj);
  activation.regs().sp[-1] = resumeKind;

  genObj->setRunning();
  return true;
}

GeneratorObject* GeneratorObject::create(JSContext* cx, HandleFunction fun) {
  MOZ_ASSERT(fun->isGenerator() && !fun->isAsync());

  // FIXME: This would be faster if we could avoid doing a lookup to get
  // the prototype for the instance.  Bug 906600.
  RootedValue pval(cx);
  if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval)) {
    return nullptr;
  }
  RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
  if (!proto) {
    proto = GlobalObject::getOrCreateGeneratorObjectPrototype(cx, cx->global());
    if (!proto) {
      return nullptr;
    }
  }
  return NewObjectWithGivenProto<GeneratorObject>(cx, proto);
}

const JSClass GeneratorObject::class_ = {
    "Generator",
    JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS),
    &classOps_,
};

const JSClassOps GeneratorObject::classOps_ = {
    nullptr,                                   // addProperty
    nullptr,                                   // delProperty
    nullptr,                                   // enumerate
    nullptr,                                   // newEnumerate
    nullptr,                                   // resolve
    nullptr,                                   // mayResolve
    nullptr,                                   // finalize
    nullptr,                                   // call
    nullptr,                                   // construct
    CallTraceMethod<AbstractGeneratorObject>,  // trace
};

static const JSFunctionSpec generator_methods[] = {
    JS_SELF_HOSTED_FN("next""GeneratorNext", 1, 0),
    JS_SELF_HOSTED_FN("throw""GeneratorThrow", 1, 0),
    JS_SELF_HOSTED_FN("return""GeneratorReturn", 1, 0),
    JS_FS_END,
};

JSObject* js::NewTenuredObjectWithFunctionPrototype(
    JSContext* cx, Handle<GlobalObject*> global) {
  RootedObject proto(cx, &cx->global()->getFunctionPrototype());
  return NewPlainObjectWithProto(cx, proto, TenuredObject);
}

static JSObject* CreateGeneratorFunction(JSContext* cx, JSProtoKey key) {
  RootedObject proto(cx, &cx->global()->getFunctionConstructor());
  Handle<PropertyName*> name = cx->names().GeneratorFunction;
  return NewFunctionWithProto(cx, Generator, 1, FunctionFlags::NATIVE_CTOR,
                              nullptr, name, proto, gc::AllocKind::FUNCTION,
                              TenuredObject);
}

static JSObject* CreateGeneratorFunctionPrototype(JSContext* cx,
                                                  JSProtoKey key) {
  return NewTenuredObjectWithFunctionPrototype(cx, cx->global());
}

static bool GeneratorFunctionClassFinish(JSContext* cx,
                                         HandleObject genFunction,
                                         HandleObject genFunctionProto) {
  Handle<GlobalObject*> global = cx->global();

  // Change the "constructor" property to non-writable before adding any other
  // properties, so it's still the last property and can be modified without a
  // dictionary-mode transition.
  MOZ_ASSERT(genFunctionProto->as<NativeObject>().getLastProperty().key() ==
             NameToId(cx->names().constructor));
  MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());

  RootedValue genFunctionVal(cx, ObjectValue(*genFunction));
  if (!DefineDataProperty(cx, genFunctionProto, cx->names().constructor,
                          genFunctionVal, JSPROP_READONLY)) {
    return false;
  }
  MOZ_ASSERT(!genFunctionProto->as<NativeObject>().inDictionaryMode());

  RootedObject iteratorProto(
      cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
  if (!iteratorProto) {
    return false;
  }

  RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(
                                      cx, &PlainObject::class_, iteratorProto));
  if (!genObjectProto) {
    return false;
  }
  if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr,
                                    generator_methods) ||
      !DefineToStringTag(cx, genObjectProto, cx->names().Generator)) {
    return false;
  }

  if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto,
                                   JSPROP_READONLY, JSPROP_READONLY) ||
      !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction)) {
    return false;
  }

  global->setGeneratorObjectPrototype(genObjectProto);

  return true;
}

static const ClassSpec GeneratorFunctionClassSpec = {
    CreateGeneratorFunction,
    CreateGeneratorFunctionPrototype,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    GeneratorFunctionClassFinish,
    ClassSpec::DontDefineConstructor,
};

const JSClass js::GeneratorFunctionClass = {
    "GeneratorFunction",
    0,
    JS_NULL_CLASS_OPS,
    &GeneratorFunctionClassSpec,
};

const Value& AbstractGeneratorObject::getUnaliasedLocal(uint32_t slot) const {
  MOZ_ASSERT(isSuspended());
  MOZ_ASSERT(hasStackStorage());
  MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed());
  return stackStorage().getDenseElement(slot);
}

void AbstractGeneratorObject::setUnaliasedLocal(uint32_t slot,
                                                const Value& value) {
  MOZ_ASSERT(isSuspended());
  MOZ_ASSERT(hasStackStorage());
  MOZ_ASSERT(slot < callee().nonLazyScript()->nfixed());
  return stackStorage().setDenseElement(slot, value);
}

void AbstractGeneratorObject::setClosed(JSContext* cx) {
  setFixedSlot(CALLEE_SLOT, NullValue());
  setFixedSlot(ENV_CHAIN_SLOT, NullValue());
  setFixedSlot(ARGS_OBJ_SLOT, NullValue());
  setFixedSlot(STACK_STORAGE_SLOT, NullValue());
  setFixedSlot(RESUME_INDEX_SLOT, NullValue());

  DebugAPI::onGeneratorClosed(cx, this);
}

bool AbstractGeneratorObject::isAfterYield() {
  return isAfterYieldOrAwait(JSOp::Yield);
}

bool AbstractGeneratorObject::isAfterAwait() {
  return isAfterYieldOrAwait(JSOp::Await);
}

bool AbstractGeneratorObject::isAfterYieldOrAwait(JSOp op) {
  if (isClosed() || isRunning()) {
    return false;
  }

  JSScript* script = callee().nonLazyScript();
  jsbytecode* code = script->code();
  uint32_t nextOffset = script->resumeOffsets()[resumeIndex()];
  if (JSOp(code[nextOffset]) != JSOp::AfterYield) {
    return false;
  }

  static_assert(JSOpLength_Yield == JSOpLength_InitialYield,
                "JSOp::Yield and JSOp::InitialYield must have the same length");
  static_assert(JSOpLength_Yield == JSOpLength_Await,
                "JSOp::Yield and JSOp::Await must have the same length");

  uint32_t offset = nextOffset - JSOpLength_Yield;
  JSOp prevOp = JSOp(code[offset]);
  MOZ_ASSERT(prevOp == JSOp::InitialYield || prevOp == JSOp::Yield ||
             prevOp == JSOp::Await);

  return prevOp == op;
}

template <>
bool JSObject::is<js::AbstractGeneratorObject>() const {
  return is<GeneratorObject>() || is<AsyncFunctionGeneratorObject>() ||
         is<AsyncGeneratorObject>();
}

GeneratorResumeKind js::ParserAtomToResumeKind(
    frontend::TaggedParserAtomIndex atom) {
  if (atom == frontend::TaggedParserAtomIndex::WellKnown::next()) {
    return GeneratorResumeKind::Next;
  }
  if (atom == frontend::TaggedParserAtomIndex::WellKnown::throw_()) {
    return GeneratorResumeKind::Throw;
  }
  MOZ_ASSERT(atom == frontend::TaggedParserAtomIndex::WellKnown::return_());
  return GeneratorResumeKind::Return;
}

JSAtom* js::ResumeKindToAtom(JSContext* cx, GeneratorResumeKind kind) {
  switch (kind) {
    case GeneratorResumeKind::Next:
      return cx->names().next;

    case GeneratorResumeKind::Throw:
      return cx->names().throw_;

    case GeneratorResumeKind::Return:
      return cx->names().return_;
  }
  MOZ_CRASH("Invalid resume kind");
}

Messung V0.5
C=88 H=98 G=93

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.