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

129 lines
6.4 KiB
TypeScript

import { ReferenceRecord, Value } from '../value.mts';
import { Evaluate, type ExpressionEvaluator } from '../evaluator.mts';
import { Q, X } from '../completion.mts';
import { IsInTailPosition, StringValue } from '../static-semantics/all.mts';
import { OutOfRange } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import {
EvaluateCall,
EvaluatePropertyAccessWithExpressionKey,
EvaluatePropertyAccessWithIdentifierKey,
} from './all.mts';
import { GetValue, MakePrivateReference } from '#self';
/** https://tc39.es/ecma262/#sec-optional-chaining-evaluation */
// OptionalExpression :
// MemberExpression OptionalChain
// CallExpression OptionalChain
// OptionalExpression OptionalChain
export function* Evaluate_OptionalExpression({ MemberExpression, OptionalChain }: ParseNode.OptionalExpression) {
// 1. Let baseReference be the result of evaluating MemberExpression.
const baseReference = Q(yield* Evaluate(MemberExpression));
// 2. Let baseValue be ? GetValue(baseReference).
const baseValue = Q(yield* GetValue(baseReference));
// 3. If baseValue is undefined or null, then
if (baseValue === Value.undefined || baseValue === Value.null) {
// a. Return undefined.
return Value.undefined;
}
// 4. Return the result of performing ChainEvaluation of OptionalChain with arguments baseValue and baseReference.
return yield* ChainEvaluation(OptionalChain, baseValue, X(baseReference));
}
/** https://tc39.es/ecma262/#sec-optional-chaining-chain-evaluation */
// OptionalChain :
// `?.` Arguments
// `?.` `[` Expression `]`
// `?.` IdentifierName
// `?.` PrivateIdentifier
// OptionalChain Arguments
// OptionalChain `[` Expression `]`
// OptionalChain `.` IdentifierName
// OptionalChain `.` PrivateIdentifier
function* ChainEvaluation(node: ParseNode.OptionalChain, baseValue: Value, baseReference: Value | ReferenceRecord): ExpressionEvaluator {
const {
OptionalChain,
Arguments,
Expression,
IdentifierName,
PrivateIdentifier,
} = node;
if (Arguments) {
if (OptionalChain) {
// 1. Let optionalChain be OptionalChain.
const optionalChain = OptionalChain;
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
// 3. Let newValue be ? GetValue(newReference).
const newValue = Q(yield* GetValue(newReference));
// 4. Let thisChain be this OptionalChain.
const thisChain = node;
// 5. Let tailCall be IsInTailPosition(thisChain).
const tailCall = IsInTailPosition(thisChain);
// 6. Return ? EvaluateCall(newValue, newReference, Arguments, tailCall).
return Q(yield* EvaluateCall(newValue, newReference, Arguments, tailCall));
}
// 1. Let thisChain be this OptionalChain.
const thisChain = node;
// 2. Let tailCall be IsInTailPosition(thisChain).
const tailCall = IsInTailPosition(thisChain);
// 3. Return ? EvaluateCall(baseValue, baseReference, Arguments, tailCall).
return Q(yield* EvaluateCall(baseValue, baseReference, Arguments, tailCall));
}
if (Expression) {
if (OptionalChain) {
// 1. Let optionalChain be OptionalChain.
const optionalChain = OptionalChain;
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
// 3. Let newValue be ? GetValue(newReference).
const newValue = Q(yield* GetValue(newReference));
// 4. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
const strict = node.strict;
// 5. Return ? EvaluatePropertyAccessWithExpressionKey(newValue, Expression, strict).
return Q(yield* EvaluatePropertyAccessWithExpressionKey(newValue, Expression, strict));
}
// 1. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
const strict = node.strict;
// 2. Return ? EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict).
return Q(yield* EvaluatePropertyAccessWithExpressionKey(baseValue, Expression, strict));
}
if (IdentifierName) {
if (OptionalChain) {
// 1. Let optionalChain be OptionalChain.
const optionalChain = OptionalChain;
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
// 3. Let newValue be ? GetValue(newReference).
const newValue = Q(yield* GetValue(newReference));
// 4. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
const strict = node.strict;
// 5. Return ! EvaluatePropertyAccessWithIdentifierKey(newValue, IdentifierName, strict).
return X(EvaluatePropertyAccessWithIdentifierKey(newValue, IdentifierName, strict));
}
// 1. If the code matched by this OptionalChain is strict mode code, let strict be true; else let strict be false.
const strict = node.strict;
// 2. Return ! EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict).
return X(EvaluatePropertyAccessWithIdentifierKey(baseValue, IdentifierName, strict));
}
if (PrivateIdentifier) {
if (OptionalChain) {
// 1. Let optionalChain be OptionalChain.
const optionalChain = OptionalChain;
// 2. Let newReference be ? ChainEvaluation of optionalChain with arguments baseValue and baseReference.
const newReference = Q(yield* ChainEvaluation(optionalChain, baseValue, baseReference));
// 3. Let newValue be ? GetValue(newReference).
const newValue = Q(yield* GetValue(newReference));
// 4. Let fieldNameString be the StringValue of PrivateIdentifier.
const fieldNameString = StringValue(PrivateIdentifier);
// 5. Return ! MakePrivateReference(nv, fieldNameString).
return X(MakePrivateReference(newValue, fieldNameString));
}
// 1. Let fieldNameString be the StringValue of PrivateIdentifier.
const fieldNameString = StringValue(PrivateIdentifier);
// 2. Return ! MakePrivateReference(bv, fieldNameString).
return X(MakePrivateReference(baseValue, fieldNameString));
}
throw new OutOfRange('ChainEvaluation', node);
}