Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/MySQL/doc/snippets/   (MySQL Server Version 8.1-8.4©)  Datei vom 12.11.2025 mit Größe 792 B image not shown  

Quelle  AsyncIteration.cpp

  Sprache: C
 

/* 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/AsyncIteration.h"

#include "builtin/Promise.h"  // js::PromiseHandler, js::CreatePromiseObjectForAsyncGenerator, js::AsyncFromSyncIteratorMethod, js::ResolvePromiseInternal, js::RejectPromiseInternal, js::InternalAsyncGeneratorAwait
#include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
#include "js/PropertySpec.h"
#include "vm/AsyncFunction.h"  // js::AutoAsyncResumeDepth
#include "vm/CompletionKind.h"
#include "vm/FunctionFlags.h"  // js::FunctionFlags
#include "vm/GeneratorObject.h"
#include "vm/GlobalObject.h"
#include "vm/Interpreter.h"
#include "vm/PlainObject.h"    // js::PlainObject
#include "vm/PromiseObject.h"  // js::PromiseObject
#include "vm/Realm.h"
#include "vm/SelfHosting.h"

#include "vm/JSObject-inl.h"
#include "vm/List-inl.h"

using namespace js;

// ---------------
// Async generator
// ---------------

const JSClass AsyncGeneratorObject::class_ = {
    "AsyncGenerator",
    JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorObject::Slots),
    &classOps_,
};

const JSClassOps AsyncGeneratorObject::classOps_ = {
    .trace = CallTraceMethod<AbstractGeneratorObject>,
};

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto
//                                 [ , internalSlotsList ] )
// https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
//
// specialized for AsyncGeneratorObjects.
static AsyncGeneratorObject* OrdinaryCreateFromConstructorAsynGen(
    JSContext* cx, HandleFunction constructor) {
  // Step 1. Assert: intrinsicDefaultProto is this specification's name of an
  //         intrinsic object. The corresponding object must be an intrinsic
  //         that is intended to be used as the [[Prototype]] value of an
  //         object.
  // (implicit)

  // Step 2. Let proto be
  //         ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
  RootedValue protoVal(cx);
  if (!GetProperty(cx, constructor, constructor, cx->names().prototype,
                   &protoVal)) {
    return nullptr;
  }

  RootedObject proto(cx, protoVal.isObject() ? &protoVal.toObject() : nullptr);
  if (!proto) {
    proto = GlobalObject::getOrCreateAsyncGeneratorPrototype(cx, cx->global());
    if (!proto) {
      return nullptr;
    }
  }

  // Step 3. If internalSlotsList is present, let slotsList be
  //         internalSlotsList.
  // Step 4. Else, let slotsList be a new empty List.
  // Step 5. Return OrdinaryObjectCreate(proto, slotsList).
  return NewObjectWithGivenProto<AsyncGeneratorObject>(cx, proto);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// EvaluateAsyncGeneratorBody
// https://tc39.es/ecma262/#sec-runtime-semantics-evaluateasyncgeneratorbody
//
// Steps 4-5.
//
// AsyncGeneratorStart ( generator, generatorBody )
// https://tc39.es/ecma262/#sec-asyncgeneratorstart
//
// Steps 1, 7.
/* static */
AsyncGeneratorObject* AsyncGeneratorObject::create(JSContext* cx,
                                                   HandleFunction asyncGen) {
  MOZ_ASSERT(asyncGen->isAsync() && asyncGen->isGenerator());

  AsyncGeneratorObject* generator =
      OrdinaryCreateFromConstructorAsynGen(cx, asyncGen);
  if (!generator) {
    return nullptr;
  }

  // EvaluateAsyncGeneratorBody
  // Step 4. Set generator.[[AsyncGeneratorState]] to suspended-start.
  generator->setSuspendedStart();

  // Step 5. Perform AsyncGeneratorStart(generator, FunctionBody).

  // AsyncGeneratorStart
  // Step 1. Assert: generator.[[AsyncGeneratorState]] is suspended-start.

  // Step 7. Set generator.[[AsyncGeneratorQueue]] to a new empty List.
  generator->clearSingleQueueRequest();

  generator->clearCachedRequest();

  return generator;
}

/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::createRequest(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    CompletionKind completionKind, HandleValue completionValue,
    Handle<PromiseObject*> promise) {
  if (!generator->hasCachedRequest()) {
    return AsyncGeneratorRequest::create(cx, completionKind, completionValue,
                                         promise);
  }

  AsyncGeneratorRequest* request = generator->takeCachedRequest();
  request->init(completionKind, completionValue, promise);
  return request;
}

/* static */ [[nodiscard]] bool AsyncGeneratorObject::enqueueRequest(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    Handle<AsyncGeneratorRequest*> request) {
  if (generator->isSingleQueue()) {
    if (generator->isSingleQueueEmpty()) {
      generator->setSingleQueueRequest(request);
      return true;
    }

    ListObject* queue = ListObject::create(cx);
    if (!queue) {
      return false;
    }

    if (!queue->append(cx, ObjectValue(*generator->singleQueueRequest()),
                       ObjectValue(*request))) {
      return false;
    }

    generator->setQueue(queue);
    return true;
  }

  return generator->queue()->append(cx, ObjectValue(*request));
}

/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::dequeueRequest(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
  if (generator->isSingleQueue()) {
    AsyncGeneratorRequest* request = generator->singleQueueRequest();
    generator->clearSingleQueueRequest();
    return request;
  }

  Rooted<ListObject*> queue(cx, generator->queue());
  return &queue->popFirstAs<AsyncGeneratorRequest>(cx);
}

/* static */
AsyncGeneratorRequest* AsyncGeneratorObject::peekRequest(
    Handle<AsyncGeneratorObject*> generator) {
  if (generator->isSingleQueue()) {
    return generator->singleQueueRequest();
  }

  return &generator->queue()->getAs<AsyncGeneratorRequest>(0);
}

const JSClass AsyncGeneratorRequest::class_ = {
    "AsyncGeneratorRequest",
    JSCLASS_HAS_RESERVED_SLOTS(AsyncGeneratorRequest::Slots),
};

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorRequest Records
// https://tc39.es/ecma262/#sec-asyncgeneratorrequest-records
/* static */
AsyncGeneratorRequest* AsyncGeneratorRequest::create(
    JSContext* cx, CompletionKind completionKind, HandleValue completionValue,
    Handle<PromiseObject*> promise) {
  AsyncGeneratorRequest* request =
      NewObjectWithGivenProto<AsyncGeneratorRequest>(cx, nullptr);
  if (!request) {
    return nullptr;
  }

  request->init(completionKind, completionValue, promise);
  return request;
}

[[nodiscard]] static bool AsyncGeneratorResume(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    CompletionKind completionKind, HandleValue argument);

[[nodiscard]] static bool AsyncGeneratorDrainQueue(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator);

[[nodiscard]] static bool AsyncGeneratorCompleteStepNormal(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value,
    bool done);

[[nodiscard]] static bool AsyncGeneratorCompleteStepThrow(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    HandleValue exception);

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorStart ( generator, generatorBody )
// https://tc39.es/ecma262/#sec-asyncgeneratorstart
//
// Steps 4.g-l. "return" case.
[[nodiscard]] static bool AsyncGeneratorReturned(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  // Step 4.g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue.
  generator->setDrainingQueue();

  // Step 4.i. If result is a return completion, set result to
  //           NormalCompletion(result.[[Value]]).
  // (implicit)

  // Step 4.j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).
  if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) {
    return false;
  }

  MOZ_ASSERT(!generator->isExecuting());
  MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
  if (generator->isDrainingQueue_AwaitingReturn()) {
    return true;
  }

  // Step 4.k. Perform AsyncGeneratorDrainQueue(acGenerator).
  // Step 4.l. Return undefined.
  return AsyncGeneratorDrainQueue(cx, generator);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorStart ( generator, generatorBody )
