diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5cefd7aa08717..38a287ae75caf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1416,9 +1416,10 @@ const SymbolLinks = class implements SymbolLinks { declare _symbolLinksBrand: any; }; -function NodeLinks(this: NodeLinks) { - this.flags = NodeCheckFlags.None; -} +const NodeLinks = class { + flags = NodeCheckFlags.None; + calculatedFlags = NodeCheckFlags.None; +}; /** @internal */ export function getNodeId(node: Node): number { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 4a9918c9671e9..7fe062f10bb1c 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -97,6 +97,7 @@ import { ParsedCommandLine, parseJsonText, Path, + PluginImport, PollingWatchKind, PrefixUnaryExpression, ProjectReference, diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index e059afd814fd9..100e400d7bb3e 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -327,6 +327,7 @@ import { NodeArray, NodeFactory, NodeFlags, + NodeImpl, nodeIsSynthesized, NonNullChain, NonNullExpression, @@ -6077,8 +6078,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode } function createRedirectedSourceFile(redirectInfo: RedirectInfo) { - const node: SourceFile = Object.create(redirectInfo.redirectTarget); - Object.defineProperties(node, { + const nodeData: any = Object.create((redirectInfo.redirectTarget as any as NodeImpl).data); + Object.defineProperties(nodeData, { id: { get(this: SourceFile) { return this.redirectInfo!.redirectTarget.id; @@ -6095,9 +6096,13 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode this.redirectInfo!.redirectTarget.symbol = value; }, }, + redirectInfo: { + value: redirectInfo, + }, }); - node.redirectInfo = redirectInfo; - return node; + const sourceFile = baseFactory.createBaseSourceFileNode(SyntaxKind.SourceFile); + (sourceFile as NodeImpl).data = nodeData; + return sourceFile as SourceFile; } function cloneRedirectedSourceFile(source: SourceFile) { @@ -6118,7 +6123,6 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode // work, we should consider switching explicit property assignments instead of using `for..in`. const node = baseFactory.createBaseSourceFileNode(SyntaxKind.SourceFile) as Mutable; node.flags |= source.flags & ~NodeFlags.Synthesized; - copyBaseNodeProperties(source, node); const sourceData = (source as any).data ?? source; const nodeData = (node as any).data ?? node; for (const p in sourceData) { @@ -6126,10 +6130,10 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode continue; } if (p === "emitNode") { - nodeData.emitNode = undefined; + node.emitNode = undefined; continue; } - nodeData[p] = sourceData[p]; + (node as any)[p] = sourceData[p]; } return node; } @@ -6379,28 +6383,16 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode (clone as Mutable).flags |= node.flags & ~NodeFlags.Synthesized; (clone as Mutable).transformFlags = node.transformFlags; setOriginal(clone, node); - copyBaseNodeProperties(node, clone); const nodeData = (node as any).data ?? node; const cloneData = (clone as any).data ?? clone; for (const key in nodeData) { if (hasProperty(cloneData, key) || !hasProperty(nodeData, key)) { continue; } - cloneData[key] = nodeData[key]; + (clone as any)[key] = nodeData[key]; } return clone; } - function copyBaseNodeProperties(node: Node, clone: Mutable) { - clone.pos = node.pos; - clone.end = node.end; - clone.kind = node.kind; - clone.id = node.id; - clone.modifierFlagsCache = node.modifierFlagsCache; - clone.transformFlags = node.transformFlags; - clone.parent = node.parent; - clone.original = node.original; - clone.emitNode = node.emitNode; - } // compound nodes function createImmediatelyInvokedFunctionExpression(statements: readonly Statement[]): ImmediatelyInvokedFunctionExpression; function createImmediatelyInvokedFunctionExpression(statements: readonly Statement[], param: ParameterDeclaration, paramValue: Expression): ImmediatelyInvokedFunctionExpression; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b2318cd65876e..88bb3621f583c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -546,7 +546,6 @@ import { TokenFlags, tokenToString, toPath, - tracing, TransformFlags, TransientSymbol, TriviaSyntaxKind, @@ -563,6 +562,7 @@ import { TypeElement, TypeFlags, TypeLiteralNode, + TypeMapper, TypeNode, TypeNodeSyntaxKind, TypeParameter, @@ -8286,41 +8286,882 @@ function Symbol(this: Symbol, flags: SymbolFlags, name: __String) { (this as any).links = undefined; // used by TransientSymbol } -function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { - // Note: if modifying this, be sure to update TypeObject in src/services/services.ts - this.flags = flags; - if (Debug.isDebugging || tracing) { +class TypeDataImpl { +} +/** @internal */ +export class TypeImpl { + checker: TypeChecker; + flags: TypeFlags; + symbol: Symbol; + _data: any; + id: number; + objectFlags: number; + constructor(checker: TypeChecker, flags: TypeFlags) { + this.id = 0; this.checker = checker; + this.flags = flags; + this.symbol = undefined!; + this._data = undefined; + this.objectFlags = 0; } -} -function Signature(this: Signature, checker: TypeChecker, flags: SignatureFlags) { - // Note: if modifying this, be sure to update SignatureObject in src/services/services.ts - this.flags = flags; - if (Debug.isDebugging) { - this.checker = checker; + get data() { + return this._data ??= new TypeDataImpl(); + } + get pattern() { + return this._data?.pattern; + } + set pattern(value: any) { + this.data.pattern = value; + } + + get aliasSymbol() { + return this._data?.aliasSymbol; + } + set aliasSymbol(value: any) { + this.data.aliasSymbol = value; + } + + get aliasTypeArguments() { + return this._data?.aliasTypeArguments; + } + set aliasTypeArguments(value: any) { + this.data.aliasTypeArguments = value; + } + + get permissiveInstantiation() { + return this._data?.permissiveInstantiation; + } + set permissiveInstantiation(value: any) { + this.data.permissiveInstantiation = value; + } + + get restrictiveInstantiation() { + return this._data?.restrictiveInstantiation; + } + set restrictiveInstantiation(value: any) { + this.data.restrictiveInstantiation = value; + } + + get immediateBaseConstraint() { + return this._data?.immediateBaseConstraint; + } + set immediateBaseConstraint(value: any) { + this.data.immediateBaseConstraint = value; + } + + get widened() { + return this._data?.widened; + } + set widened(value: any) { + this.data.widened = value; + } + + get intrinsicName() { + return this._data?.intrinsicName; + } + set intrinsicName(value: any) { + this.data.intrinsicName = value; + } + + get debugIntrinsicName() { + return this._data?.debugIntrinsicName; + } + set debugIntrinsicName(value: any) { + this.data.debugIntrinsicName = value; + } + + // get objectFlags() { return this._data?.objectFlags; } + // set objectFlags(value: any) { this.data.objectFlags = value } + + get freshType() { + return this._data?.freshType; + } + set freshType(value: any) { + this.data.freshType = value; + } + + get regularType() { + return this._data?.regularType; + } + set regularType(value: any) { + this.data.regularType = value; + } + + get value() { + return this._data?.value; + } + set value(value: any) { + this.data.value = value; + } + + get escapedName() { + return this._data?.escapedName; + } + set escapedName(value: any) { + this.data.escapedName = value; + } + + get members() { + return this._data?.members; + } + set members(value: any) { + this.data.members = value; + } + + get properties() { + return this._data?.properties; + } + set properties(value: any) { + this.data.properties = value; + } + + get callSignatures() { + return this._data?.callSignatures; + } + set callSignatures(value: any) { + this.data.callSignatures = value; + } + + get constructSignatures() { + return this._data?.constructSignatures; + } + set constructSignatures(value: any) { + this.data.constructSignatures = value; + } + + get indexInfos() { + return this._data?.indexInfos; + } + set indexInfos(value: any) { + this.data.indexInfos = value; + } + + get objectTypeWithoutAbstractConstructSignatures() { + return this._data?.objectTypeWithoutAbstractConstructSignatures; + } + set objectTypeWithoutAbstractConstructSignatures(value: any) { + this.data.objectTypeWithoutAbstractConstructSignatures = value; + } + + get typeParameters() { + return this._data?.typeParameters; + } + set typeParameters(value: any) { + this.data.typeParameters = value; + } + + get outerTypeParameters() { + return this._data?.outerTypeParameters; + } + set outerTypeParameters(value: any) { + this.data.outerTypeParameters = value; + } + + get localTypeParameters() { + return this._data?.localTypeParameters; + } + set localTypeParameters(value: any) { + this.data.localTypeParameters = value; + } + + get thisType() { + return this._data?.thisType; + } + set thisType(value: any) { + this.data.thisType = value; + } + + get resolvedBaseConstructorType() { + return this._data?.resolvedBaseConstructorType; + } + set resolvedBaseConstructorType(value: any) { + this.data.resolvedBaseConstructorType = value; + } + + get resolvedBaseTypes() { + return this._data?.resolvedBaseTypes; + } + set resolvedBaseTypes(value: any) { + this.data.resolvedBaseTypes = value; + } + + get baseTypesResolved() { + return this._data?.baseTypesResolved; + } + set baseTypesResolved(value: any) { + this.data.baseTypesResolved = value; + } + + get declaredProperties() { + return this._data?.declaredProperties; + } + set declaredProperties(value: any) { + this.data.declaredProperties = value; + } + + get declaredCallSignatures() { + return this._data?.declaredCallSignatures; + } + set declaredCallSignatures(value: any) { + this.data.declaredCallSignatures = value; + } + + get declaredConstructSignatures() { + return this._data?.declaredConstructSignatures; + } + set declaredConstructSignatures(value: any) { + this.data.declaredConstructSignatures = value; + } + + get declaredIndexInfos() { + return this._data?.declaredIndexInfos; + } + set declaredIndexInfos(value: any) { + this.data.declaredIndexInfos = value; + } + + get instantiations() { + return this._data?.instantiations; + } + set instantiations(value: any) { + this.data.instantiations = value; + } + + get variances() { + return this._data?.variances; + } + set variances(value: any) { + this.data.variances = value; + } + + get target() { + return this._data?.target; + } + set target(value: any) { + this.data.target = value; + } + + get node() { + return this._data?.node; + } + set node(value: any) { + this.data.node = value; + } + + get mapper() { + return this._data?.mapper; + } + set mapper(value: any) { + this.data.mapper = value; + } + + get resolvedTypeArguments() { + return this._data?.resolvedTypeArguments; + } + set resolvedTypeArguments(value: any) { + this.data.resolvedTypeArguments = value; + } + + get literalType() { + return this._data?.literalType; + } + set literalType(value: any) { + this.data.literalType = value; + } + + get cachedEquivalentBaseType() { + return this._data?.cachedEquivalentBaseType; + } + set cachedEquivalentBaseType(value: any) { + this.data.cachedEquivalentBaseType = value; + } + + get elementFlags() { + return this._data?.elementFlags; + } + set elementFlags(value: any) { + this.data.elementFlags = value; + } + + get minLength() { + return this._data?.minLength; + } + set minLength(value: any) { + this.data.minLength = value; + } + + get fixedLength() { + return this._data?.fixedLength; + } + set fixedLength(value: any) { + this.data.fixedLength = value; + } + + get hasRestElement() { + return this._data?.hasRestElement; + } + set hasRestElement(value: any) { + this.data.hasRestElement = value; + } + + get combinedFlags() { + return this._data?.combinedFlags; + } + set combinedFlags(value: any) { + this.data.combinedFlags = value; + } + + get readonly() { + return this._data?.readonly; + } + set readonly(value: any) { + this.data.readonly = value; + } + + get labeledElementDeclarations() { + return this._data?.labeledElementDeclarations; + } + set labeledElementDeclarations(value: any) { + this.data.labeledElementDeclarations = value; + } + + get declaration() { + return this._data?.declaration; + } + set declaration(value: any) { + this.data.declaration = value; + } + + get typeParameter() { + return this._data?.typeParameter; + } + set typeParameter(value: any) { + this.data.typeParameter = value; + } + + get constraintType() { + return this._data?.constraintType; + } + set constraintType(value: any) { + this.data.constraintType = value; + } + + get nameType() { + return this._data?.nameType; + } + set nameType(value: any) { + this.data.nameType = value; + } + + get templateType() { + return this._data?.templateType; + } + set templateType(value: any) { + this.data.templateType = value; + } + + get modifiersType() { + return this._data?.modifiersType; + } + set modifiersType(value: any) { + this.data.modifiersType = value; + } + + get resolvedApparentType() { + return this._data?.resolvedApparentType; + } + set resolvedApparentType(value: any) { + this.data.resolvedApparentType = value; + } + + get containsError() { + return this._data?.containsError; + } + set containsError(value: any) { + this.data.containsError = value; + } + + get elementType() { + return this._data?.elementType; + } + set elementType(value: any) { + this.data.elementType = value; + } + + get finalArrayType() { + return this._data?.finalArrayType; + } + set finalArrayType(value: any) { + this.data.finalArrayType = value; + } + + get source() { + return this._data?.source; + } + set source(value: any) { + this.data.source = value; + } + + get mappedType() { + return this._data?.mappedType; } + set mappedType(value: any) { + this.data.mappedType = value; + } + + get types() { + return this._data?.types; + } + set types(value: any) { + this.data.types = value; + } + + get propertyCache() { + return this._data?.propertyCache; + } + set propertyCache(value: any) { + this.data.propertyCache = value; + } + + get propertyCacheWithoutObjectFunctionPropertyAugment() { + return this._data?.propertyCacheWithoutObjectFunctionPropertyAugment; + } + set propertyCacheWithoutObjectFunctionPropertyAugment(value: any) { + this.data.propertyCacheWithoutObjectFunctionPropertyAugment = value; + } + + get resolvedProperties() { + return this._data?.resolvedProperties; + } + set resolvedProperties(value: any) { + this.data.resolvedProperties = value; + } + + get resolvedIndexType() { + return this._data?.resolvedIndexType; + } + set resolvedIndexType(value: any) { + this.data.resolvedIndexType = value; + } + + get resolvedStringIndexType() { + return this._data?.resolvedStringIndexType; + } + set resolvedStringIndexType(value: any) { + this.data.resolvedStringIndexType = value; + } + + get resolvedBaseConstraint() { + return this._data?.resolvedBaseConstraint; + } + set resolvedBaseConstraint(value: any) { + this.data.resolvedBaseConstraint = value; + } + + get iterationTypesOfGeneratorReturnType() { + return this._data?.iterationTypesOfGeneratorReturnType; + } + set iterationTypesOfGeneratorReturnType(value: any) { + this.data.iterationTypesOfGeneratorReturnType = value; + } + + get iterationTypesOfAsyncGeneratorReturnType() { + return this._data?.iterationTypesOfAsyncGeneratorReturnType; + } + set iterationTypesOfAsyncGeneratorReturnType(value: any) { + this.data.iterationTypesOfAsyncGeneratorReturnType = value; + } + + get iterationTypesOfIterable() { + return this._data?.iterationTypesOfIterable; + } + set iterationTypesOfIterable(value: any) { + this.data.iterationTypesOfIterable = value; + } + + get iterationTypesOfIterator() { + return this._data?.iterationTypesOfIterator; + } + set iterationTypesOfIterator(value: any) { + this.data.iterationTypesOfIterator = value; + } + + get iterationTypesOfAsyncIterable() { + return this._data?.iterationTypesOfAsyncIterable; + } + set iterationTypesOfAsyncIterable(value: any) { + this.data.iterationTypesOfAsyncIterable = value; + } + + get iterationTypesOfAsyncIterator() { + return this._data?.iterationTypesOfAsyncIterator; + } + set iterationTypesOfAsyncIterator(value: any) { + this.data.iterationTypesOfAsyncIterator = value; + } + + get iterationTypesOfIteratorResult() { + return this._data?.iterationTypesOfIteratorResult; + } + set iterationTypesOfIteratorResult(value: any) { + this.data.iterationTypesOfIteratorResult = value; + } + + get resolvedReducedType() { + return this._data?.resolvedReducedType; + } + set resolvedReducedType(value: any) { + this.data.resolvedReducedType = value; + } + + get origin() { + return this._data?.origin; + } + set origin(value: any) { + this.data.origin = value; + } + + get keyPropertyName() { + return this._data?.keyPropertyName; + } + set keyPropertyName(value: any) { + this.data.keyPropertyName = value; + } + + get constituentMap() { + return this._data?.constituentMap; + } + set constituentMap(value: any) { + this.data.constituentMap = value; + } + + get arrayFallbackSignatures() { + return this._data?.arrayFallbackSignatures; + } + set arrayFallbackSignatures(value: any) { + this.data.arrayFallbackSignatures = value; + } + + get promiseTypeOfPromiseConstructor() { + return this._data?.promiseTypeOfPromiseConstructor; + } + set promiseTypeOfPromiseConstructor(value: any) { + this.data.promiseTypeOfPromiseConstructor = value; + } + + get promisedTypeOfPromise() { + return this._data?.promisedTypeOfPromise; + } + set promisedTypeOfPromise(value: any) { + this.data.promisedTypeOfPromise = value; + } + + get awaitedTypeOfType() { + return this._data?.awaitedTypeOfType; + } + set awaitedTypeOfType(value: any) { + this.data.awaitedTypeOfType = value; + } + + get uniqueLiteralFilledInstantiation() { + return this._data?.uniqueLiteralFilledInstantiation; + } + set uniqueLiteralFilledInstantiation(value: any) { + this.data.uniqueLiteralFilledInstantiation = value; + } + + get syntheticType() { + return this._data?.syntheticType; + } + set syntheticType(value: any) { + this.data.syntheticType = value; + } + + get defaultOnlyType() { + return this._data?.defaultOnlyType; + } + set defaultOnlyType(value: any) { + this.data.defaultOnlyType = value; + } + + get constraint() { + return this._data?.constraint; + } + set constraint(value: any) { + this.data.constraint = value; + } + + get default() { + return this._data?.default; + } + set default(value: any) { + this.data.default = value; + } + + get isThisType() { + return this._data?.isThisType; + } + set isThisType(value: any) { + this.data.isThisType = value; + } + + get resolvedDefaultType() { + return this._data?.resolvedDefaultType; + } + set resolvedDefaultType(value: any) { + this.data.resolvedDefaultType = value; + } + + get objectType() { + return this._data?.objectType; + } + set objectType(value: any) { + this.data.objectType = value; + } + + get indexType() { + return this._data?.indexType; + } + set indexType(value: any) { + this.data.indexType = value; + } + + get accessFlags() { + return this._data?.accessFlags; + } + set accessFlags(value: any) { + this.data.accessFlags = value; + } + + get simplifiedForReading() { + return this._data?.simplifiedForReading; + } + set simplifiedForReading(value: any) { + this.data.simplifiedForReading = value; + } + + get simplifiedForWriting() { + return this._data?.simplifiedForWriting; + } + set simplifiedForWriting(value: any) { + this.data.simplifiedForWriting = value; + } + + get type() { + return this._data?.type; + } + set type(value: any) { + this.data.type = value; + } + + get indexFlags() { + return this._data?.indexFlags; + } + set indexFlags(value: any) { + this.data.indexFlags = value; + } + + get root() { + return this._data?.root; + } + set root(value: any) { + this.data.root = value; + } + + get checkType() { + return this._data?.checkType; + } + set checkType(value: any) { + this.data.checkType = value; + } + + get extendsType() { + return this._data?.extendsType; + } + set extendsType(value: any) { + this.data.extendsType = value; + } + + get resolvedTrueType() { + return this._data?.resolvedTrueType; + } + set resolvedTrueType(value: any) { + this.data.resolvedTrueType = value; + } + + get resolvedFalseType() { + return this._data?.resolvedFalseType; + } + set resolvedFalseType(value: any) { + this.data.resolvedFalseType = value; + } + + get resolvedInferredTrueType() { + return this._data?.resolvedInferredTrueType; + } + set resolvedInferredTrueType(value: any) { + this.data.resolvedInferredTrueType = value; + } + + get resolvedDefaultConstraint() { + return this._data?.resolvedDefaultConstraint; + } + set resolvedDefaultConstraint(value: any) { + this.data.resolvedDefaultConstraint = value; + } + + get resolvedConstraintOfDistributive() { + return this._data?.resolvedConstraintOfDistributive; + } + set resolvedConstraintOfDistributive(value: any) { + this.data.resolvedConstraintOfDistributive = value; + } + + get combinedMapper() { + return this._data?.combinedMapper; + } + set combinedMapper(value: any) { + this.data.combinedMapper = value; + } + + get texts() { + return this._data?.texts; + } + set texts(value: any) { + this.data.texts = value; + } + + get baseType() { + return this._data?.baseType; + } + set baseType(value: any) { + this.data.baseType = value; + } +} +// function Type(this: Type, checker: TypeChecker, flags: TypeFlags) { +// // Note: if modifying this, be sure to update TypeObject in src/services/services.ts +// this.flags = flags; +// if (Debug.isDebugging || tracing) { +// this.checker = checker; +// } +// } +class SignatureDataImpl { } +/** @internal */ +export class SignatureImpl { + flags: SignatureFlags; + checker!: TypeChecker; + declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration + typeParameters?: readonly TypeParameter[]; // Type parameters (undefined if non-generic) + parameters: readonly Symbol[]; // Parameters + thisParameter?: Symbol; // symbol of this-type parameter + resolvedReturnType?: Type; // Lazily set by `getReturnTypeOfSignature`. + resolvedTypePredicate?: TypePredicate; + minArgumentCount: number; // Number of non-optional parameters + resolvedMinArgumentCount?: number; // Number of non-optional parameters (excluding trailing `void`) + target?: Signature; // Instantiation target + mapper?: TypeMapper; // Instantiation mapper + compositeSignatures?: Signature[]; // Underlying signatures of a union/intersection signature + compositeKind?: TypeFlags; // TypeFlags.Union if the underlying signatures are from union members, otherwise TypeFlags.Intersection + + constructor(checker: TypeChecker, flags: SignatureFlags) { + // Note: if modifying this, be sure to update SignatureObject in src/services/services.ts + this.flags = flags; + if (Debug.isDebugging) { + this.checker = checker; + } + this.declaration = undefined; + this.typeParameters = undefined; + this.parameters = undefined!; + this.thisParameter = undefined; + this.resolvedReturnType = undefined; + this.resolvedTypePredicate = undefined; + this.minArgumentCount = undefined!; + this.resolvedMinArgumentCount = undefined; + this.target = undefined; + this.mapper = undefined; + this.compositeSignatures = undefined; + this.compositeKind = undefined; + } -class NodeImpl implements Node { - pos; - end; - kind; - id = 0; - flags = NodeFlags.None; + _data: any = undefined; + get data() { + return this._data ??= new SignatureDataImpl(); + } + get erasedSignatureCache() { + return this._data?.erasedSignatureCache; + } + set erasedSignatureCache(value: any) { + this.data.erasedSignatureCache = value; + } + get canonicalSignatureCache() { + return this._data?.canonicalSignatureCache; + } + set canonicalSignatureCache(value: any) { + this.data.canonicalSignatureCache = value; + } + get baseSignatureCache() { + return this._data?.baseSignatureCache; + } + set baseSignatureCache(value: any) { + this.data.baseSignatureCache = value; + } + get optionalCallSignatureCache() { + return this._data?.optionalCallSignatureCache; + } + set optionalCallSignatureCache(value: any) { + this.data.optionalCallSignatureCache = value; + } + get isolatedSignatureType() { + return this._data?.isolatedSignatureType; + } + set isolatedSignatureType(value: any) { + this.data.isolatedSignatureType = value; + } + get instantiations() { + return this._data?.instantiations; + } + set instantiations(value: any) { + this.data.instantiations = value; + } + get implementationSignatureCache() { + return this._data?.implementationSignatureCache; + } + set implementationSignatureCache(value: any) { + this.data.implementationSignatureCache = value; + } +} +class NodeDataImpl { +} +/** @internal */ +export class NodeImpl { + pos; // Node, Token, Identifier + end; // Node, Token, Identifier + kind; // Node, Token, Identifier + id = 0; // Node, Token, Identifier + flags = NodeFlags.None; // Node, Token, Identifier + transformFlags = TransformFlags.None; // Node, Token, Identifier + parent: Node = undefined!; // Node, Token, Identifier + original = undefined; // Node, Identifier + emitNode = undefined; // Node, Token, Identifier modifierFlagsCache = ModifierFlags.None; - transformFlags = TransformFlags.None; - parent = undefined!; - original = undefined; - emitNode = undefined; constructor(kind: SyntaxKind, pos: number, end: number) { + this.kind = kind; this.pos = pos; this.end = end; - this.kind = kind; - this.data = {}; + this.data = new NodeDataImpl(); } + data: any; + get sourceText() { + return this.data.sourceText; + } + set sourceText(value: any) { + this.data.sourceText = value; + } + get jsDoc() { return this.data.jsDoc; } @@ -8328,6 +9169,9 @@ class NodeImpl implements Node { this.data.jsDoc = value; } + // get modifierFlagsCache() { return this.data.modifierFlagsCache; } + // set modifierFlagsCache(value: any) { this.data.modifierFlagsCache = value } + get escapedText() { return this.data.escapedText; } @@ -8371,7 +9215,7 @@ class NodeImpl implements Node { } get name() { - return this.data.name; + return this.data?.name; } set name(value: any) { this.data.name = value; @@ -9656,8 +10500,8 @@ export const objectAllocator: ObjectAllocator = { getPrivateIdentifierConstructor: () => NodeImpl as any, getSourceFileConstructor: () => NodeImpl as any, getSymbolConstructor: () => Symbol as any, - getTypeConstructor: () => Type as any, - getSignatureConstructor: () => Signature as any, + getTypeConstructor: () => TypeImpl as any, + getSignatureConstructor: () => SignatureImpl as any, getSourceMapSourceConstructor: () => SourceMapSource as any, }; diff --git a/src/harness/harnessUtils.ts b/src/harness/harnessUtils.ts index d7989da8d6980..66de3bcec4b5a 100644 --- a/src/harness/harnessUtils.ts +++ b/src/harness/harnessUtils.ts @@ -177,13 +177,43 @@ export function sourceFileToJSON(file: ts.Node): string { return ts.Debug.formatNodeFlags(f); } + function addSerializableFlags(o: any, n: ts.Node) { + // Clear the flags that are produced by aggregating child values. That is ephemeral + // data we don't care about in the dump. We only care what the parser set directly + // on the AST. + let flags = n.flags & ~(ts.NodeFlags.JavaScriptFile | ts.NodeFlags.HasAggregatedChildData); + if (ts.isIdentifier(n)) { + if (flags & ts.NodeFlags.IdentifierHasExtendedUnicodeEscape) { + o.hasExtendedUnicodeEscape = true; + flags &= ~ts.NodeFlags.IdentifierHasExtendedUnicodeEscape; + } + } + if (flags) { + o.flags = getNodeFlagName(flags); + } + } function serializeNode(n: ts.Node): any { const o: any = { kind: getKindName(n.kind) }; if (ts.containsParseError(n)) { o.containsParseError = true; } + let nodeData; + if (n instanceof ts.NodeImpl) { + nodeData = (n as any as ts.NodeImpl).data; + o.pos = n.pos; + o.end = n.end; + addSerializableFlags(o, n); + if (ts.isNodeKind(n.kind) || ts.isLiteralKind(n.kind)) { + o.modifierFlagsCache = n.modifierFlagsCache; + } + o.transformFlags = n.transformFlags; + } + else { + nodeData = n; + } + if (!nodeData) return o; - for (const propertyName of Object.getOwnPropertyNames(n) as readonly (keyof ts.SourceFile | keyof ts.Identifier | keyof ts.StringLiteral)[]) { + for (const propertyName of Object.getOwnPropertyNames(nodeData) as readonly (keyof ts.SourceFile | keyof ts.Identifier | keyof ts.StringLiteral)[]) { switch (propertyName) { case "parent": case "symbol": @@ -198,29 +228,15 @@ export function sourceFileToJSON(file: ts.Node): string { case "emitNode": // Blocklist of items we never put in the baseline file. break; - + case "flags": + addSerializableFlags(o, nodeData); + break; case "hasExtendedUnicodeEscape": - if ((n as any).hasExtendedUnicodeEscape) { + if (nodeData.hasExtendedUnicodeEscape) { o.hasExtendedUnicodeEscape = true; } break; - case "flags": - // Clear the flags that are produced by aggregating child values. That is ephemeral - // data we don't care about in the dump. We only care what the parser set directly - // on the AST. - let flags = n.flags & ~(ts.NodeFlags.JavaScriptFile | ts.NodeFlags.HasAggregatedChildData); - if (ts.isIdentifier(n)) { - if (flags & ts.NodeFlags.IdentifierHasExtendedUnicodeEscape) { - o.hasExtendedUnicodeEscape = true; - flags &= ~ts.NodeFlags.IdentifierHasExtendedUnicodeEscape; - } - } - if (flags) { - o[propertyName] = getNodeFlagName(flags); - } - break; - case "parseDiagnostics": o[propertyName] = convertDiagnostics((n as any)[propertyName]); break; @@ -239,7 +255,7 @@ export function sourceFileToJSON(file: ts.Node): string { break; default: - o[propertyName] = (n as any)[propertyName]; + o[propertyName] = nodeData[propertyName]; } } @@ -319,8 +335,8 @@ function assertArrayStructuralEquals(array1: ts.NodeArray, array2: ts.N } function findChildName(parent: any, child: any) { - for (const name in parent) { - if (ts.hasProperty(parent, name) && parent[name] === child) { + for (const name in parent.data) { + if (ts.hasProperty(parent.data, name) && parent[name] === child) { return name; } } diff --git a/src/services/services.ts b/src/services/services.ts index 2bd5c94bb0efe..3d80ce7cad11f 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -13,7 +13,6 @@ import { CancellationToken, changeCompilerHostLikeToUseCache, CharacterCodes, - CheckJsDirective, Classifications, ClassifiedSpan, ClassifiedSpan2020, @@ -58,17 +57,14 @@ import { EditorOptions, EditorSettings, ElementAccessExpression, - EmitNode, EmitTextWriter, emptyArray, emptyOptions, EndOfFileToken, - EntityName, equateValues, ExportDeclaration, Extension, extensionFromPath, - FileReference, FileTextChanges, filter, find, @@ -189,7 +185,6 @@ import { isTextWhiteSpaceLike, isThisTypeParameter, isTransientSymbol, - JSDoc, JsDoc, JSDocContainer, JSDocParsingMode, @@ -203,7 +198,6 @@ import { LanguageService, LanguageServiceHost, LanguageServiceMode, - LanguageVariant, lastOrUndefined, length, LineAndCharacter, @@ -225,6 +219,7 @@ import { Node, NodeArray, NodeFlags, + NodeImpl, noop, normalizePath, NumberLiteralType, @@ -248,8 +243,6 @@ import { Path, positionIsSynthesized, PossibleProgramFileInfo, - PragmaMap, - PrivateIdentifier, Program, PropertyName, PropertySignature, @@ -281,6 +274,7 @@ import { SignatureHelp, SignatureHelpItems, SignatureHelpItemsOptions, + SignatureImpl, SignatureKind, singleElementArray, SmartSelectionRange, @@ -289,8 +283,6 @@ import { SourceFileLike, SourceMapSource, startsWith, - Statement, - StringLiteral, StringLiteralLike, StringLiteralType, Symbol, @@ -313,16 +305,13 @@ import { timestamp, TodoComment, TodoCommentDescriptor, - Token, toPath, tracing, - TransformFlags, Type, TypeChecker, TypeFlags, - TypeNode, + TypeImpl, TypeParameter, - TypePredicate, TypeReference, typeToDisplayParts, UnionOrIntersectionType, @@ -344,44 +333,40 @@ import * as classifier2020 from "./classifier2020.js"; /** The version of the language service API */ export const servicesVersion = "0.8"; -function createNode(kind: TKind, pos: number, end: number, parent: Node): NodeObject | TokenObject | IdentifierObject | PrivateIdentifierObject { - const node = isNodeKind(kind) ? new NodeObject(kind, pos, end) : - kind === SyntaxKind.Identifier ? new IdentifierObject(SyntaxKind.Identifier, pos, end) : - kind === SyntaxKind.PrivateIdentifier ? new PrivateIdentifierObject(SyntaxKind.PrivateIdentifier, pos, end) : - new TokenObject(kind, pos, end); +function createNode(kind: TKind, pos: number, end: number, parent: Node): Node { + const node = new NodeObject(kind, pos, end); node.parent = parent; node.flags = parent.flags & NodeFlags.ContextFlags; return node; } -class NodeObject implements Node { - public kind: TKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public modifierFlagsCache: ModifierFlags; - public transformFlags: TransformFlags; - public parent: Node; - public symbol!: Symbol; // Actually optional, but it was too annoying to access `node.symbol!` everywhere since in many cases we know it must be defined - public jsDoc?: JSDoc[]; - public original?: Node; - public id?: number; - public emitNode?: EmitNode; +class NodeObject extends NodeImpl implements Node { + override kind!: TKind; + + declare _declarationBrand: any; + declare _localsContainerBrand: any; + declare _primaryExpressionBrand: any; + declare _memberExpressionBrand: any; + declare _leftHandSideExpressionBrand: any; + declare _updateExpressionBrand: any; + declare _unaryExpressionBrand: any; + declare _expressionBrand: any; + declare _jsdocContainerBrand: any; + declare _flowContainerBrand: any; constructor(kind: TKind, pos: number, end: number) { - // Note: if modifying this, be sure to update Node in src/compiler/utilities.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.modifierFlagsCache = ModifierFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.original = undefined; - this.emitNode = undefined; + super(kind, pos, end); } + override get text(): string { + if (this.kind === SyntaxKind.Identifier || this.kind === SyntaxKind.PrivateIdentifier) { + return idText(this as any as Identifier); + } + return super.text; + } + override set text(value) { + super.text = value; + } private assertHasRealPosition(message?: string) { // eslint-disable-next-line local/debug-assert Debug.assert(!positionIsSynthesized(this.pos) && !positionIsSynthesized(this.end), message || "Node must have a real position for this operation"); @@ -443,37 +428,273 @@ class NodeObject implements Node { } public getChildren(sourceFile?: SourceFileLike): readonly Node[] { - this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine"); - return getNodeChildren(this) ?? setNodeChildren(this, createChildren(this, sourceFile)); + if (isNodeKind(this.kind)) { + this.assertHasRealPosition("Node without a real position cannot be scanned and thus has no token nodes - use forEachChild and collect the result if that's fine"); + return getNodeChildren(this) ?? setNodeChildren(this, createChildren(this, sourceFile)); + } + else { + return this.kind === SyntaxKind.EndOfFileToken ? (this as Node as EndOfFileToken).jsDoc || emptyArray : emptyArray; + } } public getFirstToken(sourceFile?: SourceFileLike): Node | undefined { - this.assertHasRealPosition(); - const children = this.getChildren(sourceFile); - if (!children.length) { + if (isNodeKind(this.kind)) { + this.assertHasRealPosition(); + const children = this.getChildren(sourceFile); + if (!children.length) { + return undefined; + } + + const child = find(children, kid => kid.kind < SyntaxKind.FirstJSDocNode || kid.kind > SyntaxKind.LastJSDocNode)!; + return child.kind < SyntaxKind.FirstNode ? + child : + child.getFirstToken(sourceFile); + } + else { return undefined; } - - const child = find(children, kid => kid.kind < SyntaxKind.FirstJSDocNode || kid.kind > SyntaxKind.LastJSDocNode)!; - return child.kind < SyntaxKind.FirstNode ? - child : - child.getFirstToken(sourceFile); } public getLastToken(sourceFile?: SourceFileLike): Node | undefined { - this.assertHasRealPosition(); - const children = this.getChildren(sourceFile); + if (isNodeKind(this.kind)) { + this.assertHasRealPosition(); + const children = this.getChildren(sourceFile); + + const child = lastOrUndefined(children); + if (!child) { + return undefined; + } - const child = lastOrUndefined(children); - if (!child) { + return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); + } + else { return undefined; } - - return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); } public forEachChild(cbNode: (node: Node) => T, cbNodeArray?: (nodes: NodeArray) => T): T | undefined { - return forEachChild(this, cbNode, cbNodeArray); + if (isNodeKind(this.kind)) { + return forEachChild(this, cbNode, cbNodeArray); + } + else { + return undefined; + } + } + + private get namedDeclarations(): Map | undefined { + return this.data.namedDeclarations; + } + private set namedDeclarations(value: Map | undefined) { + this.data.namedDeclarations = value; + } + + public get scriptSnapshot(): IScriptSnapshot { + return this.data.scriptSnapshot; + } + public set scriptSnapshot(value: IScriptSnapshot) { + this.data.scriptSnapshot = value; + } + + public get nameTable(): Map<__String, number> | undefined { + return this.data.nameTable; + } + public set nameTable(value: Map<__String, number> | undefined) { + this.data.nameTable = value; + } + + public update(newText: string, textChangeRange: TextChangeRange): SourceFile { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + return updateSourceFile(this as SourceFile, newText, textChangeRange); + } + + public getLineAndCharacterOfPosition(position: number): LineAndCharacter { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + return getLineAndCharacterOfPosition(this, position); + } + + public getLineStarts(): readonly number[] { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + return getLineStarts(this); + } + + public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits); + } + + public getLineEndOfPosition(pos: number): number { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + const { line } = this.getLineAndCharacterOfPosition(pos); + const lineStarts = this.getLineStarts(); + + let lastCharPos: number | undefined; + if (line + 1 >= lineStarts.length) { + lastCharPos = this.getEnd(); + } + if (!lastCharPos) { + lastCharPos = lineStarts[line + 1] - 1; + } + + const fullText = this.getFullText(); + // if the new line is "\r\n", we should return the last non-new-line-character position + return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos; + } + + public getNamedDeclarations(): Map { + Debug.assertEqual(this.kind, SyntaxKind.SourceFile); + if (!this.namedDeclarations) { + this.namedDeclarations = this.computeNamedDeclarations(); + } + + return this.namedDeclarations; + } + + private computeNamedDeclarations(): Map { + const result = createMultiMap(); + + this.forEachChild(visit); + + return result; + + function addDeclaration(declaration: Declaration) { + const name = getDeclarationName(declaration); + if (name) { + result.add(name, declaration); + } + } + + function getDeclarations(name: string) { + let declarations = result.get(name); + if (!declarations) { + result.set(name, declarations = []); + } + return declarations; + } + + function getDeclarationName(declaration: Declaration) { + const name = getNonAssignedNameOfDeclaration(declaration); + return name && (isComputedPropertyName(name) && isPropertyAccessExpression(name.expression) ? name.expression.name.text + : isPropertyName(name) ? getNameFromPropertyName(name) : undefined); + } + + function visit(node: Node): void { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + const functionDeclaration = node as FunctionLikeDeclaration; + const declarationName = getDeclarationName(functionDeclaration); + + if (declarationName) { + const declarations = getDeclarations(declarationName); + const lastDeclaration = lastOrUndefined(declarations); + + // Check whether this declaration belongs to an "overload group". + if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { + // Overwrite the last declaration if it was an overload + // and this one is an implementation. + if (functionDeclaration.body && !(lastDeclaration as FunctionLikeDeclaration).body) { + declarations[declarations.length - 1] = functionDeclaration; + } + } + else { + declarations.push(functionDeclaration); + } + } + forEachChild(node, visit); + break; + + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.ImportEqualsDeclaration: + case SyntaxKind.ExportSpecifier: + case SyntaxKind.ImportSpecifier: + case SyntaxKind.ImportClause: + case SyntaxKind.NamespaceImport: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.TypeLiteral: + addDeclaration(node as Declaration); + forEachChild(node, visit); + break; + + case SyntaxKind.Parameter: + // Only consider parameter properties + if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { + break; + } + // falls through + + case SyntaxKind.VariableDeclaration: + case SyntaxKind.BindingElement: { + const decl = node as VariableDeclaration; + if (isBindingPattern(decl.name)) { + forEachChild(decl.name, visit); + break; + } + if (decl.initializer) { + visit(decl.initializer); + } + } + // falls through + case SyntaxKind.EnumMember: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + addDeclaration(node as Declaration); + break; + + case SyntaxKind.ExportDeclaration: + // Handle named exports case e.g.: + // export {a, b as B} from "mod"; + const exportDeclaration = node as ExportDeclaration; + if (exportDeclaration.exportClause) { + if (isNamedExports(exportDeclaration.exportClause)) { + forEach(exportDeclaration.exportClause.elements, visit); + } + else { + visit(exportDeclaration.exportClause.name); + } + } + break; + + case SyntaxKind.ImportDeclaration: + const importClause = (node as ImportDeclaration).importClause; + if (importClause) { + // Handle default import case e.g.: + // import d from "mod"; + if (importClause.name) { + addDeclaration(importClause.name); + } + + // Handle named bindings in imports e.g.: + // import * as NS from "mod"; + // import {a, b as B} from "mod"; + if (importClause.namedBindings) { + if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { + addDeclaration(importClause.namedBindings); + } + else { + forEach(importClause.namedBindings.elements, visit); + } + } + } + break; + + case SyntaxKind.BinaryExpression: + if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) { + addDeclaration(node as BinaryExpression); + } + // falls through + + default: + forEachChild(node, visit); + } + } } } @@ -523,117 +744,28 @@ function addSyntheticNodes(nodes: Node[], pos: number, end: number, parent: Node continue; } Debug.fail(`Did not expect ${Debug.formatSyntaxKind(parent.kind)} to have an Identifier in its trivia`); - } - nodes.push(createNode(token, pos, textPos, parent)); - } - pos = textPos; - if (token === SyntaxKind.EndOfFileToken) { - break; - } - } -} - -function createSyntaxList(nodes: NodeArray, parent: Node): Node { - const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList; - const children: Node[] = []; - let pos = nodes.pos; - for (const node of nodes) { - addSyntheticNodes(children, pos, node.pos, parent); - children.push(node); - pos = node.end; - } - addSyntheticNodes(children, pos, nodes.end, parent); - setNodeChildren(list, children); - return list; -} - -class TokenOrIdentifierObject implements Node { - public kind: TKind; - public pos: number; - public end: number; - public flags: NodeFlags; - public modifierFlagsCache!: ModifierFlags; - public transformFlags: TransformFlags; - public parent: Node; - public symbol!: Symbol; - public jsDocComments?: JSDoc[]; - public id?: number; - public emitNode?: EmitNode | undefined; - - constructor(kind: TKind, pos: number, end: number) { - // Note: if modifying this, be sure to update Token and Identifier in src/compiler/utilities.ts - this.pos = pos; - this.end = end; - this.kind = kind; - this.id = 0; - this.flags = NodeFlags.None; - this.transformFlags = TransformFlags.None; - this.parent = undefined!; - this.emitNode = undefined; - } - - public getSourceFile(): SourceFile { - return getSourceFileOfNode(this); - } - - public getStart(sourceFile?: SourceFileLike, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this, sourceFile, includeJsDocComment); - } - - public getFullStart(): number { - return this.pos; - } - - public getEnd(): number { - return this.end; - } - - public getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - public getFullWidth(): number { - return this.end - this.pos; - } - - public getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - public getFullText(sourceFile?: SourceFile): string { - return (sourceFile || this.getSourceFile()).text.substring(this.pos, this.end); - } - - public getText(sourceFile?: SourceFile): string { - if (!sourceFile) { - sourceFile = this.getSourceFile(); - } - return sourceFile.text.substring(this.getStart(sourceFile), this.getEnd()); - } - - public getChildCount(): number { - return this.getChildren().length; - } - - public getChildAt(index: number): Node { - return this.getChildren()[index]; - } - - public getChildren(): Node[] { - return this.kind === SyntaxKind.EndOfFileToken ? (this as Node as EndOfFileToken).jsDoc || emptyArray : emptyArray; - } - - public getFirstToken(): Node | undefined { - return undefined; - } - - public getLastToken(): Node | undefined { - return undefined; + } + nodes.push(createNode(token, pos, textPos, parent)); + } + pos = textPos; + if (token === SyntaxKind.EndOfFileToken) { + break; + } } +} - public forEachChild(): T | undefined { - return undefined; +function createSyntaxList(nodes: NodeArray, parent: Node): Node { + const list = createNode(SyntaxKind.SyntaxList, nodes.pos, nodes.end, parent) as any as SyntaxList; + const children: Node[] = []; + let pos = nodes.pos; + for (const node of nodes) { + addSyntheticNodes(children, pos, node.pos, parent); + children.push(node); + pos = node.end; } + addSyntheticNodes(children, pos, nodes.end, parent); + setNodeChildren(list, children); + return list; } class SymbolObject implements Symbol { @@ -770,61 +902,7 @@ class SymbolObject implements Symbol { } } -class TokenObject extends TokenOrIdentifierObject implements Token { - constructor(kind: TKind, pos: number, end: number) { - super(kind, pos, end); - } -} - -class IdentifierObject extends TokenOrIdentifierObject implements Identifier { - public escapedText!: __String; - declare _primaryExpressionBrand: any; - declare _memberExpressionBrand: any; - declare _leftHandSideExpressionBrand: any; - declare _updateExpressionBrand: any; - declare _unaryExpressionBrand: any; - declare _expressionBrand: any; - declare _declarationBrand: any; - declare _jsdocContainerBrand: any; - declare _flowContainerBrand: any; - typeArguments!: NodeArray; - constructor(kind: SyntaxKind.Identifier, pos: number, end: number) { - super(kind, pos, end); - } - - get text(): string { - return idText(this); - } -} - -class PrivateIdentifierObject extends TokenOrIdentifierObject implements PrivateIdentifier { - public escapedText!: __String; - declare _primaryExpressionBrand: any; - declare _memberExpressionBrand: any; - declare _leftHandSideExpressionBrand: any; - declare _updateExpressionBrand: any; - declare _unaryExpressionBrand: any; - declare _expressionBrand: any; - constructor(kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) { - super(kind, pos, end); - } - - get text(): string { - return idText(this); - } -} - -class TypeObject implements Type { - checker: TypeChecker; - flags: TypeFlags; - objectFlags?: ObjectFlags; - id!: number; - symbol!: Symbol; - constructor(checker: TypeChecker, flags: TypeFlags) { - // Note: if modifying this, be sure to update Type in src/compiler/types.ts - this.flags = flags; - this.checker = checker; - } +class TypeObject extends TypeImpl implements Type { getFlags(): TypeFlags { return this.flags; } @@ -912,37 +990,19 @@ class TypeObject implements Type { } } -class SignatureObject implements Signature { - flags: SignatureFlags; - checker: TypeChecker; - declaration!: SignatureDeclaration; - typeParameters?: TypeParameter[]; - parameters!: Symbol[]; - thisParameter!: Symbol; - resolvedReturnType!: Type; - resolvedTypePredicate: TypePredicate | undefined; - minTypeArgumentCount!: number; - minArgumentCount!: number; - - // Undefined is used to indicate the value has not been computed. If, after computing, the - // symbol has no doc comment, then the empty array will be returned. - documentationComment?: SymbolDisplayPart[]; - jsDocTags?: JSDocTagInfo[]; // same - +class SignatureObject extends SignatureImpl implements Signature { constructor(checker: TypeChecker, flags: SignatureFlags) { - // Note: if modifying this, be sure to update Signature in src/compiler/types.ts - this.flags = flags; + super(checker, flags); this.checker = checker; } - getDeclaration(): SignatureDeclaration { - return this.declaration; + return this.declaration as SignatureDeclaration; } getTypeParameters(): TypeParameter[] | undefined { - return this.typeParameters; + return this.typeParameters as TypeParameter[]; } getParameters(): Symbol[] { - return this.parameters; + return this.parameters as Symbol[]; } getReturnType(): Type { return this.checker.getReturnTypeOfSignature(this); @@ -959,11 +1019,11 @@ class SignatureObject implements Signature { } getDocumentationComment(): SymbolDisplayPart[] { - return this.documentationComment || (this.documentationComment = getDocumentationComment(singleElementArray(this.declaration), this.checker)); + return this.data.documentationComment || (this.data.documentationComment = getDocumentationComment(singleElementArray(this.declaration), this.checker)); } getJsDocTags(): JSDocTagInfo[] { - return this.jsDocTags || (this.jsDocTags = getJsDocTagsOfDeclarations(singleElementArray(this.declaration), this.checker)); + return this.data.jsDocTags || (this.data.jsDocTags = getJsDocTagsOfDeclarations(singleElementArray(this.declaration), this.checker)); } } @@ -1036,250 +1096,6 @@ function findBaseOfDeclaration(checker: TypeChecker, declaration: Declaration }); } -class SourceFileObject extends NodeObject implements SourceFile { - declare _declarationBrand: any; - declare _localsContainerBrand: any; - public fileName!: string; - public path!: Path; - public resolvedPath!: Path; - public originalFileName!: string; - public text!: string; - public scriptSnapshot!: IScriptSnapshot; - public lineMap!: readonly number[]; - - public statements!: NodeArray; - public endOfFileToken!: Token; - - public amdDependencies!: { name: string; path: string; }[]; - public moduleName!: string; - public referencedFiles!: FileReference[]; - public typeReferenceDirectives!: FileReference[]; - public libReferenceDirectives!: FileReference[]; - - public syntacticDiagnostics!: DiagnosticWithLocation[]; - public parseDiagnostics!: DiagnosticWithLocation[]; - public bindDiagnostics!: DiagnosticWithLocation[]; - public bindSuggestionDiagnostics?: DiagnosticWithLocation[]; - - public isDeclarationFile!: boolean; - public isDefaultLib!: boolean; - public hasNoDefaultLib!: boolean; - public externalModuleIndicator!: Node; // The first node that causes this file to be an external module - public commonJsModuleIndicator!: Node; // The first node that causes this file to be a CommonJS module - public nodeCount!: number; - public identifierCount!: number; - public symbolCount!: number; - public version!: string; - public scriptKind!: ScriptKind; - public languageVersion!: ScriptTarget; - public languageVariant!: LanguageVariant; - public identifiers!: Map; - public nameTable: Map<__String, number> | undefined; - public imports!: readonly StringLiteralLike[]; - public moduleAugmentations!: StringLiteral[]; - private namedDeclarations: Map | undefined; - public ambientModuleNames!: string[]; - public checkJsDirective: CheckJsDirective | undefined; - public errorExpectations: TextRange[] | undefined; - public possiblyContainDynamicImport?: boolean; - public pragmas!: PragmaMap; - public localJsxFactory: EntityName | undefined; - public localJsxNamespace: __String | undefined; - - constructor(kind: SyntaxKind.SourceFile, pos: number, end: number) { - super(kind, pos, end); - } - - public update(newText: string, textChangeRange: TextChangeRange): SourceFile { - return updateSourceFile(this, newText, textChangeRange); - } - - public getLineAndCharacterOfPosition(position: number): LineAndCharacter { - return getLineAndCharacterOfPosition(this, position); - } - - public getLineStarts(): readonly number[] { - return getLineStarts(this); - } - - public getPositionOfLineAndCharacter(line: number, character: number, allowEdits?: true): number { - return computePositionOfLineAndCharacter(getLineStarts(this), line, character, this.text, allowEdits); - } - - public getLineEndOfPosition(pos: number): number { - const { line } = this.getLineAndCharacterOfPosition(pos); - const lineStarts = this.getLineStarts(); - - let lastCharPos: number | undefined; - if (line + 1 >= lineStarts.length) { - lastCharPos = this.getEnd(); - } - if (!lastCharPos) { - lastCharPos = lineStarts[line + 1] - 1; - } - - const fullText = this.getFullText(); - // if the new line is "\r\n", we should return the last non-new-line-character position - return fullText[lastCharPos] === "\n" && fullText[lastCharPos - 1] === "\r" ? lastCharPos - 1 : lastCharPos; - } - - public getNamedDeclarations(): Map { - if (!this.namedDeclarations) { - this.namedDeclarations = this.computeNamedDeclarations(); - } - - return this.namedDeclarations; - } - - private computeNamedDeclarations(): Map { - const result = createMultiMap(); - - this.forEachChild(visit); - - return result; - - function addDeclaration(declaration: Declaration) { - const name = getDeclarationName(declaration); - if (name) { - result.add(name, declaration); - } - } - - function getDeclarations(name: string) { - let declarations = result.get(name); - if (!declarations) { - result.set(name, declarations = []); - } - return declarations; - } - - function getDeclarationName(declaration: Declaration) { - const name = getNonAssignedNameOfDeclaration(declaration); - return name && (isComputedPropertyName(name) && isPropertyAccessExpression(name.expression) ? name.expression.name.text - : isPropertyName(name) ? getNameFromPropertyName(name) : undefined); - } - - function visit(node: Node): void { - switch (node.kind) { - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - const functionDeclaration = node as FunctionLikeDeclaration; - const declarationName = getDeclarationName(functionDeclaration); - - if (declarationName) { - const declarations = getDeclarations(declarationName); - const lastDeclaration = lastOrUndefined(declarations); - - // Check whether this declaration belongs to an "overload group". - if (lastDeclaration && functionDeclaration.parent === lastDeclaration.parent && functionDeclaration.symbol === lastDeclaration.symbol) { - // Overwrite the last declaration if it was an overload - // and this one is an implementation. - if (functionDeclaration.body && !(lastDeclaration as FunctionLikeDeclaration).body) { - declarations[declarations.length - 1] = functionDeclaration; - } - } - else { - declarations.push(functionDeclaration); - } - } - forEachChild(node, visit); - break; - - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.ImportEqualsDeclaration: - case SyntaxKind.ExportSpecifier: - case SyntaxKind.ImportSpecifier: - case SyntaxKind.ImportClause: - case SyntaxKind.NamespaceImport: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.TypeLiteral: - addDeclaration(node as Declaration); - forEachChild(node, visit); - break; - - case SyntaxKind.Parameter: - // Only consider parameter properties - if (!hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { - break; - } - // falls through - - case SyntaxKind.VariableDeclaration: - case SyntaxKind.BindingElement: { - const decl = node as VariableDeclaration; - if (isBindingPattern(decl.name)) { - forEachChild(decl.name, visit); - break; - } - if (decl.initializer) { - visit(decl.initializer); - } - } - // falls through - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - addDeclaration(node as Declaration); - break; - - case SyntaxKind.ExportDeclaration: - // Handle named exports case e.g.: - // export {a, b as B} from "mod"; - const exportDeclaration = node as ExportDeclaration; - if (exportDeclaration.exportClause) { - if (isNamedExports(exportDeclaration.exportClause)) { - forEach(exportDeclaration.exportClause.elements, visit); - } - else { - visit(exportDeclaration.exportClause.name); - } - } - break; - - case SyntaxKind.ImportDeclaration: - const importClause = (node as ImportDeclaration).importClause; - if (importClause) { - // Handle default import case e.g.: - // import d from "mod"; - if (importClause.name) { - addDeclaration(importClause.name); - } - - // Handle named bindings in imports e.g.: - // import * as NS from "mod"; - // import {a, b as B} from "mod"; - if (importClause.namedBindings) { - if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) { - addDeclaration(importClause.namedBindings); - } - else { - forEach(importClause.namedBindings.elements, visit); - } - } - } - break; - - case SyntaxKind.BinaryExpression: - if (getAssignmentDeclarationKind(node as BinaryExpression) !== AssignmentDeclarationKind.None) { - addDeclaration(node as BinaryExpression); - } - // falls through - - default: - forEachChild(node, visit); - } - } - } -} - class SourceMapSourceObject implements SourceMapSource { fileName: string; text: string; @@ -1301,11 +1117,11 @@ class SourceMapSourceObject implements SourceMapSource { function getServicesObjectAllocator(): ObjectAllocator { return { getNodeConstructor: () => NodeObject, - getTokenConstructor: () => TokenObject, + getTokenConstructor: () => NodeObject, - getIdentifierConstructor: () => IdentifierObject, - getPrivateIdentifierConstructor: () => PrivateIdentifierObject, - getSourceFileConstructor: () => SourceFileObject, + getIdentifierConstructor: () => NodeObject, + getPrivateIdentifierConstructor: () => NodeObject, + getSourceFileConstructor: () => NodeObject, getSymbolConstructor: () => SymbolObject, getTypeConstructor: () => TypeObject, getSignatureConstructor: () => SignatureObject, diff --git a/src/services/transpile.ts b/src/services/transpile.ts index ccf8396247ca8..835e430f03d77 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -26,6 +26,7 @@ import { optionDeclarations, parseCustomTypeOption, ScriptTarget, + SourceFile, toPath, transpileOptionValueCompilerOptions, } from "./_namespaces/ts.js"; @@ -109,10 +110,14 @@ interface Symbol { readonly [Symbol.toStringTag]: string; }`; const barebonesLibName = "lib.d.ts"; -const barebonesLibSourceFile = createSourceFile(barebonesLibName, barebonesLibContent, { languageVersion: ScriptTarget.Latest }); +/* eslint-disable-next-line no-var */ +var barebonesLibSourceFile: SourceFile | undefined; function transpileWorker(input: string, transpileOptions: TranspileOptions, declaration?: boolean): TranspileOutput { const diagnostics: Diagnostic[] = []; + if (!barebonesLibSourceFile) { + barebonesLibSourceFile = createSourceFile(barebonesLibName, barebonesLibContent, { languageVersion: ScriptTarget.Latest }); + } const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : {};