tsc-ts 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api/compiler/ast.d.ts +9 -3
- package/dist/api/compiler/ast.js +17 -12
- package/dist/api/compiler/checker-classes.js +2 -1
- package/dist/api/compiler/checker-contextual.d.ts +20 -0
- package/dist/api/compiler/checker-contextual.js +111 -1
- package/dist/api/compiler/checker-expressions.js +14 -1
- package/dist/api/compiler/checker-jsx.js +2 -1
- package/dist/api/compiler/checker-type-factory.js +53 -12
- package/dist/api/compiler/checker.d.ts +5 -1
- package/dist/api/compiler/checker.js +10 -4
- package/dist/api/compiler/compile-program.d.ts +5 -0
- package/dist/api/compiler/compile-program.js +8 -0
- package/dist/api/compiler/expr-record.d.ts +11 -0
- package/dist/api/compiler/expr-record.js +53 -0
- package/dist/api/compiler/identifier-name.d.ts +9 -0
- package/dist/api/compiler/identifier-name.js +10 -0
- package/dist/api/compiler/jsdoc.js +4 -2
- package/dist/api/compiler/parser.d.ts +2 -1
- package/dist/api/compiler/parser.js +10 -8
- package/dist/api/compiler/printer/factory.js +3 -2
- package/dist/api/compiler/printer/printer.js +3 -2
- package/dist/api/compiler/reparser.js +4 -3
- package/dist/api/compiler/scanner.d.ts +8 -0
- package/dist/api/compiler/scanner.js +58 -42
- package/dist/api/compiler/shared-results.d.ts +376 -143
- package/dist/api/compiler/shared-results.js +1550 -773
- package/dist/api/compiler/transformers/declarations.js +2 -1
- package/package.json +9 -9
- package/src/compiler/ast.ts +19 -12
- package/src/compiler/cgroup.ts +157 -0
- package/src/compiler/checker-classes.ts +2 -1
- package/src/compiler/checker-contextual.ts +104 -0
- package/src/compiler/checker-expressions.ts +16 -1
- package/src/compiler/checker-jsx.ts +2 -1
- package/src/compiler/checker-type-factory.ts +53 -14
- package/src/compiler/checker.ts +11 -4
- package/src/compiler/compile-program.ts +8 -0
- package/src/compiler/decoder.ts +2 -1
- package/src/compiler/doctor-report.ts +133 -0
- package/src/compiler/expr-record.ts +59 -0
- package/src/compiler/identifier-name.ts +13 -0
- package/src/compiler/jsdoc.ts +4 -2
- package/src/compiler/parallel-check.ts +353 -146
- package/src/compiler/parallel-emit.ts +3 -3
- package/src/compiler/parser.ts +10 -8
- package/src/compiler/printer/factory.ts +3 -2
- package/src/compiler/printer/printer.ts +4 -2
- package/src/compiler/reparser.ts +7 -3
- package/src/compiler/run-config.ts +1383 -0
- package/src/compiler/run-record-read.ts +87 -0
- package/src/compiler/run-records.ts +575 -0
- package/src/compiler/scanner.ts +57 -41
- package/src/compiler/shared-results.ts +1626 -896
- package/src/compiler/thread-entry.ts +3 -1
- package/src/compiler/transformers/declarations.ts +2 -1
- package/src/compiler/tsc-build.ts +64 -6
- package/src/compiler/tsc-cli.ts +33 -4
- package/src/compiler/tsc-compile.ts +213 -8
- package/src/compiler/tsc-watch.ts +25 -4
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// The type the last completed check of an expression produced, by node and check mode, for the
|
|
2
|
+
// expressions that are checked with no contextual type.
|
|
3
|
+
//
|
|
4
|
+
// The checker checks some expressions more than once with the same inputs: a body that the eager
|
|
5
|
+
// pass reached and the deferred pass reaches again, a node re-checked after a loop body, an
|
|
6
|
+
// argument of a second inference round. A check is a function of the node, the check mode and the
|
|
7
|
+
// checker's state, and the state only grows while the checker runs, so a check that completed
|
|
8
|
+
// earlier answers the next check of the same node: the later check would produce the type the
|
|
9
|
+
// earlier one produced, report the diagnostics it reported and defer the nodes it deferred.
|
|
10
|
+
//
|
|
11
|
+
// A contextual type is part of the inputs, so the record can only serve an expression whose check
|
|
12
|
+
// has none: the checker-contextual predicate says so before the record is consulted, and a check
|
|
13
|
+
// under a contextual type never reads or writes the record. For the same reason the record is not
|
|
14
|
+
// consulted while an inference context is in force: what the check infers into depends on it.
|
|
15
|
+
//
|
|
16
|
+
// The record is a direct-mapped table that stores the key beside the value, so a lookup that lands
|
|
17
|
+
// on another node reads the entry as a miss. Its size bounds the memory and the cache the lookups
|
|
18
|
+
// use: an entry is a node id, a mode and a type slot, the whole table takes about 200 KB, and 2^14
|
|
19
|
+
// entries serve nearly all repeats (2^14 against 2^20 loses 0.1% of the check on the largest
|
|
20
|
+
// program measured).
|
|
21
|
+
/** Entries in the record; a power of two. */
|
|
22
|
+
const EntryCount = 1 << 14;
|
|
23
|
+
const EntryMask = EntryCount - 1;
|
|
24
|
+
/** The types by entry; a slot holds the type of the check recorded at that entry. */
|
|
25
|
+
function newTypeSlots() {
|
|
26
|
+
const slots = [];
|
|
27
|
+
for (let i = 0; i < EntryCount; i++)
|
|
28
|
+
slots.push(undefined);
|
|
29
|
+
return slots;
|
|
30
|
+
}
|
|
31
|
+
function entryOf(nodeId, mode) {
|
|
32
|
+
return (Math.imul(nodeId, 0x9e3779b1) ^ mode) & EntryMask;
|
|
33
|
+
}
|
|
34
|
+
export class ExprCheckRecord {
|
|
35
|
+
/** The node id of the check at each entry, plus one; zero where no check is recorded. */
|
|
36
|
+
nodeIds = new Int32Array(EntryCount);
|
|
37
|
+
modes = new Uint8Array(EntryCount);
|
|
38
|
+
types = newTypeSlots();
|
|
39
|
+
/** The type the last completed check of the node under `mode` produced, if the entry holds it. */
|
|
40
|
+
get(nodeId, mode) {
|
|
41
|
+
const entry = entryOf(nodeId, mode);
|
|
42
|
+
if (this.nodeIds[entry] !== nodeId + 1 || this.modes[entry] !== mode)
|
|
43
|
+
return undefined;
|
|
44
|
+
return this.types[entry];
|
|
45
|
+
}
|
|
46
|
+
/** Records that a check of the node under `mode` produced `type`. */
|
|
47
|
+
set(nodeId, mode, type) {
|
|
48
|
+
const entry = entryOf(nodeId, mode);
|
|
49
|
+
this.nodeIds[entry] = nodeId + 1;
|
|
50
|
+
this.modes[entry] = mode;
|
|
51
|
+
this.types[entry] = type;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProgramLifetime } from '@scriptc/types';
|
|
2
|
+
/**
|
|
3
|
+
* The text of an identifier. The identifiers a parser's scanner interns with the same text share one
|
|
4
|
+
* (scanner.ts `IdentifierTable`), and natively an identifier refers to it in 4 bytes.
|
|
5
|
+
*/
|
|
6
|
+
export declare class IdentifierName implements ProgramLifetime {
|
|
7
|
+
readonly text: string;
|
|
8
|
+
constructor(text: string);
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The text of an identifier. The identifiers a parser's scanner interns with the same text share one
|
|
3
|
+
* (scanner.ts `IdentifierTable`), and natively an identifier refers to it in 4 bytes.
|
|
4
|
+
*/
|
|
5
|
+
export class IdentifierName {
|
|
6
|
+
text;
|
|
7
|
+
constructor(text) {
|
|
8
|
+
this.text = text;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -7,6 +7,7 @@ import { isWhiteSpaceLike } from './character-codes.js';
|
|
|
7
7
|
import { Diagnostic } from './diagnostics.js';
|
|
8
8
|
import { Reparser } from './reparser.js';
|
|
9
9
|
import { getLeadingCommentRanges, getTrailingCommentRanges } from './scanner.js';
|
|
10
|
+
import { IdentifierName } from './identifier-name.js';
|
|
10
11
|
/** Comment text collected while parsing, with the indentation it has reached. */
|
|
11
12
|
class CommentText {
|
|
12
13
|
parts = [];
|
|
@@ -1076,16 +1077,17 @@ export class JSDocParser {
|
|
|
1076
1077
|
p.parseErrorAtCurrentToken(293 /* Diagnostics.Identifier_expected_0_is_a_reserved_word_that_cannot_be_used_here */, [
|
|
1077
1078
|
p.scanner.getTokenText(),
|
|
1078
1079
|
]);
|
|
1079
|
-
const missing = p.newIdentifier('');
|
|
1080
|
+
const missing = p.newIdentifier(new IdentifierName(''));
|
|
1080
1081
|
p.finishNode(missing, p.nodePos());
|
|
1081
1082
|
return missing;
|
|
1082
1083
|
}
|
|
1083
1084
|
const pos = p.scanner.getTokenStart();
|
|
1084
1085
|
const end = p.scanner.getTokenEnd();
|
|
1085
1086
|
const text = p.scanner.getIdentifierValue();
|
|
1087
|
+
const name = p.scanner.getIdentifierName();
|
|
1086
1088
|
p.internIdentifier(text);
|
|
1087
1089
|
p.nextTokenJSDoc();
|
|
1088
|
-
const result = p.newIdentifier(
|
|
1090
|
+
const result = p.newIdentifier(name);
|
|
1089
1091
|
p.finishNodeWithEnd(result, pos, end);
|
|
1090
1092
|
return result;
|
|
1091
1093
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Block, CaseBlock, CatchClause, ExternalModuleIndicatorOptions, Identifier, ImportAttributes, ImportClause, JsxAttributes, JsxClosingElement, JsxClosingFragment, JsxOpeningElement, ModifierList, Node, NodeFlags, NodeList, ObjectLiteralExpression, ParseOptions, ScriptKind, SourceFile, TemplateHead, TemplateLiteralTypeSpan, TemplateSpan, ThisTypeNode, Token, Tristate, TypeParameterDeclaration, VariableDeclaration, VariableDeclarationList, type EntityName, type Expression, type Statement, type TypeNode } from './ast.js';
|
|
2
|
+
import { IdentifierName } from './identifier-name.js';
|
|
2
3
|
import { OperatorPrecedence } from './ast-utilities.js';
|
|
3
4
|
import { Diagnostic, type DiagnosticMessage } from './diagnostics.js';
|
|
4
5
|
import { JSDocParser } from './jsdoc.js';
|
|
@@ -385,7 +386,7 @@ export declare class Parser extends ScannerErrorReporter {
|
|
|
385
386
|
* A new identifier of the class `use` calls for. Callers create it after the identifier's token, so
|
|
386
387
|
* for an element name the current token tells a shorthand property from an assignment or a method.
|
|
387
388
|
*/
|
|
388
|
-
newIdentifier(
|
|
389
|
+
newIdentifier(name: IdentifierName, use?: IdentifierUse): Identifier;
|
|
389
390
|
/** Whether the current token ends an object literal element name that is not a shorthand property. */
|
|
390
391
|
isElementNameEnd(): boolean;
|
|
391
392
|
createMissingIdentifier(use?: IdentifierUse): Identifier;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
// for (const statement of file.statements.nodes) console.log(statement.kind);
|
|
7
7
|
// ```
|
|
8
8
|
import { appendToList, ArrayLiteralExpression, ArrayTypeNode, ArrowFunction, AsExpression, AwaitExpression, BigIntLiteral, BinaryExpression, BindingElement, BindingPattern, Block, BreakStatement, CallExpression, CallSignatureDeclaration, CaseBlock, CaseOrDefaultClause, CatchClause, CheckJsDirective, ClassDeclaration, ClassExpression, ClassStaticBlockDeclaration, ComputedPropertyName, ConditionalExpression, ConditionalTypeNode, ConstructSignatureDeclaration, ConstructorDeclaration, ConstructorTypeNode, ContinueStatement, DebuggerStatement, Decorator, DeleteExpression, DoStatement, ElementAccessExpression, emptyCommentDirectives, emptyDiagnostics, emptyFileReferences, emptyNodes, emptyPragmas, EmptyStatement, EnumDeclaration, EnumMember, ExportAssignment, ExportDeclaration, ExportSpecifier, ExpressionStatement, ExpressionWithTypeArguments, ExternalModuleIndicatorOptions, ExternalModuleReference, FileReference, ForInOrOfStatement, ForStatement, FunctionDeclaration, FunctionExpression, FunctionTypeNode, GetAccessorDeclaration, HeritageClause, Identifier, IfStatement, ImportAttribute, ImportAttributes, ImportClause, ImportDeclaration, ImportEqualsDeclaration, ImportSpecifier, ImportTypeNode, IndexSignatureDeclaration, IndexedAccessTypeNode, InferTypeNode, InterfaceDeclaration, IntersectionTypeNode, JSDocAllType, JSDocNonNullableType, JSDocNullableType, JSDocOptionalType, JSDocVariadicType, JsxAttribute, JsxAttributes, JsxClosingElement, JsxClosingFragment, JsxElement, JsxExpression, JsxFragment, JsxNamespacedName, JsxOpeningElement, JsxOpeningFragment, JsxSelfClosingElement, JsxSpreadAttribute, JsxText, KeywordExpression, KeywordTypeNode, LabeledStatement, LiteralTypeNode, MappedTypeNode, MetaProperty, MethodDeclaration, MethodSignatureDeclaration, MissingDeclaration, MissingNodeList, ModifierList, ModuleBlock, ModuleDeclaration, NamedExports, NamedImports, NamedTupleMember, NamespaceExport, NamespaceExportDeclaration, NamespaceImport, NewExpression, NoSubstitutionTemplateLiteral, NodeList, NodeVisitor, NonNullExpression, NumericLiteral, ObjectLiteralExpression, OmittedExpression, OptionalTypeNode, ParameterDeclaration, ParenthesizedExpression, ParenthesizedTypeNode, PostfixUnaryExpression, Pragma, PragmaArgument, PrefixUnaryExpression, PrivateIdentifier, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertySignatureDeclaration, QualifiedName, ReferenceIdentifier, RegularExpressionLiteral, RestTypeNode, ReturnStatement, SatisfiesExpression, SemicolonClassElement, SetAccessorDeclaration, ShorthandPropertyAssignment, SourceFile, SpreadAssignment, SpreadElement, StringLiteral, SwitchStatement, TaggedTemplateExpression, TemplateExpression, TemplateHead, TemplateLiteralTypeNode, TemplateLiteralTypeSpan, TemplateMiddle, TemplateSpan, TemplateTail, ThisTypeNode, ThrowStatement, Token, TryStatement, TupleTypeNode, TypeAliasDeclaration, TypeAssertion, TypeLiteralNode, TypeOfExpression, TypeOperatorNode, TypeParameterDeclaration, TypePredicateNode, TypeQueryNode, TypeReferenceNode, UnionTypeNode, VariableDeclaration, VariableDeclarationList, VariableStatement, VoidExpression, WhileStatement, WithStatement, YieldExpression, getBody, getIsTypeOnly, getModifiers, getName, getPostfixToken, getQuestionToken, getStatements, getText, getType, getTypeArguments, getTypeParameters, newCallExpression, newPropertyAssignment, } from './ast.js';
|
|
9
|
+
import { IdentifierName } from './identifier-name.js';
|
|
9
10
|
import { canHaveDecorators, canHaveIllegalDecorators, compareNodePositions, getBinaryOperatorPrecedence, getExternalModuleName, getImportTypeNodeLiteral, getTextOfNodeFromSourceText, isAmbientModule, isAnyImportOrReExport, isAssignmentOperator, isClassMemberModifier, isFunctionLike, isImportCall, isImportMeta, isImportPhaseMetaProperty, isInJSFile, isKeyword, isKeywordOrPunctuation, isLeftHandSideExpression, isModifierKind, isRequireCall, isReservedWord, isStringLiteralLike, modifierToFlag, modifiersToFlags, nodeIsMissing, nodeIsPresent, tagNamesAreEquivalent, tokenIsIdentifierOrKeyword, tokenIsIdentifierOrKeywordOrGreaterThan, } from './ast-utilities.js';
|
|
10
11
|
import { isLineBreak } from './character-codes.js';
|
|
11
12
|
import { Diagnostic, messageCode } from './diagnostics.js';
|
|
@@ -2308,7 +2309,7 @@ export class Parser extends ScannerErrorReporter {
|
|
|
2308
2309
|
}
|
|
2309
2310
|
else {
|
|
2310
2311
|
this.parseErrorAt(skipTrivia(this.sourceText, name.pos), name.end, 1 /* Diagnostics.Identifier_expected */);
|
|
2311
|
-
identifierName = this.newIdentifier('');
|
|
2312
|
+
identifierName = this.newIdentifier(new IdentifierName(''));
|
|
2312
2313
|
this.finishNode(identifierName, name.pos);
|
|
2313
2314
|
}
|
|
2314
2315
|
return this.checkJSSyntax(this.finishNode(new ImportSpecifier(isTypeOnly, propertyName, identifierName), pos));
|
|
@@ -2758,13 +2759,13 @@ export class Parser extends ScannerErrorReporter {
|
|
|
2758
2759
|
* A new identifier of the class `use` calls for. Callers create it after the identifier's token, so
|
|
2759
2760
|
* for an element name the current token tells a shorthand property from an assignment or a method.
|
|
2760
2761
|
*/
|
|
2761
|
-
newIdentifier(
|
|
2762
|
+
newIdentifier(name, use = 0 /* IdentifierUse.Reference */) {
|
|
2762
2763
|
this.identifierCount++;
|
|
2763
|
-
if (text === 'await')
|
|
2764
|
+
if (name.text === 'await')
|
|
2764
2765
|
this.statementHasAwaitIdentifier = true;
|
|
2765
2766
|
if (use === 1 /* IdentifierUse.Name */ || (use === 2 /* IdentifierUse.ElementName */ && this.isElementNameEnd()))
|
|
2766
|
-
return new Identifier(
|
|
2767
|
-
return new ReferenceIdentifier(
|
|
2767
|
+
return new Identifier(name);
|
|
2768
|
+
return new ReferenceIdentifier(name);
|
|
2768
2769
|
}
|
|
2769
2770
|
/** Whether the current token ends an object literal element name that is not a shorthand property. */
|
|
2770
2771
|
isElementNameEnd() {
|
|
@@ -2772,7 +2773,7 @@ export class Parser extends ScannerErrorReporter {
|
|
|
2772
2773
|
return token === 58 /* SyntaxKind.ColonToken */ || token === 20 /* SyntaxKind.OpenParenToken */ || token === 29 /* SyntaxKind.LessThanToken */;
|
|
2773
2774
|
}
|
|
2774
2775
|
createMissingIdentifier(use = 0 /* IdentifierUse.Reference */) {
|
|
2775
|
-
const result = this.newIdentifier('', use);
|
|
2776
|
+
const result = this.newIdentifier(new IdentifierName(''), use);
|
|
2776
2777
|
this.finishNode(result, this.nodePos());
|
|
2777
2778
|
return result;
|
|
2778
2779
|
}
|
|
@@ -3959,7 +3960,7 @@ export class Parser extends ScannerErrorReporter {
|
|
|
3959
3960
|
// An unclosed opening element took its parent's closing element: restructure
|
|
3960
3961
|
// (<div>(...<span>...</div>)) into (<div>(...<span>...</>)</div>). The parent reports it.
|
|
3961
3962
|
const end = lastChild.children.end;
|
|
3962
|
-
const missingIdentifier = this.finishNodeWithEnd(this.newIdentifier(''), end, end);
|
|
3963
|
+
const missingIdentifier = this.finishNodeWithEnd(this.newIdentifier(new IdentifierName('')), end, end);
|
|
3963
3964
|
const newClosingElement = new JsxClosingElement(missingIdentifier);
|
|
3964
3965
|
this.finishNodeWithEnd(newClosingElement, end, end);
|
|
3965
3966
|
const newLast = new JsxElement(lastChild.openingElement, lastChild.children, newClosingElement);
|
|
@@ -4888,11 +4889,12 @@ export class Parser extends ScannerErrorReporter {
|
|
|
4888
4889
|
if (isIdentifier) {
|
|
4889
4890
|
const pos = this.scanner.hasPrecedingJSDocLeadingAsterisks() ? this.scanner.getTokenStart() : this.nodePos();
|
|
4890
4891
|
const text = this.scanner.getIdentifierValue();
|
|
4892
|
+
const name = this.scanner.getIdentifierName();
|
|
4891
4893
|
const record = this.scanner.recordIdentifierValue(text);
|
|
4892
4894
|
this.nextTokenWithoutCheck();
|
|
4893
4895
|
if (record)
|
|
4894
4896
|
this.identifierTexts.push(text);
|
|
4895
|
-
const result = this.newIdentifier(
|
|
4897
|
+
const result = this.newIdentifier(name, use);
|
|
4896
4898
|
this.finishNode(result, pos);
|
|
4897
4899
|
return result;
|
|
4898
4900
|
}
|
|
@@ -8,6 +8,7 @@ import { NodeFactoryBase } from './factory-base.js';
|
|
|
8
8
|
import { addDisposableResourceHelper, classPrivateFieldGetHelper, classPrivateFieldInHelper, classPrivateFieldSetHelper, disposeResourcesHelper, exportStarHelper, importDefaultHelper, importStarHelper, makeTemplateObjectHelper, rewriteRelativeImportExtensionsHelper, } from './helpers.js';
|
|
9
9
|
import { formatGeneratedName, isMemberName, nodeIsSynthesized } from './utilities.js';
|
|
10
10
|
import { isOuterExpression, outerExpressionOperand } from '../transformers/utilities.js';
|
|
11
|
+
import { IdentifierName } from '../identifier-name.js';
|
|
11
12
|
/**
|
|
12
13
|
* The node factory of an emit context. The context is the factory (`EmitContext` extends it), so the two are one
|
|
13
14
|
* object: a factory and a context that pointed at each other made every context a reference cycle.
|
|
@@ -36,7 +37,7 @@ export class NodeFactory extends NodeFactoryBase {
|
|
|
36
37
|
return copy;
|
|
37
38
|
}
|
|
38
39
|
cloneIdentifier(node) {
|
|
39
|
-
return this.cloneNode(node, new Identifier(node.
|
|
40
|
+
return this.cloneNode(node, new Identifier(node.identifierName));
|
|
40
41
|
}
|
|
41
42
|
createNodeList(nodes) {
|
|
42
43
|
return new NodeList(-1, -1, nodes);
|
|
@@ -56,7 +57,7 @@ export class NodeFactory extends NodeFactoryBase {
|
|
|
56
57
|
return this.created(new Token(kind));
|
|
57
58
|
}
|
|
58
59
|
createIdentifier(text) {
|
|
59
|
-
return this.created(new Identifier(text));
|
|
60
|
+
return this.created(new Identifier(new IdentifierName(text)));
|
|
60
61
|
}
|
|
61
62
|
createKeywordExpression(kind) {
|
|
62
63
|
return this.created(new KeywordExpression(kind));
|
|
@@ -277,7 +277,7 @@ export class Printer extends NameGeneratorHost {
|
|
|
277
277
|
case 297 /* SyntaxKind.JsxNamespacedName */:
|
|
278
278
|
// Its text either way: the source text, or the text of a node from elsewhere.
|
|
279
279
|
if (this.textIsSourceText(node))
|
|
280
|
-
return node.text;
|
|
280
|
+
return node instanceof Identifier ? node.text : node.text;
|
|
281
281
|
if (!canUseSourceFile || !this.isInCurrentSourceFile(node)) {
|
|
282
282
|
if (node instanceof Identifier)
|
|
283
283
|
return node.text;
|
|
@@ -311,7 +311,8 @@ export class Printer extends NameGeneratorHost {
|
|
|
311
311
|
return false;
|
|
312
312
|
if ((node.flags & (8 /* NodeFlags.Reparsed */ | 4194304 /* NodeFlags.JSDoc */ | 268435456 /* NodeFlags.ReparserTransformedLiteral */)) !== 0)
|
|
313
313
|
return false;
|
|
314
|
-
|
|
314
|
+
// Natively an identifier's text is read through its name, and a private identifier's from its field.
|
|
315
|
+
const text = node instanceof Identifier ? node.text : node.text;
|
|
315
316
|
const source = this.sourceText;
|
|
316
317
|
const end = node.end;
|
|
317
318
|
const start = end - text.length;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { ArrayTypeNode, ArrowFunction, AsExpression, BinaryExpression, CallSignatureDeclaration, ClassDeclaration, ClassExpression, ConstructSignatureDeclaration, ConstructorDeclaration, ConstructorTypeNode, ElementAccessExpression, ExportAssignment, ExpressionStatement, ExpressionWithTypeArguments, FunctionDeclaration, FunctionExpression, FunctionTypeNode, GetAccessorDeclaration, HeritageClause, Identifier, ImportDeclaration, IndexSignatureDeclaration, JSDoc, JSDocAugmentsTag, JSDocCallbackTag, JSDocImplementsTag, JSDocImportTag, JSDocOverloadTag, JSDocParameterOrPropertyTag, JSDocSatisfiesTag, JSDocSignature, JSDocTemplateTag, JSDocThisTag, JSDocTypeExpression, JSDocTypeLiteral, JSDocTypedefTag, JSDocVariadicType, JSDocOptionalType, KeywordTypeNode, MethodDeclaration, MethodSignatureDeclaration, ModifierList, ModuleBlock, ModuleDeclaration, NodeList, NodeVisitor, NumericLiteral, ParameterDeclaration, ParenthesizedExpression, PropertyAccessExpression, PropertyAssignment, PropertyDeclaration, PropertySignatureDeclaration, QualifiedName, ReturnStatement, SatisfiesExpression, SetAccessorDeclaration, ShorthandPropertyAssignment, StringLiteral, Token, TypeAliasDeclaration, TypeLiteralNode, TypeParameterDeclaration, VariableDeclaration, VariableStatement, getComment, getModifiers, getName, getParameters, getText, getType, getTypeExpression, getTypeParameters, FullPropertyAssignment, } from './ast.js';
|
|
10
10
|
import { isAssignmentOperator, isFunctionLike, isInJSFile, isLeftHandSideExpression, isStringLiteralLike, } from './ast-utilities.js';
|
|
11
11
|
import { isIdentifierPart, isIdentifierStart } from './character-codes.js';
|
|
12
|
+
import { IdentifierName } from './identifier-name.js';
|
|
12
13
|
/** Sets the parent of every node in a subtree. */
|
|
13
14
|
class ParentInChildrenSetter extends NodeVisitor {
|
|
14
15
|
parent = undefined;
|
|
@@ -219,7 +220,7 @@ export class Reparser {
|
|
|
219
220
|
const param = params[pi];
|
|
220
221
|
let parameter;
|
|
221
222
|
if (param instanceof JSDocThisTag) {
|
|
222
|
-
const thisIdent = new Identifier('this');
|
|
223
|
+
const thisIdent = new Identifier(new IdentifierName('this'));
|
|
223
224
|
thisIdent.pos = param.pos;
|
|
224
225
|
thisIdent.end = param.end;
|
|
225
226
|
thisIdent.flags = p.contextFlags | 8 /* NodeFlags.Reparsed */;
|
|
@@ -249,7 +250,7 @@ export class Reparser {
|
|
|
249
250
|
let name = param.name;
|
|
250
251
|
if (name instanceof Identifier && !isValidIdentifier(name.text)) {
|
|
251
252
|
// Replace invalid characters with `_`, or use `_N` for an empty name, for a valid parameter name.
|
|
252
|
-
name = this.addTransformedReparse(new Identifier(sanitizeIdentifier(name.text, pi)), name);
|
|
253
|
+
name = this.addTransformedReparse(new Identifier(new IdentifierName(sanitizeIdentifier(name.text, pi))), name);
|
|
253
254
|
}
|
|
254
255
|
else {
|
|
255
256
|
name = this.addDeepCloneReparse(name);
|
|
@@ -433,7 +434,7 @@ export class Reparser {
|
|
|
433
434
|
firstName.kind !== 109 /* SyntaxKind.ThisKeyword */ &&
|
|
434
435
|
!(firstName instanceof Identifier && firstName.text === 'this'))) {
|
|
435
436
|
// tsgo gives the `this` name no location.
|
|
436
|
-
const thisParam = new ParameterDeclaration(undefined, undefined, new Identifier('this'), undefined, undefined, undefined);
|
|
437
|
+
const thisParam = new ParameterDeclaration(undefined, undefined, new Identifier(new IdentifierName('this')), undefined, undefined, undefined);
|
|
437
438
|
thisParam.type = this.addDeepCloneReparse(getType(tag.typeExpression));
|
|
438
439
|
this.finishReparsedNode(thisParam, tag.tagName);
|
|
439
440
|
const newParams = [thisParam];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IdentifierName } from './identifier-name.js';
|
|
1
2
|
import type { DiagnosticMessage } from './diagnostics.js';
|
|
2
3
|
import { EscapeSequenceScanningFlags, RegularExpressionFlags } from './regexp-checker.js';
|
|
3
4
|
import { SyntaxKind } from './syntax-kind.js';
|
|
@@ -60,6 +61,7 @@ export declare class Scanner {
|
|
|
60
61
|
/** @internal */ tokenFlags: TokenFlags;
|
|
61
62
|
/** @internal */ tokenValue: string;
|
|
62
63
|
/** @internal */ valueStart: number;
|
|
64
|
+
private tokenName;
|
|
63
65
|
/** @internal */ valueEnd: number;
|
|
64
66
|
private valueHash;
|
|
65
67
|
private hashStart;
|
|
@@ -68,6 +70,7 @@ export declare class Scanner {
|
|
|
68
70
|
/** @internal */ skipJSDocLeadingAsterisks: number;
|
|
69
71
|
private readonly cutUnits;
|
|
70
72
|
private readonly identifiers;
|
|
73
|
+
private lastEntry;
|
|
71
74
|
private loadedBlocks;
|
|
72
75
|
private textKind;
|
|
73
76
|
private lineFeedFrom;
|
|
@@ -113,6 +116,11 @@ export declare class Scanner {
|
|
|
113
116
|
recordIdentifierValue(text: string): boolean;
|
|
114
117
|
/** @internal Like `getTokenValue`, but identifiers with the same text share one string. */
|
|
115
118
|
getIdentifierValue(): string;
|
|
119
|
+
/**
|
|
120
|
+
* The identifier value as a name that identifiers with the same text share, after
|
|
121
|
+
* `getIdentifierValue`; a cooked value (with escapes) gets a name of its own.
|
|
122
|
+
*/
|
|
123
|
+
getIdentifierName(): IdentifierName;
|
|
116
124
|
getCommentDirectives(): readonly CommentDirective[];
|
|
117
125
|
hasUnicodeEscape(): boolean;
|
|
118
126
|
hasExtendedUnicodeEscape(): boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { charSize, codePointAt, isASCIILetter, isDigit, isHexDigit, isIdentifierPart, isIdentifierStart, isLineBreak, isOctalDigit, isWhiteSpaceLike, isWhiteSpaceSingleLine, } from './character-codes.js';
|
|
2
|
+
import { IdentifierName } from './identifier-name.js';
|
|
2
3
|
import { parsePseudoBigInt } from './pseudo-bigint.js';
|
|
3
4
|
import { checkRegularExpression, regularExpressionFlag, } from './regexp-checker.js';
|
|
4
5
|
import { CommentDirective, CommentRange, } from './types.js';
|
|
@@ -308,58 +309,58 @@ function rangeHash(chars, start, end) {
|
|
|
308
309
|
function finishHash(hash) {
|
|
309
310
|
return hash ^ (hash >>> 15);
|
|
310
311
|
}
|
|
311
|
-
/**
|
|
312
|
+
/**
|
|
313
|
+
* Names for ranges of one text, keyed by their code units: equal identifiers share one
|
|
314
|
+
* `IdentifierName`.
|
|
315
|
+
*/
|
|
312
316
|
class IdentifierTable {
|
|
313
|
-
text;
|
|
314
317
|
// Pairs of (entry index + 1, hash) per slot, (0, 0) when empty; linear probing.
|
|
315
318
|
slots;
|
|
316
319
|
// Each entry's length in code units.
|
|
317
320
|
lengths;
|
|
318
|
-
|
|
321
|
+
names;
|
|
319
322
|
count;
|
|
320
|
-
// Per entry: 1 once its
|
|
323
|
+
// Per entry: 1 once its name was recorded among the file's identifier texts (`markRecorded`).
|
|
321
324
|
recorded;
|
|
322
|
-
|
|
323
|
-
lastEntry;
|
|
324
|
-
constructor(text) {
|
|
325
|
-
this.text = text;
|
|
325
|
+
constructor() {
|
|
326
326
|
this.slots = new Int32Array(512);
|
|
327
327
|
this.lengths = new Int32Array(128);
|
|
328
|
-
this.
|
|
328
|
+
this.names = [];
|
|
329
329
|
this.count = 0;
|
|
330
330
|
this.recorded = new Uint8Array(128);
|
|
331
|
-
|
|
331
|
+
}
|
|
332
|
+
/** Empties the table for a new text. */
|
|
333
|
+
clear() {
|
|
334
|
+
if (this.count === 0)
|
|
335
|
+
return;
|
|
336
|
+
this.slots.fill(0);
|
|
337
|
+
this.names.length = 0;
|
|
338
|
+
this.recorded.fill(0, 0, this.count);
|
|
339
|
+
this.count = 0;
|
|
332
340
|
}
|
|
333
341
|
/**
|
|
334
|
-
* Whether `text`,
|
|
335
|
-
* texts; marks it recorded. Texts the table doesn't know are
|
|
342
|
+
* Whether `text`, the text of the entry `entry` that `intern` returned (-1 for none), still needs
|
|
343
|
+
* recording among the file's identifier texts; marks it recorded. Texts the table doesn't know are
|
|
344
|
+
* always recorded.
|
|
336
345
|
*/
|
|
337
|
-
markRecorded(text) {
|
|
338
|
-
|
|
339
|
-
if (entry < 0 || this.texts[entry] !== text)
|
|
346
|
+
markRecorded(entry, text) {
|
|
347
|
+
if (entry < 0 || this.names[entry].text !== text)
|
|
340
348
|
return true;
|
|
341
349
|
if (this.recorded[entry] !== 0)
|
|
342
350
|
return false;
|
|
343
351
|
this.recorded[entry] = 1;
|
|
344
352
|
return true;
|
|
345
353
|
}
|
|
346
|
-
/**
|
|
347
|
-
|
|
348
|
-
this.
|
|
349
|
-
if (this.count === 0)
|
|
350
|
-
return;
|
|
351
|
-
this.slots.fill(0);
|
|
352
|
-
this.recorded.fill(0, 0, this.count);
|
|
353
|
-
this.texts.length = 0;
|
|
354
|
-
this.count = 0;
|
|
355
|
-
this.lastEntry = -1;
|
|
354
|
+
/** The name of the entry `intern` returned. */
|
|
355
|
+
name(entry) {
|
|
356
|
+
return this.names[entry];
|
|
356
357
|
}
|
|
357
358
|
/**
|
|
358
|
-
* `text.substring(start, end)`,
|
|
359
|
-
* A candidate of the same hash and length is compared with `startsWith`, which natively
|
|
360
|
-
* the bytes of both strings at once.
|
|
359
|
+
* The entry of `text.substring(start, end)`, added when the table has no equal range; `hash` is its
|
|
360
|
+
* `rangeHash`. A candidate of the same hash and length is compared with `startsWith`, which natively
|
|
361
|
+
* compares the bytes of both strings at once.
|
|
361
362
|
*/
|
|
362
|
-
intern(start, end, hash) {
|
|
363
|
+
intern(text, start, end, hash) {
|
|
363
364
|
start = start | 0;
|
|
364
365
|
end = end | 0;
|
|
365
366
|
hash = hash | 0;
|
|
@@ -372,15 +373,11 @@ class IdentifierTable {
|
|
|
372
373
|
if (entry === 0)
|
|
373
374
|
break;
|
|
374
375
|
if ((slots[(slot * 2 + 1) | 0] | 0) === hash && (this.lengths[(entry - 1) | 0] | 0) === length) {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
this.lastEntry = (entry - 1) | 0;
|
|
378
|
-
return candidate;
|
|
379
|
-
}
|
|
376
|
+
if (text.startsWith(this.names[(entry - 1) | 0].text, start))
|
|
377
|
+
return (entry - 1) | 0;
|
|
380
378
|
}
|
|
381
379
|
slot = (slot + 1) & mask;
|
|
382
380
|
}
|
|
383
|
-
const value = this.text.substring(start, end);
|
|
384
381
|
const index = this.count | 0;
|
|
385
382
|
if (index === this.lengths.length) {
|
|
386
383
|
const lengths = new Int32Array(index * 2);
|
|
@@ -391,14 +388,13 @@ class IdentifierTable {
|
|
|
391
388
|
this.recorded = recorded;
|
|
392
389
|
}
|
|
393
390
|
this.lengths[index] = length;
|
|
394
|
-
this.
|
|
391
|
+
this.names.push(new IdentifierName(text.substring(start, end)));
|
|
395
392
|
this.count = (index + 1) | 0;
|
|
396
|
-
this.lastEntry = index;
|
|
397
393
|
slots[(slot * 2) | 0] = (index + 1) | 0;
|
|
398
394
|
slots[(slot * 2 + 1) | 0] = hash;
|
|
399
395
|
if (this.count * 2 > mask + 1)
|
|
400
396
|
this.slots = rehash(slots);
|
|
401
|
-
return
|
|
397
|
+
return index;
|
|
402
398
|
}
|
|
403
399
|
}
|
|
404
400
|
/** `IdentifierTable` slots in a table twice the size. */
|
|
@@ -546,6 +542,8 @@ export class Scanner {
|
|
|
546
542
|
// so identifiers and keywords don't allocate until their value is read.
|
|
547
543
|
/** @internal */ tokenValue;
|
|
548
544
|
/** @internal */ valueStart;
|
|
545
|
+
// The name `getIdentifierValue` last interned, whose text `tokenValue` then holds.
|
|
546
|
+
tokenName;
|
|
549
547
|
/** @internal */ valueEnd;
|
|
550
548
|
// `rangeHash` of `chars[hashStart, hashEnd)`, which the ASCII identifier scan computes as it goes.
|
|
551
549
|
valueHash;
|
|
@@ -556,6 +554,8 @@ export class Scanner {
|
|
|
556
554
|
// The code units zeroed by `setTextEnd`.
|
|
557
555
|
cutUnits;
|
|
558
556
|
identifiers;
|
|
557
|
+
// The entry `getIdentifierValue` last interned, -1 for none.
|
|
558
|
+
lastEntry;
|
|
559
559
|
// With code units on demand: per block of `unitBlockSize` code units, 1 once `chars` holds them
|
|
560
560
|
// (`loadUnits`); `chars` is zero elsewhere.
|
|
561
561
|
loadedBlocks;
|
|
@@ -594,6 +594,7 @@ export class Scanner {
|
|
|
594
594
|
this.tokenFlags = 0 /* TokenFlags.None */;
|
|
595
595
|
this.tokenValue = '';
|
|
596
596
|
this.valueStart = -1;
|
|
597
|
+
this.tokenName = undefined;
|
|
597
598
|
this.valueEnd = -1;
|
|
598
599
|
this.valueHash = 0;
|
|
599
600
|
this.hashStart = -1;
|
|
@@ -601,7 +602,8 @@ export class Scanner {
|
|
|
601
602
|
this.commentDirectives = [];
|
|
602
603
|
this.skipJSDocLeadingAsterisks = 0;
|
|
603
604
|
this.cutUnits = new Uint16Array(4);
|
|
604
|
-
this.identifiers = new IdentifierTable(
|
|
605
|
+
this.identifiers = new IdentifierTable();
|
|
606
|
+
this.lastEntry = -1;
|
|
605
607
|
this.textKind = 0 /* TextKind.Unknown */;
|
|
606
608
|
this.lineFeedFrom = -1;
|
|
607
609
|
this.lineFeedAt = -1;
|
|
@@ -613,7 +615,8 @@ export class Scanner {
|
|
|
613
615
|
this.text = text;
|
|
614
616
|
this.chars = toCodeUnits(text);
|
|
615
617
|
this.loadedBlocks = undefined;
|
|
616
|
-
this.identifiers.
|
|
618
|
+
this.identifiers.clear();
|
|
619
|
+
this.lastEntry = -1;
|
|
617
620
|
this.textKind = 0 /* TextKind.Unknown */;
|
|
618
621
|
this.lineFeedFrom = -1;
|
|
619
622
|
this.lineFeedAt = -1;
|
|
@@ -737,7 +740,7 @@ export class Scanner {
|
|
|
737
740
|
* file to be recorded; files record each identifier text once.
|
|
738
741
|
*/
|
|
739
742
|
recordIdentifierValue(text) {
|
|
740
|
-
return this.identifiers.markRecorded(text);
|
|
743
|
+
return this.identifiers.markRecorded(this.lastEntry, text);
|
|
741
744
|
}
|
|
742
745
|
/** @internal Like `getTokenValue`, but identifiers with the same text share one string. */
|
|
743
746
|
getIdentifierValue() {
|
|
@@ -745,11 +748,24 @@ export class Scanner {
|
|
|
745
748
|
if (start >= 0) {
|
|
746
749
|
const end = this.valueEnd;
|
|
747
750
|
const hash = start === this.hashStart && end === this.hashEnd ? this.valueHash : rangeHash(this.chars, start, end);
|
|
748
|
-
|
|
751
|
+
const entry = this.identifiers.intern(this.text, start, end, hash);
|
|
752
|
+
this.lastEntry = entry;
|
|
753
|
+
this.tokenName = this.identifiers.name(entry);
|
|
754
|
+
this.tokenValue = this.tokenName.text;
|
|
749
755
|
this.valueStart = -1;
|
|
750
756
|
}
|
|
751
757
|
return this.tokenValue;
|
|
752
758
|
}
|
|
759
|
+
/**
|
|
760
|
+
* The identifier value as a name that identifiers with the same text share, after
|
|
761
|
+
* `getIdentifierValue`; a cooked value (with escapes) gets a name of its own.
|
|
762
|
+
*/
|
|
763
|
+
getIdentifierName() {
|
|
764
|
+
const name = this.tokenName;
|
|
765
|
+
if (name !== undefined && name.text === this.tokenValue)
|
|
766
|
+
return name;
|
|
767
|
+
return new IdentifierName(this.tokenValue);
|
|
768
|
+
}
|
|
753
769
|
getCommentDirectives() {
|
|
754
770
|
return this.commentDirectives;
|
|
755
771
|
}
|