// https://tc39.es/ecma262/#sec-asyncgeneratorstart
//
// Steps 4.g-l. "throw" case.
[[nodiscard]] static bool AsyncGeneratorThrown(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
  // Step 4.g. Set acGenerator.[[AsyncGeneratorState]] to draining-queue.
  generator->setDrainingQueue();

  // Not much we can do about uncatchable exceptions, so just bail.
  if (!cx->isExceptionPending()) {
    return false;
  }

  // Step 4.j. Perform AsyncGeneratorCompleteStep(acGenerator, result, true).
  RootedValue value(cx);
  if (!GetAndClearException(cx, &value)) {
    return false;
  }
  if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
    return false;
  }

  MOZ_ASSERT(!generator->isExecuting());
  MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
  if (generator->isDrainingQueue_AwaitingReturn()) {
    return true;
  }

  // Step 4.k. Perform AsyncGeneratorDrainQueue(acGenerator).
  // Step 4.l. Return undefined.
  return AsyncGeneratorDrainQueue(cx, generator);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
// https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
//
// Steps 4-5.
[[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedFulfilled(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  MOZ_ASSERT(generator->isExecuting_AwaitingYieldReturn(),
             "YieldReturn-Await fulfilled when not in "
             "'AwaitingYieldReturn' state");

  // Step 4. Assert: awaited is a normal completion.
  // Step 5. Return ReturnCompletion(awaited.[[Value]]).
  return AsyncGeneratorResume(cx, generator, CompletionKind::Return, value);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
// https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
//
// Step 3.
[[nodiscard]] static bool AsyncGeneratorYieldReturnAwaitedRejected(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    HandleValue reason) {
  MOZ_ASSERT(
      generator->isExecuting_AwaitingYieldReturn(),
      "YieldReturn-Await rejected when not in 'AwaitingYieldReturn' state");

  // Step 3. If awaited is a throw completion, return ? awaited.
  return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
// https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
//
// Step 2.
[[nodiscard]] static bool AsyncGeneratorUnwrapYieldResumptionWithReturn(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    JS::Handle<JS::Value> value) {
  // Step 2. Let awaited be Completion(Await(resumptionValue.[[Value]])).
  //
  // NOTE: Given that Await needs to be performed asynchronously,
  //       we use an implementation-defined state "AwaitingYieldReturn"
  //       to wait for the result.
  generator->setExecuting_AwaitingYieldReturn();

  return InternalAsyncGeneratorAwait(
      cx, generator, value,
      PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled,
      PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorYield ( value )
// https://tc39.es/ecma262/#sec-asyncgeneratoryield
//
// Stesp 9-12.
//
// AsyncGeneratorUnwrapYieldResumption ( resumptionValue )
// https://tc39.es/ecma262/#sec-asyncgeneratorunwrapyieldresumption
//
// Step 1.
[[nodiscard]] static bool AsyncGeneratorYield(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value,
    bool* resumeAgain, CompletionKind* resumeCompletionKind,
    JS::MutableHandle<JS::Value> resumeValue) {
  *resumeAgain = false;

  // Step 9. Perform
  //         ! AsyncGeneratorCompleteStep(generator, completion, false,
  //                                      previousRealm).
  if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, false)) {
    return false;
  }

  MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
  // NOTE: This transition doesn't basically happen, but could happen if
  //       Debugger API is used, or the job queue is forcibly drained.
  if (generator->isDrainingQueue_AwaitingReturn()) {
    return true;
  }

  // Step 10. Let queue be generator.[[AsyncGeneratorQueue]].
  // Step 11. If queue is not empty, then
  if (!generator->isQueueEmpty()) {
    // Step 11.a. NOTE: Execution continues without suspending the generator.
    // Step 11.b. Let toYield be the first element of queue.
    Rooted<AsyncGeneratorRequest*> toYield(
        cx, AsyncGeneratorObject::peekRequest(generator));
    if (!toYield) {
      return false;
    }

    CompletionKind completionKind = toYield->completionKind();

    // Step 11.c. Let resumptionValue be Completion(toYield.[[Completion]]).
    RootedValue completionValue(cx, toYield->completionValue());

    // Step 11.d. Return ?
    //            AsyncGeneratorUnwrapYieldResumption(resumptionValue).
    //
    // AsyncGeneratorUnwrapYieldResumption
    // Step 1. If resumptionValue is not a return completion, return ?
    //         resumptionValue.
    if (completionKind != CompletionKind::Return) {
      *resumeAgain = true;
      *resumeCompletionKind = completionKind;
      resumeValue.set(completionValue);
      return true;
    }

    // Step 2.
    return AsyncGeneratorUnwrapYieldResumptionWithReturn(cx, generator,
                                                         completionValue);
  }

  // Step 12. Else,
  // Step 12.a. Set generator.[[AsyncGeneratorState]] to suspended-yield.
  generator->setSuspendedYield();

  // Step 12.b. Remove genContext from the execution context stack and
  //            restore the execution context that is at the top of the
  //            execution context stack as the running execution context.
  // Step 12.c. Let callerContext be the running execution context.
  // Step 12.d. Resume callerContext passing undefined. If genContext is ever
  //            resumed again, let resumptionValue be the Completion Record with
  //            which it is resumed.
  // (done as part of bytecode)

  // Step 12.e. Assert: If control reaches here, then genContext is the
  //            running execution context again.
  // Step 12.f. Return ?
  //            AsyncGeneratorUnwrapYieldResumption(resumptionValue).
  // (done in AsyncGeneratorResume on the next resume)

  return true;
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// Await in async function
// https://tc39.es/ecma262/#await
//
// Steps 3.c-f.
[[nodiscard]] static bool AsyncGeneratorAwaitedFulfilled(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  MOZ_ASSERT(generator->isExecuting(),
             "Await fulfilled when not in 'Executing' state");

  // Step 3.c. Push asyncContext onto the execution context stack; asyncContext
  //           is now the running execution context.
  // Step 3.d. Resume the suspended evaluation of asyncContext using
  //           NormalCompletion(v) as the result of the operation that
  //           suspended it.
  // Step 3.f. Return undefined.
  return AsyncGeneratorResume(cx, generator, CompletionKind::Normal, value);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// Await in async function
// https://tc39.es/ecma262/#await
//
// Steps 5.c-f.
[[nodiscard]] static bool AsyncGeneratorAwaitedRejected(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    HandleValue reason) {
  MOZ_ASSERT(generator->isExecuting(),
             "Await rejected when not in 'Executing' state");

  // Step 5.c. Push asyncContext onto the execution context stack; asyncContext
  //           is now the running execution context.
  // Step 5.d. Resume the suspended evaluation of asyncContext using
  //           ThrowCompletion(reason) as the result of the operation that
  //           suspended it.
  // Step 5.f. Return undefined.
  return AsyncGeneratorResume(cx, generator, CompletionKind::Throw, reason);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// Await in async function
// https://tc39.es/ecma262/#await
[[nodiscard]] static bool AsyncGeneratorAwait(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  return InternalAsyncGeneratorAwait(
      cx, generator, value, PromiseHandler::AsyncGeneratorAwaitedFulfilled,
      PromiseHandler::AsyncGeneratorAwaitedRejected);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )
// https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep
//
// "normal" case.
//
// Per spec, this function should never fail.
// Our implementation can fail due to OOM.
[[nodiscard]] static bool AsyncGeneratorCompleteStepNormal(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value,
    bool done) {
  // Step 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.
  MOZ_ASSERT(!generator->isQueueEmpty());

  // Step 2. Let next be the first element of generator.[[AsyncGeneratorQueue]].
  // Step 3. Remove the first element from generator.[[AsyncGeneratorQueue]].
  AsyncGeneratorRequest* next =
      AsyncGeneratorObject::dequeueRequest(cx, generator);
  if (!next) {
    return false;
  }

  // Step 4. Let promiseCapability be next.[[Capability]].
  Rooted<PromiseObject*> resultPromise(cx, next->promise());

  generator->cacheRequest(next);

  // Step 5. Let value be completion.[[Value]].
  // (passed by caller)

  // Step 6. If completion is a throw completion, then
  // (done in AsyncGeneratorCompleteStepThrow)

  // Step 7. Else,
  // Step 7.a. Assert: completion is a normal completion.

  // Step 7.b. If realm is present, then
  // (skipped)

  // Step 7.c. Else,
  // Step 7.c.i. Let iteratorResult be CreateIteratorResultObject(value,
  //             done).
  JSObject* resultObj = CreateIterResultObject(cx, value, done);
  if (!resultObj) {
    return false;
  }

  // Step 7.d. Perform
  //           ! Call(promiseCapability.[[Resolve]], undefined,
  //                  « iteratorResult »).
  // Step 8. Return unused.
  RootedValue resultValue(cx, ObjectValue(*resultObj));
  return ResolvePromiseInternal(cx, resultPromise, resultValue);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorCompleteStep ( generator, completion, done [ , realm ] )
// https://tc39.es/ecma262/#sec-asyncgeneratorcompletestep
//
// "throw" case.
//
// Per spec, this function should never fail.
// Our implementation can fail due to OOM.
[[nodiscard]] static bool AsyncGeneratorCompleteStepThrow(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    HandleValue exception) {
  // Step 1. Assert: generator.[[AsyncGeneratorQueue]] is not empty.
  MOZ_ASSERT(!generator->isQueueEmpty());

  // Step 2. Let next be the first element of generator.[[AsyncGeneratorQueue]].
  // Step 3. Remove the first element from generator.[[AsyncGeneratorQueue]].
  AsyncGeneratorRequest* next =
      AsyncGeneratorObject::dequeueRequest(cx, generator);
  if (!next) {
    return false;
  }

  // Step 4. Let promiseCapability be next.[[Capability]].
  Rooted<PromiseObject*> resultPromise(cx, next->promise());

  generator->cacheRequest(next);

  // Step 5. Let value be completion.[[Value]].
  // (passed by caller)

  // Step 6. If completion is a throw completion, then
  // Step 6.a. Perform
  //           ! Call(promiseCapability.[[Reject]], undefined, « value »).
  // Step 8. Return unused.
  return RejectPromiseInternal(cx, resultPromise, exception);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorAwaitReturn ( generator )
// https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn
//
// Steps 11.a-e.
[[nodiscard]] static bool AsyncGeneratorAwaitReturnFulfilled(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  // Step 11.a. Assert: generator.[[AsyncGeneratorState]] is draining-queue.
  //
  // NOTE: We use the implementation-defined state DrainingQueue_AwaitingReturn
  //       for the Await during draining-queue, and it's set back to the
  //       original draining-queue when the await operation finishes.
  MOZ_ASSERT(generator->isDrainingQueue_AwaitingReturn());
  generator->setDrainingQueue();

  // Step 11.b. Let result be NormalCompletion(value).
  // Step 11.c. Perform AsyncGeneratorCompleteStep(generator, result, true).
  if (!AsyncGeneratorCompleteStepNormal(cx, generator, value, true)) {
    return false;
  }

  MOZ_ASSERT(!generator->isExecuting());
  MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
  if (generator->isDrainingQueue_AwaitingReturn()) {
    return true;
  }

  // Step 11.d. Perform AsyncGeneratorDrainQueue(generator).
  // Step 11.e. Return undefined.
  return AsyncGeneratorDrainQueue(cx, generator);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorAwaitReturn ( generator )
// https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn
//
// Steps 13.a-e.
[[nodiscard]] static bool AsyncGeneratorAwaitReturnRejected(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue value) {
  // Step 13.a. Assert: generator.[[AsyncGeneratorState]] is draining-queue.
  //
  // See the comment for AsyncGeneratorAwaitReturnFulfilled.
  MOZ_ASSERT(generator->isDrainingQueue_AwaitingReturn());
  generator->setDrainingQueue();

  // Step 13.b. Let result be ThrowCompletion(reason).
  // Step 13.c. Perform AsyncGeneratorCompleteStep(generator, result, true).
  if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
    return false;
  }

  MOZ_ASSERT(!generator->isExecuting());
  MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
  if (generator->isDrainingQueue_AwaitingReturn()) {
    return true;
  }

  // Step 13.d. Perform AsyncGeneratorDrainQueue(generator).
  // Step 13.e. Return undefined.
  return AsyncGeneratorDrainQueue(cx, generator);
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorAwaitReturn ( generator )
// https://tc39.es/ecma262/#sec-asyncgeneratorawaitreturn
[[nodiscard]] static bool AsyncGeneratorAwaitReturn(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator, HandleValue next) {
  // Step 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue.
  MOZ_ASSERT(generator->isDrainingQueue());
  generator->setDrainingQueue_AwaitingReturn();

  // Step 2. Let queue be generator.[[AsyncGeneratorQueue]].
  // Step 3. Assert: queue is not empty.
  MOZ_ASSERT(!generator->isQueueEmpty());

  // Step 4. Let next be the first element of queue.
  // (passed by caller)

  // Step 5. Let completion be Completion(next.[[Completion]]).
  // Step 6. Assert: completion is a return completion.
  // (implicit)

  // Step 7. Let promiseCompletion be Completion(PromiseResolve(%Promise%,
  //         completion.[[Value]])).

  // Step 9. Assert: promiseCompletion is a normal completion.
  // Step 10. Let promise be promiseCompletion.[[Value]].
  // Step 11. Let fulfilledClosure be a new Abstract Closure with parameters
  //          (value) that captures generator and performs the following steps
  //          when called:
  // Step 12. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1,
  //          "", « »).
  // Step 13. Let rejectedClosure be a new Abstract Closure with parameters
  //          (reason) that captures generator and performs the following steps
  //          when called:
  // Step 14. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "",
  //          « »).
  // Step 15. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
  // Step 16. Return unused.
  if (!InternalAsyncGeneratorAwait(
          cx, generator, next,
          PromiseHandler::AsyncGeneratorAwaitReturnFulfilled,
          PromiseHandler::AsyncGeneratorAwaitReturnRejected)) {
    // This branch can be taken with one of the following:
    //   * (a) abrupt completion in PromiseResolve at step 7, such as
    //         getting `compeltion.[[Value]].constructor` property throws
    //   * (b) OOM in PromiseResolve
    //   * (c) OOM in PerformPromiseThen
    //
    // (c) happens after step 8, but OOM is an implementation details and
    // we can treat the OOM as if it happened during PromiseResolve,
    // and thus performing the step 8 here is okay.
    //
    // Step 8. If promiseCompletion is an abrupt completion, then

    // Not much we can do about uncatchable exceptions, so just bail.
    if (!cx->isExceptionPending()) {
      return false;
    }

    RootedValue value(cx);
    if (!GetAndClearException(cx, &value)) {
      return false;
    }

    // Step 8.a. Perform AsyncGeneratorCompleteStep(generator,
    //           promiseCompletion, true).
    if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
      return false;
    }

    MOZ_ASSERT(!generator->isExecuting());
    MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
    if (generator->isDrainingQueue_AwaitingReturn()) {
      return true;
    }

    // Step 8.b. Perform AsyncGeneratorDrainQueue(generator).
    // Step 8.c. Return unused.
    return AsyncGeneratorDrainQueue(cx, generator);
  }

  return true;
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorDrainQueue ( generator )
// https://tc39.es/ecma262/#sec-asyncgeneratordrainqueue
[[nodiscard]] static bool AsyncGeneratorDrainQueue(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
  // Step 1. Assert: generator.[[AsyncGeneratorState]] is draining-queue.
  //
  // NOTE: DrainingQueue_AwaitingReturn shouldn't reach here.
  MOZ_ASSERT(generator->isDrainingQueue());

  // Step 2. Let queue be generator.[[AsyncGeneratorQueue]].
  // Step 3. Repeat, while queue is not empty,
  Rooted<AsyncGeneratorRequest*> next(cx);
  RootedValue value(cx);
  while (!generator->isQueueEmpty()) {
    // Step 3.a. Let next be the first element of queue.
    next = AsyncGeneratorObject::peekRequest(generator);
    if (!next) {
      return false;
    }

    // Step 3.b. Let completion be Completion(next.[[Completion]]).
    CompletionKind completionKind = next->completionKind();

    // Step 3.c. If completion is a return completion, then
    if (completionKind == CompletionKind::Return) {
      value = next->completionValue();

      // Step 3.c.i. Perform AsyncGeneratorAwaitReturn(generator).
      // Step 3.c.ii. Return unused.
      return AsyncGeneratorAwaitReturn(cx, generator, value);
    }

    // Step 3.d. Else,
    if (completionKind == CompletionKind::Throw) {
      value = next->completionValue();

      // Step 3.d.ii. Perform AsyncGeneratorCompleteStep(generator, completion,
      //              true).
      if (!AsyncGeneratorCompleteStepThrow(cx, generator, value)) {
        return false;
      }
    } else {
      // Step 3.d.i. If completion is a normal completion, then
      // Step 3.d.i.1. Set completion to NormalCompletion(undefined).
      // Step 3.d.ii. Perform AsyncGeneratorCompleteStep(generator, completion,
      //              true).
      if (!AsyncGeneratorCompleteStepNormal(cx, generator, UndefinedHandleValue,
                                            true)) {
        return false;
      }
    }

    MOZ_ASSERT(!generator->isExecuting());
    MOZ_ASSERT(!generator->isExecuting_AwaitingYieldReturn());
    if (generator->isDrainingQueue_AwaitingReturn()) {
      return true;
    }
  }

  // Step 4. Set generator.[[AsyncGeneratorState]] to completed.
  generator->setCompleted();

  // Step 5. Return unused.
  return true;
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorValidate ( generator, generatorBrand )
// https://tc39.es/ecma262/#sec-asyncgeneratorvalidate
//
// Testing part.
[[nodiscard]] static bool IsAsyncGeneratorValid(HandleValue asyncGenVal) {
  // Step 1. Perform
  //         ? RequireInternalSlot(generator, [[AsyncGeneratorContext]]).
  // Step 2. Perform
  //         ? RequireInternalSlot(generator, [[AsyncGeneratorState]]).
  // Step 3. Perform
  //         ? RequireInternalSlot(generator, [[AsyncGeneratorQueue]]).
  // Step 4. If generator.[[GeneratorBrand]] is not generatorBrand, throw a
  //         TypeError exception.
  return asyncGenVal.isObject() &&
         asyncGenVal.toObject().canUnwrapAs<AsyncGeneratorObject>();
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorValidate ( generator, generatorBrand )
// https://tc39.es/ecma262/#sec-asyncgeneratorvalidate
//
// Throwing part.
[[nodiscard]] static bool AsyncGeneratorValidateThrow(
    JSContext* cx, MutableHandleValue result) {
  Rooted<PromiseObject*> resultPromise(
      cx, CreatePromiseObjectForAsyncGenerator(cx));
  if (!resultPromise) {
    return false;
  }

  RootedValue badGeneratorError(cx);
  if (!GetTypeError(cx, JSMSG_NOT_AN_ASYNC_GENERATOR, &badGeneratorError)) {
    return false;
  }

  if (!RejectPromiseInternal(cx, resultPromise, badGeneratorError)) {
    return false;
  }

  result.setObject(*resultPromise);
  return true;
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorEnqueue ( generator, completion, promiseCapability )
// https://tc39.es/ecma262/#sec-asyncgeneratorenqueue
[[nodiscard]] static bool AsyncGeneratorEnqueue(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    CompletionKind completionKind, HandleValue completionValue,
    Handle<PromiseObject*> resultPromise) {
  // Step 1. Let request be
  //         AsyncGeneratorRequest { [[Completion]]: completion,
  //                                 [[Capability]]: promiseCapability }.
  Rooted<AsyncGeneratorRequest*> request(
      cx, AsyncGeneratorObject::createRequest(cx, generator, completionKind,
                                              completionValue, resultPromise));
  if (!request) {
    return false;
  }

  // Step 2. Append request to generator.[[AsyncGeneratorQueue]].
  // Step 3. Return unused.
  return AsyncGeneratorObject::enqueueRequest(cx, generator, request);
}

class MOZ_STACK_CLASS MaybeEnterAsyncGeneratorRealm {
  mozilla::Maybe<AutoRealm> ar_;

 public:
  MaybeEnterAsyncGeneratorRealm() = default;
  ~MaybeEnterAsyncGeneratorRealm() = default;

  // Enter async generator's realm, and wrap the method's argument value if
  // necessary.
  [[nodiscard]] bool maybeEnterAndWrap(JSContext* cx,
                                       Handle<AsyncGeneratorObject*> generator,
                                       MutableHandleValue value) {
    if (generator->compartment() == cx->compartment()) {
      return true;
    }

    ar_.emplace(cx, generator);
    return cx->compartment()->wrap(cx, value);
  }

  // Leave async generator's realm, and wrap the method's result value if
  // necessary.
  [[nodiscard]] bool maybeLeaveAndWrap(JSContext* cx,
                                       MutableHandleValue result) {
    if (!ar_) {
      return true;
    }
    ar_.reset();

    return cx->compartment()->wrap(cx, result);
  }
};

[[nodiscard]] static bool AsyncGeneratorMethodSanityCheck(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator) {
  if (generator->isSuspendedStart() || generator->isSuspendedYield() ||
      generator->isCompleted()) {
    // The spec assumes the queue is empty when async generator methods are
    // called with those state, but our debugger allows calling those methods
    // in unexpected state, such as before suspendedStart.
    if (MOZ_UNLIKELY(!generator->isQueueEmpty())) {
      JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                                JSMSG_SUSPENDED_QUEUE_NOT_EMPTY);
      return false;
    }
  }

  return true;
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// %AsyncGeneratorPrototype%.next ( value )
// https://tc39.es/ecma262/#sec-asyncgenerator-prototype-next
bool js::AsyncGeneratorNext(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).
  // Step 4. IfAbruptRejectPromise(result, promiseCapability).
  // (reordered)
  if (!IsAsyncGeneratorValid(args.thisv())) {
    return AsyncGeneratorValidateThrow(cx, args.rval());
  }

  // Step 1. Let generator be the this value.
  // (implicit)
  Rooted<AsyncGeneratorObject*> generator(
      cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());

  MaybeEnterAsyncGeneratorRealm maybeEnterRealm;

  RootedValue completionValue(cx, args.get(0));
  if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
    return false;
  }

  // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  Rooted<PromiseObject*> resultPromise(
      cx, CreatePromiseObjectForAsyncGenerator(cx));
  if (!resultPromise) {
    return false;
  }

  if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
    return false;
  }

  // Step 5. Let state be generator.[[AsyncGeneratorState]].
  // Step 6. If state is completed, then
  if (generator->isCompleted()) {
    MOZ_ASSERT(generator->isQueueEmpty());

    // Step 6.a. Let iteratorResult be CreateIteratorResultObject(undefined,
    //           true).
    JSObject* resultObj =
        CreateIterResultObject(cx, UndefinedHandleValue, true);
    if (!resultObj) {
      return false;
    }

    // Step 6.b. Perform ! Call(promiseCapability.[[Resolve]], undefined, «
    //           iteratorResult »).
    RootedValue resultValue(cx, ObjectValue(*resultObj));
    if (!ResolvePromiseInternal(cx, resultPromise, resultValue)) {
      return false;
    }
  } else {
    // Step 7. Let completion be NormalCompletion(value).
    // Step 8. Perform AsyncGeneratorEnqueue(generator, completion,
    //         promiseCapability).
    if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Normal,
                               completionValue, resultPromise)) {
      return false;
    }

    // Step 9. If state is either suspended-start or suspended-yield, then
    if (generator->isSuspendedStart() || generator->isSuspendedYield()) {
      MOZ_ASSERT(generator->isQueueLengthOne());

      // Step 9.a. Perform AsyncGeneratorResume(generator, completion).
      if (!AsyncGeneratorResume(cx, generator, CompletionKind::Normal,
                                completionValue)) {
        return false;
      }
    } else {
      // Step 10. Else,
      // Step 10.a. Assert: state is either executing or draining-queue.
      MOZ_ASSERT(generator->isExecuting() ||
                 generator->isExecuting_AwaitingYieldReturn() ||
                 generator->isDrainingQueue() ||
                 generator->isDrainingQueue_AwaitingReturn());
    }
  }

  // Step 6.c. Return promiseCapability.[[Promise]].
  // and
  // Step 11. Return promiseCapability.[[Promise]].
  args.rval().setObject(*resultPromise);

  return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// %AsyncGeneratorPrototype%.return ( value )
// https://tc39.es/ecma262/#sec-asyncgenerator-prototype-return
bool js::AsyncGeneratorReturn(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).
  // Step 4. IfAbruptRejectPromise(result, promiseCapability).
  // (reordered)
  if (!IsAsyncGeneratorValid(args.thisv())) {
    return AsyncGeneratorValidateThrow(cx, args.rval());
  }

  // Step 1. Let generator be the this value.
  Rooted<AsyncGeneratorObject*> generator(
      cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());

  MaybeEnterAsyncGeneratorRealm maybeEnterRealm;

  RootedValue completionValue(cx, args.get(0));
  if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
    return false;
  }

  // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  Rooted<PromiseObject*> resultPromise(
      cx, CreatePromiseObjectForAsyncGenerator(cx));
  if (!resultPromise) {
    return false;
  }

  if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
    return false;
  }

  // Step 5. Let completion be ReturnCompletion(value).
  // Step 6. Perform AsyncGeneratorEnqueue(generator, completion,
  //         promiseCapability).
  if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Return,
                             completionValue, resultPromise)) {
    return false;
  }

  // Step 7. Let state be generator.[[AsyncGeneratorState]].
  // Step 8. If state is either suspended-start or completed, then
  if (generator->isSuspendedStart() || generator->isCompleted()) {
    MOZ_ASSERT(generator->isQueueLengthOne());

    // Step 8.a. Set generator.[[AsyncGeneratorState]] to draining-queue.
    generator->setDrainingQueue();

    // Step 8.b. Perform AsyncGeneratorAwaitReturn(generator).
    if (!AsyncGeneratorAwaitReturn(cx, generator, completionValue)) {
      return false;
    }
  } else if (generator->isSuspendedYield()) {
    // Step 9. Else if state is suspended-yield, then
    MOZ_ASSERT(generator->isQueueLengthOne());

    // Step 9.a. Perform AsyncGeneratorResume(generator, completion).
    //
    // https://tc39.es/ecma262/#sec-asyncgeneratorresume
    // AsyncGeneratorResume ( generator, completion )
    //
    // Step 7. Resume the suspended evaluation of genContext using completion as
    //         the result of the operation that suspended it. Let result be the
    //         Completion Record returned by the resumed computation.
    // Step 10. Return unused.
    //
    // AsyncGeneratorYield ( value )
    // https://tc39.es/ecma262/#sec-asyncgeneratoryield
    //
    // Step 12.d. Resume callerContext passing undefined. If genContext is ever
    //            resumed again, let resumptionValue be the Completion Record
    //            with which it is resumed.
    // Step 12.e. Assert: If control reaches here, then genContext is the
    //            running execution context again.
    // Step 12.f. Return ?
    //            AsyncGeneratorUnwrapYieldResumption(resumptionValue).
    //
    if (!AsyncGeneratorUnwrapYieldResumptionWithReturn(cx, generator,
                                                       completionValue)) {
      // The failure path here is for the Await inside
      // AsyncGeneratorUnwrapYieldResumption, where a corrupted Promise is
      // passed and called there.
      //
      // Per spec, the operation should be performed after resuming the
      // generator, but given that we're performing the Await before
      // resuming the generator, we need to handle the special throw completion
      // here.

      // For uncatchable exception, there's nothing we can do.
      if (!cx->isExceptionPending()) {
        return false;
      }

      // Resume the generator with throw completion, so that it behaves in the
      // same way as the Await throws.
      RootedValue exception(cx);
      if (!GetAndClearException(cx, &exception)) {
        return false;
      }
      if (!AsyncGeneratorResume(cx, generator, CompletionKind::Throw,
                                exception)) {
        return false;
      }
    }
  } else {
    // Step 10. Else,
    // Step 10.a. Assert: state is either executing or draining-queue.
    MOZ_ASSERT(generator->isExecuting() ||
               generator->isExecuting_AwaitingYieldReturn() ||
               generator->isDrainingQueue() ||
               generator->isDrainingQueue_AwaitingReturn());
  }

  // Step 11. Return promiseCapability.[[Promise]].
  args.rval().setObject(*resultPromise);

  return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// %AsyncGeneratorPrototype%.throw ( exception )
// https://tc39.es/ecma262/#sec-asyncgenerator-prototype-throw
bool js::AsyncGeneratorThrow(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 3. Let result be Completion(AsyncGeneratorValidate(generator, empty)).
  // Step 4. IfAbruptRejectPromise(result, promiseCapability).
  // (reordered)
  if (!IsAsyncGeneratorValid(args.thisv())) {
    return AsyncGeneratorValidateThrow(cx, args.rval());
  }

  // Step 1. Let generator be the this value.
  Rooted<AsyncGeneratorObject*> generator(
      cx, &args.thisv().toObject().unwrapAs<AsyncGeneratorObject>());

  MaybeEnterAsyncGeneratorRealm maybeEnterRealm;

  RootedValue completionValue(cx, args.get(0));
  if (!maybeEnterRealm.maybeEnterAndWrap(cx, generator, &completionValue)) {
    return false;
  }

  // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  Rooted<PromiseObject*> resultPromise(
      cx, CreatePromiseObjectForAsyncGenerator(cx));
  if (!resultPromise) {
    return false;
  }

  if (!AsyncGeneratorMethodSanityCheck(cx, generator)) {
    return false;
  }

  // Step 5. Let state be generator.[[AsyncGeneratorState]].
  // Step 6. If state is suspended-start, then
  if (generator->isSuspendedStart()) {
    // Step 6.a. Set generator.[[AsyncGeneratorState]] to completed.
    // Step 6.b. Set state to completed.
    generator->setCompleted();
  }

  // Step 7. If state is completed, then
  if (generator->isCompleted()) {
    MOZ_ASSERT(generator->isQueueEmpty());

    // Step 7.a. Perform ! Call(promiseCapability.[[Reject]], undefined, «
    //           exception »).
    if (!RejectPromiseInternal(cx, resultPromise, completionValue)) {
      return false;
    }
  } else {
    // Step 8. Let completion be ThrowCompletion(exception).
    // Step 9. Perform AsyncGeneratorEnqueue(generator, completion,
    //         promiseCapability).
    if (!AsyncGeneratorEnqueue(cx, generator, CompletionKind::Throw,
                               completionValue, resultPromise)) {
      return false;
    }

    // Step 10. If state is suspended-yield, then
    if (generator->isSuspendedYield()) {
      MOZ_ASSERT(generator->isQueueLengthOne());

      // Step 10.a. Perform AsyncGeneratorResume(generator, completion).
      if (!AsyncGeneratorResume(cx, generator, CompletionKind::Throw,
                                completionValue)) {
        return false;
      }
    } else {
      // Step 11. Else,
      // Step 11.a. Assert: state is either executing or draining-queue.
      MOZ_ASSERT(generator->isExecuting() ||
                 generator->isExecuting_AwaitingYieldReturn() ||
                 generator->isDrainingQueue() ||
                 generator->isDrainingQueue_AwaitingReturn());
    }
  }

  // Step 7.b. Return promiseCapability.[[Promise]].
  // and
  // Step 12. Return promiseCapability.[[Promise]].
  args.rval().setObject(*resultPromise);

  return maybeEnterRealm.maybeLeaveAndWrap(cx, args.rval());
}

// ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
//
// AsyncGeneratorResume ( generator, completion )
// https://tc39.es/ecma262/#sec-asyncgeneratorresume
//
// This returns false only for internal errors.
[[nodiscard]] static bool AsyncGeneratorResume(
    JSContext* cx, Handle<AsyncGeneratorObject*> generator,
    CompletionKind completionKind, HandleValue argument) {
  AutoAsyncResumeDepth autoDepth(cx);

  // Given that yield can resume again, we implement it as a loop.
  JS::Rooted<JS::Value> resumeArgument(cx, argument);
  while (true) {
    MOZ_ASSERT(!generator->isClosed(),
               "closed generator when resuming async generator");
    MOZ_ASSERT(generator->isSuspended(),
               "non-suspended generator when resuming async generator");

    // Step 1. Assert: generator.[[AsyncGeneratorState]] is either
    //         suspended-start or suspended-yield.
    //
    // NOTE: We're using suspend/resume also for await. and the state can be
    //       anything.

    // Step 2. Let genContext be generator.[[AsyncGeneratorContext]].
    // Step 3. Let callerContext be the running execution context.
    // Step 4. Suspend callerContext.
    // (handled in generator)

    // Step 5. Set generator.[[AsyncGeneratorState]] to executing.
    generator->setExecuting();

    // Step 6. Push genContext onto the execution context stack; genContext is
    //         now the running execution context.
    // Step 7. Resume the suspended evaluation of genContext using completion as
    //         the result of the operation that suspended it. Let result be the
    //         Completion Record returned by the resumed computation.
    // Step 8. Assert: result is never an abrupt completion.
    // Step 9. Assert: When we return here, genContext has already been removed
    //         from the execution context stack and callerContext is the
    //         currently running execution context.
    // Step 10. Return unused.
    //
    // NOTE: Once the CallSelfHostedFunction call returns, the generator itslef
    //       is suspended implementation-wise.
    //       On the other hand, spec-wise, the generator may still be running
    //       and some operations (a part of Await or Yield, etc) are supposed
    //       to be handled with the genContext.
    //       when the execution continues, we resume the generator with
    //       the corresponding completion value.
    Handle<PropertyName*> funName = completionKind == CompletionKind::Normal
                                        ? cx->names().AsyncGeneratorNext
                                    : completionKind == CompletionKind::Throw
                                        ? cx->names().AsyncGeneratorThrow
                                        : cx->names().AsyncGeneratorReturn;
    FixedInvokeArgs<1> args(cx);
    args[0].set(resumeArgument);
    RootedValue thisOrRval(cx, ObjectValue(*generator));
    if (!CallSelfHostedFunction(cx, funName, thisOrRval, args, &thisOrRval)) {
      if (!generator->isClosed()) {
        generator->setClosed(cx);
      }
      return AsyncGeneratorThrown(cx, generator);
    }

    if (generator->isAfterAwait()) {
      if (!AsyncGeneratorAwait(cx, generator, thisOrRval)) {
        // Not much we can do about uncatchable exceptions, so just bail.
        if (!cx->isExceptionPending()) {
          return false;
        }
        // This can happen if PromiseResolve inside Await fails.
        //
        // Per spec, that happens without suspending the generator.
        // Given that implementation-wise we've already suspended the generator,
        // resume it with a throw completion.
        //
        // Other OOM can also hit this path, but that should also be
        // treated as an error before suspending the generator.
        if (!GetAndClearException(cx, &resumeArgument)) {
          return false;
        }
        completionKind = CompletionKind::Throw;
        continue;
      }
      return true;
    }

    if (generator->isAfterYield()) {
      bool resumeAgain = false;
      if (!AsyncGeneratorYield(cx, generator, thisOrRval, &resumeAgain,
                               &completionKind, &resumeArgument)) {
        // Not much we can do about uncatchable exceptions, so just bail.
        if (!cx->isExceptionPending()) {
          return false;
        }
        // This can also happen if PromiseResolve inside Await fails
        // during AsyncGeneratorUnwrapYieldResumption.
        // AsyncGeneratorUnwrapYieldResumption is performed only if the
        // queue is not empty.
        //
        // Per spec, the PromiseResolve failure happens before suspending the
        // generator.
        // Given that implementation-wise we've already suspended the generator,
        // resume it with a throw completion.
        //
        // On the other hand, per spec, if the queue is empty, this generator
        // is getting suspended and there's no fallible operation.
        // However, we can hit OOM during it, for example in
        // AsyncGeneratorCompleteStep while creating the iterator result object.
        //
        // And in this case, there's no spec-compliant way to report it, to
        // either to the generator itself or to the consumer of the generator.
        // Treat it as an error happened as a a part of microtask handling,
        // and propagate it to the microtask queue.
        if (generator->isQueueEmpty()) {
          return false;
        }
        if (!GetAndClearException(cx, &resumeArgument)) {
          return false;
        }
        completionKind = CompletionKind::Throw;
        continue;
      }
      if (resumeAgain) {
        MOZ_ASSERT(completionKind != CompletionKind::Return);
        continue;
      }
      return true;
    }

    return AsyncGeneratorReturned(cx, generator, thisOrRval);
  }
}

#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
/**
 * Explicit Resource Management Proposal
 * 27.1.3.1 %AsyncIteratorPrototype% [ @@asyncDispose ] ( )
 * https://arai-a.github.io/ecma262-compare/?pr=3000&id=sec-%25asynciteratorprototype%25-%40%40asyncdispose
 */

static bool AsyncIteratorDispose(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1. Let O be the this value.
  JS::Handle<JS::Value> O = args.thisv();

  // Step 2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
  JS::Rooted<PromiseObject*> promise(cx,
                                     PromiseObject::createSkippingExecutor(cx));
  if (!promise) {
    return false;
  }

  // Step 3. Let return be Completion(GetMethod(O, "return")).
  JS::Rooted<JS::Value> returnMethod(cx);
  if (!GetProperty(cx, O, cx->names().return_, &returnMethod)) {
    // Step 4. IfAbruptRejectPromise(return, promiseCapability).
    return AbruptRejectPromise(cx, args, promise, nullptr);
  }

  // Step 5. If return is undefined, then
  // As per the spec GetMethod returns undefined if the property is either null
  // or undefined thus here we check for both.
  if (returnMethod.isNullOrUndefined()) {
    // Step 5.a. Perform ! Call(promiseCapability.[[Resolve]], undefined, «
    // undefined »).
    if (!PromiseObject::resolve(cx, promise, JS::UndefinedHandleValue)) {
      return false;
    }
    args.rval().setObject(*promise);
    return true;
  }

  // GetMethod also throws a TypeError exception if the function is not callable
  // thus we perform that check here.
  if (!IsCallable(returnMethod)) {
    ReportIsNotFunction(cx, returnMethod);
    return AbruptRejectPromise(cx, args, promise, nullptr);
  }

  // Step 6. Else,
  // Step 6.a. Let result be Completion(Call(return, O, « undefined »)).
  JS::Rooted<JS::Value> rval(cx);
  if (!Call(cx, returnMethod, O, JS::UndefinedHandleValue, &rval)) {
    // Step 6.b. IfAbruptRejectPromise(result, promiseCapability).
    return AbruptRejectPromise(cx, args, promise, nullptr);
  }

  // Step 6.c-g.
  if (!InternalAsyncIteratorDisposeAwait(cx, rval, promise)) {
    return AbruptRejectPromise(cx, args, promise, nullptr);
  }

  // Step 7. Return promiseCapability.[[Promise]].
  args.rval().setObject(*promise);
  return true;
}
#endif

static const JSFunctionSpec async_generator_methods[] = {
    JS_FN("next", js::AsyncGeneratorNext, 10),
    JS_FN("throw", js::AsyncGeneratorThrow, 10),
    JS_FN("return", js::AsyncGeneratorReturn, 10),
    JS_FS_END,
};

static JSObject* CreateAsyncGeneratorFunction(JSContext* cx, JSProtoKey key) {
  RootedObject proto(cx, &cx->global()->getFunctionConstructor());
  Handle<PropertyName*> name = cx->names().AsyncGeneratorFunction;

  // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
  //
  // The AsyncGeneratorFunction Constructor
  // https://tc39.es/ecma262/#sec-asyncgeneratorfunction-constructor
  return NewFunctionWithProto(cx, AsyncGeneratorConstructor, 1,
                              FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
                              gc::AllocKind::FUNCTION, TenuredObject);
}

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

static bool AsyncGeneratorFunctionClassFinish(JSContext* cx,
                                              HandleObject asyncGenFunction,
                                              HandleObject asyncGenerator) {
  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(asyncGenerator->as<NativeObject>().getLastProperty().key() ==
             NameToId(cx->names().constructor));
  MOZ_ASSERT(!asyncGenerator->as<NativeObject>().inDictionaryMode());

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

  RootedObject asyncIterProto(
      cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
  if (!asyncIterProto) {
    return false;
  }

  // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
  //
  // AsyncGenerator Objects
  // https://tc39.es/ecma262/#sec-asyncgenerator-objects
  RootedObject asyncGenProto(cx, GlobalObject::createBlankPrototypeInheriting(
                                     cx, &PlainObject::class_, asyncIterProto));
  if (!asyncGenProto) {
    return false;
  }
  if (!DefinePropertiesAndFunctions(cx, asyncGenProto, nullptr,
                                    async_generator_methods) ||
      !DefineToStringTag(cx, asyncGenProto, cx->names().AsyncGenerator)) {
    return false;
  }

  // ES2026 draft rev bdfd596ffad5aeb2957aed4e1db36be3665c69ec
  //
  // Properties of the AsyncGeneratorFunction Prototype Object
  // https://tc39.es/ecma262/#sec-properties-of-asyncgeneratorfunction-prototype
  if (!LinkConstructorAndPrototype(cx, asyncGenerator, asyncGenProto,
                                   JSPROP_READONLY, JSPROP_READONLY) ||
      !DefineToStringTag(cx, asyncGenerator,
                         cx->names().AsyncGeneratorFunction)) {
    return false;
  }

  global->setAsyncGeneratorPrototype(asyncGenProto);

  return true;
}

static const ClassSpec AsyncGeneratorFunctionClassSpec = {
    CreateAsyncGeneratorFunction,
    CreateAsyncGeneratorFunctionPrototype,
    nullptr,
    nullptr,
    nullptr,
    nullptr,
    AsyncGeneratorFunctionClassFinish,
    ClassSpec::DontDefineConstructor,
};

const JSClass js::AsyncGeneratorFunctionClass = {
    "AsyncGeneratorFunction",
    0,
    JS_NULL_CLASS_OPS,
    &AsyncGeneratorFunctionClassSpec,
};

[[nodiscard]] bool js::AsyncGeneratorPromiseReactionJob(
    JSContext* cx, PromiseHandler handler,
    Handle<AsyncGeneratorObject*> generator, HandleValue argument) {
  // Await's handlers don't return a value, nor throw any exceptions.
  // They fail only on OOM.
  switch (handler) {
    case PromiseHandler::AsyncGeneratorAwaitedFulfilled:
      return AsyncGeneratorAwaitedFulfilled(cx, generator, argument);

    case PromiseHandler::AsyncGeneratorAwaitedRejected:
      return AsyncGeneratorAwaitedRejected(cx, generator, argument);

    case PromiseHandler::AsyncGeneratorAwaitReturnFulfilled:
      return AsyncGeneratorAwaitReturnFulfilled(cx, generator, argument);

    case PromiseHandler::AsyncGeneratorAwaitReturnRejected:
      return AsyncGeneratorAwaitReturnRejected(cx, generator, argument);

    case PromiseHandler::AsyncGeneratorYieldReturnAwaitedFulfilled:
      return AsyncGeneratorYieldReturnAwaitedFulfilled(cx, generator, argument);

    case PromiseHandler::AsyncGeneratorYieldReturnAwaitedRejected:
      return AsyncGeneratorYieldReturnAwaitedRejected(cx, generator, argument);

    default:
      MOZ_CRASH("Bad handler in AsyncGeneratorPromiseReactionJob");
  }
}

// ---------------------
// AsyncFromSyncIterator
// ---------------------

const JSClass AsyncFromSyncIteratorObject::class_ = {
    "AsyncFromSyncIteratorObject",
    JSCLASS_HAS_RESERVED_SLOTS(AsyncFromSyncIteratorObject::Slots),
};

/*
 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
 *
 * CreateAsyncFromSyncIterator ( syncIteratorRecord )
 * https://tc39.es/ecma262/#sec-createasyncfromsynciterator
 */

JSObject* js::CreateAsyncFromSyncIterator(JSContext* cx, HandleObject iter,
                                          HandleValue nextMethod) {
  // Steps 1-5.
  return AsyncFromSyncIteratorObject::create(cx, iter, nextMethod);
}

/*
 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
 *
 * CreateAsyncFromSyncIterator ( syncIteratorRecord )
 * https://tc39.es/ecma262/#sec-createasyncfromsynciterator
 */

/* static */
JSObject* AsyncFromSyncIteratorObject::create(JSContext* cx, HandleObject iter,
                                              HandleValue nextMethod) {
  // Step 1. Let asyncIterator be
  //         OrdinaryObjectCreate(%AsyncFromSyncIteratorPrototype%, «
  //         [[SyncIteratorRecord]] »).
  RootedObject proto(cx,
                     GlobalObject::getOrCreateAsyncFromSyncIteratorPrototype(
                         cx, cx->global()));
  if (!proto) {
    return nullptr;
  }

  AsyncFromSyncIteratorObject* asyncIter =
      NewObjectWithGivenProto<AsyncFromSyncIteratorObject>(cx, proto);
  if (!asyncIter) {
    return nullptr;
  }

  // Step 3. Let nextMethod be ! Get(asyncIterator, "next").
  // (done in caller)

  // Step 2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.
  // Step 4. Let iteratorRecord be the Iterator Record { [[Iterator]]:
  //         asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
  asyncIter->init(iter, nextMethod);

  // Step 5. Return iteratorRecord.
  return asyncIter;
}

/**
 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
 *
 * %AsyncFromSyncIteratorPrototype%.next ( [ value ] )
 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.next
 */

static bool AsyncFromSyncIteratorNext(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Normal);
}

/**
 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
 *
 * %AsyncFromSyncIteratorPrototype%.return ( [ value ] )
 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.return
 */

static bool AsyncFromSyncIteratorReturn(JSContext* cx, unsigned argc,
                                        Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Return);
}

/**
 * ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
 *
 * %AsyncFromSyncIteratorPrototype%.throw ( [ value ] )
 * https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%.throw
 */

static bool AsyncFromSyncIteratorThrow(JSContext* cx, unsigned argc,
                                       Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);
  return AsyncFromSyncIteratorMethod(cx, args, CompletionKind::Throw);
}

