From 6b1cc8972d3e59f12cbc895e53ce90e76977faff Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 5 Dec 2016 14:13:32 -0800 Subject: [PATCH 01/20] Use native maps when they're available --- scripts/processDiagnosticMessages.ts | 10 +- src/compiler/binder.ts | 24 +- src/compiler/checker.ts | 397 +++++++++--------- src/compiler/commandLineParser.ts | 75 ++-- src/compiler/core.ts | 327 +++++++++++---- src/compiler/declarationEmitter.ts | 12 +- src/compiler/emitter.ts | 15 +- src/compiler/factory.ts | 28 +- src/compiler/parser.ts | 2 +- src/compiler/performance.ts | 20 +- src/compiler/program.ts | 44 +- src/compiler/scanner.ts | 17 +- src/compiler/sourcemap.ts | 2 +- src/compiler/sys.ts | 19 +- src/compiler/transformer.ts | 18 +- src/compiler/transformers/es2015.ts | 17 +- src/compiler/transformers/generators.ts | 24 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/module/module.ts | 48 +-- src/compiler/transformers/module/system.ts | 48 +-- src/compiler/transformers/ts.ts | 14 +- src/compiler/tsc.ts | 25 +- src/compiler/types.ts | 20 +- src/compiler/utilities.ts | 45 +- src/compiler/visitor.ts | 54 +-- src/harness/fourslash.ts | 28 +- src/harness/harness.ts | 15 +- src/harness/harnessLanguageService.ts | 4 +- src/harness/projectsRunner.ts | 21 +- .../unittests/cachingInServerLSHost.ts | 17 +- src/harness/unittests/moduleResolution.ts | 40 +- .../unittests/reuseProgramStructure.ts | 9 +- src/harness/unittests/session.ts | 16 +- .../unittests/tsserverProjectSystem.ts | 38 +- src/harness/unittests/typingsInstaller.ts | 2 +- src/server/builder.ts | 12 +- src/server/client.ts | 6 +- src/server/editorServices.ts | 70 +-- src/server/lsHost.ts | 7 +- src/server/project.ts | 52 +-- src/server/scriptInfo.ts | 2 +- src/server/session.ts | 6 +- src/server/typingsCache.ts | 25 +- .../typingsInstaller/typingsInstaller.ts | 30 +- src/server/utilities.ts | 21 +- src/services/classifier.ts | 2 +- src/services/codefixes/codeFixProvider.ts | 8 +- src/services/codefixes/importFixes.ts | 24 +- src/services/completions.ts | 38 +- src/services/documentHighlights.ts | 4 +- src/services/documentRegistry.ts | 8 +- src/services/findAllReferences.ts | 21 +- src/services/formatting/rules.ts | 2 +- src/services/goToDefinition.ts | 2 +- src/services/jsTyping.ts | 40 +- src/services/navigateTo.ts | 18 +- src/services/navigationBar.ts | 16 +- src/services/patternMatcher.ts | 6 +- src/services/services.ts | 11 +- src/services/signatureHelp.ts | 2 +- src/services/transpile.ts | 4 +- .../computedPropertyNames10_ES5.types | 4 +- .../computedPropertyNames10_ES6.types | 4 +- .../computedPropertyNames11_ES5.types | 4 +- .../computedPropertyNames11_ES6.types | 4 +- .../computedPropertyNames4_ES5.types | 4 +- .../computedPropertyNames4_ES6.types | 4 +- ...utedPropertyNamesContextualType6_ES5.types | 2 +- ...utedPropertyNamesContextualType6_ES6.types | 2 +- ...ntiationAssignmentWithIndexingOnLHS3.types | 16 +- ...rConstrainsPropertyDeclarations.errors.txt | 4 +- ...ConstrainsPropertyDeclarations2.errors.txt | 4 +- .../numericIndexerConstraint5.errors.txt | 4 +- .../objectLiteralIndexerErrors.errors.txt | 8 +- .../reference/objectLiteralIndexers.types | 10 +- ...ctTypeWithStringNamedNumericProperty.types | 30 +- ...rConstrainsPropertyDeclarations.errors.txt | 8 +- tests/cases/fourslash/fourslash.ts | 12 +- tests/cases/fourslash/localGetReferences.ts | 5 +- tslint.json | 3 +- 80 files changed, 1117 insertions(+), 949 deletions(-) diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index e5eaa46c8e5b9..db7a9e0f1f267 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; @@ -74,7 +74,7 @@ function buildUniqueNameMap(names: string[]): ts.Map { var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined); for (var i = 0; i < names.length; i++) { - nameMap[names[i]] = uniqueNames[i]; + nameMap.set(names[i], uniqueNames[i]); } return nameMap; @@ -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[name]); + var propName = convertPropertyName(nameMap.get(name)); result += ' ' + propName + @@ -114,7 +114,7 @@ function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, for (var i = 0; i < names.length; i++) { var name = names[i]; var diagnosticDetails = messageTable[name]; - var propName = convertPropertyName(nameMap[name]); + var propName = convertPropertyName(nameMap.get(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 a6f5993f6c88d..18a6d00e922dd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -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 = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + symbol = symbolTable.get(name) || set(symbolTable, name, createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { - classifiableNames[name] = name; + classifiableNames.set(name, name); } if (symbol.flags & excludes) { if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. - symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name); + symbol = set(symbolTable, name, createSymbol(SymbolFlags.None, name)); } else { if (node.name) { @@ -1570,7 +1570,7 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); typeLiteralSymbol.members = createMap(); - typeLiteralSymbol.members[symbol.name] = symbol; + typeLiteralSymbol.members.set(symbol.name, symbol); } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1601,9 +1601,9 @@ namespace ts { ? ElementKind.Property : ElementKind.Accessor; - const existingKind = seen[identifier.text]; + const existingKind = seen.get(identifier.text); if (!existingKind) { - seen[identifier.text] = currentKind; + seen.set(identifier.text, currentKind); continue; } @@ -2208,7 +2208,7 @@ namespace ts { constructorFunction.parent = classPrototype; classPrototype.parent = leftSideOfAssignment; - const funcSymbol = container.locals[constructorFunction.text]; + const funcSymbol = container.locals.get(constructorFunction.text); if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { return; } @@ -2239,7 +2239,7 @@ namespace ts { bindAnonymousDeclaration(node, SymbolFlags.Class, bindingName); // Add name of class expression into the map for semantic classifier if (node.name) { - classifiableNames[node.name.text] = node.name.text; + classifiableNames.set(node.name.text, node.name.text); } } @@ -2255,14 +2255,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"); - if (symbol.exports[prototypeSymbol.name]) { + const symbolExport = symbol.exports.get(prototypeSymbol.name); + if (symbolExport) { if (node.name) { node.name.parent = node; } - file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], - Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); + file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); } - symbol.exports[prototypeSymbol.name] = prototypeSymbol; + symbol.exports.set(prototypeSymbol.name, prototypeSymbol); prototypeSymbol.parent = symbol; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc97d347e95ce..00a6ad2c36619 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,4 +1,4 @@ -/// +/// /// /* @internal */ @@ -371,7 +371,7 @@ namespace ts { } const builtinGlobals = createMap(); - builtinGlobals[undefinedSymbol.name] = undefinedSymbol; + builtinGlobals.set(undefinedSymbol.name, undefinedSymbol); initializeTypeChecker(); @@ -492,18 +492,19 @@ namespace ts { } function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { - for (const id in source) { - let targetSymbol = target[id]; + source.forEach((sourceSymbol, id) => { + let targetSymbol = target.get(id); if (!targetSymbol) { - target[id] = source[id]; + target.set(id, sourceSymbol); } else { if (!(targetSymbol.flags & SymbolFlags.Merged)) { - target[id] = targetSymbol = cloneSymbol(targetSymbol); + targetSymbol = cloneSymbol(targetSymbol); + target.set(id, targetSymbol); } - mergeSymbol(targetSymbol, source[id]); + mergeSymbol(targetSymbol, sourceSymbol); } - } + }); } function mergeModuleAugmentation(moduleName: LiteralExpression): void { @@ -544,15 +545,16 @@ namespace ts { } function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { - for (const id in source) { - if (target[id]) { + source.forEach((sourceSymbol, id) => { + const targetSymbol = target.get(id); + if (targetSymbol) { // Error on redeclarations - forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); + forEach(targetSymbol.declarations, addDeclarationDiagnostic(id, message)); } else { - target[id] = source[id]; + target.set(id, sourceSymbol); } - } + }); function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id)); @@ -580,7 +582,7 @@ namespace ts { function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { if (meaning) { - const symbol = symbols[name]; + const symbol = symbols.get(name); if (symbol) { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (symbol.flags & meaning) { @@ -765,7 +767,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["default"]) { + if (result = moduleExports.get("default")) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.name === name) { break loop; @@ -784,9 +786,10 @@ 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. - if (moduleExports[name] && - moduleExports[name].flags === SymbolFlags.Alias && - getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) { + const moduleExport = moduleExports.get(name); + if (moduleExport && + moduleExport.flags === SymbolFlags.Alias && + getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) { break; } } @@ -1114,11 +1117,16 @@ namespace ts { const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier); if (moduleSymbol) { - const exportDefaultSymbol = isShorthandAmbientModuleSymbol(moduleSymbol) ? - moduleSymbol : - moduleSymbol.exports["export="] ? - getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : - resolveSymbol(moduleSymbol.exports["default"]); + let exportDefaultSymbol: Symbol; + if (isShorthandAmbientModuleSymbol(moduleSymbol)) { + exportDefaultSymbol = moduleSymbol; + } + else { + const exportValue = moduleSymbol.exports.get("export="); + exportDefaultSymbol = exportValue + ? getPropertyOfType(getTypeOfSymbol(exportValue), "default") + : resolveSymbol(moduleSymbol.exports.get("default")); + } if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); @@ -1168,7 +1176,7 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exportedSymbol = getExportsOfSymbol(symbol)[name]; + const exportedSymbol = getExportsOfSymbol(symbol).get(name); if (exportedSymbol) { return resolveSymbol(exportedSymbol); } @@ -1196,7 +1204,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["export="]) { + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get("export=")) { symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { @@ -1475,7 +1483,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["export="])) || moduleSymbol; + return moduleSymbol && getMergedSymbol(resolveSymbol(moduleSymbol.exports.get("export="))) || moduleSymbol; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' @@ -1491,7 +1499,7 @@ namespace ts { } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports["export="] !== undefined; + return moduleSymbol.exports.get("export=") !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { @@ -1501,7 +1509,7 @@ namespace ts { function tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined { const symbolTable = getExportsOfModule(moduleSymbol); if (symbolTable) { - return symbolTable[memberName]; + return symbolTable.get(memberName); } } @@ -1524,24 +1532,30 @@ namespace ts { * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables */ 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 (!source) return; + + source.forEach((sourceSymbol, id) => { + if (id === "default") return; + + const targetSymbol = target.get(id); + if (!targetSymbol) { + target.set(id, sourceSymbol); if (lookupTable && exportNode) { - lookupTable[id] = { + lookupTable.set(id, { specifierText: getTextOfNode(exportNode.moduleSpecifier) - } as ExportCollisionTracker; + } as ExportCollisionTracker); } } - else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { - if (!lookupTable[id].exportsWithDuplicate) { - lookupTable[id].exportsWithDuplicate = [exportNode]; + else if (lookupTable && exportNode && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { + const collisionTracker = lookupTable.get(id); + if (!collisionTracker.exportsWithDuplicate) { + collisionTracker.exportsWithDuplicate = [exportNode]; } else { - lookupTable[id].exportsWithDuplicate.push(exportNode); + collisionTracker.exportsWithDuplicate.push(exportNode); } } - } + }); } function getExportsForModule(moduleSymbol: Symbol): SymbolTable { @@ -1561,7 +1575,7 @@ 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["__export"]; + const exportStars = symbol.exports.get("__export"); if (exportStars) { const nestedSymbols = createMap(); const lookupTable = createMap(); @@ -1575,21 +1589,20 @@ namespace ts { node as ExportDeclaration ); } - for (const id in lookupTable) { - const { exportsWithDuplicate } = lookupTable[id]; + lookupTable.forEach(({ exportsWithDuplicate }, 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[id]) { - continue; + if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols.has(id)) { + return; } 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[id].specifierText, + lookupTable.get(id).specifierText, id )); } - } + }); extendExportSymbols(symbols, nestedSymbols); } return symbols; @@ -1684,15 +1697,14 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; - for (const id in members) { + members.forEach((symbol, id) => { if (!isReservedMemberName(id)) { if (!result) result = []; - const symbol = members[id]; if (symbolIsValue(symbol)) { result.push(symbol); } } - } + }); return result || emptyArray; } @@ -1765,12 +1777,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols[symbol.name])) { + if (isAccessible(symbols.get(symbol.name))) { return [symbol]; } // Check if symbol is any of the alias - return forEachProperty(symbols, symbolFromSymbolTable => { + return forEachInMap(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -1805,7 +1817,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[symbol.name]; + let symbolFromSymbolTable = symbolTable.get(symbol.name); if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; @@ -3068,15 +3080,15 @@ namespace ts { const members = createMap(); const names = createMap(); for (const name of properties) { - names[getTextOfPropertyName(name)] = true; + names.set(getTextOfPropertyName(name), true); } for (const prop of getPropertiesOfType(source)) { - const inNamesToRemove = prop.name in names; + const inNamesToRemove = names.has(prop.name); const isPrivate = getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected); const isMethod = prop.flags & SymbolFlags.Method; const isSetOnlyAccessor = prop.flags & SymbolFlags.SetAccessor && !(prop.flags & SymbolFlags.GetAccessor); if (!inNamesToRemove && !isPrivate && !isMethod && !isSetOnlyAccessor) { - members[prop.name] = prop; + members.set(prop.name, prop); } } const stringIndexInfo = getIndexInfoOfType(source, IndexKind.String); @@ -3338,7 +3350,7 @@ namespace ts { const symbol = createSymbol(flags, text); symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; - members[symbol.name] = symbol; + members.set(symbol.name, symbol); }); const result = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, undefined); if (includePatternInType) { @@ -3939,7 +3951,7 @@ namespace ts { type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; (type).instantiations = createMap(); - (type).instantiations[getTypeListId(type.typeParameters)] = type; + (type).instantiations.set(getTypeListId(type.typeParameters), type); (type).target = type; (type).typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -3982,7 +3994,7 @@ namespace ts { // an instantiation of the type alias with the type parameters supplied as type arguments. links.typeParameters = typeParameters; links.instantiations = createMap(); - links.instantiations[getTypeListId(typeParameters)] = type; + links.instantiations.set(getTypeListId(typeParameters), type); } } else { @@ -4002,7 +4014,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[(expr).text]; + expr.kind === SyntaxKind.Identifier && !!symbol.exports.get((expr).text); } function enumHasLiteralMembers(symbol: Symbol) { @@ -4040,8 +4052,8 @@ namespace ts { for (const member of (declaration).members) { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); - if (!memberTypes[value]) { - const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); + if (!memberTypes.has(value)) { + const memberType = set(memberTypes, value, createEnumLiteralType(memberSymbol, enumType, "" + value)); memberTypeList.push(memberType); } } @@ -4051,7 +4063,7 @@ namespace ts { if (memberTypeList.length > 1) { enumType.flags |= TypeFlags.Union; (enumType).types = memberTypeList; - unionTypes[getTypeListId(memberTypeList)] = enumType; + unionTypes.set(getTypeListId(memberTypeList), enumType); } } } @@ -4063,7 +4075,7 @@ namespace ts { if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)); links.declaredType = enumType.flags & TypeFlags.Union ? - enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] : + enumType.memberTypes.get(getEnumMemberValue(symbol.valueDeclaration)) : enumType; } return links.declaredType; @@ -4195,7 +4207,7 @@ namespace ts { function createSymbolTable(symbols: Symbol[]): SymbolTable { const result = createMap(); for (const symbol of symbols) { - result[symbol.name] = symbol; + result.set(symbol.name, symbol); } return result; } @@ -4205,15 +4217,15 @@ namespace ts { function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { const result = createMap(); for (const symbol of symbols) { - result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); + result.set(symbol.name, mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper)); } return result; } function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!symbols[s.name]) { - symbols[s.name] = s; + if (!symbols.has(s.name)) { + symbols.set(s.name, s); } } } @@ -4222,8 +4234,8 @@ namespace ts { if (!(type).declaredProperties) { const symbol = type.symbol; (type).declaredProperties = getNamedMembers(symbol.members); - (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members["__call"]); - (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members["__new"]); + (type).declaredCallSignatures = getSignaturesOfSymbol(symbol.members.get("__call")); + (type).declaredConstructSignatures = getSignaturesOfSymbol(symbol.members.get("__new")); (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); (type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); } @@ -4468,8 +4480,8 @@ namespace ts { } else if (symbol.flags & SymbolFlags.TypeLiteral) { const members = symbol.members; - const callSignatures = getSignaturesOfSymbol(members["__call"]); - const constructSignatures = getSignaturesOfSymbol(members["__new"]); + const callSignatures = getSignaturesOfSymbol(members.get("__call")); + const constructSignatures = getSignaturesOfSymbol(members.get("__new")); const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); setStructuredTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); @@ -4483,7 +4495,7 @@ namespace ts { } if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); - constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]); + constructSignatures = getSignaturesOfSymbol(symbol.members.get("__constructor")); if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } @@ -4541,7 +4553,7 @@ namespace ts { const prop = createSymbol(SymbolFlags.Property | SymbolFlags.Transient | (isOptional ? SymbolFlags.Optional : 0), propName); prop.type = propType; prop.isReadonly = templateReadonly || homomorphicProp && isReadonlySymbol(homomorphicProp); - members[propName] = prop; + members.set(propName, prop); } else if (t.flags & TypeFlags.String) { stringIndexInfo = createIndexInfo(propType, templateReadonly); @@ -4623,7 +4635,7 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = resolved.members.get(name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4644,13 +4656,12 @@ namespace ts { const props = type.resolvedProperties; if (props) { const result: Symbol[] = []; - for (const key in props) { - const prop = props[key]; + props.forEach(prop => { // We need to filter out partial properties in union types if (!(prop.flags & SymbolFlags.SyntheticProperty && (prop).isPartial)) { result.push(prop); } - } + }); return result; } return emptyArray; @@ -4770,11 +4781,11 @@ namespace ts { // 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[name]; + let property = properties.get(name); if (!property) { property = createUnionOrIntersectionProperty(type, name); if (property) { - properties[name] = property; + properties.set(name, property); } } return property; @@ -4798,7 +4809,7 @@ namespace ts { type = getApparentType(type); if (type.flags & TypeFlags.Object) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = resolved.members.get(name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4897,11 +4908,11 @@ namespace ts { function symbolsToArray(symbols: SymbolTable): Symbol[] { const result: Symbol[] = []; - for (const id in symbols) { + symbols.forEach((symbol, id) => { if (!isReservedMemberName(id)) { - result.push(symbols[id]); + result.push(symbol); } - } + }); return result; } @@ -5175,7 +5186,7 @@ namespace ts { function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { const instantiations = signature.instantiations || (signature.instantiations = createMap()); const id = getTypeListId(typeArguments); - return instantiations[id] || (instantiations[id] = createSignatureInstantiation(signature, typeArguments)); + return instantiations.get(id) || set(instantiations, id, createSignatureInstantiation(signature, typeArguments)); } function createSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { @@ -5209,7 +5220,7 @@ namespace ts { } function getIndexSymbol(symbol: Symbol): Symbol { - return symbol.members["__index"]; + return symbol.members.get("__index"); } function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { @@ -5323,9 +5334,9 @@ namespace ts { function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { const id = getTypeListId(typeArguments); - let type = target.instantiations[id]; + let type = target.instantiations.get(id); if (!type) { - type = target.instantiations[id] = createObjectType(ObjectFlags.Reference, target.symbol); + type = set(target.instantiations, id, createObjectType(ObjectFlags.Reference, target.symbol)); type.flags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; type.target = target; type.typeArguments = typeArguments; @@ -5372,7 +5383,7 @@ namespace ts { const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters; const id = getTypeListId(typeArguments); - return links.instantiations[id] || (links.instantiations[id] = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, typeArguments))); + return links.instantiations.get(id) || set(links.instantiations, id, instantiateTypeNoAlias(type, createTypeMapper(typeParameters, typeArguments))); } // Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include @@ -5613,7 +5624,7 @@ namespace ts { type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; type.instantiations = createMap(); - type.instantiations[getTypeListId(type.typeParameters)] = type; + type.instantiations.set(getTypeListId(type.typeParameters), type); type.target = type; type.typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter); @@ -5818,10 +5829,10 @@ namespace ts { return types[0]; } const id = getTypeListId(types); - let type = unionTypes[id]; + let type = unionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = unionTypes[id] = createType(TypeFlags.Union | propagatedFlags); + type = set(unionTypes, id, createType(TypeFlags.Union | propagatedFlags)); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5892,10 +5903,10 @@ namespace ts { /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments); } const id = getTypeListId(typeSet); - let type = intersectionTypes[id]; + let type = intersectionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = intersectionTypes[id] = createType(TypeFlags.Intersection | propagatedFlags); + type = set(intersectionTypes, id, createType(TypeFlags.Intersection | propagatedFlags)); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -6054,7 +6065,7 @@ namespace ts { } // Otherwise we defer the operation by creating an indexed access type. const id = objectType.id + "," + indexType.id; - return indexedAccessTypes[id] || (indexedAccessTypes[id] = createIndexedAccessType(objectType, indexType)); + return indexedAccessTypes.get(id) || set(indexedAccessTypes, id, createIndexedAccessType(objectType, indexType)); } const apparentObjectType = getApparentType(objectType); if (indexType.flags & TypeFlags.Union && !(indexType.flags & TypeFlags.Primitive)) { @@ -6099,7 +6110,7 @@ namespace ts { if (!links.resolvedType) { // Deferred resolution of members is handled by resolveObjectTypeMembers const aliasSymbol = getAliasSymbolForTypeNode(node); - if (isEmpty(node.symbol.members) && !aliasSymbol) { + if (mapIsEmpty(node.symbol.members) && !aliasSymbol) { links.resolvedType = emptyTypeLiteralType; } else { @@ -6164,19 +6175,19 @@ namespace ts { const isOwnProperty = !(rightProp.flags & SymbolFlags.Method) || isFromObjectLiteral; const isSetterWithoutGetter = rightProp.flags & SymbolFlags.SetAccessor && !(rightProp.flags & SymbolFlags.GetAccessor); if (getDeclarationModifierFlagsFromSymbol(rightProp) & (ModifierFlags.Private | ModifierFlags.Protected)) { - skippedPrivateMembers[rightProp.name] = true; + skippedPrivateMembers.set(rightProp.name, true); } else if (isOwnProperty && !isSetterWithoutGetter) { - members[rightProp.name] = rightProp; + members.set(rightProp.name, rightProp); } } for (const leftProp of getPropertiesOfType(left)) { if (leftProp.flags & SymbolFlags.SetAccessor && !(leftProp.flags & SymbolFlags.GetAccessor) - || leftProp.name in skippedPrivateMembers) { + || skippedPrivateMembers.has(leftProp.name)) { continue; } - if (leftProp.name in members) { - const rightProp = members[leftProp.name]; + if (members.has(leftProp.name)) { + const rightProp = members.get(leftProp.name); const rightType = getTypeOfSymbol(rightProp); if (maybeTypeOfKind(rightType, TypeFlags.Undefined) || rightProp.flags & SymbolFlags.Optional) { const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations); @@ -6187,11 +6198,11 @@ namespace ts { result.rightSpread = rightProp; result.declarations = declarations; result.isReadonly = isReadonlySymbol(leftProp) || isReadonlySymbol(rightProp); - members[leftProp.name] = result; + members.set(leftProp.name, result); } } else { - members[leftProp.name] = leftProp; + members.set(leftProp.name, leftProp); } } return createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); @@ -6221,7 +6232,7 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return map[text] || (map[text] = createLiteralType(flags, text)); + return map.get(text) || set(map, text, createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -7006,13 +7017,14 @@ namespace ts { return true; } const id = source.id + "," + target.id; - if (enumRelation[id] !== undefined) { - return enumRelation[id]; + const relation = enumRelation.get(id); + if (relation !== undefined) { + return relation; } 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 enumRelation[id] = false; + return set(enumRelation, id, false); } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -7023,11 +7035,11 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - return enumRelation[id] = false; + return set(enumRelation, id, false); } } } - return enumRelation[id] = true; + return set(enumRelation, id, true); } function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { @@ -7070,7 +7082,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[id]; + const related = relation.get(id); if (related !== undefined) { return related === RelationComparisonResult.Succeeded; } @@ -7527,12 +7539,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[id]; + const related = relation.get(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[id] = RelationComparisonResult.FailedAndReported; + relation.set(id, RelationComparisonResult.FailedAndReported); } else { return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; @@ -7541,7 +7553,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][id]) { + if (maybeStack[i].get(id)) { return Ternary.Maybe; } } @@ -7559,7 +7571,7 @@ namespace ts { sourceStack[depth] = source; targetStack[depth] = target; maybeStack[depth] = createMap(); - maybeStack[depth][id] = RelationComparisonResult.Succeeded; + maybeStack[depth].set(id, RelationComparisonResult.Succeeded); depth++; const saveExpandingFlags = expandingFlags; if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1; @@ -7592,12 +7604,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]; - copyProperties(maybeCache, destinationCache); + copyMapEntries(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[id] = reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed; + relation.set(id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); } return result; } @@ -8266,7 +8278,7 @@ namespace ts { for (const property of getPropertiesOfObjectType(type)) { const original = getTypeOfSymbol(property); const updated = f(original); - members[property.name] = updated === original ? property : createTransientSymbol(property, updated); + members.set(property.name, updated === original ? property : createTransientSymbol(property, updated)); }; return members; } @@ -8508,7 +8520,7 @@ namespace ts { inferredProp.declarations = prop.declarations; inferredProp.type = inferredPropType; inferredProp.isReadonly = readonlyMask && isReadonlySymbol(prop); - members[prop.name] = inferredProp; + members.set(prop.name, inferredProp); } if (indexInfo) { const inferredIndexType = inferTargetType(indexInfo.type); @@ -8683,10 +8695,10 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (visited[key]) { + if (visited.get(key)) { return; } - visited[key] = true; + visited.set(key, true); if (depth === 0) { sourceStack = []; targetStack = []; @@ -9067,7 +9079,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["bind"] && isTypeSubtypeOf(type, globalFunctionType)); + resolved.members.get("bind") && isTypeSubtypeOf(type, globalFunctionType)); } function getTypeFacts(type: Type): TypeFacts { @@ -9687,8 +9699,9 @@ namespace ts { if (!key) { key = getFlowCacheKey(reference); } - if (cache[key]) { - return cache[key]; + const cached = cache.get(key); + if (cached) { + return cached; } // 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. @@ -9722,8 +9735,9 @@ 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. - if (cache[key]) { - return cache[key]; + const cached = cache.get(key); + if (cached) { + return cached; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); @@ -9747,7 +9761,7 @@ namespace ts { if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } - return cache[key] = result; + return set(cache, key, result); } function isMatchingReferenceDiscriminant(expr: Expression) { @@ -9870,14 +9884,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[literal.text]; + const targetType = typeofTypesByName.get(literal.text); if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : - typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; + typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject : + typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } @@ -11524,7 +11538,7 @@ namespace ts { } } else { - propertiesTable[member.name] = member; + propertiesTable.set(member.name, member); } propertiesArray.push(member); } @@ -11533,12 +11547,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[prop.name]) { + if (!propertiesTable.get(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[prop.name] = prop; + propertiesTable.set(prop.name, prop); propertiesArray.push(prop); } } @@ -11676,7 +11690,7 @@ namespace ts { checkTypeAssignableTo(exprType, correspondingPropType, node); } - nameTable[node.name.text] = true; + nameTable.set(node.name.text, true); return exprType; } @@ -11689,24 +11703,25 @@ namespace ts { 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[prop.name]) { + if (!nameTable.get(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[prop.name] = true; + nameTable.set(prop.name, true); } } return type; } function getJsxType(name: string) { - if (jsxTypes[name] === undefined) { - return jsxTypes[name] = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType; + const jsxType = jsxTypes.get(name); + if (jsxType === undefined) { + return set(jsxTypes, name, getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); } - return jsxTypes[name]; + return jsxType; } /** @@ -12039,11 +12054,9 @@ namespace ts { // was spreaded in, though, assume that it provided all required properties if (targetAttributesType && !sawSpreadedAny) { const targetProperties = getPropertiesOfType(targetAttributesType); - for (let i = 0; i < targetProperties.length; i++) { - if (!(targetProperties[i].flags & SymbolFlags.Optional) && - !nameTable[targetProperties[i].name]) { - - error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); + for (const targetProperty of targetProperties) { + if (!(targetProperty.flags & SymbolFlags.Optional) && !nameTable.get(targetProperty.name)) { + error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperty.name, typeToString(targetAttributesType)); } } } @@ -15462,17 +15475,17 @@ namespace ts { } function addName(names: Map, location: Node, name: string, meaning: Accessor) { - const prev = names[name]; + const prev = names.get(name); if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } else { - names[name] = prev | meaning; + names.set(name, prev | meaning); } } else { - names[name] = meaning; + names.set(name, meaning); } } } @@ -15492,12 +15505,12 @@ namespace ts { continue; } - if (names[memberName]) { + if (names.get(memberName)) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } else { - names[memberName] = true; + names.set(memberName, true); } } } @@ -16681,8 +16694,7 @@ namespace ts { function checkUnusedLocalsAndParameters(node: Node): void { if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) { - for (const key in node.locals) { - const local = node.locals[key]; + node.locals.forEach(local => { if (!local.isReferenced) { if (local.valueDeclaration && getRootDeclaration(local.valueDeclaration).kind === SyntaxKind.Parameter) { const parameter = getRootDeclaration(local.valueDeclaration); @@ -16697,7 +16709,7 @@ namespace ts { forEach(local.declarations, d => errorUnusedLocal(d.name || d, local.name)); } } - } + }); } } @@ -16763,8 +16775,7 @@ namespace ts { function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { - for (const key in node.locals) { - const local = node.locals[key]; + node.locals.forEach(local => { if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { @@ -16772,7 +16783,7 @@ namespace ts { } } } - } + }); } } @@ -17801,12 +17812,12 @@ namespace ts { else { const blockLocals = catchClause.block.locals; if (blockLocals) { - for (const caughtName in catchClause.locals) { - const blockLocal = blockLocals[caughtName]; + forEachKeyInMap(catchClause.locals, caughtName => { + const blockLocal = blockLocals.get(caughtName); if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); } - } + }); } } } @@ -18230,15 +18241,15 @@ namespace ts { } const seen = createMap<{ prop: Symbol; containingType: Type }>(); - forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); + forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen.set(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[prop.name]; + const existing = seen.get(prop.name); if (!existing) { - seen[prop.name] = { prop: prop, containingType: base }; + seen.set(prop.name, { prop: prop, containingType: base }); } else { const isInheritedProperty = existing.containingType !== type; @@ -18980,19 +18991,14 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - for (const id in moduleSymbol.exports) { - if (id !== "export=") { - return true; - } - } - return false; + return someKeyInMap(moduleSymbol.exports, id => id !== "export="); } function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { const moduleSymbol = getSymbolOfNode(node); const links = getSymbolLinks(moduleSymbol); if (!links.exportsChecked) { - const exportEqualsSymbol = moduleSymbol.exports["export="]; + const exportEqualsSymbol = moduleSymbol.exports.get("export="); if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; if (!isTopLevelInExternalModuleAugmentation(declaration)) { @@ -19001,21 +19007,20 @@ namespace ts { } // Checks for export * conflicts const exports = getExportsOfModule(moduleSymbol); - for (const id in exports) { + exports && exports.forEach(({ declarations, flags }, id) => { if (id === "__export") { - continue; + return; } - 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)) { - continue; + return; } 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) - continue; + return; } if (exportedDeclarationsCount > 1) { for (const declaration of declarations) { @@ -19024,7 +19029,7 @@ namespace ts { } } } - } + }); links.exportsChecked = true; } @@ -19407,18 +19412,17 @@ 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[id]) { - symbols[id] = symbol; + if (!symbols.has(id)) { + symbols.set(id, symbol); } } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { if (meaning) { - for (const id in source) { - const symbol = source[id]; + source.forEach(symbol => { copySymbol(symbol, meaning); - } + }); } } } @@ -19829,8 +19833,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[p.name]) { - propsByName[p.name] = p; + if (!propsByName.has(p.name)) { + propsByName.set(p.name, p); } }); } @@ -19897,7 +19901,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachProperty(getExportsOfModule(moduleSymbol), isValue); + : someInMap(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; @@ -20247,7 +20251,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return !!globals[name]; + return globals.has(name); } function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol { @@ -20304,14 +20308,13 @@ namespace ts { if (resolvedTypeReferenceDirectives) { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createFileMap(); - for (const key in resolvedTypeReferenceDirectives) { - const resolvedDirective = resolvedTypeReferenceDirectives[key]; + resolvedTypeReferenceDirectives.forEach((resolvedDirective, key) => { if (!resolvedDirective) { - continue; + return; } const file = host.getSourceFile(resolvedDirective.resolvedFileName); fileToDirective.set(file.path, key); - } + }); } return { getReferencedExportContainer, @@ -20455,11 +20458,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; - for (const id in source) { - if (!(id in globals)) { - globals[id] = source[id]; + source.forEach((sourceSymbol, id) => { + if (!globals.has(id)) { + globals.set(id, sourceSymbol); } - } + }); } } @@ -21211,17 +21214,17 @@ namespace ts { continue; } - if (!seen[effectiveName]) { - seen[effectiveName] = currentKind; + const existingKind = seen.get(effectiveName); + if (!existingKind) { + seen.set(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[effectiveName] = currentKind | existingKind; + seen.set(effectiveName, currentKind | existingKind); } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); @@ -21243,8 +21246,8 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!seen[name.text]) { - seen[name.text] = true; + if (!seen.get(name.text)) { + seen.set(name.text, true); } else { return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); @@ -21741,11 +21744,11 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; - for (const sym in globals) { + globals.forEach((global, sym) => { if (ambientModuleSymbolRegex.test(sym)) { - result.push(globals[sym]); + result.push(global); } - } + }); return result; } } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 025218e62fdbb..7571804b5f48a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -531,9 +531,9 @@ namespace ts { const optionNameMap = createMap(); const shortOptionNames = createMap(); forEach(optionDeclarations, option => { - optionNameMap[option.name.toLowerCase()] = option; + optionNameMap.set(option.name.toLowerCase(), option); if (option.shortName) { - shortOptionNames[option.shortName] = option.name; + shortOptionNames.set(option.shortName, option.name); } }); @@ -543,7 +543,7 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { - const namesOfType = Object.keys(opt.type).map(key => `'${key}'`).join(", "); + const namesOfType = keysOfMap(opt.type).map(key => `'${key}'`).join(", "); return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -598,13 +598,13 @@ namespace ts { s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); // Try to translate short option names to their full equivalents. - if (s in shortOptionNames) { - s = shortOptionNames[s]; + const short = shortOptionNames.get(s); + if (short !== undefined) { + s = short; } - if (s in optionNameMap) { - const opt = optionNameMap[s]; - + const opt = optionNameMap.get(s); + if (opt) { if (opt.isTSConfigOnly) { errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name)); } @@ -727,7 +727,7 @@ namespace ts { * @param fileNames array of filenames to be generated into tsconfig.json */ /* @internal */ - export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: MapLike } { const compilerOptions = extend(options, defaultInitCompilerOptions); const configurations: any = { compilerOptions: serializeCompilerOptions(compilerOptions) @@ -752,18 +752,17 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined { + function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name - for (const key in customTypeMap) { - if (customTypeMap[key] === value) { + return forEachInMap(customTypeMap, (mapValue, key) => { + if (mapValue === value) { return key; } - } - return undefined; + }); } - function serializeCompilerOptions(options: CompilerOptions): Map { - const result = createMap(); + function serializeCompilerOptions(options: CompilerOptions): MapLike { + const result = createMapLike(); const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { @@ -779,7 +778,7 @@ namespace ts { break; default: const value = options[name]; - let optionDefinition = optionsNameMap[name.toLowerCase()]; + let optionDefinition = optionsNameMap.get(name.toLowerCase()); if (optionDefinition) { const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); if (!customTypeMap) { @@ -1049,8 +1048,8 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - if (id in optionNameMap) { - const opt = optionNameMap[id]; + const opt = optionNameMap.get(id); + if (opt) { defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } else { @@ -1086,8 +1085,9 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - if (key in opt.type) { - return opt.type[key]; + const val = opt.type.get(key); + if (val !== undefined) { + return val; } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -1215,7 +1215,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 = 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. @@ -1226,7 +1226,7 @@ namespace ts { if (fileNames) { for (const fileName of fileNames) { const file = combinePaths(basePath, fileName); - literalFileMap[keyMapper(file)] = file; + literalFileMap.set(keyMapper(file), file); } } @@ -1249,14 +1249,14 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!(key in literalFileMap) && !(key in wildcardFileMap)) { - wildcardFileMap[key] = file; + if (!literalFileMap.has(key) && !wildcardFileMap.has(key)) { + wildcardFileMap.set(key, file); } } } - const literalFiles = reduceProperties(literalFileMap, addFileToOutput, []); - const wildcardFiles = reduceProperties(wildcardFileMap, addFileToOutput, []); + const literalFiles = valuesOfMap(literalFileMap); + const wildcardFiles = valuesOfMap(wildcardFileMap); wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), @@ -1287,7 +1287,7 @@ namespace ts { /** * Gets directories in a set of include patterns that should be watched for changes. */ - function getWildcardDirectories(include: string[], exclude: string[], path: string, useCaseSensitiveFileNames: boolean): Map { + function getWildcardDirectories(include: string[], exclude: string[], path: string, useCaseSensitiveFileNames: boolean): MapLike { // We watch a directory recursively if it contains a wildcard anywhere in a directory segment // of the pattern: // @@ -1302,7 +1302,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 = createMapLike(); if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { @@ -1331,7 +1331,7 @@ namespace ts { delete wildcardDirectories[key]; } } - } + }; } return wildcardDirectories; @@ -1365,7 +1365,7 @@ namespace ts { for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { const higherPriorityExtension = extensions[i]; const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); - if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) { + if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) { return true; } } @@ -1387,21 +1387,10 @@ namespace ts { for (let i = nextExtensionPriority; i < extensions.length; i++) { const lowerPriorityExtension = extensions[i]; const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); - delete wildcardFiles[lowerPriorityPath]; + wildcardFiles.delete(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 dec6b7cc48568..affd225b3a72c 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -23,13 +23,13 @@ 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 + const createObject = Object.create; + /** Create a MapLike with good performance. Prefer this over a literal `{}`. */ + export function createMapLike(): 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 @@ -37,17 +37,141 @@ namespace ts { map["__"] = undefined; delete map["__"]; + return map; + } + + /** Create a new map. If a template object is provided, the map will copy entries from it. */ + export function createMap(template?: MapLike): Map { + const map: Map = new MapCtr(); + // 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]; + map.set(key, template[key]); } return map; } + /** Create a map from [key, value] pairs. This avoids casting keys to strings. */ + export function createMapFromPairs(...pairs: [number, T][]): Map { + const map: Map = new MapCtr(); + + for (const [key, value] of pairs) { + map.set(key, value); + } + + return map; + } + + /** Methods on native maps but not on shim maps. Only used in this file. */ + interface ES6Map extends Map { + readonly size: number; + entries(): Iterator<[string, T]>; + keys(): Iterator; + } + + /** ES6 Iterator type. */ + interface Iterator { + next(): { value: T, done: false } | { value: never, done: true }; + } + + /** Shim maps support certain methods directly. For native maps we use a function. */ + interface ShimMap extends Map { + isEmpty(): boolean; + forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined; + forEachKey(callback: (key: string) => U | undefined): U | undefined; + some(predicate: (value: T, key: string) => boolean): boolean; + someKey(predicate: (key: string) => boolean): boolean; + } + + // The global Map object. This may not be available, so we must test for it. + declare const Map: { new(): Map } | undefined; + // Internet Explorer's Map doesn't support iteration, so don't use it. + // tslint:disable-next-line:no-in-operator + const usingNativeMaps = typeof Map !== "undefined" && "entries" in Map.prototype; + const MapCtr = usingNativeMaps ? Map : shimMap(); + + // Keep the class inside a function so it doesn't get compiled if it's not used. + function shimMap(): { new(): Map } { + return class implements ShimMap { + private data = createMapLike(); + + get(key: MapKey): T { + return this.data[key]; + } + + set(key: MapKey, value: T): this { + this.data[key] = value; + return this; + } + + has(key: MapKey): boolean { + // tslint:disable-next-line:no-in-operator + return key in this.data; + } + + delete(key: MapKey): boolean { + const had = this.has(key); + if (had) { + delete this.data[key]; + } + return had; + } + + clear(): void { + this.data = createMapLike(); + } + + forEach(action: (value: T, key: string) => void): void { + for (const key in this.data) { + action(this.data[key], key); + } + } + + isEmpty(): boolean { + return !this.someKey(() => true); + } + + forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined { + return this.forEachKey(key => callback(this.data[key], key)); + } + + forEachKey(callback: (key: string) => U | undefined): U | undefined { + for (const key in this.data) { + const result = callback(key); + if (result !== undefined) { + return result; + } + } + } + + some(predicate: (value: T, key: string) => boolean): boolean { + return this.someKey(key => predicate(this.data[key], key)); + } + + someKey(predicate: (key: string) => boolean): boolean { + for (const key in this.data) { + if (predicate(key)) { + return true; + } + } + return false; + } + } + } + + /** + * Unlike `map.set(key, value)`, this returns the value, making it useful as an expression. + * Prefer `map.set(key, value)` for statements. + */ + export function set(map: Map, key: MapKey, value: T): T { + map.set(key, value); + return value; + } + export function createFileMap(keyMapper?: (key: string) => string): FileMap { - let files = createMap(); + const files = createMap(); return { get, set, @@ -59,39 +183,34 @@ namespace ts { }; function forEachValueInMap(f: (key: Path, value: T) => void) { - for (const key in files) { - f(key, files[key]); - } + files.forEach((file, key) => { + f(key, file); + }); } function getKeys() { - const keys: Path[] = []; - for (const key in files) { - keys.push(key); - } - return keys; + return keysOfMap(files) as Path[]; } // path should already be well-formed so it does not need to be normalized function get(path: Path): T { - return files[toKey(path)]; + return files.get(toKey(path)); } function set(path: Path, value: T) { - files[toKey(path)] = value; + files.set(toKey(path), value); } function contains(path: Path) { - return toKey(path) in files; + return files.has(toKey(path)); } function remove(path: Path) { - const key = toKey(path); - delete files[key]; + files.delete(toKey(path)); } function clear() { - files = createMap(); + files.clear(); } function toKey(path: Path): string { @@ -748,9 +867,6 @@ namespace ts { /** * 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. */ @@ -761,9 +877,6 @@ namespace ts { /** * 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. */ @@ -788,49 +901,86 @@ namespace ts { } /** - * 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. + * Array of every key in a map. + * May not actually return string[] if numbers were put into the map. */ - 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; + export function keysOfMap(map: Map): string[] { + const keys: string[] = []; + forEachKeyInMap(map, key => { + keys.push(key); + }); + return keys; } - /** - * 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; + /** Array of every value in a map. */ + export function valuesOfMap(map: Map): T[] { + const values: T[] = []; + map.forEach(value => { + values.push(value); + }); + return values; } /** - * 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. + * Calls `callback` for each entry in the map, returning the first defined result. + * Use `map.forEach` instead for normal iteration. */ - export function copyProperties(source: Map, target: MapLike): void { - for (const key in source) { - target[key] = source[key]; + export const forEachInMap: (map: Map, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps + ? (map: ES6Map, callback: (value: T, key: string) => U | undefined) => { + const iterator = map.entries(); + while (true) { + const { value: pair, done } = iterator.next(); + if (done) return undefined; + const [key, value] = pair; + const result = callback(value, key); + if (result !== undefined) return result; + } } - } + : (map: ShimMap, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback); - export function appendProperty(map: Map, key: string | number, value: T): Map { - if (key === undefined || value === undefined) return map; - if (map === undefined) map = createMap(); - map[key] = value; - return map; + /** `forEachInMap` for just keys. */ + export const forEachKeyInMap: (map: Map<{}>, callback: (key: string) => T | undefined) => T | undefined = usingNativeMaps + ? (map: ES6Map, callback: (key: string) => T | undefined) => { + const iterator = map.keys(); + while (true) { + const { value: key, done } = iterator.next(); + if (done) return undefined; + const result = callback(key); + if (result !== undefined) return result; + } + } + : (map: ShimMap, callback: (key: string) => T | undefined) => map.forEachKey(callback); + + /** Whether `predicate` is true for some entry in the map. */ + export const someInMap: (map: Map, predicate: (value: T, key: string) => boolean) => boolean = usingNativeMaps + ? (map: ES6Map, predicate: (value: T, key: string) => boolean) => { + const iterator = map.entries(); + while (true) { + const { value: pair, done } = iterator.next(); + if (done) return false; + const [key, value] = pair; + if (predicate(value, key)) return true; + } + } + : (map: ShimMap, predicate: (value: T, key: string) => boolean) => map.some(predicate); + + /** Whether `predicate` is true for some key in the map. */ + export const someKeyInMap: (map: Map<{}>, predicate: (key: string) => boolean) => boolean = usingNativeMaps + ? (map: ES6Map<{}>, predicate: (key: string) => boolean) => { + const iterator = map.keys(); + while (true) { + const { value: key, done } = iterator.next(); + if (done) return false; + if (predicate(key)) return true; + } + } + : (map: ShimMap<{}>, predicate: (key: string) => boolean) => map.someKey(predicate); + + /** Copy entries from `source` to `target`. */ + export function copyMapEntries(source: Map, target: Map): void { + source.forEach((value, key) => { + target.set(key, value); + }); } export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; @@ -845,24 +995,6 @@ namespace ts { 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; - } - /** * Performs a shallow equality comparison of the contents of two map-likes. * @@ -882,6 +1014,20 @@ namespace ts { return true; } + /** True if the maps have the same keys and values. */ + export function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: T, right: T) => 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 map from the elements of an array. * @@ -897,23 +1043,18 @@ namespace ts { 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; + result.set(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 const mapIsEmpty: (map: Map<{}>) => boolean = usingNativeMaps + ? (map: ES6Map<{}>) => map.size === 0 + : (map: ShimMap<{}>) => map.isEmpty(); export function cloneMap(map: Map) { const clone = createMap(); - copyProperties(map, clone); + copyMapEntries(map, clone); return clone; } @@ -943,13 +1084,13 @@ namespace ts { * Creates the array if it does not already exist. */ export function multiMapAdd(map: Map, key: string | number, value: V): V[] { - const values = map[key]; + const values = map.get(key); if (values) { values.push(value); return values; } else { - return map[key] = [value]; + return set(map, key, [value]); } } @@ -959,11 +1100,11 @@ namespace ts { * 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]; + const values = map.get(key); if (values) { unorderedRemoveItem(values, value); if (!values.length) { - delete map[key]; + map.delete(key); } } } @@ -1066,7 +1207,7 @@ namespace ts { return text.replace(/{(\d+)}/g, (_match, index?) => args[+index + baseIndex]); } - export let localizedDiagnosticMessages: Map = undefined; + export let localizedDiagnosticMessages: MapLike = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index cd98622e080a1..e010519d64120 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -156,9 +156,9 @@ namespace ts { }); if (usedTypeDirectiveReferences) { - for (const directive in usedTypeDirectiveReferences) { + forEachKeyInMap(usedTypeDirectiveReferences, directive => { referencesOutput += `/// ${newLine}`; - } + }); } return { @@ -271,8 +271,8 @@ namespace ts { usedTypeDirectiveReferences = createMap(); } for (const directive of typeReferenceDirectives) { - if (!(directive in usedTypeDirectiveReferences)) { - usedTypeDirectiveReferences[directive] = directive; + if (!usedTypeDirectiveReferences.has(directive)) { + usedTypeDirectiveReferences.set(directive, directive); } } } @@ -581,14 +581,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!(baseName in currentIdentifiers)) { + if (!currentIdentifiers.has(baseName)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!(name in currentIdentifiers)) { + if (!currentIdentifiers.has(name)) { return name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 90738d828beb9..191a39a57970e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2031,11 +2031,11 @@ namespace ts { // Skip the helper if it can be bundled but hasn't already been emitted and we // are emitting a bundled module. if (shouldBundle) { - if (bundledHelpers[helper.name]) { + if (bundledHelpers.get(helper.name)) { continue; } - bundledHelpers[helper.name] = true; + bundledHelpers.set(helper.name, true); } } else if (isBundle) { @@ -2487,15 +2487,16 @@ namespace ts { function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); + !currentFileIdentifiers.has(name) && + !generatedNameSet.has(name); } function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendantOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { + if (node.locals) { + const local = node.locals.get(name); // We conservatively include alias symbols to cover cases where they're emitted as locals - if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { + if (local && local.flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; } } @@ -2544,7 +2545,7 @@ namespace ts { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - return generatedNameSet[generatedName] = generatedName; + return set(generatedNameSet, generatedName, generatedName); } i++; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ebcea221f9e8c..2ef73dea727a0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2643,7 +2643,7 @@ namespace ts { function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { if (!destRanges) destRanges = createMap(); - copyProperties(sourceRanges, destRanges); + copyMapEntries(sourceRanges, destRanges); return destRanges; } @@ -2745,7 +2745,7 @@ namespace ts { export function getTokenSourceMapRange(node: Node, token: SyntaxKind) { const emitNode = node.emitNode; const tokenSourceMapRanges = emitNode && emitNode.tokenSourceMapRanges; - return tokenSourceMapRanges && tokenSourceMapRanges[token]; + return tokenSourceMapRanges && tokenSourceMapRanges.get(token); } /** @@ -2758,7 +2758,7 @@ namespace ts { export function setTokenSourceMapRange(node: T, token: SyntaxKind, range: TextRange) { const emitNode = getOrCreateEmitNode(node); const tokenSourceMapRanges = emitNode.tokenSourceMapRanges || (emitNode.tokenSourceMapRanges = createMap()); - tokenSourceMapRanges[token] = range; + tokenSourceMapRanges.set(token, range); return node; } @@ -2969,10 +2969,8 @@ 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) { - if (sourceFile.renamedDependencies && hasProperty(sourceFile.renamedDependencies, moduleName.text)) { - return createLiteral(sourceFile.renamedDependencies[moduleName.text]); - } - return undefined; + const rename = sourceFile.renamedDependencies && sourceFile.renamedDependencies.get(moduleName.text); + return rename && createLiteral(rename); } /** @@ -3332,7 +3330,7 @@ namespace ts { else { // export { x, y } for (const specifier of (node).exportClause.elements) { - if (!uniqueExports[specifier.name.text]) { + if (!uniqueExports.get(specifier.name.text)) { const name = specifier.propertyName || specifier.name; multiMapAdd(exportSpecifiers, name.text, specifier); @@ -3343,7 +3341,7 @@ namespace ts { multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); } - uniqueExports[specifier.name.text] = true; + uniqueExports.set(specifier.name.text, true); exportedNames = append(exportedNames, specifier.name); } } @@ -3377,9 +3375,9 @@ namespace ts { else { // export function x() { } const name = (node).name; - if (!uniqueExports[name.text]) { + if (!uniqueExports.get(name.text)) { multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = true; + uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } } @@ -3398,9 +3396,9 @@ namespace ts { else { // export class x { } const name = (node).name; - if (!uniqueExports[name.text]) { + if (!uniqueExports.get(name.text)) { multiMapAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports[name.text] = true; + uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } } @@ -3421,8 +3419,8 @@ namespace ts { } } else if (!isGeneratedIdentifier(decl.name)) { - if (!uniqueExports[decl.name.text]) { - uniqueExports[decl.name.text] = true; + if (!uniqueExports.get(decl.name.text)) { + uniqueExports.set(decl.name.text, true); exportedNames = append(exportedNames, decl.name); } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4aefa1600776a..3da9093ee5a5e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1131,7 +1131,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return identifiers[text] || (identifiers[text] = text); + return identifiers.get(text) || set(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 a48eb117e28b9..8c24b3b9f1be5 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -27,8 +27,8 @@ namespace ts.performance { */ export function mark(markName: string) { if (enabled) { - marks[markName] = timestamp(); - counts[markName] = (counts[markName] || 0) + 1; + marks.set(markName, timestamp()); + counts.set(markName, (counts.get(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[endMarkName] || timestamp(); - const start = startMarkName && marks[startMarkName] || profilerStart; - measures[measureName] = (measures[measureName] || 0) + (end - start); + const end = endMarkName && marks.get(endMarkName) || timestamp(); + const start = startMarkName && marks.get(startMarkName) || profilerStart; + measures.set(measureName, (measures.get(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[markName] || 0; + return counts && counts.get(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[measureName] || 0; + return measures && measures.get(measureName) || 0; } /** @@ -74,9 +74,9 @@ namespace ts.performance { * @param cb The action to perform for each measure */ export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - for (const key in measures) { - cb(key, measures[key]); - } + measures.forEach((measure, key) => { + cb(key, measure); + }); } /** Enables (and resets) performance measurements for the compiler. */ diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 73976d5d02e73..3da9f57ca3903 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -110,11 +110,11 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (directoryPath in existingDirectories) { + if (existingDirectories.has(directoryPath)) { return true; } if (sys.directoryExists(directoryPath)) { - existingDirectories[directoryPath] = true; + existingDirectories.set(directoryPath, true); return true; } return false; @@ -138,11 +138,11 @@ namespace ts { const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore && fileName in outputFingerprints) { - const fingerprint = outputFingerprints[fileName]; - + if (mtimeBefore) { + const fingerprint = outputFingerprints.get(fileName); // If output has not been changed, and the file has no external modification - if (fingerprint.byteOrderMark === writeByteOrderMark && + if (fingerprint && + fingerprint.byteOrderMark === writeByteOrderMark && fingerprint.hash === hash && fingerprint.mtime.getTime() === mtimeBefore.getTime()) { return; @@ -153,11 +153,11 @@ namespace ts { const mtimeAfter = sys.getModifiedTime(fileName); - outputFingerprints[fileName] = { + outputFingerprints.set(fileName, { hash, byteOrderMark: writeByteOrderMark, mtime: mtimeAfter - }; + }); } function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { @@ -277,9 +277,9 @@ namespace ts { const resolutions: T[] = []; const cache = createMap(); for (const name of names) { - const result = name in cache - ? cache[name] - : cache[name] = loader(name, containingFile); + const result = cache.has(name) + ? cache.get(name) + : set(cache, name, loader(name, containingFile)); resolutions.push(result); } return resolutions; @@ -454,7 +454,7 @@ namespace ts { classifiableNames = createMap(); for (const sourceFile of files) { - copyProperties(sourceFile.classifiableNames, classifiableNames); + copyMapEntries(sourceFile.classifiableNames, classifiableNames); } } @@ -729,7 +729,7 @@ namespace ts { } function isSourceFileFromExternalLibrary(file: SourceFile): boolean { - return sourceFilesFoundSearchingNodeModules[file.path]; + return sourceFilesFoundSearchingNodeModules.get(file.path); } function getDiagnosticsProducingTypeChecker() { @@ -1292,20 +1292,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[file.path] && currentNodeModulesDepth == 0) { - sourceFilesFoundSearchingNodeModules[file.path] = false; + if (file && sourceFilesFoundSearchingNodeModules.get(file.path) && currentNodeModulesDepth == 0) { + sourceFilesFoundSearchingNodeModules.set(file.path, false); if (!options.noResolve) { processReferencedFiles(file, isDefaultLib); processTypeReferenceDirectives(file); } - modulesWithElidedImports[file.path] = false; + modulesWithElidedImports.set(file.path, false); processImportedModules(file); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && modulesWithElidedImports[file.path]) { + else if (file && modulesWithElidedImports.get(file.path)) { if (currentNodeModulesDepth < maxNodeModuleJsDepth) { - modulesWithElidedImports[file.path] = false; + modulesWithElidedImports.set(file.path, false); processImportedModules(file); } } @@ -1326,7 +1326,7 @@ namespace ts { filesByName.set(path, file); if (file) { - sourceFilesFoundSearchingNodeModules[path] = (currentNodeModulesDepth > 0); + sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0); file.path = path; if (host.useCaseSensitiveFileNames()) { @@ -1387,7 +1387,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[typeReferenceDirective]; + const previousResolution = resolvedTypeReferenceDirectives.get(typeReferenceDirective); if (previousResolution && previousResolution.primary) { return; } @@ -1427,7 +1427,7 @@ namespace ts { } if (saveResolution) { - resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective; + resolvedTypeReferenceDirectives.set(typeReferenceDirective, resolvedTypeReferenceDirective); } } @@ -1480,7 +1480,7 @@ namespace ts { const shouldAddFile = resolvedFileName && !getResolutionDiagnostic(options, resolution) && !options.noResolve && i < file.imports.length && !elideImport; if (elideImport) { - modulesWithElidedImports[file.path] = true; + modulesWithElidedImports.set(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 82302e98e37a8..4f32b453ecd56 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -275,9 +275,9 @@ namespace ts { function makeReverseMap(source: Map): string[] { const result: string[] = []; - for (const name in source) { - result[source[name]] = name; - } + source.forEach((value, name) => { + result[value] = name; + }); return result; } @@ -289,7 +289,7 @@ namespace ts { /* @internal */ export function stringToToken(s: string): SyntaxKind { - return textToToken[s]; + return textToToken.get(s); } /* @internal */ @@ -363,8 +363,6 @@ namespace ts { return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); } - const hasOwnProperty = Object.prototype.hasOwnProperty; - export function isWhiteSpace(ch: number): boolean { return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); } @@ -1183,8 +1181,11 @@ namespace ts { const len = tokenValue.length; if (len >= 2 && len <= 11) { const ch = tokenValue.charCodeAt(0); - if (ch >= CharacterCodes.a && ch <= CharacterCodes.z && hasOwnProperty.call(textToToken, tokenValue)) { - return token = textToToken[tokenValue]; + if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) { + token = textToToken.get(tokenValue); + if (token !== undefined) { + return token; + } } } return token = SyntaxKind.Identifier; diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 650b9b0ef02b2..a814a91c355ca 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[token]; + const range = emitNode && emitNode.tokenSourceMapRanges && emitNode.tokenSourceMapRanges.get(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 a1e82a66595b0..c34527f895403 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -18,7 +18,7 @@ namespace ts { getFileSize?(path: string): number; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** - * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that + * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching */ watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; @@ -248,18 +248,18 @@ namespace ts { function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - const watcher = dirWatchers[dirName]; + const watcher = dirWatchers.get(dirName); if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); - delete dirWatchers[dirName]; + dirWatchers.delete(dirName); } } } function addDirWatcher(dirPath: string): void { - let watcher = dirWatchers[dirPath]; + let watcher = dirWatchers.get(dirPath); if (watcher) { watcher.referenceCount += 1; return; @@ -270,7 +270,7 @@ namespace ts { (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); watcher.referenceCount = 1; - dirWatchers[dirPath] = watcher; + dirWatchers.set(dirPath, watcher); return; } @@ -300,9 +300,12 @@ namespace ts { ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations - if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) { - for (const fileCallback of fileWatcherCallbacks[fileName]) { - fileCallback(fileName); + if ((eventName === "change" || eventName === "rename")) { + const callbacks = fileWatcherCallbacks.get(fileName); + if (callbacks) { + for (const fileCallback of callbacks) { + fileCallback(fileName); + } } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 10a718448e9e7..1440481d38eea 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -13,14 +13,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 = createMapFromPairs( + [ModuleKind.ES2015, transformES2015Module], + [ModuleKind.System, transformSystemModule], + [ModuleKind.AMD, transformModule], + [ModuleKind.CommonJS, transformModule], + [ModuleKind.UMD, transformModule], + [ModuleKind.None, transformModule], + ); const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -56,7 +56,7 @@ namespace ts { transformers.push(transformGenerators); } - transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]); + transformers.push(moduleTransformerMap.get(moduleKind) || moduleTransformerMap.get(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 c1398f75da605..13c44f2d7d5dc 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -561,7 +561,7 @@ namespace ts { // - break/continue is non-labeled and located in non-converted loop/switch statement const jump = node.kind === SyntaxKind.BreakStatement ? Jump.Break : Jump.Continue; const canUseBreakOrContinue = - (node.label && convertedLoopState.labels && convertedLoopState.labels[node.label.text]) || + (node.label && convertedLoopState.labels && convertedLoopState.labels.get(node.label.text)) || (!node.label && (convertedLoopState.allowedNonLabeledJumps & jump)); if (!canUseBreakOrContinue) { @@ -1929,7 +1929,7 @@ namespace ts { if (!convertedLoopState.labels) { convertedLoopState.labels = createMap(); } - convertedLoopState.labels[node.label.text] = node.label.text; + convertedLoopState.labels.set(node.label.text, node.label.text); } let result: VisitResult; @@ -1941,7 +1941,7 @@ namespace ts { } if (convertedLoopState) { - convertedLoopState.labels[node.label.text] = undefined; + convertedLoopState.labels.set(node.label.text, undefined); } return result; @@ -2548,13 +2548,13 @@ namespace ts { if (!state.labeledNonLocalBreaks) { state.labeledNonLocalBreaks = createMap(); } - state.labeledNonLocalBreaks[labelText] = labelMarker; + state.labeledNonLocalBreaks.set(labelText, labelMarker); } else { if (!state.labeledNonLocalContinues) { state.labeledNonLocalContinues = createMap(); } - state.labeledNonLocalContinues[labelText] = labelMarker; + state.labeledNonLocalContinues.set(labelText, labelMarker); } } @@ -2562,13 +2562,12 @@ namespace ts { if (!table) { return; } - for (const labelText in table) { - const labelMarker = table[labelText]; + table.forEach((labelMarker, labelText) => { const statements: Statement[] = []; // if there are no outer converted loop or outer label in question is located inside outer converted loop // then emit labeled break\continue // otherwise propagate pair 'label -> marker' to outer converted loop and emit 'return labelMarker' so outer loop can later decide what to do - if (!outerLoop || (outerLoop.labels && outerLoop.labels[labelText])) { + if (!outerLoop || (outerLoop.labels && outerLoop.labels.get(labelText))) { const label = createIdentifier(labelText); statements.push(isBreak ? createBreak(label) : createContinue(label)); } @@ -2577,7 +2576,7 @@ namespace ts { statements.push(createReturn(loopResultName)); } caseClauses.push(createCaseClause(createLiteral(labelMarker), statements)); - } + }); } function processLoopVariableDeclaration(decl: VariableDeclaration | BindingElement, loopParameters: ParameterDeclaration[], loopOutParameters: LoopOutParameter[]) { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index c383902d4959e..0b853649e81d3 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -217,13 +217,13 @@ namespace ts { Endfinally = 7, } - const instructionNames = createMap({ - [Instruction.Return]: "return", - [Instruction.Break]: "break", - [Instruction.Yield]: "yield", - [Instruction.YieldStar]: "yield*", - [Instruction.Endfinally]: "endfinally", - }); + const instructionNames = createMapFromPairs( + [Instruction.Return, "return"], + [Instruction.Break, "break"], + [Instruction.Yield, "yield"], + [Instruction.YieldStar, "yield*"], + [Instruction.Endfinally, "endfinally"], + ); export function transformGenerators(context: TransformationContext) { const { @@ -1921,12 +1921,12 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier) { - if (renamedCatchVariables && hasProperty(renamedCatchVariables, node.text)) { + if (renamedCatchVariables && renamedCatchVariables.has(node.text)) { const original = getOriginalNode(node); if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); if (declaration) { - const name = getProperty(renamedCatchVariableDeclarations, String(getOriginalNodeId(declaration))); + const name = renamedCatchVariableDeclarations.get(getOriginalNodeId(declaration)); if (name) { const clone = getMutableClone(name); setSourceMapRange(clone, node); @@ -2096,8 +2096,8 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); } - renamedCatchVariables[text] = true; - renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; + renamedCatchVariables.set(text, true); + renamedCatchVariableDeclarations.set(getOriginalNodeId(variable), name); const exception = peekBlock(); Debug.assert(exception.state < ExceptionBlockState.Catch); @@ -2401,7 +2401,7 @@ namespace ts { */ function createInstruction(instruction: Instruction): NumericLiteral { const literal = createLiteral(instruction); - literal.trailingComment = instructionNames[instruction]; + literal.trailingComment = instructionNames.get(instruction); return literal; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index ecb5dd053e084..0f7a0eb7fba21 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -229,7 +229,7 @@ namespace ts { return String.fromCharCode(parseInt(hex, 16)); } else { - const ch = entities[word]; + const ch = entities.get(word); // If this is not a valid entity, then just use `match` (replace it with itself, i.e. don't replace) return ch ? String.fromCharCode(ch) : match; } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index c900b7d20e198..2e926406fb4e2 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -10,12 +10,12 @@ namespace ts { importAliasNames: ParameterDeclaration[]; } - const transformModuleDelegates = createMap<(node: SourceFile) => SourceFile>({ - [ModuleKind.None]: transformCommonJSModule, - [ModuleKind.CommonJS]: transformCommonJSModule, - [ModuleKind.AMD]: transformAMDModule, - [ModuleKind.UMD]: transformUMDModule, - }); + const transformModuleDelegates = createMapFromPairs<(node: SourceFile) => SourceFile>( + [ModuleKind.None, transformCommonJSModule], + [ModuleKind.CommonJS, transformCommonJSModule], + [ModuleKind.AMD, transformAMDModule], + [ModuleKind.UMD, transformUMDModule], + ); const { startLexicalEnvironment, @@ -60,10 +60,10 @@ namespace ts { } currentSourceFile = node; - currentModuleInfo = moduleInfoMap[getOriginalNodeId(node)] = collectExternalModuleInfo(node, resolver, compilerOptions); + currentModuleInfo = set(moduleInfoMap, getOriginalNodeId(node), collectExternalModuleInfo(node, resolver, compilerOptions)); // Perform the transformation. - const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None]; + const transformModule = transformModuleDelegates.get(moduleKind) || transformModuleDelegates.get(ModuleKind.None); const updated = transformModule(node); currentSourceFile = undefined; @@ -444,7 +444,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -523,7 +523,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -610,7 +610,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); + deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true)); } else { statements = appendExportStatement(statements, createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); @@ -651,7 +651,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -690,7 +690,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -741,7 +741,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); + deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node)); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -794,7 +794,7 @@ namespace ts { // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original); + deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original)); } return node; @@ -820,9 +820,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports[id]; + const statements = deferredExports.get(id); if (statements) { - delete deferredExports[id]; + deferredExports.delete(id); return append(statements, node); } @@ -973,7 +973,7 @@ namespace ts { */ function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined { const name = getDeclarationName(decl); - const exportSpecifiers = currentModuleInfo.exportSpecifiers[name.text]; + const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(name.text); if (exportSpecifiers) { for (const exportSpecifier of exportSpecifiers) { statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name); @@ -997,7 +997,7 @@ namespace ts { function appendExportStatement(statements: Statement[] | undefined, exportName: Identifier, expression: Expression, location?: TextRange, allowComments?: boolean): Statement[] | undefined { if (exportName.text === "default") { const sourceFile = getOriginalNode(currentSourceFile, isSourceFile); - if (sourceFile && !sourceFile.symbol.exports["___esModule"]) { + if (sourceFile && !sourceFile.symbol.exports.get("___esModule")) { if (languageVersion === ScriptTarget.ES3) { statements = append(statements, createStatement( @@ -1102,7 +1102,7 @@ namespace ts { function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node; - currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; + currentModuleInfo = moduleInfoMap.get(getOriginalNodeId(currentSourceFile)); noSubstitution = createMap(); previousOnEmitNode(emitContext, node, emitCallback); @@ -1128,7 +1128,7 @@ namespace ts { */ function onSubstituteNode(emitContext: EmitContext, node: Node) { node = previousOnSubstituteNode(emitContext, node); - if (node.id && noSubstitution[node.id]) { + if (node.id && noSubstitution.get(node.id)) { return node; } @@ -1254,7 +1254,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution[getNodeId(expression)] = true; + noSubstitution.set(getNodeId(expression), true); expression = createExportExpression(exportName, expression, /*location*/ node); } @@ -1296,7 +1296,7 @@ namespace ts { : node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution[getNodeId(expression)] = true; + noSubstitution.set(getNodeId(expression), true); expression = createExportExpression(exportName, expression); } @@ -1318,7 +1318,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (valueDeclaration) { return currentModuleInfo - && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; + && currentModuleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration)); } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 91e29e0988603..8d1e1749c0b63 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -73,11 +73,11 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions); + moduleInfo = set(moduleInfoMap, id, collectExternalModuleInfo(node, resolver, compilerOptions)); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. - exportFunction = exportFunctionsMap[id] = createUniqueName("exports"); + exportFunction = set(exportFunctionsMap, id, createUniqueName("exports")); contextObject = createUniqueName("context"); // Add the body of the module. @@ -121,7 +121,7 @@ namespace ts { } if (noSubstitution) { - noSubstitutionMap[id] = noSubstitution; + noSubstitutionMap.set(id, noSubstitution); noSubstitution = undefined; } @@ -147,13 +147,13 @@ namespace ts { const externalImport = externalImports[i]; const externalModuleName = getExternalModuleNameLiteral(externalImport, currentSourceFile, host, resolver, compilerOptions); const text = externalModuleName.text; - if (hasProperty(groupIndices, text)) { + const groupIndex = groupIndices.get(text); + if (groupIndex !== undefined) { // deduplicate/group entries in dependency list by the dependency name - const groupIndex = groupIndices[text]; dependencyGroups[groupIndex].externalImports.push(externalImport); } else { - groupIndices[text] = dependencyGroups.length; + groupIndices.set(text, dependencyGroups.length); dependencyGroups.push({ name: externalModuleName, externalImports: [externalImport] @@ -305,7 +305,7 @@ namespace ts { // this set is used to filter names brought by star expors. // local names set should only be added if we have anything exported - if (!moduleInfo.exportedNames && isEmpty(moduleInfo.exportSpecifiers)) { + if (!moduleInfo.exportedNames && mapIsEmpty(moduleInfo.exportSpecifiers)) { // no exported declarations (export var ...) or export specifiers (export {x}) // check if we have any non star export declarations. let hasExportDeclarationWithExportClause = false; @@ -608,7 +608,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -631,7 +631,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -656,7 +656,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), expression, /*allowComments*/ true); + deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), expression, /*allowComments*/ true)); } else { return createExportStatement(createIdentifier("default"), expression, /*allowComments*/ true); @@ -688,7 +688,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); } else { hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); @@ -730,7 +730,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); + deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -770,7 +770,7 @@ namespace ts { if (isMarkedDeclaration) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); + deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node, isExportedDeclaration)); } else { statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); @@ -883,7 +883,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); const isExportedDeclaration = hasModifier(node.original, ModifierFlags.Export); - deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original, isExportedDeclaration); + deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original, isExportedDeclaration)); } return node; @@ -909,9 +909,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports[id]; + const statements = deferredExports.get(id); if (statements) { - delete deferredExports[id]; + deferredExports.delete(id); return append(statements, node); } @@ -1080,7 +1080,7 @@ namespace ts { } const name = getDeclarationName(decl); - const exportSpecifiers = moduleInfo.exportSpecifiers[name.text]; + const exportSpecifiers = moduleInfo.exportSpecifiers.get(name.text); if (exportSpecifiers) { for (const exportSpecifier of exportSpecifiers) { if (exportSpecifier.name.text !== excludeName) { @@ -1554,12 +1554,12 @@ namespace ts { if (node.kind === SyntaxKind.SourceFile) { const id = getOriginalNodeId(node); currentSourceFile = node; - moduleInfo = moduleInfoMap[id]; - exportFunction = exportFunctionsMap[id]; - noSubstitution = noSubstitutionMap[id]; + moduleInfo = moduleInfoMap.get(id); + exportFunction = exportFunctionsMap.get(id); + noSubstitution = noSubstitutionMap.get(id); if (noSubstitution) { - delete noSubstitutionMap[id]; + noSubstitutionMap.delete(id); } previousOnEmitNode(emitContext, node, emitCallback); @@ -1756,7 +1756,7 @@ namespace ts { exportedNames = append(exportedNames, getDeclarationName(valueDeclaration)); } - exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); + exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration))); } } @@ -1770,7 +1770,7 @@ namespace ts { */ function preventSubstitution(node: T): T { if (noSubstitution === undefined) noSubstitution = createMap(); - noSubstitution[getNodeId(node)] = true; + noSubstitution.set(getNodeId(node), true); return node; } @@ -1780,7 +1780,7 @@ namespace ts { * @param node The node to test. */ function isSubstitutionPrevented(node: Node) { - return noSubstitution && node.id && noSubstitution[node.id]; + return noSubstitution && node.id && noSubstitution.get(node.id); } } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 77af854269e33..4b0f981f27ce3 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -752,7 +752,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { // record an alias as the class name is not in scope for statics. enableSubstitutionForClassAliases(); - classAliases[getOriginalNodeId(node)] = getSynthesizedClone(temp); + classAliases.set(getOriginalNodeId(node), getSynthesizedClone(temp)); } // To preserve the behavior of the old emitter, we explicitly indent @@ -1420,7 +1420,7 @@ namespace ts { return undefined; } - const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; + const classAlias = classAliases && classAliases.get(getOriginalNodeId(node)); const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); const decorate = createDecorateHelper(context, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); @@ -2530,8 +2530,8 @@ namespace ts { currentScopeFirstDeclarationsOfName = createMap(); } - if (!(name in currentScopeFirstDeclarationsOfName)) { - currentScopeFirstDeclarationsOfName[name] = node; + if (!currentScopeFirstDeclarationsOfName.has(name)) { + currentScopeFirstDeclarationsOfName.set(name, node); } } } @@ -2544,7 +2544,7 @@ namespace ts { if (currentScopeFirstDeclarationsOfName) { const name = node.symbol && node.symbol.name; if (name) { - return currentScopeFirstDeclarationsOfName[name] === node; + return currentScopeFirstDeclarationsOfName.get(name) === node; } } @@ -3085,7 +3085,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); - classAliases[getOriginalNodeId(node)] = classAlias; + classAliases.set(getOriginalNodeId(node), classAlias); hoistVariableDeclaration(classAlias); return classAlias; } @@ -3230,7 +3230,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases[declaration.id]; + const classAlias = classAliases.get(declaration.id); if (classAlias) { const clone = getSynthesizedClone(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index a7304eebe4b81..0e66ccb14b86c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -67,11 +67,11 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap = createMap({ - [DiagnosticCategory.Warning]: yellowForegroundEscapeSequence, - [DiagnosticCategory.Error]: redForegroundEscapeSequence, - [DiagnosticCategory.Message]: blueForegroundEscapeSequence, - }); + const categoryFormatMap = createMapFromPairs( + [DiagnosticCategory.Warning, yellowForegroundEscapeSequence], + [DiagnosticCategory.Error, redForegroundEscapeSequence], + [DiagnosticCategory.Message, blueForegroundEscapeSequence], + ); function formatAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; @@ -139,7 +139,7 @@ namespace ts { output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; } - const categoryColor = categoryFormatMap[diagnostic.category]; + const categoryColor = categoryFormatMap.get(diagnostic.category); const category = DiagnosticCategory[diagnostic.category].toLowerCase(); output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`; output += sys.newLine + sys.newLine; @@ -378,9 +378,8 @@ namespace ts { } function cachedFileExists(fileName: string): boolean { - return fileName in cachedExistingFiles - ? cachedExistingFiles[fileName] - : cachedExistingFiles[fileName] = hostFileExists(fileName); + const fileExists = cachedExistingFiles.get(fileName); + return fileExists !== undefined ? fileExists : set(cachedExistingFiles, fileName, hostFileExists(fileName)); } function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { @@ -674,13 +673,9 @@ namespace ts { if (option.name === "lib") { description = getDiagnosticText(option.description); - const options: string[] = []; const element = (option).element; const typeMap = >element.type; - for (const key in typeMap) { - options.push(`'${key}'`); - } - optionsDescriptionMap[description] = options; + optionsDescriptionMap.set(description, keysOfMap(typeMap).map(key => `'${key}'`)); } else { description = getDiagnosticText(option.description); @@ -702,7 +697,7 @@ namespace ts { for (let i = 0; i < usageColumn.length; i++) { const usage = usageColumn[i]; const description = descriptionColumn[i]; - const kindsList = optionsDescriptionMap[description]; + const kindsList = optionsDescriptionMap.get(description); output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine); if (kindsList) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c1592d30eaeac..055049999ec30 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,11 +1,25 @@ namespace ts { - + /** + * Type of objects whose values are all of the same type. + * The `in` and `for-in` operators can *not* be safely used, + * since `Object.prototype` may be modified by outside code. + */ export interface MapLike { [index: string]: T; } - export interface Map extends MapLike { - __mapBrand: any; + /** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */ + export type MapKey = string | number; + + /** Minimal ES6 Map interface. */ + export interface Map { + get(key: MapKey): T; + has(key: MapKey): boolean; + set(key: MapKey, value: T): this; + delete(key: MapKey): boolean; + clear(): void; + /** `key` may *not* be a string if it was set with a number and we are not using the shim. */ + forEach(action: (value: T, key: string) => void): void; } // branded string type used to store absolute, normalized and canonicalized paths diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3dd7054a40530..94b0fa2098796 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -70,11 +70,11 @@ namespace ts { } export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { - return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); + return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules.get(moduleNameText)); } export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModuleFull { - return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined; + return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules.get(moduleNameText) : undefined; } export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModuleFull): void { @@ -82,7 +82,7 @@ namespace ts { sourceFile.resolvedModules = createMap(); } - sourceFile.resolvedModules[moduleNameText] = resolvedModule; + sourceFile.resolvedModules.set(moduleNameText, resolvedModule); } export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void { @@ -90,7 +90,7 @@ namespace ts { sourceFile.resolvedTypeReferenceDirectiveNames = createMap(); } - sourceFile.resolvedTypeReferenceDirectiveNames[typeReferenceDirectiveName] = resolvedTypeReferenceDirective; + sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, resolvedTypeReferenceDirective); } /* @internal */ @@ -112,7 +112,7 @@ namespace ts { } for (let i = 0; i < names.length; i++) { const newResolution = newResolutions[i]; - const oldResolution = oldResolutions && oldResolutions[names[i]]; + const oldResolution = oldResolutions && oldResolutions.get(names[i]); const changed = oldResolution ? !newResolution || !comparer(oldResolution, newResolution) @@ -2217,22 +2217,16 @@ namespace ts { } function reattachFileDiagnostics(newFile: SourceFile): void { - if (!hasProperty(fileDiagnostics, newFile.fileName)) { - return; - } - - for (const diagnostic of fileDiagnostics[newFile.fileName]) { - diagnostic.file = newFile; - } + forEach(fileDiagnostics.get(newFile.fileName), diagnostic => diagnostic.file = newFile); } function add(diagnostic: Diagnostic): void { let diagnostics: Diagnostic[]; if (diagnostic.file) { - diagnostics = fileDiagnostics[diagnostic.file.fileName]; + diagnostics = fileDiagnostics.get(diagnostic.file.fileName); if (!diagnostics) { diagnostics = []; - fileDiagnostics[diagnostic.file.fileName] = diagnostics; + fileDiagnostics.set(diagnostic.file.fileName, diagnostics); } } else { @@ -2252,7 +2246,7 @@ namespace ts { function getDiagnostics(fileName?: string): Diagnostic[] { sortAndDeduplicate(); if (fileName) { - return fileDiagnostics[fileName] || []; + return fileDiagnostics.get(fileName) || []; } const allDiagnostics: Diagnostic[] = []; @@ -2262,9 +2256,9 @@ namespace ts { forEach(nonFileDiagnostics, pushDiagnostic); - for (const key in fileDiagnostics) { - forEach(fileDiagnostics[key], pushDiagnostic); - } + fileDiagnostics.forEach(diagnostics => { + forEach(diagnostics, pushDiagnostic); + }); return sortAndDeduplicateDiagnostics(allDiagnostics); } @@ -2277,9 +2271,9 @@ namespace ts { diagnosticsModified = false; nonFileDiagnostics = sortAndDeduplicateDiagnostics(nonFileDiagnostics); - for (const key in fileDiagnostics) { - fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); - } + fileDiagnostics.forEach((diagnostics, key) => { + fileDiagnostics.set(key, sortAndDeduplicateDiagnostics(diagnostics)); + }); } } @@ -2316,7 +2310,7 @@ namespace ts { return s; function getReplacement(c: string) { - return escapedCharsMap[c] || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); + return escapedCharsMap.get(c) || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); } } @@ -3322,13 +3316,14 @@ namespace ts { export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; if (syntaxKindEnum) { - if (syntaxKindCache[kind]) { - return syntaxKindCache[kind]; + const cached = syntaxKindCache.get(kind); + if (cached !== undefined) { + return cached; } for (const name in syntaxKindEnum) { if (syntaxKindEnum[name] === kind) { - return syntaxKindCache[kind] = kind.toString() + " (" + name + ")"; + return set(syntaxKindCache, kind, kind.toString() + " (" + name + ")"); } } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 43e01ca56bb59..c9ea2af1a8782 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -46,54 +46,54 @@ namespace ts { * supplant the existing `forEachChild` implementation if performance is not * significantly impacted. */ - const nodeEdgeTraversalMap = createMap({ - [SyntaxKind.QualifiedName]: [ + const nodeEdgeTraversalMap = createMapFromPairs( + [SyntaxKind.QualifiedName, [ { name: "left", test: isEntityName }, { name: "right", test: isIdentifier } - ], - [SyntaxKind.Decorator]: [ + ]], + [SyntaxKind.Decorator, [ { name: "expression", test: isLeftHandSideExpression } - ], - [SyntaxKind.TypeAssertionExpression]: [ + ]], + [SyntaxKind.TypeAssertionExpression, [ { name: "type", test: isTypeNode }, { name: "expression", test: isUnaryExpression } - ], - [SyntaxKind.AsExpression]: [ + ]], + [SyntaxKind.AsExpression, [ { name: "expression", test: isExpression }, { name: "type", test: isTypeNode } - ], - [SyntaxKind.NonNullExpression]: [ + ]], + [SyntaxKind.NonNullExpression, [ { name: "expression", test: isLeftHandSideExpression } - ], - [SyntaxKind.EnumDeclaration]: [ + ]], + [SyntaxKind.EnumDeclaration, [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, { name: "name", test: isIdentifier }, { name: "members", test: isEnumMember } - ], - [SyntaxKind.ModuleDeclaration]: [ + ]], + [SyntaxKind.ModuleDeclaration, [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, { name: "name", test: isModuleName }, { name: "body", test: isModuleBody } - ], - [SyntaxKind.ModuleBlock]: [ + ]], + [SyntaxKind.ModuleBlock, [ { name: "statements", test: isStatement } - ], - [SyntaxKind.ImportEqualsDeclaration]: [ + ]], + [SyntaxKind.ImportEqualsDeclaration, [ { name: "decorators", test: isDecorator }, { name: "modifiers", test: isModifier }, { name: "name", test: isIdentifier }, { name: "moduleReference", test: isModuleReference } - ], - [SyntaxKind.ExternalModuleReference]: [ + ]], + [SyntaxKind.ExternalModuleReference, [ { name: "expression", test: isExpression, optional: true } - ], - [SyntaxKind.EnumMember]: [ + ]], + [SyntaxKind.EnumMember, [ { name: "name", test: isPropertyName }, { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ] - }); + ]] + ); function reduceNode(node: Node, f: (memo: T, node: Node) => T, initial: T) { return node ? f(initial, node) : initial; @@ -530,7 +530,7 @@ namespace ts { break; default: - const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = (>node)[edge.name]; @@ -1188,10 +1188,10 @@ namespace ts { default: let updated: Node & MapLike; - const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { - const value = >(>node)[edge.name]; + const value = >(>node)[edge.name]; if (value !== undefined) { const visited = isArray(value) ? visitNodes(value, visitor, edge.test, 0, value.length, edge.parenthesize, node) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 7c7a06db0b6c9..d4d6056f32020 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -96,7 +96,7 @@ namespace FourSlash { }); export function escapeXmlAttributeValue(s: string) { - return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]); + return s.replace(/[&<>"'\/]/g, ch => entityMap.get(ch)); } // Name of testcase metadata including ts.CompilerOptions properties that will be used by globalOptions @@ -222,7 +222,7 @@ namespace FourSlash { } function tryAdd(path: string) { - const inputFile = inputFiles[path]; + const inputFile = inputFiles.get(path); if (inputFile && !Harness.isDefaultLibraryFile(path)) { languageServiceAdapterHost.addScript(path, inputFile, /*isRootFile*/ true); return true; @@ -256,7 +256,7 @@ namespace FourSlash { ts.forEach(testData.files, file => { // Create map between fileName and its content for easily looking up when resolveReference flag is specified - this.inputFiles[file.fileName] = file.content; + this.inputFiles.set(file.fileName, file.content); if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") { const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content); @@ -322,11 +322,11 @@ namespace FourSlash { } else { // resolveReference file-option is not specified then do not resolve any files and include all inputFiles - for (const fileName in this.inputFiles) { + this.inputFiles.forEach((file, fileName) => { if (!Harness.isDefaultLibraryFile(fileName)) { - this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true); + this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true); } - } + }); this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false); } @@ -659,11 +659,12 @@ namespace FourSlash { const completions = this.getCompletionListAtCaret(); const uniqueItems = ts.createMap(); for (const item of completions.entries) { - if (!(item.name in uniqueItems)) { - uniqueItems[item.name] = item.kind; + const uniqueItem = uniqueItems.get(item.name); + if (!uniqueItem) { + uniqueItems.set(item.name, item.kind); } else { - assert.equal(item.kind, uniqueItems[item.name], `Items should have the same kind, got ${item.kind} and ${uniqueItems[item.name]}`); + assert.equal(item.kind, uniqueItem, `Items should have the same kind, got ${item.kind} and ${uniqueItem}`); } } } @@ -831,7 +832,7 @@ namespace FourSlash { } public verifyRangesWithSameTextReferenceEachOther() { - ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); + this.rangesByText().forEach(ranges => this.verifyRangesReferenceEachOther(ranges)); } public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) { @@ -903,7 +904,8 @@ namespace FourSlash { } public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) { - ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => { + for (const name in namesAndTexts) if (ts.hasProperty(namesAndTexts, name)) { + const text = namesAndTexts[name]; if (text instanceof Array) { assert(text.length === 2); const [expectedText, expectedDocumentation] = text; @@ -912,7 +914,7 @@ namespace FourSlash { else { this.verifyQuickInfoAt(name, text); } - }); + } } public verifyQuickInfoString(expectedText: string, expectedDocumentation?: string) { @@ -2087,7 +2089,7 @@ namespace FourSlash { "<": ts.CharacterCodes.lessThan }); - const charCode = openBraceMap[openingBrace]; + const charCode = openBraceMap.get(openingBrace); if (!charCode) { this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`); diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 26a4a6f963dc9..265fe4c142c81 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -918,10 +918,9 @@ namespace Harness { return undefined; } - if (!libFileNameSourceFileMap[fileName]) { - libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest); - } - return libFileNameSourceFileMap[fileName]; + const sourceFile = libFileNameSourceFileMap.get(fileName); + return sourceFile || ts.set(libFileNameSourceFileMap, fileName, + createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest)); } export function getDefaultLibFileName(options: ts.CompilerOptions): string { @@ -1103,10 +1102,10 @@ namespace Harness { optionsIndex = ts.createMap(); const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations); for (const option of optionDeclarations) { - optionsIndex[option.name.toLowerCase()] = option; + optionsIndex.set(option.name.toLowerCase(), option); } } - return optionsIndex[name.toLowerCase()]; + return optionsIndex.get(name.toLowerCase()); } export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void { @@ -1466,7 +1465,7 @@ namespace Harness { const fullResults = ts.createMap(); for (const sourceFile of allFiles) { - fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); + fullResults.set(sourceFile.unitName, fullWalker.getTypeAndSymbols(sourceFile.unitName)); } // Produce baselines. The first gives the types for all expressions. @@ -1519,7 +1518,7 @@ namespace Harness { allFiles.forEach(file => { const codeLines = file.content.split("\n"); - typeWriterResults[file.unitName].forEach(result => { + typeWriterResults.get(file.unitName).forEach(result => { if (isSymbolBaseline && !result.symbol) { return; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index a49f89267299f..2ddc38499d082 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -262,7 +262,7 @@ namespace Harness.LanguageService { this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); - const imports = ts.createMap(); + const imports = ts.createMapLike(); for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -275,7 +275,7 @@ namespace Harness.LanguageService { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); - const resolutions = ts.createMap(); + const resolutions = ts.createMapLike(); const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index f7541dc9bfe9e..f1cc992c4a7d5 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -256,17 +256,20 @@ class ProjectRunner extends RunnerBase { // Set the values specified using json const optionNameMap = ts.arrayToMap(ts.optionDeclarations, option => option.name); for (const name in testCase) { - if (name !== "mapRoot" && name !== "sourceRoot" && name in optionNameMap) { - const option = optionNameMap[name]; - const optType = option.type; - let value = testCase[name]; - if (typeof optType !== "string") { - const key = value.toLowerCase(); - if (key in optType) { - value = optType[key]; + if (name !== "mapRoot" && name !== "sourceRoot") { + const option = optionNameMap.get(name); + if (option) { + const optType = option.type; + let value = testCase[name]; + if (typeof optType !== "string") { + const key = value.toLowerCase(); + const optTypeValue = optType.get(key); + if (optTypeValue) { + value = optTypeValue; + } } + compilerOptions[option.name] = value; } - compilerOptions[option.name] = value; } } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 4286d3555d883..b6a90f53259c8 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -8,25 +8,28 @@ namespace ts { function createDefaultServerHost(fileMap: Map): server.ServerHost { const existingDirectories = createMap(); - for (const name in fileMap) { + forEachKeyInMap(fileMap, name => { let dir = getDirectoryPath(name); let previous: string; do { - existingDirectories[dir] = true; + existingDirectories.set(dir, true); previous = dir; dir = getDirectoryPath(dir); } while (dir !== previous); - } + }); return { args: [], newLine: "\r\n", useCaseSensitiveFileNames: false, write: noop, - readFile: path => path in fileMap ? fileMap[path].content : undefined, + readFile: path => { + const file = fileMap.get(path); + return file && file.content; + }, writeFile: notImplemented, resolvePath: notImplemented, - fileExists: path => path in fileMap, - directoryExists: path => existingDirectories[path] || false, + fileExists: path => fileMap.has(path), + directoryExists: path => existingDirectories.get(path) || false, createDirectory: noop, getExecutingFilePath: () => "", getCurrentDirectory: () => "", @@ -191,7 +194,7 @@ namespace ts { assert.isTrue(typeof diags[0].messageText === "string" && ((diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message"); // assert that import will success once file appear on disk - fileMap[imported.name] = imported; + fileMap.set(imported.name, imported); fileExistsCalledForBar = false; rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`); diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 35313e153082c..8e4ace3865595 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -36,7 +36,7 @@ namespace ts { for (const f of files) { let name = getDirectoryPath(f.name); while (true) { - directories[name] = name; + directories.set(name, name); const baseName = getDirectoryPath(name); if (baseName === name) { break; @@ -46,20 +46,19 @@ namespace ts { } return { readFile, - directoryExists: path => { - return path in directories; - }, + directoryExists: path => directories.has(path), fileExists: path => { - assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`); - return path in map; + assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); + return map.has(path); } }; } else { - return { readFile, fileExists: path => path in map, }; + return { readFile, fileExists: path => map.has(path) }; } function readFile(path: string): string { - return path in map ? map[path].content : undefined; + const file = map.get(path); + return file && file.content; } } @@ -300,7 +299,8 @@ namespace ts { const host: CompilerHost = { getSourceFile: (fileName: string, languageVersion: ScriptTarget) => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; + const file = files.get(path); + return file && createSourceFile(fileName, file, languageVersion); }, getDefaultLibFileName: () => "lib.d.ts", writeFile: notImplemented, @@ -311,7 +311,7 @@ namespace ts { useCaseSensitiveFileNames: () => false, fileExists: fileName => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return path in files; + return files.has(path); }, readFile: notImplemented }; @@ -371,7 +371,11 @@ export = C; function test(files: Map, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); if (!useCaseSensitiveFileNames) { - files = reduceProperties(files, (files, file, fileName) => (files[getCanonicalFileName(fileName)] = file, files), createMap()); + const oldFiles = files; + files = createMap(); + oldFiles.forEach((file, fileName) => { + files.set(getCanonicalFileName(fileName), file); + }); } const host: CompilerHost = { @@ -380,7 +384,8 @@ export = C; return library; } const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; + const file = files.get(path); + return file && createSourceFile(fileName, file, languageVersion); }, getDefaultLibFileName: () => "lib.d.ts", writeFile: notImplemented, @@ -391,7 +396,7 @@ export = C; useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, fileExists: fileName => { const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return path in files; + return files.has(path); }, readFile: notImplemented }; @@ -1020,8 +1025,8 @@ import b = require("./moduleB"); const names = map(files, f => f.name); const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES2015)), f => f.fileName); const compilerHost: CompilerHost = { - fileExists : fileName => fileName in sourceFiles, - getSourceFile: fileName => sourceFiles[fileName], + fileExists : fileName => sourceFiles.has(fileName), + getSourceFile: fileName => sourceFiles.get(fileName), getDefaultLibFileName: () => "lib.d.ts", writeFile: notImplemented, getCurrentDirectory: () => "/", @@ -1029,7 +1034,10 @@ import b = require("./moduleB"); getCanonicalFileName: f => f.toLowerCase(), getNewLine: () => "\r\n", useCaseSensitiveFileNames: () => false, - readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined + readFile: fileName => { + const file = sourceFiles.get(fileName); + return file && file.text; + } }; const program1 = createProgram(names, {}, compilerHost); const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics(); diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 6dbd74e71d2d3..54c05a9c38346 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -122,7 +122,7 @@ namespace ts { trace: s => trace.push(s), getTrace: () => trace, getSourceFile(fileName): SourceFile { - return files[fileName]; + return files.get(fileName); }, getDefaultLibFileName(): string { return "lib.d.ts"; @@ -143,9 +143,10 @@ namespace ts { getNewLine(): string { return sys ? sys.newLine : newLine; }, - fileExists: fileName => fileName in files, + fileExists: fileName => files.has(fileName), readFile: fileName => { - return fileName in files ? files[fileName].text : undefined; + const file = files.get(fileName); + return file && file.text; }, }; } @@ -188,7 +189,7 @@ namespace ts { } else { assert.isTrue(cache !== undefined, `expected ${caption} to be set`); - assert.isTrue(equalOwnProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); + assert.isTrue(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 8800e84474bbd..da24fe1606b3a 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -422,9 +422,10 @@ namespace ts.server { handle(msg: protocol.Message): void { if (msg.type === "response") { const response = msg; - if (response.request_seq in this.callbacks) { - this.callbacks[response.request_seq](response); - delete this.callbacks[response.request_seq]; + const handler = this.callbacks.get(response.request_seq); + if (handler) { + handler(response); + this.callbacks.delete(response.request_seq); } } else if (msg.type === "event") { @@ -434,13 +435,14 @@ namespace ts.server { } emit(name: string, args: any): void { - if (name in this.eventHandlers) { - this.eventHandlers[name](args); + const handler = this.eventHandlers.get(name); + if (handler) { + handler(args); } } on(name: string, handler: (args: any) => void): void { - this.eventHandlers[name] = handler; + this.eventHandlers.set(name, handler); } connect(session: InProcSession): void { @@ -458,7 +460,7 @@ namespace ts.server { command, arguments: args }); - this.callbacks[this.seq] = callback; + this.callbacks.set(this.seq, callback); } }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 77ebee6fadd70..bec3a70087909 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -243,12 +243,18 @@ namespace ts.projectSystem { } export function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { - assert.equal(reduceProperties(map, count => count + 1, 0), expectedKeys.length, `${caption}: incorrect size of map`); + assert.equal(mapSize(map), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { - assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`); + assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${keysOfMap(map)}`); } } + function mapSize(map: Map): number { + let size = 0; + map.forEach(() => { size++; }); + return size; + } + export function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); for (const f of expectedFileNames) { @@ -291,38 +297,30 @@ namespace ts.projectSystem { } export class Callbacks { - private map: { [n: number]: TimeOutCallback } = {}; + private map = createMap(); private nextId = 1; register(cb: (...args: any[]) => void, args: any[]) { const timeoutId = this.nextId; this.nextId++; - this.map[timeoutId] = cb.bind(undefined, ...args); + this.map.set(timeoutId, cb.bind(undefined, ...args)); return timeoutId; } unregister(id: any) { if (typeof id === "number") { - delete this.map[id]; + this.map.delete(id); } } count() { - let n = 0; - for (const _ in this.map) { - // TODO: GH#11734 - _; - n++; - } - return n; + return mapSize(this.map); } invoke() { - for (const id in this.map) { - if (hasProperty(this.map, id)) { - this.map[id](); - } - } - this.map = {}; + this.map.forEach(cb => { + cb(); + }); + this.map.clear(); } } @@ -439,7 +437,7 @@ namespace ts.projectSystem { triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); - const callbacks = this.watchedDirectories[path]; + const callbacks = this.watchedDirectories.get(path); if (callbacks) { for (const callback of callbacks) { callback.cb(fileName); @@ -449,7 +447,7 @@ namespace ts.projectSystem { triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); - const callbacks = this.watchedFiles[path]; + const callbacks = this.watchedFiles.get(path); if (callbacks) { for (const callback of callbacks) { callback(path, removed); diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index 62d2f7ac80146..d99f12294afbe 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -14,7 +14,7 @@ namespace ts.projectSystem { function createTypesRegistry(...list: string[]): Map { const map = createMap(); for (const l of list) { - map[l] = undefined; + map.set(l, undefined); } return map; } diff --git a/src/server/builder.ts b/src/server/builder.ts index 5354e7ed50838..44fb2224371cf 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -336,24 +336,24 @@ namespace ts.server { // Use slice to clone the array to avoid manipulating in place const queue = fileInfo.referencedBy.slice(0); const fileNameSet = createMap(); - fileNameSet[scriptInfo.fileName] = scriptInfo; + fileNameSet.set(scriptInfo.fileName, scriptInfo); while (queue.length > 0) { const processingFileInfo = queue.pop(); if (processingFileInfo.updateShapeSignature() && processingFileInfo.referencedBy.length > 0) { for (const potentialFileInfo of processingFileInfo.referencedBy) { - if (!fileNameSet[potentialFileInfo.scriptInfo.fileName]) { + if (!fileNameSet.has(potentialFileInfo.scriptInfo.fileName)) { queue.push(potentialFileInfo); } } } - fileNameSet[processingFileInfo.scriptInfo.fileName] = processingFileInfo.scriptInfo; + fileNameSet.set(processingFileInfo.scriptInfo.fileName, processingFileInfo.scriptInfo); } const result: string[] = []; - for (const fileName in fileNameSet) { - if (shouldEmitFile(fileNameSet[fileName])) { + fileNameSet.forEach((scriptInfo, fileName) => { + if (shouldEmitFile(scriptInfo)) { result.push(fileName); } - } + }); return result; } } diff --git a/src/server/client.ts b/src/server/client.ts index 3b09a92675497..13d16073d1c41 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -31,10 +31,10 @@ namespace ts.server { } private getLineMap(fileName: string): number[] { - let lineMap = this.lineMaps[fileName]; + let lineMap = this.lineMaps.get(fileName); if (!lineMap) { const scriptSnapshot = this.host.getScriptSnapshot(fileName); - lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())); + lineMap = set(this.lineMaps, fileName, ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()))); } return lineMap; } @@ -140,7 +140,7 @@ namespace ts.server { changeFile(fileName: string, start: number, end: number, newText: string): void { // clear the line map after an edit - this.lineMaps[fileName] = undefined; + this.lineMaps.set(fileName, undefined); const lineOffset = this.positionToOneBasedLineOffset(fileName, start); const endLineOffset = this.positionToOneBasedLineOffset(fileName, end); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 9e53b3cd52638..a358b0dab0019 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1,4 +1,4 @@ -/// +/// /// /// /// @@ -41,10 +41,10 @@ namespace ts.server { if (typeof option.type === "object") { const optionMap = >option.type; // verify that map contains only numbers - for (const id in optionMap) { - Debug.assert(typeof optionMap[id] === "number"); - } - map[option.name] = optionMap; + optionMap.forEach(value => { + Debug.assert(typeof value === "number"); + }); + map.set(option.name, optionMap); } } return map; @@ -59,20 +59,19 @@ namespace ts.server { export function convertFormatOptions(protocolOptions: protocol.FormatCodeSettings): FormatCodeSettings { if (typeof protocolOptions.indentStyle === "string") { - protocolOptions.indentStyle = indentStyle[protocolOptions.indentStyle.toLowerCase()]; + protocolOptions.indentStyle = indentStyle.get(protocolOptions.indentStyle.toLowerCase()); Debug.assert(protocolOptions.indentStyle !== undefined); } return protocolOptions; } export function convertCompilerOptions(protocolOptions: protocol.ExternalProjectCompilerOptions): CompilerOptions & protocol.CompileOnSaveMixin { - for (const id in compilerOptionConverters) { + compilerOptionConverters.forEach((mappedValues, id) => { const propertyValue = protocolOptions[id]; if (typeof propertyValue === "string") { - const mappedValues = compilerOptionConverters[id]; - protocolOptions[id] = mappedValues[propertyValue.toLowerCase()]; + protocolOptions[id] = mappedValues.get(propertyValue.toLowerCase()); } - } + }); return protocolOptions; } @@ -189,11 +188,12 @@ namespace ts.server { stopWatchingDirectory(directory: string) { // if the ref count for this directory watcher drops to 0, it's time to close it - this.directoryWatchersRefCount[directory]--; - if (this.directoryWatchersRefCount[directory] === 0) { + const refCount = this.directoryWatchersRefCount.get(directory) - 1; + this.directoryWatchersRefCount.set(directory, refCount); + if (refCount === 0) { this.projectService.logger.info(`Close directory watcher for: ${directory}`); - this.directoryWatchersForTsconfig[directory].close(); - delete this.directoryWatchersForTsconfig[directory]; + this.directoryWatchersForTsconfig.get(directory).close(); + this.directoryWatchersForTsconfig.delete(directory); } } @@ -201,13 +201,13 @@ namespace ts.server { let currentPath = getDirectoryPath(fileName); let parentPath = getDirectoryPath(currentPath); while (currentPath != parentPath) { - if (!this.directoryWatchersForTsconfig[currentPath]) { + if (!this.directoryWatchersForTsconfig.has(currentPath)) { this.projectService.logger.info(`Add watcher for: ${currentPath}`); - this.directoryWatchersForTsconfig[currentPath] = this.projectService.host.watchDirectory(currentPath, callback); - this.directoryWatchersRefCount[currentPath] = 1; + this.directoryWatchersForTsconfig.set(currentPath, this.projectService.host.watchDirectory(currentPath, callback)); + this.directoryWatchersRefCount.set(currentPath, 1); } else { - this.directoryWatchersRefCount[currentPath] += 1; + this.directoryWatchersRefCount.set(currentPath, this.directoryWatchersRefCount.get(currentPath) + 1); } project.directoriesWatchedForTsconfig.push(currentPath); currentPath = parentPath; @@ -425,7 +425,7 @@ namespace ts.server { } else { if (info && (!info.isOpen)) { - // file has been changed which might affect the set of referenced files in projects that include + // file has been changed which might affect the set of referenced files in projects that include // this file and set of inferred projects info.reloadFromFile(); this.updateProjectGraphs(info.containingProjects); @@ -444,7 +444,7 @@ namespace ts.server { this.filenameToScriptInfo.remove(info.path); this.lastDeletedFile = info; - // capture list of projects since detachAllProjects will wipe out original list + // capture list of projects since detachAllProjects will wipe out original list const containingProjects = info.containingProjects.slice(); info.detachAllProjects(); @@ -600,7 +600,7 @@ namespace ts.server { const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info); if (!this.useSingleInferredProject) { // if useOneInferredProject is not set then try to fixup ownership of open files - // check 'defaultProject !== inferredProject' is necessary to handle cases + // check 'defaultProject !== inferredProject' is necessary to handle cases // when creation inferred project for some file has added other open files into this project (i.e. as referenced files) // we definitely don't want to delete the project that was just created for (const f of this.openFiles) { @@ -610,7 +610,7 @@ namespace ts.server { } const defaultProject = f.getDefaultProject(); if (isRootFileInInferredProject(info) && defaultProject !== inferredProject && inferredProject.containsScriptInfo(f)) { - // open file used to be root in inferred project, + // open file used to be root in inferred project, // this inferred project is different from the one we've just created for current file // and new inferred project references this open file. // We should delete old inferred project and attach open file to the new one @@ -990,7 +990,7 @@ namespace ts.server { if (toAdd) { for (const f of toAdd) { if (f.isOpen && isRootFileInInferredProject(f)) { - // if file is already root in some inferred project + // if file is already root in some inferred project // - remove the file from that project and delete the project if necessary const inferredProject = f.containingProjects[0]; inferredProject.removeFile(f); @@ -1143,7 +1143,7 @@ namespace ts.server { this.logger.info(`Host information ${args.hostInfo}`); } if (args.formatOptions) { - mergeMaps(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions)); + mergeMapLikes(this.hostConfiguration.formatCodeOptions, convertFormatOptions(args.formatOptions)); this.logger.info("Format host information updated"); } } @@ -1265,7 +1265,7 @@ namespace ts.server { for (const file of changedFiles) { const scriptInfo = this.getScriptInfo(file.fileName); Debug.assert(!!scriptInfo); - // apply changes in reverse order + // apply changes in reverse order for (let i = file.changes.length - 1; i >= 0; i--) { const change = file.changes[i]; scriptInfo.editContent(change.span.start, change.span.start + change.span.length, change.newText); @@ -1302,7 +1302,7 @@ namespace ts.server { closeExternalProject(uncheckedFileName: string, suppressRefresh = false): void { const fileName = toNormalizedPath(uncheckedFileName); - const configFiles = this.externalProjectToConfiguredProjectMap[fileName]; + const configFiles = this.externalProjectToConfiguredProjectMap.get(fileName); if (configFiles) { let shouldRefreshInferredProjects = false; for (const configFile of configFiles) { @@ -1310,7 +1310,7 @@ namespace ts.server { shouldRefreshInferredProjects = true; } } - delete this.externalProjectToConfiguredProjectMap[fileName]; + this.externalProjectToConfiguredProjectMap.delete(fileName); if (shouldRefreshInferredProjects && !suppressRefresh) { this.refreshInferredProjects(); } @@ -1331,19 +1331,19 @@ namespace ts.server { // record project list before the update const projectsToClose = arrayToMap(this.externalProjects, p => p.getProjectName(), _ => true); for (const externalProjectName in this.externalProjectToConfiguredProjectMap) { - projectsToClose[externalProjectName] = true; + projectsToClose.set(externalProjectName, true); } for (const externalProject of projects) { this.openExternalProject(externalProject, /*suppressRefreshOfInferredProjects*/ true); // delete project that is present in input list - delete projectsToClose[externalProject.projectFileName]; + projectsToClose.delete(externalProject.projectFileName); } // close projects that were missing in the input list - for (const externalProjectName in projectsToClose) { + forEachKeyInMap(projectsToClose, externalProjectName => { this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true) - } + }); this.refreshInferredProjects(); } @@ -1386,7 +1386,7 @@ namespace ts.server { // close existing project and later we'll open a set of configured projects for these files this.closeExternalProject(proj.projectFileName, /*suppressRefresh*/ true); } - else if (this.externalProjectToConfiguredProjectMap[proj.projectFileName]) { + else if (this.externalProjectToConfiguredProjectMap.get(proj.projectFileName)) { // this project used to include config files if (!tsConfigFiles) { // config files were removed from the project - close existing external project which in turn will close configured projects @@ -1394,7 +1394,7 @@ namespace ts.server { } else { // project previously had some config files - compare them with new set of files and close all configured projects that correspond to unused files - const oldConfigFiles = this.externalProjectToConfiguredProjectMap[proj.projectFileName]; + const oldConfigFiles = this.externalProjectToConfiguredProjectMap.get(proj.projectFileName); let iNew = 0; let iOld = 0; while (iNew < tsConfigFiles.length && iOld < oldConfigFiles.length) { @@ -1422,7 +1422,7 @@ namespace ts.server { } if (tsConfigFiles) { // store the list of tsconfig files that belong to the external project - this.externalProjectToConfiguredProjectMap[proj.projectFileName] = tsConfigFiles; + this.externalProjectToConfiguredProjectMap.set(proj.projectFileName, tsConfigFiles); for (const tsconfigFile of tsConfigFiles) { let project = this.findConfiguredProjectByProjectName(tsconfigFile); if (!project) { @@ -1438,7 +1438,7 @@ namespace ts.server { } else { // no config files - remove the item from the collection - delete this.externalProjectToConfiguredProjectMap[proj.projectFileName]; + this.externalProjectToConfiguredProjectMap.delete(proj.projectFileName); this.createAndAddExternalProject(proj.projectFileName, rootFiles, proj.options, proj.typeAcquisition); } if (!suppressRefreshOfInferredProjects) { diff --git a/src/server/lsHost.ts b/src/server/lsHost.ts index aa37008ff6679..ffeeb57a0e834 100644 --- a/src/server/lsHost.ts +++ b/src/server/lsHost.ts @@ -75,15 +75,16 @@ namespace ts.server { for (const name of names) { // check if this is a duplicate entry in the list - let resolution = newResolutions[name]; + let resolution = newResolutions.get(name); if (!resolution) { - const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name]; + const existingResolution = currentResolutionsInFile && currentResolutionsInFile.get(name); if (moduleResolutionIsValid(existingResolution)) { // ok, it is safe to use existing name resolution results resolution = existingResolution; } else { - newResolutions[name] = resolution = loader(name, containingFile, compilerOptions, this); + resolution = loader(name, containingFile, compilerOptions, this); + newResolutions.set(name, resolution); } if (logChanges && this.filesWithChangedSetOfUnresolvedImports && !resolutionIsEqualTo(existingResolution, resolution)) { this.filesWithChangedSetOfUnresolvedImports.push(path); diff --git a/src/server/project.ts b/src/server/project.ts index 049f61269f886..14889dccd8a47 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -495,9 +495,9 @@ namespace ts.server { } let unresolvedImports: string[]; if (file.resolvedModules) { - for (const name in file.resolvedModules) { + file.resolvedModules.forEach((resolvedModule, name) => { // pick unresolved non-relative names - if (!file.resolvedModules[name] && !isExternalModuleNameRelative(name)) { + if (!resolvedModule && !isExternalModuleNameRelative(name)) { // for non-scoped names extract part up-to the first slash // for scoped names - extract up to the second slash let trimmed = name.trim(); @@ -511,7 +511,7 @@ namespace ts.server { (unresolvedImports || (unresolvedImports = [])).push(trimmed); result.push(trimmed); } - } + }); } this.cachedUnresolvedImportsPerFile.set(file.path, unresolvedImports || emptyArray); } @@ -537,7 +537,7 @@ namespace ts.server { } // 1. no changes in structure, no changes in unresolved imports - do nothing - // 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files + // 2. no changes in structure, unresolved imports were changed - collect unresolved imports for all files // (can reuse cached imports for files that were not changed) // 3. new files were added/removed, but compilation settings stays the same - collect unresolved imports for all new/modified files // (can reuse cached imports for files that were not changed) @@ -679,16 +679,16 @@ namespace ts.server { const added: string[] = []; const removed: string[] = []; - for (const id in currentFiles) { - if (!hasProperty(lastReportedFileNames, id)) { + forEachKeyInMap(currentFiles, id => { + if (!lastReportedFileNames.has(id)) { added.push(id); } - } - for (const id in lastReportedFileNames) { - if (!hasProperty(currentFiles, id)) { + }); + forEachKeyInMap(lastReportedFileNames, id => { + if (!currentFiles.has(id)) { removed.push(id); } - } + }); this.lastReportedFileNames = currentFiles; this.lastReportedVersion = this.projectStructureVersion; return { info, changes: { added, removed }, projectErrors: this.projectErrors }; @@ -722,7 +722,7 @@ namespace ts.server { if (symbol && symbol.declarations && symbol.declarations[0]) { const declarationSourceFile = symbol.declarations[0].getSourceFile(); if (declarationSourceFile) { - referencedFiles[declarationSourceFile.path] = true; + referencedFiles.set(declarationSourceFile.path, true); } } } @@ -734,25 +734,24 @@ namespace ts.server { if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { for (const referencedFile of sourceFile.referencedFiles) { const referencedPath = toPath(referencedFile.fileName, currentDirectory, getCanonicalFileName); - referencedFiles[referencedPath] = true; + referencedFiles.set(referencedPath, true); } } // Handle type reference directives if (sourceFile.resolvedTypeReferenceDirectiveNames) { - for (const typeName in sourceFile.resolvedTypeReferenceDirectiveNames) { - const resolvedTypeReferenceDirective = sourceFile.resolvedTypeReferenceDirectiveNames[typeName]; + sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { if (!resolvedTypeReferenceDirective) { - continue; + return; } const fileName = resolvedTypeReferenceDirective.resolvedFileName; const typeFilePath = toPath(fileName, currentDirectory, getCanonicalFileName); - referencedFiles[typeFilePath] = true; - } + referencedFiles.set(typeFilePath, true); + }) } - const allFileNames = map(Object.keys(referencedFiles), key => key); + const allFileNames = keysOfMap(referencedFiles) as Path[]; return filter(allFileNames, file => this.projectService.host.fileExists(file)); } @@ -888,18 +887,19 @@ namespace ts.server { return; } const configDirectoryPath = getDirectoryPath(this.getConfigFilePath()); - this.directoriesWatchedForWildcards = reduceProperties(this.wildcardDirectories, (watchers, flag, directory) => { + + this.directoriesWatchedForWildcards = createMap(); + this.wildcardDirectories.forEach((flag, directory) => { if (comparePaths(configDirectoryPath, directory, ".", !this.projectService.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; this.projectService.logger.info(`Add ${recursive ? "recursive " : ""}watcher for: ${directory}`); - watchers[directory] = this.projectService.host.watchDirectory( + this.directoriesWatchedForWildcards.set(directory, this.projectService.host.watchDirectory( directory, path => callback(this, path), recursive - ); + )); } - return watchers; - }, >{}); + }); } stopWatchingDirectory() { @@ -923,9 +923,9 @@ namespace ts.server { this.typeRootsWatchers = undefined; } - for (const id in this.directoriesWatchedForWildcards) { - this.directoriesWatchedForWildcards[id].close(); - } + this.directoriesWatchedForWildcards.forEach(watcher => { + watcher.close(); + }); this.directoriesWatchedForWildcards = undefined; this.stopWatchingDirectory(); diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 84649863a7b3a..fa9eac0e8e399 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -95,7 +95,7 @@ namespace ts.server { if (!this.formatCodeSettings) { this.formatCodeSettings = getDefaultFormatCodeSettings(this.host); } - mergeMaps(this.formatCodeSettings, formatSettings); + mergeMapLikes(this.formatCodeSettings, formatSettings); } } diff --git a/src/server/session.ts b/src/server/session.ts index 9abc5a447bfce..0c200b33b845f 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1607,14 +1607,14 @@ namespace ts.server { }); public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) { - if (command in this.handlers) { + if (this.handlers.has(command)) { throw new Error(`Protocol handler already exists for command "${command}"`); } - this.handlers[command] = handler; + this.handlers.set(command, handler); } public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } { - const handler = this.handlers[request.command]; + const handler = this.handlers.get(request.command); if (handler) { return handler(request); } diff --git a/src/server/typingsCache.ts b/src/server/typingsCache.ts index 8b03d59003c6d..9379ae82d0ea2 100644 --- a/src/server/typingsCache.ts +++ b/src/server/typingsCache.ts @@ -35,17 +35,18 @@ namespace ts.server { let unique = 0; for (const v of arr1) { - if (set[v] !== true) { - set[v] = true; + if (set.get(v) !== true) { + set.set(v, true); unique++; } } for (const v of arr2) { - if (!hasProperty(set, v)) { + const isSet = set.get(v); + if (isSet === undefined) { return false; } - if (set[v] === true) { - set[v] = false; + if (isSet === true) { + set.set(v, false); unique--; } } @@ -83,7 +84,7 @@ namespace ts.server { return emptyArray; } - const entry = this.perProjectCache[project.getProjectName()]; + const entry = this.perProjectCache.get(project.getProjectName()); const result: SortedReadonlyArray = entry ? entry.typings : emptyArray; if (forceRefresh || !entry || @@ -92,13 +93,13 @@ namespace ts.server { unresolvedImportsChanged(unresolvedImports, entry.unresolvedImports)) { // Note: entry is now poisoned since it does not really contain typings for a given combination of compiler options\typings options. // instead it acts as a placeholder to prevent issuing multiple requests - this.perProjectCache[project.getProjectName()] = { + this.perProjectCache.set(project.getProjectName(), { compilerOptions: project.getCompilerOptions(), typeAcquisition, typings: result, unresolvedImports, poisoned: true - }; + }); // something has been changed, issue a request to update typings this.installer.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports); } @@ -106,21 +107,21 @@ namespace ts.server { } updateTypingsForProject(projectName: string, compilerOptions: CompilerOptions, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, newTypings: string[]) { - this.perProjectCache[projectName] = { + this.perProjectCache.set(projectName, { compilerOptions, typeAcquisition, typings: toSortedReadonlyArray(newTypings), unresolvedImports, poisoned: false - }; + }); } deleteTypingsForProject(projectName: string) { - delete this.perProjectCache[projectName]; + this.perProjectCache.delete(projectName); } onProjectClosed(project: Project) { - delete this.perProjectCache[project.getProjectName()]; + this.perProjectCache.delete(project.getProjectName()); this.installer.onProjectClosed(project); } } diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 7a09c1f6c216d..82184f86850b2 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -112,7 +112,7 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Closing file watchers for project '${projectName}'`); } - const watchers = this.projectWatchers[projectName]; + const watchers = this.projectWatchers.get(projectName); if (!watchers) { if (this.log.isEnabled()) { this.log.writeLine(`No watchers are registered for project '${projectName}'`); @@ -123,7 +123,7 @@ namespace ts.server.typingsInstaller { w.close(); } - delete this.projectWatchers[projectName]; + this.projectWatchers.delete(projectName); if (this.log.isEnabled()) { this.log.writeLine(`Closing file watchers for project '${projectName}' - done.`); @@ -177,7 +177,7 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Processing cache location '${cacheLocation}'`); } - if (this.knownCachesSet[cacheLocation]) { + if (this.knownCachesSet.get(cacheLocation)) { if (this.log.isEnabled()) { this.log.writeLine(`Cache location was already processed...`); } @@ -201,10 +201,10 @@ namespace ts.server.typingsInstaller { } const typingFile = typingToFileName(cacheLocation, packageName, this.installTypingHost, this.log); if (!typingFile) { - this.missingTypingsSet[packageName] = true; + this.missingTypingsSet.set(packageName, true); continue; } - const existingTypingFile = this.packageNameToTypingLocation[packageName]; + const existingTypingFile = this.packageNameToTypingLocation.get(packageName); if (existingTypingFile === typingFile) { continue; } @@ -216,14 +216,14 @@ namespace ts.server.typingsInstaller { if (this.log.isEnabled()) { this.log.writeLine(`Adding entry into typings cache: '${packageName}' => '${typingFile}'`); } - this.packageNameToTypingLocation[packageName] = typingFile; + this.packageNameToTypingLocation.set(packageName, typingFile); } } } if (this.log.isEnabled()) { this.log.writeLine(`Finished processing cache location '${cacheLocation}'`); } - this.knownCachesSet[cacheLocation] = true; + this.knownCachesSet.set(cacheLocation, true); } private filterTypings(typingsToInstall: string[]) { @@ -232,12 +232,12 @@ namespace ts.server.typingsInstaller { } const result: string[] = []; for (const typing of typingsToInstall) { - if (this.missingTypingsSet[typing] || this.packageNameToTypingLocation[typing]) { + if (this.missingTypingsSet.get(typing) || this.packageNameToTypingLocation.get(typing)) { continue; } const validationResult = validatePackageName(typing); if (validationResult === PackageNameValidationResult.Ok) { - if (typing in this.typesRegistry) { + if (this.typesRegistry.has(typing)) { result.push(typing); } else { @@ -248,7 +248,7 @@ namespace ts.server.typingsInstaller { } else { // add typing name to missing set so we won't process it again - this.missingTypingsSet[typing] = true; + this.missingTypingsSet.set(typing, true); if (this.log.isEnabled()) { switch (validationResult) { case PackageNameValidationResult.EmptyName: @@ -323,7 +323,7 @@ namespace ts.server.typingsInstaller { this.log.writeLine(`install request failed, marking packages as missing to prevent repeated requests: ${JSON.stringify(filteredTypings)}`); } for (const typing of filteredTypings) { - this.missingTypingsSet[typing] = true; + this.missingTypingsSet.set(typing, true); } return; } @@ -336,11 +336,11 @@ namespace ts.server.typingsInstaller { for (const packageName of filteredTypings) { const typingFile = typingToFileName(cachePath, packageName, this.installTypingHost, this.log); if (!typingFile) { - this.missingTypingsSet[packageName] = true; + this.missingTypingsSet.set(packageName, true); continue; } - if (!this.packageNameToTypingLocation[packageName]) { - this.packageNameToTypingLocation[packageName] = typingFile; + if (!this.packageNameToTypingLocation.has(packageName)) { + this.packageNameToTypingLocation.set(packageName, typingFile); } installedTypingFiles.push(typingFile); } @@ -395,7 +395,7 @@ namespace ts.server.typingsInstaller { }, /*pollingInterval*/ 2000); watchers.push(w); } - this.projectWatchers[projectName] = watchers; + this.projectWatchers.set(projectName, watchers); } private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings { diff --git a/src/server/utilities.ts b/src/server/utilities.ts index fd370da7fa15f..eb7801e5fd9ba 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -92,7 +92,7 @@ namespace ts.server { }; } - export function mergeMaps(target: MapLike, source: MapLike ): void { + export function mergeMapLikes(target: MapLike, source: MapLike ): void { for (const key in source) { if (hasProperty(source, key)) { target[key] = source[key]; @@ -142,20 +142,20 @@ namespace ts.server { export function createNormalizedPathMap(): NormalizedPathMap { /* tslint:disable:no-null-keyword */ - const map: Map = Object.create(null); + const map = createMap(); /* tslint:enable:no-null-keyword */ return { get(path) { - return map[path]; + return map.get(path); }, set(path, value) { - map[path] = value; + map.set(path, value); }, contains(path) { - return hasProperty(map, path); + return map.has(path); }, remove(path) { - delete map[path]; + map.delete(path); } }; } @@ -195,16 +195,17 @@ namespace ts.server { } public schedule(operationId: string, delay: number, cb: () => void) { - if (hasProperty(this.pendingTimeouts, operationId)) { + const pendingTimeout = this.pendingTimeouts.get(operationId); + if (pendingTimeout) { // another operation was already scheduled for this id - cancel it - this.host.clearTimeout(this.pendingTimeouts[operationId]); + this.host.clearTimeout(pendingTimeout); } // schedule new operation, pass arguments - this.pendingTimeouts[operationId] = this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb); + this.pendingTimeouts.set(operationId, this.host.setTimeout(ThrottledOperations.run, delay, this, operationId, cb)); } private static run(self: ThrottledOperations, operationId: string, cb: () => void) { - delete self.pendingTimeouts[operationId]; + self.pendingTimeouts.delete(operationId); cb(); } } diff --git a/src/services/classifier.ts b/src/services/classifier.ts index c22aec6a78655..6c143ec2af0db 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -557,7 +557,7 @@ namespace ts { // Only bother calling into the typechecker if this is an identifier that // could possibly resolve to a type name. This makes classification run // in a third of the time it would normally take. - if (classifiableNames[identifier.text]) { + if (classifiableNames.get(identifier.text)) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { const type = classifySymbol(symbol, getMeaningFromLocation(node)); diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index e4489fc2dca94..c6373fc1909e8 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -20,21 +20,21 @@ namespace ts { export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { - let fixes = codeFixes[error]; + let fixes = codeFixes.get(error); if (!fixes) { fixes = []; - codeFixes[error] = fixes; + codeFixes.set(error, fixes); } fixes.push(action); }); } export function getSupportedErrorCodes() { - return Object.keys(codeFixes); + return keysOfMap(codeFixes); } export function getFixes(context: CodeFixContext): CodeAction[] { - const fixes = codeFixes[context.errorCode]; + const fixes = codeFixes.get(context.errorCode); let allActions: CodeAction[] = []; forEach(fixes, f => { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index bda310f2d3359..0e7743e29fc3a 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -21,18 +21,19 @@ namespace ts.codefix { return; } - if (!this.symbolIdToActionMap[symbolId]) { - this.symbolIdToActionMap[symbolId] = [newAction]; + const actions = this.symbolIdToActionMap.get(symbolId); + if (!actions) { + this.symbolIdToActionMap.set(symbolId, [newAction]); return; } if (newAction.kind === "CodeChange") { - this.symbolIdToActionMap[symbolId].push(newAction); + actions.push(newAction); return; } const updatedNewImports: ImportCodeAction[] = []; - for (const existingAction of this.symbolIdToActionMap[symbolId]) { + for (const existingAction of this.symbolIdToActionMap.get(symbolId)) { if (existingAction.kind === "CodeChange") { // only import actions should compare updatedNewImports.push(existingAction); @@ -62,7 +63,7 @@ namespace ts.codefix { } // if we reach here, it means the new one is better or equal to all of the existing ones. updatedNewImports.push(newAction); - this.symbolIdToActionMap[symbolId] = updatedNewImports; + this.symbolIdToActionMap.set(symbolId, updatedNewImports); } addActions(symbolId: number, newActions: ImportCodeAction[]) { @@ -73,9 +74,9 @@ namespace ts.codefix { getAllActions() { let result: ImportCodeAction[] = []; - for (const symbolId in this.symbolIdToActionMap) { - result = concatenate(result, this.symbolIdToActionMap[symbolId]); - } + this.symbolIdToActionMap.forEach(actions => { + result = concatenate(result, actions); + }); return result; } @@ -162,8 +163,9 @@ namespace ts.codefix { function getImportDeclarations(moduleSymbol: Symbol) { const moduleSymbolId = getUniqueSymbolId(moduleSymbol); - if (cachedImportDeclarations[moduleSymbolId]) { - return cachedImportDeclarations[moduleSymbolId]; + const cached = cachedImportDeclarations.get(moduleSymbolId); + if (cached) { + return cached; } const existingDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[] = []; @@ -173,7 +175,7 @@ namespace ts.codefix { existingDeclarations.push(getImportDeclaration(importModuleSpecifier)); } } - cachedImportDeclarations[moduleSymbolId] = existingDeclarations; + cachedImportDeclarations.set(moduleSymbolId, existingDeclarations); return existingDeclarations; function getImportDeclaration(moduleSpecifier: LiteralExpression) { diff --git a/src/services/completions.ts b/src/services/completions.ts index b710aa8cd78bb..f7efe1fec5662 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -64,14 +64,14 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; const nameTable = getNameTable(sourceFile); - for (const name in nameTable) { + nameTable.forEach((namePosition, name) => { // Skip identifiers produced only from the current location - if (nameTable[name] === position) { - continue; + if (namePosition === position) { + return; } - if (!uniqueNames[name]) { - uniqueNames[name] = name; + if (!uniqueNames.get(name)) { + uniqueNames.set(name, name); const displayName = getCompletionEntryDisplayName(unescapeIdentifier(name), compilerOptions.target, /*performCharacterChecks*/ true); if (displayName) { const entry = { @@ -83,7 +83,7 @@ namespace ts.Completions { entries.push(entry); } } - } + }); return entries; } @@ -122,9 +122,9 @@ namespace ts.Completions { const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); - if (!uniqueNames[id]) { + if (!uniqueNames.get(id)) { entries.push(entry); - uniqueNames[id] = id; + uniqueNames.set(id, id); } } } @@ -373,14 +373,14 @@ namespace ts.Completions { const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); - if (!foundFiles[foundFileName]) { - foundFiles[foundFileName] = true; + if (!foundFiles.get(foundFileName)) { + foundFiles.set(foundFileName, true); } } - for (const foundFile in foundFiles) { + forEachKeyInMap(foundFiles, foundFile => { result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span)); - } + }); } // If possible, get folder completion as well @@ -1563,14 +1563,14 @@ namespace ts.Completions { } const name = element.propertyName || element.name; - existingImportsOrExports[name.text] = true; + existingImportsOrExports.set(name.text, true); } - if (!someProperties(existingImportsOrExports)) { + if (mapIsEmpty(existingImportsOrExports)) { return filter(exportsOfModule, e => e.name !== "default"); } - return filter(exportsOfModule, e => e.name !== "default" && !existingImportsOrExports[e.name]); + return filter(exportsOfModule, e => e.name !== "default" && !existingImportsOrExports.get(e.name)); } /** @@ -1616,10 +1616,10 @@ namespace ts.Completions { existingName = (m.name).text; } - existingMemberNames[existingName] = true; + existingMemberNames.set(existingName, true); } - return filter(contextualMemberSymbols, m => !existingMemberNames[m.name]); + return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.name)); } /** @@ -1637,11 +1637,11 @@ namespace ts.Completions { } if (attr.kind === SyntaxKind.JsxAttribute) { - seenNames[(attr).name.text] = true; + seenNames.set((attr).name.text, true); } } - return filter(symbols, a => !seenNames[a.name]); + return filter(symbols, a => !seenNames.get(a.name)); } } diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 0f18427e56885..212e145ca609f 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -44,11 +44,11 @@ namespace ts.DocumentHighlights { for (const referencedSymbol of referencedSymbols) { for (const referenceEntry of referencedSymbol.references) { const fileName = referenceEntry.fileName; - let documentHighlights = fileNameToDocumentHighlights[fileName]; + let documentHighlights = fileNameToDocumentHighlights.get(fileName); if (!documentHighlights) { documentHighlights = { fileName, highlightSpans: [] }; - fileNameToDocumentHighlights[fileName] = documentHighlights; + fileNameToDocumentHighlights.set(fileName, documentHighlights); result.push(documentHighlights); } diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index d73e4d3b0a125..4291abf6c1fd0 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -113,16 +113,16 @@ namespace ts { } function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap { - let bucket = buckets[key]; + let bucket = buckets.get(key); if (!bucket && createIfMissing) { - buckets[key] = bucket = createFileMap(); + buckets.set(key, bucket = createFileMap()); } return bucket; } function reportStats() { - const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => { - const entries = buckets[name]; + const bucketInfoArray = keysOfMap(buckets).filter(name => name && name.charAt(0) === "_").map(name => { + const entries = buckets.get(name); const sourceFiles: { name: string; refCount: number; references: string[]; }[] = []; entries.forEachValue((key, entry) => { sourceFiles.push({ diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 313a3d2ea3e71..9e53537b69b49 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -96,7 +96,7 @@ namespace ts.FindAllReferences { const nameTable = getNameTable(sourceFile); - if (nameTable[internedName] !== undefined) { + if (nameTable.get(internedName) !== undefined) { result = result || []; getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } @@ -501,14 +501,14 @@ namespace ts.FindAllReferences { function findOwnConstructorCalls(classSymbol: Symbol): Node[] { const result: Node[] = []; - for (const decl of classSymbol.members["__constructor"].declarations) { + for (const decl of classSymbol.members.get("__constructor").declarations) { Debug.assert(decl.kind === SyntaxKind.Constructor); const ctrKeyword = decl.getChildAt(0); Debug.assert(ctrKeyword.kind === SyntaxKind.ConstructorKeyword); result.push(ctrKeyword); } - forEachProperty(classSymbol.exports, member => { + forEachInMap(classSymbol.exports, member => { const decl = member.valueDeclaration; if (decl && decl.kind === SyntaxKind.MethodDeclaration) { const body = (decl).body; @@ -528,7 +528,7 @@ namespace ts.FindAllReferences { /** Find references to `super` in the constructor of an extending class. */ function superConstructorAccesses(cls: ClassLikeDeclaration): Node[] { const symbol = cls.symbol; - const ctr = symbol.members["__constructor"]; + const ctr = symbol.members.get("__constructor"); if (!ctr) { return []; } @@ -715,12 +715,13 @@ namespace ts.FindAllReferences { } const key = getSymbolId(symbol) + "," + getSymbolId(parent); - if (key in cachedResults) { - return cachedResults[key]; + const cached = cachedResults.get(key); + if (cached !== undefined) { + return cached; } // Set the key so that we don't infinitely recurse - cachedResults[key] = false; + cachedResults.set(key, false); const inherits = forEach(symbol.getDeclarations(), declaration => { if (isClassLike(declaration)) { @@ -744,7 +745,7 @@ namespace ts.FindAllReferences { return false; }); - cachedResults[key] = inherits; + cachedResults.set(key, inherits); return inherits; } @@ -1078,7 +1079,7 @@ namespace ts.FindAllReferences { // the function will add any found symbol of the property-name, then its sub-routine will call // getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already // visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol. - if (symbol.name in previousIterationSymbolsCache) { + if (previousIterationSymbolsCache.has(symbol.name)) { return; } @@ -1105,7 +1106,7 @@ namespace ts.FindAllReferences { } // Visit the typeReference as well to see if it directly or indirectly use that property - previousIterationSymbolsCache[symbol.name] = symbol; + previousIterationSymbolsCache.set(symbol.name, symbol); getPropertySymbolsFromBaseTypes(type.symbol, propertyName, result, previousIterationSymbolsCache); } } diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 2095f062bd103..215289159c289 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -4,7 +4,7 @@ namespace ts.formatting { export class Rules { public getRuleName(rule: Rule) { - const o: ts.Map = this; + const o: ts.MapLike = this; for (const name in o) { if (o[name] === rule) { return name; diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 4c005640d6ae8..d6ade7aed6bd9 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -14,7 +14,7 @@ namespace ts.GoToDefinition { // Type reference directives const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position); if (typeReferenceDirective) { - const referenceFile = program.getResolvedTypeReferenceDirectives()[typeReferenceDirective.fileName]; + const referenceFile = program.getResolvedTypeReferenceDirectives().get(typeReferenceDirective.fileName); if (referenceFile && referenceFile.resolvedFileName) { return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)]; } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 04ac60c4e7438..e587a14b459f8 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -17,11 +17,11 @@ namespace ts.JsTyping { interface PackageJson { _requiredBy?: string[]; - dependencies?: Map; - devDependencies?: Map; + dependencies?: MapLike; + devDependencies?: MapLike; name?: string; - optionalDependencies?: Map; - peerDependencies?: Map; + optionalDependencies?: MapLike; + peerDependencies?: MapLike; typings?: string; }; @@ -107,34 +107,34 @@ namespace ts.JsTyping { // add typings for unresolved imports if (unresolvedImports) { for (const moduleId of unresolvedImports) { - const typingName = moduleId in nodeCoreModules ? "node" : moduleId; - if (!(typingName in inferredTypings)) { - inferredTypings[typingName] = undefined; + const typingName = nodeCoreModules.has(moduleId) ? "node" : moduleId; + if (!inferredTypings.has(typingName)) { + inferredTypings.set(typingName, undefined); } } } // Add the cached typing locations for inferred typings that are already installed - for (const name in packageNameToTypingLocation) { - if (name in inferredTypings && !inferredTypings[name]) { - inferredTypings[name] = packageNameToTypingLocation[name]; + packageNameToTypingLocation.forEach((typingLocation, name) => { + if (inferredTypings.has(name) && inferredTypings.get(name) === undefined) { + inferredTypings.set(name, typingLocation); } - } + }); // Remove typings that the user has added to the exclude list for (const excludeTypingName of exclude) { - delete inferredTypings[excludeTypingName]; + inferredTypings.delete(excludeTypingName); } const newTypingNames: string[] = []; const cachedTypingPaths: string[] = []; - for (const typing in inferredTypings) { - if (inferredTypings[typing] !== undefined) { - cachedTypingPaths.push(inferredTypings[typing]); + inferredTypings.forEach((inferred, typing) => { + if (inferred !== undefined) { + cachedTypingPaths.push(inferred); } else { newTypingNames.push(typing); } - } + }); return { cachedTypingPaths, newTypingNames, filesToWatch }; /** @@ -146,8 +146,8 @@ namespace ts.JsTyping { } for (const typing of typingNames) { - if (!(typing in inferredTypings)) { - inferredTypings[typing] = undefined; + if (!inferredTypings.has(typing)) { + inferredTypings.set(typing, undefined); } } } @@ -189,7 +189,7 @@ namespace ts.JsTyping { const cleanedTypingNames = map(inferredTypingNames, f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "")); if (safeList !== EmptySafeList) { - mergeTypings(filter(cleanedTypingNames, f => f in safeList)); + mergeTypings(filter(cleanedTypingNames, f => safeList.has(f))); } const hasJsxFile = forEach(fileNames, f => ensureScriptKind(f, getScriptKindFromFileName(f)) === ScriptKind.JSX); @@ -236,7 +236,7 @@ namespace ts.JsTyping { } if (packageJson.typings) { const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName)); - inferredTypings[packageJson.name] = absolutePath; + inferredTypings.set(packageJson.name, absolutePath); } else { typingNames.push(packageJson.name); diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 82f4dd49f2b73..40f229412681e 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -7,23 +7,21 @@ namespace ts.NavigateTo { let rawItems: RawNavigateToItem[] = []; // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[] - forEach(sourceFiles, sourceFile => { + for (const sourceFile of sourceFiles) { cancellationToken.throwIfCancellationRequested(); if (excludeDtsFiles && fileExtensionIs(sourceFile.fileName, ".d.ts")) { - return; + continue; } - const nameToDeclarations = sourceFile.getNamedDeclarations(); - for (const name in nameToDeclarations) { - const declarations = nameToDeclarations[name]; + forEachInMap(sourceFile.getNamedDeclarations(), (declarations, name) => { if (declarations) { // First do a quick check to see if the name of the declaration matches the // last portion of the (possibly) dotted name they're searching for. let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name); if (!matches) { - continue; + return; // continue to next named declarations } for (const declaration of declarations) { @@ -32,13 +30,13 @@ namespace ts.NavigateTo { if (patternMatcher.patternContainsDots) { const containers = getContainers(declaration); if (!containers) { - return undefined; + return true; // Break out of named declarations and go to the next source file. } matches = patternMatcher.getMatches(containers, name); if (!matches) { - continue; + return; // continue to next named declarations } } @@ -47,8 +45,8 @@ namespace ts.NavigateTo { rawItems.push({ name, fileName, matchKind, isCaseSensitive: allMatchesAreCaseSensitive(matches), declaration }); } } - } - }); + }); + } // Remove imports when the imported declaration is already in the list and has the same name. rawItems = filter(rawItems, item => { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 1cbaa3d640faf..7d95787939de2 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -248,9 +248,9 @@ namespace ts.NavigationBar { return true; } - const itemsWithSameName = nameToItems[name]; + const itemsWithSameName = nameToItems.get(name); if (!itemsWithSameName) { - nameToItems[name] = child; + nameToItems.set(name, child); return true; } @@ -268,7 +268,7 @@ namespace ts.NavigationBar { if (tryMerge(itemWithSameName, child)) { return false; } - nameToItems[name] = [itemWithSameName, child]; + nameToItems.set(name, [itemWithSameName, child]); return true; } @@ -626,15 +626,15 @@ namespace ts.NavigationBar { /** * Matches all whitespace characters in a string. Eg: - * + * * "app. - * + * * onactivated" - * + * * matches because of the newline, whereas - * + * * "app.onactivated" - * + * * does not match. */ const whiteSpaceRegex = /\s+/g; diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index b4f67ca056ddb..b8941ef3147ac 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -188,11 +188,7 @@ namespace ts { } function getWordSpans(word: string): TextSpan[] { - if (!(word in stringToWordSpans)) { - stringToWordSpans[word] = breakIntoWordSpans(word); - } - - return stringToWordSpans[word]; + return stringToWordSpans.get(word) || set(stringToWordSpans, word, breakIntoWordSpans(word)); } function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch { diff --git a/src/services/services.ts b/src/services/services.ts index d28dd87110525..77a5e916067ed 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -532,7 +532,7 @@ namespace ts { } function getDeclarations(name: string) { - return result[name] || (result[name] = []); + return result.get(name) || set(result, name, []); } function getDeclarationName(declaration: Declaration) { @@ -1956,9 +1956,11 @@ namespace ts { function walk(node: Node) { switch (node.kind) { - case SyntaxKind.Identifier: - nameTable[(node).text] = nameTable[(node).text] === undefined ? node.pos : -1; + case SyntaxKind.Identifier: { + const text = (node).text; + nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1); break; + } case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: // We want to store any numbers/strings if they were a name that could be @@ -1970,7 +1972,8 @@ namespace ts { isArgumentOfElementAccessExpression(node) || isLiteralComputedPropertyDeclarationName(node)) { - nameTable[(node).text] = nameTable[(node).text] === undefined ? node.pos : -1; + const text = (node).text; + nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1); } break; default: diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 44c2ede9fbb52..aa799eb32c71f 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -237,7 +237,7 @@ namespace ts.SignatureHelp { const typeChecker = program.getTypeChecker(); for (const sourceFile of program.getSourceFiles()) { const nameToDeclarations = sourceFile.getNamedDeclarations(); - const declarations = nameToDeclarations[name.text]; + const declarations = nameToDeclarations.get(name.text); if (declarations) { for (const declaration of declarations) { diff --git a/src/services/transpile.ts b/src/services/transpile.ts index da54faed1a2f7..d989a528497d8 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -126,7 +126,7 @@ namespace ts { function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { // Lazily create this value to fix module loading errors. commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number")); + typeof o.type === "object" && !forEachInMap(o.type, v => typeof v !== "number")); options = clone(options); @@ -142,7 +142,7 @@ namespace ts { options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } else { - if (!forEachProperty(opt.type, v => v === value)) { + if (!someInMap(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } diff --git a/tests/baselines/reference/computedPropertyNames10_ES5.types b/tests/baselines/reference/computedPropertyNames10_ES5.types index 650890fb10ebe..9613097929857 100644 --- a/tests/baselines/reference/computedPropertyNames10_ES5.types +++ b/tests/baselines/reference/computedPropertyNames10_ES5.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: () => void; [x: number]: () => void; [0](): void; [""](): void; } ->{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [0](): void; [""](): void; } +>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; } +>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; } [s]() { }, >s : string diff --git a/tests/baselines/reference/computedPropertyNames10_ES6.types b/tests/baselines/reference/computedPropertyNames10_ES6.types index 2ff86ed9712d0..5bbda2e8f19c0 100644 --- a/tests/baselines/reference/computedPropertyNames10_ES6.types +++ b/tests/baselines/reference/computedPropertyNames10_ES6.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: () => void; [x: number]: () => void; [0](): void; [""](): void; } ->{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [0](): void; [""](): void; } +>v : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; } +>{ [s]() { }, [n]() { }, [s + s]() { }, [s + n]() { }, [+s]() { }, [""]() { }, [0]() { }, [a]() { }, [true]() { }, [`hello bye`]() { }, [`hello ${a} bye`]() { }} : { [x: string]: () => void; [x: number]: () => void; [""](): void; [0](): void; } [s]() { }, >s : string diff --git a/tests/baselines/reference/computedPropertyNames11_ES5.types b/tests/baselines/reference/computedPropertyNames11_ES5.types index 0787a889f4031..88fbc690323e8 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES5.types +++ b/tests/baselines/reference/computedPropertyNames11_ES5.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: any; [x: number]: any; readonly [0]: number; [""]: any; } ->{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; readonly [0]: number; [""]: any; } +>v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } +>{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } get [s]() { return 0; }, >s : string diff --git a/tests/baselines/reference/computedPropertyNames11_ES6.types b/tests/baselines/reference/computedPropertyNames11_ES6.types index 4783d07a2afa0..458d6d1de4904 100644 --- a/tests/baselines/reference/computedPropertyNames11_ES6.types +++ b/tests/baselines/reference/computedPropertyNames11_ES6.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: any; [x: number]: any; readonly [0]: number; [""]: any; } ->{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; readonly [0]: number; [""]: any; } +>v : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } +>{ get [s]() { return 0; }, set [n](v) { }, get [s + s]() { return 0; }, set [s + n](v) { }, get [+s]() { return 0; }, set [""](v) { }, get [0]() { return 0; }, set [a](v) { }, get [true]() { return 0; }, set [`hello bye`](v) { }, get [`hello ${a} bye`]() { return 0; }} : { [x: string]: any; [x: number]: any; [""]: any; readonly [0]: number; } get [s]() { return 0; }, >s : string diff --git a/tests/baselines/reference/computedPropertyNames4_ES5.types b/tests/baselines/reference/computedPropertyNames4_ES5.types index 64ef5f6a61119..6f875aaddac3f 100644 --- a/tests/baselines/reference/computedPropertyNames4_ES5.types +++ b/tests/baselines/reference/computedPropertyNames4_ES5.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: string | number; [x: number]: string | number; [0]: number; [""]: number; } ->{ [s]: 0, [n]: n, [s + s]: 1, [s + n]: 2, [+s]: s, [""]: 0, [0]: 0, [a]: 1, [true]: 0, [`hello bye`]: 0, [`hello ${a} bye`]: 0} : { [x: string]: string | number; [x: number]: string | number; [0]: number; [""]: number; } +>v : { [x: string]: string | number; [x: number]: string | number; [""]: number; [0]: number; } +>{ [s]: 0, [n]: n, [s + s]: 1, [s + n]: 2, [+s]: s, [""]: 0, [0]: 0, [a]: 1, [true]: 0, [`hello bye`]: 0, [`hello ${a} bye`]: 0} : { [x: string]: string | number; [x: number]: string | number; [""]: number; [0]: number; } [s]: 0, >s : string diff --git a/tests/baselines/reference/computedPropertyNames4_ES6.types b/tests/baselines/reference/computedPropertyNames4_ES6.types index 0c608e2160114..e9feac0a81fcb 100644 --- a/tests/baselines/reference/computedPropertyNames4_ES6.types +++ b/tests/baselines/reference/computedPropertyNames4_ES6.types @@ -9,8 +9,8 @@ var a: any; >a : any var v = { ->v : { [x: string]: string | number; [x: number]: string | number; [0]: number; [""]: number; } ->{ [s]: 0, [n]: n, [s + s]: 1, [s + n]: 2, [+s]: s, [""]: 0, [0]: 0, [a]: 1, [true]: 0, [`hello bye`]: 0, [`hello ${a} bye`]: 0} : { [x: string]: string | number; [x: number]: string | number; [0]: number; [""]: number; } +>v : { [x: string]: string | number; [x: number]: string | number; [""]: number; [0]: number; } +>{ [s]: 0, [n]: n, [s + s]: 1, [s + n]: 2, [+s]: s, [""]: 0, [0]: 0, [a]: 1, [true]: 0, [`hello bye`]: 0, [`hello ${a} bye`]: 0} : { [x: string]: string | number; [x: number]: string | number; [""]: number; [0]: number; } [s]: 0, >s : string diff --git a/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.types b/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.types index 91ad88c8aded5..f144bb9d020e9 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.types +++ b/tests/baselines/reference/computedPropertyNamesContextualType6_ES5.types @@ -19,7 +19,7 @@ declare function foo(obj: I): T foo({ >foo({ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]}) : string | number | boolean | (() => void) | number[] >foo : (obj: I) => T ->{ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]} : { [x: string]: true | "" | (() => void) | 0 | number[]; [x: number]: (() => void) | 0 | number[]; 0: () => void; p: ""; } +>{ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]} : { [x: string]: true | "" | (() => void) | 0 | number[]; [x: number]: (() => void) | 0 | number[]; p: ""; 0: () => void; } p: "", >p : string diff --git a/tests/baselines/reference/computedPropertyNamesContextualType6_ES6.types b/tests/baselines/reference/computedPropertyNamesContextualType6_ES6.types index 131e49dd173fa..6b8c8ae2e34fc 100644 --- a/tests/baselines/reference/computedPropertyNamesContextualType6_ES6.types +++ b/tests/baselines/reference/computedPropertyNamesContextualType6_ES6.types @@ -19,7 +19,7 @@ declare function foo(obj: I): T foo({ >foo({ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]}) : string | number | boolean | (() => void) | number[] >foo : (obj: I) => T ->{ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]} : { [x: string]: true | "" | (() => void) | 0 | number[]; [x: number]: (() => void) | 0 | number[]; 0: () => void; p: ""; } +>{ p: "", 0: () => { }, ["hi" + "bye"]: true, [0 + 1]: 0, [+"hi"]: [0]} : { [x: string]: true | "" | (() => void) | 0 | number[]; [x: number]: (() => void) | 0 | number[]; p: ""; 0: () => void; } p: "", >p : string diff --git a/tests/baselines/reference/emitCompoundExponentiationAssignmentWithIndexingOnLHS3.types b/tests/baselines/reference/emitCompoundExponentiationAssignmentWithIndexingOnLHS3.types index 94a5f2173aa24..32e5b33e41132 100644 --- a/tests/baselines/reference/emitCompoundExponentiationAssignmentWithIndexingOnLHS3.types +++ b/tests/baselines/reference/emitCompoundExponentiationAssignmentWithIndexingOnLHS3.types @@ -1,8 +1,8 @@ === tests/cases/conformance/es7/exponentiationOperator/emitCompoundExponentiationAssignmentWithIndexingOnLHS3.ts === var object = { ->object : { 0: number; _0: number; } ->{ _0: 2, get 0() { return this._0; }, set 0(x: number) { this._0 = x; },} : { 0: number; _0: number; } +>object : { _0: number; 0: number; } +>{ _0: 2, get 0() { return this._0; }, set 0(x: number) { this._0 = x; },} : { _0: number; 0: number; } _0: 2, >_0 : number @@ -30,31 +30,31 @@ var object = { object[0] **= object[0]; >object[0] **= object[0] : number >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 object[0] **= object[0] **= 2; >object[0] **= object[0] **= 2 : number >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 >object[0] **= 2 : number >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 >2 : 2 object[0] **= object[0] ** 2; >object[0] **= object[0] ** 2 : number >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 >object[0] ** 2 : number >object[0] : number ->object : { 0: number; _0: number; } +>object : { _0: number; 0: number; } >0 : 0 >2 : 2 diff --git a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt index ec0f330f1a308..f532bd237559d 100644 --- a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt +++ b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations.errors.txt @@ -5,7 +5,7 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(36,16): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(50,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(68,5): error TS2412: Property '2.0' of type 'number' is not assignable to numeric index type 'string'. -tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(79,5): error TS2322: Type '{ 1.0: string; 2.0: number; a: string; b: number; c: () => void; "d": string; "e": number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. +tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(79,5): error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. Object literal may only specify known properties, and 'a' does not exist in type '{ [x: number]: string; }'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(88,9): error TS2304: Cannot find name 'Myn'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations.ts(90,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. @@ -107,7 +107,7 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo var b: { [x: number]: string; } = { a: '', ~~~~~ -!!! error TS2322: Type '{ 1.0: string; 2.0: number; a: string; b: number; c: () => void; "d": string; "e": number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. +!!! error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: any; X: string; foo(): string; }' is not assignable to type '{ [x: number]: string; }'. !!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type '{ [x: number]: string; }'. b: 1, c: () => { }, diff --git a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations2.errors.txt b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations2.errors.txt index e6d22ff071287..74cd032c40e32 100644 --- a/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations2.errors.txt +++ b/tests/baselines/reference/numericIndexerConstrainsPropertyDeclarations2.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(16,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(25,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(34,5): error TS2412: Property '3.0' of type 'number' is not assignable to numeric index type 'A'. -tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(44,5): error TS2322: Type '{ 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }'. +tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerConstrainsPropertyDeclarations2.ts(44,5): error TS2322: Type '{ 1.0: A; 2.0: B; "2.5": B; 3.0: number; "4.0": string; }' is not assignable to type '{ [x: number]: A; }'. Object literal may only specify known properties, and '"4.0"' does not exist in type '{ [x: number]: A; }'. @@ -57,6 +57,6 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/numericIndexerCo 3.0: 1, "4.0": '' ~~~~~~~~~ -!!! error TS2322: Type '{ 1.0: A; 2.0: B; 3.0: number; "2.5": B; "4.0": string; }' is not assignable to type '{ [x: number]: A; }'. +!!! error TS2322: Type '{ 1.0: A; 2.0: B; "2.5": B; 3.0: number; "4.0": string; }' is not assignable to type '{ [x: number]: A; }'. !!! error TS2322: Object literal may only specify known properties, and '"4.0"' does not exist in type '{ [x: number]: A; }'. } \ No newline at end of file diff --git a/tests/baselines/reference/numericIndexerConstraint5.errors.txt b/tests/baselines/reference/numericIndexerConstraint5.errors.txt index cb888de08c4ec..04b7e0dc337c3 100644 --- a/tests/baselines/reference/numericIndexerConstraint5.errors.txt +++ b/tests/baselines/reference/numericIndexerConstraint5.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/numericIndexerConstraint5.ts(2,5): error TS2322: Type '{ 0: Date; name: string; }' is not assignable to type '{ [name: number]: string; }'. +tests/cases/compiler/numericIndexerConstraint5.ts(2,5): error TS2322: Type '{ name: string; 0: Date; }' is not assignable to type '{ [name: number]: string; }'. Property '0' is incompatible with index signature. Type 'Date' is not assignable to type 'string'. @@ -7,6 +7,6 @@ tests/cases/compiler/numericIndexerConstraint5.ts(2,5): error TS2322: Type '{ 0: var x = { name: "x", 0: new Date() }; var z: { [name: number]: string } = x; ~ -!!! error TS2322: Type '{ 0: Date; name: string; }' is not assignable to type '{ [name: number]: string; }'. +!!! error TS2322: Type '{ name: string; 0: Date; }' is not assignable to type '{ [name: number]: string; }'. !!! error TS2322: Property '0' is incompatible with index signature. !!! error TS2322: Type 'Date' is not assignable to type 'string'. \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralIndexerErrors.errors.txt b/tests/baselines/reference/objectLiteralIndexerErrors.errors.txt index 9d5ff7ea0a2ba..53abb5b7658d1 100644 --- a/tests/baselines/reference/objectLiteralIndexerErrors.errors.txt +++ b/tests/baselines/reference/objectLiteralIndexerErrors.errors.txt @@ -1,8 +1,8 @@ -tests/cases/compiler/objectLiteralIndexerErrors.ts(13,5): error TS2322: Type '{ 0: A; x: B; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. +tests/cases/compiler/objectLiteralIndexerErrors.ts(13,5): error TS2322: Type '{ x: B; 0: A; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. Property '0' is incompatible with index signature. Type 'A' is not assignable to type 'B'. Property 'y' is missing in type 'A'. -tests/cases/compiler/objectLiteralIndexerErrors.ts(14,1): error TS2322: Type '{ 0: A; x: any; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. +tests/cases/compiler/objectLiteralIndexerErrors.ts(14,1): error TS2322: Type '{ x: any; 0: A; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. Property '0' is incompatible with index signature. Type 'A' is not assignable to type 'B'. @@ -22,12 +22,12 @@ tests/cases/compiler/objectLiteralIndexerErrors.ts(14,1): error TS2322: Type '{ var o1: { [s: string]: A;[n: number]: B; } = { x: b, 0: a }; // both indexers are A ~~ -!!! error TS2322: Type '{ 0: A; x: B; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. +!!! error TS2322: Type '{ x: B; 0: A; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. !!! error TS2322: Property '0' is incompatible with index signature. !!! error TS2322: Type 'A' is not assignable to type 'B'. !!! error TS2322: Property 'y' is missing in type 'A'. o1 = { x: c, 0: a }; // string indexer is any, number indexer is A ~~ -!!! error TS2322: Type '{ 0: A; x: any; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. +!!! error TS2322: Type '{ x: any; 0: A; }' is not assignable to type '{ [s: string]: A; [n: number]: B; }'. !!! error TS2322: Property '0' is incompatible with index signature. !!! error TS2322: Type 'A' is not assignable to type 'B'. \ No newline at end of file diff --git a/tests/baselines/reference/objectLiteralIndexers.types b/tests/baselines/reference/objectLiteralIndexers.types index 9e3aa4bac49c3..ef242fd1fe30f 100644 --- a/tests/baselines/reference/objectLiteralIndexers.types +++ b/tests/baselines/reference/objectLiteralIndexers.types @@ -31,23 +31,23 @@ var o1: { [s: string]: A;[n: number]: B; } = { x: a, 0: b }; // string indexer i >A : A >n : number >B : B ->{ x: a, 0: b } : { 0: B; x: A; } +>{ x: a, 0: b } : { x: A; 0: B; } >x : A >a : A >b : B o1 = { x: b, 0: c }; // both indexers are any ->o1 = { x: b, 0: c } : { 0: any; x: B; } +>o1 = { x: b, 0: c } : { x: B; 0: any; } >o1 : { [s: string]: A; [n: number]: B; } ->{ x: b, 0: c } : { 0: any; x: B; } +>{ x: b, 0: c } : { x: B; 0: any; } >x : B >b : B >c : any o1 = { x: c, 0: b }; // string indexer is any, number indexer is B ->o1 = { x: c, 0: b } : { 0: B; x: any; } +>o1 = { x: c, 0: b } : { x: any; 0: B; } >o1 : { [s: string]: A; [n: number]: B; } ->{ x: c, 0: b } : { 0: B; x: any; } +>{ x: c, 0: b } : { x: any; 0: B; } >x : any >c : any >b : B diff --git a/tests/baselines/reference/objectTypeWithStringNamedNumericProperty.types b/tests/baselines/reference/objectTypeWithStringNamedNumericProperty.types index e050d33024c54..1f34c4fafacb3 100644 --- a/tests/baselines/reference/objectTypeWithStringNamedNumericProperty.types +++ b/tests/baselines/reference/objectTypeWithStringNamedNumericProperty.types @@ -267,7 +267,7 @@ var r13 = i[-01] >01 : 1 var a: { ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } "0.1": void; ".1": Object; @@ -289,19 +289,19 @@ var a: { var r1 = a['0.1']; >r1 : void >a['0.1'] : void ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'0.1' : "0.1" var r2 = a['.1']; >r2 : Object >a['.1'] : Object ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'.1' : ".1" var r3 = a['1']; >r3 : number >a['1'] : number ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'1' : "1" var r3 = c[1]; @@ -313,7 +313,7 @@ var r3 = c[1]; var r4 = a['1.']; >r4 : string >a['1.'] : string ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'1.' : "1." var r3 = c[1.]; // same as indexing by 1 when done numerically @@ -325,13 +325,13 @@ var r3 = c[1.]; // same as indexing by 1 when done numerically var r5 = a['1..']; >r5 : boolean >a['1..'] : boolean ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'1..' : "1.." var r6 = a['1.0']; >r6 : Date >a['1.0'] : Date ->a : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } +>a : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": Date; } >'1.0' : "1.0" var r3 = c[1.0]; // same as indexing by 1 when done numerically @@ -394,8 +394,8 @@ var r13 = i[-01] >01 : 1 var b = { ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } ->{ "0.1": null, ".1": new Object(), "1": 1, "1.": "", "1..": true, "1.0": new Date(), "-1.0": /123/, "-1": Date} : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>{ "0.1": null, ".1": new Object(), "1": 1, "1.": "", "1..": true, "1.0": new Date(), "-1.0": /123/, "-1": Date} : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } "0.1": null, >null : void @@ -429,19 +429,19 @@ var b = { var r1 = b['0.1']; >r1 : void >b['0.1'] : void ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'0.1' : "0.1" var r2 = b['.1']; >r2 : Object >b['.1'] : Object ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'.1' : ".1" var r3 = b['1']; >r3 : number >b['1'] : number ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'1' : "1" var r3 = c[1]; @@ -453,7 +453,7 @@ var r3 = c[1]; var r4 = b['1.']; >r4 : string >b['1.'] : string ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'1.' : "1." var r3 = c[1.]; // same as indexing by 1 when done numerically @@ -465,13 +465,13 @@ var r3 = c[1.]; // same as indexing by 1 when done numerically var r5 = b['1..']; >r5 : boolean >b['1..'] : boolean ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'1..' : "1.." var r6 = b['1.0']; >r6 : Date >b['1.0'] : Date ->b : { "1": number; "0.1": void; ".1": Object; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } +>b : { "0.1": void; ".1": Object; "1": number; "1.": string; "1..": boolean; "1.0": Date; "-1.0": RegExp; "-1": DateConstructor; } >'1.0' : "1.0" var r3 = c[1.0]; // same as indexing by 1 when done numerically diff --git a/tests/baselines/reference/stringIndexerConstrainsPropertyDeclarations.errors.txt b/tests/baselines/reference/stringIndexerConstrainsPropertyDeclarations.errors.txt index 8f4bbe2025a52..606cc39afef98 100644 --- a/tests/baselines/reference/stringIndexerConstrainsPropertyDeclarations.errors.txt +++ b/tests/baselines/reference/stringIndexerConstrainsPropertyDeclarations.errors.txt @@ -22,8 +22,8 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerCon tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(71,5): error TS2411: Property 'foo' of type '() => string' is not assignable to string index type 'string'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(73,5): error TS2411: Property '"4.0"' of type 'number' is not assignable to string index type 'string'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(74,5): error TS2411: Property 'f' of type 'MyString' is not assignable to string index type 'string'. -tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(78,5): error TS2322: Type '{ 1.0: string; 2.0: number; a: string; b: number; c: () => void; "d": string; "e": number; "3.0": string; "4.0": number; f: MyString; X: string; foo(): string; }' is not assignable to type '{ [x: string]: string; }'. - Property '2.0' is incompatible with index signature. +tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(78,5): error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: MyString; X: string; foo(): string; }' is not assignable to type '{ [x: string]: string; }'. + Property 'b' is incompatible with index signature. Type 'number' is not assignable to type 'string'. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(90,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerConstrainsPropertyDeclarations.ts(93,9): error TS1056: Accessors are only available when targeting ECMAScript 5 and higher. @@ -157,8 +157,8 @@ tests/cases/conformance/types/objectTypeLiteral/indexSignatures/stringIndexerCon // error var b: { [x: string]: string; } = { ~ -!!! error TS2322: Type '{ 1.0: string; 2.0: number; a: string; b: number; c: () => void; "d": string; "e": number; "3.0": string; "4.0": number; f: MyString; X: string; foo(): string; }' is not assignable to type '{ [x: string]: string; }'. -!!! error TS2322: Property '2.0' is incompatible with index signature. +!!! error TS2322: Type '{ a: string; b: number; c: () => void; "d": string; "e": number; 1.0: string; 2.0: number; "3.0": string; "4.0": number; f: MyString; X: string; foo(): string; }' is not assignable to type '{ [x: string]: string; }'. +!!! error TS2322: Property 'b' is incompatible with index signature. !!! error TS2322: Type 'number' is not assignable to type 'string'. a: '', b: 1, diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 745a746db116b..f97e122aabbbf 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -43,6 +43,16 @@ // TODO: figure out a better solution to the API exposure problem. declare module ts { + export type MapKey = string | number; + export interface Map { + forEach(action: (value: T, key: string) => void): void; + get(key: MapKey): T; + has(key: MapKey): boolean; + set(key: MapKey, value: T): this; + delete(key: MapKey): boolean; + clear(): void; + } + interface SymbolDisplayPart { text: string; kind: string; @@ -103,7 +113,7 @@ declare namespace FourSlashInterface { markerNames(): string[]; marker(name?: string): Marker; ranges(): Range[]; - rangesByText(): { [text: string]: Range[] }; + rangesByText(): ts.Map; markerByName(s: string): Marker; } class goTo { diff --git a/tests/cases/fourslash/localGetReferences.ts b/tests/cases/fourslash/localGetReferences.ts index b05346a366a4a..fa9e59cabcfc3 100644 --- a/tests/cases/fourslash/localGetReferences.ts +++ b/tests/cases/fourslash/localGetReferences.ts @@ -202,15 +202,14 @@ goTo.marker("4"); verify.referencesAre([]); const rangesByText = test.rangesByText(); -for (const text in rangesByText) { - const ranges = rangesByText[text]; +rangesByText.forEach((ranges, text) => { if (text === "globalVar") { verify.rangesReferenceEachOther(ranges.filter(isShadow)); verify.rangesReferenceEachOther(ranges.filter(r => !isShadow(r))); } else { verify.rangesReferenceEachOther(ranges); } -} +}); function isShadow(r) { return r.marker && r.marker.data && r.marker.data.shadow; diff --git a/tslint.json b/tslint.json index 26657981a63b7..13de7acec634e 100644 --- a/tslint.json +++ b/tslint.json @@ -46,6 +46,7 @@ "prefer-const": true, "no-increment-decrement": true, "object-literal-surrounding-space": true, - "no-type-assertion-whitespace": true + "no-type-assertion-whitespace": true, + "no-in-operator": true } } From b15ffda7f699097c7abf49252558dcafb3da3a50 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 8 Dec 2016 06:55:49 -0800 Subject: [PATCH 02/20] Simplify forEachKeyInMap and someKeyInMap --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 46 +++++++++-------------------------------- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00a6ad2c36619..67c4db715e250 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18991,7 +18991,7 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - return someKeyInMap(moduleSymbol.exports, id => id !== "export="); + return someInMap(moduleSymbol.exports, (_, id) => id !== "export="); } function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index affd225b3a72c..3fe3b386701a1 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -68,7 +68,6 @@ namespace ts { interface ES6Map extends Map { readonly size: number; entries(): Iterator<[string, T]>; - keys(): Iterator; } /** ES6 Iterator type. */ @@ -80,9 +79,7 @@ namespace ts { interface ShimMap extends Map { isEmpty(): boolean; forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined; - forEachKey(callback: (key: string) => U | undefined): U | undefined; some(predicate: (value: T, key: string) => boolean): boolean; - someKey(predicate: (key: string) => boolean): boolean; } // The global Map object. This may not be available, so we must test for it. @@ -130,16 +127,12 @@ namespace ts { } isEmpty(): boolean { - return !this.someKey(() => true); + return !this.some(() => true); } forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined { - return this.forEachKey(key => callback(this.data[key], key)); - } - - forEachKey(callback: (key: string) => U | undefined): U | undefined { for (const key in this.data) { - const result = callback(key); + const result = callback(this.data[key], key); if (result !== undefined) { return result; } @@ -147,12 +140,8 @@ namespace ts { } some(predicate: (value: T, key: string) => boolean): boolean { - return this.someKey(key => predicate(this.data[key], key)); - } - - someKey(predicate: (key: string) => boolean): boolean { for (const key in this.data) { - if (predicate(key)) { + if (predicate(this.data[key], key)) { return true; } } @@ -939,17 +928,9 @@ namespace ts { : (map: ShimMap, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback); /** `forEachInMap` for just keys. */ - export const forEachKeyInMap: (map: Map<{}>, callback: (key: string) => T | undefined) => T | undefined = usingNativeMaps - ? (map: ES6Map, callback: (key: string) => T | undefined) => { - const iterator = map.keys(); - while (true) { - const { value: key, done } = iterator.next(); - if (done) return undefined; - const result = callback(key); - if (result !== undefined) return result; - } - } - : (map: ShimMap, callback: (key: string) => T | undefined) => map.forEachKey(callback); + export function forEachKeyInMap(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined { + return forEachInMap(map, (_, key) => callback(key)); + } /** Whether `predicate` is true for some entry in the map. */ export const someInMap: (map: Map, predicate: (value: T, key: string) => boolean) => boolean = usingNativeMaps @@ -964,17 +945,10 @@ namespace ts { } : (map: ShimMap, predicate: (value: T, key: string) => boolean) => map.some(predicate); - /** Whether `predicate` is true for some key in the map. */ - export const someKeyInMap: (map: Map<{}>, predicate: (key: string) => boolean) => boolean = usingNativeMaps - ? (map: ES6Map<{}>, predicate: (key: string) => boolean) => { - const iterator = map.keys(); - while (true) { - const { value: key, done } = iterator.next(); - if (done) return false; - if (predicate(key)) return true; - } - } - : (map: ShimMap<{}>, predicate: (key: string) => boolean) => map.someKey(predicate); + /** `someInMap` for just keys. */ + export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean { + return someInMap(map, (_, key) => predicate(key)); + } /** Copy entries from `source` to `target`. */ export function copyMapEntries(source: Map, target: Map): void { From 863e4d6fa06b7d705592167285f17dd12981d8e8 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 8 Dec 2016 08:00:10 -0800 Subject: [PATCH 03/20] Clean up helpers --- src/compiler/core.ts | 23 +++++++------------ src/compiler/types.ts | 1 + .../unittests/reuseProgramStructure.ts | 14 +++++++++++ .../unittests/tsserverProjectSystem.ts | 10 ++------ 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3fe3b386701a1..3c370f7b154c8 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -66,7 +66,6 @@ namespace ts { /** Methods on native maps but not on shim maps. Only used in this file. */ interface ES6Map extends Map { - readonly size: number; entries(): Iterator<[string, T]>; } @@ -130,6 +129,14 @@ namespace ts { return !this.some(() => true); } + get size(): number { + let size = 0; + for (const _ in this.data) { + size++; + } + return size; + } + forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined { for (const key in this.data) { const result = callback(this.data[key], key); @@ -988,20 +995,6 @@ namespace ts { return true; } - /** True if the maps have the same keys and values. */ - export function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: T, right: T) => 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 map from the elements of an array. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 055049999ec30..75e1e82e7f6ee 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -20,6 +20,7 @@ namespace ts { clear(): void; /** `key` may *not* be a string if it was set with a number and we are not using the shim. */ forEach(action: (value: T, key: string) => void): void; + readonly size: number; } // branded string type used to store absolute, normalized and canonicalized paths diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 54c05a9c38346..eb4395154e41c 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -193,6 +193,20 @@ namespace ts { } } + /** True if the maps have the same keys and values. */ + function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: T, right: T) => 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; + } + function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map): void { checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule); } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index bec3a70087909..9beefe480df71 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -243,18 +243,12 @@ namespace ts.projectSystem { } export function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { - assert.equal(mapSize(map), expectedKeys.length, `${caption}: incorrect size of map`); + assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${keysOfMap(map)}`); } } - function mapSize(map: Map): number { - let size = 0; - map.forEach(() => { size++; }); - return size; - } - export function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); for (const f of expectedFileNames) { @@ -313,7 +307,7 @@ namespace ts.projectSystem { } count() { - return mapSize(this.map); + return this.map.size; } invoke() { From 8121de74d47b6ea70a5b64375967a15727f95c90 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 8 Dec 2016 08:33:58 -0800 Subject: [PATCH 04/20] Fix target error for gulp --- Gulpfile.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/Gulpfile.ts b/Gulpfile.ts index 054e99c80037e..7072becc2dad1 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -330,6 +330,7 @@ const builtGeneratedDiagnosticMessagesJSON = path.join(builtLocalDirectory, "dia // processDiagnosticMessages script gulp.task(processDiagnosticMessagesJs, false, [], () => { const settings: tsc.Settings = getCompilerSettings({ + target: "es5", declaration: false, removeComments: true, noResolve: false, From 8dbb8e7bf6ac861515670a86c23da6ae9275eda2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 8 Dec 2016 08:34:56 -0800 Subject: [PATCH 05/20] Add comment --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 75e1e82e7f6ee..ca2295e5ba2c7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -11,7 +11,7 @@ namespace ts { /** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */ export type MapKey = string | number; - /** Minimal ES6 Map interface. */ + /** Minimal ES6 Map interface. Does not include iterators as those are hard to shim performantly. */ export interface Map { get(key: MapKey): T; has(key: MapKey): boolean; From 5c304d0d0f3d4e08d0bd11ea48a479cbc422c417 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 12 Dec 2016 08:15:55 -0800 Subject: [PATCH 06/20] Remove `createObject`; use `Object.create` directly. --- src/compiler/core.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index fd9eedeeece13..4ed20bcdecfec 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -26,10 +26,9 @@ namespace ts { // 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; - const createObject = Object.create; /** Create a MapLike with good performance. Prefer this over a literal `{}`. */ export function createMapLike(): MapLike { - const map = createObject(null); // tslint:disable-line:no-null-keyword + const map = Object.create(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 From b53b5cf4ab8f60090cc6ad764d9cfae718d3a50e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 12 Dec 2016 08:42:12 -0800 Subject: [PATCH 07/20] Remove the "set" function and use `map.set` with multiple lines of code if necessary. --- src/compiler/binder.ts | 7 ++- src/compiler/checker.ts | 52 ++++++++++++++++------ src/compiler/core.ts | 15 ++----- src/compiler/emitter.ts | 3 +- src/compiler/parser.ts | 6 ++- src/compiler/program.ts | 10 +++-- src/compiler/transformers/module/module.ts | 3 +- src/compiler/transformers/module/system.ts | 6 ++- src/compiler/tsc.ts | 7 ++- src/compiler/utilities.ts | 4 +- src/harness/harness.ts | 8 ++-- src/server/client.ts | 3 +- src/services/patternMatcher.ts | 6 ++- src/services/services.ts | 6 ++- 14 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 18a6d00e922dd..046b67ccd1c96 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -349,7 +349,10 @@ 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 = symbolTable.get(name) || set(symbolTable, name, createSymbol(SymbolFlags.None, name)); + symbol = symbolTable.get(name); + if (!symbol) { + symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); + } if (name && (includes & SymbolFlags.Classifiable)) { classifiableNames.set(name, name); @@ -359,7 +362,7 @@ namespace ts { if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. - symbol = set(symbolTable, name, createSymbol(SymbolFlags.None, name)); + symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); } else { if (node.name) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6bbbab8822ea1..2394d5017a32f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4062,7 +4062,8 @@ namespace ts { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); if (!memberTypes.has(value)) { - const memberType = set(memberTypes, value, createEnumLiteralType(memberSymbol, enumType, "" + value)); + const memberType = createEnumLiteralType(memberSymbol, enumType, "" + value); + memberTypes.set(value, memberType); memberTypeList.push(memberType); } } @@ -5192,7 +5193,11 @@ namespace ts { function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { const instantiations = signature.instantiations || (signature.instantiations = createMap()); const id = getTypeListId(typeArguments); - return instantiations.get(id) || set(instantiations, id, createSignatureInstantiation(signature, typeArguments)); + let instantiation = instantiations.get(id); + if (!instantiation) { + instantiations.set(id, instantiation = createSignatureInstantiation(signature, typeArguments)); + } + return instantiation; } function createSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature { @@ -5348,7 +5353,8 @@ namespace ts { const id = getTypeListId(typeArguments); let type = target.instantiations.get(id); if (!type) { - type = set(target.instantiations, id, createObjectType(ObjectFlags.Reference, target.symbol)); + type = createObjectType(ObjectFlags.Reference, target.symbol); + target.instantiations.set(id, type); type.flags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; type.target = target; type.typeArguments = typeArguments; @@ -5395,7 +5401,11 @@ namespace ts { const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters; const id = getTypeListId(typeArguments); - return links.instantiations.get(id) || set(links.instantiations, id, instantiateTypeNoAlias(type, createTypeMapper(typeParameters, typeArguments))); + let instantiation = links.instantiations.get(id); + if (!instantiation) { + links.instantiations.set(id, instantiation = instantiateTypeNoAlias(type, createTypeMapper(typeParameters, typeArguments))); + } + return instantiation; } // Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include @@ -5844,7 +5854,8 @@ namespace ts { let type = unionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = set(unionTypes, id, createType(TypeFlags.Union | propagatedFlags)); + type = createType(TypeFlags.Union | propagatedFlags); + unionTypes.set(id, type); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5918,7 +5929,8 @@ namespace ts { let type = intersectionTypes.get(id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = set(intersectionTypes, id, createType(TypeFlags.Intersection | propagatedFlags)); + type = createType(TypeFlags.Intersection | propagatedFlags); + intersectionTypes.set(id, type); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -6100,7 +6112,11 @@ namespace ts { } // Otherwise we defer the operation by creating an indexed access type. const id = objectType.id + "," + indexType.id; - return indexedAccessTypes.get(id) || set(indexedAccessTypes, id, createIndexedAccessType(objectType, indexType)); + let type = indexedAccessTypes.get(id); + if (!type) { + indexedAccessTypes.set(id, type = createIndexedAccessType(objectType, indexType)); + } + return type; } // In the following we resolve T[K] to the type of the property in T selected by K. const apparentObjectType = getApparentType(objectType); @@ -6268,7 +6284,11 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return map.get(text) || set(map, text, createLiteralType(flags, text)); + let type = map.get(text); + if (!type) { + map.set(text, type = createLiteralType(flags, text)); + } + return type; } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -7060,7 +7080,8 @@ namespace ts { 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 set(enumRelation, id, false); + enumRelation.set(id, false); + return false; } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -7071,11 +7092,13 @@ namespace ts { errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name, typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } - return set(enumRelation, id, false); + enumRelation.set(id, false); + return false; } } } - return set(enumRelation, id, true); + enumRelation.set(id, true); + return true; } function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { @@ -9815,7 +9838,8 @@ namespace ts { if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } - return set(cache, key, result); + cache.set(key, result); + return result; } function isMatchingReferenceDiscriminant(expr: Expression) { @@ -11771,9 +11795,9 @@ namespace ts { } function getJsxType(name: string) { - const jsxType = jsxTypes.get(name); + let jsxType = jsxTypes.get(name); if (jsxType === undefined) { - return set(jsxTypes, name, getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); + jsxTypes.set(name, jsxType = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); } return jsxType; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 4ed20bcdecfec..6239dc93146f4 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -156,15 +156,6 @@ namespace ts { } } - /** - * Unlike `map.set(key, value)`, this returns the value, making it useful as an expression. - * Prefer `map.set(key, value)` for statements. - */ - export function set(map: Map, key: MapKey, value: T): T { - map.set(key, value); - return value; - } - export function createFileMap(keyMapper?: (key: string) => string): FileMap { const files = createMap(); return { @@ -1050,14 +1041,14 @@ namespace ts { * Creates the array if it does not already exist. */ export function multiMapAdd(map: Map, key: string | number, value: V): V[] { - const values = map.get(key); + let values = map.get(key); if (values) { values.push(value); - return values; } else { - return set(map, key, [value]); + map.set(key, values = [value]); } + return values; } /** diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 191a39a57970e..00144f2d00b4c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2545,7 +2545,8 @@ namespace ts { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - return set(generatedNameSet, generatedName, generatedName); + generatedNameSet.set(generatedName, generatedName); + return generatedName; } i++; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3da9093ee5a5e..bb53eeba0e0fc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1131,7 +1131,11 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return identifiers.get(text) || set(identifiers, text, text); + let identifier = identifiers.get(text); + if (identifier === undefined) { + identifiers.set(text, identifier = text); + } + return identifier; } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 3da9f57ca3903..ab04a21ee692b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -277,9 +277,13 @@ namespace ts { const resolutions: T[] = []; const cache = createMap(); for (const name of names) { - const result = cache.has(name) - ? cache.get(name) - : set(cache, name, loader(name, containingFile)); + let result: T; + if (cache.has(name)) { + result = cache.get(name); + } + else { + cache.set(name, result = loader(name, containingFile)); + } resolutions.push(result); } return resolutions; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 2e926406fb4e2..5d05667e4c2dd 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -60,7 +60,8 @@ namespace ts { } currentSourceFile = node; - currentModuleInfo = set(moduleInfoMap, getOriginalNodeId(node), collectExternalModuleInfo(node, resolver, compilerOptions)); + currentModuleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); + moduleInfoMap.set(getOriginalNodeId(node), currentModuleInfo); // Perform the transformation. const transformModule = transformModuleDelegates.get(moduleKind) || transformModuleDelegates.get(ModuleKind.None); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 8d1e1749c0b63..bc613bcfaf675 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -73,11 +73,13 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = set(moduleInfoMap, id, collectExternalModuleInfo(node, resolver, compilerOptions)); + moduleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); + moduleInfoMap.set(id, moduleInfo); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. - exportFunction = set(exportFunctionsMap, id, createUniqueName("exports")); + exportFunction = createUniqueName("exports"); + exportFunctionsMap.set(id, exportFunction); contextObject = createUniqueName("context"); // Add the body of the module. diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 0e66ccb14b86c..5de16a20fc2ec 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -378,8 +378,11 @@ namespace ts { } function cachedFileExists(fileName: string): boolean { - const fileExists = cachedExistingFiles.get(fileName); - return fileExists !== undefined ? fileExists : set(cachedExistingFiles, fileName, hostFileExists(fileName)); + let fileExists = cachedExistingFiles.get(fileName); + if (fileExists === undefined) { + cachedExistingFiles.set(fileName, fileExists = hostFileExists(fileName)); + } + return fileExists; } function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 94b0fa2098796..25973cae5cbc1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3323,7 +3323,9 @@ namespace ts { for (const name in syntaxKindEnum) { if (syntaxKindEnum[name] === kind) { - return set(syntaxKindCache, kind, kind.toString() + " (" + name + ")"); + const result = `${kind} (${name})`; + syntaxKindCache.set(kind, result); + return result; } } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 265fe4c142c81..c94a9360745a3 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -918,9 +918,11 @@ namespace Harness { return undefined; } - const sourceFile = libFileNameSourceFileMap.get(fileName); - return sourceFile || ts.set(libFileNameSourceFileMap, fileName, - createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest)); + let sourceFile = libFileNameSourceFileMap.get(fileName); + if (!sourceFile) { + libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest)); + } + return sourceFile; } export function getDefaultLibFileName(options: ts.CompilerOptions): string { diff --git a/src/server/client.ts b/src/server/client.ts index 13d16073d1c41..d9e93769bd9ea 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -34,7 +34,8 @@ namespace ts.server { let lineMap = this.lineMaps.get(fileName); if (!lineMap) { const scriptSnapshot = this.host.getScriptSnapshot(fileName); - lineMap = set(this.lineMaps, fileName, ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()))); + lineMap = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())); + this.lineMaps.set(fileName, lineMap); } return lineMap; } diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index b8941ef3147ac..940290a2f94af 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -188,7 +188,11 @@ namespace ts { } function getWordSpans(word: string): TextSpan[] { - return stringToWordSpans.get(word) || set(stringToWordSpans, word, breakIntoWordSpans(word)); + let spans = stringToWordSpans.get(word); + if (!spans) { + stringToWordSpans.set(word, spans = breakIntoWordSpans(word)); + } + return spans; } function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch { diff --git a/src/services/services.ts b/src/services/services.ts index 87ae9fb8561fe..6d0d384bcdda3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -532,7 +532,11 @@ namespace ts { } function getDeclarations(name: string) { - return result.get(name) || set(result, name, []); + let declarations = result.get(name); + if (!declarations) { + result.set(name, declarations = []); + } + return declarations; } function getDeclarationName(declaration: Declaration) { From 8386d491abacdf2f43aacace0efcaa37b27b748d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 27 Dec 2016 11:17:33 -0800 Subject: [PATCH 08/20] Use sparse arrays for number-keyed maps --- src/compiler/checker.ts | 9 +- src/compiler/core.ts | 36 +++--- src/compiler/factory.ts | 30 ++--- src/compiler/sourcemap.ts | 2 +- src/compiler/transformer.ts | 22 ++-- src/compiler/transformers/generators.ts | 28 ++--- src/compiler/transformers/module/module.ts | 55 ++++----- src/compiler/transformers/module/system.ts | 53 +++++---- src/compiler/transformers/ts.ts | 14 +-- src/compiler/tsc.ts | 16 +-- src/compiler/types.ts | 22 ++-- src/compiler/utilities.ts | 6 +- src/compiler/visitor.ts | 104 +++++++++--------- src/harness/unittests/session.ts | 10 +- .../unittests/tsserverProjectSystem.ts | 22 ++-- src/services/codefixes/codeFixProvider.ts | 10 +- src/services/codefixes/importFixes.ts | 22 ++-- 17 files changed, 240 insertions(+), 221 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2394d5017a32f..47a7db169862d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4054,16 +4054,15 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes = createMap(); + const memberTypes = sparseArray(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); for (const member of (declaration).members) { const memberSymbol = getSymbolOfNode(member); const value = getEnumMemberValue(member); - if (!memberTypes.has(value)) { - const memberType = createEnumLiteralType(memberSymbol, enumType, "" + value); - memberTypes.set(value, memberType); + if (!memberTypes[value]) { + const memberType = memberTypes[value] = createEnumLiteralType(memberSymbol, enumType, "" + value); memberTypeList.push(memberType); } } @@ -4085,7 +4084,7 @@ namespace ts { if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)); links.declaredType = enumType.flags & TypeFlags.Union ? - enumType.memberTypes.get(getEnumMemberValue(symbol.valueDeclaration)) : + enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] : enumType; } return links.declaredType; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6239dc93146f4..36847796b6ef5 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -39,6 +39,8 @@ namespace ts { return map; } + export const sparseArray: () => SparseArray = createMapLike; + /** Create a new map. If a template object is provided, the map will copy entries from it. */ export function createMap(template?: MapLike): Map { const map: Map = new MapCtr(); @@ -52,17 +54,6 @@ namespace ts { return map; } - /** Create a map from [key, value] pairs. This avoids casting keys to strings. */ - export function createMapFromPairs(...pairs: [number, T][]): Map { - const map: Map = new MapCtr(); - - for (const [key, value] of pairs) { - map.set(key, value); - } - - return map; - } - /** Methods on native maps but not on shim maps. Only used in this file. */ interface ES6Map extends Map { entries(): Iterator<[string, T]>; @@ -92,21 +83,21 @@ namespace ts { return class implements ShimMap { private data = createMapLike(); - get(key: MapKey): T { + get(key: string): T { return this.data[key]; } - set(key: MapKey, value: T): this { + set(key: string, value: T): this { this.data[key] = value; return this; } - has(key: MapKey): boolean { + has(key: string): boolean { // tslint:disable-next-line:no-in-operator return key in this.data; } - delete(key: MapKey): boolean { + delete(key: string): boolean { const had = this.has(key); if (had) { delete this.data[key]; @@ -890,7 +881,7 @@ namespace ts { * Array of every key in a map. * May not actually return string[] if numbers were put into the map. */ - export function keysOfMap(map: Map): string[] { + export function keysOfMap(map: Map<{}>): string[] { const keys: string[] = []; forEachKeyInMap(map, key => { keys.push(key); @@ -1040,7 +1031,7 @@ namespace ts { * 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[] { + export function multiMapAdd(map: Map, key: string, value: V): V[] { let values = map.get(key); if (values) { values.push(value); @@ -1051,6 +1042,17 @@ namespace ts { return values; } + export function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { + let values = map[key]; + if (values) { + values.push(value); + } + else { + map[key] = values = [value]; + } + return values; + } + /** * Removes a value from an array of values associated with the key. * Does not preserve the order of those values. diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2ef73dea727a0..15a946648b1cd 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1,4 +1,4 @@ -/// +/// /// /* @internal */ @@ -2641,9 +2641,11 @@ namespace ts { return destEmitNode; } - function mergeTokenSourceMapRanges(sourceRanges: Map, destRanges: Map) { - if (!destRanges) destRanges = createMap(); - copyMapEntries(sourceRanges, destRanges); + function mergeTokenSourceMapRanges(sourceRanges: SparseArray, destRanges: SparseArray) { + if (!destRanges) destRanges = []; + for (const key in sourceRanges) { + destRanges[key] = sourceRanges[key]; + } return destRanges; } @@ -2745,7 +2747,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]; } /** @@ -2757,8 +2759,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 = []); + tokenSourceMapRanges[token] = range; return node; } @@ -3270,7 +3272,7 @@ namespace ts { externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: Map; // export specifiers by name - exportedBindings: Map; // exported names of local declarations + exportedBindings: SparseArray; // exported names of local declarations exportedNames: Identifier[]; // all exported names local to module exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* @@ -3279,7 +3281,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMap(); - const exportedBindings = createMap(); + const exportedBindings = sparseArray(); const uniqueExports = createMap(); let exportedNames: Identifier[]; let hasExportDefault = false; @@ -3338,7 +3340,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (decl) { - multiMapAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); } uniqueExports.set(specifier.name.text, true); @@ -3368,7 +3370,7 @@ namespace ts { if (hasModifier(node, ModifierFlags.Default)) { // export default function() { } if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); hasExportDefault = true; } } @@ -3376,7 +3378,7 @@ namespace ts { // export function x() { } const name = (node).name; if (!uniqueExports.get(name.text)) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } @@ -3389,7 +3391,7 @@ namespace ts { if (hasModifier(node, ModifierFlags.Default)) { // export default class { } if (!hasExportDefault) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), getDeclarationName(node)); hasExportDefault = true; } } @@ -3397,7 +3399,7 @@ namespace ts { // export class x { } const name = (node).name; if (!uniqueExports.get(name.text)) { - multiMapAdd(exportedBindings, getOriginalNodeId(node), name); + multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); uniqueExports.set(name.text, true); exportedNames = append(exportedNames, name); } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index a814a91c355ca..650b9b0ef02b2 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/transformer.ts b/src/compiler/transformer.ts index 1440481d38eea..cef8c701455a6 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -1,4 +1,4 @@ -/// +/// /// /// /// @@ -13,14 +13,16 @@ /* @internal */ namespace ts { - const moduleTransformerMap = createMapFromPairs( - [ModuleKind.ES2015, transformES2015Module], - [ModuleKind.System, transformSystemModule], - [ModuleKind.AMD, transformModule], - [ModuleKind.CommonJS, transformModule], - [ModuleKind.UMD, transformModule], - [ModuleKind.None, transformModule], - ); + function getModuleTransformer(moduleKind: ModuleKind): Transformer { + switch (moduleKind) { + case ModuleKind.ES2015: + return transformES2015Module; + case ModuleKind.System: + return transformSystemModule; + default: + return transformModule; + } + } const enum SyntaxKindFeatureFlags { Substitution = 1 << 0, @@ -56,7 +58,7 @@ namespace ts { transformers.push(transformGenerators); } - transformers.push(moduleTransformerMap.get(moduleKind) || moduleTransformerMap.get(ModuleKind.None)); + transformers.push(getModuleTransformer(moduleKind)); // The ES5 transformer is last so that it can substitute expressions like `exports.default` // for ES3. diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 0b853649e81d3..85aba96387b8b 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1,4 +1,4 @@ -/// +/// /// // Transforms generator functions into a compatible ES5 representation with similar runtime @@ -217,13 +217,15 @@ namespace ts { Endfinally = 7, } - const instructionNames = createMapFromPairs( - [Instruction.Return, "return"], - [Instruction.Break, "break"], - [Instruction.Yield, "yield"], - [Instruction.YieldStar, "yield*"], - [Instruction.Endfinally, "endfinally"], - ); + function getInstructionName(instruction: Instruction): string { + switch (instruction) { + case Instruction.Return: return "return"; + case Instruction.Break: return "break"; + case Instruction.Yield: return "yield"; + case Instruction.YieldStar: return "yield*"; + case Instruction.Endfinally: return "endfinally"; + } + } export function transformGenerators(context: TransformationContext) { const { @@ -241,7 +243,7 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; - let renamedCatchVariableDeclarations: Map; + let renamedCatchVariableDeclarations: SparseArray; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; @@ -1926,7 +1928,7 @@ namespace ts { if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); if (declaration) { - const name = renamedCatchVariableDeclarations.get(getOriginalNodeId(declaration)); + const name = renamedCatchVariableDeclarations[getOriginalNodeId(declaration)]; if (name) { const clone = getMutableClone(name); setSourceMapRange(clone, node); @@ -2092,12 +2094,12 @@ namespace ts { if (!renamedCatchVariables) { renamedCatchVariables = createMap(); - renamedCatchVariableDeclarations = createMap(); + renamedCatchVariableDeclarations = sparseArray(); context.enableSubstitution(SyntaxKind.Identifier); } renamedCatchVariables.set(text, true); - renamedCatchVariableDeclarations.set(getOriginalNodeId(variable), name); + renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; const exception = peekBlock(); Debug.assert(exception.state < ExceptionBlockState.Catch); @@ -2401,7 +2403,7 @@ namespace ts { */ function createInstruction(instruction: Instruction): NumericLiteral { const literal = createLiteral(instruction); - literal.trailingComment = instructionNames.get(instruction); + literal.trailingComment = getInstructionName(instruction); return literal; } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 5d05667e4c2dd..e5e27c8360df2 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1,4 +1,4 @@ -/// +/// /// /*@internal*/ @@ -10,12 +10,13 @@ namespace ts { importAliasNames: ParameterDeclaration[]; } - const transformModuleDelegates = createMapFromPairs<(node: SourceFile) => SourceFile>( - [ModuleKind.None, transformCommonJSModule], - [ModuleKind.CommonJS, transformCommonJSModule], - [ModuleKind.AMD, transformAMDModule], - [ModuleKind.UMD, transformUMDModule], - ); + function getTransformModuleDelegate(moduleKind: ModuleKind): (node: SourceFile) => SourceFile { + switch (moduleKind) { + case ModuleKind.AMD: return transformAMDModule; + case ModuleKind.UMD: return transformUMDModule; + default: return transformCommonJSModule; + } + } const { startLexicalEnvironment, @@ -38,12 +39,12 @@ namespace ts { context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = createMap(); // The ExternalModuleInfo for each file. - const deferredExports = createMap(); // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. + const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. - let noSubstitution: Map; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; @@ -61,10 +62,10 @@ namespace ts { currentSourceFile = node; currentModuleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); - moduleInfoMap.set(getOriginalNodeId(node), currentModuleInfo); + moduleInfoMap[getOriginalNodeId(node)] = currentModuleInfo; // Perform the transformation. - const transformModule = transformModuleDelegates.get(moduleKind) || transformModuleDelegates.get(ModuleKind.None); + const transformModule = getTransformModuleDelegate(moduleKind); const updated = transformModule(node); currentSourceFile = undefined; @@ -445,7 +446,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -524,7 +525,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -611,7 +612,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true)); + deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); } else { statements = appendExportStatement(statements, createIdentifier("default"), node.expression, /*location*/ node, /*allowComments*/ true); @@ -652,7 +653,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -691,7 +692,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -742,7 +743,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node); } else { statements = appendExportsOfVariableStatement(statements, node); @@ -795,7 +796,7 @@ namespace ts { // statement. if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original); } return node; @@ -821,9 +822,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports.get(id); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(id); + delete deferredExports[id]; return append(statements, node); } @@ -1103,8 +1104,8 @@ namespace ts { function onEmitNode(emitContext: EmitContext, node: Node, emitCallback: (emitContext: EmitContext, node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node; - currentModuleInfo = moduleInfoMap.get(getOriginalNodeId(currentSourceFile)); - noSubstitution = createMap(); + currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; + noSubstitution = sparseArray(); previousOnEmitNode(emitContext, node, emitCallback); @@ -1129,7 +1130,7 @@ namespace ts { */ function onSubstituteNode(emitContext: EmitContext, node: Node) { node = previousOnSubstituteNode(emitContext, node); - if (node.id && noSubstitution.get(node.id)) { + if (node.id && noSubstitution[node.id]) { return node; } @@ -1255,7 +1256,7 @@ namespace ts { let expression: Expression = node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.set(getNodeId(expression), true); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression, /*location*/ node); } @@ -1297,7 +1298,7 @@ namespace ts { : node; for (const exportName of exportedNames) { // Mark the node to prevent triggering this rule again. - noSubstitution.set(getNodeId(expression), true); + noSubstitution[getNodeId(expression)] = true; expression = createExportExpression(exportName, expression); } @@ -1319,7 +1320,7 @@ namespace ts { || resolver.getReferencedValueDeclaration(name); if (valueDeclaration) { return currentModuleInfo - && currentModuleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration)); + && currentModuleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]; } } } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index bc613bcfaf675..c20e6b5cf32e8 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1,4 +1,4 @@ -/// +/// /// /*@internal*/ @@ -28,10 +28,10 @@ namespace ts { context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = createMap(); // The ExternalModuleInfo for each file. - const deferredExports = createMap(); // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap = createMap(); // The export function associated with a source file. - const noSubstitutionMap = createMap>(); // Set of nodes for which substitution rules should be ignored for each file. + const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. + const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap = sparseArray(); // The export function associated with a source file. + const noSubstitutionMap = sparseArray>(); // Set of nodes for which substitution rules should be ignored for each file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -39,7 +39,7 @@ namespace ts { let contextObject: Identifier; // The context object for the current file. let hoistedStatements: Statement[]; let enclosingBlockScopedContainer: Node; - let noSubstitution: Map; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; @@ -73,13 +73,12 @@ namespace ts { // see comment to 'substitutePostfixUnaryExpression' for more details // Collect information about the external module and dependency groups. - moduleInfo = collectExternalModuleInfo(node, resolver, compilerOptions); - moduleInfoMap.set(id, moduleInfo); + moduleInfo = moduleInfoMap[id] = collectExternalModuleInfo(node, resolver, compilerOptions); // Make sure that the name of the 'exports' function does not conflict with // existing identifiers. exportFunction = createUniqueName("exports"); - exportFunctionsMap.set(id, exportFunction); + exportFunctionsMap[id] = exportFunction; contextObject = createUniqueName("context"); // Add the body of the module. @@ -123,7 +122,7 @@ namespace ts { } if (noSubstitution) { - noSubstitutionMap.set(id, noSubstitution); + noSubstitutionMap[id] = noSubstitution; noSubstitution = undefined; } @@ -610,7 +609,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportDeclaration(statements, node); @@ -633,7 +632,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfImportEqualsDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfImportEqualsDeclaration(deferredExports[id], node); } else { statements = appendExportsOfImportEqualsDeclaration(statements, node); @@ -658,7 +657,7 @@ namespace ts { if (original && hasAssociatedEndOfDeclarationMarker(original)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportStatement(deferredExports.get(id), createIdentifier("default"), expression, /*allowComments*/ true)); + deferredExports[id] = appendExportStatement(deferredExports[id], createIdentifier("default"), expression, /*allowComments*/ true); } else { return createExportStatement(createIdentifier("default"), expression, /*allowComments*/ true); @@ -690,7 +689,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { hoistedStatements = appendExportsOfHoistedDeclaration(hoistedStatements, node); @@ -732,7 +731,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node)) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfHoistedDeclaration(deferredExports.get(id), node)); + deferredExports[id] = appendExportsOfHoistedDeclaration(deferredExports[id], node); } else { statements = appendExportsOfHoistedDeclaration(statements, node); @@ -772,7 +771,7 @@ namespace ts { if (isMarkedDeclaration) { // Defer exports until we encounter an EndOfDeclarationMarker node const id = getOriginalNodeId(node); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node, isExportedDeclaration)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node, isExportedDeclaration); } else { statements = appendExportsOfVariableStatement(statements, node, /*exportSelf*/ false); @@ -885,7 +884,7 @@ namespace ts { if (hasAssociatedEndOfDeclarationMarker(node) && node.original.kind === SyntaxKind.VariableStatement) { const id = getOriginalNodeId(node); const isExportedDeclaration = hasModifier(node.original, ModifierFlags.Export); - deferredExports.set(id, appendExportsOfVariableStatement(deferredExports.get(id), node.original, isExportedDeclaration)); + deferredExports[id] = appendExportsOfVariableStatement(deferredExports[id], node.original, isExportedDeclaration); } return node; @@ -911,9 +910,9 @@ namespace ts { // end of the transformed declaration. We use this marker to emit any deferred exports // of the declaration. const id = getOriginalNodeId(node); - const statements = deferredExports.get(id); + const statements = deferredExports[id]; if (statements) { - deferredExports.delete(id); + delete deferredExports[id]; return append(statements, node); } @@ -1556,12 +1555,12 @@ namespace ts { if (node.kind === SyntaxKind.SourceFile) { const id = getOriginalNodeId(node); currentSourceFile = node; - moduleInfo = moduleInfoMap.get(id); - exportFunction = exportFunctionsMap.get(id); - noSubstitution = noSubstitutionMap.get(id); + moduleInfo = moduleInfoMap[id]; + exportFunction = exportFunctionsMap[id]; + noSubstitution = noSubstitutionMap[id]; if (noSubstitution) { - noSubstitutionMap.delete(id); + delete noSubstitutionMap[id]; } previousOnEmitNode(emitContext, node, emitCallback); @@ -1758,7 +1757,7 @@ namespace ts { exportedNames = append(exportedNames, getDeclarationName(valueDeclaration)); } - exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings.get(getOriginalNodeId(valueDeclaration))); + exportedNames = addRange(exportedNames, moduleInfo && moduleInfo.exportedBindings[getOriginalNodeId(valueDeclaration)]); } } @@ -1771,8 +1770,8 @@ namespace ts { * @param node The node which should not be substituted. */ function preventSubstitution(node: T): T { - if (noSubstitution === undefined) noSubstitution = createMap(); - noSubstitution.set(getNodeId(node), true); + if (noSubstitution === undefined) noSubstitution = sparseArray(); + noSubstitution[getNodeId(node)] = true; return node; } @@ -1782,7 +1781,7 @@ namespace ts { * @param node The node to test. */ function isSubstitutionPrevented(node: Node) { - return noSubstitution && node.id && noSubstitution.get(node.id); + return noSubstitution && node.id && noSubstitution[node.id]; } } } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 4b0f981f27ce3..ef7001f3735a4 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -1,4 +1,4 @@ -/// +/// /// /// @@ -60,7 +60,7 @@ namespace ts { * A map that keeps track of aliases created for classes with decorators to avoid issues * with the double-binding behavior of classes. */ - let classAliases: Map; + let classAliases: SparseArray; /** * Keeps track of whether we are within any containing namespaces when performing @@ -752,7 +752,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { // record an alias as the class name is not in scope for statics. enableSubstitutionForClassAliases(); - classAliases.set(getOriginalNodeId(node), getSynthesizedClone(temp)); + classAliases[getOriginalNodeId(node)] = getSynthesizedClone(temp); } // To preserve the behavior of the old emitter, we explicitly indent @@ -1420,7 +1420,7 @@ namespace ts { return undefined; } - const classAlias = classAliases && classAliases.get(getOriginalNodeId(node)); + const classAlias = classAliases && classAliases[getOriginalNodeId(node)]; const localName = getLocalName(node, /*allowComments*/ false, /*allowSourceMaps*/ true); const decorate = createDecorateHelper(context, decoratorExpressions, localName); const expression = createAssignment(localName, classAlias ? createAssignment(classAlias, decorate) : decorate); @@ -3085,7 +3085,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); - classAliases.set(getOriginalNodeId(node), classAlias); + classAliases[getOriginalNodeId(node)] = classAlias; hoistVariableDeclaration(classAlias); return classAlias; } @@ -3117,7 +3117,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = createMap(); + classAliases = sparseArray(); } } @@ -3230,7 +3230,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases.get(declaration.id); + const classAlias = classAliases[declaration.id]; if (classAlias) { const clone = getSynthesizedClone(classAlias); setSourceMapRange(clone, node); diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 5de16a20fc2ec..ba5a0d8faea90 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts { @@ -67,11 +67,13 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap = createMapFromPairs( - [DiagnosticCategory.Warning, yellowForegroundEscapeSequence], - [DiagnosticCategory.Error, redForegroundEscapeSequence], - [DiagnosticCategory.Message, blueForegroundEscapeSequence], - ); + function getCategoryFormat(category: DiagnosticCategory): string { + switch (category) { + case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence; + case DiagnosticCategory.Error: return redForegroundEscapeSequence; + case DiagnosticCategory.Message: return blueForegroundEscapeSequence; + } + } function formatAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; @@ -139,7 +141,7 @@ namespace ts { output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; } - const categoryColor = categoryFormatMap.get(diagnostic.category); + const categoryColor = getCategoryFormat(diagnostic.category); const category = DiagnosticCategory[diagnostic.category].toLowerCase(); output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`; output += sys.newLine + sys.newLine; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b98a5a493ba98..75ff806d605af 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,4 +1,4 @@ -namespace ts { +namespace ts { /** * Type of objects whose values are all of the same type. * The `in` and `for-in` operators can *not* be safely used, @@ -8,15 +8,19 @@ namespace ts { [index: string]: T; } - /** It's allowed to get/set into a map with numbers. However, when iterating, you may get strings back due to the shim being an ordinary object (which only allows string keys). */ - export type MapKey = string | number; + /** + * Like MapLike, but keys must be numbers. + */ + export interface SparseArray { + [key: number]: T; + } /** Minimal ES6 Map interface. Does not include iterators as those are hard to shim performantly. */ export interface Map { - get(key: MapKey): T; - has(key: MapKey): boolean; - set(key: MapKey, value: T): this; - delete(key: MapKey): boolean; + get(key: string): T; + has(key: string): boolean; + set(key: string, value: T): this; + delete(key: string): boolean; clear(): void; /** `key` may *not* be a string if it was set with a number and we are not using the shim. */ forEach(action: (value: T, key: string) => void): void; @@ -2853,7 +2857,7 @@ namespace ts { // Enum types (TypeFlags.Enum) export interface EnumType extends Type { - memberTypes: Map; + memberTypes: SparseArray; } // Enum types (TypeFlags.EnumLiteral) @@ -3682,7 +3686,7 @@ namespace ts { flags?: EmitFlags; // Flags that customize emit commentRange?: TextRange; // The text range to use when emitting leading or trailing comments sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings - tokenSourceMapRanges?: Map; // The text range to use when emitting source mappings for tokens + tokenSourceMapRanges?: SparseArray; // The text range to use when emitting source mappings for tokens constantValue?: number; // The constant value of an expression externalHelpersModuleName?: Identifier; // The local name for an imported helpers module helpers?: EmitHelper[]; // Emit helpers for the node diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 25973cae5cbc1..ead1a3b62de3c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3311,12 +3311,12 @@ namespace ts { return false; } - const syntaxKindCache = createMap(); + const syntaxKindCache = sparseArray(); export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; if (syntaxKindEnum) { - const cached = syntaxKindCache.get(kind); + const cached = syntaxKindCache[kind]; if (cached !== undefined) { return cached; } @@ -3324,7 +3324,7 @@ namespace ts { for (const name in syntaxKindEnum) { if (syntaxKindEnum[name] === kind) { const result = `${kind} (${name})`; - syntaxKindCache.set(kind, result); + syntaxKindCache[kind] = result; return result; } } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index c9ea2af1a8782..2afb1f8301e18 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -1,4 +1,4 @@ -/// +/// /// /// @@ -46,54 +46,56 @@ namespace ts { * supplant the existing `forEachChild` implementation if performance is not * significantly impacted. */ - const nodeEdgeTraversalMap = createMapFromPairs( - [SyntaxKind.QualifiedName, [ - { name: "left", test: isEntityName }, - { name: "right", test: isIdentifier } - ]], - [SyntaxKind.Decorator, [ - { name: "expression", test: isLeftHandSideExpression } - ]], - [SyntaxKind.TypeAssertionExpression, [ - { name: "type", test: isTypeNode }, - { name: "expression", test: isUnaryExpression } - ]], - [SyntaxKind.AsExpression, [ - { name: "expression", test: isExpression }, - { name: "type", test: isTypeNode } - ]], - [SyntaxKind.NonNullExpression, [ - { name: "expression", test: isLeftHandSideExpression } - ]], - [SyntaxKind.EnumDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier }, - { name: "members", test: isEnumMember } - ]], - [SyntaxKind.ModuleDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isModuleName }, - { name: "body", test: isModuleBody } - ]], - [SyntaxKind.ModuleBlock, [ - { name: "statements", test: isStatement } - ]], - [SyntaxKind.ImportEqualsDeclaration, [ - { name: "decorators", test: isDecorator }, - { name: "modifiers", test: isModifier }, - { name: "name", test: isIdentifier }, - { name: "moduleReference", test: isModuleReference } - ]], - [SyntaxKind.ExternalModuleReference, [ - { name: "expression", test: isExpression, optional: true } - ]], - [SyntaxKind.EnumMember, [ - { name: "name", test: isPropertyName }, - { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } - ]] - ); + function getNodeEdgeTraversal(kind: SyntaxKind): NodeTraversalPath { + switch (kind) { + case SyntaxKind.QualifiedName: return [ + { name: "left", test: isEntityName }, + { name: "right", test: isIdentifier } + ]; + case SyntaxKind.Decorator: return [ + { name: "expression", test: isLeftHandSideExpression } + ]; + case SyntaxKind.TypeAssertionExpression: return [ + { name: "type", test: isTypeNode }, + { name: "expression", test: isUnaryExpression } + ]; + case SyntaxKind.AsExpression: return [ + { name: "expression", test: isExpression }, + { name: "type", test: isTypeNode } + ]; + case SyntaxKind.NonNullExpression: return [ + { name: "expression", test: isLeftHandSideExpression } + ]; + case SyntaxKind.EnumDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isIdentifier }, + { name: "members", test: isEnumMember } + ]; + case SyntaxKind.ModuleDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isModuleName }, + { name: "body", test: isModuleBody } + ]; + case SyntaxKind.ModuleBlock: return [ + { name: "statements", test: isStatement } + ]; + case SyntaxKind.ImportEqualsDeclaration: return [ + { name: "decorators", test: isDecorator }, + { name: "modifiers", test: isModifier }, + { name: "name", test: isIdentifier }, + { name: "moduleReference", test: isModuleReference } + ]; + case SyntaxKind.ExternalModuleReference: return [ + { name: "expression", test: isExpression, optional: true } + ]; + case SyntaxKind.EnumMember: return [ + { name: "name", test: isPropertyName }, + { name: "initializer", test: isExpression, optional: true, parenthesize: parenthesizeExpressionForList } + ]; + } + } function reduceNode(node: Node, f: (memo: T, node: Node) => T, initial: T) { return node ? f(initial, node) : initial; @@ -530,7 +532,7 @@ namespace ts { break; default: - const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); + const edgeTraversalPath = getNodeEdgeTraversal(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = (>node)[edge.name]; @@ -1188,7 +1190,7 @@ namespace ts { default: let updated: Node & MapLike; - const edgeTraversalPath = nodeEdgeTraversalMap.get(kind); + const edgeTraversalPath = getNodeEdgeTraversal(kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = >(>node)[edge.name]; diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index da24fe1606b3a..70cda27747b7e 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -1,4 +1,4 @@ -/// +/// const expect: typeof _chai.expect = _chai.expect; @@ -416,16 +416,16 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks = createMap<(resp: protocol.Response) => void>(); + private callbacks = sparseArray<(resp: protocol.Response) => void>(); private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { if (msg.type === "response") { const response = msg; - const handler = this.callbacks.get(response.request_seq); + const handler = this.callbacks[response.request_seq]; if (handler) { handler(response); - this.callbacks.delete(response.request_seq); + delete this.callbacks[response.request_seq]; } } else if (msg.type === "event") { @@ -460,7 +460,7 @@ namespace ts.server { command, arguments: args }); - this.callbacks.set(this.seq, callback); + this.callbacks[this.seq] = callback; } }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 5b4ba7d7223c5..ae213bd0a5091 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts.projectSystem { @@ -290,30 +290,34 @@ namespace ts.projectSystem { } export class Callbacks { - private map = createMap(); + private map = sparseArray(); private nextId = 1; register(cb: (...args: any[]) => void, args: any[]) { const timeoutId = this.nextId; this.nextId++; - this.map.set(timeoutId, cb.bind(undefined, ...args)); + this.map[timeoutId] = cb.bind(undefined, ...args); return timeoutId; } unregister(id: any) { if (typeof id === "number") { - this.map.delete(id); + delete this.map[id]; } } count() { - return this.map.size; + let n = 0; + for (const _ in this.map) { + n++; + } + return n; } invoke() { - this.map.forEach(cb => { - cb(); - }); - this.map.clear(); + for (const key in this.map) { + this.map[key](); + } + this.map = sparseArray(); } } diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index c6373fc1909e8..fe3e71f1e1c57 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -16,25 +16,25 @@ namespace ts { } export namespace codefix { - const codeFixes = createMap(); + const codeFixes = sparseArray(); export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { - let fixes = codeFixes.get(error); + let fixes = codeFixes[error]; if (!fixes) { fixes = []; - codeFixes.set(error, fixes); + codeFixes[error] = fixes; } fixes.push(action); }); } export function getSupportedErrorCodes() { - return keysOfMap(codeFixes); + return keysOfSparseArray(codeFixes); } export function getFixes(context: CodeFixContext): CodeAction[] { - const fixes = codeFixes.get(context.errorCode); + const fixes = codeFixes[context.errorCode]; let allActions: CodeAction[] = []; forEach(fixes, f => { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 0e7743e29fc3a..0df3c3134fe09 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -14,16 +14,16 @@ namespace ts.codefix { } class ImportCodeActionMap { - private symbolIdToActionMap = createMap(); + private symbolIdToActionMap = sparseArray(); addAction(symbolId: number, newAction: ImportCodeAction) { if (!newAction) { return; } - const actions = this.symbolIdToActionMap.get(symbolId); + const actions = this.symbolIdToActionMap[symbolId]; if (!actions) { - this.symbolIdToActionMap.set(symbolId, [newAction]); + this.symbolIdToActionMap[symbolId] = [newAction]; return; } @@ -33,7 +33,7 @@ namespace ts.codefix { } const updatedNewImports: ImportCodeAction[] = []; - for (const existingAction of this.symbolIdToActionMap.get(symbolId)) { + for (const existingAction of this.symbolIdToActionMap[symbolId]) { if (existingAction.kind === "CodeChange") { // only import actions should compare updatedNewImports.push(existingAction); @@ -63,7 +63,7 @@ namespace ts.codefix { } // if we reach here, it means the new one is better or equal to all of the existing ones. updatedNewImports.push(newAction); - this.symbolIdToActionMap.set(symbolId, updatedNewImports); + this.symbolIdToActionMap[symbolId] = updatedNewImports; } addActions(symbolId: number, newActions: ImportCodeAction[]) { @@ -74,9 +74,9 @@ namespace ts.codefix { getAllActions() { let result: ImportCodeAction[] = []; - this.symbolIdToActionMap.forEach(actions => { - result = concatenate(result, actions); - }); + for (const key in this.symbolIdToActionMap) { + result = concatenate(result, this.symbolIdToActionMap[key]) + } return result; } @@ -125,7 +125,7 @@ namespace ts.codefix { const symbolIdActionMap = new ImportCodeActionMap(); // this is a module id -> module import declaration map - const cachedImportDeclarations = createMap<(ImportDeclaration | ImportEqualsDeclaration)[]>(); + const cachedImportDeclarations = sparseArray<(ImportDeclaration | ImportEqualsDeclaration)[]>(); let cachedNewImportInsertPosition: number; const allPotentialModules = checker.getAmbientModules(); @@ -163,7 +163,7 @@ namespace ts.codefix { function getImportDeclarations(moduleSymbol: Symbol) { const moduleSymbolId = getUniqueSymbolId(moduleSymbol); - const cached = cachedImportDeclarations.get(moduleSymbolId); + const cached = cachedImportDeclarations[moduleSymbolId]; if (cached) { return cached; } @@ -175,7 +175,7 @@ namespace ts.codefix { existingDeclarations.push(getImportDeclaration(importModuleSpecifier)); } } - cachedImportDeclarations.set(moduleSymbolId, existingDeclarations); + cachedImportDeclarations[moduleSymbolId] = existingDeclarations; return existingDeclarations; function getImportDeclaration(moduleSpecifier: LiteralExpression) { From 55552585d0f8b060e4794826d833229344fa0b2e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 27 Dec 2016 12:37:42 -0800 Subject: [PATCH 09/20] Add iterators to Map interface, and shim iterators --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 167 ++++++++++----------- src/compiler/transformers/module/system.ts | 2 +- src/compiler/types.ts | 11 +- src/services/codefixes/codeFixProvider.ts | 2 +- src/services/completions.ts | 2 +- 6 files changed, 96 insertions(+), 90 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 47a7db169862d..4ecfdc4d2f49e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6161,7 +6161,7 @@ namespace ts { if (!links.resolvedType) { // Deferred resolution of members is handled by resolveObjectTypeMembers const aliasSymbol = getAliasSymbolForTypeNode(node); - if (mapIsEmpty(node.symbol.members) && !aliasSymbol) { + if (node.symbol.members.size === 0 && !aliasSymbol) { links.resolvedType = emptyTypeLiteralType; } else { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 36847796b6ef5..a676688749414 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -54,40 +54,48 @@ namespace ts { return map; } - /** Methods on native maps but not on shim maps. Only used in this file. */ - interface ES6Map extends Map { - entries(): Iterator<[string, T]>; - } - - /** ES6 Iterator type. */ - interface Iterator { - next(): { value: T, done: false } | { value: never, done: true }; - } - - /** Shim maps support certain methods directly. For native maps we use a function. */ - interface ShimMap extends Map { - isEmpty(): boolean; - forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined; - some(predicate: (value: T, key: string) => boolean): boolean; - } - // The global Map object. This may not be available, so we must test for it. declare const Map: { new(): Map } | undefined; // Internet Explorer's Map doesn't support iteration, so don't use it. // tslint:disable-next-line:no-in-operator - const usingNativeMaps = typeof Map !== "undefined" && "entries" in Map.prototype; - const MapCtr = usingNativeMaps ? Map : shimMap(); + const MapCtr = typeof Map !== "undefined" && "entries" in Map.prototype ? Map : shimMap(); // Keep the class inside a function so it doesn't get compiled if it's not used. function shimMap(): { new(): Map } { - return class implements ShimMap { + + class MapIterator { + private data: MapLike; + private keys: string[]; + private index = 0; + private selector: (data: MapLike, key: string) => U; + constructor(data: MapLike, selector: (data: MapLike, key: string) => U) { + this.data = data; + this.selector = selector; + this.keys = Object.keys(data); + } + + public next(): { value: U, done: false } | { value: never, done: true } { + const index = this.index; + if (index < this.keys.length) { + this.index++; + return { value: this.selector(this.data, this.keys[index]), done: false }; + } + return { value: undefined as never, done: true } + } + } + + return class implements Map { private data = createMapLike(); + public size = 0; get(key: string): T { return this.data[key]; } set(key: string, value: T): this { + if (!this.has(key)) { + this.size++; + } this.data[key] = value; return this; } @@ -98,51 +106,35 @@ namespace ts { } delete(key: string): boolean { - const had = this.has(key); - if (had) { + if (this.has(key)) { + this.size--; delete this.data[key]; + return true; } - return had; + return false; } clear(): void { this.data = createMapLike(); + this.size = 0; } - forEach(action: (value: T, key: string) => void): void { - for (const key in this.data) { - action(this.data[key], key); - } + keys() { + return new MapIterator(this.data, (_data, key) => key); } - isEmpty(): boolean { - return !this.some(() => true); - } - - get size(): number { - let size = 0; - for (const _ in this.data) { - size++; - } - return size; + values() { + return new MapIterator(this.data, (data, key) => data[key]); } - forEachInMap(callback: (value: T, key: string) => U | undefined): U | undefined { - for (const key in this.data) { - const result = callback(this.data[key], key); - if (result !== undefined) { - return result; - } - } + entries() { + return new MapIterator(this.data, (data, key) => [key, data[key]]); } - some(predicate: (value: T, key: string) => boolean): boolean { + forEach(action: (value: T, key: string) => void): void { for (const key in this.data) { - if (predicate(this.data[key], key)) { - return true; - } + action(this.data[key], key); } - return false; } } } @@ -877,65 +869,76 @@ namespace ts { return keys; } + function arrayFrom(iterator: Iterator): T[] { + const result: T[] = []; + for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { + result.push(value); + } + return result; + } + /** * Array of every key in a map. * May not actually return string[] if numbers were put into the map. */ export function keysOfMap(map: Map<{}>): string[] { - const keys: string[] = []; - forEachKeyInMap(map, key => { - keys.push(key); - }); - return keys; + return arrayFrom(map.keys()); } /** Array of every value in a map. */ export function valuesOfMap(map: Map): T[] { - const values: T[] = []; - map.forEach(value => { - values.push(value); - }); - return values; + return arrayFrom(map.values()); } /** * Calls `callback` for each entry in the map, returning the first defined result. * Use `map.forEach` instead for normal iteration. */ - export const forEachInMap: (map: Map, callback: (value: T, key: string) => U | undefined) => U | undefined = usingNativeMaps - ? (map: ES6Map, callback: (value: T, key: string) => U | undefined) => { - const iterator = map.entries(); - while (true) { - const { value: pair, done } = iterator.next(); - if (done) return undefined; - const [key, value] = pair; - const result = callback(value, key); - if (result !== undefined) return result; + export function forEachInMap(map: Map, callback: (value: T, key: string) => U | undefined): U | undefined { + const iterator = map.entries(); + for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { + const [key, value] = pair; + const result = callback(value, key); + if (result !== undefined) { + return result; } } - : (map: ShimMap, callback: (value: T, key: string) => U | undefined) => map.forEachInMap(callback); + return undefined; + } /** `forEachInMap` for just keys. */ export function forEachKeyInMap(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined { - return forEachInMap(map, (_, key) => callback(key)); + const iterator = map.keys(); + for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { + const result = callback(key); + if (result !== undefined) { + return result; + } + } + return undefined; } /** Whether `predicate` is true for some entry in the map. */ - export const someInMap: (map: Map, predicate: (value: T, key: string) => boolean) => boolean = usingNativeMaps - ? (map: ES6Map, predicate: (value: T, key: string) => boolean) => { - const iterator = map.entries(); - while (true) { - const { value: pair, done } = iterator.next(); - if (done) return false; - const [key, value] = pair; - if (predicate(value, key)) return true; + export function someInMap(map: Map, predicate: (value: T, key: string) => boolean): boolean { + const iterator = map.entries(); + for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { + const [key, value] = pair; + if (predicate(value, key)) { + return true; } } - : (map: ShimMap, predicate: (value: T, key: string) => boolean) => map.some(predicate); + return false; + } /** `someInMap` for just keys. */ export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean { - return someInMap(map, (_, key) => predicate(key)); + const iterator = map.keys(); + for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { + if (predicate(key)) { + return true; + } + } + return false; } /** Copy entries from `source` to `target`. */ @@ -996,10 +999,6 @@ namespace ts { return result; } - export const mapIsEmpty: (map: Map<{}>) => boolean = usingNativeMaps - ? (map: ES6Map<{}>) => map.size === 0 - : (map: ShimMap<{}>) => map.isEmpty(); - export function cloneMap(map: Map) { const clone = createMap(); copyMapEntries(map, clone); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index c20e6b5cf32e8..28179969ba210 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -306,7 +306,7 @@ namespace ts { // this set is used to filter names brought by star expors. // local names set should only be added if we have anything exported - if (!moduleInfo.exportedNames && mapIsEmpty(moduleInfo.exportSpecifiers)) { + if (!moduleInfo.exportedNames && moduleInfo.exportSpecifiers.size === 0) { // no exported declarations (export var ...) or export specifiers (export {x}) // check if we have any non star export declarations. let hasExportDeclarationWithExportClause = false; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 75ff806d605af..a842dbb093dc2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -15,16 +15,23 @@ [key: number]: T; } - /** Minimal ES6 Map interface. Does not include iterators as those are hard to shim performantly. */ + /** ES6 Map interface. */ export interface Map { get(key: string): T; has(key: string): boolean; set(key: string, value: T): this; delete(key: string): boolean; clear(): void; - /** `key` may *not* be a string if it was set with a number and we are not using the shim. */ forEach(action: (value: T, key: string) => void): void; readonly size: number; + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[string, T]>; + } + + /** ES6 Iterator type. */ + export interface Iterator { + next(): { value: T, done: false } | { value: never, done: true }; } // branded string type used to store absolute, normalized and canonicalized paths diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index fe3e71f1e1c57..f4cb411b064f4 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -30,7 +30,7 @@ namespace ts { } export function getSupportedErrorCodes() { - return keysOfSparseArray(codeFixes); + return Object.keys(codeFixes); } export function getFixes(context: CodeFixContext): CodeAction[] { diff --git a/src/services/completions.ts b/src/services/completions.ts index f7efe1fec5662..4715222db08cb 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1566,7 +1566,7 @@ namespace ts.Completions { existingImportsOrExports.set(name.text, true); } - if (mapIsEmpty(existingImportsOrExports)) { + if (existingImportsOrExports.size === 0) { return filter(exportsOfModule, e => e.name !== "default"); } From 3d7b5e6019b73e062dcd01fb6376a7087cf61a12 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 27 Dec 2016 12:46:28 -0800 Subject: [PATCH 10/20] Make `createMapLike` private and rename to `createDictionaryObject` --- src/compiler/commandLineParser.ts | 6 +++--- src/compiler/core.ts | 10 +++++----- src/harness/harnessLanguageService.ts | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index b3c538afd0a28..4708b8427636b 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -762,7 +762,7 @@ namespace ts { } function serializeCompilerOptions(options: CompilerOptions): MapLike { - const result = createMapLike(); + const result: ts.MapLike = {}; const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { @@ -1302,7 +1302,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 = createMapLike(); + const wildcardDirectories: ts.MapLike = {}; if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { @@ -1325,7 +1325,7 @@ namespace ts { } // Remove any subpaths under an existing recursively watched directory. - for (const key in wildcardDirectories) { + for (const key in wildcardDirectories) if (hasProperty(wildcardDirectories, key)) { for (const recursiveKey of recursiveKeys) { if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { delete wildcardDirectories[key]; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a676688749414..693f6673234f7 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -26,8 +26,8 @@ namespace ts { // 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; - /** Create a MapLike with good performance. Prefer this over a literal `{}`. */ - export function createMapLike(): MapLike { + /** Create a MapLike with good performance. */ + function createDictionaryObject(): MapLike { const map = Object.create(null); // tslint:disable-line:no-null-keyword // Using 'delete' on an object causes V8 to put the object in dictionary mode. @@ -39,7 +39,7 @@ namespace ts { return map; } - export const sparseArray: () => SparseArray = createMapLike; + export const sparseArray: () => SparseArray = createDictionaryObject; /** Create a new map. If a template object is provided, the map will copy entries from it. */ export function createMap(template?: MapLike): Map { @@ -85,7 +85,7 @@ namespace ts { } return class implements Map { - private data = createMapLike(); + private data = createDictionaryObject(); public size = 0; get(key: string): T { @@ -115,7 +115,7 @@ namespace ts { } clear(): void { - this.data = createMapLike(); + this.data = createDictionaryObject(); this.size = 0; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 2ddc38499d082..cd44fbf7a3a28 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -262,7 +262,7 @@ namespace Harness.LanguageService { this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); - const imports = ts.createMapLike(); + const imports: ts.MapLike = {}; for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -275,7 +275,7 @@ namespace Harness.LanguageService { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); - const resolutions = ts.createMapLike(); + const resolutions: ts.MapLike = {}; const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); From f510897dbd5d1f8d0024c1b14243270dfe7006a3 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Dec 2016 08:58:31 -0800 Subject: [PATCH 11/20] Remove "sparseArray" constructor function and just use array literals --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 2 -- src/compiler/factory.ts | 2 +- src/compiler/moduleNameResolver.ts | 6 +++--- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/module/module.ts | 6 +++--- src/compiler/transformers/module/system.ts | 10 +++++----- src/compiler/transformers/ts.ts | 2 +- src/compiler/utilities.ts | 4 ++-- src/harness/unittests/session.ts | 2 +- src/harness/unittests/tsserverProjectSystem.ts | 4 ++-- src/services/codefixes/codeFixProvider.ts | 2 +- src/services/codefixes/importFixes.ts | 4 ++-- 13 files changed, 23 insertions(+), 25 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 165e4c3cca752..3ed4c383ef2a3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4084,7 +4084,7 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes = sparseArray(); + const memberTypes: SparseArray = []; for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index a7864a0646f85..e9dbdb8786a07 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -39,8 +39,6 @@ namespace ts { return map; } - export const sparseArray: () => SparseArray = createDictionaryObject; - /** Create a new map. If a template object is provided, the map will copy entries from it. */ export function createMap(template?: MapLike): Map { const map: Map = new MapCtr(); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 661a0346f264f..38e28fc2de0a3 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3299,7 +3299,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMap(); - const exportedBindings = sparseArray(); + const exportedBindings: SparseArray = []; const uniqueExports = createMap(); let exportedNames: Identifier[]; let hasExportDefault = false; diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index a8b634c023401..1efe9078f5a9a 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -388,7 +388,7 @@ namespace ts { } } } - + function getCommonPrefix(directory: Path, resolution: string) { if (resolution === undefined) { return undefined; @@ -418,7 +418,7 @@ namespace ts { trace(host, Diagnostics.Resolving_module_0_from_1, moduleName, containingFile); } const containingDirectory = getDirectoryPath(containingFile); - let perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory); + const perFolderCache = cache && cache.getOrCreateCacheForDirectory(containingDirectory); let result = perFolderCache && perFolderCache.get(moduleName); if (result) { @@ -991,7 +991,7 @@ namespace ts { * - { value: } - found - stop searching */ type SearchResult = { value: T | undefined } | undefined; - + /** * Wraps value to SearchResult. * @returns undefined if value is undefined or { value } otherwise diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7cf7602efa0c4..8c9da9c48477f 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -2096,7 +2096,7 @@ namespace ts { if (!renamedCatchVariables) { renamedCatchVariables = createMap(); - renamedCatchVariableDeclarations = sparseArray(); + renamedCatchVariableDeclarations = []; context.enableSubstitution(SyntaxKind.Identifier); } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index e5e27c8360df2..192f1f81a8f9e 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -39,8 +39,8 @@ namespace ts { context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. - const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap: SparseArray = []; // The ExternalModuleInfo for each file. + const deferredExports: SparseArray = []; // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. @@ -1105,7 +1105,7 @@ namespace ts { if (node.kind === SyntaxKind.SourceFile) { currentSourceFile = node; currentModuleInfo = moduleInfoMap[getOriginalNodeId(currentSourceFile)]; - noSubstitution = sparseArray(); + noSubstitution = []; previousOnEmitNode(emitContext, node, emitCallback); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 5f23ccd0669da..d332f3376b15b 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -28,10 +28,10 @@ namespace ts { context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap = sparseArray(); // The ExternalModuleInfo for each file. - const deferredExports = sparseArray(); // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap = sparseArray(); // The export function associated with a source file. - const noSubstitutionMap = sparseArray>(); // Set of nodes for which substitution rules should be ignored for each file. + const moduleInfoMap: SparseArray = []; // The ExternalModuleInfo for each file. + const deferredExports: SparseArray = []; // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap: SparseArray = []; // The export function associated with a source file. + const noSubstitutionMap: SparseArray> = []; // Set of nodes for which substitution rules should be ignored for each file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -1771,7 +1771,7 @@ namespace ts { * @param node The node which should not be substituted. */ function preventSubstitution(node: T): T { - if (noSubstitution === undefined) noSubstitution = sparseArray(); + if (noSubstitution === undefined) noSubstitution = []; noSubstitution[getNodeId(node)] = true; return node; } diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 0947c894db8c9..96b00434660d1 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -3125,7 +3125,7 @@ namespace ts { context.enableSubstitution(SyntaxKind.Identifier); // Keep track of class aliases. - classAliases = sparseArray(); + classAliases = []; } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7fdda448ae895..44bb05dd4a853 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1,4 +1,4 @@ -/// +/// /* @internal */ namespace ts { @@ -3365,7 +3365,7 @@ namespace ts { return false; } - const syntaxKindCache = sparseArray(); + const syntaxKindCache: SparseArray = []; export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 70cda27747b7e..82f6ede865f24 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -416,7 +416,7 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks = sparseArray<(resp: protocol.Response) => void>(); + private callbacks: SparseArray<(resp: protocol.Response) => void> = []; private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 98a34d2a3bb01..4ae2dc7a3894e 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -292,7 +292,7 @@ namespace ts.projectSystem { } export class Callbacks { - private map = sparseArray(); + private map: SparseArray = []; private nextId = 1; register(cb: (...args: any[]) => void, args: any[]) { @@ -319,7 +319,7 @@ namespace ts.projectSystem { for (const key in this.map) { this.map[key](); } - this.map = sparseArray(); + this.map = []; } } diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index f4cb411b064f4..e1e3ef1886579 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -16,7 +16,7 @@ namespace ts { } export namespace codefix { - const codeFixes = sparseArray(); + const codeFixes: SparseArray = []; export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 0df3c3134fe09..81b299bf708d6 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -14,7 +14,7 @@ namespace ts.codefix { } class ImportCodeActionMap { - private symbolIdToActionMap = sparseArray(); + private symbolIdToActionMap: SparseArray = []; addAction(symbolId: number, newAction: ImportCodeAction) { if (!newAction) { @@ -125,7 +125,7 @@ namespace ts.codefix { const symbolIdActionMap = new ImportCodeActionMap(); // this is a module id -> module import declaration map - const cachedImportDeclarations = sparseArray<(ImportDeclaration | ImportEqualsDeclaration)[]>(); + const cachedImportDeclarations: SparseArray<(ImportDeclaration | ImportEqualsDeclaration)[]> = []; let cachedNewImportInsertPosition: number; const allPotentialModules = checker.getAmbientModules(); From 39c19a74eae41dc83f44885bfa740d50e7b17c81 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Dec 2016 09:05:52 -0800 Subject: [PATCH 12/20] Inline `keysOfMap` and `valuesOfMap`. --- src/compiler/commandLineParser.ts | 8 ++++---- src/compiler/core.ts | 18 +++--------------- src/compiler/tsc.ts | 2 +- src/harness/unittests/tsserverProjectSystem.ts | 2 +- src/server/project.ts | 6 +++--- src/services/documentRegistry.ts | 4 ++-- 6 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a32a5137bad13..7bf50f675aa4a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1,4 +1,4 @@ -/// +/// /// /// /// @@ -543,7 +543,7 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { - const namesOfType = keysOfMap(opt.type).map(key => `'${key}'`).join(", "); + const namesOfType = arrayFrom(opt.type.keys()).map(key => `'${key}'`).join(", "); return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -1255,8 +1255,8 @@ namespace ts { } } - const literalFiles = valuesOfMap(literalFileMap); - const wildcardFiles = valuesOfMap(wildcardFileMap); + const literalFiles = arrayFrom(literalFileMap.values()); + const wildcardFiles = arrayFrom(wildcardFileMap.values()); wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e9dbdb8786a07..9f8f1ba3f5558 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -156,7 +156,7 @@ namespace ts { } function getKeys() { - return keysOfMap(files) as Path[]; + return arrayFrom(files.keys()) as Path[]; } // path should already be well-formed so it does not need to be normalized @@ -867,7 +867,8 @@ namespace ts { return keys; } - function arrayFrom(iterator: Iterator): T[] { + /** Shims `Array.from`. */ + export function arrayFrom(iterator: Iterator): T[] { const result: T[] = []; for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { result.push(value); @@ -875,19 +876,6 @@ namespace ts { return result; } - /** - * Array of every key in a map. - * May not actually return string[] if numbers were put into the map. - */ - export function keysOfMap(map: Map<{}>): string[] { - return arrayFrom(map.keys()); - } - - /** Array of every value in a map. */ - export function valuesOfMap(map: Map): T[] { - return arrayFrom(map.values()); - } - /** * Calls `callback` for each entry in the map, returning the first defined result. * Use `map.forEach` instead for normal iteration. diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index ba5a0d8faea90..a0e7d5ba3238c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -680,7 +680,7 @@ namespace ts { description = getDiagnosticText(option.description); const element = (option).element; const typeMap = >element.type; - optionsDescriptionMap.set(description, keysOfMap(typeMap).map(key => `'${key}'`)); + optionsDescriptionMap.set(description, arrayFrom(typeMap.keys()).map(key => `'${key}'`)); } else { description = getDiagnosticText(option.description); diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 4ae2dc7a3894e..caa51cd5566f4 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -246,7 +246,7 @@ namespace ts.projectSystem { export function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { - assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${keysOfMap(map)}`); + assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`); } } diff --git a/src/server/project.ts b/src/server/project.ts index 505eb2f63fb6d..054b484392e05 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1,4 +1,4 @@ -/// +/// /// /// /// @@ -617,7 +617,7 @@ namespace ts.server { const added: string[] = []; const removed: string[] = []; - const updated: string[] = keysOfMap(updatedFileNames); + const updated: string[] = arrayFrom(updatedFileNames.keys()); forEachKeyInMap(currentFiles, id => { if (!lastReportedFileNames.has(id)) { @@ -691,7 +691,7 @@ namespace ts.server { }) } - const allFileNames = keysOfMap(referencedFiles) as Path[]; + const allFileNames = arrayFrom(referencedFiles.keys()) as Path[]; return filter(allFileNames, file => this.projectService.host.fileExists(file)); } diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 4291abf6c1fd0..80b0133ccfe55 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -1,4 +1,4 @@ -namespace ts { +namespace ts { /** * The document registry represents a store of SourceFile objects that can be shared between * multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST) @@ -121,7 +121,7 @@ namespace ts { } function reportStats() { - const bucketInfoArray = keysOfMap(buckets).filter(name => name && name.charAt(0) === "_").map(name => { + const bucketInfoArray = arrayFrom(buckets.keys()).filter(name => name && name.charAt(0) === "_").map(name => { const entries = buckets.get(name); const sourceFiles: { name: string; refCount: number; references: string[]; }[] = []; entries.forEachValue((key, entry) => { From 932eaa3f90c7418c6e4ce09cdab7ca30309e19ca Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Dec 2016 09:16:38 -0800 Subject: [PATCH 13/20] Rename and consolidate map iteration helpers --- src/compiler/checker.ts | 10 ++--- src/compiler/commandLineParser.ts | 2 +- src/compiler/core.ts | 37 ++++--------------- src/compiler/declarationEmitter.ts | 2 +- src/compiler/program.ts | 2 +- .../unittests/cachingInServerLSHost.ts | 4 +- .../unittests/reuseProgramStructure.ts | 6 +-- src/server/editorServices.ts | 2 +- src/server/project.ts | 4 +- src/services/completions.ts | 4 +- src/services/findAllReferences.ts | 4 +- src/services/navigateTo.ts | 4 +- src/services/transpile.ts | 6 +-- 13 files changed, 32 insertions(+), 55 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ed4c383ef2a3..b8e51c7f390c2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1815,7 +1815,7 @@ namespace ts { } // Check if symbol is any of the alias - return forEachInMap(symbols, symbolFromSymbolTable => { + return forEachEntry(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -7758,7 +7758,7 @@ 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]; - copyMapEntries(maybeCache, destinationCache); + copyEntries(maybeCache, destinationCache); } else { // A false result goes straight into global cache (when something is false under assumptions it @@ -18001,7 +18001,7 @@ namespace ts { else { const blockLocals = catchClause.block.locals; if (blockLocals) { - forEachKeyInMap(catchClause.locals, caughtName => { + forEachKey(catchClause.locals, caughtName => { const blockLocal = blockLocals.get(caughtName); if (blockLocal && (blockLocal.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(blockLocal.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName); @@ -19187,7 +19187,7 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - return someInMap(moduleSymbol.exports, (_, id) => id !== "export="); + return forEachEntry(moduleSymbol.exports, (_, id) => id !== "export="); } function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) { @@ -20097,7 +20097,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : someInMap(getExportsOfModule(moduleSymbol), isValue); + : forEachEntry(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7bf50f675aa4a..3f89d3e3f9d74 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -753,7 +753,7 @@ namespace ts { function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: Map): string | undefined { // There is a typeMap associated with this command-line option so use it to map value back to its name - return forEachInMap(customTypeMap, (mapValue, key) => { + return forEachEntry(customTypeMap, (mapValue, key) => { if (mapValue === value) { return key; } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 9f8f1ba3f5558..effe24a253e56 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -877,15 +877,15 @@ namespace ts { } /** - * Calls `callback` for each entry in the map, returning the first defined result. + * Calls `callback` for each entry in the map, returning the first truthy result. * Use `map.forEach` instead for normal iteration. */ - export function forEachInMap(map: Map, callback: (value: T, key: string) => U | undefined): U | undefined { + export function forEachEntry(map: Map, callback: (value: T, key: string) => U | undefined): U | undefined { const iterator = map.entries(); for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { const [key, value] = pair; const result = callback(value, key); - if (result !== undefined) { + if (result) { return result; } } @@ -893,42 +893,19 @@ namespace ts { } /** `forEachInMap` for just keys. */ - export function forEachKeyInMap(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined { + export function forEachKey(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined { const iterator = map.keys(); for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { const result = callback(key); - if (result !== undefined) { + if (result) { return result; } } return undefined; } - /** Whether `predicate` is true for some entry in the map. */ - export function someInMap(map: Map, predicate: (value: T, key: string) => boolean): boolean { - const iterator = map.entries(); - for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { - const [key, value] = pair; - if (predicate(value, key)) { - return true; - } - } - return false; - } - - /** `someInMap` for just keys. */ - export function someKeyInMap(map: Map<{}>, predicate: (key: string) => boolean): boolean { - const iterator = map.keys(); - for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { - if (predicate(key)) { - return true; - } - } - return false; - } - /** Copy entries from `source` to `target`. */ - export function copyMapEntries(source: Map, target: Map): void { + export function copyEntries(source: Map, target: Map): void { source.forEach((value, key) => { target.set(key, value); }); @@ -987,7 +964,7 @@ namespace ts { export function cloneMap(map: Map) { const clone = createMap(); - copyMapEntries(map, clone); + copyEntries(map, clone); return clone; } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index e010519d64120..a006ab4fcedf8 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -156,7 +156,7 @@ namespace ts { }); if (usedTypeDirectiveReferences) { - forEachKeyInMap(usedTypeDirectiveReferences, directive => { + forEachKey(usedTypeDirectiveReferences, directive => { referencesOutput += `/// ${newLine}`; }); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index b6138e92a7b47..324a21f229ea3 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -464,7 +464,7 @@ namespace ts { classifiableNames = createMap(); for (const sourceFile of files) { - copyMapEntries(sourceFile.classifiableNames, classifiableNames); + copyEntries(sourceFile.classifiableNames, classifiableNames); } } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index b6a90f53259c8..8389186c486e5 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -1,4 +1,4 @@ -/// +/// namespace ts { interface File { @@ -8,7 +8,7 @@ namespace ts { function createDefaultServerHost(fileMap: Map): server.ServerHost { const existingDirectories = createMap(); - forEachKeyInMap(fileMap, name => { + forEachKey(fileMap, name => { let dir = getDirectoryPath(name); let previous: string; do { diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index eb4395154e41c..8a89b8d27c6a9 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -1,4 +1,4 @@ -/// +/// /// namespace ts { @@ -197,13 +197,13 @@ namespace ts { function mapsAreEqual(left: Map, right: Map, valuesAreEqual?: (left: T, right: T) => boolean): boolean { if (left === right) return true; if (!left || !right) return false; - const someInLeftHasNoMatch = someInMap(left, (leftValue, leftKey) => { + const someInLeftHasNoMatch = forEachEntry(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)); + const someInRightHasNoMatch = forEachKey(right, rightKey => !left.has(rightKey)); return !someInRightHasNoMatch; } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 239cd385f57ba..8865d821d29df 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1373,7 +1373,7 @@ namespace ts.server { } // close projects that were missing in the input list - forEachKeyInMap(projectsToClose, externalProjectName => { + forEachKey(projectsToClose, externalProjectName => { this.closeExternalProject(externalProjectName, /*suppressRefresh*/ true) }); diff --git a/src/server/project.ts b/src/server/project.ts index 054b484392e05..94a08a485f93c 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -619,12 +619,12 @@ namespace ts.server { const removed: string[] = []; const updated: string[] = arrayFrom(updatedFileNames.keys()); - forEachKeyInMap(currentFiles, id => { + forEachKey(currentFiles, id => { if (!lastReportedFileNames.has(id)) { added.push(id); } }); - forEachKeyInMap(lastReportedFileNames, id => { + forEachKey(lastReportedFileNames, id => { if (!currentFiles.has(id)) { removed.push(id); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 4715222db08cb..04e3952b88bb9 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1,4 +1,4 @@ -/// +/// /* @internal */ namespace ts.Completions { @@ -378,7 +378,7 @@ namespace ts.Completions { } } - forEachKeyInMap(foundFiles, foundFile => { + forEachKey(foundFiles, foundFile => { result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span)); }); } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 9e53537b69b49..a7bf76e0656bc 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -1,4 +1,4 @@ -/* @internal */ +/* @internal */ namespace ts.FindAllReferences { export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true); @@ -508,7 +508,7 @@ namespace ts.FindAllReferences { result.push(ctrKeyword); } - forEachInMap(classSymbol.exports, member => { + classSymbol.exports.forEach(member => { const decl = member.valueDeclaration; if (decl && decl.kind === SyntaxKind.MethodDeclaration) { const body = (decl).body; diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index 40f229412681e..aa72da1ba5c29 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -1,4 +1,4 @@ -/* @internal */ +/* @internal */ namespace ts.NavigateTo { type RawNavigateToItem = { name: string; fileName: string; matchKind: PatternMatchKind; isCaseSensitive: boolean; declaration: Declaration }; @@ -14,7 +14,7 @@ namespace ts.NavigateTo { continue; } - forEachInMap(sourceFile.getNamedDeclarations(), (declarations, name) => { + forEachEntry(sourceFile.getNamedDeclarations(), (declarations, name) => { if (declarations) { // First do a quick check to see if the name of the declaration matches the // last portion of the (possibly) dotted name they're searching for. diff --git a/src/services/transpile.ts b/src/services/transpile.ts index d989a528497d8..b72399384b7db 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -1,4 +1,4 @@ -namespace ts { +namespace ts { export interface TranspileOptions { compilerOptions?: CompilerOptions; fileName?: string; @@ -126,7 +126,7 @@ namespace ts { function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { // Lazily create this value to fix module loading errors. commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachInMap(o.type, v => typeof v !== "number")); + typeof o.type === "object" && !forEachEntry(o.type, v => typeof v !== "number")); options = clone(options); @@ -142,7 +142,7 @@ namespace ts { options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } else { - if (!someInMap(opt.type, v => v === value)) { + if (!forEachEntry(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } From 145f0b2f18b2cc4ad69ed577920a541faf1ad123 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Dec 2016 09:33:43 -0800 Subject: [PATCH 14/20] Add `createMultiMap` to replace `multiMapAdd` and `multiMapRemove` --- src/compiler/core.ts | 50 +++++++++---------- src/compiler/factory.ts | 16 +++++- src/compiler/sys.ts | 8 +-- src/harness/fourslash.ts | 4 +- .../unittests/tsserverProjectSystem.ts | 12 ++--- src/services/services.ts | 4 +- 6 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index effe24a253e56..3669ece5104e0 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -989,43 +989,43 @@ namespace ts { 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, value: V): V[] { - let values = map.get(key); - if (values) { - values.push(value); - } - else { - map.set(key, values = [value]); - } - return values; + export interface MultiMap extends Map { + /** + * 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. + */ + add(key: string, value: T): T[]; + /** + * 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]`. + */ + remove(key: string, value: T): void; } - export function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { - let values = map[key]; + export function createMultiMap(): MultiMap { + const map = createMap() as MultiMap; + map.add = multiMapAdd; + map.remove = multiMapRemove; + return map; + } + function multiMapAdd(this: MultiMap, key: string, value: T) { + let values = this.get(key); if (values) { values.push(value); } else { - map[key] = values = [value]; + this.set(key, values = [value]); } return values; - } - /** - * 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.get(key); + } + function multiMapRemove(this: MultiMap, key: string, value: T) { + const values = this.get(key); if (values) { unorderedRemoveItem(values, value); if (!values.length) { - map.delete(key); + this.delete(key); } } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 38e28fc2de0a3..06108f2d0b258 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3298,7 +3298,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; - const exportSpecifiers = createMap(); + const exportSpecifiers = createMultiMap(); const exportedBindings: SparseArray = []; const uniqueExports = createMap(); let exportedNames: Identifier[]; @@ -3352,7 +3352,7 @@ namespace ts { for (const specifier of (node).exportClause.elements) { if (!uniqueExports.get(specifier.name.text)) { const name = specifier.propertyName || specifier.name; - multiMapAdd(exportSpecifiers, name.text, specifier); + exportSpecifiers.add(name.text, specifier); const decl = resolver.getReferencedImportDeclaration(name) || resolver.getReferencedValueDeclaration(name); @@ -3446,4 +3446,16 @@ namespace ts { } return exportedNames; } + + /** Use a sparse array as a multi-map. */ + function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { + let values = map[key]; + if (values) { + values.push(value); + } + else { + map[key] = values = [value]; + } + return values; + } } diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index b10d746e43561..3c28efd0de889 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -1,4 +1,4 @@ -/// +/// namespace ts { export type FileWatcherCallback = (fileName: string, removed?: boolean) => void; @@ -243,7 +243,7 @@ namespace ts { function createWatchedFileSet() { const dirWatchers = createMap(); // One file can have multiple watchers - const fileWatcherCallbacks = createMap(); + const fileWatcherCallbacks = createMultiMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { @@ -275,7 +275,7 @@ namespace ts { } function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void { - multiMapAdd(fileWatcherCallbacks, filePath, callback); + fileWatcherCallbacks.add(filePath, callback); } function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile { @@ -291,7 +291,7 @@ namespace ts { } function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { - multiMapRemove(fileWatcherCallbacks, filePath, callback); + fileWatcherCallbacks.remove(filePath, callback); } function fileEventHandler(eventName: string, relativeFileName: string, baseDirPath: string) { diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 04fd55b110eba..8b127afa081b2 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1760,10 +1760,10 @@ namespace FourSlash { } public rangesByText(): ts.Map { - const result = ts.createMap(); + const result = ts.createMultiMap(); for (const range of this.getRanges()) { const text = this.rangeText(range); - ts.multiMapAdd(result, text, range); + result.add(text, range); } return result; } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index caa51cd5566f4..c8729d4949778 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -334,8 +334,8 @@ namespace ts.projectSystem { private timeoutCallbacks = new Callbacks(); private immediateCallbacks = new Callbacks(); - readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>(); - readonly watchedFiles = createMap(); + readonly watchedDirectories = createMultiMap<{ cb: DirectoryWatcherCallback, recursive: boolean }>(); + readonly watchedFiles = createMultiMap(); private filesOrFolders: FileOrFolder[]; @@ -421,11 +421,11 @@ namespace ts.projectSystem { watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { const path = this.toPath(directoryName); const cbWithRecursive = { cb: callback, recursive }; - multiMapAdd(this.watchedDirectories, path, cbWithRecursive); + this.watchedDirectories.add(path, cbWithRecursive); return { referenceCount: 0, directoryName, - close: () => multiMapRemove(this.watchedDirectories, path, cbWithRecursive) + close: () => this.watchedDirectories.remove(path, cbWithRecursive) }; } @@ -455,8 +455,8 @@ namespace ts.projectSystem { watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); - multiMapAdd(this.watchedFiles, path, callback); - return { close: () => multiMapRemove(this.watchedFiles, path, callback) }; + this.watchedFiles.add(path, callback); + return { close: () => this.watchedFiles.remove(path, callback) }; } // TOOD: record and invoke callbacks to simulate timer events diff --git a/src/services/services.ts b/src/services/services.ts index 7cd2dce196d4d..86fbf22b71a3c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -518,7 +518,7 @@ namespace ts { } private computeNamedDeclarations(): Map { - const result = createMap(); + const result = createMultiMap(); forEachChild(this, visit); @@ -527,7 +527,7 @@ namespace ts { function addDeclaration(declaration: Declaration) { const name = getDeclarationName(declaration); if (name) { - multiMapAdd(result, name, declaration); + result.add(name, declaration); } } From 2e6f369e8fa15ad75668467d1dbcb2b411d7f75d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 28 Dec 2016 09:39:58 -0800 Subject: [PATCH 15/20] Replace SparseArray with T[] --- src/compiler/checker.ts | 2 +- src/compiler/factory.ts | 8 ++++---- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/module/module.ts | 6 +++--- src/compiler/transformers/module/system.ts | 10 +++++----- src/compiler/transformers/ts.ts | 2 +- src/compiler/types.ts | 11 ++--------- src/compiler/utilities.ts | 2 +- src/harness/unittests/session.ts | 2 +- src/harness/unittests/tsserverProjectSystem.ts | 2 +- src/services/codefixes/codeFixProvider.ts | 2 +- src/services/codefixes/importFixes.ts | 4 ++-- 12 files changed, 23 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fcc44ba087f7a..48a0876d88184 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4084,7 +4084,7 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes: SparseArray = []; + const memberTypes: EnumLiteralType[] = []; for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index a326c0c0475be..f63094ae16d7e 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2646,7 +2646,7 @@ namespace ts { return destEmitNode; } - function mergeTokenSourceMapRanges(sourceRanges: SparseArray, destRanges: SparseArray) { + function mergeTokenSourceMapRanges(sourceRanges: TextRange[], destRanges: TextRange[]) { if (!destRanges) destRanges = []; for (const key in sourceRanges) { destRanges[key] = sourceRanges[key]; @@ -3277,7 +3277,7 @@ namespace ts { externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[]; // imports of other external modules externalHelpersImportDeclaration: ImportDeclaration | undefined; // import of external helpers exportSpecifiers: Map; // export specifiers by name - exportedBindings: SparseArray; // exported names of local declarations + exportedBindings: Identifier[][]; // exported names of local declarations exportedNames: Identifier[]; // all exported names local to module exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* @@ -3286,7 +3286,7 @@ namespace ts { export function collectExternalModuleInfo(sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMultiMap(); - const exportedBindings: SparseArray = []; + const exportedBindings: Identifier[][] = []; const uniqueExports = createMap(); let exportedNames: Identifier[]; let hasExportDefault = false; @@ -3435,7 +3435,7 @@ namespace ts { } /** Use a sparse array as a multi-map. */ - function multiMapSparseArrayAdd(map: SparseArray, key: number, value: V): V[] { + function multiMapSparseArrayAdd(map: V[][], key: number, value: V): V[] { let values = map[key]; if (values) { values.push(value); diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 8c9da9c48477f..7dcfe76a42774 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -243,7 +243,7 @@ namespace ts { let currentSourceFile: SourceFile; let renamedCatchVariables: Map; - let renamedCatchVariableDeclarations: SparseArray; + let renamedCatchVariableDeclarations: Identifier[]; let inGeneratorFunctionBody: boolean; let inStatementContainingYield: boolean; diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index b42f4bf4bee5d..0adea4fd5407b 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -39,12 +39,12 @@ namespace ts { context.enableSubstitution(SyntaxKind.ShorthandPropertyAssignment); // Substitutes shorthand property assignments for imported/exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap: SparseArray = []; // The ExternalModuleInfo for each file. - const deferredExports: SparseArray = []; // Exports to defer until an EndOfDeclarationMarker is found. + const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. + const deferredExports: Statement[][] = []; // Exports to defer until an EndOfDeclarationMarker is found. let currentSourceFile: SourceFile; // The current file. let currentModuleInfo: ExternalModuleInfo; // The ExternalModuleInfo for the current file. - let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index d332f3376b15b..3f1488b649d19 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -28,10 +28,10 @@ namespace ts { context.enableSubstitution(SyntaxKind.PostfixUnaryExpression); // Substitutes updates to exported symbols. context.enableEmitNotification(SyntaxKind.SourceFile); // Restore state when substituting nodes in a file. - const moduleInfoMap: SparseArray = []; // The ExternalModuleInfo for each file. - const deferredExports: SparseArray = []; // Exports to defer until an EndOfDeclarationMarker is found. - const exportFunctionsMap: SparseArray = []; // The export function associated with a source file. - const noSubstitutionMap: SparseArray> = []; // Set of nodes for which substitution rules should be ignored for each file. + const moduleInfoMap: ExternalModuleInfo[] = []; // The ExternalModuleInfo for each file. + const deferredExports: Statement[][] = []; // Exports to defer until an EndOfDeclarationMarker is found. + const exportFunctionsMap: Identifier[] = []; // The export function associated with a source file. + const noSubstitutionMap: boolean[][] = []; // Set of nodes for which substitution rules should be ignored for each file. let currentSourceFile: SourceFile; // The current file. let moduleInfo: ExternalModuleInfo; // ExternalModuleInfo for the current file. @@ -39,7 +39,7 @@ namespace ts { let contextObject: Identifier; // The context object for the current file. let hoistedStatements: Statement[]; let enclosingBlockScopedContainer: Node; - let noSubstitution: SparseArray; // Set of nodes for which substitution rules should be ignored. + let noSubstitution: boolean[]; // Set of nodes for which substitution rules should be ignored. return transformSourceFile; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 96b00434660d1..e2b8b8fe2ec40 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -60,7 +60,7 @@ namespace ts { * A map that keeps track of aliases created for classes with decorators to avoid issues * with the double-binding behavior of classes. */ - let classAliases: SparseArray; + let classAliases: Identifier[]; /** * Keeps track of whether we are within any containing namespaces when performing diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d734998cd7f92..2489434c82d82 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8,13 +8,6 @@ [index: string]: T; } - /** - * Like MapLike, but keys must be numbers. - */ - export interface SparseArray { - [key: number]: T; - } - /** ES6 Map interface. */ export interface Map { get(key: string): T; @@ -2854,7 +2847,7 @@ // Enum types (TypeFlags.Enum) export interface EnumType extends Type { - memberTypes: SparseArray; + memberTypes: EnumLiteralType[]; } // Enum types (TypeFlags.EnumLiteral) @@ -3684,7 +3677,7 @@ flags?: EmitFlags; // Flags that customize emit commentRange?: TextRange; // The text range to use when emitting leading or trailing comments sourceMapRange?: TextRange; // The text range to use when emitting leading or trailing source mappings - tokenSourceMapRanges?: SparseArray; // The text range to use when emitting source mappings for tokens + tokenSourceMapRanges?: TextRange[]; // The text range to use when emitting source mappings for tokens constantValue?: number; // The constant value of an expression externalHelpersModuleName?: Identifier; // The local name for an imported helpers module helpers?: EmitHelper[]; // Emit helpers for the node diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5e15d97752b56..f74c450d61a53 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3362,7 +3362,7 @@ namespace ts { return false; } - const syntaxKindCache: SparseArray = []; + const syntaxKindCache: string[] = []; export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 82f6ede865f24..15c43d9e76bf7 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -416,7 +416,7 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks: SparseArray<(resp: protocol.Response) => void> = []; + private callbacks: Array<(resp: protocol.Response) => void> = []; private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 4d4873465b521..b2e2eca295186 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -292,7 +292,7 @@ namespace ts.projectSystem { } export class Callbacks { - private map: SparseArray = []; + private map: TimeOutCallback[] = []; private nextId = 1; register(cb: (...args: any[]) => void, args: any[]) { diff --git a/src/services/codefixes/codeFixProvider.ts b/src/services/codefixes/codeFixProvider.ts index e1e3ef1886579..10d0b8eef3464 100644 --- a/src/services/codefixes/codeFixProvider.ts +++ b/src/services/codefixes/codeFixProvider.ts @@ -16,7 +16,7 @@ namespace ts { } export namespace codefix { - const codeFixes: SparseArray = []; + const codeFixes: CodeFix[][] = []; export function registerCodeFix(action: CodeFix) { forEach(action.errorCodes, error => { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 81b299bf708d6..8cd0a9664a05c 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -14,7 +14,7 @@ namespace ts.codefix { } class ImportCodeActionMap { - private symbolIdToActionMap: SparseArray = []; + private symbolIdToActionMap: ImportCodeAction[][] = []; addAction(symbolId: number, newAction: ImportCodeAction) { if (!newAction) { @@ -125,7 +125,7 @@ namespace ts.codefix { const symbolIdActionMap = new ImportCodeActionMap(); // this is a module id -> module import declaration map - const cachedImportDeclarations: SparseArray<(ImportDeclaration | ImportEqualsDeclaration)[]> = []; + const cachedImportDeclarations: (ImportDeclaration | ImportEqualsDeclaration)[][] = []; let cachedNewImportInsertPosition: number; const allPotentialModules = checker.getAmbientModules(); From 9221336bf1bd8e1c53ec5f3cef12c829aa4c1328 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 6 Jan 2017 13:13:45 -0800 Subject: [PATCH 16/20] Fix name: `mapEntries` is more accurate --- src/compiler/core.ts | 2 +- src/harness/unittests/configurationExtension.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 3b2197d6efa69..b65634565b7fd 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -506,7 +506,7 @@ namespace ts { return result; } - export function mapValues(map: Map, f: (key: string, value: T) => [string, U]): Map { + export function mapEntries(map: Map, f: (key: string, value: T) => [string, U]): Map { if (!map) { return undefined; } diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index 03eb825811ba7..c68d07f512313 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -89,7 +89,7 @@ namespace ts { }); const caseInsensitiveBasePath = "c:/dev/"; - const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapValues(testContents, (key, content) => [`c:${key}`, content])); + const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapEntries(testContents, (key, content) => [`c:${key}`, content])); const caseSensitiveBasePath = "/dev/"; const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents); From c28d98a1468f9a0541dd4ac64bebcd96d18fe289 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 9 Jan 2017 06:36:07 -0800 Subject: [PATCH 17/20] Fix test --- ...eFixClassImplementInterfaceComputedPropertyLiterals.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts index 465f9a5b4f1b3..341b4e59814a3 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts @@ -10,12 +10,12 @@ //// class C implements I {[| |]} verify.rangeAfterCodeFix(` - [1](): string { - throw new Error('Method not implemented.'); - } - [2]: boolean; ["foo"](o: any): boolean { throw new Error('Method not implemented.'); } ["x"]: boolean; + [1](): string { + throw new Error('Method not implemented.'); + } + [2]: boolean; `); \ No newline at end of file From 37e18d974158b73d3eb9c7d07b65b34df836120d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 11 Jan 2017 09:52:50 -0800 Subject: [PATCH 18/20] Add `createMapFromTemplate` helper --- src/compiler/checker.ts | 6 ++--- src/compiler/commandLineParser.ts | 12 +++++----- src/compiler/core.ts | 7 +++++- src/compiler/scanner.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/utilities.ts | 2 +- src/harness/fourslash.ts | 2 +- .../unittests/cachingInServerLSHost.ts | 4 ++-- .../unittests/configurationExtension.ts | 2 +- src/harness/unittests/moduleResolution.ts | 22 +++++++++---------- .../unittests/reuseProgramStructure.ts | 12 +++++----- src/server/editorServices.ts | 2 +- src/services/jsTyping.ts | 2 +- src/services/transpile.ts | 2 +- 14 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3348db7272a50..efa10943793b1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -315,7 +315,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts = createMap({ + const typeofEQFacts = createMapFromTemplate({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -325,7 +325,7 @@ namespace ts { "function": TypeFacts.TypeofEQFunction }); - const typeofNEFacts = createMap({ + const typeofNEFacts = createMapFromTemplate({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -335,7 +335,7 @@ namespace ts { "function": TypeFacts.TypeofNEFunction }); - const typeofTypesByName = createMap({ + const typeofTypesByName = createMapFromTemplate({ "string": stringType, "number": numberType, "boolean": booleanType, diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index a8c55a88e3f80..b222df53630e0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -65,7 +65,7 @@ namespace ts { }, { name: "jsx", - type: createMap({ + type: createMapFromTemplate({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React }), @@ -100,7 +100,7 @@ namespace ts { { name: "module", shortName: "m", - type: createMap({ + type: createMapFromTemplate({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -114,7 +114,7 @@ namespace ts { }, { name: "newLine", - type: createMap({ + type: createMapFromTemplate({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed }), @@ -263,7 +263,7 @@ namespace ts { { name: "target", shortName: "t", - type: createMap({ + type: createMapFromTemplate({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES2015, @@ -300,7 +300,7 @@ namespace ts { }, { name: "moduleResolution", - type: createMap({ + type: createMapFromTemplate({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, }), @@ -409,7 +409,7 @@ namespace ts { type: "list", element: { name: "lib", - type: createMap({ + type: createMapFromTemplate({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 34c47bcd2f41d..7e9cb460ba8d6 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -40,7 +40,12 @@ namespace ts { } /** Create a new map. If a template object is provided, the map will copy entries from it. */ - export function createMap(template?: MapLike): Map { + export function createMap(): Map { + return new MapCtr(); + } + + //!!! + export function createMapFromTemplate(template?: MapLike): Map { const map: Map = new MapCtr(); // Copies keys/values from template. Note that for..in will not throw if diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 259eaa0f5f3e3..84f1f624d9594 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -56,7 +56,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken = createMap({ + const textToToken = createMapFromTemplate({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 4879b3c4fa39a..1307a7f8551f9 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -286,7 +286,7 @@ namespace ts { } } - const entities = createMap({ + const entities = createMapFromTemplate({ "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d3e1f07663883..932e75d034505 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2351,7 +2351,7 @@ namespace ts { // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; - const escapedCharsMap = createMap({ + const escapedCharsMap = createMapFromTemplate({ "\0": "\\0", "\t": "\\t", "\v": "\\v", diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ff4964b284bd3..a75acec53a51a 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -90,7 +90,7 @@ namespace FourSlash { export import IndentStyle = ts.IndentStyle; - const entityMap = ts.createMap({ + const entityMap = ts.createMapFromTemplate({ "&": "&", "\"": """, "'": "'", diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 8389186c486e5..caeab18958e02 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -86,7 +86,7 @@ namespace ts { content: `foo()` }; - const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported })); + const serverHost = createDefaultServerHost(createMapFromTemplate({ [root.name]: root, [imported.name]: imported })); const { project, rootScriptInfo } = createProject(root.name, serverHost); // ensure that imported file was found @@ -170,7 +170,7 @@ namespace ts { content: `export var y = 1` }; - const fileMap = createMap({ [root.name]: root }); + const fileMap = createMapFromTemplate({ [root.name]: root }); const serverHost = createDefaultServerHost(fileMap); const originalFileExists = serverHost.fileExists; diff --git a/src/harness/unittests/configurationExtension.ts b/src/harness/unittests/configurationExtension.ts index c68d07f512313..886ee5d54b55f 100644 --- a/src/harness/unittests/configurationExtension.ts +++ b/src/harness/unittests/configurationExtension.ts @@ -2,7 +2,7 @@ /// namespace ts { - const testContents = createMap({ + const testContents = createMapFromTemplate({ "/dev/tsconfig.json": `{ "extends": "./configs/base", "files": [ diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 8e4ace3865595..aa30f1e01e2bd 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -331,7 +331,7 @@ namespace ts { } it("should find all modules", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c/first/shared.ts": ` class A {} export = A`, @@ -350,7 +350,7 @@ export = C; }); it("should find modules in node_modules", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/parent/node_modules/mod/index.d.ts": "export var x", "/parent/app/myapp.ts": `import {x} from "mod"` }); @@ -358,7 +358,7 @@ export = C; }); it("should find file referenced via absolute and relative names", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c.ts": `/// `, "/a/b/b.ts": "var x" }); @@ -409,7 +409,7 @@ export = C; } it("should succeed when the same file is referenced using absolute and relative names", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); @@ -417,7 +417,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); @@ -425,7 +425,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (imports)", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" }); @@ -433,7 +433,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (imports, relative module names)", () => { - const files = createMap({ + const files = createMapFromTemplate({ "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" }); @@ -441,7 +441,7 @@ export = C; }); it("should fail when two files exist on disk that differs only in casing", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" @@ -450,7 +450,7 @@ export = C; }); it("should fail when module name in 'require' calls has inconsistent casing", () => { - const files = createMap({ + const files = createMapFromTemplate({ "moduleA.ts": `import a = require("./ModuleC")`, "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" @@ -459,7 +459,7 @@ export = C; }); it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/B/c/moduleA.ts": `import a = require("./ModuleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -471,7 +471,7 @@ import b = require("./moduleB"); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); }); it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => { - const files = createMap({ + const files = createMapFromTemplate({ "/a/B/c/moduleA.ts": `import a = require("./moduleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 8a89b8d27c6a9..d13dd7a26fa34 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -322,7 +322,7 @@ namespace ts { const options: CompilerOptions = { target }; const program_1 = newProgram(files, ["a.ts"], options); - checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") })); + checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") })); checkResolvedModulesCache(program_1, "b.ts", undefined); const program_2 = updateProgram(program_1, ["a.ts"], options, files => { @@ -331,7 +331,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") })); + checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") })); checkResolvedModulesCache(program_1, "b.ts", undefined); // imports has changed - program is not reused @@ -348,7 +348,7 @@ namespace ts { files[0].text = files[0].text.updateImportsAndExports(newImports); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": createResolvedModule("b.ts"), "c": undefined })); + checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined })); }); it("resolved type directives cache follows type directives", () => { @@ -359,7 +359,7 @@ namespace ts { const options: CompilerOptions = { target, typeRoots: ["/types"] }; const program_1 = newProgram(files, ["/a.ts"], options); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); const program_2 = updateProgram(program_1, ["/a.ts"], options, files => { @@ -368,7 +368,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); // type reference directives has changed - program is not reused @@ -386,7 +386,7 @@ namespace ts { files[0].text = files[0].text.updateReferences(newReferences); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); }); it("can reuse ambient module declarations from non-modified files", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 8153804a7bdaf..25d58142a066e 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -51,7 +51,7 @@ namespace ts.server { } const compilerOptionConverters = prepareConvertersForEnumLikeCompilerOptions(optionDeclarations); - const indentStyle = createMap({ + const indentStyle = createMapFromTemplate({ "none": IndentStyle.None, "block": IndentStyle.Block, "smart": IndentStyle.Smart diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index e587a14b459f8..248ceef1bd355 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -76,7 +76,7 @@ namespace ts.JsTyping { if (!safeList) { const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - safeList = result.config ? createMap(result.config) : EmptySafeList; + safeList = result.config ? createMapFromTemplate(result.config) : EmptySafeList; } const filesToWatch: string[] = []; diff --git a/src/services/transpile.ts b/src/services/transpile.ts index b72399384b7db..0b90e9d030b72 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -63,7 +63,7 @@ } if (transpileOptions.renamedDependencies) { - sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); + sourceFile.renamedDependencies = createMapFromTemplate(transpileOptions.renamedDependencies); } const newLine = getNewLineCharacter(options); From b98e82e5c4f1172309546e51d9b871aa2e0c1dc7 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 12 Jan 2017 12:25:00 -0800 Subject: [PATCH 19/20] Fix one more use of `createMapFromTemplate` --- src/server/typingsInstaller/nodeTypingsInstaller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index 19f794ad57ca2..ff20e89e2d7ea 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -46,7 +46,7 @@ namespace ts.server.typingsInstaller { } try { const content = JSON.parse(host.readFile(typesRegistryFilePath)); - return createMap(content.entries); + return createMapFromTemplate(content.entries); } catch (e) { if (log.isEnabled()) { From 6b6c34bef14ff50733c479b52cdb4b27c20c981d Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 12 Jan 2017 13:56:45 -0800 Subject: [PATCH 20/20] Fix typo --- src/compiler/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 02cc2b4a45357..654b1bc291273 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -895,7 +895,7 @@ namespace ts { return undefined; } - /** `forEachInMap` for just keys. */ + /** `forEachEntry` for just keys. */ export function forEachKey(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined { const iterator = map.keys(); for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) {