1 | import {
|
2 | isBlockScopedDeclaration,
|
3 | isFunctionScopedDeclaration,
|
4 | isNonTopLevelDeclaration,
|
5 | } from "./parser/tokenizer";
|
6 |
|
7 | import {TokenType as tt} from "./parser/tokenizer/types";
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | export default function identifyShadowedGlobals(
|
15 | tokens,
|
16 | scopes,
|
17 | globalNames,
|
18 | ) {
|
19 | if (!hasShadowedGlobals(tokens, globalNames)) {
|
20 | return;
|
21 | }
|
22 | markShadowedGlobals(tokens, scopes, globalNames);
|
23 | }
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 | export function hasShadowedGlobals(tokens, globalNames) {
|
31 | for (const token of tokens.tokens) {
|
32 | if (
|
33 | token.type === tt.name &&
|
34 | isNonTopLevelDeclaration(token) &&
|
35 | globalNames.has(tokens.identifierNameForToken(token))
|
36 | ) {
|
37 | return true;
|
38 | }
|
39 | }
|
40 | return false;
|
41 | }
|
42 |
|
43 | function markShadowedGlobals(
|
44 | tokens,
|
45 | scopes,
|
46 | globalNames,
|
47 | ) {
|
48 | const scopeStack = [];
|
49 | let scopeIndex = scopes.length - 1;
|
50 |
|
51 |
|
52 | for (let i = tokens.tokens.length - 1; ; i--) {
|
53 | while (scopeStack.length > 0 && scopeStack[scopeStack.length - 1].startTokenIndex === i + 1) {
|
54 | scopeStack.pop();
|
55 | }
|
56 | while (scopeIndex >= 0 && scopes[scopeIndex].endTokenIndex === i + 1) {
|
57 | scopeStack.push(scopes[scopeIndex]);
|
58 | scopeIndex--;
|
59 | }
|
60 |
|
61 | if (i < 0) {
|
62 | break;
|
63 | }
|
64 |
|
65 | const token = tokens.tokens[i];
|
66 | const name = tokens.identifierNameForToken(token);
|
67 | if (scopeStack.length > 1 && token.type === tt.name && globalNames.has(name)) {
|
68 | if (isBlockScopedDeclaration(token)) {
|
69 | markShadowedForScope(scopeStack[scopeStack.length - 1], tokens, name);
|
70 | } else if (isFunctionScopedDeclaration(token)) {
|
71 | let stackIndex = scopeStack.length - 1;
|
72 | while (stackIndex > 0 && !scopeStack[stackIndex].isFunctionScope) {
|
73 | stackIndex--;
|
74 | }
|
75 | if (stackIndex < 0) {
|
76 | throw new Error("Did not find parent function scope.");
|
77 | }
|
78 | markShadowedForScope(scopeStack[stackIndex], tokens, name);
|
79 | }
|
80 | }
|
81 | }
|
82 | if (scopeStack.length > 0) {
|
83 | throw new Error("Expected empty scope stack after processing file.");
|
84 | }
|
85 | }
|
86 |
|
87 | function markShadowedForScope(scope, tokens, name) {
|
88 | for (let i = scope.startTokenIndex; i < scope.endTokenIndex; i++) {
|
89 | const token = tokens.tokens[i];
|
90 | if (token.type === tt.name && tokens.identifierNameForToken(token) === name) {
|
91 | token.shadowsGlobal = true;
|
92 | }
|
93 | }
|
94 | }
|