mirror of
https://github.com/bendtherules/completion-viz.git
synced 2026-08-18 13:42:51 +00:00
201 lines
9.1 KiB
JavaScript
201 lines
9.1 KiB
JavaScript
import {
|
|
Completion,
|
|
NormalCompletion,
|
|
Q, X,
|
|
EnsureCompletion,
|
|
} from '../completion.mjs';
|
|
import { surroundingAgent } from '../engine.mjs';
|
|
import { Value } from '../value.mjs';
|
|
import { Evaluate } from '../evaluator.mjs';
|
|
import { resume } from '../helpers.mjs';
|
|
import {
|
|
Assert,
|
|
CreateIterResultObject,
|
|
RequireInternalSlot,
|
|
} from './all.mjs';
|
|
|
|
// This file covers abstract operations defined in #sec-generator-objects
|
|
|
|
// #sec-generatorstart
|
|
export function GeneratorStart(generator, generatorBody) {
|
|
// 1. Assert: The value of generator.[[GeneratorState]] is undefined.
|
|
Assert(generator.GeneratorState === Value.undefined);
|
|
// 2. Let genContext be the running execution context.
|
|
const genContext = surroundingAgent.runningExecutionContext;
|
|
// 3. Set the Generator component of genContext to generator.
|
|
genContext.Generator = generator;
|
|
// 4. Set the code evaluation state of genContext such that when evaluation is resumed
|
|
// for that execution context the following steps will be performed:
|
|
genContext.codeEvaluationState = (function* resumer() {
|
|
// a. Let result be the result of evaluating generatorBody.
|
|
const result = EnsureCompletion(yield* Evaluate(generatorBody));
|
|
// b. Assert: If we return here, the generator either threw an exception or
|
|
// performed either an implicit or explicit return.
|
|
// c. 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.
|
|
surroundingAgent.executionContextStack.pop(genContext);
|
|
// d. Set generator.[[GeneratorState]] to completed.
|
|
generator.GeneratorState = 'completed';
|
|
// e. Once a generator enters the completed state it never leaves it and its
|
|
// associated execution context is never resumed. Any execution state associated
|
|
// with generator can be discarded at this point.
|
|
genContext.codeEvaluationState = null;
|
|
// f. If result.[[Type]] is normal, let resultValue be undefined.
|
|
let resultValue;
|
|
if (result.Type === 'normal') {
|
|
resultValue = Value.undefined;
|
|
} else if (result.Type === 'return') {
|
|
// g. Else if result.[[Type]] is return, let resultValue be result.[[Value]].
|
|
resultValue = result.Value;
|
|
} else {
|
|
// i. Assert: result.[[Type]] is throw.
|
|
Assert(result.Type === 'throw');
|
|
// ii. Return Completion(result).
|
|
return Completion(result);
|
|
}
|
|
// i. Return CreateIterResultObject(resultValue, true).
|
|
return X(CreateIterResultObject(resultValue, Value.true));
|
|
}());
|
|
// 5. Set generator.[[GeneratorContext]] to genContext.
|
|
generator.GeneratorContext = genContext;
|
|
// 6. Set generator.[[GeneratorState]] to suspendedStart.
|
|
generator.GeneratorState = 'suspendedStart';
|
|
// 7. Return NormalCompletion(undefined).
|
|
return NormalCompletion(Value.undefined);
|
|
}
|
|
|
|
// #sec-generatorvalidate
|
|
export function GeneratorValidate(generator) {
|
|
// 1. Perform ? RequireInternalSlot(generator, [[GeneratorState]]).
|
|
Q(RequireInternalSlot(generator, 'GeneratorState'));
|
|
// 2. Assert: generator also has a [[GeneratorContext]] internal slot.
|
|
Assert('GeneratorContext' in generator);
|
|
// 3. Let state be generator.[[GeneratorState]].
|
|
const state = generator.GeneratorState;
|
|
// 4. If state is executing, throw a TypeError exception.
|
|
if (state === 'executing') {
|
|
return surroundingAgent.Throw('TypeError', 'GeneratorRunning');
|
|
}
|
|
// 5. Return state.
|
|
return state;
|
|
}
|
|
|
|
// #sec-generatorresume
|
|
export function GeneratorResume(generator, value) {
|
|
// 1. Let state be ? GeneratorValidate(generator).
|
|
const state = Q(GeneratorValidate(generator));
|
|
// 2. If state is completed, return CreateIterResultObject(undefined, true).
|
|
if (state === 'completed') {
|
|
return X(CreateIterResultObject(Value.undefined, Value.true));
|
|
}
|
|
// 3. Assert: state is either suspendedStart or suspendedYield.
|
|
Assert(state === 'suspendedStart' || state === 'suspendedYield');
|
|
// 4. Let genContext be generator.[[GeneratorContext]].
|
|
const genContext = generator.GeneratorContext;
|
|
// 5. Let methodContext be the running execution context.
|
|
// 6. Suspend methodContext.
|
|
const methodContext = surroundingAgent.runningExecutionContext;
|
|
// 7. Set generator.[[GeneratorState]] to executing.
|
|
generator.GeneratorState = 'executing';
|
|
// 8. Push genContext onto the execution context stack.
|
|
surroundingAgent.executionContextStack.push(genContext);
|
|
// 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as
|
|
// the result of the operation that suspended it. Let result be the value returned by
|
|
// the resumed computation.
|
|
const result = EnsureCompletion(resume(genContext, NormalCompletion(value)));
|
|
// 10. Assert: When we return here, genContext has already been removed from the execution
|
|
// context stack and methodContext is the currently running execution context.
|
|
Assert(surroundingAgent.runningExecutionContext === methodContext);
|
|
// 11. Return Completion(result).
|
|
return Completion(result);
|
|
}
|
|
|
|
// #sec-generatorresumeabrupt
|
|
export function GeneratorResumeAbrupt(generator, abruptCompletion) {
|
|
// 1. Let state be ? GeneratorValidate(generator).
|
|
let state = Q(GeneratorValidate(generator));
|
|
// 2. If state is suspendedStart, then
|
|
if (state === 'suspendedStart') {
|
|
// a. Set generator.[[GeneratorState]] to completed.
|
|
generator.GeneratorState = 'completed';
|
|
// b. Once a generator enters the completed state it never leaves it and its
|
|
// associated execution context is never resumed. Any execution state associate
|
|
// with generator can be discarded at this point.
|
|
generator.GeneratorContext = null;
|
|
// c. Set state to completed.
|
|
state = 'completed';
|
|
}
|
|
// 3. If state is completed, then
|
|
if (state === 'completed') {
|
|
// a. If abruptCompletion.[[Type]] is return, then
|
|
if (abruptCompletion.Type === 'return') {
|
|
// i. Return CreateIterResultObject(abruptCompletion.[[Value]], true).
|
|
return X(CreateIterResultObject(abruptCompletion.Value, Value.true));
|
|
}
|
|
// b. Return Completion(abruptCompletion).
|
|
return Completion(abruptCompletion);
|
|
}
|
|
// 4. Assert: state is suspendedYield.
|
|
Assert(state === 'suspendedYield');
|
|
// 5. Let genContext be generator.[[GeneratorContext]].
|
|
const genContext = generator.GeneratorContext;
|
|
// 6. Let methodContext be the running execution context.
|
|
// 7. Suspend methodContext.
|
|
const methodContext = surroundingAgent.runningExecutionContext;
|
|
// 8. Set generator.[[GeneratorState]] to executing.
|
|
generator.GeneratorState = 'executing';
|
|
// 9. Push genContext onto the execution context stack.
|
|
surroundingAgent.executionContextStack.push(genContext);
|
|
// 10. Resume the suspended evaluation of genContext using abruptCompletion as the
|
|
// result of the operation that suspended it. Let result be the completion record
|
|
// returned by the resumed computation.
|
|
const result = EnsureCompletion(resume(genContext, abruptCompletion));
|
|
// 11. Assert: When we return here, genContext has already been removed from the
|
|
// execution context stack and methodContext is the currently running execution context.
|
|
Assert(surroundingAgent.runningExecutionContext === methodContext);
|
|
// 12. Return Completion(result).
|
|
return Completion(result);
|
|
}
|
|
|
|
// #sec-getgeneratorkind
|
|
export function GetGeneratorKind() {
|
|
// 1. Let genContext be the running execution context.
|
|
const genContext = surroundingAgent.runningExecutionContext;
|
|
// 2. If genContext does not have a Generator component, return non-generator.
|
|
if (!genContext.Generator) {
|
|
return 'non-generator';
|
|
}
|
|
// 3. Let generator be the Generator component of genContext.
|
|
const generator = genContext.Generator;
|
|
// 4. If generator has an [[AsyncGeneratorState]] internal slot, return async.
|
|
if ('AsyncGeneratorState' in generator) {
|
|
return 'async';
|
|
}
|
|
// 5. Else, return sync.
|
|
return 'sync';
|
|
}
|
|
|
|
// #sec-generatoryield
|
|
export function* GeneratorYield(iterNextObj) {
|
|
// 1. Assert: iterNextObj is an Object that implements the IteratorResult interface.
|
|
// 2. Let genContext be the running execution context.
|
|
const genContext = surroundingAgent.runningExecutionContext;
|
|
// 3. Assert: genContext is the execution context of a generator.
|
|
Assert(genContext.Generator !== undefined);
|
|
// 4. Let generator be the value of the Generator component of genContext.
|
|
const generator = genContext.Generator;
|
|
// 5. Assert: GetGeneratorKind is sync.
|
|
Assert(GetGeneratorKind() === 'sync');
|
|
// 6. Set generator.GeneratorState to suspendedYield.
|
|
generator.GeneratorState = 'suspendedYield';
|
|
// 7. Remove genContext from the execution context stack.
|
|
surroundingAgent.executionContextStack.pop(genContext);
|
|
// 8. Set the code evaluation state of genContext such that when evaluation is resumed with
|
|
// a Completion resumptionValue the following steps will be performed:
|
|
// a. Return resumptionValue
|
|
const resumptionValue = yield NormalCompletion(iterNextObj);
|
|
// 9. Return NormalCompletion(iterNextObj).
|
|
return resumptionValue;
|
|
// 10. NOTE: this returns to the evaluation of the operation that had most previously resumed evaluation of genContext.
|
|
}
|