diff --git a/Jakefile.js b/Jakefile.js index c62a4ffd6ce0e..f9091d441933d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -55,6 +55,7 @@ function measure(marker) { } var compilerSources = [ + "dataStructures.ts", "core.ts", "performance.ts", "sys.ts", @@ -89,6 +90,7 @@ var compilerSources = [ }); var servicesSources = [ + "dataStructures.ts", "core.ts", "performance.ts", "sys.ts", diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index 431cf46018050..4451f044a0152 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]; + ts._s(nameMap, 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(ts._g(nameMap, 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(ts._g(nameMap, name)); result += '\r\n "' + createKey(propName, diagnosticDetails.code) + '"' + ' : "' + name.replace(/[\"]/g, '\\"') + '"'; if (i !== names.length - 1) { diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 49434e80abab4..3acb430627981 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -126,7 +126,7 @@ namespace ts { let symbolCount = 0; let Symbol: { new (flags: SymbolFlags, name: string): Symbol }; - let classifiableNames: Map; + let classifiableNames: Set; const unreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; const reportedUnreachableFlow: FlowNode = { flags: FlowFlags.Unreachable }; @@ -140,7 +140,7 @@ namespace ts { options = opts; languageVersion = getEmitScriptTarget(options); inStrictMode = !!file.externalModuleIndicator; - classifiableNames = createMap(); + classifiableNames = createSet(); symbolCount = 0; skipTransformFlagAggregation = isDeclarationFile(file); @@ -332,17 +332,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 = _getOrUpdate(symbolTable, name, name => createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { - classifiableNames[name] = name; + _add(classifiableNames, 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 = _s(symbolTable, name, createSymbol(SymbolFlags.None, name)); } else { if (node.name) { @@ -1444,7 +1444,7 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); typeLiteralSymbol.members = createMap(); - typeLiteralSymbol.members[symbol.name] = symbol; + _s(typeLiteralSymbol.members, symbol.name, symbol); } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1475,9 +1475,9 @@ namespace ts { ? ElementKind.Property : ElementKind.Accessor; - const existingKind = seen[identifier.text]; + const existingKind = _g(seen, identifier.text); if (!existingKind) { - seen[identifier.text] = currentKind; + _s(seen, identifier.text, currentKind); continue; } @@ -2049,7 +2049,7 @@ namespace ts { constructorFunction.parent = classPrototype; classPrototype.parent = leftSideOfAssignment; - const funcSymbol = container.locals[constructorFunction.text]; + const funcSymbol = _g(container.locals, constructorFunction.text); if (!funcSymbol || !(funcSymbol.flags & SymbolFlags.Function || isDeclarationOfFunctionExpression(funcSymbol))) { return; } @@ -2089,7 +2089,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; + _add(classifiableNames, node.name.text); } } @@ -2105,14 +2105,15 @@ 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 = _g(symbol.exports, prototypeSymbol.name); + if (symbolExport) { if (node.name) { node.name.parent = node; } - file.bindDiagnostics.push(createDiagnosticForNode(symbol.exports[prototypeSymbol.name].declarations[0], + file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, prototypeSymbol.name)); } - symbol.exports[prototypeSymbol.name] = prototypeSymbol; + _s(symbol.exports, prototypeSymbol.name, prototypeSymbol); prototypeSymbol.parent = symbol; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 61cf701eb953b..3907f267878d6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -291,7 +291,7 @@ namespace ts { NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts = createMap({ + const typeofEQFacts = createMapFromMapLike({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -301,7 +301,7 @@ namespace ts { "function": TypeFacts.TypeofEQFunction }); - const typeofNEFacts = createMap({ + const typeofNEFacts = createMapFromMapLike({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -311,7 +311,7 @@ namespace ts { "function": TypeFacts.TypeofNEFunction }); - const typeofTypesByName = createMap({ + const typeofTypesByName = createMapFromMapLike({ "string": stringType, "number": numberType, "boolean": booleanType, @@ -351,7 +351,7 @@ namespace ts { } const builtinGlobals = createMap(); - builtinGlobals[undefinedSymbol.name] = undefinedSymbol; + _s(builtinGlobals, undefinedSymbol.name, undefinedSymbol); initializeTypeChecker(); @@ -456,18 +456,18 @@ namespace ts { } function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { - for (const id in source) { - let targetSymbol = target[id]; + _each(source, (id, sourceSymbol) => { + let targetSymbol = _g(target, id); if (!targetSymbol) { - target[id] = source[id]; + _s(target, id, sourceSymbol); } else { if (!(targetSymbol.flags & SymbolFlags.Merged)) { - target[id] = targetSymbol = cloneSymbol(targetSymbol); + _s(target, id, targetSymbol = cloneSymbol(targetSymbol)); } - mergeSymbol(targetSymbol, source[id]); + mergeSymbol(targetSymbol, sourceSymbol); } - } + }); } function mergeModuleAugmentation(moduleName: LiteralExpression): void { @@ -508,15 +508,16 @@ namespace ts { } function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { - for (const id in source) { - if (target[id]) { + _each(source, (id, sourceSymbol) => { + const symbol = _g(target, id); + if (symbol) { // Error on redeclarations - forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); + forEach(symbol.declarations, addDeclarationDiagnostic(id, message)); } else { - target[id] = source[id]; + _s(target, id, sourceSymbol); } - } + }); function addDeclarationDiagnostic(id: string, message: DiagnosticMessage) { return (declaration: Declaration) => diagnostics.add(createDiagnosticForNode(declaration, message, id)); @@ -540,7 +541,7 @@ namespace ts { function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { if (meaning) { - const symbol = symbols[name]; + const symbol = _g(symbols, name); if (symbol) { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); if (symbol.flags & meaning) { @@ -721,7 +722,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 = _g(moduleExports, "default")) { const localSymbol = getLocalSymbolForExportDefault(result); if (localSymbol && (result.flags & meaning) && localSymbol.name === name) { break loop; @@ -740,9 +741,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 = _g(moduleExports, name); + if (moduleExport && + moduleExport.flags === SymbolFlags.Alias && + getDeclarationOfKind(moduleExport, SyntaxKind.ExportSpecifier)) { break; } } @@ -1057,11 +1059,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 = _g(moduleSymbol.exports, "export="); + exportDefaultSymbol = exportValue + ? getPropertyOfType(getTypeOfSymbol(exportValue), "default") + : resolveSymbol(_g(moduleSymbol.exports, "default")); + } if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); @@ -1111,7 +1118,7 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exportedSymbol = getExportsOfSymbol(symbol)[name]; + const exportedSymbol = _g(getExportsOfSymbol(symbol), name); if (exportedSymbol) { return resolveSymbol(exportedSymbol); } @@ -1139,7 +1146,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 && _g(moduleSymbol.exports, "export=")) { symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { @@ -1399,7 +1406,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(_g(moduleSymbol.exports, "export="))) || moduleSymbol; } // An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export =' @@ -1415,7 +1422,7 @@ namespace ts { } function hasExportAssignmentSymbol(moduleSymbol: Symbol): boolean { - return moduleSymbol.exports["export="] !== undefined; + return _g(moduleSymbol.exports, "export=") !== undefined; } function getExportsOfModuleAsArray(moduleSymbol: Symbol): Symbol[] { @@ -1441,24 +1448,28 @@ 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; + + _each(source, (id, sourceSymbol) => { + const targetSymbol = _g(target, id); + if (id !== "default" && !targetSymbol) { + _s(target, id, sourceSymbol); if (lookupTable && exportNode) { - lookupTable[id] = { + _s(lookupTable, 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 && id !== "default" && targetSymbol && resolveSymbol(targetSymbol) !== resolveSymbol(sourceSymbol)) { + const bar = _g(lookupTable, id); //name + if (!bar.exportsWithDuplicate) { + bar.exportsWithDuplicate = [exportNode]; } else { - lookupTable[id].exportsWithDuplicate.push(exportNode); + bar.exportsWithDuplicate.push(exportNode); } } - } + }); } function getExportsForModule(moduleSymbol: Symbol): SymbolTable { @@ -1474,7 +1485,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 = _g(symbol.exports, "__export"); if (exportStars) { const nestedSymbols = createMap(); const lookupTable = createMap(); @@ -1488,21 +1499,20 @@ namespace ts { node as ExportDeclaration ); } - for (const id in lookupTable) { - const { exportsWithDuplicate } = lookupTable[id]; + _each(lookupTable, (id, { exportsWithDuplicate }) => { // 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) || _g(symbols, 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, + _g(lookupTable, id).specifierText, id )); } - } + }); extendExportSymbols(symbols, nestedSymbols); } return symbols; @@ -1596,15 +1606,14 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; - for (const id in members) { + _each(members, (id, symbol) => { if (!isReservedMemberName(id)) { if (!result) result = []; - const symbol = members[id]; if (symbolIsValue(symbol)) { result.push(symbol); } } - } + }); return result || emptyArray; } @@ -1677,12 +1686,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols[symbol.name])) { + if (isAccessible(_g(symbols, symbol.name))) { return [symbol]; } // Check if symbol is any of the alias - return forEachProperty(symbols, symbolFromSymbolTable => { + return _findMapValue(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -1717,7 +1726,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 = _g(symbolTable, symbol.name); if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; @@ -2449,7 +2458,7 @@ namespace ts { } writeIndexSignature(resolved.stringIndexInfo, SyntaxKind.StringKeyword); writeIndexSignature(resolved.numberIndexInfo, SyntaxKind.NumberKeyword); - for (const p of resolved.properties) { + for (const p of sortInV8ObjectInsertionOrder(resolved.properties, p => p.name)) { const t = getTypeOfSymbol(p); if (p.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(t).length) { const signatures = getSignaturesOfType(t, SignatureKind.Call); @@ -3171,7 +3180,7 @@ namespace ts { const symbol = createSymbol(flags, text); symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors); symbol.bindingElement = e; - members[symbol.name] = symbol; + _s(members, symbol.name, symbol); }); const result = createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); if (includePatternInType) { @@ -3762,7 +3771,7 @@ namespace ts { type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; (type).instantiations = createMap(); - (type).instantiations[getTypeListId(type.typeParameters)] = type; + _s((type).instantiations, getTypeListId(type.typeParameters), type); (type).target = type; (type).typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter | TypeFlags.ThisType); @@ -3804,7 +3813,7 @@ namespace ts { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. links.instantiations = createMap(); - links.instantiations[getTypeListId(links.typeParameters)] = type; + _s(links.instantiations, getTypeListId(links.typeParameters), type); } } else { @@ -3824,7 +3833,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 && !!_g(symbol.exports, (expr).text); } function enumHasLiteralMembers(symbol: Symbol) { @@ -3862,8 +3871,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 (!_getWakka(memberTypes, value)) { + const memberType = _setWakka(memberTypes, value, createEnumLiteralType(memberSymbol, enumType, "" + value)); memberTypeList.push(memberType); } } @@ -3873,7 +3882,7 @@ namespace ts { if (memberTypeList.length > 1) { enumType.flags |= TypeFlags.Union; (enumType).types = memberTypeList; - unionTypes[getTypeListId(memberTypeList)] = enumType; + _s(unionTypes, getTypeListId(memberTypeList), enumType); } } } @@ -3885,7 +3894,7 @@ namespace ts { if (!links.declaredType) { const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol)); links.declaredType = enumType.flags & TypeFlags.Union ? - enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] : + _getWakka(enumType.memberTypes, getEnumMemberValue(symbol.valueDeclaration)) : enumType; } return links.declaredType; @@ -4017,7 +4026,7 @@ namespace ts { function createSymbolTable(symbols: Symbol[]): SymbolTable { const result = createMap(); for (const symbol of symbols) { - result[symbol.name] = symbol; + _s(result, symbol.name, symbol); } return result; } @@ -4027,15 +4036,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); + _s(result, symbol.name, mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper)); } return result; } function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!symbols[s.name]) { - symbols[s.name] = s; + if (!_g(symbols, s.name)) { + _s(symbols, s.name, s); } } } @@ -4044,8 +4053,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(_g(symbol.members, "__call")); + (type).declaredConstructSignatures = getSignaturesOfSymbol(_g(symbol.members, "__new")); (type).declaredStringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); (type).declaredNumberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); } @@ -4285,8 +4294,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(_g(members, "__call")); + const constructSignatures = getSignaturesOfSymbol(_g(members, "__new")); const stringIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.String); const numberIndexInfo = getIndexInfoOfSymbol(symbol, IndexKind.Number); setObjectTypeMembers(type, members, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); @@ -4300,7 +4309,7 @@ namespace ts { } if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); - constructSignatures = getSignaturesOfSymbol(symbol.members["__constructor"]); + constructSignatures = getSignaturesOfSymbol(_g(symbol.members, "__constructor")); if (!constructSignatures.length) { constructSignatures = getDefaultConstructSignatures(classType); } @@ -4356,7 +4365,7 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = _g(resolved.members, name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4377,13 +4386,12 @@ namespace ts { const props = type.resolvedProperties; if (props) { const result: Symbol[] = []; - for (const key in props) { - const prop = props[key]; + _each(props, (key, 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; @@ -4501,11 +4509,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 = _g(properties, name); if (!property) { property = createUnionOrIntersectionProperty(type, name); if (property) { - properties[name] = property; + _s(properties, name, property); } } return property; @@ -4529,7 +4537,7 @@ namespace ts { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - const symbol = resolved.members[name]; + const symbol = _g(resolved.members, name); if (symbol && symbolIsValue(symbol)) { return symbol; } @@ -4628,11 +4636,11 @@ namespace ts { function symbolsToArray(symbols: SymbolTable): Symbol[] { const result: Symbol[] = []; - for (const id in symbols) { + _each(symbols, (id, symbol) => { if (!isReservedMemberName(id)) { - result.push(symbols[id]); + result.push(symbol); } - } + }); return result; } @@ -4927,7 +4935,7 @@ namespace ts { } function getIndexSymbol(symbol: Symbol): Symbol { - return symbol.members["__index"]; + return _g(symbol.members, "__index"); } function getIndexDeclarationOfSymbol(symbol: Symbol, kind: IndexKind): SignatureDeclaration { @@ -5041,11 +5049,11 @@ namespace ts { function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference { const id = getTypeListId(typeArguments); - let type = target.instantiations[id]; + let type = _g(target.instantiations, id); if (!type) { const propagatedFlags = typeArguments ? getPropagatingFlagsOfTypes(typeArguments, /*excludeKinds*/ 0) : 0; const flags = TypeFlags.Reference | propagatedFlags; - type = target.instantiations[id] = createObjectType(flags, target.symbol); + type = _s(target.instantiations, id, createObjectType(flags, target.symbol)); type.target = target; type.typeArguments = typeArguments; } @@ -5098,7 +5106,7 @@ namespace ts { } const typeArguments = map(node.typeArguments, getTypeFromTypeNodeNoAlias); const id = getTypeListId(typeArguments); - return links.instantiations[id] || (links.instantiations[id] = instantiateType(type, createTypeMapper(typeParameters, typeArguments))); + return _g(links.instantiations, id) || _s(links.instantiations, id, instantiateType(type, createTypeMapper(typeParameters, typeArguments))); } if (node.typeArguments) { error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); @@ -5327,7 +5335,7 @@ namespace ts { type.outerTypeParameters = undefined; type.localTypeParameters = typeParameters; type.instantiations = createMap(); - type.instantiations[getTypeListId(type.typeParameters)] = type; + _s(type.instantiations, getTypeListId(type.typeParameters), type); type.target = type; type.typeArguments = type.typeParameters; type.thisType = createType(TypeFlags.TypeParameter | TypeFlags.ThisType); @@ -5510,10 +5518,10 @@ namespace ts { return types[0]; } const id = getTypeListId(types); - let type = unionTypes[id]; + let type = _g(unionTypes, id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags); + type = _s(unionTypes, id, createObjectType(TypeFlags.Union | propagatedFlags)); type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5567,10 +5575,10 @@ namespace ts { return typeSet[0]; } const id = getTypeListId(typeSet); - let type = intersectionTypes[id]; + let type = _g(intersectionTypes, id); if (!type) { const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); - type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | propagatedFlags); + type = _s(intersectionTypes, id, createObjectType(TypeFlags.Intersection | propagatedFlags)); type.types = typeSet; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; @@ -5622,7 +5630,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 _g(map, text) || _s(map, text, createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -6301,13 +6309,14 @@ namespace ts { return true; } const id = source.id + "," + target.id; - if (enumRelation[id] !== undefined) { - return enumRelation[id]; + const relation = _g(enumRelation, 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 _s(enumRelation, id, false); } const targetEnumType = getTypeOfSymbol(target.symbol); for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) { @@ -6318,11 +6327,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 _s(enumRelation, id, false); } } } - return enumRelation[id] = true; + return _s(enumRelation, id, true); } function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) { @@ -6365,7 +6374,7 @@ namespace ts { } if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) { const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id; - const related = relation[id]; + const related = _g(relation, id); if (related !== undefined) { return related === RelationComparisonResult.Succeeded; } @@ -6762,12 +6771,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 = _g(relation, id); if (related !== undefined) { if (reportErrors && related === RelationComparisonResult.Failed) { // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported // failure and continue computing the relation such that errors get reported. - relation[id] = RelationComparisonResult.FailedAndReported; + _s(relation, id, RelationComparisonResult.FailedAndReported); } else { return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False; @@ -6776,7 +6785,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 (_g(maybeStack[i], id)) { return Ternary.Maybe; } } @@ -6794,7 +6803,7 @@ namespace ts { sourceStack[depth] = source; targetStack[depth] = target; maybeStack[depth] = createMap(); - maybeStack[depth][id] = RelationComparisonResult.Succeeded; + _s(maybeStack[depth], id, RelationComparisonResult.Succeeded); depth++; const saveExpandingFlags = expandingFlags; if (!(expandingFlags & 1) && isDeeplyNestedGeneric(source, sourceStack, depth)) expandingFlags |= 1; @@ -6824,12 +6833,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); + copyMapPropertiesFromTo(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; + _s(relation, id, reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed); } return result; } @@ -7018,14 +7027,26 @@ namespace ts { return result; } - function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean): Ternary { + //document the new parameter + function eachPropertyRelatedTo(source: Type, target: Type, kind: IndexKind, reportErrors: boolean, redoingInV8ObjectInsertionOrder?: boolean): Ternary { let result = Ternary.True; - for (const prop of getPropertiesOfObjectType(source)) { + let properties = getPropertiesOfObjectType(source); + if (redoingInV8ObjectInsertionOrder) { + properties = sortInV8ObjectInsertionOrder(properties, prop => prop.name); + } + for (const prop of properties) { if (kind === IndexKind.String || isNumericLiteralName(prop.name)) { - const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors); + const related = isRelatedTo(getTypeOfSymbol(prop), target, reportErrors && redoingInV8ObjectInsertionOrder); if (!related) { if (reportErrors) { - reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); + // For consistency, if we report errors we make sure to report the first error in V8's object insertion order. + if (!redoingInV8ObjectInsertionOrder) { + const related = eachPropertyRelatedTo(source, target, kind, reportErrors, /*redoingInV8ObjectInsertionOrder*/ true); + Debug.assert(related === Ternary.False); + } + else { + reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); + } } return Ternary.False; } @@ -7468,7 +7489,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); + _s(members, property.name, updated === original ? property : createTransientSymbol(property, updated)); }; return members; } @@ -7686,7 +7707,7 @@ namespace ts { let targetStack: Type[]; let depth = 0; let inferiority = 0; - const visited = createMap(); + const visited = createSet(); inferFromTypes(originalSource, originalTarget); function isInProcess(source: Type, target: Type) { @@ -7822,10 +7843,10 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (visited[key]) { + if (_setHas(visited, key)) { return; } - visited[key] = true; + _add(visited, key); if (depth === 0) { sourceStack = []; targetStack = []; @@ -8176,7 +8197,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)); + _g(resolved.members, "bind") && isTypeSubtypeOf(type, globalFunctionType)); } function getTypeFacts(type: Type): TypeFacts { @@ -8593,8 +8614,9 @@ namespace ts { if (!key) { key = getFlowCacheKey(reference); } - if (cache[key]) { - return cache[key]; + const cached = _g(cache, 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. @@ -8624,8 +8646,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 = _g(cache, key); + if (cached) { + return cached; } if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); @@ -8649,7 +8672,7 @@ namespace ts { if (isIncomplete(firstAntecedentType)) { return createFlowType(result, /*incomplete*/ true); } - return cache[key] = result; + return _s(cache, key, result); } function isMatchingReferenceDiscriminant(expr: Expression) { @@ -8772,14 +8795,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 = _g(typeofTypesByName, literal.text); if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : - typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; + _g(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : + _g(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } @@ -10347,7 +10370,7 @@ namespace ts { } } else { - propertiesTable[member.name] = member; + _s(propertiesTable, member.name, member); } propertiesArray.push(member); } @@ -10356,12 +10379,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 (!_g(propertiesTable, prop.name)) { if (!(prop.flags & SymbolFlags.Optional)) { error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); } - propertiesTable[prop.name] = prop; + _s(propertiesTable, prop.name, prop); propertiesArray.push(prop); } } @@ -10434,7 +10457,7 @@ namespace ts { } } - function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Map) { + function checkJsxAttribute(node: JsxAttribute, elementAttributesType: Type, nameTable: Set) { let correspondingPropType: Type = undefined; // Look up the corresponding property for this attribute @@ -10473,34 +10496,35 @@ namespace ts { checkTypeAssignableTo(exprType, correspondingPropType, node); } - nameTable[node.name.text] = true; + _add(nameTable, node.name.text); return exprType; } - function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Map) { + function checkJsxSpreadAttribute(node: JsxSpreadAttribute, elementAttributesType: Type, nameTable: Set) { const type = checkExpression(node.expression); const props = getPropertiesOfType(type); for (const prop of props) { // Is there a corresponding property in the element attributes type? Skip checking of properties // that have already been assigned to, as these are not actually pushed into the resulting type - if (!nameTable[prop.name]) { + if (!_setHas(nameTable, prop.name)) { const targetPropSym = getPropertyOfType(elementAttributesType, prop.name); if (targetPropSym) { const msg = chainDiagnosticMessages(undefined, Diagnostics.Property_0_of_JSX_spread_attribute_is_not_assignable_to_target_property, prop.name); checkTypeAssignableTo(getTypeOfSymbol(prop), getTypeOfSymbol(targetPropSym), node, undefined, msg); } - nameTable[prop.name] = true; + _add(nameTable, prop.name); } } return type; } function getJsxType(name: string) { - if (jsxTypes[name] === undefined) { - return jsxTypes[name] = getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType; + const jsxType = _g(jsxTypes, name); + if (jsxType === undefined) { + return _s(jsxTypes, name, getExportedTypeFromNamespace(JsxNames.JSX, name) || unknownType); } - return jsxTypes[name]; + return jsxType; } /** @@ -10805,7 +10829,7 @@ namespace ts { const targetAttributesType = getJsxElementAttributesType(node); - const nameTable = createMap(); + const nameTable = createSet(); // Process this array in right-to-left order so we know which // attributes (mostly from spreads) are being overwritten and // thus should have their types ignored @@ -10829,7 +10853,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - !nameTable[targetProperties[i].name]) { + !_setHas(nameTable, targetProperties[i].name)) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } @@ -14270,23 +14294,23 @@ namespace ts { } function addName(names: Map, location: Node, name: string, meaning: Accessor) { - const prev = names[name]; + const prev = _g(names, name); if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } else { - names[name] = prev | meaning; + _s(names, name, prev | meaning); } } else { - names[name] = meaning; + _s(names, name, meaning); } } } function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names = createMap(); + const names = createSet(); for (const member of node.members) { if (member.kind == SyntaxKind.PropertySignature) { let memberName: string; @@ -14300,12 +14324,12 @@ namespace ts { continue; } - if (names[memberName]) { + if (_setHas(names, memberName)) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } else { - names[memberName] = true; + _add(names, memberName); } } } @@ -15509,8 +15533,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]; + _eachValue(node.locals, local => { if (!local.isReferenced) { if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { const parameter = local.valueDeclaration; @@ -15525,7 +15548,7 @@ namespace ts { forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); } } - } + }); } } @@ -15579,8 +15602,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]; + _eachValue(node.locals, local => { if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { @@ -15588,7 +15610,7 @@ namespace ts { } } } - } + }); } } @@ -16599,7 +16621,7 @@ namespace ts { const identifierName = (catchClause.variableDeclaration.name).text; const locals = catchClause.block.locals; if (locals) { - const localSymbol = locals[identifierName]; + const localSymbol = _g(locals, identifierName); if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName); } @@ -17020,15 +17042,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 => { _s(seen, p.name, { prop: p, containingType: type }); }); let ok = true; for (const base of baseTypes) { const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { - const existing = seen[prop.name]; + const existing = _g(seen, prop.name); if (!existing) { - seen[prop.name] = { prop: prop, containingType: base }; + _s(seen, prop.name, { prop: prop, containingType: base }); } else { const isInheritedProperty = existing.containingType !== type; @@ -17764,19 +17786,14 @@ namespace ts { } function hasExportedMembers(moduleSymbol: Symbol) { - for (const id in moduleSymbol.exports) { - if (id !== "export=") { - return true; - } - } - return false; + return _someKey(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 = _g(moduleSymbol.exports, "export="); if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) { const declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration; if (!isTopLevelInExternalModuleAugmentation(declaration)) { @@ -17785,21 +17802,20 @@ namespace ts { } // Checks for export * conflicts const exports = getExportsOfModule(moduleSymbol); - for (const id in exports) { + _each(exports, (id, { declarations, flags }) => { 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) { @@ -17808,7 +17824,7 @@ namespace ts { } } } - } + }); links.exportsChecked = true; } @@ -18161,18 +18177,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 (!_g(symbols, id)) { + _s(symbols, id, symbol); } } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { if (meaning) { - for (const id in source) { - const symbol = source[id]; + _each(source, (id, symbol) => { copySymbol(symbol, meaning); - } + }); } } } @@ -18580,8 +18595,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 (!_g(propsByName, p.name)) { + _s(propsByName, p.name, p); } }); } @@ -18644,7 +18659,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachProperty(getExportsOfModule(moduleSymbol), isValue); + : _someValue(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; @@ -18994,7 +19009,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return !!globals[name]; + return !!_g(globals, name); } function getReferencedValueSymbol(reference: Identifier, startInDeclarationContainer?: boolean): Symbol { @@ -19051,14 +19066,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]; + _each(resolvedTypeReferenceDirectives, (key, resolvedDirective) => { if (!resolvedDirective) { - continue; + return; } const file = host.getSourceFile(resolvedDirective.resolvedFileName); fileToDirective.set(file.path, key); - } + }); } return { getReferencedExportContainer, @@ -19199,11 +19213,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]; + _each(source, (id, sourceSymbol) => { + if (!_has(globals, id)) { + _s(globals, id, sourceSymbol); } - } + }); } if ((compilerOptions.isolatedModules || isExternalModule(file)) && !file.isDeclarationFile) { const fileRequestedExternalEmitHelpers = file.flags & NodeFlags.EmitHelperFlags; @@ -19964,17 +19978,17 @@ namespace ts { continue; } - if (!seen[effectiveName]) { - seen[effectiveName] = currentKind; + const existingKind = _g(seen, effectiveName); + if (!existingKind) { + _s(seen, effectiveName, currentKind); } else { - const existingKind = seen[effectiveName]; if (currentKind === Property && existingKind === Property) { grammarErrorOnNode(name, Diagnostics.Duplicate_identifier_0, getTextOfNode(name)); } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { - seen[effectiveName] = currentKind | existingKind; + _s(seen, effectiveName, currentKind | existingKind); } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); @@ -19988,7 +20002,7 @@ namespace ts { } function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - const seen = createMap(); + const seen = createSet(); for (const attr of node.attributes) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; @@ -19996,8 +20010,8 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!seen[name.text]) { - seen[name.text] = true; + if (!_setHas(seen, name.text)) { + _add(seen, name.text); } else { return grammarErrorOnNode(name, Diagnostics.JSX_elements_cannot_have_multiple_attributes_with_the_same_name); @@ -20501,11 +20515,11 @@ namespace ts { function getAmbientModules(): Symbol[] { const result: Symbol[] = []; - for (const sym in globals) { + _each(globals, (sym, global) => { 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 c097f18eaec4b..04bbb092bee50 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -62,7 +62,7 @@ namespace ts { }, { name: "jsx", - type: createMap({ + type: createMapFromMapLike({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React }), @@ -92,7 +92,7 @@ namespace ts { { name: "module", shortName: "m", - type: createMap({ + type: createMapFromMapLike({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -106,7 +106,7 @@ namespace ts { }, { name: "newLine", - type: createMap({ + type: createMapFromMapLike({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed }), @@ -255,7 +255,7 @@ namespace ts { { name: "target", shortName: "t", - type: createMap({ + type: createMapFromMapLike({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6, @@ -289,7 +289,7 @@ namespace ts { }, { name: "moduleResolution", - type: createMap({ + type: createMapFromMapLike({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, }), @@ -398,7 +398,7 @@ namespace ts { type: "list", element: { name: "lib", - type: createMap({ + type: createMapFromMapLike({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -493,9 +493,9 @@ namespace ts { const optionNameMap = createMap(); const shortOptionNames = createMap(); forEach(optionDeclarations, option => { - optionNameMap[option.name.toLowerCase()] = option; + _s(optionNameMap, option.name.toLowerCase(), option); if (option.shortName) { - shortOptionNames[option.shortName] = option.name; + _s(shortOptionNames, option.shortName, option.name); } }); @@ -506,9 +506,7 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { const namesOfType: string[] = []; - for (const key in opt.type) { - namesOfType.push(` '${key}'`); - } + _eachKey(opt.type, key => namesOfType.push(` '${key}'`)); return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -516,8 +514,8 @@ namespace ts { export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = trimString((value || "")).toLowerCase(); const map = opt.type; - if (key in map) { - return map[key]; + if (_has(map, key)) { + return _g(map, key); } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -570,12 +568,12 @@ 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]; + if (_has(shortOptionNames, s)) { + s = _g(shortOptionNames, s); } - if (s in optionNameMap) { - const opt = optionNameMap[s]; + if (_has(optionNameMap, s)) { + const opt = _g(optionNameMap, s); if (opt.isTSConfigOnly) { errors.push(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_specified_in_tsconfig_json_file, opt.name)); @@ -693,7 +691,9 @@ namespace ts { * @param fileNames array of filenames to be generated into tsconfig.json */ /* @internal */ - export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: Map } { + //I changed this to return MapLike instead of Map... + //return type is still wrong since it has 'files'... + export function generateTSConfig(options: CompilerOptions, fileNames: string[]): { compilerOptions: MapLike } { const compilerOptions = extend(options, defaultInitCompilerOptions); const configurations: any = { compilerOptions: serializeCompilerOptions(compilerOptions) @@ -718,17 +718,17 @@ namespace ts { } } - function getNameOfCompilerOptionValue(value: CompilerOptionsValue, customTypeMap: MapLike): string | undefined { + //Changed this from MapLike to Map, because it's only used with Maps + 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 _find(customTypeMap, (key, customValue) => { + if (customValue === value) { return key; } - } - return undefined; + }); } - function serializeCompilerOptions(options: CompilerOptions): Map { + function serializeCompilerOptions(options: CompilerOptions): MapLike { const result = createMap(); const optionsNameMap = getOptionNameMap().optionNameMap; @@ -745,13 +745,13 @@ namespace ts { break; default: const value = options[name]; - let optionDefinition = optionsNameMap[name.toLowerCase()]; + let optionDefinition = _g(optionsNameMap, name.toLowerCase()); if (optionDefinition) { const customTypeMap = getCustomTypeMapOfCommandLineOption(optionDefinition); if (!customTypeMap) { // There is no map associated with this compiler option then use the value as-is // This is the case if the value is expect to be string, number, boolean or list of string - result[name] = value; + _s(result, name, value); } else { if (optionDefinition.type === "list") { @@ -759,11 +759,11 @@ namespace ts { for (const element of value as (string | number)[]) { convertedValue.push(getNameOfCompilerOptionValue(element, customTypeMap)); } - result[name] = convertedValue; + _s(result, name, convertedValue); } else { // There is a typeMap associated with this command-line option so use it to map value back to its name - result[name] = getNameOfCompilerOptionValue(value, customTypeMap); + _s(result, name, getNameOfCompilerOptionValue(value, customTypeMap)); } } } @@ -771,7 +771,7 @@ namespace ts { } } } - return result; + return _toMapLike(result); } } @@ -983,8 +983,8 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - if (id in optionNameMap) { - const opt = optionNameMap[id]; + if (_has(optionNameMap, id)) { + const opt = _g(optionNameMap, id); defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } else { @@ -1020,8 +1020,8 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - if (key in opt.type) { - return opt.type[key]; + if (_has(opt.type, key)) { + return _g(opt.type, key); } else { errors.push(createCompilerDiagnosticForInvalidCustomType(opt)); @@ -1160,7 +1160,7 @@ namespace ts { if (fileNames) { for (const fileName of fileNames) { const file = combinePaths(basePath, fileName); - literalFileMap[keyMapper(file)] = file; + _s(literalFileMap, keyMapper(file), file); } } @@ -1183,8 +1183,8 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!(key in literalFileMap) && !(key in wildcardFileMap)) { - wildcardFileMap[key] = file; + if (!_has(literalFileMap, key) && !_has(wildcardFileMap, key)) { + _s(wildcardFileMap, key, file); } } } @@ -1194,7 +1194,7 @@ namespace ts { wildcardFiles.sort(host.useCaseSensitiveFileNames ? compareStrings : compareStringsCaseInsensitive); return { fileNames: literalFiles.concat(wildcardFiles), - wildcardDirectories + wildcardDirectories: _toMapLike(wildcardDirectories) }; } @@ -1248,9 +1248,9 @@ namespace ts { if (match) { const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase(); const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None; - const existingFlags = wildcardDirectories[key]; + const existingFlags = _g(wildcardDirectories, key); if (existingFlags === undefined || existingFlags < flags) { - wildcardDirectories[key] = flags; + _s(wildcardDirectories, key, flags); if (flags === WatchDirectoryFlags.Recursive) { recursiveKeys.push(key); } @@ -1259,13 +1259,13 @@ namespace ts { } // Remove any subpaths under an existing recursively watched directory. - for (const key in wildcardDirectories) { + _eachKey(wildcardDirectories, key => { for (const recursiveKey of recursiveKeys) { if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { - delete wildcardDirectories[key]; + _delete(wildcardDirectories, key); } } - } + }); } return wildcardDirectories; @@ -1285,7 +1285,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 (_has(literalFiles, higherPriorityPath) || _has(wildcardFiles, higherPriorityPath)) { return true; } } @@ -1307,7 +1307,7 @@ namespace ts { for (let i = nextExtensionPriority; i < extensions.length; i++) { const lowerPriorityExtension = extensions[i]; const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension)); - delete wildcardFiles[lowerPriorityPath]; + _delete(wildcardFiles, lowerPriorityPath); } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 72d05e1cfd7c5..5c550dca1d5b9 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1,3 +1,4 @@ +/// /// /// @@ -19,26 +20,6 @@ namespace ts { True = -1 } - const createObject = Object.create; - - export function createMap(template?: MapLike): Map { - const map: Map = createObject(null); // tslint:disable-line:no-null-keyword - - // Using 'delete' on an object causes V8 to put the object in dictionary mode. - // This disables creation of hidden classes, which are expensive when an object is - // constantly changing shape. - map["__"] = undefined; - delete map["__"]; - - // Copies keys/values from template. Note that for..in will not throw if - // template is undefined, and instead will just exit the loop. - for (const key in template) if (hasOwnProperty.call(template, key)) { - map[key] = template[key]; - } - - return map; - } - export function createFileMap(keyMapper?: (key: string) => string): FileMap { let files = createMap(); return { @@ -51,27 +32,25 @@ namespace ts { }; function forEachValueInMap(f: (key: Path, value: T) => void) { - for (const key in files) { - f(key, files[key]); - } + _each(files, f); } // path should already be well-formed so it does not need to be normalized function get(path: Path): T { - return files[toKey(path)]; + return _g(files, toKey(path)); } function set(path: Path, value: T) { - files[toKey(path)] = value; + _s(files, toKey(path), value); } function contains(path: Path) { - return toKey(path) in files; + return _has(files, toKey(path)); } function remove(path: Path) { const key = toKey(path); - delete files[key]; + _delete(files, key); } function clear() { @@ -377,6 +356,7 @@ namespace ts { return result; } + //Is this used??? export function mapObject(object: MapLike, f: (key: string, x: T) => [string, U]): MapLike { let result: MapLike; if (object) { @@ -570,242 +550,6 @@ namespace ts { return initial; } - const hasOwnProperty = Object.prototype.hasOwnProperty; - - /** - * Indicates whether a map-like contains an own property with the specified key. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * the 'in' operator. - * - * @param map A map-like. - * @param key A property key. - */ - export function hasProperty(map: MapLike, key: string): boolean { - return hasOwnProperty.call(map, key); - } - - /** - * Gets the value of an owned property in a map-like. - * - * NOTE: This is intended for use only with MapLike objects. For Map objects, use - * an indexer. - * - * @param map A map-like. - * @param key A property key. - */ - export function getProperty(map: MapLike, key: string): T | undefined { - return hasOwnProperty.call(map, key) ? map[key] : undefined; - } - - /** - * Gets the owned, enumerable property keys of a map-like. - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * Object.keys instead as it offers better performance. - * - * @param map A map-like. - */ - export function getOwnKeys(map: MapLike): string[] { - const keys: string[] = []; - for (const key in map) if (hasOwnProperty.call(map, key)) { - keys.push(key); - } - return keys; - } - - /** - * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. - * - * @param map A map for which properties should be enumerated. - * @param callback A callback to invoke for each property. - */ - export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U { - let result: U; - for (const key in map) { - if (result = callback(map[key], key)) break; - } - return result; - } - - /** - * Returns true if a Map has some matching property. - * - * @param map A map whose properties should be tested. - * @param predicate An optional callback used to test each property. - */ - export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) { - for (const key in map) { - if (!predicate || predicate(map[key], key)) return true; - } - return false; - } - - /** - * Performs a shallow copy of the properties from a source Map to a target MapLike - * - * @param source A map from which properties should be copied. - * @param target A map to which properties should be copied. - */ - export function copyProperties(source: Map, target: MapLike): void { - for (const key in source) { - target[key] = source[key]; - } - } - - export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; - export function assign, T2>(t: T1, arg1: T2): T1 & T2; - export function assign>(t: T1, ...args: any[]): any; - export function assign>(t: T1, ...args: any[]) { - for (const arg of args) { - for (const p of getOwnKeys(arg)) { - t[p] = arg[p]; - } - } - return t; - } - - /** - * Reduce the properties of a map. - * - * NOTE: This is intended for use with Map objects. For MapLike objects, use - * reduceOwnProperties instead as it offers better runtime safety. - * - * @param map The map to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - for (const key in map) { - result = callback(result, map[key], String(key)); - } - return result; - } - - /** - * Reduce the properties defined on a map-like (but not from its prototype chain). - * - * NOTE: This is intended for use with MapLike objects. For Map objects, use - * reduceProperties instead as it offers better performance. - * - * @param map The map-like to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - for (const key in map) if (hasOwnProperty.call(map, key)) { - result = callback(result, map[key], String(key)); - } - return result; - } - - /** - * Performs a shallow equality comparison of the contents of two map-likes. - * - * @param left A map-like whose properties should be compared. - * @param right A map-like whose properties should be compared. - */ - export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { - if (left === right) return true; - if (!left || !right) return false; - for (const key in left) if (hasOwnProperty.call(left, key)) { - if (!hasOwnProperty.call(right, key) === undefined) return false; - if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; - } - for (const key in right) if (hasOwnProperty.call(right, key)) { - if (!hasOwnProperty.call(left, key)) return false; - } - return true; - } - - /** - * Creates a map from the elements of an array. - * - * @param array the array of input elements. - * @param makeKey a function that produces a key for a given element. - * - * This function makes no effort to avoid collisions; if any two elements produce - * the same key with the given 'makeKey' function, then the element with the higher - * index in the array will be the one associated with the produced key. - */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; - export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { - const result = createMap(); - for (const value of array) { - result[makeKey(value)] = makeValue ? makeValue(value) : value; - } - return result; - } - - export function isEmpty(map: Map) { - for (const id in map) { - if (hasProperty(map, id)) { - return false; - } - } - return true; - } - - export function cloneMap(map: Map) { - const clone = createMap(); - copyProperties(map, clone); - return clone; - } - - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - if (hasOwnProperty.call(object, id)) { - result[id] = (object)[id]; - } - } - return result; - } - - export function extend(first: T1 , second: T2): T1 & T2 { - const result: T1 & T2 = {}; - for (const id in second) if (hasOwnProperty.call(second, id)) { - (result as any)[id] = (second as any)[id]; - } - for (const id in first) if (hasOwnProperty.call(first, id)) { - (result as any)[id] = (first as any)[id]; - } - return result; - } - - /** - * Adds the value to an array of values associated with the key, and returns the array. - * Creates the array if it does not already exist. - */ - export function multiMapAdd(map: Map, key: string, value: V): V[] { - const values = map[key]; - if (values) { - values.push(value); - return values; - } - else { - return map[key] = [value]; - } - } - - /** - * Removes a value from an array of values associated with the key. - * Does not preserve the order of those values. - * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. - */ - export function multiMapRemove(map: Map, key: string, value: V): void { - const values = map[key]; - if (values) { - unorderedRemoveItem(values, value); - if (!values.length) { - delete map[key]; - } - } - } - /** * Tests whether a value is an array. */ @@ -833,7 +577,7 @@ namespace ts { export let localizedDiagnosticMessages: Map = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { - return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; + return localizedDiagnosticMessages && _g(localizedDiagnosticMessages, message.key) || message.message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic; diff --git a/src/compiler/dataStructures.ts b/src/compiler/dataStructures.ts new file mode 100644 index 0000000000000..acb7226f666c4 --- /dev/null +++ b/src/compiler/dataStructures.ts @@ -0,0 +1,679 @@ +//TODO: move this back to types.ts +namespace ts { + export interface MapLike { + [index: string]: T; + } + + /** + * Represents a mapping from string keys to values. + * This is an abstract data type: only functions in dataStructures.ts should use the internal representation of Maps. + * The internal representation depends on whether the native Map class is available. + */ + //Note: we shouldn't have publicly exported members of the Map type. Use MapLike instead in those situations. + export interface Map { + // Ensure that Map and Map are incompatible + __mapBrand: T; + } +} + +//sort me +/* @internal */ +namespace ts { + //document + export function sortInV8ObjectInsertionOrder(values: T[], toKey: (t: T) => string): T[] { + const naturals: T[] = []; //name + const everythingElse: T[] = []; + for (const value of values) { + // "0" looks like a natural but "08" doesn't. + const looksLikeNatural = /^(0|([1-9]\d*))$/.test(toKey(value)); + (looksLikeNatural ? naturals : everythingElse).push(value); + } + function toInt(value: T): number { + return parseInt(toKey(value), 10); + } + naturals.sort((a, b) => toInt(a) - toInt(b)); + return naturals.concat(everythingElse); + } +} + +/* @internal */ +//map implementation +namespace ts { + export interface Iterator { + next(): { value: T, done: boolean }; //TODO: LKG updated, so use { value: T, done: false } | { value: never, done: true } + } + + interface NativeMap extends Map { + clear(): void; + delete(key: string): boolean; + get(key: string): T; + has(key: string): boolean; + set(key: string, value: T): this; + + keys(): Iterator; + values(): Iterator; + entries(): Iterator<[string, T]>; + + //kill? + forEach(f: (value: T, key: string) => void): void; + } + + declare const Map: { new(): NativeMap } | undefined; + const realMaps = typeof Map !== "undefined"; + + const createObject = Object.create; + const hasOwnProperty = Object.prototype.hasOwnProperty; + + export const createMap: () => Map = realMaps + ? () => new Map() + : createDictionaryModeObject; + + //move + export function createMapFromMapLike(template: MapLike) { + const map = createMap(); + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in template) if (hasOwnProperty.call(template, key)) { + _s(map, key, template[key]); + } + return map; + } + + //TODO: don't export + export function createDictionaryModeObject(): any { + const map = createObject(null); // tslint:disable-line:no-null-keyword + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map["__"] = undefined; + delete map["__"]; + + return map; + } + + export const _g: (map: Map, key: string) => T = realMaps + ? (map: NativeMap, key: string) => map.get(key) + : (map: MapLike, key: string) => map[key]; + + export const _s: (map: Map, key: string, value: T) => T = realMaps + ? (map: NativeMap, key: string, value: T) => { + map.set(key, value); + return value; + } + : (map: MapLike, key: string, value: T) => map[key] = value; + + //TODO: many calls to _has could be replaced by calling '_get' and checking the result + export const _has: (map: Map, key: string) => boolean = realMaps + ? (map: NativeMap, key: string) => map.has(key) + : (map: MapLike, key: string) => key in map; + + export const _delete: (map: Map, key: string) => void = realMaps + ? (map: NativeMap, key: string) => { + map.delete(key); + } + : (map: MapLike, key: string) => { + delete map[key]; + }; + + export const _each: (map: Map, f: (key: string, value: T) => void) => void = realMaps + ? (map: NativeMap, f: (key: string, value: T) => void) => { + /*const iter = map.entries(); + while (true) { + const { value: pair, done } = iter.next(); + if (done) { + return; + } + const [key, value] = pair; + f(key, value); + }*/ + map.forEach((value, key) => { f(key, value) }); // No idea whether this is better or worse... + } + : (map: MapLike, f: (key: string, value: T) => void): void => { + for (const key in map) { + f(key, map[key]); + } + }; + + export const _find: (map: Map, f: (key: string, value: T) => U | undefined) => U | undefined = realMaps + ? (map: NativeMap, f: (key: string, value: T) => U | undefined) => { + const iter = map.entries(); + while (true) { + const { value: pair, done } = iter.next(); + if (done) { + return undefined; + } + const [key, value] = pair; + const result = f(key, value); + if (result !== undefined) { + return result; + } + } + } + : (map: MapLike, f: (key: string, value: T) => U | undefined) => { + for (const key in map) { + const result = f(key, map[key]); + if (result !== undefined) + return result; + } + return undefined; + }; + + export const _someKey: (map: Map, predicate: (key: string) => boolean) => boolean = realMaps + ? (map: NativeMap, predicate: (key: string) => boolean) => { + const iter = map.keys(); + while (true) { + const { value: key, done } = iter.next(); + if (done) { + return false; + } + if (predicate(key)) { + return true; + } + } + } + : (map: MapLike, predicate: (key: string) => boolean) => { + for (const key in map) { + if (predicate(key)) { + return true; + } + } + return false; + }; + + //only used in one place, kill? Write in terms of _someEntry? + export const _someValue: (map: Map, predicate: (value: T) => boolean) => boolean = realMaps + ? (map: NativeMap, f: (value: T) => boolean) => { + const iter = map.values(); + while (true) { + const { value, done } = iter.next(); + if (done) { + return false; + } + if (f(value)) { + return true; + } + } + } + : (map: MapLike, f: (value: T) => boolean) => { + for (const key in map) { + if (f(map[key])) { + return true; + } + } + return false; + }; + + export const _someEntry: (map: Map, predicate: (key: string, value: T) => boolean) => boolean = realMaps + ? (map: NativeMap, f: (key: string, value: T) => boolean) => { + const iter = map.entries(); + while (true) { + const { value: pair, done } = iter.next(); + if (done) { + return false; + } + const [key, value] = pair; + if (f(key, value)) { + return true; + } + } + } + : (map: MapLike, f: (key: string, value: T) => boolean) => { + for (const key in map) { + if (f(key, map[key])) { + return true; + } + } + return false; + }; + + export const _eachKey: (map: Map, f: (key: string) => void) => void = realMaps + ? (map: NativeMap, f: (key: string) => void) => { + const iter = map.keys(); + while (true) { + const { value: key, done } = iter.next(); + if (done) { + return; + } + f(key); + } + //map.forEach((value, key) => f(key)); //does this do unnecessary lookup? + } + : (map: MapLike, f: (key: string) => void) => { + for (const key in map) { + f(key); + } + }; + + //reconsider + export const _eachAndBreakIfReturningTrue: (map: Map, f: (key: string, value: T) => boolean) => void = realMaps + ? (map: NativeMap, f: (key: string, value: T) => boolean) => { + const iter = map.entries(); + while (true) { + const { value: pair, done } = iter.next(); + if (done) { + return; + } + const [key, value] = pair; + f(key, value); + } + } + : (map: MapLike, f: (key: string, value: T) => boolean) => { + for (const key in map) { + const shouldBreak = f(key, map[key]); + if (shouldBreak) { + break; + } + } + }; + + export const _eachValue: (map: Map, f: (value: T) => void) => void = realMaps + ? (map: NativeMap, f: (value: T) => void) => { + const iter = map.values(); + while (true) { + const { value, done } = iter.next(); + if (done) { + return; + } + f(value); + } + //map.forEach(f); + } + : (map: MapLike, f: (value: T) => void) => { + for (const key in map) { + f(map[key]); + } + }; + + export const _toMapLike: (map: Map) => MapLike = realMaps + ? (map: NativeMap) => { + const obj = createDictionaryModeObject(); + _each(map, (key, value) => { + obj[key] = value; + }); + return obj; + } + : (map: MapLike) => map; + + export const _findMapValue: (map: Map, f: (value: T) => U | undefined) => U | undefined = realMaps + ? (map: NativeMap, f: (value: T) => U | undefined) => { + const iter = map.values(); + while (true) { + const { value, done } = iter.next(); + if (done) { + return undefined; + } + const result = f(value); + if (result !== undefined) { + return result; + } + } + } + : (map: MapLike, f: (value: T) => U | undefined) => { + for (const key in map) { + const result = f(map[key]); + if (result !== undefined) { + return result; + } + } + return undefined; + }; +} + + +//Map extensions: don't depend on internal details +/* @internal */ +namespace ts { + export function isEmpty(map: Map): boolean { + return !_someKey(map, () => true); + } + + //rename these + export function _deleteWakka(map: Map, key: any): void { + _delete(map, key.toString()); + } + export function _hasWakka(map: Map, key: any): boolean { + return _has(map, key.toString()); + } + export function _getWakka(map: Map, key: any): T { + return _g(map, key.toString()); + } + export function _setWakka(map: Map, key: any, value: T): T { + return _s(map, key.toString(), value); + } + + export function _mod(map: Map, key: string, modifier: (value: T) => T) { + _s(map, key, modifier(_g(map, key))); + } + + export function cloneMap(map: Map) { + const clone = createMap(); + copyMapPropertiesFromTo(map, clone); + return clone; + } + + /** + * Performs a shallow copy of the properties from a source Map to a target Map + * + * @param source A map from which properties should be copied. + * @param target A map to which properties should be copied. + */ + //rename : "entries", not "properties" + export function copyMapPropertiesFromTo(source: Map, target: Map): void { + _each(source, (key, value) => { + _s(target, key, value); + }); + } + + //move + export function copySetValuesFromTo(source: Set, target: Set): void { + _eachInSet(source, value => _add(target, value)); + } + + //kill? + /** + * 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; + _each(map, (key, value) => { + result = callback(result, value, String(key)); //why cast to string??? + }); + return result; + } + + export function _mapValuesMutate(map: Map, mapValue: (value: T) => T): void { + _each(map, (key, value) => { + _s(map, key, mapValue(value)); + }); + } + + export function _ownKeys(map: Map): string[] { + const keys: string[] = []; + _eachKey(map, key => { + keys.push(key); + }); + return keys; + } + + export function _getOrUpdate(map: Map, key: string, getValue: (key: string) => T): T { + return _has(map, key) ? _g(map, key) : _s(map, key, getValue(key)); + } + + /** + * Creates a map from the elements of an array. + * + * @param array the array of input elements. + * @param makeKey a function that produces a key for a given element. + * + * This function makes no effort to avoid collisions; if any two elements produce + * the same key with the given 'makeKey' function, then the element with the higher + * index in the array will be the one associated with the produced key. + */ + export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + const result = createMap(); + for (const value of array) { + _s(result, makeKey(value), makeValue ? makeValue(value) : value); + } + return result; + } + + /** + * Adds the value to an array of values associated with the key, and returns the array. + * Creates the array if it does not already exist. + */ + export function multiMapAdd(map: Map, key: string, value: V): V[] { + const values = _g(map, key); + if (values) { + values.push(value); + return values; + } + else { + return _s(map, key, [value]); + } + } + + /** + * Removes a value from an array of values associated with the key. + * Does not preserve the order of those values. + * Does nothing if `key` is not in `map`, or `value` is not in `map[key]`. + */ + export function multiMapRemove(map: Map, key: string, value: V): void { + const values = _g(map, key); + if (values) { + unorderedRemoveItem(values, value); + if (!values.length) { + _delete(map, key); + } + } + } + + //todo: neater + export function _equalMaps(left: Map, right: Map, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + const someInLeftHasNoMatch = _someEntry(left, (leftKey, leftValue) => { + if (!_has(right, leftKey)) return true; + const rightValue = _g(right, leftKey); + return !(equalityComparer ? equalityComparer(leftValue, rightValue) : leftValue === rightValue); + }); + if (someInLeftHasNoMatch) return false; + const someInRightHasNoMatch = _someKey(right, rightKey => !_has(left, rightKey)); + return !someInRightHasNoMatch; + } +} + + +/* @internal */ +namespace ts { + export interface Set { + __setBrand: any; + } + + interface NativeSet extends Set { + readonly size: number; + add(value: string): this; + has(value: string): boolean; + delete(value: string): void; + values(): Iterator; + forEach(f: (value: string) => void): void; + } + + interface SetLike extends Set { + [value: string]: boolean; //TODO: `: true` + } + + declare const Set: { new(): NativeSet } | undefined; + const realSets = typeof Set !== "undefined"; + + /*interface StringSetModule { + createSet(): Set + add(set: Set, value: string): string; + } + const StringSet: StringSetModule = realSets ? + { + createSet: () => new Set(), + add(set: NativeSet, value: string) { + set.add(value); + return value; + } + } + : + { + createSet: () => createDictionaryModeObject(), + add(set: SetLike, value: string) { + set[value] = true; + return value; + } + }*/ + + export const createSet: () => Set = realSets + ? () => new Set() + : () => createDictionaryModeObject(); + + export const _add: (set: Set, value: string) => string = realSets + ? (set: NativeSet, value: string) => { + set.add(value); + return value; + } + : (set: SetLike, value: string) => { + set[value] = true; + return value; + } + + export const _setHas: (set: Set, value: string) => boolean = realSets + ? (set: NativeSet, value: string) => set.has(value) + : (set: SetLike, value: string) => value in set + + export const _deleteFromSet: (set: Set, value: string) => void = realSets + ? (set: NativeSet, value: string) => { + set.delete(value) + } + : (set: SetLike, value: string) => { + delete set[value]; + } + + export const _setIsEmpty: (set: Set) => boolean = realSets + ? (set: NativeSet) => set.size === 0 + : (set: SetLike) => { + for (const value in set) + return false; + return true; + } + + export const _eachInSet: (set: Set, f: (value: string) => void) => void = realSets + ? (set: NativeSet, f: (value: string) => void) => { + set.forEach(f); + } + : (set: SetLike, f: (value: string) => void) => { + for (const value in set) + f(value); + } +} + +//MAPLIKE +/* @internal */ +namespace ts { + const hasOwnProperty = Object.prototype.hasOwnProperty; //neater + + export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; + } + } + return result; + } + + /** + * Indicates whether a map-like contains an own property with the specified key. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * the 'in' operator. + * + * @param map A map-like. + * @param key A property key. + */ + export function hasProperty(map: MapLike, key: string): boolean { + return hasOwnProperty.call(map, key); + } + + /** + * Gets the value of an owned property in a map-like. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * an indexer. + * + * @param map A map-like. + * @param key A property key. + */ + export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; + } + + /** + * Gets the owned, enumerable property keys of a map-like. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * Object.keys instead as it offers better performance. + * + * @param map A map-like. + */ + export function getOwnKeys(map: MapLike): string[] { + const keys: string[] = []; + for (const key in map) if (hasOwnProperty.call(map, key)) { + keys.push(key); + } + return keys; + } + + export function assign, T2, T3>(t: T1, arg1: T2, arg2: T3): T1 & T2 & T3; + export function assign, T2>(t: T1, arg1: T2): T1 & T2; + export function assign>(t: T1, ...args: any[]): any; + export function assign>(t: T1, ...args: any[]) { + for (const arg of args) { + for (const p of getOwnKeys(arg)) { + t[p] = arg[p]; + } + } + return t; + } + + /** + * Reduce the properties defined on a map-like (but not from its prototype chain). + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * reduceProperties instead as it offers better performance. + * + * @param map The map-like to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) if (hasOwnProperty.call(map, key)) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key) === undefined) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; + } + for (const key in right) if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; + } + return true; + } + + export function extend(first: T1 , second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in second) if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; + } + for (const id in first) if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; + } + return result; + } +} diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index dfca14bd0d1c2..466043fc4a327 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -75,7 +75,7 @@ namespace ts { // and we could be collecting these paths from multiple files into single one with --out option let referencesOutput = ""; - let usedTypeDirectiveReferences: Map; + let usedTypeDirectiveReferences: Set; // Emit references corresponding to each file const emittedReferencedFiles: SourceFile[] = []; @@ -156,9 +156,9 @@ namespace ts { }); if (usedTypeDirectiveReferences) { - for (const directive in usedTypeDirectiveReferences) { + _eachInSet(usedTypeDirectiveReferences, directive => { referencesOutput += `/// ${newLine}`; - } + }); } return { @@ -267,11 +267,11 @@ namespace ts { } if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = createMap(); + usedTypeDirectiveReferences = createSet(); } for (const directive of typeReferenceDirectives) { - if (!(directive in usedTypeDirectiveReferences)) { - usedTypeDirectiveReferences[directive] = directive; + if (!_setHas(usedTypeDirectiveReferences, directive)) { + _add(usedTypeDirectiveReferences, directive); } } } @@ -535,14 +535,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!(baseName in currentIdentifiers)) { + if (!_has(currentIdentifiers, baseName)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!(name in currentIdentifiers)) { + if (!_has(currentIdentifiers, name)) { return name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 14e743cf59f76..48bff388b075f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -218,7 +218,7 @@ const _super = (function (geti, seti) { let nodeIdToGeneratedName: string[]; let autoGeneratedIdToGeneratedName: string[]; - let generatedNameSet: Map; + let generatedNameSet: Set; let tempFlags: TempFlags; let currentSourceFile: SourceFile; let currentText: string; @@ -296,7 +296,7 @@ const _super = (function (geti, seti) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); nodeIdToGeneratedName = []; autoGeneratedIdToGeneratedName = []; - generatedNameSet = createMap(); + generatedNameSet = createSet(); isOwnFileEmit = !isBundledEmit; // Emit helpers from all the files @@ -2703,15 +2703,15 @@ const _super = (function (geti, seti) { function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); + !_has(currentFileIdentifiers, name) && + !_setHas(generatedNameSet, 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 && _has(node.locals, 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 (_g(node.locals, name).flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; } } @@ -2760,7 +2760,7 @@ const _super = (function (geti, seti) { while (true) { const generatedName = baseName + i; if (isUniqueName(generatedName)) { - return generatedNameSet[generatedName] = generatedName; + return _add(generatedNameSet, generatedName); } i++; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 48296145dbc11..246e416fdca3a 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2716,8 +2716,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]); + if (sourceFile.renamedDependencies && _has(sourceFile.renamedDependencies, moduleName.text)) { + return createLiteral(_g(sourceFile.renamedDependencies, moduleName.text)); } return undefined; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9bd9311b74921..bc4482d245b13 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1101,7 +1101,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return identifiers[text] || (identifiers[text] = text); + return _getOrUpdate(identifiers, text, 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 e8f064e81cdf3..d417e11d8acc2 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; + _s(marks, markName, timestamp()); + _s(counts, markName, (_g(counts, markName) || 0) + 1); profilerEvent(markName); } } @@ -44,9 +44,9 @@ namespace ts.performance { */ export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { if (enabled) { - const end = endMarkName && marks[endMarkName] || timestamp(); - const start = startMarkName && marks[startMarkName] || profilerStart; - measures[measureName] = (measures[measureName] || 0) + (end - start); + const end = endMarkName && _g(marks, endMarkName) || timestamp(); + const start = startMarkName && _g(marks, startMarkName) || profilerStart; + _s(measures, measureName, (_g(measures, measureName) || 0) + (end - start)); } } @@ -56,7 +56,7 @@ namespace ts.performance { * @param markName The name of the mark. */ export function getCount(markName: string) { - return counts && counts[markName] || 0; + return counts && _g(counts, markName) || 0; } /** @@ -65,7 +65,7 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && measures[measureName] || 0; + return measures && _g(measures, measureName) || 0; } /** @@ -74,9 +74,7 @@ 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]); - } + _each(measures, cb); } /** Enables (and resets) performance measurements for the compiler. */ diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ab306e90f3a29..534bcb0bf8f57 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -863,7 +863,7 @@ namespace ts { } export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { - const existingDirectories = createMap(); + const existingDirectories = createSet(); function getCanonicalFileName(fileName: string): string { // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. @@ -895,11 +895,11 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (directoryPath in existingDirectories) { + if (_setHas(existingDirectories, directoryPath)) { return true; } if (sys.directoryExists(directoryPath)) { - existingDirectories[directoryPath] = true; + _add(existingDirectories, directoryPath); return true; } return false; @@ -923,8 +923,8 @@ namespace ts { const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore && fileName in outputFingerprints) { - const fingerprint = outputFingerprints[fileName]; + if (mtimeBefore && _has(outputFingerprints, fileName)) { + const fingerprint = _g(outputFingerprints, fileName); // If output has not been changed, and the file has no external modification if (fingerprint.byteOrderMark === writeByteOrderMark && @@ -938,11 +938,11 @@ namespace ts { const mtimeAfter = sys.getModifiedTime(fileName); - outputFingerprints[fileName] = { + _s(outputFingerprints, fileName, { hash, byteOrderMark: writeByteOrderMark, mtime: mtimeAfter - }; + }); } function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { @@ -1062,9 +1062,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 = _has(cache, name) + ? _g(cache, name) + : _s(cache, name, loader(name, containingFile)); resolutions.push(result); } return resolutions; @@ -1114,7 +1114,7 @@ namespace ts { let commonSourceDirectory: string; let diagnosticsProducingTypeChecker: TypeChecker; let noDiagnosticsTypeChecker: TypeChecker; - let classifiableNames: Map; + let classifiableNames: Set; let resolvedTypeReferenceDirectives = createMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); @@ -1258,14 +1258,15 @@ namespace ts { return commonSourceDirectory; } - function getClassifiableNames() { + function getClassifiableNames(): Set { if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - classifiableNames = createMap(); + classifiableNames = createSet(); for (const sourceFile of files) { - copyProperties(sourceFile.classifiableNames, classifiableNames); + //copyMapPropertiesFromTo(sourceFile.classifiableNames, classifiableNames); + copySetValuesFromTo(sourceFile.classifiableNames, classifiableNames); } } @@ -1415,7 +1416,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], + isSourceFileFromExternalLibrary: (file: SourceFile) => !!_g(sourceFilesFoundSearchingNodeModules, file.path), writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, @@ -1964,20 +1965,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 && _g(sourceFilesFoundSearchingNodeModules, file.path) && currentNodeModulesDepth == 0) { + _s(sourceFilesFoundSearchingNodeModules, file.path, false); if (!options.noResolve) { processReferencedFiles(file, getDirectoryPath(fileName), isDefaultLib); processTypeReferenceDirectives(file); } - modulesWithElidedImports[file.path] = false; + _s(modulesWithElidedImports, file.path, false); processImportedModules(file, getDirectoryPath(fileName)); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && modulesWithElidedImports[file.path]) { + else if (file && _g(modulesWithElidedImports, file.path)) { if (currentNodeModulesDepth < maxNodeModulesJsDepth) { - modulesWithElidedImports[file.path] = false; + _s(modulesWithElidedImports, file.path, false); processImportedModules(file, getDirectoryPath(fileName)); } } @@ -1998,7 +1999,7 @@ namespace ts { filesByName.set(path, file); if (file) { - sourceFilesFoundSearchingNodeModules[path] = (currentNodeModulesDepth > 0); + _s(sourceFilesFoundSearchingNodeModules, path, currentNodeModulesDepth > 0); file.path = path; if (host.useCaseSensitiveFileNames()) { @@ -2060,7 +2061,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 = _g(resolvedTypeReferenceDirectives, typeReferenceDirective); if (previousResolution && previousResolution.primary) { return; } @@ -2097,7 +2098,7 @@ namespace ts { } if (saveResolution) { - resolvedTypeReferenceDirectives[typeReferenceDirective] = resolvedTypeReferenceDirective; + _s(resolvedTypeReferenceDirectives, typeReferenceDirective, resolvedTypeReferenceDirective); } } @@ -2141,7 +2142,7 @@ namespace ts { const shouldAddFile = resolution && !options.noResolve && i < file.imports.length && !elideImport; if (elideImport) { - modulesWithElidedImports[file.path] = true; + _s(modulesWithElidedImports, file.path, true); } else if (shouldAddFile) { findSourceFile(resolution.resolvedFileName, diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index e7b3872f2e3bb..490b77d194966 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 = createMapFromMapLike({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -274,9 +274,9 @@ namespace ts { function makeReverseMap(source: Map): string[] { const result: string[] = []; - for (const name in source) { - result[source[name]] = name; - } + _each(source, (name, num) => { + result[num] = name; + }); return result; } @@ -288,7 +288,7 @@ namespace ts { /* @internal */ export function stringToToken(s: string): SyntaxKind { - return textToToken[s]; + return _g(textToToken, s); } /* @internal */ @@ -362,8 +362,6 @@ namespace ts { return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); } - const hasOwnProperty = Object.prototype.hasOwnProperty; - export function isWhiteSpace(ch: number): boolean { return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); } @@ -1182,8 +1180,8 @@ 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 && _has(textToToken, tokenValue)) { + return token = _g(textToToken, tokenValue); } } return token = SyntaxKind.Identifier; diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 437cf8e05ec06..3335d4d03ad72 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -246,18 +246,18 @@ namespace ts { function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - const watcher = dirWatchers[dirName]; + const watcher = _g(dirWatchers, dirName); if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); - delete dirWatchers[dirName]; + _delete(dirWatchers, dirName); } } } function addDirWatcher(dirPath: string): void { - let watcher = dirWatchers[dirPath]; + let watcher = _g(dirWatchers, dirPath); if (watcher) { watcher.referenceCount += 1; return; @@ -268,7 +268,7 @@ namespace ts { (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) ); watcher.referenceCount = 1; - dirWatchers[dirPath] = watcher; + _s(dirWatchers, dirPath, watcher); return; } @@ -298,9 +298,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 = _g(fileWatcherCallbacks, fileName); + if (callbacks) { + for (const fileCallback of _g(fileWatcherCallbacks, fileName)) { + fileCallback(fileName); + } } } } diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 6242a2f23474c..4e1005eb89861 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -10,7 +10,7 @@ /* @internal */ namespace ts { - const moduleTransformerMap = createMap({ + const moduleTransformerMap = createMapFromMapLike({ [ModuleKind.ES6]: transformES6Module, [ModuleKind.System]: transformSystemModule, [ModuleKind.AMD]: transformModule, @@ -170,7 +170,7 @@ namespace ts { const transformers: Transformer[] = []; transformers.push(transformTypeScript); - transformers.push(moduleTransformerMap[moduleKind] || moduleTransformerMap[ModuleKind.None]); + transformers.push(_getWakka(moduleTransformerMap, moduleKind) || _getWakka(moduleTransformerMap, ModuleKind.None)); if (jsx === JsxEmit.React) { transformers.push(transformJsx); @@ -431,7 +431,7 @@ namespace ts { let range: TextRange; let current = node; while (current) { - range = current.id ? tokenSourceMapRanges[current.id + "-" + token] : undefined; + range = current.id ? _g(tokenSourceMapRanges, current.id + "-" + token) : undefined; if (range !== undefined) { break; } @@ -458,7 +458,7 @@ namespace ts { lastTokenSourceMapRangeNode = node; lastTokenSourceMapRangeToken = token; lastTokenSourceMapRange = range; - tokenSourceMapRanges[getNodeId(node) + "-" + token] = range; + _s(tokenSourceMapRanges, getNodeId(node) + "-" + token, range); return node; } diff --git a/src/compiler/transformers/es6.ts b/src/compiler/transformers/es6.ts index e8ba800e19062..cb8f58d7626d2 100644 --- a/src/compiler/transformers/es6.ts +++ b/src/compiler/transformers/es6.ts @@ -500,7 +500,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 && _g(convertedLoopState.labels, node.label.text)) || (!node.label && (convertedLoopState.allowedNonLabeledJumps & jump)); if (!canUseBreakOrContinue) { @@ -1700,7 +1700,7 @@ namespace ts { if (!convertedLoopState.labels) { convertedLoopState.labels = createMap(); } - convertedLoopState.labels[node.label.text] = node.label.text; + _s(convertedLoopState.labels, node.label.text, node.label.text); } let result: VisitResult; @@ -1712,7 +1712,7 @@ namespace ts { } if (convertedLoopState) { - convertedLoopState.labels[node.label.text] = undefined; + _s(convertedLoopState.labels, node.label.text, undefined); } return result; @@ -2157,8 +2157,7 @@ namespace ts { extraVariableDeclarations = []; } // hoist collected variable declarations - for (const name in currentState.hoistedLocalVariables) { - const identifier = currentState.hoistedLocalVariables[name]; + for (const identifier of currentState.hoistedLocalVariables) { extraVariableDeclarations.push(createVariableDeclaration(identifier)); } } @@ -2309,13 +2308,13 @@ namespace ts { if (!state.labeledNonLocalBreaks) { state.labeledNonLocalBreaks = createMap(); } - state.labeledNonLocalBreaks[labelText] = labelMarker; + _s(state.labeledNonLocalBreaks, labelText, labelMarker); } else { if (!state.labeledNonLocalContinues) { state.labeledNonLocalContinues = createMap(); } - state.labeledNonLocalContinues[labelText] = labelMarker; + _s(state.labeledNonLocalContinues, labelText, labelMarker); } } @@ -2323,13 +2322,12 @@ namespace ts { if (!table) { return; } - for (const labelText in table) { - const labelMarker = table[labelText]; + _each(table, (labelText, labelMarker) => { 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 && _g(outerLoop.labels, labelText))) { const label = createIdentifier(labelText); statements.push(isBreak ? createBreak(label) : createContinue(label)); } @@ -2338,7 +2336,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 5f142b6abe96e..5704e595b7384 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -217,7 +217,7 @@ namespace ts { Endfinally = 7, } - const instructionNames = createMap({ + const instructionNames = createMapFromMapLike({ [Instruction.Return]: "return", [Instruction.Break]: "break", [Instruction.Yield]: "yield", @@ -243,7 +243,7 @@ namespace ts { context.onSubstituteNode = onSubstituteNode; let currentSourceFile: SourceFile; - let renamedCatchVariables: Map; + let renamedCatchVariables: Set; let renamedCatchVariableDeclarations: Map; let inGeneratorFunctionBody: boolean; @@ -1903,12 +1903,12 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier) { - if (renamedCatchVariables && hasProperty(renamedCatchVariables, node.text)) { + if (renamedCatchVariables && _setHas(renamedCatchVariables, 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 = _g(renamedCatchVariableDeclarations, String(getOriginalNodeId(declaration))); if (name) { const clone = getMutableClone(name); setSourceMapRange(clone, node); @@ -2073,13 +2073,13 @@ namespace ts { const name = declareLocal(text); if (!renamedCatchVariables) { - renamedCatchVariables = createMap(); + renamedCatchVariables = createSet(); renamedCatchVariableDeclarations = createMap(); context.enableSubstitution(SyntaxKind.Identifier); } - renamedCatchVariables[text] = true; - renamedCatchVariableDeclarations[getOriginalNodeId(variable)] = name; + _add(renamedCatchVariables, text); + _setWakka(renamedCatchVariableDeclarations, getOriginalNodeId(variable), name); const exception = peekBlock(); Debug.assert(exception.state < ExceptionBlockState.Catch); @@ -2383,7 +2383,7 @@ namespace ts { */ function createInstruction(instruction: Instruction): NumericLiteral { const literal = createLiteral(instruction); - literal.trailingComment = instructionNames[instruction]; + literal.trailingComment = _getWakka(instructionNames, instruction); return literal; } diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 9e6aa507cce5a..b6bd146571d9b 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -223,7 +223,7 @@ namespace ts { return String.fromCharCode(parseInt(hex, 16)); } else { - const ch = entities[word]; + const ch = _g(entities, 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; } @@ -272,7 +272,7 @@ namespace ts { } function createEntitiesMap(): Map { - return createMap({ + return createMapFromMapLike({ "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 53004da2c171e..7b0b2eb79e18f 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -4,7 +4,7 @@ /*@internal*/ namespace ts { export function transformModule(context: TransformationContext) { - const transformModuleDelegates = createMap<(node: SourceFile) => SourceFile>({ + const transformModuleDelegates = createMapFromMapLike<(node: SourceFile) => SourceFile>({ [ModuleKind.None]: transformCommonJSModule, [ModuleKind.CommonJS]: transformCommonJSModule, [ModuleKind.AMD]: transformAMDModule, @@ -61,7 +61,7 @@ namespace ts { ({ externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues } = collectExternalModuleInfo(node, resolver)); // Perform the transformation. - const transformModule = transformModuleDelegates[moduleKind] || transformModuleDelegates[ModuleKind.None]; + const transformModule = _getWakka(transformModuleDelegates, moduleKind) || _getWakka(transformModuleDelegates, ModuleKind.None); const updated = transformModule(node); aggregateTransformFlags(updated); @@ -523,7 +523,7 @@ namespace ts { const original = getOriginalNode(currentSourceFile); Debug.assert(original.kind === SyntaxKind.SourceFile); - if (!original.symbol.exports["___esModule"]) { + if (!_g(original.symbol.exports, "___esModule")) { if (languageVersion === ScriptTarget.ES3) { statements.push( createStatement( @@ -578,8 +578,8 @@ namespace ts { } function addExportMemberAssignments(statements: Statement[], name: Identifier): void { - if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { - for (const specifier of exportSpecifiers[name.text]) { + if (!exportEquals && exportSpecifiers && _has(exportSpecifiers, name.text)) { + for (const specifier of _g(exportSpecifiers, name.text)) { statements.push( startOnNewLine( createStatement( @@ -666,12 +666,12 @@ namespace ts { } } else { - if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + if (!exportEquals && exportSpecifiers && _has(exportSpecifiers, name.text)) { const sourceFileId = getOriginalNodeId(currentSourceFile); - if (!bindingNameExportSpecifiersForFileMap[sourceFileId]) { - bindingNameExportSpecifiersForFileMap[sourceFileId] = createMap(); + if (!_getWakka(bindingNameExportSpecifiersForFileMap, sourceFileId)) { + _setWakka(bindingNameExportSpecifiersForFileMap, sourceFileId, createMap()); } - bindingNameExportSpecifiersForFileMap[sourceFileId][name.text] = exportSpecifiers[name.text]; + _s(_getWakka(bindingNameExportSpecifiersForFileMap, sourceFileId), name.text, _g(exportSpecifiers, name.text)); addExportMemberAssignments(resultStatements, name); } } @@ -823,7 +823,7 @@ namespace ts { function onEmitNode(node: Node, emit: (node: Node) => void): void { if (node.kind === SyntaxKind.SourceFile) { - bindingNameExportSpecifiersMap = bindingNameExportSpecifiersForFileMap[getOriginalNodeId(node)]; + bindingNameExportSpecifiersMap = _getWakka(bindingNameExportSpecifiersForFileMap, getOriginalNodeId(node)); previousOnEmitNode(node, emit); bindingNameExportSpecifiersMap = undefined; } @@ -889,10 +889,10 @@ namespace ts { const left = node.left; // If the left-hand-side of the binaryExpression is an identifier and its is export through export Specifier if (isIdentifier(left) && isAssignmentOperator(node.operatorToken.kind)) { - if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, left.text)) { + if (bindingNameExportSpecifiersMap && _has(bindingNameExportSpecifiersMap, left.text)) { setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); let nestedExportAssignment: BinaryExpression; - for (const specifier of bindingNameExportSpecifiersMap[left.text]) { + for (const specifier of _g(bindingNameExportSpecifiersMap, left.text)) { nestedExportAssignment = nestedExportAssignment ? createExportAssignment(specifier.name, nestedExportAssignment) : createExportAssignment(specifier.name, node); @@ -909,7 +909,7 @@ namespace ts { const operator = node.operator; const operand = node.operand; if (isIdentifier(operand) && bindingNameExportSpecifiersForFileMap) { - if (bindingNameExportSpecifiersMap && hasProperty(bindingNameExportSpecifiersMap, operand.text)) { + if (bindingNameExportSpecifiersMap && _has(bindingNameExportSpecifiersMap, operand.text)) { setNodeEmitFlags(node, NodeEmitFlags.NoSubstitution); let transformedUnaryExpression: BinaryExpression; if (node.kind === SyntaxKind.PostfixUnaryExpression) { @@ -923,7 +923,7 @@ namespace ts { setNodeEmitFlags(transformedUnaryExpression, NodeEmitFlags.NoSubstitution); } let nestedExportAssignment: BinaryExpression; - for (const specifier of bindingNameExportSpecifiersMap[operand.text]) { + for (const specifier of _g(bindingNameExportSpecifiersMap, operand.text)) { nestedExportAssignment = nestedExportAssignment ? createExportAssignment(specifier.name, nestedExportAssignment) : createExportAssignment(specifier.name, transformedUnaryExpression || node); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 5e3b028122775..a083161faaba7 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1330,14 +1330,14 @@ namespace ts { const externalImport = externalImports[i]; const externalModuleName = getExternalModuleNameLiteral(externalImport, currentSourceFile, host, resolver, compilerOptions); const text = externalModuleName.text; - if (hasProperty(groupIndices, text)) { + if (_has(groupIndices, text)) { // deduplicate/group entries in dependency list by the dependency name - const groupIndex = groupIndices[text]; + const groupIndex = _g(groupIndices, text); dependencyGroups[groupIndex].externalImports.push(externalImport); continue; } else { - groupIndices[text] = dependencyGroups.length; + _s(groupIndices, text, dependencyGroups.length); dependencyGroups.push({ name: externalModuleName, externalImports: [externalImport] diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 1d01e51f6d8d7..97a5e18e11bf6 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -687,7 +687,7 @@ namespace ts { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? node.name.text : "default"); - classAliases[getOriginalNodeId(node)] = classAlias; + _setWakka(classAliases, getOriginalNodeId(node), classAlias); } const declaredName = getDeclarationName(node, /*allowComments*/ true); @@ -771,7 +771,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); + _setWakka(classAliases, getOriginalNodeId(node), getSynthesizedClone(temp)); } // To preserve the behavior of the old emitter, we explicitly indent @@ -3326,7 +3326,7 @@ namespace ts { // constructor references in static property initializers. const declaration = resolver.getReferencedValueDeclaration(node); if (declaration) { - const classAlias = classAliases[declaration.id]; + const classAlias = _getWakka(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 b39c017384e16..f3fd9832fa0c3 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -93,7 +93,7 @@ namespace ts { return false; } try { - ts.localizedDiagnosticMessages = JSON.parse(fileContents); + ts.localizedDiagnosticMessages = createMapFromMapLike(JSON.parse(fileContents)); } catch (e) { errors.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath)); @@ -127,7 +127,7 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap = createMap({ + const categoryFormatMap = createMapFromMapLike({ [DiagnosticCategory.Warning]: yellowForegroundEscapeSequence, [DiagnosticCategory.Error]: redForegroundEscapeSequence, [DiagnosticCategory.Message]: blueForegroundEscapeSequence, @@ -199,7 +199,7 @@ namespace ts { output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; } - const categoryColor = categoryFormatMap[diagnostic.category]; + const categoryColor = _getWakka(categoryFormatMap, 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; @@ -438,9 +438,12 @@ namespace ts { } function cachedFileExists(fileName: string): boolean { - return fileName in cachedExistingFiles - ? cachedExistingFiles[fileName] - : cachedExistingFiles[fileName] = hostFileExists(fileName); + return _has(cachedExistingFiles, fileName) + ? _g(cachedExistingFiles, fileName) + : _s(cachedExistingFiles, fileName, hostFileExists(fileName)); + //return fileName in cachedExistingFiles + // ? cachedExistingFiles[fileName] + // : cachedExistingFiles[fileName] = hostFileExists(fileName); } function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { @@ -729,10 +732,10 @@ namespace ts { const options: string[] = []; const element = (option).element; const typeMap = >element.type; - for (const key in typeMap) { + _eachKey(typeMap, key => { options.push(`'${key}'`); - } - optionsDescriptionMap[description] = options; + }); + _s(optionsDescriptionMap, description, options); } else { description = getDiagnosticText(option.description); @@ -754,7 +757,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 = _g(optionsDescriptionMap, description); output.push(usage + makePadding(marginLength - usage.length + 2) + description + sys.newLine); if (kindsList) { diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index f128c994af12b..8dfa32b488486 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -12,6 +12,7 @@ }, "files": [ "core.ts", + "dataStructures.ts", "performance.ts", "sys.ts", "types.ts", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ab48ae543c2b7..ae01bcbaccbad 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,13 +1,4 @@ namespace ts { - - export interface MapLike { - [index: string]: T; - } - - export interface Map extends MapLike { - __mapBrand: any; - } - // branded string type used to store absolute, normalized and canonicalized paths // arbitrary file name can be converted to Path via toPath function export type Path = string & { __pathBrand: any }; @@ -1794,7 +1785,7 @@ namespace ts { // Stores a line map for the file. // This field should never be used directly to obtain line map, use getLineMap function instead. /* @internal */ lineMap: number[]; - /* @internal */ classifiableNames?: Map; + /* @internal */ classifiableNames?: Set; // Stores a mapping 'external module reference text' -> 'resolved file name' | undefined // It is used to resolve module names in the checker. // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead @@ -1882,7 +1873,7 @@ namespace ts { // language service). /* @internal */ getDiagnosticsProducingTypeChecker(): TypeChecker; - /* @internal */ getClassifiableNames(): Map; + /* @internal */ getClassifiableNames(): Set; /* @internal */ getNodeCount(): number; /* @internal */ getIdentifierCount(): number; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3417a1830ddc4..92d88d6b94252 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -103,11 +103,11 @@ namespace ts { } export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { - return !!(sourceFile && sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); + return !!(sourceFile && sourceFile.resolvedModules && _g(sourceFile.resolvedModules, moduleNameText)); } export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule { - return hasResolvedModule(sourceFile, moduleNameText) ? sourceFile.resolvedModules[moduleNameText] : undefined; + return hasResolvedModule(sourceFile, moduleNameText) ? _g(sourceFile.resolvedModules, moduleNameText) : undefined; } export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void { @@ -115,7 +115,7 @@ namespace ts { sourceFile.resolvedModules = createMap(); } - sourceFile.resolvedModules[moduleNameText] = resolvedModule; + _s(sourceFile.resolvedModules, moduleNameText, resolvedModule); } export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void { @@ -123,7 +123,7 @@ namespace ts { sourceFile.resolvedTypeReferenceDirectiveNames = createMap(); } - sourceFile.resolvedTypeReferenceDirectiveNames[typeReferenceDirectiveName] = resolvedTypeReferenceDirective; + _s(sourceFile.resolvedTypeReferenceDirectiveNames, typeReferenceDirectiveName, resolvedTypeReferenceDirective); } /* @internal */ @@ -143,7 +143,7 @@ namespace ts { } for (let i = 0; i < names.length; i++) { const newResolution = newResolutions[i]; - const oldResolution = oldResolutions && oldResolutions[names[i]]; + const oldResolution = oldResolutions && _g(oldResolutions, names[i]); const changed = oldResolution ? !newResolution || !comparer(oldResolution, newResolution) @@ -2237,11 +2237,11 @@ namespace ts { } function reattachFileDiagnostics(newFile: SourceFile): void { - if (!hasProperty(fileDiagnostics, newFile.fileName)) { + if (!_has(fileDiagnostics, newFile.fileName)) { return; } - for (const diagnostic of fileDiagnostics[newFile.fileName]) { + for (const diagnostic of _g(fileDiagnostics, newFile.fileName)) { diagnostic.file = newFile; } } @@ -2249,10 +2249,10 @@ namespace ts { function add(diagnostic: Diagnostic): void { let diagnostics: Diagnostic[]; if (diagnostic.file) { - diagnostics = fileDiagnostics[diagnostic.file.fileName]; + diagnostics = _g(fileDiagnostics, diagnostic.file.fileName); if (!diagnostics) { diagnostics = []; - fileDiagnostics[diagnostic.file.fileName] = diagnostics; + _s(fileDiagnostics, diagnostic.file.fileName, diagnostics); } } else { @@ -2272,7 +2272,7 @@ namespace ts { function getDiagnostics(fileName?: string): Diagnostic[] { sortAndDeduplicate(); if (fileName) { - return fileDiagnostics[fileName] || []; + return _g(fileDiagnostics, fileName) || []; } const allDiagnostics: Diagnostic[] = []; @@ -2282,9 +2282,8 @@ namespace ts { forEach(nonFileDiagnostics, pushDiagnostic); - for (const key in fileDiagnostics) { - forEach(fileDiagnostics[key], pushDiagnostic); - } + _eachValue(fileDiagnostics, diagnostics => + forEach(diagnostics, pushDiagnostic)); return sortAndDeduplicateDiagnostics(allDiagnostics); } @@ -2297,9 +2296,7 @@ namespace ts { diagnosticsModified = false; nonFileDiagnostics = sortAndDeduplicateDiagnostics(nonFileDiagnostics); - for (const key in fileDiagnostics) { - fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); - } + _mapValuesMutate(fileDiagnostics, sortAndDeduplicateDiagnostics); } } @@ -2309,7 +2306,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 = createMapFromMapLike({ "\0": "\\0", "\t": "\\t", "\v": "\\v", @@ -2336,7 +2333,7 @@ namespace ts { return s; function getReplacement(c: string) { - return escapedCharsMap[c] || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); + return _g(escapedCharsMap, c) || get16BitUnicodeEscapeSequence(c.charCodeAt(0)); } } @@ -3371,13 +3368,13 @@ namespace ts { export function formatSyntaxKind(kind: SyntaxKind): string { const syntaxKindEnum = (ts).SyntaxKind; if (syntaxKindEnum) { - if (syntaxKindCache[kind]) { - return syntaxKindCache[kind]; + if (_hasWakka(syntaxKindCache, kind)) { + return _getWakka(syntaxKindCache, kind); } for (const name in syntaxKindEnum) { if (syntaxKindEnum[name] === kind) { - return syntaxKindCache[kind] = kind.toString() + " (" + name + ")"; + return _setWakka(syntaxKindCache, kind, kind.toString() + " (" + name + ")"); } } } @@ -3555,7 +3552,7 @@ namespace ts { // export { x, y } for (const specifier of (node).exportClause.elements) { const name = (specifier.propertyName || specifier.name).text; - (exportSpecifiers[name] || (exportSpecifiers[name] = [])).push(specifier); + multiMapAdd(exportSpecifiers, name, specifier); } } break; diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index e147eda8535a6..da8de6196ad0c 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -46,7 +46,7 @@ namespace ts { * supplant the existing `forEachChild` implementation if performance is not * significantly impacted. */ - const nodeEdgeTraversalMap = createMap({ + const nodeEdgeTraversalMap = createMapFromMapLike({ [SyntaxKind.QualifiedName]: [ { name: "left", test: isEntityName }, { name: "right", test: isIdentifier } @@ -520,7 +520,7 @@ namespace ts { break; default: - const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + const edgeTraversalPath = _getWakka(nodeEdgeTraversalMap, kind); if (edgeTraversalPath) { for (const edge of edgeTraversalPath) { const value = (>node)[edge.name]; @@ -1141,10 +1141,10 @@ namespace ts { default: let updated: Node & MapLike; - const edgeTraversalPath = nodeEdgeTraversalMap[kind]; + const edgeTraversalPath = _getWakka(nodeEdgeTraversalMap, 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 a076812462e1e..0dde18712a988 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -99,7 +99,7 @@ namespace FourSlash { export import IndentStyle = ts.IndentStyle; - const entityMap = ts.createMap({ + const entityMap = ts.createMapFromMapLike({ "&": "&", "\"": """, "'": "'", @@ -109,7 +109,7 @@ namespace FourSlash { }); export function escapeXmlAttributeValue(s: string) { - return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]); + return s.replace(/[&<>"'\/]/g, ch => ts._g(entityMap, ch)); } // Name of testcase metadata including ts.CompilerOptions properties that will be used by globalOptions @@ -241,7 +241,7 @@ namespace FourSlash { } function tryAdd(path: string) { - const inputFile = inputFiles[path]; + const inputFile = ts._g(inputFiles, path); if (inputFile && !Harness.isDefaultLibraryFile(path)) { languageServiceAdapterHost.addScript(path, inputFile, /*isRootFile*/ true); return true; @@ -275,7 +275,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; + ts._s(this.inputFiles, file.fileName, file.content); if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") { const configJson = ts.parseConfigFileTextToJson(file.fileName, file.content); @@ -341,11 +341,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) { + ts._each(this.inputFiles, (fileName, inputFile) => { if (!Harness.isDefaultLibraryFile(fileName)) { - this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true); + this.languageServiceAdapterHost.addScript(fileName, inputFile, /*isRootFile*/ true); } - } + }); this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false); } @@ -699,11 +699,11 @@ 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; + if (!ts._has(uniqueItems, item.name)) { + ts._s(uniqueItems, 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, ts._g(uniqueItems, item.name), `Items should have the same kind, got ${item.kind} and ${ts._g(uniqueItems, item.name)}`); } } } @@ -888,7 +888,7 @@ namespace FourSlash { } public verifyRangesWithSameTextReferenceEachOther() { - ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); + ts._eachValue(this.rangesByTextMap(), ranges => this.verifyRangesReferenceEachOther(ranges)); } public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) { @@ -965,7 +965,8 @@ namespace FourSlash { } public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) { - ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => { + //TODO: don't bother creating a map + ts._each(ts.createMapFromMapLike(namesAndTexts), (name, text) => { if (text instanceof Array) { assert(text.length === 2); const [expectedText, expectedDocumentation] = text; @@ -1811,7 +1812,7 @@ namespace FourSlash { return this.testData.ranges; } - public rangesByText(): ts.Map { + private rangesByTextMap(): ts.Map { const result = ts.createMap(); for (const range of this.getRanges()) { const text = this.rangeText(range); @@ -1820,6 +1821,10 @@ namespace FourSlash { return result; } + public rangesByText(): ts.MapLike { + return ts._toMapLike(this.rangesByTextMap()); + } + private rangeText({fileName, start, end}: Range, more = false): string { return this.getFileContent(fileName).slice(start, end); } @@ -2068,7 +2073,7 @@ namespace FourSlash { public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { - const openBraceMap = ts.createMap({ + const openBraceMap = ts.createMapFromMapLike({ "(": ts.CharacterCodes.openParen, "{": ts.CharacterCodes.openBrace, "[": ts.CharacterCodes.openBracket, @@ -2078,7 +2083,7 @@ namespace FourSlash { "<": ts.CharacterCodes.lessThan }); - const charCode = openBraceMap[openingBrace]; + const charCode = ts._g(openBraceMap, openingBrace); if (!charCode) { this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`); @@ -2945,7 +2950,7 @@ namespace FourSlashInterface { return this.state.getRanges(); } - public rangesByText(): ts.Map { + public rangesByText(): ts.MapLike { return this.state.rangesByText(); } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index b4fd8736b2f7d..2b51c66d05d29 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -925,7 +925,7 @@ namespace Harness { export const defaultLibFileName = "lib.d.ts"; export const es2015DefaultLibFileName = "lib.es2015.d.ts"; - const libFileNameSourceFileMap= ts.createMap({ + const libFileNameSourceFileMap= ts.createMapFromMapLike({ [defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest) }); @@ -934,10 +934,10 @@ namespace Harness { return undefined; } - if (!libFileNameSourceFileMap[fileName]) { - libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest); + if (!ts._g(libFileNameSourceFileMap, fileName)) { + ts._s(libFileNameSourceFileMap, fileName, createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest)); } - return libFileNameSourceFileMap[fileName]; + return ts._g(libFileNameSourceFileMap, fileName); } export function getDefaultLibFileName(options: ts.CompilerOptions): string { @@ -1085,10 +1085,10 @@ namespace Harness { optionsIndex = ts.createMap(); const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations); for (const option of optionDeclarations) { - optionsIndex[option.name.toLowerCase()] = option; + ts._s(optionsIndex, option.name.toLowerCase(), option); } } - return optionsIndex[name.toLowerCase()]; + return ts._g(optionsIndex, name.toLowerCase()); } export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void { @@ -1442,7 +1442,7 @@ namespace Harness { const fullResults = ts.createMap(); for (const sourceFile of allFiles) { - fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); + ts._s(fullResults, sourceFile.unitName, fullWalker.getTypeAndSymbols(sourceFile.unitName)); } // Produce baselines. The first gives the types for all expressions. @@ -1495,7 +1495,7 @@ namespace Harness { allFiles.forEach(file => { const codeLines = file.content.split("\n"); - typeWriterResults[file.unitName].forEach(result => { + ts._g(typeWriterResults, file.unitName).forEach(result => { if (isSymbolBaseline && !result.symbol) { return; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index a67f68eb1a92b..2604c33c80d74 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -247,7 +247,7 @@ namespace Harness.LanguageService { super(cancellationToken, options); this.nativeHost = new NativeLanguageServiceHost(cancellationToken, options); - if (preprocessToResolve) { + if (preprocessToResolve) { //??? shims-pp has this turned on const compilerOptions = this.nativeHost.getCompilationSettings(); const moduleResolutionHost: ts.ModuleResolutionHost = { fileExists: fileName => this.getScriptInfo(fileName) !== undefined, @@ -259,7 +259,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.MapLike = {};//ts.createMap(); for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -272,7 +272,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.MapLike = {}; //ts.createMap(); 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/loggedIO.ts b/src/harness/loggedIO.ts index 07dbf4a4e1c43..5f7dc1dd99804 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -91,7 +91,7 @@ namespace Playback { } function memoize(func: (s: string) => T): Memoized { - let lookup: { [s: string]: T } = {}; + let lookup: { [s: string]: T } = {}; //TODO: use a Map const run: Memoized = >((s: string) => { if (lookup.hasOwnProperty(s)) return lookup[s]; return lookup[s] = func(s); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 415c09caff6d9..4cc900fbaf2ba 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -256,14 +256,14 @@ 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]; + if (name !== "mapRoot" && name !== "sourceRoot" && ts._has(optionNameMap, name)) { + const option = ts._g(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 (ts._has(optType, key)) { + value = ts._g(optType, key); } } compilerOptions[option.name] = value; diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 0b8935b380eb6..3d76d51dbefd1 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -7,16 +7,16 @@ namespace ts { } function createDefaultServerHost(fileMap: Map): server.ServerHost { - const existingDirectories = createMap(); - for (const name in fileMap) { + const existingDirectories = createSet(); + _eachKey(fileMap, name => { let dir = getDirectoryPath(name); let previous: string; do { - existingDirectories[dir] = true; + _add(existingDirectories, dir); previous = dir; dir = getDirectoryPath(dir); } while (dir !== previous); - } + }); return { args: [], newLine: "\r\n", @@ -24,7 +24,7 @@ namespace ts { write: (s: string) => { }, readFile: (path: string, encoding?: string): string => { - return path in fileMap ? fileMap[path].content : undefined; + return _has(fileMap, path) ? _g(fileMap, path).content : undefined; }, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => { throw new Error("NYI"); @@ -33,10 +33,10 @@ namespace ts { throw new Error("NYI"); }, fileExists: (path: string): boolean => { - return path in fileMap; + return _has(fileMap, path); }, directoryExists: (path: string): boolean => { - return existingDirectories[path] || false; + return _setHas(existingDirectories, path); }, createDirectory: (path: string) => { }, @@ -102,7 +102,7 @@ namespace ts { content: `foo()` }; - const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported })); + const serverHost = createDefaultServerHost(createMapFromMapLike({ [root.name]: root, [imported.name]: imported })); const { project, rootScriptInfo } = createProject(root.name, serverHost); // ensure that imported file was found @@ -194,7 +194,7 @@ namespace ts { content: `export var y = 1` }; - const fileMap = createMap({ [root.name]: root }); + const fileMap = createMapFromMapLike({ [root.name]: root }); const serverHost = createDefaultServerHost(fileMap); const originalFileExists = serverHost.fileExists; @@ -218,7 +218,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; + _s(fileMap, imported.name, imported); fileExistsCalledForBar = false; rootScriptInfo.editContent(0, content.length, `import {y} from "bar"`); diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 3e1ac171216e0..3f1e96b2cbe6d 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -10,11 +10,11 @@ namespace ts { const map = arrayToMap(files, f => f.name); if (hasDirectoryExists) { - const directories = createMap(); + const directories = createSet(); for (const f of files) { let name = getDirectoryPath(f.name); while (true) { - directories[name] = name; + _add(directories, name); const baseName = getDirectoryPath(name); if (baseName === name) { break; @@ -25,19 +25,19 @@ namespace ts { return { readFile, directoryExists: path => { - return path in directories; + return _setHas(directories, path); }, fileExists: path => { - assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`); - return path in map; + assert.isTrue(_setHas(directories, getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); + return _has(map, path); } }; } else { - return { readFile, fileExists: path => path in map, }; + return { readFile, fileExists: path => _has(map, path) }; } function readFile(path: string): string { - return path in map ? map[path].content : undefined; + return _has(map, path) ? _g(map, path).content : undefined; } } @@ -287,7 +287,7 @@ 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; + return _has(files, path) ? createSourceFile(fileName, _g(files, path), languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -298,7 +298,7 @@ namespace ts { useCaseSensitiveFileNames: () => false, fileExists: fileName => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return path in files; + return _has(files, path); }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -318,7 +318,7 @@ namespace ts { } it("should find all modules", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c/first/shared.ts": ` class A {} export = A`, @@ -337,7 +337,7 @@ export = C; }); it("should find modules in node_modules", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/parent/node_modules/mod/index.d.ts": "export var x", "/parent/app/myapp.ts": `import {x} from "mod"` }); @@ -345,7 +345,7 @@ export = C; }); it("should find file referenced via absolute and relative names", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c.ts": `/// `, "/a/b/b.ts": "var x" }); @@ -358,7 +358,8 @@ 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()); + //don't use reduce then! silly billy + files = reduceProperties(files, (files, file, fileName) => (_s(files, getCanonicalFileName(fileName), file), files), createMap()); } const host: CompilerHost = { @@ -367,7 +368,7 @@ export = C; return library; } const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; + return _has(files, path) ? createSourceFile(fileName, _g(files, path), languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -378,7 +379,7 @@ export = C; useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, fileExists: fileName => { const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return path in files; + return _has(files, path); }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -391,7 +392,7 @@ export = C; } it("should succeed when the same file is referenced using absolute and relative names", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); @@ -399,7 +400,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" }); @@ -407,7 +408,7 @@ export = C; }); it("should fail when two files used in program differ only in casing (imports)", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" }); @@ -415,7 +416,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 = createMapFromMapLike({ "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" }); @@ -423,7 +424,7 @@ export = C; }); it("should fail when two files exist on disk that differs only in casing", () => { - const files = createMap({ + const files = createMapFromMapLike({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" @@ -432,7 +433,7 @@ export = C; }); it("should fail when module name in 'require' calls has inconsistent casing", () => { - const files = createMap({ + const files = createMapFromMapLike({ "moduleA.ts": `import a = require("./ModuleC")`, "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" @@ -441,7 +442,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 = createMapFromMapLike({ "/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", @@ -453,7 +454,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 = createMapFromMapLike({ "/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", @@ -1019,8 +1020,8 @@ import b = require("./moduleB"); const names = map(files, f => f.name); const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES6)), f => f.fileName); const compilerHost: CompilerHost = { - fileExists : fileName => fileName in sourceFiles, - getSourceFile: fileName => sourceFiles[fileName], + fileExists : fileName => _has(sourceFiles, fileName), + getSourceFile: fileName => _g(sourceFiles, fileName), getDefaultLibFileName: () => "lib.d.ts", writeFile(file, text) { throw new Error("NYI"); @@ -1030,7 +1031,7 @@ import b = require("./moduleB"); getCanonicalFileName: f => f.toLowerCase(), getNewLine: () => "\r\n", useCaseSensitiveFileNames: () => false, - readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined + readFile: fileName => _has(sourceFiles, fileName) ? _g(sourceFiles, fileName).text : undefined }; 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 60687d34c55af..ac56495d51197 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -106,7 +106,7 @@ namespace ts { return { getSourceFile(fileName): SourceFile { - return files[fileName]; + return _g(files, fileName); }, getDefaultLibFileName(): string { return "lib.d.ts"; @@ -129,9 +129,9 @@ namespace ts { getNewLine(): string { return sys ? sys.newLine : newLine; }, - fileExists: fileName => fileName in files, + fileExists: fileName => _has(files, fileName), readFile: fileName => { - return fileName in files ? files[fileName].text : undefined; + return _has(files, fileName) ? _g(files, fileName).text : undefined; } }; } @@ -183,7 +183,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(_equalMaps(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } @@ -306,7 +306,7 @@ namespace ts { const options: CompilerOptions = { target }; const program_1 = newProgram(files, ["a.ts"], options); - checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); + checkResolvedModulesCache(program_1, "a.ts", createMapFromMapLike({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); const program_2 = updateProgram(program_1, ["a.ts"], options, files => { @@ -315,7 +315,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); + checkResolvedModulesCache(program_1, "a.ts", createMapFromMapLike({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); // imports has changed - program is not reused @@ -332,7 +332,7 @@ namespace ts { files[0].text = files[0].text.updateImportsAndExports(newImports); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" }, "c": undefined })); + checkResolvedModulesCache(program_4, "a.ts", createMapFromMapLike({ "b": { resolvedFileName: "b.ts" }, "c": undefined })); }); it("resolved type directives cache follows type directives", () => { @@ -343,7 +343,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", createMapFromMapLike({ "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 => { @@ -352,7 +352,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", createMapFromMapLike({ "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 @@ -370,7 +370,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", createMapFromMapLike({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); }); }); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 83baa20e3f7b2..63107c36f32f8 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -108,7 +108,7 @@ namespace ts.server { it("should not throw when commands are executed with invalid arguments", () => { let i = 0; for (const name in CommandNames) { - if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) { + if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) { //TODO: use hasProperty continue; } const req: protocol.Request = { @@ -369,9 +369,9 @@ 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]; + if (_hasWakka(this.callbacks, response.request_seq)) { + _getWakka(this.callbacks, response.request_seq)(response); + _deleteWakka(this.callbacks, response.request_seq); } } else if (msg.type === "event") { @@ -381,13 +381,13 @@ namespace ts.server { } emit(name: string, args: any): void { - if (name in this.eventHandlers) { - this.eventHandlers[name](args); + if (_has(this.eventHandlers, name)) { + _g(this.eventHandlers, name)(args); } } on(name: string, handler: (args: any) => void): void { - this.eventHandlers[name] = handler; + _s(this.eventHandlers, name, handler); } connect(session: InProcSession): void { @@ -405,7 +405,7 @@ namespace ts.server { command, arguments: args }); - this.callbacks[this.seq] = callback; + _setWakka(this.callbacks, this.seq, callback); } }; diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index ec050bef4b369..7c8e25a658a04 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -71,7 +71,7 @@ namespace ts { function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { assert.equal(reduceProperties(map, count => count + 1, 0), 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(_has(map, name), `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`); } } @@ -209,7 +209,7 @@ namespace ts { triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); - const callbacks = this.watchedDirectories[path]; + const callbacks = _g(this.watchedDirectories, path); if (callbacks) { for (const callback of callbacks) { callback.cb(fileName); @@ -219,7 +219,7 @@ namespace ts { triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); - const callbacks = this.watchedFiles[path]; + const callbacks = _g(this.watchedFiles, path); if (callbacks) { for (const callback of callbacks) { callback(path, removed); diff --git a/src/server/client.ts b/src/server/client.ts index 5032056c2f338..338b99fb022c8 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -37,10 +37,10 @@ namespace ts.server { } private getLineMap(fileName: string): number[] { - let lineMap = this.lineMaps[fileName]; + let lineMap = _g(this.lineMaps, fileName); if (!lineMap) { const scriptSnapshot = this.host.getScriptSnapshot(fileName); - lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())); + lineMap = _s(this.lineMaps, fileName, ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength()))); } return lineMap; } @@ -146,7 +146,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; + _s(this.lineMaps, 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 e8df088a5c279..c75b352407d10 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -18,7 +18,7 @@ namespace ts.server { const lineCollectionCapacity = 4; function mergeFormatOptions(formatCodeOptions: FormatCodeOptions, formatOptions: protocol.FormatOptions): void { - const hasOwnProperty = Object.prototype.hasOwnProperty; + const hasOwnProperty = Object.prototype.hasOwnProperty; //no no no, use hasProperty Object.keys(formatOptions).forEach((key) => { const codeKey = key.charAt(0).toUpperCase() + key.substring(1); if (hasOwnProperty.call(formatCodeOptions, codeKey)) { @@ -136,9 +136,9 @@ namespace ts.server { for (const name of names) { // check if this is a duplicate entry in the list - let resolution = newResolutions[name]; + let resolution = _g(newResolutions, name); if (!resolution) { - const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name]; + const existingResolution = currentResolutionsInFile && _g(currentResolutionsInFile, name); if (moduleResolutionIsValid(existingResolution)) { // ok, it is safe to use existing name resolution results resolution = existingResolution; @@ -146,7 +146,7 @@ namespace ts.server { else { resolution = loader(name, containingFile, compilerOptions, this.moduleResolutionHost); resolution.lastCheckTime = Date.now(); - newResolutions[name] = resolution; + _s(newResolutions, name, resolution); } } @@ -469,7 +469,7 @@ namespace ts.server { return undefined; } - return this.filenameToSourceFile[info.fileName]; + return _g(this.filenameToSourceFile, info.fileName); } getSourceFileFromName(filename: string, requireOpen?: boolean) { @@ -511,7 +511,7 @@ namespace ts.server { const sourceFiles = this.program.getSourceFiles(); for (let i = 0, len = sourceFiles.length; i < len; i++) { const normFilename = ts.normalizePath(sourceFiles[i].fileName); - this.filenameToSourceFile[normFilename] = sourceFiles[i]; + _s(this.filenameToSourceFile, normFilename, sourceFiles[i]); } } @@ -566,7 +566,7 @@ namespace ts.server { } let strBuilder = ""; - ts.forEachProperty(this.filenameToSourceFile, + ts._eachValue(this.filenameToSourceFile, sourceFile => { strBuilder += sourceFile.fileName + "\n"; }); return strBuilder; } @@ -642,7 +642,7 @@ namespace ts.server { getFormatCodeOptions(file?: string) { if (file) { - const info = this.filenameToScriptInfo[file]; + const info = _g(this.filenameToScriptInfo, file); if (info) { return info.formatCodeOptions; } @@ -651,7 +651,7 @@ namespace ts.server { } watchedFileChanged(fileName: string) { - const info = this.filenameToScriptInfo[fileName]; + const info = _g(this.filenameToScriptInfo, fileName); if (!info) { this.psLogger.info("Error: got watch notification for unknown file: " + fileName); } @@ -685,13 +685,13 @@ namespace ts.server { } startTimerForDetectingProjectFileListChanges(project: Project) { - if (this.timerForDetectingProjectFileListChanges[project.projectFilename]) { - this.host.clearTimeout(this.timerForDetectingProjectFileListChanges[project.projectFilename]); + if (_g(this.timerForDetectingProjectFileListChanges, project.projectFilename)) { + this.host.clearTimeout(_g(this.timerForDetectingProjectFileListChanges, project.projectFilename)); } - this.timerForDetectingProjectFileListChanges[project.projectFilename] = this.host.setTimeout( + _s(this.timerForDetectingProjectFileListChanges, project.projectFilename, this.host.setTimeout( () => this.handleProjectFileListChanges(project), 250 - ); + )); } handleProjectFileListChanges(project: Project) { @@ -774,7 +774,7 @@ namespace ts.server { setHostConfiguration(args: ts.server.protocol.ConfigureRequestArguments) { if (args.file) { - const info = this.filenameToScriptInfo[args.file]; + const info = _g(this.filenameToScriptInfo, args.file); if (info) { info.setFormatOptions(args.formatOptions); this.log("Host configuration update for file " + args.file, "Info"); @@ -803,14 +803,14 @@ namespace ts.server { let currentPath = ts.getDirectoryPath(root.fileName); let parentPath = ts.getDirectoryPath(currentPath); while (currentPath != parentPath) { - if (!project.projectService.directoryWatchersForTsconfig[currentPath]) { + if (!_g(project.projectService.directoryWatchersForTsconfig, currentPath)) { this.log("Add watcher for: " + currentPath); - project.projectService.directoryWatchersForTsconfig[currentPath] = - this.host.watchDirectory(currentPath, fileName => this.directoryWatchedForTsconfigChanged(fileName)); - project.projectService.directoryWatchersRefCount[currentPath] = 1; + _s(project.projectService.directoryWatchersForTsconfig, currentPath, + this.host.watchDirectory(currentPath, fileName => this.directoryWatchedForTsconfigChanged(fileName))); + _s(project.projectService.directoryWatchersRefCount, currentPath, 1); } else { - project.projectService.directoryWatchersRefCount[currentPath] += 1; + _mod(project.projectService.directoryWatchersRefCount, currentPath, refCount => refCount + 1); } project.directoriesWatchedForTsconfig.push(currentPath); currentPath = parentPath; @@ -831,7 +831,7 @@ namespace ts.server { } if (!info.isOpen) { - this.filenameToScriptInfo[info.fileName] = undefined; + _s(this.filenameToScriptInfo, info.fileName, undefined); const referencingProjects = this.findReferencingProjects(info); if (info.defaultProject) { info.defaultProject.removeRoot(info); @@ -871,18 +871,18 @@ namespace ts.server { if (project.isConfiguredProject()) { project.projectFileWatcher.close(); project.directoryWatcher.close(); - forEachProperty(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); + _eachValue(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); delete project.directoriesWatchedForWildcards; unorderedRemoveItem(this.configuredProjects, project); } else { for (const directory of project.directoriesWatchedForTsconfig) { // if the ref count for this directory watcher drops to 0, it's time to close it - project.projectService.directoryWatchersRefCount[directory]--; - if (!project.projectService.directoryWatchersRefCount[directory]) { + _mod(project.projectService.directoryWatchersRefCount, directory, refCount => refCount - 1); + if (!_g(project.projectService.directoryWatchersRefCount, directory)) { this.log("Close directory watcher for: " + directory); - project.projectService.directoryWatchersForTsconfig[directory].close(); - delete project.projectService.directoryWatchersForTsconfig[directory]; + _g(project.projectService.directoryWatchersForTsconfig, directory).close(); + _delete(project.projectService.directoryWatchersForTsconfig, directory); } } unorderedRemoveItem(this.inferredProjects, project); @@ -1138,7 +1138,7 @@ namespace ts.server { getScriptInfo(filename: string) { filename = ts.normalizePath(filename); - return this.filenameToScriptInfo[filename]; + return _g(this.filenameToScriptInfo, filename); } /** @@ -1147,7 +1147,7 @@ namespace ts.server { */ openFile(fileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) { fileName = ts.normalizePath(fileName); - let info = this.filenameToScriptInfo[fileName]; + let info = _g(this.filenameToScriptInfo, fileName); if (!info) { let content: string; if (this.host.fileExists(fileName)) { @@ -1162,7 +1162,7 @@ namespace ts.server { info = new ScriptInfo(this.host, fileName, content, openedByClient); info.scriptKind = scriptKind; info.setFormatOptions(this.getFormatCodeOptions()); - this.filenameToScriptInfo[fileName] = info; + _s(this.filenameToScriptInfo, fileName, info); if (!info.isOpen) { info.fileWatcher = this.host.watchFile(fileName, _ => { this.watchedFileChanged(fileName); }); } @@ -1261,7 +1261,7 @@ namespace ts.server { * @param filename is absolute pathname */ closeClientFile(filename: string) { - const info = this.filenameToScriptInfo[filename]; + const info = _g(this.filenameToScriptInfo, filename); if (info) { this.closeOpenFile(info); info.isOpen = false; @@ -1270,14 +1270,14 @@ namespace ts.server { } getProjectForFile(filename: string) { - const scriptInfo = this.filenameToScriptInfo[filename]; + const scriptInfo = _g(this.filenameToScriptInfo, filename); if (scriptInfo) { return scriptInfo.defaultProject; } } printProjectsForFile(filename: string) { - const scriptInfo = this.filenameToScriptInfo[filename]; + const scriptInfo = _g(this.filenameToScriptInfo, filename); if (scriptInfo) { this.psLogger.startGroup(); this.psLogger.info("Projects for " + filename); @@ -1431,19 +1431,33 @@ namespace ts.server { /*recursive*/ true ); - project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { + /*project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); - watchers[directory] = this.host.watchDirectory( + _s(watchers, directory, this.host.watchDirectory( directory, path => this.directoryWatchedForSourceFilesChanged(project, path), recursive - ); + )); } return watchers; - }, >{}); + }, >{});*/ + + project.directoriesWatchedForWildcards = createMap(); + //neater + _each(createMapFromMapLike(projectOptions.wildcardDirectories), (directory, flag) => { + if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { + const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; + this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); + _s(project.directoriesWatchedForWildcards, directory, this.host.watchDirectory( + directory, + path => this.directoryWatchedForSourceFilesChanged(project, path), + recursive + )); + } + }); return { project: project, errors }; } diff --git a/src/server/session.ts b/src/server/session.ts index 1cf971cf5cf8a..7cacc2ee618bf 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1100,7 +1100,7 @@ namespace ts.server { return { response, responseRequired: true }; } - private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({ + private handlers = createMapFromMapLike<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({ [CommandNames.Exit]: () => { this.exit(); return { responseRequired: false }; @@ -1244,14 +1244,14 @@ namespace ts.server { }); public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) { - if (command in this.handlers) { + if (_has(this.handlers, command)) { throw new Error(`Protocol handler already exists for command "${command}"`); } - this.handlers[command] = handler; + _s(this.handlers, command, handler); } public executeCommand(request: protocol.Request): { response?: any, responseRequired?: boolean } { - const handler = this.handlers[request.command]; + const handler = _g(this.handlers, request.command); if (handler) { return handler(request); } diff --git a/src/services/classifier.ts b/src/services/classifier.ts index 29a878224cd3a..71fdf99e83122 100644 --- a/src/services/classifier.ts +++ b/src/services/classifier.ts @@ -462,7 +462,7 @@ namespace ts { } /* @internal */ - export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map, span: TextSpan): ClassifiedSpan[] { + export function getSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Set, span: TextSpan): ClassifiedSpan[] { return convertClassifications(getEncodedSemanticClassifications(typeChecker, cancellationToken, sourceFile, classifiableNames, span)); } @@ -487,7 +487,7 @@ namespace ts { } /* @internal */ - export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Map, span: TextSpan): Classifications { + export function getEncodedSemanticClassifications(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFile: SourceFile, classifiableNames: Set, span: TextSpan): Classifications { const result: number[] = []; processNode(sourceFile); @@ -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 (_setHas(classifiableNames, identifier.text)) { const symbol = typeChecker.getSymbolAtLocation(node); if (symbol) { const type = classifySymbol(symbol, getMeaningFromLocation(node)); diff --git a/src/services/completions.ts b/src/services/completions.ts index d6a4da89ed19f..6296799725e9c 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -58,18 +58,17 @@ namespace ts.Completions { return { isMemberCompletion, isNewIdentifierLocation: isNewIdentifierLocation || isSourceFileJavaScript(sourceFile), entries }; - function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Map): CompletionEntry[] { + function getJavaScriptCompletionEntries(sourceFile: SourceFile, position: number, uniqueNames: Set): CompletionEntry[] { const entries: CompletionEntry[] = []; - const nameTable = getNameTable(sourceFile); - for (const name in nameTable) { + _each(getNameTable(sourceFile), (name, nameTablePosition) => { // Skip identifiers produced only from the current location - if (nameTable[name] === position) { - continue; + if (nameTablePosition === position) { + return; } - if (!uniqueNames[name]) { - uniqueNames[name] = name; + if (!_setHas(uniqueNames, name)) { + _add(uniqueNames, name); const displayName = getCompletionEntryDisplayName(unescapeIdentifier(name), compilerOptions.target, /*performCharacterChecks*/ true); if (displayName) { const entry = { @@ -81,7 +80,7 @@ namespace ts.Completions { entries.push(entry); } } - } + }); return entries; } @@ -112,17 +111,17 @@ namespace ts.Completions { } - function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map { + function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Set { const start = timestamp(); - const uniqueNames = createMap(); + const uniqueNames = createSet(); if (symbols) { for (const symbol of symbols) { const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); - if (!uniqueNames[id]) { + if (!_setHas(uniqueNames, id)) { entries.push(entry); - uniqueNames[id] = id; + _add(uniqueNames, id); } } } @@ -340,7 +339,7 @@ namespace ts.Completions { const files = tryReadDirectory(host, baseDirectory, extensions, /*exclude*/undefined, /*include*/["./*"]); if (files) { - const foundFiles = createMap(); + const foundFiles = createSet(); for (let filePath of files) { filePath = normalizePath(filePath); if (exclude && comparePaths(filePath, exclude, scriptPath, ignoreCase) === Comparison.EqualTo) { @@ -349,14 +348,14 @@ namespace ts.Completions { const foundFileName = includeExtensions ? getBaseFileName(filePath) : removeFileExtension(getBaseFileName(filePath)); - if (!foundFiles[foundFileName]) { - foundFiles[foundFileName] = true; + if (!_setHas(foundFiles, foundFileName)) { + _add(foundFiles, foundFileName); } } - for (const foundFile in foundFiles) { + _eachInSet(foundFiles, foundFile => { result.push(createCompletionEntryForModule(foundFile, ScriptElementKind.scriptElement, span)); - } + }); } // If possible, get folder completion as well @@ -394,7 +393,7 @@ namespace ts.Completions { if (paths) { for (const path in paths) { - if (paths.hasOwnProperty(path)) { + if (paths.hasOwnProperty(path)) { //use hasProperty if (path === "*") { if (paths[path]) { for (const pattern of paths[path]) { @@ -1513,7 +1512,7 @@ namespace ts.Completions { * do not occur at the current position and have not otherwise been typed. */ function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { - const existingImportsOrExports = createMap(); + const existingImportsOrExports = createSet(); for (const element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out @@ -1522,14 +1521,14 @@ namespace ts.Completions { } const name = element.propertyName || element.name; - existingImportsOrExports[name.text] = true; + _add(existingImportsOrExports, name.text); } - if (!someProperties(existingImportsOrExports)) { + if (_setIsEmpty(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" && !_setHas(existingImportsOrExports, e.name)); } /** @@ -1543,7 +1542,7 @@ namespace ts.Completions { return contextualMemberSymbols; } - const existingMemberNames = createMap(); + const existingMemberNames = createSet(); for (const m of existingMembers) { // Ignore omitted expressions for missing members if (m.kind !== SyntaxKind.PropertyAssignment && @@ -1573,10 +1572,10 @@ namespace ts.Completions { existingName = (m.name).text; } - existingMemberNames[existingName] = true; + _add(existingMemberNames, existingName); } - return filter(contextualMemberSymbols, m => !existingMemberNames[m.name]); + return filter(contextualMemberSymbols, m => !_setHas(existingMemberNames, m.name)); } /** @@ -1586,7 +1585,7 @@ namespace ts.Completions { * do not occur at the current position and have not otherwise been typed. */ function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray): Symbol[] { - const seenNames = createMap(); + const seenNames = createSet(); for (const attr of attributes) { // If this is the current item we are editing right now, do not filter it out if (attr.getStart() <= position && position <= attr.getEnd()) { @@ -1594,11 +1593,11 @@ namespace ts.Completions { } if (attr.kind === SyntaxKind.JsxAttribute) { - seenNames[(attr).name.text] = true; + _add(seenNames, (attr).name.text); } } - return filter(symbols, a => !seenNames[a.name]); + return filter(symbols, a => !_setHas(seenNames, a.name)); } } diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 0f18427e56885..51c8a256373fd 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 = _g(fileNameToDocumentHighlights, fileName); if (!documentHighlights) { documentHighlights = { fileName, highlightSpans: [] }; - fileNameToDocumentHighlights[fileName] = documentHighlights; + _s(fileNameToDocumentHighlights, fileName, documentHighlights); result.push(documentHighlights); } diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index d73e4d3b0a125..90abf477319e1 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 = _g(buckets, key); if (!bucket && createIfMissing) { - buckets[key] = bucket = createFileMap(); + _s(buckets, 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 entries = _g(buckets, 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 dcda3e40b1a67..4391465b7d976 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 (_g(nameTable, 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 _g(classSymbol.members, "__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 => { + _eachValue(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 = _g(symbol.members, "__constructor"); if (!ctr) { return []; } @@ -715,12 +715,12 @@ namespace ts.FindAllReferences { } const key = getSymbolId(symbol) + "," + getSymbolId(parent); - if (key in cachedResults) { - return cachedResults[key]; + if (_has(cachedResults, key)) { + return _g(cachedResults, key); } // Set the key so that we don't infinitely recurse - cachedResults[key] = false; + _s(cachedResults, key, false); const inherits = forEach(symbol.getDeclarations(), declaration => { if (isClassLike(declaration)) { @@ -744,7 +744,7 @@ namespace ts.FindAllReferences { return false; }); - cachedResults[key] = inherits; + _s(cachedResults, key, inherits); return inherits; } @@ -1078,7 +1078,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 (_has(previousIterationSymbolsCache, symbol.name)) { return; } @@ -1105,7 +1105,7 @@ namespace ts.FindAllReferences { } // Visit the typeReference as well to see if it directly or indirectly use that property - previousIterationSymbolsCache[symbol.name] = symbol; + _s(previousIterationSymbolsCache, 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..cb81cc477c88e 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 = _g(program.getResolvedTypeReferenceDirectives(), 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 4f0e06244d26f..a88329a5c2f40 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -15,6 +15,7 @@ namespace ts.JsTyping { interface PackageJson { _requiredBy?: string[]; + //Is this a real Mpa, or a MapLike? dependencies?: Map; devDependencies?: Map; name?: string; @@ -58,7 +59,7 @@ namespace ts.JsTyping { if (!safeList) { const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - safeList = createMap(result.config); + safeList = createMapFromMapLike(result.config); } const filesToWatch: string[] = []; @@ -87,27 +88,28 @@ namespace ts.JsTyping { getTypingNamesFromSourceFileNames(fileNames); // 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]; + _each(packageNameToTypingLocation, (name, typingLocation) => { + if (_has(inferredTypings, name) && !_g(inferredTypings, name)) { + _s(inferredTypings, name, typingLocation); } - } + }); // Remove typings that the user has added to the exclude list for (const excludeTypingName of exclude) { - delete inferredTypings[excludeTypingName]; + _delete(inferredTypings, excludeTypingName); } const newTypingNames: string[] = []; const cachedTypingPaths: string[] = []; - for (const typing in inferredTypings) { - if (inferredTypings[typing] !== undefined) { - cachedTypingPaths.push(inferredTypings[typing]); + + _each(inferredTypings, (typing, inferredTyping) => { + if (inferredTyping !== undefined) { + cachedTypingPaths.push(inferredTyping); } else { newTypingNames.push(typing); } - } + }); return { cachedTypingPaths, newTypingNames, filesToWatch }; /** @@ -119,8 +121,8 @@ namespace ts.JsTyping { } for (const typing of typingNames) { - if (!(typing in inferredTypings)) { - inferredTypings[typing] = undefined; + if (!_has(inferredTypings, typing)) { + _s(inferredTypings, typing, undefined); } } } @@ -134,16 +136,16 @@ namespace ts.JsTyping { const jsonConfig: PackageJson = result.config; filesToWatch.push(jsonPath); if (jsonConfig.dependencies) { - mergeTypings(getOwnKeys(jsonConfig.dependencies)); + mergeTypings(_ownKeys(jsonConfig.dependencies)); } if (jsonConfig.devDependencies) { - mergeTypings(getOwnKeys(jsonConfig.devDependencies)); + mergeTypings(_ownKeys(jsonConfig.devDependencies)); } if (jsonConfig.optionalDependencies) { - mergeTypings(getOwnKeys(jsonConfig.optionalDependencies)); + mergeTypings(_ownKeys(jsonConfig.optionalDependencies)); } if (jsonConfig.peerDependencies) { - mergeTypings(getOwnKeys(jsonConfig.peerDependencies)); + mergeTypings(_ownKeys(jsonConfig.peerDependencies)); } } } @@ -162,7 +164,7 @@ namespace ts.JsTyping { mergeTypings(cleanedTypingNames); } else { - mergeTypings(filter(cleanedTypingNames, f => f in safeList)); + mergeTypings(filter(cleanedTypingNames, f => _has(safeList, f))); } const hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); @@ -209,7 +211,7 @@ namespace ts.JsTyping { } if (packageJson.typings) { const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName)); - inferredTypings[packageJson.name] = absolutePath; + _s(inferredTypings, packageJson.name, absolutePath); } else { typingNames.push(packageJson.name); diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index a73a82db4b7ab..d5d596bc49f77 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -9,13 +9,13 @@ namespace ts.NavigateTo { // This means "compare in a case insensitive manner." const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" }; - // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[] - forEach(sourceFiles, sourceFile => { + //Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[] + /*forEach(sourceFiles, sourceFile => { cancellationToken.throwIfCancellationRequested(); const nameToDeclarations = sourceFile.getNamedDeclarations(); - for (const name in nameToDeclarations) { - const declarations = nameToDeclarations[name]; + for (const name in nameToDeclarations) { //todo: actual map loop + const declarations = _g(nameToDeclarations, 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. @@ -31,7 +31,7 @@ namespace ts.NavigateTo { if (patternMatcher.patternContainsDots) { const containers = getContainers(declaration); if (!containers) { - return undefined; + return undefined; //goto next source file } matches = patternMatcher.getMatches(containers, name); @@ -47,7 +47,41 @@ namespace ts.NavigateTo { } } } - }); + });*/ + + for (const sourceFile of sourceFiles) { + cancellationToken.throwIfCancellationRequested(); + + const nameToDeclarations = sourceFile.getNamedDeclarations(); + _eachAndBreakIfReturningTrue(nameToDeclarations, (name, declarations) => { + if (!declarations) { return; } + // 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) { return; } + + for (const declaration of declarations) { + // It was a match! If the pattern has dots in it, then also see if the + // declaration container matches as well. + if (patternMatcher.patternContainsDots) { + const containers = getContainers(declaration); + if (!containers) { + return true; //This means: break out of the declarations loop and go to the next source file + } + + matches = patternMatcher.getMatches(containers, name); + + if (!matches) { + continue; + } + } + + const fileName = sourceFile.fileName; + const matchKind = bestMatchKind(matches); + 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 5c8c47e96665a..8d944bf4b7b43 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -241,9 +241,9 @@ namespace ts.NavigationBar { return true; } - const itemsWithSameName = nameToItems[name]; + const itemsWithSameName = _g(nameToItems, name); if (!itemsWithSameName) { - nameToItems[name] = child; + _s(nameToItems, name, child); return true; } @@ -261,7 +261,7 @@ namespace ts.NavigationBar { if (tryMerge(itemWithSameName, child)) { return false; } - nameToItems[name] = [itemWithSameName, child]; + _s(nameToItems, name, [itemWithSameName, child]); return true; } diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index b4f67ca056ddb..0280fa075e227 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 _getOrUpdate(stringToWordSpans, word, breakIntoWordSpans); } function matchTextChunk(candidate: string, chunk: TextChunk, punctuationStripped: boolean): PatternMatch { diff --git a/src/services/services.ts b/src/services/services.ts index ad07172308365..d83fabc65f234 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -507,7 +507,7 @@ namespace ts { } function getDeclarations(name: string) { - return result[name] || (result[name] = []); + return _getOrUpdate(result, name, () => []); } function getDeclarationName(declaration: Declaration) { @@ -923,7 +923,7 @@ namespace ts { const currentDirectory = host.getCurrentDirectory(); // Check if the localized messages json is set, otherwise query the host for it if (!localizedDiagnosticMessages && host.getLocalizedDiagnosticMessages) { - localizedDiagnosticMessages = host.getLocalizedDiagnosticMessages(); + localizedDiagnosticMessages = createMapFromMapLike(host.getLocalizedDiagnosticMessages()); } function log(message: string) { @@ -1839,7 +1839,7 @@ namespace ts { function walk(node: Node) { switch (node.kind) { case SyntaxKind.Identifier: - nameTable[(node).text] = nameTable[(node).text] === undefined ? node.pos : -1; + _s(nameTable, (node).text, _g(nameTable, (node).text) === undefined ? node.pos : -1); break; case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: @@ -1852,7 +1852,7 @@ namespace ts { isArgumentOfElementAccessExpression(node) || isLiteralComputedPropertyDeclarationName(node)) { - nameTable[(node).text] = nameTable[(node).text] === undefined ? node.pos : -1; + _s(nameTable, (node).text, _g(nameTable, (node).text) === undefined ? node.pos : -1); } break; default: diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 211e55b23ba5e..a601b26bd2b71 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 = _g(nameToDeclarations, name.text); if (declarations) { for (const declaration of declarations) { diff --git a/src/services/transpile.ts b/src/services/transpile.ts index 303ca0d4173a2..5981bc71d1de0 100644 --- a/src/services/transpile.ts +++ b/src/services/transpile.ts @@ -63,7 +63,7 @@ namespace ts { } if (transpileOptions.renamedDependencies) { - sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); + sourceFile.renamedDependencies = createMapFromMapLike(transpileOptions.renamedDependencies); } const newLine = getNewLineCharacter(options); @@ -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" && !_someValue(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 (!_someValue(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } diff --git a/src/services/types.ts b/src/services/types.ts index e8c411cc1c839..c922efe9c22b0 100644 --- a/src/services/types.ts +++ b/src/services/types.ts @@ -131,7 +131,7 @@ namespace ts { getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot | undefined; - getLocalizedDiagnosticMessages?(): any; + getLocalizedDiagnosticMessages?(): any; //any??? getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; diff --git a/tslint.json b/tslint.json index f058eab3dd658..e7d217ec261bd 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 } }