Skip to content

Commit 6a3068a

Browse files
committed
symbol tables
1 parent b4da853 commit 6a3068a

File tree

1 file changed

+91
-7
lines changed

1 file changed

+91
-7
lines changed

docs/compiler/binder-symboltable.md

+91-7
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,111 @@
1-
### SymbolTable
1+
### SymbolTable
22

3-
Its implemented as a simple HashMap. Here is the interface (`types.ts`):
3+
Its implemented as a simple HashMap. Here is the interface (`types.ts`):
44

55
```ts
66
interface SymbolTable {
77
[index: string]: Symbol;
88
}
99
```
1010

11-
SymbolTables as initialized by binding. There are a few SymbolTables used by the compiler.
11+
SymbolTables as initialized by binding. There are a few SymbolTables used by the compiler.
1212

13-
On `Node`:
13+
On `Node`:
1414
```ts
1515
locals?: SymbolTable; // Locals associated with node
1616
```
1717

18-
On `Symbol`:
18+
On `Symbol`:
1919

2020
```ts
2121
members?: SymbolTable; // Class, interface or literal instance members
2222
exports?: SymbolTable; // Module exports
2323
```
2424

25-
#### `locals`
26-
We saw locals getting initialized by `bindChildren` based on `ContainerFlags`. Here's how it gets populated:
25+
Note: We saw `locals` getting initialized (to `{}`) by `bindChildren` based on `ContainerFlags`.
2726

27+
#### SymbolTable population
28+
SymbolTable are populated with `Symbols` primarily by a call to `declareSymbol`. This function is presented below in entirety:
29+
30+
```ts
31+
/**
32+
* Declares a Symbol for the node and adds it to symbols. Reports errors for conflicting identifier names.
33+
* @param symbolTable - The symbol table which node will be added to.
34+
* @param parent - node's parent declaration.
35+
* @param node - The declaration to be added to the symbol table
36+
* @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.)
37+
* @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations.
38+
*/
39+
function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol {
40+
Debug.assert(!hasDynamicName(node));
41+
42+
// The exported symbol for an export default function/class node is always named "default"
43+
let name = node.flags & NodeFlags.Default && parent ? "default" : getDeclarationName(node);
44+
45+
let symbol: Symbol;
46+
if (name !== undefined) {
47+
48+
// Check and see if the symbol table already has a symbol with this name. If not,
49+
// create a new symbol with this name and add it to the table. Note that we don't
50+
// give the new symbol any flags *yet*. This ensures that it will not conflict
51+
// with the 'excludes' flags we pass in.
52+
//
53+
// If we do get an existing symbol, see if it conflicts with the new symbol we're
54+
// creating. For example, a 'var' symbol and a 'class' symbol will conflict within
55+
// the same symbol table. If we have a conflict, report the issue on each
56+
// declaration we have for this symbol, and then create a new symbol for this
57+
// declaration.
58+
//
59+
// If we created a new symbol, either because we didn't have a symbol with this name
60+
// in the symbol table, or we conflicted with an existing symbol, then just add this
61+
// node as the sole declaration of the new symbol.
62+
//
63+
// Otherwise, we'll be merging into a compatible existing symbol (for example when
64+
// you have multiple 'vars' with the same name in the same container). In this case
65+
// just add this node into the declarations list of the symbol.
66+
symbol = hasProperty(symbolTable, name)
67+
? symbolTable[name]
68+
: (symbolTable[name] = createSymbol(SymbolFlags.None, name));
69+
70+
if (name && (includes & SymbolFlags.Classifiable)) {
71+
classifiableNames[name] = name;
72+
}
73+
74+
if (symbol.flags & excludes) {
75+
if (node.name) {
76+
node.name.parent = node;
77+
}
78+
79+
// Report errors every position with duplicate declaration
80+
// Report errors on previous encountered declarations
81+
let message = symbol.flags & SymbolFlags.BlockScopedVariable
82+
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
83+
: Diagnostics.Duplicate_identifier_0;
84+
forEach(symbol.declarations, declaration => {
85+
file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
86+
});
87+
file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
88+
89+
symbol = createSymbol(SymbolFlags.None, name);
90+
}
91+
}
92+
else {
93+
symbol = createSymbol(SymbolFlags.None, "__missing");
94+
}
95+
96+
addDeclarationToSymbol(symbol, node, includes);
97+
symbol.parent = parent;
98+
99+
return symbol;
100+
}
101+
```
102+
103+
Which SymbolTable gets populated is driven by the first argument to this function. e.g. when adding a declaration to a *container* of kind `SyntaxKind.ClassDeclaration` or `SytanxKind.ClassExpression` the function `declareClassMember` will get called which has the following code:
104+
105+
```ts
106+
function declareClassMember(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
107+
return node.flags & NodeFlags.Static
108+
? declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes)
109+
: declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes);
110+
}
111+
```

0 commit comments

Comments
 (0)