static const JSFunctionSpec async_from_sync_iter_methods[] = {
    JS_FN("next", AsyncFromSyncIteratorNext, 10),
    JS_FN("throw", AsyncFromSyncIteratorThrow, 10),
    JS_FN("return", AsyncFromSyncIteratorReturn, 10),
    JS_FS_END,
};

bool GlobalObject::initAsyncFromSyncIteratorProto(
    JSContext* cx, Handle<GlobalObject*> global) {
  if (global->hasBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto)) {
    return true;
  }

  RootedObject asyncIterProto(
      cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
  if (!asyncIterProto) {
    return false;
  }

  // ES2024 draft rev 53454a9a596d90473d2152ef04656d605162cd4c
  //
  // The %AsyncFromSyncIteratorPrototype% Object
  // https://tc39.es/ecma262/#sec-%asyncfromsynciteratorprototype%-object
  RootedObject asyncFromSyncIterProto(
      cx, GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
                                                       asyncIterProto));
  if (!asyncFromSyncIterProto) {
    return false;
  }
  if (!DefinePropertiesAndFunctions(cx, asyncFromSyncIterProto, nullptr,
                                    async_from_sync_iter_methods) ||
      !DefineToStringTag(cx, asyncFromSyncIterProto,
                         cx->names().Async_from_Sync_Iterator_)) {
    return false;
  }

  global->initBuiltinProto(ProtoKind::AsyncFromSyncIteratorProto,
                           asyncFromSyncIterProto);
  return true;
}

