From 12f6dcefa1d31379ae25563967457e87ac8ad772 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 27 Oct 2016 15:50:21 -0700 Subject: [PATCH] Revert "Merge pull request #11354 from Microsoft/map4" This reverts commit adfdae0dc47db5ad8248d26929a7b31cb895a539, reversing changes made to aad663cebf6a89f7029af57d8cb6a0a011854978. --- Jakefile.js | 2 - scripts/processDiagnosticMessages.ts | 18 +- src/compiler/binder.ts | 44 +- src/compiler/checker.ts | 488 +++++++------- src/compiler/collections.ts | 609 ------------------ src/compiler/commandLineParser.ts | 128 ++-- src/compiler/core.ts | 287 ++++++++- src/compiler/declarationEmitter.ts | 18 +- src/compiler/emitter.ts | 18 +- src/compiler/factory.ts | 20 +- src/compiler/parser.ts | 6 +- src/compiler/performance.ts | 30 +- src/compiler/program.ts | 72 ++- src/compiler/scanner.ts | 21 +- src/compiler/sourcemap.ts | 2 +- src/compiler/sys.ts | 21 +- src/compiler/transformer.ts | 18 +- src/compiler/transformers/es2015.ts | 33 +- src/compiler/transformers/generators.ts | 32 +- src/compiler/transformers/jsx.ts | 8 +- src/compiler/transformers/module/module.ts | 57 +- src/compiler/transformers/module/system.ts | 63 +- src/compiler/transformers/ts.ts | 24 +- src/compiler/tsc.ts | 34 +- src/compiler/tsconfig.json | 1 - src/compiler/types.ts | 73 +-- src/compiler/utilities.ts | 86 +-- src/compiler/visitor.ts | 54 +- src/harness/fourslash.ts | 47 +- src/harness/harness.ts | 30 +- src/harness/harnessLanguageService.ts | 4 +- src/harness/loggedIO.ts | 10 +- src/harness/projectsRunner.ts | 21 +- .../unittests/cachingInServerLSHost.ts | 23 +- src/harness/unittests/moduleResolution.ts | 64 +- .../unittests/reuseProgramStructure.ts | 27 +- src/harness/unittests/session.ts | 22 +- .../unittests/tsserverProjectSystem.ts | 37 +- src/harness/unittests/typingsInstaller.ts | 4 +- src/server/builder.ts | 15 +- src/server/client.ts | 8 +- src/server/editorServices.ts | 140 ++-- src/server/lsHost.ts | 14 +- src/server/project.ts | 59 +- src/server/scriptInfo.ts | 2 +- src/server/session.ts | 8 +- src/server/tsconfig.library.json | 2 +- src/server/typingsCache.ts | 29 +- .../typingsInstaller/typingsInstaller.ts | 38 +- src/server/utilities.ts | 41 +- src/services/classifier.ts | 6 +- src/services/codefixes/codeFixProvider.ts | 17 +- src/services/completions.ts | 55 +- src/services/documentHighlights.ts | 6 +- src/services/documentRegistry.ts | 39 +- src/services/findAllReferences.ts | 31 +- src/services/formatting/rules.ts | 2 +- src/services/goToDefinition.ts | 2 +- src/services/jsTyping.ts | 51 +- src/services/navigateTo.ts | 19 +- src/services/navigationBar.ts | 18 +- src/services/patternMatcher.ts | 8 +- src/services/services.ts | 28 +- src/services/shims.ts | 2 +- src/services/signatureHelp.ts | 2 +- src/services/transpile.ts | 6 +- src/services/types.ts | 4 +- tslint.json | 3 +- 68 files changed, 1430 insertions(+), 1781 deletions(-) delete mode 100644 src/compiler/collections.ts diff --git a/Jakefile.js b/Jakefile.js index 8425751c4d41c..8ce70eb353f9e 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -57,7 +57,6 @@ function measure(marker) { } var compilerSources = [ - "collections.ts", "core.ts", "performance.ts", "sys.ts", @@ -94,7 +93,6 @@ var compilerSources = [ }); var servicesSources = [ - "collections.ts", "core.ts", "performance.ts", "sys.ts", diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index a7e2f24fdb885..e5eaa46c8e5b9 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -27,7 +27,7 @@ function main(): void { var inputFilePath = sys.args[0].replace(/\\/g, "/"); var inputStr = sys.readFile(inputFilePath); - + var diagnosticMessages: InputDiagnosticMessageTable = JSON.parse(inputStr); var names = Utilities.getObjectKeys(diagnosticMessages); @@ -44,7 +44,7 @@ function main(): void { function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosticMessageTable) { const originalMessageForCode: string[] = []; let numConflicts = 0; - + for (const currentMessage of messages) { const code = diagnosticTable[currentMessage].code; @@ -68,19 +68,19 @@ function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosti } } -function buildUniqueNameMap(names: string[]): ts.Map { - var nameMap = ts.createMap(); +function buildUniqueNameMap(names: string[]): ts.Map { + var nameMap = ts.createMap(); var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined); for (var i = 0; i < names.length; i++) { - nameMap.set(names[i], uniqueNames[i]); + nameMap[names[i]] = uniqueNames[i]; } return nameMap; } -function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { +function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { var result = '// \r\n' + '/// \r\n' + @@ -91,7 +91,7 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; - var propName = convertPropertyName(nameMap.get(name)); + var propName = convertPropertyName(nameMap[name]); result += ' ' + propName + @@ -107,14 +107,14 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: return result; } -function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { +function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map): string { var result = '{'; var names = Utilities.getObjectKeys(messageTable); for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; - var propName = convertPropertyName(nameMap.get(name)); + var propName = convertPropertyName(nameMap[name]); result += '\r\n "' + createKey(propName, diagnosticDetails.code) + '"' + ' : "' + name.replace(/[\"]/g, '\\"') + '"'; if (i !== names.length - 1) { diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9bf8d4846ce12..9c2c749e112a6 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -133,7 +133,7 @@ namespace ts { let symbolCount = 0; let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; - let classifiableNames: Set; + let classifiableNames: Map; const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; @@ -147,7 +147,7 @@ namespace ts { options = opts; languageVersion = getEmitScriptTarget(options); inStrictMode = bindInStrictMode(file, opts); - classifiableNames = createSet(); + classifiableNames = createMap(); symbolCount = 0; skipTransformFlagAggregation = isDeclarationFile(file); @@ -207,11 +207,11 @@ namespace ts { symbol.declarations.push(node); if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) { - symbol.exports = createMap(); + symbol.exports = createMap(); } if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) { - symbol.members = createMap(); + symbol.members = createMap(); } if (symbolFlags & SymbolFlags.Value) { @@ -349,17 +349,17 @@ namespace ts { // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = getOrUpdate(symbolTable, name, name => createSymbol(SymbolFlags.None, name)); + symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { - classifiableNames.add(name); + classifiableNames[name] = name; } if (symbol.flags & excludes) { if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. - symbol = setAndReturn(symbolTable, name, createSymbol(SymbolFlags.None, name)); + symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name); } else { if (node.name) { @@ -484,7 +484,7 @@ namespace ts { if (containerFlags & ContainerFlags.IsContainer) { container = blockScopeContainer = node; if (containerFlags & ContainerFlags.HasLocals) { - container.locals = createMap(); + container.locals = createMap(); } addToContainerChain(container); } @@ -1525,7 +1525,8 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = createMap([[symbol.name, symbol]]); + typeLiteralSymbol.members = createMap(); + typeLiteralSymbol.members[symbol.name] = symbol; } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1535,7 +1536,7 @@ namespace ts { } if (inStrictMode) { - const seen = createMap(); + const seen = createMap(); for (const prop of node.properties) { if (prop.name.kind !== SyntaxKind.Identifier) { @@ -1556,9 +1557,9 @@ namespace ts { ? ElementKind.Property : ElementKind.Accessor; - const existingKind = seen.get(identifier.text); + const existingKind = seen[identifier.text]; if (!existingKind) { - seen.set(identifier.text, currentKind); + seen[identifier.text] = currentKind; continue; } @@ -1591,7 +1592,7 @@ namespace ts { // fall through. default: if (!blockScopeContainer.locals) { - blockScopeContainer.locals = createMap(); + blockScopeContainer.locals = createMap(); addToContainerChain(blockScopeContainer); } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); @@ -2071,7 +2072,7 @@ namespace ts { } } - file.symbol.globalExports = file.symbol.globalExports || createMap(); + file.symbol.globalExports = file.symbol.globalExports || createMap(); declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } @@ -2118,7 +2119,7 @@ namespace ts { Debug.assert(isInJavaScriptFile(node)); // Declare a 'member' if the container is an ES5 class or ES6 constructor if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) { - container.symbol.members = container.symbol.members || createMap(); + container.symbol.members = container.symbol.members || createMap(); // It's acceptable for multiple 'this' assignments of the same identifier to occur declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); } @@ -2150,14 +2151,14 @@ namespace ts { constructorFunction.parent = classPrototype; classPrototype.parent = leftSideOfAssignment; - const funcSymbol = container.locals.get(constructorFunction.text); + const funcSymbol = container.locals[constructorFunction.text]; if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { return; } // Set up the members collection if it doesn't exist already if (!funcSymbol.members) { - funcSymbol.members = createMap(); + funcSymbol.members = createMap(); } // Declare the method/property @@ -2190,7 +2191,7 @@ namespace ts { bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName); // Add name of class expression into the map for semantic classifier if (node.name) { - classifiableNames.add(node.name.text); + classifiableNames[node.name.text] = node.name.text; } } @@ -2206,15 +2207,14 @@ namespace ts { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - const symbolExport = symbol.exports.get(prototypeSymbol.name); - if (symbolExport) { + if (symbol.exports[prototypeSymbol.name]) { if (node.name) { node.name.parent = node; } - file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], + file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); } - symbol.exports.set(prototypeSymbol.name, prototypeSymbol); + symbol.exports[prototypeSymbol.name] = prototypeSymbol; prototypeSymbol.parent = symbol; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 281190d325653..4f8f9b3f68c7c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -47,7 +47,7 @@ namespace ts { let symbolCount = 0; const emptyArray: any[] = []; - const emptySymbols = createMap(); + const emptySymbols = createMap(); const compilerOptions = host.getCompilerOptions(); const languageVersion = compilerOptions.target || ScriptTarget.ES3; @@ -111,10 +111,10 @@ namespace ts { }; const tupleTypes: GenericType[] = []; - const unionTypes = createMap(); - const intersectionTypes = createMap(); - const stringLiteralTypes = createMap(); - const numericLiteralTypes = createMap(); + const unionTypes = createMap(); + const intersectionTypes = createMap(); + const stringLiteralTypes = createMap(); + const numericLiteralTypes = createMap(); const evolvingArrayTypes: EvolvingArrayType[] = []; const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); @@ -139,7 +139,7 @@ namespace ts { const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyGenericType.instantiations = createMap(); + emptyGenericType.instantiations = createMap(); const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated @@ -155,7 +155,7 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); - const globals = createMap(); + const globals = createMap(); /** * List of every ambient module with a "*" wildcard. * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. @@ -221,7 +221,7 @@ namespace ts { const mergedSymbols: Symbol[] = []; const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; - const flowLoopCaches: Map[] = []; + const flowLoopCaches: Map[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; @@ -295,7 +295,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts = mapOfMapLike({ + const typeofEQFacts = createMap({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -305,7 +305,7 @@ namespace ts { "function": TypeFacts.TypeofEQFunction }); - const typeofNEFacts = mapOfMapLike({ + const typeofNEFacts = createMap({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -315,7 +315,7 @@ namespace ts { "function": TypeFacts.TypeofNEFunction }); - const typeofTypesByName = mapOfMapLike({ + const typeofTypesByName = createMap({ "string": stringType, "number": numberType, "boolean": booleanType, @@ -325,7 +325,7 @@ namespace ts { let jsxElementType: Type; /** Things we lazy load from the JSX namespace */ - const jsxTypes = createMap(); + const jsxTypes = createMap(); const JsxNames = { JSX: "JSX", IntrinsicElements: "IntrinsicElements", @@ -336,11 +336,11 @@ namespace ts { IntrinsicClassAttributes: "IntrinsicClassAttributes" }; - const subtypeRelation = createMap(); - const assignableRelation = createMap(); - const comparableRelation = createMap(); - const identityRelation = createMap(); - const enumRelation = createMap(); + const subtypeRelation = createMap(); + const assignableRelation = createMap(); + const comparableRelation = createMap(); + const identityRelation = createMap(); + const enumRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -354,7 +354,8 @@ namespace ts { ResolvedReturnType } - const builtinGlobals = createMap([[undefinedSymbol.name, undefinedSymbol]]); + const builtinGlobals = createMap(); + builtinGlobals[undefinedSymbol.name] = undefinedSymbol; initializeTypeChecker(); @@ -437,11 +438,11 @@ namespace ts { target.declarations.push(node); }); if (source.members) { - if (!target.members) target.members = createMap(); + if (!target.members) target.members = createMap(); mergeSymbolTable(target.members, source.members); } if (source.exports) { - if (!target.exports) target.exports = createMap(); + if (!target.exports) target.exports = createMap(); mergeSymbolTable(target.exports, source.exports); } recordMergedSymbol(target, source); @@ -459,18 +460,18 @@ namespace ts { } function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { - source.forEach((sourceSymbol, id) => { - let targetSymbol = target.get(id); + for (const id in source) { + let targetSymbol = target[id]; if (!targetSymbol) { - target.set(id, sourceSymbol); + target[id] = source[id]; } else { if (!(targetSymbol.flags & SymbolFlags.Merged)) { - target.set(id, targetSymbol = cloneSymbol(targetSymbol)); + target[id] = targetSymbol = cloneSymbol(targetSymbol); } - mergeSymbol(targetSymbol, sourceSymbol); + mergeSymbol(targetSymbol, source[id]); } - }); + } } function mergeModuleAugmentation(moduleName: LiteralExpression): void { @@ -511,16 +512,15 @@ namespace ts { } function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { - source.forEach((sourceSymbol, id) => { - const symbol = target.get(id); - if (symbol) { + for (const id in source) { + if (target[id]) { // Error on redeclarations - forEach(symbol.declarations, addDeclarationDiagnostic(id, message)); + forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); } else { - target.set(id, sourceSymbol); + target[id] = source[id]; } - }); + } function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id)); @@ -548,7 +548,7 @@ namespace ts { function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { if (meaning) { - const symbol = symbols.get(name); + const symbol = symbols[name]; if (symbol) { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (symbol.flags & meaning) { @@ -733,7 +733,7 @@ namespace ts { // It's an external module. First see if the module has an export default and if the local // name of that export default matches. - if (result = moduleExports.get("default")) { + if (result = moduleExports["default"]) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.name === name) { break loop; @@ -752,10 +752,9 @@ namespace ts { // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. - const moduleExport = moduleExports.get(name); - if (moduleExport && - moduleExport.flags === SymbolFlags.Alias && - getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) { + if (moduleExports[name] && + moduleExports[name].flags === SymbolFlags.Alias && + getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) { break; } } @@ -1070,16 +1069,11 @@ namespace ts { const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier); if (moduleSymbol) { - let exportDefaultSymbol: Symbol; - if (isUntypedModuleSymbol(moduleSymbol)) { - exportDefaultSymbol = moduleSymbol; - } - else { - const exportValue = moduleSymbol.exports.get("export="); - exportDefaultSymbol = exportValue - ? getPropertyOfType(getTypeOfSymbol(exportValue), "default") - : resolveSymbol(moduleSymbol.exports.get("default")); - } + const exportDefaultSymbol = isUntypedModuleSymbol(moduleSymbol) ? + moduleSymbol : + moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); @@ -1129,7 +1123,7 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exportedSymbol = getExportsOfSymbol(symbol).get(name); + const exportedSymbol = getExportsOfSymbol(symbol)[name]; if (exportedSymbol) { return resolveSymbol(exportedSymbol); } @@ -1157,7 +1151,7 @@ namespace ts { let symbolFromVariable: Symbol; // First check if module was specified with "export=". If so, get the member from the resolved type - if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=")) { + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { @@ -1418,9 +1412,9 @@ namespace ts { // This provides a name to the module. See the test tests/cases/fourslash/untypedModuleImport.ts const newSymbol = createSymbol(SymbolFlags.ValueModule, quotedName); // Module symbols are expected to have 'exports', although since this is an untyped module it can be empty. - newSymbol.exports = createMap(); + newSymbol.exports = createMap(); // Cache it so subsequent accesses will return the same module. - globals.set(quotedName, newSymbol); + globals[quotedName] = newSymbol; return newSymbol; } @@ -1446,7 +1440,7 @@ namespace ts { // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { - return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="))) || moduleSymbol; + return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports["export="])) || moduleSymbol; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' @@ -1462,7 +1456,7 @@ namespace ts { } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports.get("export=") !== undefined; + return moduleSymbol.exports["export="] !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { @@ -1487,29 +1481,25 @@ namespace ts { * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ - function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { - if (!source) return; - - source.forEach((sourceSymbol, id) => { - const targetSymbol = target.get(id); - if (id !== "default" && !targetSymbol) { - target.set(id, sourceSymbol); + function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { + for (const id in source) { + if (id !== "default" && !target[id]) { + target[id] = source[id]; if (lookupTable && exportNode) { - lookupTable.set(id, { + lookupTable[id] = { specifierText: getTextOfNode(exportNode.moduleSpecifier) - } as ExportCollisionTracker); + } as ExportCollisionTracker; } } - else if (lookupTable && exportNode && id !== "default" && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { - const collisionTracker = lookupTable.get(id); - if (!collisionTracker.exportsWithDuplicate) { - collisionTracker.exportsWithDuplicate = [exportNode]; + else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { + if (!lookupTable[id].exportsWithDuplicate) { + lookupTable[id].exportsWithDuplicate = [exportNode]; } else { - collisionTracker.exportsWithDuplicate.push(exportNode); + lookupTable[id].exportsWithDuplicate.push(exportNode); } } - }); + } } function getExportsForModule(moduleSymbol: Symbol): SymbolTable { @@ -1529,10 +1519,10 @@ namespace ts { visitedSymbols.push(symbol); const symbols = cloneMap(symbol.exports); // All export * declarations are collected in an __export symbol by the binder - const exportStars = symbol.exports.get("__export"); + const exportStars = symbol.exports["__export"]; if (exportStars) { - const nestedSymbols = createMap(); - const lookupTable = createMap(); + const nestedSymbols = createMap(); + const lookupTable = createMap(); for (const node of exportStars.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier); const exportedSymbols = visit(resolvedModule); @@ -1543,20 +1533,21 @@ namespace ts { node as ExportDeclaration ); } - lookupTable.forEach(({ exportsWithDuplicate }, id) => { + for (const id in lookupTable) { + const { exportsWithDuplicate } = lookupTable[id]; // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.get(id)) { - return; + if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols[id]) { + continue; } for (const node of exportsWithDuplicate) { diagnostics.add(createDiagnosticForNode( node, Diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, - lookupTable.get(id).specifierText, + lookupTable[id].specifierText, id )); } - }); + } extendExportSymbols(symbols, nestedSymbols); } return symbols; @@ -1651,14 +1642,15 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; - members.forEach((symbol, id) => { + for (const id in members) { if (!isReservedMemberName(id)) { if (!result) result = []; + const symbol = members[id]; if (symbolIsValue(symbol)) { result.push(symbol); } } - }); + } return result || emptyArray; } @@ -1731,12 +1723,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols.get(symbol.name))) { + if (isAccessible(symbols[symbol.name])) { return [symbol]; } // Check if symbol is any of the alias - return findInMap(symbols, symbolFromSymbolTable => { + return forEachProperty(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -1771,7 +1763,7 @@ namespace ts { let qualify = false; forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok - let symbolFromSymbolTable = symbolTable.get(symbol.name); + let symbolFromSymbolTable = symbolTable[symbol.name]; if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; @@ -2508,7 +2500,7 @@ namespace ts { } writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); - for (const p of sortInV8ObjectInsertionOrder(resolved.properties, p => p.name)) { + for (const p of resolved.properties) { const t = getTypeOfSymbol(p); if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) { const signatures = getSignaturesOfType(t, SignatureKind.Call); @@ -3237,7 +3229,7 @@ namespace ts { // Return the type implied by an object binding pattern function getTypeFromObjectBindingPattern(pattern: ObjectBindingPattern, includePatternInType: boolean, reportErrors: boolean): Type { - const members = createMap(); + const members = createMap(); let hasComputedProperties = false; forEach(pattern.elements, e => { const name = e.propertyName || e.name; @@ -3252,7 +3244,7 @@ namespace ts { const symbol = createSymbol(flags, text); symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; - members.set(symbol.name, symbol); + members[symbol.name] = symbol; }); const result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); if (includePatternInType) { @@ -3842,7 +3834,8 @@ namespace ts { type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; - (type).instantiations = createMap([[getTypeListId(type.typeParameters), type]]); + (type).instantiations = createMap(); + (type).instantiations[getTypeListId(type.typeParameters)] = type; (type).target = type; (type).typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -3884,7 +3877,8 @@ namespace ts { if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. - links.instantiations = createMap([[getTypeListId(links.typeParameters), type]]); + links.instantiations = createMap(); + links.instantiations[getTypeListId(links.typeParameters)] = type; } } else { @@ -3904,7 +3898,7 @@ namespace ts { return expr.kind === SyntaxKind.NumericLiteral || expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral || - expr.kind === SyntaxKind.Identifier && !!symbol.exports.get((expr).text); + expr.kind === SyntaxKind.Identifier && !!symbol.exports[(expr).text]; } function enumHasLiteralMembers(symbol: Symbol) { @@ -3935,7 +3929,7 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes: { [enumMemberValue: number]: EnumLiteralType } = []; + const memberTypes = createMap(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); @@ -3943,7 +3937,8 @@ namespace ts { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); if (!memberTypes[value]) { - memberTypeList.push(memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value)); + const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); + memberTypeList.push(memberType); } } } @@ -3952,7 +3947,7 @@ namespace ts { if (memberTypeList.length > 1) { enumType.flags |= TypeFlags.Union; (enumType).types = memberTypeList; - unionTypes.set(getTypeListId(memberTypeList), enumType); + unionTypes[getTypeListId(memberTypeList)] = enumType; } } } @@ -4094,19 +4089,27 @@ namespace ts { } function createSymbolTable(symbols: Symbol[]): SymbolTable { - return arrayToMap(symbols, symbol => symbol.name); + const result = createMap(); + for (const symbol of symbols) { + result[symbol.name] = symbol; + } + return result; } // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { - return arrayToMap(symbols, symbol => symbol.name, symbol => mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper)); + const result = createMap(); + for (const symbol of symbols) { + result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); + } + return result; } function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!symbols.get(s.name)) { - symbols.set(s.name, s); + if (!symbols[s.name]) { + symbols[s.name] = s; } } } @@ -4115,8 +4118,8 @@ namespace ts { if (!(type).declaredProperties) { const symbol = type.symbol; (type).declaredProperties = getNamedMembers(symbol.members); - (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call")); - (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new")); + (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]); + (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]); (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); (type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); } @@ -4356,8 +4359,8 @@ namespace ts { } else if (symbol.flags & SymbolFlags.TypeLiteral) { const members = symbol.members; - const callSignatures = getSignaturesOfSymbol(members.get("__call")); - const constructSignatures = getSignaturesOfSymbol(members.get("__new")); + const callSignatures = getSignaturesOfSymbol(members["__call"]); + const constructSignatures = getSignaturesOfSymbol(members["__new"]); const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); @@ -4371,7 +4374,7 @@ namespace ts { } if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); - constructSignatures = getSignaturesOfSymbol(symbol.members.get("__constructor")); + constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]); if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } @@ -4429,7 +4432,7 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members.get(name); + const symbol = resolved.members[name]; if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4450,12 +4453,13 @@ namespace ts { const props = type.resolvedProperties; if (props) { const result: Symbol[] = []; - props.forEach(prop => { + for (const key in props) { + const prop = props[key]; // We need to filter out partial properties in union types if (!(prop.flags & SymbolFlags.SyntheticProperty && (prop).isPartial)) { result.push(prop); } - }); + } return result; } return emptyArray; @@ -4572,12 +4576,12 @@ namespace ts { // these partial properties when identifying discriminant properties, but otherwise they are filtered out // and do not appear to be present in the union type. function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol { - const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); - let property = properties.get(name); + const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); + let property = properties[name]; if (!property) { property = createUnionOrIntersectionProperty(type, name); if (property) { - properties.set(name, property); + properties[name] = property; } } return property; @@ -4601,7 +4605,7 @@ namespace ts { type = getApparentType(type); if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members.get(name); + const symbol = resolved.members[name]; if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4700,11 +4704,11 @@ namespace ts { function symbolsToArray(symbols: SymbolTable): Symbol[] { const result: Symbol[] = []; - symbols.forEach((symbol, id) => { + for (const id in symbols) { if (!isReservedMemberName(id)) { - result.push(symbol); + result.push(symbols[id]); } - }); + } return result; } @@ -4996,7 +5000,7 @@ namespace ts { } function getIndexSymbol(symbol: Symbol): Symbol { - return symbol.members.get("__index"); + return symbol.members["__index"]; } function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { @@ -5110,9 +5114,9 @@ namespace ts { function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { const id = getTypeListId(typeArguments); - let type = target.instantiations.get(id); + let type = target.instantiations[id]; if (!type) { - type = setAndReturn(target.instantiations, id, createObjectType(ObjectFlags.Reference, target.symbol)); + type = target.instantiations[id] = createObjectType(ObjectFlags.Reference, target.symbol); type.flags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; type.target = target; type.typeArguments = typeArguments; @@ -5168,7 +5172,7 @@ namespace ts { } const typeArguments = map(node.typeArguments, getTypeFromTypeNodeNoAlias); const id = getTypeListId(typeArguments); - return links.instantiations.get(id) || setAndReturn(links.instantiations, id, instantiateType(type, createTypeMapper(typeParameters, typeArguments))); + return links.instantiations[id] || (links.instantiations[id] = instantiateType(type, createTypeMapper(typeParameters, typeArguments))); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); @@ -5393,7 +5397,8 @@ namespace ts { type.typeParameters = typeParameters; type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; - type.instantiations = createMap([[getTypeListId(type.typeParameters), type]]); + type.instantiations = createMap(); + type.instantiations[getTypeListId(type.typeParameters)] = type; type.target = type; type.typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -5597,10 +5602,10 @@ namespace ts { return types[0]; } const id = getTypeListId(types); - let type = unionTypes.get(id); + let type = unionTypes[id]; if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = setAndReturn(unionTypes, id, createType(TypeFlags.Union | propagatedFlags)); + type = unionTypes[id] = createType(TypeFlags.Union | propagatedFlags); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5668,10 +5673,10 @@ namespace ts { return typeSet[0]; } const id = getTypeListId(typeSet); - let type = intersectionTypes.get(id); + let type = intersectionTypes[id]; if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = setAndReturn(intersectionTypes, id, createType(TypeFlags.Intersection | propagatedFlags)); + type = intersectionTypes[id] = createType(TypeFlags.Intersection | propagatedFlags); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5723,7 +5728,7 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return map.get(text) || setAndReturn(map, text, createLiteralType(flags, text)); + return map[text] || (map[text] = createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -6419,14 +6424,13 @@ namespace ts { return true; } const id = source.id + "," + target.id; - const relation = enumRelation.get(id); - if (relation !== undefined) { - return relation; + if (enumRelation[id] !== undefined) { + return enumRelation[id]; } if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum) || (source.flags & TypeFlags.Union) !== (target.flags & TypeFlags.Union)) { - return setAndReturn(enumRelation, id, false); + return enumRelation[id] = false; } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -6437,14 +6441,14 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - return setAndReturn(enumRelation, id, false); + return enumRelation[id] = false; } } } - return setAndReturn(enumRelation, id, true); + return enumRelation[id] = true; } - function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { + function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { if (target.flags & TypeFlags.Never) return false; if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return true; if (source.flags & TypeFlags.StringLike && target.flags & TypeFlags.String) return true; @@ -6472,7 +6476,7 @@ namespace ts { return false; } - function isTypeRelatedTo(source: Type, target: Type, relation: Map) { + function isTypeRelatedTo(source: Type, target: Type, relation: Map) { if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) { source = (source).regularType; } @@ -6484,7 +6488,7 @@ namespace ts { } if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) { const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; - const related = relation.get(id); + const related = relation[id]; if (related !== undefined) { return related === RelationComparisonResult.Succeeded; } @@ -6508,7 +6512,7 @@ namespace ts { function checkTypeRelatedTo( source: Type, target: Type, - relation: Map, + relation: Map, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { @@ -6516,7 +6520,7 @@ namespace ts { let errorInfo: DiagnosticMessageChain; let sourceStack: Type[]; let targetStack: Type[]; - let maybeStack: Map[]; + let maybeStack: Map[]; let expandingFlags: number; let depth = 0; let overflow = false; @@ -6875,12 +6879,12 @@ namespace ts { return Ternary.False; } const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; - const related = relation.get(id); + const related = relation[id]; if (related !== undefined) { if (reportErrors && related === RelationComparisonResult.Failed) { // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported // failure and continue computing the relation such that errors get reported. - relation.set(id, RelationComparisonResult.FailedAndReported); + relation[id] = RelationComparisonResult.FailedAndReported; } else { return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; @@ -6889,7 +6893,7 @@ namespace ts { if (depth > 0) { for (let i = 0; i < depth; i++) { // If source and target are already being compared, consider them related with assumptions - if (maybeStack[i].get(id)) { + if (maybeStack[i][id]) { return Ternary.Maybe; } } @@ -6906,7 +6910,8 @@ namespace ts { } sourceStack[depth] = source; targetStack[depth] = target; - maybeStack[depth] = createMap([[id, RelationComparisonResult.Succeeded]]); + maybeStack[depth] = createMap(); + maybeStack[depth][id] = RelationComparisonResult.Succeeded; depth++; const saveExpandingFlags = expandingFlags; if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1; @@ -6936,12 +6941,12 @@ namespace ts { const maybeCache = maybeStack[depth]; // If result is definitely true, copy assumptions to global cache, else copy to next level up const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1]; - copyMapEntriesFromTo(maybeCache, destinationCache); + copyProperties(maybeCache, destinationCache); } else { // A false result goes straight into global cache (when something is false under assumptions it // will also be false without assumptions) - relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); + relation[id] = reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed; } return result; } @@ -7130,29 +7135,14 @@ namespace ts { return result; } - /** - * For consistency we report the first error in V8 object insertion order. - * Since that's slow and there usually isn't an error, we only sort properties the second time around. - */ - function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean, redoingInV8ObjectInsertionOrder?: boolean): Ternary { + function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { let result = Ternary.True; - let properties = getPropertiesOfObjectType(source); - if (redoingInV8ObjectInsertionOrder) { - properties = sortInV8ObjectInsertionOrder(properties, prop => prop.name); - } - for (const prop of properties) { + for (const prop of getPropertiesOfObjectType(source)) { if (kind === IndexKind.String || isNumericLiteralName(prop.name)) { - const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors && redoingInV8ObjectInsertionOrder); + const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); if (!related) { if (reportErrors) { - // For consistency, if we report errors we make sure to report the first error in V8's object insertion order. - if (!redoingInV8ObjectInsertionOrder) { - const related = eachPropertyRelatedTo(source, target, kind, reportErrors, /*redoingInV8ObjectInsertionOrder*/ true); - Debug.assert(related === Ternary.False); - } - else { - reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); - } + reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); } return Ternary.False; } @@ -7591,11 +7581,11 @@ namespace ts { } function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { - const members = createMap(); + const members = createMap(); for (const property of getPropertiesOfObjectType(type)) { const original = getTypeOfSymbol(property); const updated = f(original); - members.set(property.name, updated === original ? property : createTransientSymbol(property, updated)); + members[property.name] = updated === original ? property : createTransientSymbol(property, updated); }; return members; } @@ -7814,7 +7804,7 @@ namespace ts { let targetStack: Type[]; let depth = 0; let inferiority = 0; - const visited = createSet(); + const visited = createMap(); inferFromTypes(originalSource, originalTarget); function isInProcess(source: Type, target: Type) { @@ -7950,10 +7940,10 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (visited.has(key)) { + if (visited[key]) { return; } - visited.add(key); + visited[key] = true; if (depth === 0) { sourceStack = []; targetStack = []; @@ -8304,7 +8294,7 @@ namespace ts { // check. This gives us a quicker out in the common case where an object type is not a function. const resolved = resolveStructuredTypeMembers(type); return !!(resolved.callSignatures.length || resolved.constructSignatures.length || - resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType)); + resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType)); } function getTypeFacts(type: Type): TypeFacts { @@ -8894,13 +8884,12 @@ namespace ts { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); + const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); if (!key) { key = getFlowCacheKey(reference); } - const cached = cache.get(key); - if (cached) { - return cached; + if (cache[key]) { + return cache[key]; } // If this flow loop junction and reference are already being processed, return // the union of the types computed for each branch so far, marked as incomplete. @@ -8934,9 +8923,8 @@ namespace ts { // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up // the resulting type and bail out. - const cached = cache.get(key); - if (cached) { - return cached; + if (cache[key]) { + return cache[key]; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); @@ -8960,7 +8948,7 @@ namespace ts { if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } - return setAndReturn(cache, key, result); + return cache[key] = result; } function isMatchingReferenceDiscriminant(expr: Expression) { @@ -9083,14 +9071,14 @@ namespace ts { // We narrow a non-union type to an exact primitive type if the non-union type // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. - const targetType = typeofTypesByName.get(literal.text); + const targetType = typeofTypesByName[literal.text]; if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : - typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; + typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : + typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } @@ -10594,7 +10582,7 @@ namespace ts { // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - const propertiesTable = createMap(); + const propertiesTable = createMap(); const propertiesArray: Symbol[] = []; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && @@ -10676,7 +10664,7 @@ namespace ts { } } else { - propertiesTable.set(member.name, member); + propertiesTable[member.name] = member; } propertiesArray.push(member); } @@ -10685,12 +10673,12 @@ namespace ts { // type with those properties for which the binding pattern specifies a default value. if (contextualTypeHasPattern) { for (const prop of getPropertiesOfType(contextualType)) { - if (!propertiesTable.get(prop.name)) { + if (!propertiesTable[prop.name]) { if (!(prop.flags & SymbolFlags.Optional)) { error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } - propertiesTable.set(prop.name, prop); + propertiesTable[prop.name] = prop; propertiesArray.push(prop); } } @@ -10767,7 +10755,7 @@ namespace ts { } } - function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Set) { + function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Map) { let correspondingPropType: Type = undefined; // Look up the corresponding property for this attribute @@ -10806,35 +10794,34 @@ namespace ts { checkTypeAssignableTo(exprType, correspondingPropType, node); } - nameTable.add(node.name.text); + nameTable[node.name.text] = true; return exprType; } - function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Set) { + function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Map) { const type = checkExpression(node.expression); const props = getPropertiesOfType(type); for (const prop of props) { // Is there a corresponding property in the element attributes type? Skip checking of properties // that have already been assigned to, as these are not actually pushed into the resulting type - if (!nameTable.has(prop.name)) { + if (!nameTable[prop.name]) { const targetPropSym = getPropertyOfType(elementAttributesType, prop.name); if (targetPropSym) { const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name); checkTypeAssignableTo(getTypeOfSymbol(prop), getTypeOfSymbol(targetPropSym), node, undefined, msg); } - nameTable.add(prop.name); + nameTable[prop.name] = true; } } return type; } function getJsxType(name: string) { - const jsxType = jsxTypes.get(name); - if (jsxType === undefined) { - return setAndReturn(jsxTypes, name, getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); + if (jsxTypes[name] === undefined) { + return jsxTypes[name] = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType; } - return jsxType; + return jsxTypes[name]; } /** @@ -11145,7 +11132,7 @@ namespace ts { const targetAttributesType = getJsxElementAttributesType(node); - const nameTable = createSet(); + const nameTable = createMap(); // Process this array in right-to-left order so we know which // attributes (mostly from spreads) are being overwritten and // thus should have their types ignored @@ -11169,7 +11156,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - !nameTable.has(targetProperties[i].name)) { + !nameTable[targetProperties[i].name]) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } @@ -11899,7 +11886,7 @@ namespace ts { return typeArgumentsAreAssignable; } - function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { + function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { const thisType = getThisTypeOfSignature(signature); if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType @@ -12433,7 +12420,7 @@ namespace ts { diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo)); } - function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { + function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) { for (const originalCandidate of candidates) { if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) { continue; @@ -14622,8 +14609,8 @@ namespace ts { Property = Getter | Setter } - const instanceNames = createMap(); - const staticNames = createMap(); + const instanceNames = createMap(); + const staticNames = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { @@ -14655,24 +14642,24 @@ namespace ts { } } - function addName(names: Map, location: Node, name: string, meaning: Accessor) { - const prev = names.get(name); + function addName(names: Map, location: Node, name: string, meaning: Accessor) { + const prev = names[name]; if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } else { - names.set(name, prev | meaning); + names[name] = prev | meaning; } } else { - names.set(name, meaning); + names[name] = meaning; } } } function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names = createSet(); + const names = createMap(); for (const member of node.members) { if (member.kind == SyntaxKind.PropertySignature) { let memberName: string; @@ -14686,12 +14673,12 @@ namespace ts { continue; } - if (names.has(memberName)) { + if (names[memberName]) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } else { - names.add(memberName); + names[memberName] = true; } } } @@ -15896,7 +15883,8 @@ namespace ts { function checkUnusedLocalsAndParameters(node: Node): void { if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) { - node.locals.forEach(local => { + for (const key in node.locals) { + const local = node.locals[key]; if (!local.isReferenced) { if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { const parameter = getRootDeclaration(local.valueDeclaration); @@ -15911,7 +15899,7 @@ namespace ts { forEach(local.declarations, d => errorUnusedLocal(d.name || d, local.name)); } } - }); + } } } @@ -15977,7 +15965,8 @@ namespace ts { function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { - node.locals.forEach(local => { + for (const key in node.locals) { + const local = node.locals[key]; if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { @@ -15985,7 +15974,7 @@ namespace ts { } } } - }); + } } } @@ -17002,12 +16991,12 @@ namespace ts { else { const blockLocals = catchClause.block.locals; if (blockLocals) { - forEachKeyInMap(catchClause.locals, caughtName => { - const blockLocal = blockLocals.get(caughtName); + for (const caughtName in catchClause.locals) { + const blockLocal = blockLocals[caughtName]; if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); } - }); + } } } } @@ -17426,15 +17415,16 @@ namespace ts { return true; } - const seen: Map = arrayToMap(resolveDeclaredMembers(type).declaredProperties, p => p.name, p => ({ prop: p, containingType: type })); + const seen = createMap<{ prop: Symbol; containingType: Type }>(); + forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); let ok = true; for (const base of baseTypes) { const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { - const existing = seen.get(prop.name); + const existing = seen[prop.name]; if (!existing) { - seen.set(prop.name, { prop, containingType: base }); + seen[prop.name] = { prop: prop, containingType: base }; } else { const isInheritedProperty = existing.containingType !== type; @@ -18176,14 +18166,19 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - return someKeyInMap(moduleSymbol.exports, id => id !== "export="); + for (const id in moduleSymbol.exports) { + if (id !== "export=") { + return true; + } + } + return false; } function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { const moduleSymbol = getSymbolOfNode(node); const links = getSymbolLinks(moduleSymbol); if (!links.exportsChecked) { - const exportEqualsSymbol = moduleSymbol.exports.get("export="); + const exportEqualsSymbol = moduleSymbol.exports["export="]; if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; if (!isTopLevelInExternalModuleAugmentation(declaration)) { @@ -18192,20 +18187,21 @@ namespace ts { } // Checks for export * conflicts const exports = getExportsOfModule(moduleSymbol); - exports && exports.forEach(({ declarations, flags }, id) => { + for (const id in exports) { if (id === "__export") { - return; + continue; } + const { declarations, flags } = exports[id]; // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. // (TS Exceptions: namespaces, function overloads, enums, and interfaces) if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) { - return; + continue; } const exportedDeclarationsCount = countWhere(declarations, isNotOverload); if (flags & SymbolFlags.TypeAlias && exportedDeclarationsCount <= 2) { // it is legal to merge type alias with other values // so count should be either 1 (just type alias) or 2 (type alias + merged value) - return; + continue; } if (exportedDeclarationsCount > 1) { for (const declaration of declarations) { @@ -18214,7 +18210,7 @@ namespace ts { } } } - }); + } links.exportsChecked = true; } @@ -18490,7 +18486,7 @@ namespace ts { } function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - const symbols = createMap(); + const symbols = createMap(); let memberFlags: ModifierFlags = ModifierFlags.None; if (isInsideWithStatementBody(location)) { @@ -18568,17 +18564,18 @@ namespace ts { // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array - if (!symbols.get(id)) { - symbols.set(id, symbol); + if (!symbols[id]) { + symbols[id] = symbol; } } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { if (meaning) { - source.forEach(symbol => { + for (const id in source) { + const symbol = source[id]; copySymbol(symbol, meaning); - }); + } } } } @@ -18989,8 +18986,8 @@ namespace ts { const propsByName = createSymbolTable(getPropertiesOfType(type)); if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) { forEach(getPropertiesOfType(globalFunctionType), p => { - if (!propsByName.get(p.name)) { - propsByName.set(p.name, p); + if (!propsByName[p.name]) { + propsByName[p.name] = p; } }); } @@ -19053,7 +19050,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : someValueInMap(getExportsOfModule(moduleSymbol), isValue); + : forEachProperty(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; @@ -19403,7 +19400,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return !!globals.get(name); + return !!globals[name]; } function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol { @@ -19460,13 +19457,14 @@ namespace ts { if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createFileMap(); - resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => { + for (const key in resolvedTypeReferenceDirectives) { + const resolvedDirective = resolvedTypeReferenceDirectives[key]; if (!resolvedDirective) { - return; + continue; } const file = host.getSourceFile(resolvedDirective.resolvedFileName); fileToDirective.set(file.path, key); - }); + } } return { getReferencedExportContainer, @@ -19590,8 +19588,6 @@ namespace ts { function initializeTypeChecker() { // Bind all source files and propagate errors - performance.mark("heapBeforeBind"); - for (const file of host.getSourceFiles()) { bindSourceFile(file, compilerOptions); } @@ -19613,9 +19609,11 @@ namespace ts { if (file.symbol && file.symbol.globalExports) { // Merge in UMD exports with first-in-wins semantics (see #9771) const source = file.symbol.globalExports; - source.forEach((sourceSymbol, id) => { - setIfNotSet(globals, id, sourceSymbol); - }); + for (const id in source) { + if (!(id in globals)) { + globals[id] = source[id]; + } + } } if ((compilerOptions.isolatedModules || isExternalModule(file)) && !file.isDeclarationFile) { const fileRequestedExternalEmitHelpers = file.flags & NodeFlags.EmitHelperFlags; @@ -19641,8 +19639,6 @@ namespace ts { // Setup global builtins addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0); - performance.mark("heapAfterBind"); - getSymbolLinks(undefinedSymbol).type = undefinedWideningType; getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments"); getSymbolLinks(unknownSymbol).type = unknownType; @@ -20315,7 +20311,7 @@ namespace ts { } function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { - const seen = createMap(); + const seen = createMap(); const Property = 1; const GetAccessor = 2; const SetAccessor = 4; @@ -20378,17 +20374,17 @@ namespace ts { continue; } - const existingKind = seen.get(effectiveName); - if (!existingKind) { - seen.set(effectiveName, currentKind); + if (!seen[effectiveName]) { + seen[effectiveName] = currentKind; } else { + const existingKind = seen[effectiveName]; if (currentKind === Property && existingKind === Property) { grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name)); } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { - seen.set(effectiveName, currentKind | existingKind); + seen[effectiveName] = currentKind | existingKind; } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); @@ -20402,7 +20398,7 @@ namespace ts { } function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - const seen = createSet(); + const seen = createMap(); for (const attr of node.attributes) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; @@ -20410,8 +20406,8 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!seen.has(name.text)) { - seen.add(name.text); + if (!seen[name.text]) { + seen[name.text] = true; } else { return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); @@ -20905,11 +20901,11 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; - globals.forEach((global, sym) => { + for (const sym in globals) { if (ambientModuleSymbolRegex.test(sym)) { - result.push(global); + result.push(globals[sym]); } - }); + } return result; } } diff --git a/src/compiler/collections.ts b/src/compiler/collections.ts deleted file mode 100644 index 01f3da2b9d342..0000000000000 --- a/src/compiler/collections.ts +++ /dev/null @@ -1,609 +0,0 @@ -// NumberMap, StringMap, and StringSet shims -/* @internal */ -namespace ts { - // The global Map object. This may not be available, so we must test for it. - // Non-ES6 native maps don't support constructor arguments, so `createMap` must provide that functionality. - declare const Map: { new(): Map } | undefined; - const usingNativeMaps = typeof Map !== "undefined"; - // tslint:disable-next-line:no-in-operator - const usingES6NativeMaps = usingNativeMaps && "keys" in Map.prototype && "values" in Map.prototype && "entries" in Map.prototype; - - /** Extra Map methods that may not be available, so we must provide fallbacks. */ - interface ES6Map extends Map { - keys(): Iterator; - values(): Iterator; - entries(): Iterator<[K, V]>; - } - - /** Simplified ES6 Iterator interface. */ - interface Iterator { - next(): { value: T, done: false } | { value: never, done: true }; - } - - /** - * Provides Map-like functionality for ES5 runtimes. - * This is intentionally *not* a full Map shim, and doesn't provide iterators (which aren't available for IE Maps anyway). - * We can only efficiently support strings and number keys, and iteration will always yield stringified keys. - */ - class ShimMap implements Map { - private data = createDictionaryModeObject(); - - /* - So long as `K extends string | number`, we can cast `key as string` and insert it into the map. - However, `forEach` will iterate over strings because values are stringified before being put in the map. - */ - - constructor() {} - - clear(): void { - this.data = createDictionaryModeObject(); - } - - delete(key: K): boolean { - const had = this.has(key); - if (had) { - delete this.data[key as string]; - } - return had; - } - - get(key: K): V { - return this.data[key as string]; - } - - has(key: K): boolean { - // tslint:disable-next-line:no-in-operator - return (key as string) in this.data; - } - - set(key: K, value: V): void { - this.data[key as string] = value; - } - - forEach(action: (value: V, key: string) => void): void { - for (const key in this.data) { - action(this.data[key], key); - } - } - } - - const MapCtr = usingNativeMaps ? Map : ShimMap; - /** - * In runtimes without Maps, this is implemented using an object. - * `pairs` is an optional list of entries to add to the new map. - */ - export function createMap(pairs?: [K, V][]): Map { - const map = new MapCtr(); - - if (pairs) { - for (const [key, value] of pairs) { - map.set(key, value); - } - } - - return map; - } - - const createObject = Object.create; - function createDictionaryModeObject(): MapLike { - const map = createObject(null); // tslint:disable-line:no-null-keyword - - // Using 'delete' on an object causes V8 to put the object in dictionary mode. - // This disables creation of hidden classes, which are expensive when an object is - // constantly changing shape. - map["__"] = undefined; - delete map["__"]; - - return map; - } - - /** - * Iterates over entries in the map, returning the first output of `getResult` that is not `undefined`. - * Only works for strings because shims iterate with `for-in`. - */ - export const findInMap: (map: Map, getResult: (value: V, key: string) => U | undefined) => U | undefined = usingES6NativeMaps - ? (map: ES6Map, f: (value: V, key: string) => U | undefined) => { - const iter = map.entries(); - while (true) { - const { value: pair, done } = iter.next(); - if (done) { - return undefined; - } - const [key, value] = pair; - const result = f(value, key); - if (result !== undefined) { - return result; - } - } - } - : (map: Map, f: (value: V, key: string) => U | undefined) => { - let result: U | undefined; - map.forEach((value, key) => { - if (result === undefined) - result = f(value, key); - }); - return result; - }; - - /** - * Whether `predicate` is true for at least one entry in the map. - * Only works for strings because shims iterate with `for-in`. - */ - export const someInMap: (map: Map, predicate: (value: V, key: string) => boolean) => boolean = usingES6NativeMaps - ? (map: ES6Map, predicate: (value: V, key: string) => boolean) => - someInIterator(map.entries(), ([key, value]) => predicate(value, key)) - : (map: Map, predicate: (value: V, key: string) => boolean) => { - let found = false; - map.forEach((value, key) => { - found = found || predicate(value, key); - }); - return found; - }; - - /** - * Whether `predicate` is true for at least one key in the map. - * Only works for strings because shims iterate with `for-in`. - */ - export const someKeyInMap: (map: Map, predicate: (key: string) => boolean) => boolean = usingES6NativeMaps - ? (map: ES6Map, predicate: (key: string) => boolean) => someInIterator(map.keys(), predicate) - : (map: Map, predicate: (key: string) => boolean) => - someInMap(map, (_value, key) => predicate(key)); - - /** Whether `predicate` is true for at least one value in the map. */ - export const someValueInMap: (map: Map, predicate: (value: T) => boolean) => boolean = usingES6NativeMaps - ? (map: ES6Map, predicate: (value: T) => boolean) => - someInIterator(map.values(), predicate) - : someInMap; - - function someInIterator(iterator: Iterator, predicate: (value: T) => boolean): boolean { - while (true) { - const { value, done } = iterator.next(); - if (done) { - return false; - } - if (predicate(value)) { - return true; - } - } - } - - /** - * Equivalent to the ES6 code: - * `for (const key of map.keys()) action(key);` - * Only works for strings because shims iterate with `for-in`. - */ - export const forEachKeyInMap: (map: Map, action: (key: string) => void) => void = usingES6NativeMaps - ? (map: ES6Map, action: (key: string) => void) => { - const iter: Iterator = map.keys(); - while (true) { - const { value: key, done } = iter.next(); - if (done) { - return; - } - action(key); - } - } - : (map: Map, action: (key: string) => void) => { - map.forEach((_value, key) => action(key)); - }; - - /** Size of a map. */ - export const mapSize: (map: Map) => number = usingNativeMaps - ? map => (map as any).size - : map => { - let size = 0; - map.forEach(() => { size++; }); - return size; - }; - - /** Convert a Map to a MapLike. */ - export function mapLikeOfMap(map: Map): MapLike { - const obj = createDictionaryModeObject(); - map.forEach((value, key) => { - obj[key] = value; - }); - return obj; - } - - /** Create a map from a MapLike. This is useful for writing large maps as object literals. */ - export function mapOfMapLike(object: MapLike): Map { - const map = createMap(); - // Copies keys/values from template. Note that for..in will not throw if - // template is undefined, and instead will just exit the loop. - for (const key in object) if (hasProperty(object, key)) { - map.set(key, object[key]); - } - return map; - } - - class ShimStringSet implements Set { - private data = createDictionaryModeObject(); - - constructor() {} - - add(value: string) { - this.data[value] = true; - } - - clear() { - this.data = createDictionaryModeObject(); - } - - delete(value: string): boolean { - const had = this.has(value); - if (had) { - delete this.data[value]; - } - return had; - } - - forEach(action: (value: string) => void) { - for (const value in this.data) { - action(value); - } - } - - has(value: string) { - // tslint:disable-next-line:no-in-operator - return value in this.data; - } - - isEmpty() { - for (const _ in this.data) { - // TODO: GH#11734 - _; - return false; - } - return true; - } - } - - declare const Set: { new(): Set } | undefined; - const usingNativeSets = typeof Set !== "undefined"; - - const SetCtr = usingNativeSets ? Set : ShimStringSet; - export function createSet(): Set { - return new SetCtr(); - } - - /** False if there are any values in the set. */ - export const setIsEmpty: (set: Set) => boolean = usingNativeSets - ? set => (set as any).size === 0 - : (set: ShimStringSet) => set.isEmpty(); - - // Map utilities - - /** Set a value in a map, then return that value. */ - export function setAndReturn(map: Map, key: K, value: V): V { - map.set(key, value); - return value; - } - - /** False if there are any entries in the map. */ - export function mapIsEmpty(map: Map): boolean { - return !someKeyInMap(map, () => true); - } - - /** Create a new copy of a Map. */ - export function cloneMap(map: Map) { - const clone = createMap(); - copyMapEntriesFromTo(map, clone); - return clone; - } - - /** - * Performs a shallow copy of the properties from a source Map to a target Map - * - * @param source A map from which properties should be copied. - * @param target A map to which properties should be copied. - */ - export function copyMapEntriesFromTo(source: Map, target: Map): void { - source.forEach((value: V, key: K) => { - target.set(key, value); - }); - } - - /** - * Equivalent to `Array.from(map.keys())`. - * Only works for strings because shims iterate with `for-in`. - */ - export function keysOfMap(map: Map): string[] { - const keys: string[] = []; - forEachKeyInMap(map, key => { keys.push(key); }); - return keys; - } - - /** Equivalent to `Array.from(map.values())`. */ - export function valuesOfMap(map: Map): V[] { - const values: V[] = []; - map.forEach((value) => { values.push(value); }); - return values; - } - - /** Return a new map with each key transformed by `getNewKey`. */ - export function transformKeys(map: Map, getNewKey: (key: string) => string): Map { - const newMap = createMap(); - map.forEach((value, key) => { - newMap.set(getNewKey(key), value); - }); - return newMap; - } - - /** Replace each value with the result of calling `getNewValue`. */ - export function updateMapValues(map: Map, getNewValue: (value: V) => V): void { - map.forEach((value, key) => { - map.set(key, getNewValue(value)); - }); - } - - /** - * Change the value at `key` by applying the given function to it. - * If there is no value at `key` then `getNewValue` will be passed `undefined`. - */ - export function modifyValue(map: Map, key: K, getNewValue: (value: V) => V) { - map.set(key, getNewValue(map.get(key))); - } - - /** - * Get a value in the map, or if not already present, set and return it. - * Treats entries set to `undefined` as equivalent to not being set (saving a call to `has`). - */ - export function getOrUpdate(map: Map, key: K, getValue: (key: K) => V): V { - const value = map.get(key); - return value !== undefined ? value : setAndReturn(map, key, getValue(key)); - } - - /** Like `getOrUpdate`, but recognizes `undefined` as having been already set. */ - export function getOrUpdateAndAllowUndefined(map: Map, key: K, getValue: (key: K) => V): V { - return map.has(key) ? map.get(key) : setAndReturn(map, key, getValue(key)); - } - - /** - * Sets the the value if the key is not already in the map. - * Returns whether the value was set. - */ - export function setIfNotSet(map: Map, key: K, value: V): boolean { - const shouldSet = !map.has(key); - if (shouldSet) { - map.set(key, value); - } - return shouldSet; - } - - /** Deletes an entry from a map and returns it; or returns undefined if the key was not in the map. */ - export function tryDelete(map: Map, key: K): V | undefined { - const current = map.get(key); - if (current !== undefined) { - map.delete(key); - return current; - } - else { - return undefined; - } - } - - /** - * Creates a map from the elements of an array. - * - * @param array the array of input elements. - * @param makeKey a function that produces a key for a given element. - * - * This function makes no effort to avoid collisions; if any two elements produce - * the same key with the given 'makeKey' function, then the element with the higher - * index in the array will be the one associated with the produced key. - */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { - const result = createMap(); - for (const value of array) { - result.set(makeKey(value), makeValue ? makeValue(value) : value); - } - return result; - } - - /** - * Adds the value to an array of values associated with the key, and returns the array. - * Creates the array if it does not already exist. - */ - export function multiMapAdd(map: Map, key: K, value: V): V[] { - const values = map.get(key); - if (values) { - values.push(value); - return values; - } - else { - return setAndReturn(map, key, [value]); - } - } - - /** - * Removes a value from an array of values associated with the key. - * Does not preserve the order of those values. - * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. - */ - export function multiMapRemove(map: Map, key: K, value: V): void { - const values = map.get(key); - if (values) { - unorderedRemoveItem(values, value); - if (!values.length) { - map.delete(key); - } - } - } - - /** True if the maps have the same keys and values. */ - export function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: V, right: V) => boolean): boolean { - if (left === right) return true; - if (!left || !right) return false; - const someInLeftHasNoMatch = someInMap(left, (leftValue, leftKey) => { - if (!right.has(leftKey)) return true; - const rightValue = right.get(leftKey); - return !(valuesAreEqual ? valuesAreEqual(leftValue, rightValue) : leftValue === rightValue); - }); - if (someInLeftHasNoMatch) return false; - const someInRightHasNoMatch = someKeyInMap(right, rightKey => !left.has(rightKey)); - return !someInRightHasNoMatch; - } - - /** - * Creates a sorted array of keys. - * Sorts keys according to the iteration order they would have if they were in an object, instead of from a Map. - * This is so that tests run consistently whether or not we have a Map shim in place. - * The difference between Map iteration order and V8 object insertion order is that V8 moves natural-number-like keys to the front. - */ - export function sortInV8ObjectInsertionOrder(values: T[], toKey: (t: T) => string): T[] { - const naturalNumberKeys: T[] = []; - const allOtherKeys: T[] = []; - for (const value of values) { - // "0" looks like a natural but "08" doesn't. - const looksLikeNatural = /^(0|([1-9]\d*))$/.test(toKey(value)); - (looksLikeNatural ? naturalNumberKeys : allOtherKeys).push(value); - } - function toInt(value: T): number { - return parseInt(toKey(value), 10); - } - naturalNumberKeys.sort((a, b) => toInt(a) - toInt(b)); - return naturalNumberKeys.concat(allOtherKeys); - } - - // Set utilities - - /** Union of the `getSet` of each element in the array. */ - export function setAggregate(array: T[], getSet: (t: T) => Set): Set { - const result = createSet(); - for (const value of array) { - copySetValuesFromTo(getSet(value), result); - } - return result; - } - - /** Adds all values in `source` to `target`. */ - function copySetValuesFromTo(source: Set, target: Set): void { - source.forEach(value => target.add(value)); - } - - /** Returns the values in `set` satisfying `predicate`. */ - export function filterSetToArray(set: Set, predicate: (value: T) => boolean): T[] { - const result: T[] = []; - set.forEach(value => { - if (predicate(value)) { - result.push(value); - } - }); - return result; - } - - // MapLike utilities - - const hasOwnProperty = Object.prototype.hasOwnProperty; - - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - if (hasOwnProperty.call(object, id)) { - result[id] = (object)[id]; - } - } - return result; - } - - /** - * Indicates whether a map-like contains an own property with the specified key. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * the 'in' operator. - * - * @param map A map-like. - * @param key A property key. - */ - export function hasProperty(map: MapLike, key: string): boolean { - return hasOwnProperty.call(map, key); - } - - /** - * Gets the value of an owned property in a map-like. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * an indexer. - * - * @param map A map-like. - * @param key A property key. - */ - export function getProperty(map: MapLike, key: string): T | undefined { - return hasOwnProperty.call(map, key) ? map[key] : undefined; - } - - /** - * Gets the owned, enumerable property keys of a map-like. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * Object.keys instead as it offers better performance. - * - * @param map A map-like. - */ - export function getOwnKeys(map: MapLike): string[] { - const keys: string[] = []; - for (const key in map) if (hasOwnProperty.call(map, key)) { - keys.push(key); - } - return keys; - } - - export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; - export function assign, T2>(t: T1, arg1: T2): T1 & T2; - export function assign>(t: T1, ...args: any[]): any; - export function assign>(t: T1, ...args: any[]) { - for (const arg of args) { - for (const p of getOwnKeys(arg)) { - t[p] = arg[p]; - } - } - return t; - } - - /** - * Reduce the properties defined on a map-like (but not from its prototype chain). - * - * @param map The map-like to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - for (const key in map) if (hasOwnProperty.call(map, key)) { - result = callback(result, map[key], String(key)); - } - return result; - } - - /** - * Performs a shallow equality comparison of the contents of two map-likes. - * - * @param left A map-like whose properties should be compared. - * @param right A map-like whose properties should be compared. - */ - export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { - if (left === right) return true; - if (!left || !right) return false; - for (const key in left) if (hasOwnProperty.call(left, key)) { - if (!hasOwnProperty.call(right, key) === undefined) return false; - if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; - } - for (const key in right) if (hasOwnProperty.call(right, key)) { - if (!hasOwnProperty.call(left, key)) return false; - } - return true; - } - - export function extend(first: T1, second: T2): T1 & T2 { - const result: T1 & T2 = {}; - for (const id in second) if (hasOwnProperty.call(second, id)) { - (result as any)[id] = (second as any)[id]; - } - for (const id in first) if (hasOwnProperty.call(first, id)) { - (result as any)[id] = (first as any)[id]; - } - return result; - } -} diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 87337cb1e2e5c..686af79a6cca0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -65,7 +65,7 @@ namespace ts { }, { name: "jsx", - type: mapOfMapLike({ + type: createMap({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React }), @@ -95,7 +95,7 @@ namespace ts { { name: "module", shortName: "m", - type: mapOfMapLike({ + type: createMap({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -109,7 +109,7 @@ namespace ts { }, { name: "newLine", - type: mapOfMapLike({ + type: createMap({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed }), @@ -258,7 +258,7 @@ namespace ts { { name: "target", shortName: "t", - type: mapOfMapLike({ + type: createMap({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES2015, @@ -294,7 +294,7 @@ namespace ts { }, { name: "moduleResolution", - type: mapOfMapLike({ + type: createMap({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, }), @@ -403,7 +403,7 @@ namespace ts { type: "list", element: { name: "lib", - type: mapOfMapLike({ + type: createMap({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -480,8 +480,8 @@ namespace ts { /* @internal */ export interface OptionNameMap { - optionNameMap: Map; - shortOptionNames: Map; + optionNameMap: Map; + shortOptionNames: Map; } /* @internal */ @@ -500,12 +500,12 @@ namespace ts { return optionNameMapCache; } - const optionNameMap = createMap(); - const shortOptionNames = createMap(); + const optionNameMap = createMap(); + const shortOptionNames = createMap(); forEach(optionDeclarations, option => { - optionNameMap.set(option.name.toLowerCase(), option); + optionNameMap[option.name.toLowerCase()] = option; if (option.shortName) { - shortOptionNames.set(option.shortName, option.name); + shortOptionNames[option.shortName] = option.name; } }); @@ -515,16 +515,16 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { - const namesOfType = keysOfMap(opt.type).map(key => `'${key}'`).join(", "); + const namesOfType = Object.keys(opt.type).map(key => `'${key}'`).join(", "); return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } /* @internal */ export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = trimString((value || "")).toLowerCase(); - const customType = opt.type.get(key); - if (customType !== undefined) { - return customType; + const map = opt.type; + if (key in map) { + return map[key]; } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -577,13 +577,13 @@ namespace ts { s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); // Try to translate short option names to their full equivalents. - const short = shortOptionNames.get(s); - if (short !== undefined) { - s = short; + if (s in shortOptionNames) { + s = shortOptionNames[s]; } - const opt = optionNameMap.get(s); - if (opt !== undefined) { + if (s in optionNameMap) { + const opt = optionNameMap[s]; + if (opt.isTSConfigOnly) { errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name)); } @@ -706,7 +706,7 @@ namespace ts { * @param fileNames array of filenames to be generated into tsconfig.json */ /* @internal */ - export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: MapLike } { + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { const compilerOptions = extend(options, defaultInitCompilerOptions); const configurations: any = { compilerOptions: serializeCompilerOptions(compilerOptions) @@ -718,7 +718,7 @@ namespace ts { return configurations; - function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { + function getCustomTypeMapOfCommandLineOption(optionDefinition: CommandLineOption): Map | undefined { if (optionDefinition.type === "string" || optionDefinition.type === "number" || optionDefinition.type === "boolean") { // this is of a type CommandLineOptionOfPrimitiveType return undefined; @@ -731,17 +731,18 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name - return findInMap(customTypeMap, (customValue, key) => { - if (customValue === value) { + for (const key in customTypeMap) { + if (customTypeMap[key] === value) { return key; } - }); + } + return undefined; } - function serializeCompilerOptions(options: CompilerOptions): MapLike { - const result = createMap(); + function serializeCompilerOptions(options: CompilerOptions): Map { + const result = createMap(); const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { @@ -757,13 +758,13 @@ namespace ts { break; default: const value = options[name]; - let optionDefinition = optionsNameMap.get(name.toLowerCase()); + let optionDefinition = optionsNameMap[name.toLowerCase()]; if (optionDefinition) { const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); if (!customTypeMap) { // There is no map associated with this compiler option then use the value as-is // This is the case if the value is expect to be string, number, boolean or list of string - result.set(name, value); + result[name] = value; } else { if (optionDefinition.type === "list") { @@ -771,11 +772,11 @@ namespace ts { for (const element of value as (string | number)[]) { convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap)); } - result.set(name, convertedValue); + result[name] = convertedValue; } else { // There is a typeMap associated with this command-line option so use it to map value back to its name - result.set(name, getNameOfCompilerOptionValue(value, customTypeMap)); + result[name] = getNameOfCompilerOptionValue(value, customTypeMap); } } } @@ -783,7 +784,7 @@ namespace ts { } } } - return mapLikeOfMap(result); + return result; } } @@ -1024,8 +1025,8 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - const opt = optionNameMap.get(id); - if (opt !== undefined) { + if (id in optionNameMap) { + const opt = optionNameMap[id]; defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } else { @@ -1061,9 +1062,8 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - const val = opt.type.get(key); - if (val !== undefined) { - return val; + if (key in opt.type) { + return opt.type[key]; } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -1172,12 +1172,12 @@ namespace ts { // Literal file names (provided via the "files" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map later when when including // wildcard paths. - const literalFileMap = createMap(); + const literalFileMap = createMap(); // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map to store paths matched // via wildcard, and to handle extension priority. - const wildcardFileMap = createMap(); + const wildcardFileMap = createMap(); if (include) { include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false); @@ -1191,7 +1191,7 @@ namespace ts { // file map that marks whether it was a regular wildcard match (with a `*` or `?` token), // or a recursive directory. This information is used by filesystem watchers to monitor for // new entries in these paths. - const wildcardDirectories: Map = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames); + const wildcardDirectories: Map = getWildcardDirectories(include, exclude, basePath, host.useCaseSensitiveFileNames); // Rather than requery this for each file and filespec, we query the supported extensions // once and store it on the expansion context. @@ -1202,7 +1202,7 @@ namespace ts { if (fileNames) { for (const fileName of fileNames) { const file = combinePaths(basePath, fileName); - literalFileMap.set(keyMapper(file), file); + literalFileMap[keyMapper(file)] = file; } } @@ -1225,17 +1225,18 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!literalFileMap.has(key)) { - setIfNotSet(wildcardFileMap, key, file); + if (!(key in literalFileMap) && !(key in wildcardFileMap)) { + wildcardFileMap[key] = file; } } } - const literalFiles = valuesOfMap(literalFileMap); - const wildcardFiles = valuesOfMap(wildcardFileMap).sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); + const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []); + const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []); + wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), - wildcardDirectories: mapLikeOfMap(wildcardDirectories) + wildcardDirectories }; } @@ -1276,7 +1277,7 @@ namespace ts { // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); - const wildcardDirectories = createMap(); + const wildcardDirectories = createMap(); if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { @@ -1289,9 +1290,9 @@ namespace ts { if (match) { const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase(); const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None; - const existingFlags = wildcardDirectories.get(key); + const existingFlags = wildcardDirectories[key]; if (existingFlags === undefined || existingFlags < flags) { - wildcardDirectories.set(key, flags); + wildcardDirectories[key] = flags; if (flags === WatchDirectoryFlags.Recursive) { recursiveKeys.push(key); } @@ -1300,13 +1301,13 @@ namespace ts { } // Remove any subpaths under an existing recursively watched directory. - forEachKeyInMap(wildcardDirectories, key => { + for (const key in wildcardDirectories) { for (const recursiveKey of recursiveKeys) { if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { - wildcardDirectories.delete(key); + delete wildcardDirectories[key]; } } - }); + } } return wildcardDirectories; @@ -1320,13 +1321,13 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function hasFileWithHigherPriorityExtension(file: string, literalFiles: Map, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority); for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { const higherPriorityExtension = extensions[i]; const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); - if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) { + if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) { return true; } } @@ -1342,16 +1343,27 @@ namespace ts { * @param extensionPriority The priority of the extension. * @param context The expansion context. */ - function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { + function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: Map, extensions: string[], keyMapper: (value: string) => string) { const extensionPriority = getExtensionPriority(file, extensions); const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority); for (let i = nextExtensionPriority; i < extensions.length; i++) { const lowerPriorityExtension = extensions[i]; const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); - wildcardFiles.delete(lowerPriorityPath); + delete wildcardFiles[lowerPriorityPath]; } } + /** + * Adds a file to an array of files. + * + * @param output The output array. + * @param file The file path. + */ + function addFileToOutput(output: string[], file: string) { + output.push(file); + return output; + } + /** * Gets a case sensitive key. * diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 208b4d0931a82..f207cf010e2a5 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,5 +1,4 @@ -/// -/// +/// /// /* @internal */ @@ -19,11 +18,31 @@ namespace ts { True = -1 } + const createObject = Object.create; + // More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times. export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator() : undefined; + export function createMap(template?: MapLike): Map { + const map: Map = createObject(null); // tslint:disable-line:no-null-keyword + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map["__"] = undefined; + delete map["__"]; + + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in template) if (hasOwnProperty.call(template, key)) { + map[key] = template[key]; + } + + return map; + } + export function createFileMap(keyMapper?: (key: string) => string): FileMap { - const files = createMap(); + let files = createMap(); return { get, set, @@ -35,33 +54,39 @@ namespace ts { }; function forEachValueInMap(f: (key: Path, value: T) => void) { - files.forEach((value, key) => f(key as Path, value)); + for (const key in files) { + f(key, files[key]); + } } - function getKeys(): Path[] { - return keysOfMap(files) as Path[]; + function getKeys() { + const keys: Path[] = []; + for (const key in files) { + keys.push(key); + } + return keys; } // path should already be well-formed so it does not need to be normalized function get(path: Path): T { - return files.get(toKey(path)); + return files[toKey(path)]; } function set(path: Path, value: T) { - files.set(toKey(path), value); + files[toKey(path)] = value; } function contains(path: Path) { - return files.has(toKey(path)); + return toKey(path) in files; } function remove(path: Path) { const key = toKey(path); - files.delete(key); + delete files[key]; } function clear() { - files.clear(); + files = createMap(); } function toKey(path: Path): string { @@ -682,6 +707,242 @@ namespace ts { return initial; } + const hasOwnProperty = Object.prototype.hasOwnProperty; + + /** + * Indicates whether a map-like contains an own property with the specified key. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * the 'in' operator. + * + * @param map A map-like. + * @param key A property key. + */ + export function hasProperty(map: MapLike, key: string): boolean { + return hasOwnProperty.call(map, key); + } + + /** + * Gets the value of an owned property in a map-like. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * an indexer. + * + * @param map A map-like. + * @param key A property key. + */ + export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; + } + + /** + * Gets the owned, enumerable property keys of a map-like. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * Object.keys instead as it offers better performance. + * + * @param map A map-like. + */ + export function getOwnKeys(map: MapLike): string[] { + const keys: string[] = []; + for (const key in map) if (hasOwnProperty.call(map, key)) { + keys.push(key); + } + return keys; + } + + /** + * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. + * + * @param map A map for which properties should be enumerated. + * @param callback A callback to invoke for each property. + */ + export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U { + let result: U; + for (const key in map) { + if (result = callback(map[key], key)) break; + } + return result; + } + + /** + * Returns true if a Map has some matching property. + * + * @param map A map whose properties should be tested. + * @param predicate An optional callback used to test each property. + */ + export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) { + for (const key in map) { + if (!predicate || predicate(map[key], key)) return true; + } + return false; + } + + /** + * Performs a shallow copy of the properties from a source Map to a target MapLike + * + * @param source A map from which properties should be copied. + * @param target A map to which properties should be copied. + */ + export function copyProperties(source: Map, target: MapLike): void { + for (const key in source) { + target[key] = source[key]; + } + } + + export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; + export function assign, T2>(t: T1, arg1: T2): T1 & T2; + export function assign>(t: T1, ...args: any[]): any; + export function assign>(t: T1, ...args: any[]) { + for (const arg of args) { + for (const p of getOwnKeys(arg)) { + t[p] = arg[p]; + } + } + return t; + } + + /** + * Reduce the properties of a map. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * reduceOwnProperties instead as it offers better runtime safety. + * + * @param map The map to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Reduce the properties defined on a map-like (but not from its prototype chain). + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * reduceProperties instead as it offers better performance. + * + * @param map The map-like to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) if (hasOwnProperty.call(map, key)) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key) === undefined) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; + } + for (const key in right) if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; + } + return true; + } + + /** + * Creates a map from the elements of an array. + * + * @param array the array of input elements. + * @param makeKey a function that produces a key for a given element. + * + * This function makes no effort to avoid collisions; if any two elements produce + * the same key with the given 'makeKey' function, then the element with the higher + * index in the array will be the one associated with the produced key. + */ + export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + const result = createMap(); + for (const value of array) { + result[makeKey(value)] = makeValue ? makeValue(value) : value; + } + return result; + } + + export function isEmpty(map: Map) { + for (const id in map) { + if (hasProperty(map, id)) { + return false; + } + } + return true; + } + + export function cloneMap(map: Map) { + const clone = createMap(); + copyProperties(map, clone); + return clone; + } + + export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; + } + } + return result; + } + + export function extend(first: T1, second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in second) if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; + } + for (const id in first) if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; + } + return result; + } + + /** + * Adds the value to an array of values associated with the key, and returns the array. + * Creates the array if it does not already exist. + */ + export function multiMapAdd(map: Map, key: string | number, value: V): V[] { + const values = map[key]; + if (values) { + values.push(value); + return values; + } + else { + return map[key] = [value]; + } + } + + /** + * Removes a value from an array of values associated with the key. + * Does not preserve the order of those values. + * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. + */ + export function multiMapRemove(map: Map, key: string, value: V): void { + const values = map[key]; + if (values) { + unorderedRemoveItem(values, value); + if (!values.length) { + delete map[key]; + } + } + } + /** * Tests whether a value is an array. */ @@ -780,10 +1041,10 @@ namespace ts { return text.replace(/{(\d+)}/g, (_match, index?) => args[+index + baseIndex]); } - export let localizedDiagnosticMessages: Map = undefined; + export let localizedDiagnosticMessages: Map = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { - return localizedDiagnosticMessages && localizedDiagnosticMessages.get(message.key) || message.message; + return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: (string | number)[]): Diagnostic; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 0c3b2abf3f04d..6458183b23c90 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -59,7 +59,7 @@ namespace ts { let resultHasExternalModuleIndicator: boolean; let currentText: string; let currentLineMap: number[]; - let currentIdentifiers: Map; + let currentIdentifiers: Map; let isCurrentFileExternalModule: boolean; let reportedDeclarationError = false; let errorNameNode: DeclarationName; @@ -75,7 +75,7 @@ namespace ts { // and we could be collecting these paths from multiple files into single one with --out option let referencesOutput = ""; - let usedTypeDirectiveReferences: Set; + let usedTypeDirectiveReferences: Map; // Emit references corresponding to each file const emittedReferencedFiles: SourceFile[] = []; @@ -156,9 +156,9 @@ namespace ts { }); if (usedTypeDirectiveReferences) { - usedTypeDirectiveReferences.forEach(directive => { + for (const directive in usedTypeDirectiveReferences) { referencesOutput += `/// ${newLine}`; - }); + } } return { @@ -267,11 +267,11 @@ namespace ts { } if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = createSet(); + usedTypeDirectiveReferences = createMap(); } for (const directive of typeReferenceDirectives) { - if (!usedTypeDirectiveReferences.has(directive)) { - usedTypeDirectiveReferences.add(directive); + if (!(directive in usedTypeDirectiveReferences)) { + usedTypeDirectiveReferences[directive] = directive; } } } @@ -535,14 +535,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!currentIdentifiers.has(baseName)) { + if (!(baseName in currentIdentifiers)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!currentIdentifiers.has(name)) { + if (!(name in currentIdentifiers)) { return name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d0568293eb7a5..1051c9adca961 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -219,11 +219,11 @@ const _super = (function (geti, seti) { let nodeIdToGeneratedName: string[]; let autoGeneratedIdToGeneratedName: string[]; - let generatedNameSet: Set; + let generatedNameSet: Map; let tempFlags: TempFlags; let currentSourceFile: SourceFile; let currentText: string; - let currentFileIdentifiers: Map; + let currentFileIdentifiers: Map; let extendsEmitted: boolean; let assignEmitted: boolean; let decorateEmitted: boolean; @@ -292,7 +292,7 @@ const _super = (function (geti, seti) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNameSet = createSet(); + generatedNameSet = createMap(); isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files @@ -2645,16 +2645,15 @@ const _super = (function (geti, seti) { function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !currentFileIdentifiers.has(name) && - !generatedNameSet.has(name); + !hasProperty(currentFileIdentifiers, name) && + !hasProperty(generatedNameSet, name); } function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { - if (node.locals) { - const local = node.locals.get(name); + if (node.locals && hasProperty(node.locals, name)) { // We conservatively include alias symbols to cover cases where they're emitted as locals - if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; } } @@ -2703,8 +2702,7 @@ const _super = (function (geti, seti) { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - generatedNameSet.add(generatedName); - return generatedName; + return generatedNameSet[generatedName] = generatedName; } i++; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 13f1230896e84..c557e3514a5de 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1622,7 +1622,7 @@ namespace ts { // flag and setting a parent node. const react = createIdentifier(reactNamespace || "React"); react.flags &= ~NodeFlags.Synthesized; - // Set the parent that is in parse tree + // Set the parent that is in parse tree // this makes sure that parent chain is intact for checker to traverse complete scope tree react.parent = getParseTreeNode(parent); return react; @@ -2805,9 +2805,9 @@ namespace ts { return destEmitNode; } - function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map): Map { - if (!destRanges) destRanges = createMap(); - copyMapEntriesFromTo(sourceRanges, destRanges); + function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { + if (!destRanges) destRanges = createMap(); + copyProperties(sourceRanges, destRanges); return destRanges; } @@ -2899,8 +2899,8 @@ namespace ts { */ export function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { const emitNode = getOrCreateEmitNode(node); - const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); - tokenSourceMapRanges.set(token, range); + const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); + tokenSourceMapRanges[token] = range; return node; } @@ -2941,7 +2941,7 @@ namespace ts { export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { const emitNode = node.emitNode; const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges.get(token); + return tokenSourceMapRanges && tokenSourceMapRanges[token]; } /** @@ -3026,8 +3026,10 @@ namespace ts { * Here we check if alternative name was provided for a given moduleName and return it if possible. */ function tryRenameExternalModule(moduleName: LiteralExpression, sourceFile: SourceFile) { - const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); - return rename && createLiteral(rename); + if (sourceFile.renamedDependencies && hasProperty(sourceFile.renamedDependencies, moduleName.text)) { + return createLiteral(sourceFile.renamedDependencies[moduleName.text]); + } + return undefined; } /** diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ea33473fe40a6..a5b02f98fd382 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -491,7 +491,7 @@ namespace ts { let currentToken: SyntaxKind; let sourceText: string; let nodeCount: number; - let identifiers: Map; + let identifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; @@ -601,7 +601,7 @@ namespace ts { parseDiagnostics = []; parsingContext = 0; - identifiers = createMap(); + identifiers = createMap(); identifierCount = 0; nodeCount = 0; @@ -1104,7 +1104,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return getOrUpdate(identifiers, text, text => text); + return identifiers[text] || (identifiers[text] = text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 6cb73e4d058c7..a48eb117e28b9 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -16,9 +16,9 @@ namespace ts.performance { let enabled = false; let profilerStart = 0; - let counts: Map; - let marks: Map; - let measures: Map; + let counts: Map; + let marks: Map; + let measures: Map; /** * Marks a performance event. @@ -27,8 +27,8 @@ namespace ts.performance { */ export function mark(markName: string) { if (enabled) { - marks.set(markName, timestamp()); - counts.set(markName, (counts.get(markName) || 0) + 1); + marks[markName] = timestamp(); + counts[markName] = (counts[markName] || 0) + 1; profilerEvent(markName); } } @@ -44,9 +44,9 @@ namespace ts.performance { */ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { if (enabled) { - const end = endMarkName && marks.get(endMarkName) || timestamp(); - const start = startMarkName && marks.get(startMarkName) || profilerStart; - measures.set(measureName, (measures.get(measureName) || 0) + (end - start)); + const end = endMarkName && marks[endMarkName] || timestamp(); + const start = startMarkName && marks[startMarkName] || profilerStart; + measures[measureName] = (measures[measureName] || 0) + (end - start); } } @@ -56,7 +56,7 @@ namespace ts.performance { * @param markName The name of the mark. */ export function getCount(markName: string) { - return counts && counts.get(markName) || 0; + return counts && counts[markName] || 0; } /** @@ -65,7 +65,7 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && measures.get(measureName) || 0; + return measures && measures[measureName] || 0; } /** @@ -74,14 +74,16 @@ namespace ts.performance { * @param cb The action to perform for each measure */ export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - measures.forEach((duration, measureName) => cb(measureName, duration)); + for (const key in measures) { + cb(key, measures[key]); + } } /** Enables (and resets) performance measurements for the compiler. */ export function enable() { - counts = createMap(); - marks = createMap(); - measures = createMap(); + counts = createMap(); + marks = createMap(); + measures = createMap(); enabled = true; profilerStart = timestamp(); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 8a2b4d86671bf..532c90f38ec61 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -82,7 +82,7 @@ namespace ts { } export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { - const existingDirectories = createSet(); + const existingDirectories = createMap(); function getCanonicalFileName(fileName: string): string { // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. @@ -114,11 +114,11 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (existingDirectories.has(directoryPath)) { + if (directoryPath in existingDirectories) { return true; } if (sys.directoryExists(directoryPath)) { - existingDirectories.add(directoryPath); + existingDirectories[directoryPath] = true; return true; } return false; @@ -132,21 +132,21 @@ namespace ts { } } - let outputFingerprints: Map; + let outputFingerprints: Map; function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void { if (!outputFingerprints) { - outputFingerprints = createMap(); + outputFingerprints = createMap(); } const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore) { - const fingerprint = outputFingerprints.get(fileName); + if (mtimeBefore && fileName in outputFingerprints) { + const fingerprint = outputFingerprints[fileName]; // If output has not been changed, and the file has no external modification - if (fingerprint && fingerprint.byteOrderMark === writeByteOrderMark && + if (fingerprint.byteOrderMark === writeByteOrderMark && fingerprint.hash === hash && fingerprint.mtime.getTime() === mtimeBefore.getTime()) { return; @@ -157,11 +157,11 @@ namespace ts { const mtimeAfter = sys.getModifiedTime(fileName); - outputFingerprints.set(fileName, { + outputFingerprints[fileName] = { hash, byteOrderMark: writeByteOrderMark, mtime: mtimeAfter - }); + }; } function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { @@ -279,11 +279,11 @@ namespace ts { return []; } const resolutions: T[] = []; - const cache = createMap(); + const cache = createMap(); for (const name of names) { - const result = cache.has(name) - ? cache.get(name) - : setAndReturn(cache, name, loader(name, containingFile)); + const result = name in cache + ? cache[name] + : cache[name] = loader(name, containingFile); resolutions.push(result); } return resolutions; @@ -295,9 +295,9 @@ namespace ts { let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; - let classifiableNames: Set; + let classifiableNames: Map; - let resolvedTypeReferenceDirectives = createMap(); + let resolvedTypeReferenceDirectives = createMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. @@ -312,10 +312,10 @@ namespace ts { // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. - const modulesWithElidedImports = createMap(); + const modulesWithElidedImports = createMap(); // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. - const sourceFilesFoundSearchingNodeModules = createMap(); + const sourceFilesFoundSearchingNodeModules = createMap(); performance.mark("beforeProgram"); @@ -448,11 +448,15 @@ namespace ts { return commonSourceDirectory; } - function getClassifiableNames(): Set { + function getClassifiableNames() { if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - classifiableNames = setAggregate(files, sourceFile => sourceFile.classifiableNames); + classifiableNames = createMap(); + + for (const sourceFile of files) { + copyProperties(sourceFile.classifiableNames, classifiableNames); + } } return classifiableNames; @@ -588,7 +592,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules.get(file.path), + isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, @@ -1128,8 +1132,8 @@ namespace ts { // Get source file from normalized fileName function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile { - let file = filesByName.get(path); - if (file !== undefined) { + if (filesByName.contains(path)) { + const file = filesByName.get(path); // try to check if we've already seen this file but with a different casing in path // NOTE: this only makes sense for case-insensitive file systems if (file && options.forceConsistentCasingInFileNames && getNormalizedAbsolutePath(file.fileName, currentDirectory) !== getNormalizedAbsolutePath(fileName, currentDirectory)) { @@ -1138,20 +1142,20 @@ namespace ts { // If the file was previously found via a node_modules search, but is now being processed as a root file, // then everything it sucks in may also be marked incorrectly, and needs to be checked again. - if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth == 0) { - sourceFilesFoundSearchingNodeModules.set(file.path, false); + if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) { + sourceFilesFoundSearchingNodeModules[file.path] = false; if (!options.noResolve) { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } - modulesWithElidedImports.set(file.path, false); + modulesWithElidedImports[file.path] = false; processImportedModules(file); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && modulesWithElidedImports.get(file.path)) { + else if (file && modulesWithElidedImports[file.path]) { if (currentNodeModulesDepth < maxNodeModuleJsDepth) { - modulesWithElidedImports.set(file.path, false); + modulesWithElidedImports[file.path] = false; processImportedModules(file); } } @@ -1160,7 +1164,7 @@ namespace ts { } // We haven't looked for this file, do so now and cache result - file = host.getSourceFile(fileName, options.target, hostErrorMessage => { + const file = host.getSourceFile(fileName, options.target, hostErrorMessage => { if (refFile !== undefined && refPos !== undefined && refEnd !== undefined) { fileProcessingDiagnostics.add(createFileDiagnostic(refFile, refPos, refEnd - refPos, Diagnostics.Cannot_read_file_0_Colon_1, fileName, hostErrorMessage)); @@ -1172,7 +1176,7 @@ namespace ts { filesByName.set(path, file); if (file) { - sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); + sourceFilesFoundSearchingNodeModules[path] = (currentNodeModulesDepth > 0); file.path = path; if (host.useCaseSensitiveFileNames()) { @@ -1233,7 +1237,7 @@ namespace ts { refFile?: SourceFile, refPos?: number, refEnd?: number): void { // If we already found this library as a primary reference - nothing to do - const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective); + const previousResolution = resolvedTypeReferenceDirectives[typeReferenceDirective]; if (previousResolution && previousResolution.primary) { return; } @@ -1270,7 +1274,7 @@ namespace ts { } if (saveResolution) { - resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective); + resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective; } } @@ -1290,7 +1294,7 @@ namespace ts { function processImportedModules(file: SourceFile) { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { - file.resolvedModules = createMap(); + file.resolvedModules = createMap(); const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral); const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory)); Debug.assert(resolutions.length === moduleNames.length); @@ -1321,7 +1325,7 @@ namespace ts { const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport; if (elideImport) { - modulesWithElidedImports.set(file.path, true); + modulesWithElidedImports[file.path] = true; } else if (shouldAddFile) { const path = toPath(resolvedFileName, currentDirectory, getCanonicalFileName); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index dec76d2b926b6..1c0baa457f4af 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -56,7 +56,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken = mapOfMapLike({ + const textToToken = createMap({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -272,11 +272,11 @@ namespace ts { lookupInUnicodeMap(code, unicodeES3IdentifierPart); } - function makeReverseMap(source: Map): string[] { + function makeReverseMap(source: Map): string[] { const result: string[] = []; - source.forEach((num, name) => { - result[num] = name; - }); + for (const name in source) { + result[source[name]] = name; + } return result; } @@ -288,7 +288,7 @@ namespace ts { /* @internal */ export function stringToToken(s: string): SyntaxKind { - return textToToken.get(s); + return textToToken[s]; } /* @internal */ @@ -362,6 +362,8 @@ namespace ts { return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); } + const hasOwnProperty = Object.prototype.hasOwnProperty; + export function isWhiteSpace(ch: number): boolean { return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); } @@ -1180,11 +1182,8 @@ namespace ts { const len = tokenValue.length; if (len >= 2 && len <= 11) { const ch = tokenValue.charCodeAt(0); - if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) { - token = textToToken.get(tokenValue); - if (token !== undefined) { - return token; - } + if (ch >= CharacterCodes.a && ch <= CharacterCodes.z && hasOwnProperty.call(textToToken, tokenValue)) { + return token = textToToken[tokenValue]; } } return token = SyntaxKind.Identifier; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index fcf85aa86a676..46db5188e339c 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -362,7 +362,7 @@ namespace ts { const emitNode = node && node.emitNode; const emitFlags = emitNode && emitNode.flags; - const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges.get(token); + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges[token]; tokenPos = skipTrivia(currentSourceText, range ? range.pos : tokenPos); if ((emitFlags & EmitFlags.NoTokenLeadingSourceMaps) === 0 && tokenPos >= 0) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 5df1976f98e34..13bbfc2ab153f 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -237,25 +237,25 @@ namespace ts { const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"]; function createWatchedFileSet() { - const dirWatchers = createMap(); + const dirWatchers = createMap(); // One file can have multiple watchers - const fileWatcherCallbacks = createMap(); + const fileWatcherCallbacks = createMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - const watcher = dirWatchers.get(dirName); + const watcher = dirWatchers[dirName]; if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); - dirWatchers.delete(dirName); + delete dirWatchers[dirName]; } } } function addDirWatcher(dirPath: string): void { - let watcher = dirWatchers.get(dirPath); + let watcher = dirWatchers[dirPath]; if (watcher) { watcher.referenceCount += 1; return; @@ -266,7 +266,7 @@ namespace ts { (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); watcher.referenceCount = 1; - dirWatchers.set(dirPath, watcher); + dirWatchers[dirPath] = watcher; return; } @@ -296,12 +296,9 @@ namespace ts { ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations - if ((eventName === "change" || eventName === "rename")) { - const callbacks = fileWatcherCallbacks.get(fileName); - if (callbacks) { - for (const fileCallback of callbacks) { - fileCallback(fileName); - } + if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) { + for (const fileCallback of fileWatcherCallbacks[fileName]) { + fileCallback(fileName); } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 4534564a854a4..ee9c081e6ebd8 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -12,14 +12,14 @@ /* @internal */ namespace ts { - const moduleTransformerMap = createMap([ - [ModuleKind.ES2015, transformES2015Module], - [ModuleKind.System, transformSystemModule], - [ModuleKind.AMD, transformModule], - [ModuleKind.CommonJS, transformModule], - [ModuleKind.UMD, transformModule], - [ModuleKind.None, transformModule], - ]); + const moduleTransformerMap = createMap({ + [ModuleKind.ES2015]: transformES2015Module, + [ModuleKind.System]: transformSystemModule, + [ModuleKind.AMD]: transformModule, + [ModuleKind.CommonJS]: transformModule, + [ModuleKind.UMD]: transformModule, + [ModuleKind.None]: transformModule, + }); const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -129,7 +129,7 @@ namespace ts { transformers.push(transformGenerators); } - transformers.push(moduleTransformerMap.get(moduleKind) || moduleTransformerMap.get(ModuleKind.None)); + transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]); // The ES5 transformer is last so that it can substitute expressions like `exports.default` // for ES3. diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 4e0b0749d3a97..25c15305c4aa2 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -70,15 +70,15 @@ namespace ts { * set of labels that occurred inside the converted loop * used to determine if labeled jump can be emitted as is or it should be dispatched to calling code */ - labels?: Map; + labels?: Map; /* * collection of labeled jumps that transfer control outside the converted loop. * maps store association 'label -> labelMarker' where * - label - value of label as it appear in code * - label marker - return value that should be interpreted by calling code as 'jump to