|
1 |
| -### SymbolTable |
| 1 | +### SymbolTable |
2 | 2 |
|
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`): |
4 | 4 |
|
5 | 5 | ```ts
|
6 | 6 | interface SymbolTable {
|
7 | 7 | [index: string]: Symbol;
|
8 | 8 | }
|
9 | 9 | ```
|
10 | 10 |
|
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. |
12 | 12 |
|
13 |
| -On `Node`: |
| 13 | +On `Node`: |
14 | 14 | ```ts
|
15 | 15 | locals?: SymbolTable; // Locals associated with node
|
16 | 16 | ```
|
17 | 17 |
|
18 |
| -On `Symbol`: |
| 18 | +On `Symbol`: |
19 | 19 |
|
20 | 20 | ```ts
|
21 | 21 | members?: SymbolTable; // Class, interface or literal instance members
|
22 | 22 | exports?: SymbolTable; // Module exports
|
23 | 23 | ```
|
24 | 24 |
|
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`. |
27 | 26 |
|
| 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