// -------------
// AsyncIterator
// -------------

static const JSFunctionSpec async_iterator_proto_methods[] = {
    JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity"00),
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    JS_SYM_FN(asyncDispose, AsyncIteratorDispose, 00),
#endif
    JS_FS_END,
};

static const JSFunctionSpec async_iterator_proto_methods_with_helpers[] = {
    JS_SELF_HOSTED_FN("map""AsyncIteratorMap"10),
    JS_SELF_HOSTED_FN("filter""AsyncIteratorFilter"10),
    JS_SELF_HOSTED_FN("take""AsyncIteratorTake"10),
    JS_SELF_HOSTED_FN("drop""AsyncIteratorDrop"10),
    JS_SELF_HOSTED_FN("asIndexedPairs""AsyncIteratorAsIndexedPairs"00),
    JS_SELF_HOSTED_FN("flatMap""AsyncIteratorFlatMap"10),
    JS_SELF_HOSTED_FN("reduce""AsyncIteratorReduce"10),
    JS_SELF_HOSTED_FN("toArray""AsyncIteratorToArray"00),
    JS_SELF_HOSTED_FN("forEach""AsyncIteratorForEach"10),
    JS_SELF_HOSTED_FN("some""AsyncIteratorSome"10),
    JS_SELF_HOSTED_FN("every""AsyncIteratorEvery"10),
    JS_SELF_HOSTED_FN("find""AsyncIteratorFind"10),
    JS_SELF_HOSTED_SYM_FN(asyncIterator, "AsyncIteratorIdentity"00),
#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
    JS_SYM_FN(asyncDispose, AsyncIteratorDispose, 00),
#endif
    JS_FS_END,
};

