Files
ask262/engine262/src/runtime-semantics/IteratorBindingInitialization.mts
T

213 lines
9.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Value } from '../value.mts';
import {
NormalCompletion,
Q, X,
} from '../completion.mts';
import { Evaluate, type PlainEvaluator } from '../evaluator.mts';
import {
StringValue,
IsAnonymousFunctionDefinition,
} from '../static-semantics/all.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { __ts_cast__ } from '../helpers.mts';
import { NamedEvaluation, BindingInitialization } from './all.mts';
import {
Assert,
GetValue,
InitializeReferencedBinding,
IteratorStep,
PutValue,
ResolveBinding,
ArrayCreate,
CreateDataPropertyOrThrow,
ToString,
F,
type IteratorRecord,
IteratorStepValue,
UndefinedValue, type EnvironmentRecord, type FunctionDeclaration,
} from '#self';
/** https://tc39.es/ecma262/#sec-function-definitions-runtime-semantics-iteratorbindinginitialization */
// FormalParameters :
// [empty]
// FormalParameterList `,` FunctionRestParameter
export function* IteratorBindingInitialization_FormalParameters(FormalParameters: ParseNode.FormalParameters, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
if (FormalParameters.length === 0) {
// 1. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
for (const FormalParameter of FormalParameters.slice(0, -1)) {
Q(yield* IteratorBindingInitialization_FormalParameter(FormalParameter, iteratorRecord, environment));
}
const last = FormalParameters[FormalParameters.length - 1];
if (last.type === 'BindingRestElement') {
return yield* IteratorBindingInitialization_FunctionRestParameter(last, iteratorRecord, environment);
}
return yield* IteratorBindingInitialization_FormalParameter(last, iteratorRecord, environment);
}
// FormalParameter : BindingElement
function IteratorBindingInitialization_FormalParameter(BindingElement: ParseNode.FormalParametersElement, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
// TODO
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return IteratorBindingInitialization_BindingElement(BindingElement as any, iteratorRecord, environment);
}
// FunctionRestParameter : BindingRestElement
function IteratorBindingInitialization_FunctionRestParameter(FunctionRestParameter: ParseNode.FunctionRestParameter, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
return IteratorBindingInitialization_BindingRestElement(FunctionRestParameter, iteratorRecord, environment);
}
// BindingElement :
// SingleNameBinding
// BindingPattern
function IteratorBindingInitialization_BindingElement(BindingElement: ParseNode.BindingElement, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
if ('BindingPattern' in BindingElement) {
return IteratorBindingInitialization_BindingPattern(BindingElement, iteratorRecord, environment);
}
return IteratorBindingInitialization_SingleNameBinding(BindingElement, iteratorRecord, environment);
}
// SingleNameBinding : BindingIdentifier Initializer?
function* IteratorBindingInitialization_SingleNameBinding({ BindingIdentifier, Initializer }: ParseNode.SingleNameBinding, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue): PlainEvaluator {
// 1. Let bindingId be StringValue of BindingIdentifier.
const bindingId = StringValue(BindingIdentifier);
// 2. Let lhs be ? ResolveBinding(bindingId, environment).
const lhs = Q(yield* ResolveBinding(bindingId, environment, BindingIdentifier.strict));
let v: Value = Value.undefined;
// 3. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.Done === Value.false) {
// a. Let next be ? IteratorStepValue(iteratorRecord).
const next = Q(yield* IteratorStepValue(iteratorRecord));
// d. If next is not DONE,
if (next !== 'done') {
v = next;
}
}
// 5. If Initializer is present and v is undefined, then
if (Initializer && v === Value.undefined) {
if (IsAnonymousFunctionDefinition(Initializer)) {
v = Q(yield* NamedEvaluation(Initializer as FunctionDeclaration, bindingId));
} else {
const defaultValue = Q(yield* Evaluate(Initializer));
v = Q(yield* GetValue(defaultValue));
}
}
// 6. If environment is undefined, return ? PutValue(lhs, v).
if (environment === Value.undefined) {
return Q(yield* PutValue(lhs, v));
}
// 7. Return InitializeReferencedBinding(lhs, v).
return yield* InitializeReferencedBinding(lhs, X(v));
}
// BindingRestElement :
// `...` BindingIdentifier
// `...` BindingPattern
function* IteratorBindingInitialization_BindingRestElement({ BindingIdentifier, BindingPattern }: ParseNode.BindingRestElement, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
if (BindingIdentifier) {
// 1. Let lhs be ? ResolveBinding(StringValue of BindingIdentifier, environment).
const lhs = Q(yield* ResolveBinding(StringValue(BindingIdentifier), environment, BindingIdentifier.strict));
// 2. Let A be ! ArrayCreate(0).
const A = X(ArrayCreate(0));
// 3. Let n be 0.
let n = 0;
// 4. Repeat,
while (true) {
let next: 'done' | Value = 'done';
// a. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.Done === Value.false) {
// i. Let next be ? IteratorStepValue(iteratorRecord).
next = Q(yield* IteratorStepValue(iteratorRecord));
}
if (next === 'done') {
// i. If environment is undefined, return ? PutValue(lhs, A).
if (environment === Value.undefined) {
return Q(yield* PutValue(lhs, A));
}
// ii. Return InitializeReferencedBinding(lhs, A).
return yield* InitializeReferencedBinding(lhs, A);
}
// f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), next).
X(CreateDataPropertyOrThrow(A, X(ToString(F(n))), next));
// g. Set n to n + 1.
n += 1;
}
} else {
// 1. Let A be ! ArrayCreate(0).
const A = X(ArrayCreate(0));
// 2. Let n be 0.
let n = 0;
// 3. Repeat,
while (true) {
let next: 'done' | Value = 'done';
// a. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.Done === Value.false) {
// i. Let next be ? IteratorStepValue(iteratorRecord).
next = Q(yield* IteratorStepValue(iteratorRecord));
}
// b. If next is done, then
if (next === 'done') {
// i. Return the result of performing BindingInitialization of BindingPattern with A and environment as the arguments.
return yield* BindingInitialization(BindingPattern!, A, environment);
}
// f. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), next).
X(CreateDataPropertyOrThrow(A, X(ToString(F(n))), Q(next)));
// g. Set n to n + 1.
n += 1;
}
}
}
function* IteratorBindingInitialization_BindingPattern({ BindingPattern, Initializer }: ParseNode.BindingElement, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue) {
let v: Value = Value.undefined;
// 1. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.Done === Value.false) {
// a. Let next be ? IteratorStepValue(iteratorRecord).
const next = Q(yield* IteratorStepValue(iteratorRecord));
if (next !== 'done') {
v = next;
}
}
// 3. If Initializer is present and v is undefined, then
if (Initializer && v instanceof UndefinedValue) {
// a. Let defaultValue be the result of evaluating Initializer.
const defaultValue = Q(yield* Evaluate(Initializer));
// b. Set v to ? GetValue(defaultValue).
v = Q(yield* GetValue(defaultValue));
}
// 4. Return the result of performing BindingInitialization of BindingPattern with v and environment as the arguments.
return yield* BindingInitialization(BindingPattern, X(v), environment);
}
function* IteratorDestructuringAssignmentEvaluation(node: ParseNode.Elision, iteratorRecord: IteratorRecord): PlainEvaluator {
Assert(node.type === 'Elision');
// 1. If iteratorRecord.[[Done]] is false, then
if (iteratorRecord.Done === Value.false) {
// a. Perform ? IteratorStep(iteratorRecord).
Q(yield* IteratorStep(iteratorRecord));
}
// 2. Return NormalCompletion(empty).
return NormalCompletion(undefined);
}
export function* IteratorBindingInitialization_ArrayBindingPattern({ BindingElementList, BindingRestElement }: ParseNode.ArrayBindingPattern, iteratorRecord: IteratorRecord, environment: EnvironmentRecord | UndefinedValue): PlainEvaluator {
for (const BindingElement of BindingElementList) {
if (BindingElement.type === 'Elision') {
Q(yield* IteratorDestructuringAssignmentEvaluation(BindingElement, iteratorRecord));
} else {
// TODO
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Q(yield* IteratorBindingInitialization_BindingElement(BindingElement as any, iteratorRecord, environment));
}
}
if (BindingRestElement) {
return Q(yield* IteratorBindingInitialization_BindingRestElement(BindingRestElement, iteratorRecord, environment));
}
return NormalCompletion(undefined);
}