Merge commit '6e92d2345e8231a44556ce7d416451f5f3e6c398' as 'engine262'

This commit is contained in:
2026-03-27 10:07:29 +05:30
433 changed files with 90478 additions and 0 deletions
@@ -0,0 +1,7 @@
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-bodytext */
// RegularExpressionLiteral :: `/` RegularExpressionBody `/` RegularExpressionFlags
export function BodyText(RegularExpressionLiteral: ParseNode.RegularExpressionLiteral) {
return RegularExpressionLiteral.RegularExpressionBody;
}
@@ -0,0 +1,101 @@
import { OutOfRange, isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { JSStringValue, Value } from '../value.mts';
import { StringValue } from './all.mts';
export function BoundNames(node: ParseNode | readonly ParseNode[]): JSStringValue[] {
if (isArray(node)) {
const names = [];
for (const item of node) {
names.push(...BoundNames(item));
}
return names;
}
switch (node.type) {
case 'BindingIdentifier':
return [StringValue(node)];
case 'LexicalDeclaration':
return BoundNames(node.BindingList);
case 'LexicalBinding':
if (node.BindingIdentifier) {
return BoundNames(node.BindingIdentifier);
}
return BoundNames(node.BindingPattern!);
case 'VariableStatement':
return BoundNames(node.VariableDeclarationList);
case 'VariableDeclaration':
if (node.BindingIdentifier) {
return BoundNames(node.BindingIdentifier);
}
return BoundNames(node.BindingPattern!);
case 'ForDeclaration':
return BoundNames(node.ForBinding);
case 'ForBinding':
if (node.BindingIdentifier) {
return BoundNames(node.BindingIdentifier);
}
return BoundNames(node.BindingPattern!);
case 'FunctionDeclaration':
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
case 'ClassDeclaration':
if (node.BindingIdentifier) {
return BoundNames(node.BindingIdentifier);
}
return [Value('*default*')];
case 'ImportSpecifier':
return BoundNames(node.ImportedBinding);
case 'ExportDeclaration':
if (node.FromClause || node.NamedExports) {
return [];
}
if (node.VariableStatement) {
return BoundNames(node.VariableStatement);
}
if (node.Declaration) {
return BoundNames(node.Declaration);
}
if (node.HoistableDeclaration) {
const declarationNames = BoundNames(node.HoistableDeclaration);
return declarationNames;
}
if (node.ClassDeclaration) {
const declarationNames = BoundNames(node.ClassDeclaration);
return declarationNames;
}
if (node.AssignmentExpression) {
return [Value('*default*')];
}
throw new OutOfRange('BoundNames', node);
case 'SingleNameBinding':
return BoundNames(node.BindingIdentifier);
case 'BindingRestElement':
if (node.BindingIdentifier) {
return BoundNames(node.BindingIdentifier);
}
return BoundNames(node.BindingPattern!);
case 'BindingRestProperty':
return BoundNames(node.BindingIdentifier);
case 'BindingElement':
return BoundNames(node.BindingPattern);
case 'BindingProperty':
return BoundNames(node.BindingElement);
case 'ObjectBindingPattern': {
const names = BoundNames(node.BindingPropertyList);
if (node.BindingRestProperty) {
names.push(...BoundNames(node.BindingRestProperty));
}
return names;
}
case 'ArrayBindingPattern': {
const names = BoundNames(node.BindingElementList);
if (node.BindingRestElement) {
names.push(...BoundNames(node.BindingRestElement));
}
return names;
}
default:
return [];
}
}
@@ -0,0 +1,112 @@
import { OutOfRange, unreachable } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { UTF16SurrogatePairToCodePoint } from './all.mts';
import { Unicode, type CodePoint } from '#self';
export type CharacterValueAcceptNode =
| ParseNode.RegExp.CharacterEscape
| ParseNode.RegExp.RegExpUnicodeEscapeSequence
| ParseNode.RegExp.ClassAtom
| ParseNode.RegExp.ClassEscape
| ParseNode.RegExp.ClassSetCharacter;
/** https://tc39.es/ecma262/#sec-patterns-static-semantics-character-value */
export function CharacterValue(node: CharacterValueAcceptNode): CodePoint {
switch (node.type) {
case 'CharacterEscape':
switch (node.production) {
case 'ControlEscape':
switch (node.ControlEscape) {
case 't':
return 0x0009 as CodePoint;
case 'n':
return 0x000A as CodePoint;
case 'v':
return 0x000B as CodePoint;
case 'f':
return 0x000C as CodePoint;
case 'r':
return 0x000D as CodePoint;
default:
unreachable(node.ControlEscape);
}
case 'AsciiLetter': {
// 1. Let ch be the code point matched by ControlLetter.
const ch = node.AsciiLetter;
// 2. Let i be ch's code point value.
const i = ch.codePointAt(0)!;
// 3. Return the remainder of dividing i by 32.
return i % 32 as CodePoint;
}
case 'HexEscapeSequence':
// 1. Return the numeric value of the code unit that is the SV of HexEscapeSequence.
return Number.parseInt(`${node.HexEscapeSequence.HexDigit_a}${node.HexEscapeSequence.HexDigit_b}`, 16) as CodePoint;
case 'RegExpUnicodeEscapeSequence':
return CharacterValue(node.RegExpUnicodeEscapeSequence);
case '0':
// 1. Return the code point value of U+0000 (NULL).
return 0x0000 as CodePoint;
case 'IdentityEscape': {
// 1. Let ch be the code point matched by IdentityEscape.
const ch = node.IdentityEscape.codePointAt(0)!;
// 2. Return the code point value of ch.
return ch as CodePoint;
}
default:
unreachable(node);
}
case 'RegExpUnicodeEscapeSequence':
switch (true) {
case 'Hex4Digits' in node:
return node.Hex4Digits as CodePoint;
case 'CodePoint' in node:
return node.CodePoint as CodePoint;
case 'HexTrailSurrogate' in node:
return UTF16SurrogatePairToCodePoint(node.HexLeadSurrogate!, node.HexTrailSurrogate!);
case 'HexLeadSurrogate' in node:
return node.HexLeadSurrogate as CodePoint;
default:
throw new OutOfRange('CharacterValue', node);
}
case 'ClassAtom':
switch (node.production) {
case '-':
// 1. Return the code point value of U+002D (HYPHEN-MINUS).
return 0x002D as CodePoint;
case 'SourceCharacter': {
// 1. Let ch be the code point matched by SourceCharacter.
const ch = node.SourceCharacter.codePointAt(0)!;
// 2. Return ch.
return ch as CodePoint;
}
case 'ClassEscape':
return CharacterValue(node.ClassEscape);
default:
unreachable(node);
}
case 'ClassEscape':
switch (node.production) {
case 'b':
// 1. Return the code point value of U+0008 (BACKSPACE).
return 0x0008 as CodePoint;
case '-':
// 1. Return the code point value of U+002D (HYPHEN-MINUS).
return 0x002D as CodePoint;
case 'CharacterEscape':
return CharacterValue(node.CharacterEscape);
case 'CharacterClassEscape':
throw new OutOfRange('CharacterValue', node);
default:
unreachable(node);
}
case 'ClassSetCharacter': {
if (node.production === 'CharacterEscape') {
return CharacterValue(node.CharacterEscape);
} else {
return Unicode.toCodePoint(node.UnicodeCharacter);
}
}
default:
unreachable(node);
}
}
@@ -0,0 +1,53 @@
import { X } from '../completion.mts';
import { UTF16SurrogatePairToCodePoint } from './all.mts';
import { Assert } from '#self';
import { isLeadingSurrogate, isTrailingSurrogate, type CodePoint } from '#self';
/** https://tc39.es/ecma262/#sec-codepointat */
export function CodePointAt(string: string, position: number) {
// 1 .Let size be the length of string.
const size = string.length;
// 2. Assert: position ≥ 0 and position < size.
Assert(position >= 0 && position < size);
// 3. Let first be the code unit at index position within string.
const first = string.charCodeAt(position);
// 4. Let cp be the code point whose numeric value is that of first.
let cp = first;
// 5. If first is not a leading surrogate or trailing surrogate, then
if (!isLeadingSurrogate(first) && !isTrailingSurrogate(first)) {
// a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: false }.
return {
CodePoint: cp as CodePoint,
CodeUnitCount: 1,
IsUnpairedSurrogate: false,
};
}
// 6. If first is a trailing surrogate or position + 1 = size, then
if (isTrailingSurrogate(first) || position + 1 === size) {
// a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
return {
CodePoint: cp as CodePoint,
CodeUnitCount: 1,
IsUnpairedSurrogate: true,
};
}
// 7. Let second be the code unit at index position + 1 within string.
const second = string.charCodeAt(position + 1);
// 8. If seconds is not a trailing surrogate, then
if (!isTrailingSurrogate(second)) {
// a. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 1, [[IsUnpairedSurrogate]]: true }.
return {
CodePoint: cp as CodePoint,
CodeUnitCount: 1,
IsUnpairedSurrogate: true,
};
}
// 9. Set cp to ! UTF16SurrogatePairToCodePoint(first, second).
cp = X(UTF16SurrogatePairToCodePoint(first, second));
// 10. Return the Record { [[CodePoint]]: cp, [[CodeUnitCount]]: 2, [[IsUnpairedSurrogate]]: false }.
return {
CodePoint: cp as CodePoint,
CodeUnitCount: 2,
IsUnpairedSurrogate: false,
};
}
@@ -0,0 +1,15 @@
import { UTF16EncodeCodePoint } from './all.mts';
import type { CodePoint } from '#self';
/** https://tc39.es/ecma262/#sec-codepointstostring */
export function CodePointsToString(text: string) {
// 1. Let result be the empty String.
let result = '';
// 2. For each code point cp in text, do
for (const cp of text) {
// a. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp).
result += UTF16EncodeCodePoint(cp.codePointAt(0)! as CodePoint);
}
// 3. Return result.
return result;
}
@@ -0,0 +1,10 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import { PropName } from './all.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-constructormethod */
// ClassElementList :
// ClassElement
// ClassElementList ClassElement
export function ConstructorMethod(ClassElementList: ParseNode.ClassElementList): ParseNode.MethodDefinition | undefined {
return ClassElementList.find((ClassElement) => ClassElement.static === false && PropName(ClassElement) === 'constructor') as ParseNode.MethodDefinition;
}
@@ -0,0 +1,33 @@
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-containsarguments */
export function ContainsArguments(node: ParseNode): ParseNode.IdentifierReference | null {
switch (node.type) {
case 'IdentifierReference':
if (node.name === 'arguments') {
return node;
}
return null;
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'MethodDefinition':
case 'GeneratorMethod':
case 'GeneratorDeclaration':
case 'GeneratorExpression':
case 'AsyncMethod':
case 'AsyncFunctionDeclaration':
case 'AsyncFunctionExpression':
return null;
default:
for (const value of Object.values(node)) {
// TODO(ts): This function does not accept a ParseNode[], when isArray(value), ContainsArguments should never return a result?
if ((value?.type || Array.isArray(value))) {
const maybe = ContainsArguments(value);
if (maybe) {
return maybe;
}
}
}
return null;
}
}
@@ -0,0 +1,59 @@
import { OutOfRange, isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
export function ContainsExpression(node: ParseNode | readonly ParseNode[]): boolean {
if (isArray(node)) {
for (const n of node) {
if (ContainsExpression(n)) {
return true;
}
}
return false;
}
switch (node.type) {
case 'SingleNameBinding':
return !!node.Initializer;
case 'BindingElement':
if (ContainsExpression(node.BindingPattern)) {
return true;
}
return !!node.Initializer;
case 'ObjectBindingPattern':
if (ContainsExpression(node.BindingPropertyList)) {
return true;
}
if (node.BindingRestProperty) {
return ContainsExpression(node.BindingRestProperty);
}
return false;
case 'BindingProperty':
if (node.PropertyName && 'ComputedPropertyName' in node.PropertyName && node.PropertyName.ComputedPropertyName) {
return true;
}
return ContainsExpression(node.BindingElement);
case 'BindingRestProperty':
if (node.BindingIdentifier) {
return false;
}
// TODO(ts): BindingRestProperty and BindingElement is different. Is there missing a case?
// @ts-expect-error
return ContainsExpression((node as ParseNode.BindingElement).BindingPattern);
case 'ArrayBindingPattern':
if (ContainsExpression(node.BindingElementList)) {
return true;
}
if (node.BindingRestElement) {
return ContainsExpression(node.BindingRestElement);
}
return false;
case 'BindingRestElement':
if (node.BindingIdentifier) {
return false;
}
return ContainsExpression(node.BindingPattern!);
case 'Elision':
return false;
default:
throw new OutOfRange('ContainsExpression', node);
}
}
@@ -0,0 +1,5 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function DeclarationPart<T extends ParseNode>(node: T): T {
return node;
}
@@ -0,0 +1,26 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import { HasInitializer } from './all.mts';
export function ExpectedArgumentCount(FormalParameterList: ParseNode.FormalParameters) {
if (FormalParameterList.length === 0) {
return 0;
}
let count = 0;
for (const FormalParameter of FormalParameterList.slice(0, -1)) {
const BindingElement = FormalParameter;
if (HasInitializer(BindingElement)) {
return count;
}
count += 1;
}
const last = FormalParameterList[FormalParameterList.length - 1];
if (last.type === 'BindingRestElement') {
return count;
}
if (HasInitializer(last)) {
return count;
}
return count + 1;
}
@@ -0,0 +1,129 @@
import { JSStringValue, NullValue, Value } from '../value.mts';
import { OutOfRange, isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import {
BoundNames, ModuleRequests, ExportEntriesForModule, type ModuleRequestRecord,
} from './all.mts';
export function ExportEntries(node: ParseNode | readonly ParseNode[]): ExportEntry[] {
if (isArray(node)) {
const entries: ExportEntry[] = [];
node.forEach((n) => {
entries.push(...ExportEntries(n));
});
return entries;
}
switch (node.type) {
case 'Module':
if (!node.ModuleBody) {
return [];
}
return ExportEntries(node.ModuleBody);
case 'ModuleBody':
return ExportEntries(node.ModuleItemList);
case 'ExportDeclaration':
switch (true) {
case !!node.ExportFromClause && !!node.FromClause: {
// `export` ExportFromClause FromClause WithClause? `;`
// 1. Let module be the sole element of ModuleRequests of FromClause.
const module = ModuleRequests(node)[0];
// 2. Return ExportEntriesForModule(ExportFromClause, module).
return ExportEntriesForModule(node.ExportFromClause, module);
}
case !!node.NamedExports: {
// `export` NamedExports `;`
// 1. Return ExportEntriesForModule(NamedExports, null).
return ExportEntriesForModule(node.NamedExports, Value.null);
}
case !!node.VariableStatement: {
// `export` VariableStatement
// 1. Let entries be a new empty List.
const entries = [];
// 2. Let names be the BoundNames of VariableStatement.
const names = BoundNames(node.VariableStatement);
// 3. For each name in names, do
for (const name of names) {
// a. Append the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: name, [[ExportName]]: name } to entries.
entries.push({
ModuleRequest: Value.null,
ImportName: Value.null,
LocalName: name,
ExportName: name,
});
}
// 4. Return entries.
return entries;
}
case !!node.Declaration: {
// `export` Declaration
// 1. Let entries be a new empty List.
const entries: ExportEntry[] = [];
// 2. Let names be the BoundNames of Declaration.
const names = BoundNames(node.Declaration);
// 3. For each name in names, do
for (const name of names) {
// a. Append the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: name, [[ExportName]]: name } to entries.
entries.push({
ModuleRequest: Value.null,
ImportName: Value.null,
LocalName: name,
ExportName: name,
});
}
// 4. Return entries.
return entries;
}
case node.default && !!node.HoistableDeclaration: {
// `export` `default` HoistableDeclaration
// 1. Let names be BoundNames of HoistableDeclaration.
const names = BoundNames(node.HoistableDeclaration);
// 2. Let localName be the sole element of names.
const localName = names[0];
// 3. Return a new List containing the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default" }.
return [{
ModuleRequest: Value.null,
ImportName: Value.null,
LocalName: localName,
ExportName: Value('default'),
}];
}
case node.default && !!node.ClassDeclaration: {
// `export` `default` ClassDeclaration
// 1. Let names be BoundNames of ClassDeclaration.
const names = BoundNames(node.ClassDeclaration);
// 2. Let localName be the sole element of names.
const localName = names[0];
// 3. Return a new List containing the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default" }.
return [{
ModuleRequest: Value.null,
ImportName: Value.null,
LocalName: localName,
ExportName: Value('default'),
}];
}
case node.default && !!node.AssignmentExpression: {
// `export` `default` AssignmentExpression `;`
// 1. Let entry be the ExportEntry Record { [[ModuleRequest]]: null, [[ImportName]]: null, [[LocalName]]: "*default*", [[ExportName]]: "default" }.
const entry = {
ModuleRequest: Value.null,
ImportName: Value.null,
LocalName: Value('*default*'),
ExportName: Value('default'),
};
// 2. Return a new List containing entry.
return [entry];
}
default:
throw new OutOfRange('ExportEntries', node);
}
default:
return [];
}
}
export interface ExportEntry {
readonly ModuleRequest: ModuleRequestRecord | NullValue;
readonly ImportName: JSStringValue | NullValue | 'all' | 'all-but-default';
readonly LocalName: JSStringValue | NullValue;
readonly ExportName: JSStringValue | NullValue;
}
@@ -0,0 +1,63 @@
import { NullValue, Value } from '../value.mts';
import { OutOfRange, isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { StringValue, type ExportEntry, type ModuleRequestRecord } from './all.mts';
export function ExportEntriesForModule(node: ParseNode | readonly ParseNode[], module: ModuleRequestRecord | NullValue): ExportEntry[] {
if (isArray(node)) {
const specs: ExportEntry[] = [];
node.forEach((n) => {
specs.push(...ExportEntriesForModule(n, module));
});
return specs;
}
switch (node.type) {
case 'ExportFromClause':
if (node.ModuleExportName) {
// 1. Let exportName be the StringValue of ModuleExportName.
const exportName = StringValue(node.ModuleExportName);
// 2. Let entry be the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: ~all~, [[LocalName]]: null, [[ExportName]]: exportName }.
const entry: ExportEntry = {
ModuleRequest: module,
ImportName: 'all',
LocalName: Value.null,
ExportName: exportName,
};
// 3. Return a new List containing entry.
return [entry];
} else {
// 1. Let entry be the ExportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: ~all-but-default~, [[LocalName]]: null, [[ExportName]]: null }.
const entry: ExportEntry = {
ModuleRequest: module,
ImportName: 'all-but-default',
LocalName: Value.null,
ExportName: Value.null,
};
// 2. Return a new List containing entry.
return [entry];
}
case 'ExportSpecifier': {
const sourceName = StringValue(node.localName);
const exportName = StringValue(node.exportName);
let localName;
let importName;
if (module === Value.null) {
localName = sourceName;
importName = Value.null;
} else { // 4. Else,
localName = Value.null;
importName = sourceName;
}
return [{
ModuleRequest: module,
ImportName: importName,
LocalName: localName,
ExportName: exportName,
}];
}
case 'NamedExports':
return ExportEntriesForModule(node.ExportsList, module);
default:
throw new OutOfRange('ExportEntriesForModule', node);
}
}
@@ -0,0 +1,7 @@
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-flagtext */
// RegularExpressionLiteral :: `/` RegularExpressionBody `/` RegularExpressionFlags
export function FlagText(RegularExpressionLiteral: ParseNode.RegularExpressionLiteral) {
return RegularExpressionLiteral.RegularExpressionFlags;
}
@@ -0,0 +1,5 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function HasInitializer(node: ParseNode): node is ParseNode & { readonly Initializer: ParseNode.Initializer; } {
return 'Initializer' in node && !!node.Initializer;
}
@@ -0,0 +1,8 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function HasName(node: ParseNode): boolean {
if (node.type === 'ParenthesizedExpression') {
return HasName(node.Expression);
}
return 'BindingIdentifier' in node && !!node.BindingIdentifier;
}
@@ -0,0 +1,36 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { ImportEntriesForModule, ModuleRequests, type ModuleRequestRecord } from './all.mts';
export function ImportEntries(node: ParseNode): ImportEntry[] {
switch (node.type) {
case 'Module':
if (node.ModuleBody) {
return ImportEntries(node.ModuleBody);
}
return [];
case 'ModuleBody': {
const entries: ImportEntry[] = [];
for (const item of node.ModuleItemList) {
entries.push(...ImportEntries(item));
}
return entries;
}
case 'ImportDeclaration':
if (node.FromClause) {
// 1. Let module be the sole element of ModuleRequests of FromClause.
const module = ModuleRequests(node)[0];
// 2. Return ImportEntriesForModule of ImportClause with argument module.
return ImportEntriesForModule(node.ImportClause!, module);
}
return [];
default:
return [];
}
}
export interface ImportEntry {
readonly ModuleRequest: ModuleRequestRecord;
readonly ImportName: JSStringValue | 'namespace-object';
readonly LocalName: JSStringValue;
}
@@ -0,0 +1,97 @@
import { Value } from '../value.mts';
import { OutOfRange } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import {
BoundNames, StringValue, type ImportEntry, type ModuleRequestRecord,
} from './all.mts';
export function ImportEntriesForModule(node: ParseNode, module: ModuleRequestRecord): ImportEntry[] {
switch (node.type) {
case 'ImportClause':
switch (true) {
case !!node.ImportedDefaultBinding && !!node.NameSpaceImport: {
// 1. Let entries be ImportEntriesForModule of ImportedDefaultBinding with argument module.
const entries = ImportEntriesForModule(node.ImportedDefaultBinding, module);
// 2. Append to entries the elements of the ImportEntriesForModule of NameSpaceImport with argument module.
entries.push(...ImportEntriesForModule(node.NameSpaceImport, module));
// 3. Return entries.
return entries;
}
case !!node.ImportedDefaultBinding && !!node.NamedImports: {
// 1. Let entries be ImportEntriesForModule of ImportedDefaultBinding with argument module.
const entries = ImportEntriesForModule(node.ImportedDefaultBinding, module);
// 2. Append to entries the elements of the ImportEntriesForModule of NamedImports with argument module.
entries.push(...ImportEntriesForModule(node.NamedImports, module));
// 3. Return entries.
return entries;
}
case !!node.ImportedDefaultBinding:
return ImportEntriesForModule(node.ImportedDefaultBinding, module);
case !!node.NameSpaceImport:
return ImportEntriesForModule(node.NameSpaceImport, module);
case !!node.NamedImports:
return ImportEntriesForModule(node.NamedImports, module);
default:
throw new OutOfRange('ImportEntriesForModule', node);
}
case 'ImportedDefaultBinding': {
// 1. Let localName be the sole element of BoundNames of ImportedBinding.
const localName = BoundNames(node.ImportedBinding)[0];
// 2. Let defaultEntry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: "default", [[LocalName]]: localName }.
const defaultEntry: ImportEntry = {
ModuleRequest: module,
ImportName: Value('default'),
LocalName: localName,
};
// 3. Return a new List containing defaultEntry.
return [defaultEntry];
}
case 'NameSpaceImport': {
// 1. Let localName be the StringValue of ImportedBinding.
const localName = StringValue(node.ImportedBinding);
// 2. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: ~namespace-object~, [[LocalName]]: localName }.
const entry: ImportEntry = {
ModuleRequest: module,
ImportName: 'namespace-object',
LocalName: localName,
};
// 3. Return a new List containing entry.
return [entry];
}
case 'NamedImports': {
const specs: ImportEntry[] = [];
node.ImportsList.forEach((n) => {
specs.push(...ImportEntriesForModule(n, module));
});
return specs;
}
case 'ImportSpecifier':
if (node.ModuleExportName) {
// 1. Let importName be the StringValue of ModuleExportName.
const importName = StringValue(node.ModuleExportName);
// 2. Let localName be the StringValue of ImportedBinding.
const localName = StringValue(node.ImportedBinding);
// 3. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: importName, [[LocalName]]: localName }.
const entry: ImportEntry = {
ModuleRequest: module,
ImportName: importName,
LocalName: localName,
};
// 4. Return a new List containing entry.
return [entry];
} else {
// 1. Let localName be the sole element of BoundNames of ImportedBinding.
const localName = BoundNames(node.ImportedBinding)[0];
// 2. Let entry be the ImportEntry Record { [[ModuleRequest]]: module, [[ImportName]]: localName, [[LocalName]]: localName }.
const entry: ImportEntry = {
ModuleRequest: module,
ImportName: localName,
LocalName: localName,
};
// 3. Return a new List containing entry.
return [entry];
}
default:
throw new OutOfRange('ImportEntriesForModule', node);
}
}
@@ -0,0 +1,14 @@
import type { ImportEntry } from './ImportEntries.mts';
/** https://tc39.es/ecma262/#sec-importedlocalnames */
export function ImportedLocalNames(importEntries: readonly ImportEntry[]) {
// 1. Let localNames be a new empty List.
const localNames = [];
// 2. For each ImportEntry Record i in importEntries, do
for (const i of importEntries) {
// a. Append i.[[LocalName]] to localNames.
localNames.push(i.LocalName);
}
// 3. Return localNames.
return localNames;
}
@@ -0,0 +1,18 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import { IsFunctionDefinition, HasName } from './all.mts';
/** https://tc39.es/ecma262/#sec-isanonymousfunctiondefinition */
export function IsAnonymousFunctionDefinition(expr: ParseNode) {
// 1. If IsFunctionDefinition of expr is false, return false.
if (!IsFunctionDefinition(expr)) {
return false;
}
// 1. Let hasName be HasName of expr.
const hasName = HasName(expr);
// 1. If hasName is true, return false.
if (hasName) {
return false;
}
// 1. Return true.
return true;
}
@@ -0,0 +1,7 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function IsComputedPropertyKey(node: ParseNode.PropertyNameLike): node is ParseNode.PropertyName {
return node.type !== 'IdentifierName'
&& node.type !== 'StringLiteral'
&& node.type !== 'NumericLiteral';
}
@@ -0,0 +1,5 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function IsConstantDeclaration(node: ParseNode | ParseNode.LetOrConst) {
return node === 'const' || (typeof node === 'object' && 'LetOrConst' in node && node.LetOrConst === 'const');
}
@@ -0,0 +1,21 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export type DestructuringParseNode = ParseNode.ObjectBindingPattern | ParseNode.ArrayBindingPattern | ParseNode.ObjectLiteral | ParseNode.ArrayLiteral | ParseNode.ForDeclaration | ParseNode.ForBinding;
export function IsDestructuring(node: ParseNode): boolean {
switch (node.type) {
case 'ObjectBindingPattern':
case 'ArrayBindingPattern':
case 'ObjectLiteral':
case 'ArrayLiteral':
return true;
case 'ForDeclaration':
return IsDestructuring(node.ForBinding);
case 'ForBinding':
if (node.BindingIdentifier) {
return false;
}
return true;
default:
return false;
}
}
@@ -0,0 +1,15 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export type FunctionDeclaration = ParseNode.FunctionExpression | ParseNode.GeneratorExpression | ParseNode.AsyncFunctionExpression | ParseNode.AsyncGeneratorExpression | ParseNode.ClassExpression | ParseNode.ArrowFunction | ParseNode.AsyncArrowFunction | ParseNode.ParenthesizedExpression & { readonly Expression: FunctionDeclaration };
export function IsFunctionDefinition(node: ParseNode): node is FunctionDeclaration {
if (node.type === 'ParenthesizedExpression') {
return IsFunctionDefinition(node.Expression);
}
return node.type === 'FunctionExpression'
|| node.type === 'GeneratorExpression'
|| node.type === 'AsyncGeneratorExpression'
|| node.type === 'AsyncFunctionExpression'
|| node.type === 'ClassExpression'
|| node.type === 'ArrowFunction'
|| node.type === 'AsyncArrowFunction';
}
@@ -0,0 +1,5 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function IsIdentifierRef(node: ParseNode): node is ParseNode.IdentifierReference {
return node.type === 'IdentifierReference';
}
@@ -0,0 +1,5 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function IsInTailPosition(_node: ParseNode): boolean {
return false;
}
@@ -0,0 +1,23 @@
import { OutOfRange, isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
export function IsSimpleParameterList(node: ParseNode | readonly ParseNode[]) {
if (isArray(node)) {
for (const n of node) {
if (!IsSimpleParameterList(n)) {
return false;
}
}
return true;
}
switch (node.type) {
case 'SingleNameBinding':
return node.Initializer === null;
case 'BindingElement':
return false;
case 'BindingRestElement':
return false;
default:
throw new OutOfRange('IsSimpleParameterList', node);
}
}
@@ -0,0 +1,10 @@
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-isstatic */
// ClassElement :
// MethodDefinition
// `static` MethodDefinition
// `;`
export function IsStatic(ClassElement: ParseNode.ClassElement) {
return ClassElement.static;
}
@@ -0,0 +1,7 @@
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-isstrict */
export function IsStrict({ ScriptBody }: ParseNode.Script) {
// 1. If ScriptBody is present and the Directive Prologue of ScriptBody contains a Use Strict Directive, return true; otherwise, return false.
return ScriptBody!.strict;
}
@@ -0,0 +1,24 @@
import { X } from '../completion.mts';
import type { JSStringValue } from '../value.mts';
import { CodePointAt } from './all.mts';
export function IsStringWellFormedUnicode(string_: JSStringValue) {
const string = string_.stringValue();
// 1. Let _strLen_ be the number of code units in string.
const strLen = string.length;
// 2. Let k be 0.
let k = 0;
// 3. Repeat, while k ≠ strLen,
while (k !== strLen) {
// a. Let cp be ! CodePointAt(string, k).
const cp = X(CodePointAt(string, k));
// b. If cp.[[IsUnpairedSurrogate]] is true, return false.
if (cp.IsUnpairedSurrogate) {
return false;
}
// c. Set k to k + cp.[[CodeUnitCount]].
k += cp.CodeUnitCount;
}
// 4. Return true.
return true;
}
@@ -0,0 +1,26 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import {
TopLevelLexicallyDeclaredNames,
} from './all.mts';
export function LexicallyDeclaredNames(node: ParseNode): JSStringValue[] {
switch (node.type) {
case 'Script':
if (node.ScriptBody) {
return LexicallyDeclaredNames(node.ScriptBody);
}
return [];
case 'ScriptBody':
return TopLevelLexicallyDeclaredNames(node.StatementList);
case 'FunctionBody':
case 'GeneratorBody':
case 'AsyncBody':
case 'AsyncGeneratorBody':
return TopLevelLexicallyDeclaredNames(node.FunctionStatementList);
case 'ClassStaticBlockBody':
return TopLevelLexicallyDeclaredNames(node.ClassStaticBlockStatementList);
default:
return [];
}
}
@@ -0,0 +1,82 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { TopLevelLexicallyScopedDeclarations, DeclarationPart } from './all.mts';
export function LexicallyScopedDeclarations(node: ParseNode | readonly ParseNode[]): (ParseNode.Declaration | ParseNode.ExportDeclaration)[] {
if (isArray(node)) {
const declarations = [];
for (const item of node) {
declarations.push(...LexicallyScopedDeclarations(item));
}
return declarations;
}
switch (node.type) {
case 'LabelledStatement':
return LexicallyScopedDeclarations(node.LabelledItem);
case 'Script':
if (node.ScriptBody) {
return LexicallyScopedDeclarations(node.ScriptBody);
}
return [];
case 'ScriptBody':
return TopLevelLexicallyScopedDeclarations(node.StatementList);
case 'Module':
if (node.ModuleBody) {
return LexicallyScopedDeclarations(node.ModuleBody);
}
return [];
case 'ModuleBody':
return LexicallyScopedDeclarations(node.ModuleItemList);
case 'FunctionBody':
case 'GeneratorBody':
case 'AsyncBody':
case 'AsyncGeneratorBody':
return TopLevelLexicallyScopedDeclarations(node.FunctionStatementList);
case 'ClassStaticBlockBody':
return TopLevelLexicallyScopedDeclarations(node.ClassStaticBlockStatementList);
case 'ImportDeclaration':
return [];
case 'ClassDeclaration':
case 'LexicalDeclaration':
case 'FunctionDeclaration':
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
return [DeclarationPart(node)];
case 'CaseBlock': {
const names = [];
if (node.CaseClauses_a) {
names.push(...LexicallyScopedDeclarations(node.CaseClauses_a));
}
if (node.DefaultClause) {
names.push(...LexicallyScopedDeclarations(node.DefaultClause));
}
if (node.CaseClauses_b) {
names.push(...LexicallyScopedDeclarations(node.CaseClauses_b));
}
return names;
}
case 'CaseClause':
case 'DefaultClause':
if (node.StatementList) {
return LexicallyScopedDeclarations(node.StatementList);
}
return [];
case 'ExportDeclaration':
if (node.Declaration) {
return [DeclarationPart(node.Declaration)];
}
if (node.HoistableDeclaration) {
return [DeclarationPart(node.HoistableDeclaration)];
}
if (node.ClassDeclaration) {
return [node.ClassDeclaration];
}
if (node.AssignmentExpression) {
return [node];
}
return [];
default:
return [];
}
}
@@ -0,0 +1,98 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { StringValue } from './all.mts';
import { type LoadedModuleRequestRecord } from '#self';
// https://tc39.es/ecma262/#modulerequest-record
export interface ModuleRequestRecord {
readonly Specifier: JSStringValue;
readonly Attributes: ImportAttributeRecord[];
readonly Phase: 'defer' | 'evaluation';
}
// https://tc39.es/ecma262/#importattribute-record
export interface ImportAttributeRecord {
readonly Key: JSStringValue;
readonly Value: JSStringValue;
}
function stringsEqual(left: JSStringValue, right: JSStringValue) {
return left === right || left.stringValue() === right.stringValue();
}
// https://tc39.es/ecma262/#sec-ModuleRequestsEqual
export function ModuleRequestsEqual(left: ModuleRequestRecord | LoadedModuleRequestRecord, right: ModuleRequestRecord | LoadedModuleRequestRecord) {
if (!stringsEqual(left.Specifier, right.Specifier)) {
return false;
}
const leftAttrs = left.Attributes;
const rightAttrs = right.Attributes;
const leftAttrsCount = leftAttrs.length;
const rightAttrsCount = rightAttrs.length;
if (leftAttrsCount !== rightAttrsCount) {
return false;
}
for (const l of leftAttrs) {
if (!rightAttrs.some((r) => stringsEqual(l.Key, r.Key) && stringsEqual(l.Value, r.Value))) {
return false;
}
}
return true;
}
// https://tc39.es/ecma262/#sec-withclausetoattributes
function WithClauseToAttributes(node: ParseNode.WithClause): ImportAttributeRecord[] {
const attributes: ImportAttributeRecord[] = [];
for (const attribute of node.WithEntries) {
attributes.push({
Key: StringValue(attribute.AttributeKey),
Value: StringValue(attribute.AttributeValue),
});
}
attributes.sort((a, b) => (a.Key.value < b.Key.value ? -1 : 1));
return attributes;
}
export function ModuleRequests(node: ParseNode): ModuleRequestRecord[] {
switch (node.type) {
case 'Module':
if (node.ModuleBody) {
return ModuleRequests(node.ModuleBody);
}
return [];
case 'ModuleBody': {
const requests: ModuleRequestRecord[] = [];
for (const item of node.ModuleItemList) {
const additionalRequests = ModuleRequests(item);
for (const mr of additionalRequests) {
if (!requests.some((r) => ModuleRequestsEqual(r, mr) && r.Phase === mr.Phase)
) {
requests.push(mr);
}
}
}
return requests;
}
case 'ImportDeclaration':
if (node.FromClause) {
const specifier = StringValue(node.FromClause);
const attributes = node.WithClause ? WithClauseToAttributes(node.WithClause) : [];
return [{ Specifier: specifier, Attributes: attributes, Phase: node.Phase }];
}
if (node.ModuleSpecifier) {
const specifier = StringValue(node.ModuleSpecifier);
const attributes = node.WithClause ? WithClauseToAttributes(node.WithClause) : [];
return [{ Specifier: specifier, Attributes: attributes, Phase: node.Phase }];
}
throw new Error('Unreachable: all imports must have either an ImportClause or a ModuleSpecifier');
case 'ExportDeclaration':
if (node.FromClause) {
const specifier = StringValue(node.FromClause);
const attributes = node.WithClause ? WithClauseToAttributes(node.WithClause) : [];
return [{ Specifier: specifier, Attributes: attributes, Phase: 'evaluation' }];
}
return [];
default:
return [];
}
}
@@ -0,0 +1,15 @@
import type { ParseNode } from '../parser/ParseNode.mts';
import { PropName } from './all.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-nonconstructorelements */
// ClassElementList :
// ClassElement
// ClassElementList ClassElement
export function NonConstructorElements(ClassElementList: ParseNode.ClassElementList) {
return ClassElementList.filter((ClassElement) => {
if (ClassElement.static === false && PropName(ClassElement) === 'constructor') {
return false;
}
return true;
});
}
@@ -0,0 +1,7 @@
/** https://tc39.es/ecma262/#sec-numericvalue */
import type { ParseNode } from '../parser/ParseNode.mts';
import { Value } from '../value.mts';
export function NumericValue(node: ParseNode.NumericLiteral) {
return Value(node.value);
}
@@ -0,0 +1,23 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { StringValue } from './all.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-privateboundidentifiers */
export function PrivateBoundIdentifiers(node: ParseNode | readonly ParseNode[]): JSStringValue[] {
if (isArray(node)) {
return node.flatMap((n) => PrivateBoundIdentifiers(n));
}
switch (node.type) {
case 'PrivateIdentifier':
return [StringValue(node)];
case 'MethodDefinition':
case 'GeneratorMethod':
case 'AsyncMethod':
case 'AsyncGeneratorMethod':
case 'FieldDefinition':
return PrivateBoundIdentifiers(node.ClassElementName);
default:
return [];
}
}
@@ -0,0 +1,23 @@
import type { ParseNode } from '../parser/ParseNode.mts';
export function PropName(node: ParseNode): string | undefined {
switch (node.type) {
case 'IdentifierName':
return node.name;
case 'StringLiteral':
return node.value;
case 'MethodDefinition':
case 'GeneratorMethod':
case 'AsyncGeneratorMethod':
case 'AsyncMethod':
case 'FieldDefinition':
return PropName(node.ClassElementName);
case 'PropertyDefinition':
if (node.PropertyName) {
return PropName(node.PropertyName);
}
break;
default:
}
return undefined;
}
@@ -0,0 +1,22 @@
import { CodePointAt } from './all.mts';
/** https://tc39.es/ecma262/#sec-stringtocodepoints */
export function StringToCodePoints(string: string) {
// 1. Let codePoints be a new empty List.
const codePoints = [];
// 2. Let size be the length of string.
const size = string.length;
// 3. Let position be 0.
let position = 0;
// 4. Repeat, while position < size,
while (position < size) {
// a. Let cp be ! CodePointAt(string, position).
const cp = CodePointAt(string, position);
// b. Append cp.[[CodePoint]] to codePoints.
codePoints.push(cp.CodePoint);
// c. Set position to position + cp.[[CodeUnitCount]].
position += cp.CodeUnitCount;
}
// 5. Return codePoints.
return codePoints;
}
@@ -0,0 +1,19 @@
import { Value } from '../value.mts';
import { OutOfRange } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
export function StringValue(node: ParseNode) {
switch (node.type) {
case 'IdentifierName':
case 'BindingIdentifier':
case 'IdentifierReference':
case 'LabelIdentifier':
return Value(node.name);
case 'PrivateIdentifier':
return Value(`#${node.name}`);
case 'StringLiteral':
return Value(node.value);
default:
throw new OutOfRange('StringValue', node);
}
}
@@ -0,0 +1,109 @@
import { Value } from '../value.mts';
import { isHexDigit, isDecimalDigit, isLineTerminator } from '../parser/Lexer.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
/** https://tc39.es/ecma262/#sec-static-semantics-tv */
export function TV(s: string) {
let buffer = '';
for (let i = 0; i < s.length; i += 1) {
if (s[i] === '\\') {
i += 1;
switch (s[i]) {
case '$':
buffer += '$';
break;
case '\\':
buffer += '\\';
break;
case '`':
buffer += '`';
break;
case '\'':
buffer += '\'';
break;
case '"':
buffer += '"';
break;
case 'b':
buffer += '\b';
break;
case 'f':
buffer += '\f';
break;
case 'n':
buffer += '\n';
break;
case 'r':
buffer += '\r';
break;
case 't':
buffer += '\t';
break;
case 'v':
buffer += '\v';
break;
case 'x':
i += 1;
if (isHexDigit(s[i]) && isHexDigit(s[i + 1])) {
const n = Number.parseInt(s.slice(i, i + 2), 16);
i += 1;
buffer += String.fromCharCode(n);
} else {
return undefined;
}
break;
case 'u':
i += 1;
if (s[i] === '{') {
i += 1;
const start = i;
do {
i += 1;
} while (isHexDigit(s[i]));
if (s[i] !== '}') {
return undefined;
}
const n = Number.parseInt(s.slice(start, i), 16);
if (n > 0x10FFFF) {
return undefined;
}
buffer += String.fromCodePoint(n);
} else if (isHexDigit(s[i]) && isHexDigit(s[i + 1])
&& isHexDigit(s[i + 2]) && isHexDigit(s[i + 3])) {
const n = Number.parseInt(s.slice(i, i + 4), 16);
i += 3;
buffer += String.fromCodePoint(n);
} else {
return undefined;
}
break;
case '0':
if (isDecimalDigit(s[i + 1])) {
return undefined;
}
return '\u{0000}';
default:
if (isLineTerminator(s)) {
return '';
}
return undefined;
}
} else {
buffer += s[i];
}
}
return buffer;
}
export function TemplateStrings(node: ParseNode.TemplateLiteral, raw: boolean) {
if (raw) {
return node.TemplateSpanList.map((s) => Value(s));
}
return node.TemplateSpanList.map((v) => {
const tv = TV(v);
if (tv === undefined) {
return Value.undefined;
}
return Value(tv);
});
}
@@ -0,0 +1,21 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { BoundNames } from './all.mts';
export function TopLevelLexicallyDeclaredNames(node: ParseNode | readonly ParseNode[]): JSStringValue[] {
if (isArray(node)) {
const names = [];
for (const StatementListItem of node) {
names.push(...TopLevelLexicallyDeclaredNames(StatementListItem));
}
return names;
}
switch (node.type) {
case 'ClassDeclaration':
case 'LexicalDeclaration':
return BoundNames(node);
default:
return [];
}
}
@@ -0,0 +1,23 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
export function TopLevelLexicallyScopedDeclarations(node: ParseNode | readonly ParseNode[]): LexicallyScopedDeclaration[] {
if (isArray(node)) {
const declarations = [];
for (const item of node) {
declarations.push(...TopLevelLexicallyScopedDeclarations(item));
}
return declarations;
}
switch (node.type) {
case 'ClassDeclaration':
case 'LexicalDeclaration':
return [node];
default:
return [];
}
}
export type LexicallyScopedDeclaration =
| ParseNode.ClassDeclaration
| ParseNode.LexicalDeclaration;
@@ -0,0 +1,26 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { BoundNames, VarDeclaredNames } from './all.mts';
export function TopLevelVarDeclaredNames(node: ParseNode | readonly ParseNode[]): JSStringValue[] {
if (isArray(node)) {
const names = [];
for (const item of node) {
names.push(...TopLevelVarDeclaredNames(item));
}
return names;
}
switch (node.type) {
case 'ClassDeclaration':
case 'LexicalDeclaration':
return [];
case 'FunctionDeclaration':
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
return BoundNames(node);
default:
return VarDeclaredNames(node);
}
}
@@ -0,0 +1,34 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { DeclarationPart, VarScopedDeclarations } from './all.mts';
export function TopLevelVarScopedDeclarations(node: ParseNode | readonly ParseNode[]): VarScopedDeclaration[] {
if (isArray(node)) {
const declarations = [];
for (const item of node) {
declarations.push(...TopLevelVarScopedDeclarations(item));
}
return declarations;
}
switch (node.type) {
case 'ClassDeclaration':
case 'LexicalDeclaration':
return [];
case 'FunctionDeclaration':
case 'GeneratorDeclaration':
case 'AsyncFunctionDeclaration':
case 'AsyncGeneratorDeclaration':
return [DeclarationPart(node)];
default:
return VarScopedDeclarations(node);
}
}
export type VarScopedDeclaration =
| ParseNode.ForBinding
| ParseNode.VariableDeclaration
| ParseNode.FunctionDeclaration
| ParseNode.GeneratorDeclaration
| ParseNode.AsyncFunctionDeclaration
| ParseNode.AsyncGeneratorDeclaration
| ParseNode.BindingIdentifier;
@@ -0,0 +1,18 @@
import { Assert } from '#self';
import type { CodePoint } from '#self';
/** https://tc39.es/ecma262/#sec-utf16encodecodepoint */
export function UTF16EncodeCodePoint(cp: CodePoint) {
// 1. Assert: 0 ≤ cp ≤ 0x10FFFF.
Assert(cp >= 0 && cp <= 0x10FFFF);
// 2. If cp ≤ 0xFFFF, return the String value consisting of the code unit whose value is cp.
if (cp <= 0xFFFF) {
return String.fromCodePoint(cp);
}
// 3. Let cu1 be the code unit whose value is floor((cp - 0x10000) / 0x400) + 0xD800.
const cu1 = Math.floor((cp - 0x10000) / 0x400) + 0xD800;
// 4. Let cu2 be the code unit whose value is ((cp - 0x10000) modulo 0x400) + 0xDC00.
const cu2 = ((cp - 0x10000) % 0x400) + 0xDC00;
// 5. Return the string-concatenation of cu1 and cu2.
return String.fromCodePoint(cu1, cu2);
}
@@ -0,0 +1,12 @@
import { Assert } from '#self';
import { isLeadingSurrogate, isTrailingSurrogate, type CodePoint } from '#self';
/** https://tc39.es/ecma262/#sec-utf16decodesurrogatepair */
export function UTF16SurrogatePairToCodePoint(lead: number, trail: number): CodePoint {
// 1. Assert: lead is a leading surrogate and trail is a trailing surrogate.
Assert(isLeadingSurrogate(lead) && isTrailingSurrogate(trail));
// 2. Let cp be (lead - 0xD800) × 0x400 + (trail - 0xDC00) + 0x10000.
const cp = (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
// 3. Return the code point cp.
return cp as CodePoint;
}
@@ -0,0 +1,109 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import type { JSStringValue } from '../value.mts';
import { BoundNames, TopLevelVarDeclaredNames } from './all.mts';
export function VarDeclaredNames(node: ParseNode | readonly ParseNode[]): JSStringValue[] {
if (isArray(node)) {
const names = [];
for (const item of node) {
names.push(...VarDeclaredNames(item));
}
return names;
}
switch (node.type) {
case 'VariableStatement':
return BoundNames(node.VariableDeclarationList);
case 'VariableDeclaration':
return BoundNames(node);
case 'IfStatement': {
const names = VarDeclaredNames(node.Statement_a);
if (node.Statement_b) {
names.push(...VarDeclaredNames(node.Statement_b));
}
return names;
}
case 'Block':
return VarDeclaredNames(node.StatementList);
case 'WhileStatement':
return VarDeclaredNames(node.Statement);
case 'DoWhileStatement':
return VarDeclaredNames(node.Statement);
case 'ForStatement': {
const names = [];
if (node.VariableDeclarationList) {
names.push(...VarDeclaredNames(node.VariableDeclarationList));
}
names.push(...VarDeclaredNames(node.Statement));
return names;
}
case 'ForInStatement':
case 'ForOfStatement':
case 'ForAwaitStatement': {
const names = [];
if (node.ForBinding) {
names.push(...BoundNames(node.ForBinding));
}
names.push(...VarDeclaredNames(node.Statement));
return names;
}
case 'WithStatement':
return VarDeclaredNames(node.Statement);
case 'SwitchStatement':
return VarDeclaredNames(node.CaseBlock);
case 'CaseBlock': {
const names = [];
if (node.CaseClauses_a) {
names.push(...VarDeclaredNames(node.CaseClauses_a));
}
if (node.DefaultClause) {
names.push(...VarDeclaredNames(node.DefaultClause));
}
if (node.CaseClauses_b) {
names.push(...VarDeclaredNames(node.CaseClauses_b));
}
return names;
}
case 'CaseClause':
case 'DefaultClause':
if (node.StatementList) {
return VarDeclaredNames(node.StatementList);
}
return [];
case 'LabelledStatement':
return VarDeclaredNames(node.LabelledItem);
case 'TryStatement': {
const names = VarDeclaredNames(node.Block);
if (node.Catch) {
names.push(...VarDeclaredNames(node.Catch));
}
if (node.Finally) {
names.push(...VarDeclaredNames(node.Finally));
}
return names;
}
case 'Catch':
return VarDeclaredNames(node.Block);
case 'Script':
if (node.ScriptBody) {
return VarDeclaredNames(node.ScriptBody);
}
return [];
case 'ScriptBody':
return TopLevelVarDeclaredNames(node.StatementList);
case 'FunctionBody':
case 'GeneratorBody':
case 'AsyncBody':
case 'AsyncGeneratorBody':
return TopLevelVarDeclaredNames(node.FunctionStatementList);
case 'ClassStaticBlockBody':
return TopLevelVarDeclaredNames(node.ClassStaticBlockStatementList);
case 'ExportDeclaration':
if (node.VariableStatement) {
return BoundNames(node);
}
return [];
default:
return [];
}
}
@@ -0,0 +1,115 @@
import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { TopLevelVarScopedDeclarations, type VarScopedDeclaration } from './all.mts';
export function VarScopedDeclarations(node: ParseNode | readonly ParseNode[]): VarScopedDeclaration[] {
if (isArray(node)) {
const declarations = [];
for (const item of node) {
declarations.push(...VarScopedDeclarations(item));
}
return declarations;
}
switch (node.type) {
case 'VariableStatement':
return VarScopedDeclarations(node.VariableDeclarationList);
case 'VariableDeclaration':
return [node];
case 'Block':
return VarScopedDeclarations(node.StatementList);
case 'IfStatement': {
const declarations = VarScopedDeclarations(node.Statement_a);
if (node.Statement_b) {
declarations.push(...VarScopedDeclarations(node.Statement_b));
}
return declarations;
}
case 'WhileStatement':
return VarScopedDeclarations(node.Statement);
case 'DoWhileStatement':
return VarScopedDeclarations(node.Statement);
case 'ForStatement': {
const names = [];
if (node.VariableDeclarationList) {
names.push(...VarScopedDeclarations(node.VariableDeclarationList));
}
names.push(...VarScopedDeclarations(node.Statement));
return names;
}
case 'ForInStatement':
case 'ForOfStatement':
case 'ForAwaitStatement': {
const declarations = [];
if (node.ForBinding) {
declarations.push(node.ForBinding);
}
declarations.push(...VarScopedDeclarations(node.Statement));
return declarations;
}
case 'WithStatement':
return VarScopedDeclarations(node.Statement);
case 'SwitchStatement':
return VarScopedDeclarations(node.CaseBlock);
case 'CaseBlock': {
const names = [];
if (node.CaseClauses_a) {
names.push(...VarScopedDeclarations(node.CaseClauses_a));
}
if (node.DefaultClause) {
names.push(...VarScopedDeclarations(node.DefaultClause));
}
if (node.CaseClauses_b) {
names.push(...VarScopedDeclarations(node.CaseClauses_b));
}
return names;
}
case 'CaseClause':
case 'DefaultClause':
if (node.StatementList) {
return VarScopedDeclarations(node.StatementList);
}
return [];
case 'LabelledStatement':
return VarScopedDeclarations(node.LabelledItem);
case 'TryStatement': {
const declarations = VarScopedDeclarations(node.Block);
if (node.Catch) {
declarations.push(...VarScopedDeclarations(node.Catch));
}
if (node.Finally) {
declarations.push(...VarScopedDeclarations(node.Finally));
}
return declarations;
}
case 'Catch':
return VarScopedDeclarations(node.Block);
case 'ExportDeclaration':
if (node.VariableStatement) {
return VarScopedDeclarations(node.VariableStatement);
}
return [];
case 'Script':
if (node.ScriptBody) {
return VarScopedDeclarations(node.ScriptBody);
}
return [];
case 'ScriptBody':
return TopLevelVarScopedDeclarations(node.StatementList);
case 'Module':
if (node.ModuleBody) {
return VarScopedDeclarations(node.ModuleBody);
}
return [];
case 'ModuleBody':
return VarScopedDeclarations(node.ModuleItemList);
case 'FunctionBody':
case 'GeneratorBody':
case 'AsyncBody':
case 'AsyncGeneratorBody':
return TopLevelVarScopedDeclarations(node.FunctionStatementList);
case 'ClassStaticBlockBody':
return TopLevelVarScopedDeclarations(node.ClassStaticBlockStatementList);
default:
return [];
}
}
+47
View File
@@ -0,0 +1,47 @@
export * from './StringValue.mts';
export * from './IsStatic.mts';
export * from './NonConstructorElements.mts';
export * from './ConstructorMethod.mts';
export * from './PropName.mts';
export * from './NumericValue.mts';
export * from './IsAnonymousFunctionDefinition.mts';
export * from './IsFunctionDefinition.mts';
export * from './HasName.mts';
export * from './IsIdentifierRef.mts';
export * from './LexicallyDeclaredNames.mts';
export * from './TopLevelLexicallyDeclaredNames.mts';
export * from './BoundNames.mts';
export * from './VarDeclaredNames.mts';
export * from './TopLevelVarDeclaredNames.mts';
export * from './VarScopedDeclarations.mts';
export * from './TopLevelVarScopedDeclarations.mts';
export * from './DeclarationPart.mts';
export * from './LexicallyScopedDeclarations.mts';
export * from './TopLevelLexicallyScopedDeclarations.mts';
export * from './IsConstantDeclaration.mts';
export * from './IsInTailPosition.mts';
export * from './ExpectedArgumentCount.mts';
export * from './HasInitializer.mts';
export * from './IsSimpleParameterList.mts';
export * from './ContainsExpression.mts';
export * from './IsStrict.mts';
export * from './BodyText.mts';
export * from './FlagText.mts';
export * from './ModuleRequests.mts';
export * from './ImportEntries.mts';
export * from './ExportEntries.mts';
export * from './ImportedLocalNames.mts';
export * from './IsDestructuring.mts';
export * from './TemplateStrings.mts';
export * from './ImportEntriesForModule.mts';
export * from './ExportEntriesForModule.mts';
export * from './CharacterValue.mts';
export * from './UTF16SurrogatePairToCodePoint.mts';
export * from './CodePointAt.mts';
export * from './StringToCodePoints.mts';
export * from './CodePointsToString.mts';
export * from './IsStringWellFormedUnicode.mts';
export * from './IsComputedPropertyKey.mts';
export * from './PrivateBoundIdentifiers.mts';
export * from './ContainsArguments.mts';
export * from './UTF16EncodeCodePoint.mts';