bool GlobalObject::initAsyncIteratorProto(JSContext* cx,
                                          Handle<GlobalObject*> global) {
  if (global->hasBuiltinProto(ProtoKind::AsyncIteratorProto)) {
    return true;
  }

  // 25.1.3 The %AsyncIteratorPrototype% Object
  RootedObject asyncIterProto(
      cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
  if (!asyncIterProto) {
    return false;
  }
  if (!DefinePropertiesAndFunctions(cx, asyncIterProto, nullptr,
                                    async_iterator_proto_methods)) {
    return false;
  }

  global->initBuiltinProto(ProtoKind::AsyncIteratorProto, asyncIterProto);
  return true;
}

// https://tc39.es/proposal-iterator-helpers/#sec-asynciterator as of revision
// 8f10db5.
static bool AsyncIteratorConstructor(JSContext* cx, unsigned argc, Value* vp) {
  CallArgs args = CallArgsFromVp(argc, vp);

  // Step 1.
  if (!ThrowIfNotConstructing(cx, args, "AsyncIterator")) {
    return false;
  }
  // Throw TypeError if NewTarget is the active function object, preventing the
  // Iterator constructor from being used directly.
  if (args.callee() == args.newTarget().toObject()) {
    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                              JSMSG_BOGUS_CONSTRUCTOR, "AsyncIterator");
    return false;
  }

  // Step 2.
  RootedObject proto(cx);
  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_AsyncIterator,
                                          &proto)) {
    return false;
  }

  JSObject* obj = NewObjectWithClassProto<AsyncIteratorObject>(cx, proto);
  if (!obj) {
    return false;
  }

  args.rval().setObject(*obj);
  return true;
}

