Skip to content

Commit a604d84

Browse files
authored
guard against visiting the same symbol table multiple times (#12818)
1 parent 23ec976 commit a604d84

File tree

5 files changed

+127
-24
lines changed

5 files changed

+127
-24
lines changed

src/compiler/checker.ts

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,7 +1742,19 @@ namespace ts {
17421742
}
17431743

17441744
function getAccessibleSymbolChain(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags, useOnlyExternalAliasing: boolean): Symbol[] {
1745-
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] {
1745+
function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable) {
1746+
return getAccessibleSymbolChainFromSymbolTableWorker(symbols, []);
1747+
}
1748+
1749+
function getAccessibleSymbolChainFromSymbolTableWorker(symbols: SymbolTable, visitedSymbolTables: SymbolTable[]): Symbol[] {
1750+
if (contains(visitedSymbolTables, symbols)) {
1751+
return undefined;
1752+
}
1753+
visitedSymbolTables.push(symbols);
1754+
const result = trySymbolTable(symbols);
1755+
visitedSymbolTables.pop();
1756+
return result;
1757+
17461758
function canQualifySymbol(symbolFromSymbolTable: Symbol, meaning: SymbolFlags) {
17471759
// If the symbol is equivalent and doesn't need further qualification, this symbol is accessible
17481760
if (!needsQualification(symbolFromSymbolTable, enclosingDeclaration, meaning)) {
@@ -1764,34 +1776,36 @@ namespace ts {
17641776
}
17651777
}
17661778

1767-
// If symbol is directly available by its name in the symbol table
1768-
if (isAccessible(symbols[symbol.name])) {
1769-
return [symbol];
1770-
}
1779+
function trySymbolTable(symbols: SymbolTable) {
1780+
// If symbol is directly available by its name in the symbol table
1781+
if (isAccessible(symbols[symbol.name])) {
1782+
return [symbol];
1783+
}
17711784

1772-
// Check if symbol is any of the alias
1773-
return forEachProperty(symbols, symbolFromSymbolTable => {
1774-
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
1775-
&& symbolFromSymbolTable.name !== "export="
1776-
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
1777-
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
1778-
// Is this external alias, then use it to name
1779-
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
1785+
// Check if symbol is any of the alias
1786+
return forEachProperty(symbols, symbolFromSymbolTable => {
1787+
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
1788+
&& symbolFromSymbolTable.name !== "export="
1789+
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
1790+
if (!useOnlyExternalAliasing || // We can use any type of alias to get the name
1791+
// Is this external alias, then use it to name
1792+
ts.forEach(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration)) {
17801793

1781-
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
1782-
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
1783-
return [symbolFromSymbolTable];
1784-
}
1794+
const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable);
1795+
if (isAccessible(symbolFromSymbolTable, resolveAlias(symbolFromSymbolTable))) {
1796+
return [symbolFromSymbolTable];
1797+
}
17851798

1786-
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
1787-
// but only if the symbolFromSymbolTable can be qualified
1788-
const accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTable(resolvedImportedSymbol.exports) : undefined;
1789-
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
1790-
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
1799+
// Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain
1800+
// but only if the symbolFromSymbolTable can be qualified
1801+
const accessibleSymbolsFromExports = resolvedImportedSymbol.exports ? getAccessibleSymbolChainFromSymbolTableWorker(resolvedImportedSymbol.exports, visitedSymbolTables) : undefined;
1802+
if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) {
1803+
return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports);
1804+
}
17911805
}
17921806
}
1793-
}
1794-
});
1807+
});
1808+
}
17951809
}
17961810

17971811
if (symbol) {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//// [tests/cases/compiler/circularReferenceInImport.ts] ////
2+
3+
//// [db.d.ts]
4+
5+
declare namespace Db {
6+
export import Types = Db;
7+
}
8+
9+
export = Db;
10+
11+
//// [app.ts]
12+
import * as Db from "./db"
13+
14+
export function foo() {
15+
return new Object()
16+
}
17+
18+
//// [app.js]
19+
"use strict";
20+
function foo() {
21+
return new Object();
22+
}
23+
exports.foo = foo;
24+
25+
26+
//// [app.d.ts]
27+
export declare function foo(): Object;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/db.d.ts ===
2+
3+
declare namespace Db {
4+
>Db : Symbol(Types, Decl(db.d.ts, 0, 0))
5+
6+
export import Types = Db;
7+
>Types : Symbol(Types, Decl(db.d.ts, 1, 22))
8+
>Db : Symbol(Types, Decl(db.d.ts, 0, 0))
9+
}
10+
11+
export = Db;
12+
>Db : Symbol(Db, Decl(db.d.ts, 0, 0))
13+
14+
=== tests/cases/compiler/app.ts ===
15+
import * as Db from "./db"
16+
>Db : Symbol(Db, Decl(app.ts, 0, 6))
17+
18+
export function foo() {
19+
>foo : Symbol(foo, Decl(app.ts, 0, 26))
20+
21+
return new Object()
22+
>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
23+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/compiler/db.d.ts ===
2+
3+
declare namespace Db {
4+
>Db : typeof Types
5+
6+
export import Types = Db;
7+
>Types : typeof Types
8+
>Db : typeof Types
9+
}
10+
11+
export = Db;
12+
>Db : typeof Db
13+
14+
=== tests/cases/compiler/app.ts ===
15+
import * as Db from "./db"
16+
>Db : typeof Db
17+
18+
export function foo() {
19+
>foo : () => Object
20+
21+
return new Object()
22+
>new Object() : Object
23+
>Object : ObjectConstructor
24+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @declaration: true
2+
3+
// @filename: db.d.ts
4+
declare namespace Db {
5+
export import Types = Db;
6+
}
7+
8+
export = Db;
9+
10+
// @filename: app.ts
11+
import * as Db from "./db"
12+
13+
export function foo() {
14+
return new Object()
15+
}

0 commit comments

Comments
 (0)