static const ClassSpec AsyncIteratorObjectClassSpec = {
    GenericCreateConstructor<AsyncIteratorConstructor, 0,
                             gc::AllocKind::FUNCTION>,
    GenericCreatePrototype<AsyncIteratorObject>,
    nullptr,
    nullptr,
    async_iterator_proto_methods_with_helpers,
    nullptr,
    nullptr,
};

const JSClass AsyncIteratorObject::class_ = {
    "AsyncIterator",
    JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
    JS_NULL_CLASS_OPS,
    &AsyncIteratorObjectClassSpec,
};

const JSClass AsyncIteratorObject::protoClass_ = {
    "AsyncIterator.prototype",
    JSCLASS_HAS_CACHED_PROTO(JSProto_AsyncIterator),
    JS_NULL_CLASS_OPS,
    &AsyncIteratorObjectClassSpec,
};

// Iterator Helper proposal
static const JSFunctionSpec async_iterator_helper_methods[] = {
    JS_SELF_HOSTED_FN("next""AsyncIteratorHelperNext"10),
    JS_SELF_HOSTED_FN("return""AsyncIteratorHelperReturn"10),
    JS_SELF_HOSTED_FN("throw""AsyncIteratorHelperThrow"10),
    JS_FS_END,
};

static const JSClass AsyncIteratorHelperPrototypeClass = {
    "Async Iterator Helper",
    0,
};

const JSClass AsyncIteratorHelperObject::class_ = {
    "Async Iterator Helper",
    JSCLASS_HAS_RESERVED_SLOTS(AsyncIteratorHelperObject::SlotCount),
};

/* static */
NativeObject* GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
    JSContext* cx, Handle<GlobalObject*> global) {
  return MaybeNativeObject(
      getOrCreateBuiltinProto(cx, global, ProtoKind::AsyncIteratorHelperProto,
                              initAsyncIteratorHelperProto));
}

/* static */
bool GlobalObject::initAsyncIteratorHelperProto(JSContext* cx,
                                                Handle<GlobalObject*> global) {
  if (global->hasBuiltinProto(ProtoKind::AsyncIteratorHelperProto)) {
    return true;
  }

  RootedObject asyncIterProto(
      cx, GlobalObject::getOrCreateAsyncIteratorPrototype(cx, global));
  if (!asyncIterProto) {
    return false;
  }

  RootedObject asyncIteratorHelperProto(
      cx, GlobalObject::createBlankPrototypeInheriting(
              cx, &AsyncIteratorHelperPrototypeClass, asyncIterProto));
  if (!asyncIteratorHelperProto) {
    return false;
  }
  if (!DefinePropertiesAndFunctions(cx, asyncIteratorHelperProto, nullptr,
                                    async_iterator_helper_methods)) {
    return false;
  }

  global->initBuiltinProto(ProtoKind::AsyncIteratorHelperProto,
                           asyncIteratorHelperProto);
  return true;
}

AsyncIteratorHelperObject* js::NewAsyncIteratorHelper(JSContext* cx) {
  RootedObject proto(cx, GlobalObject::getOrCreateAsyncIteratorHelperPrototype(
                             cx, cx->global()));
  if (!proto) {
    return nullptr;
  }
  return NewObjectWithGivenProto<AsyncIteratorHelperObject>(cx, proto);
}

Messung V0.5 in Prozent
C=70 H=97 G=84

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.