-
Notifications
You must be signed in to change notification settings - Fork 1
/
scope.js
112 lines (104 loc) · 4.01 KB
/
scope.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
var walk = require("acorn-walk");
exports.buildScopes = function(ast, fail) {
var scopes = [];
function makeScope(prev, type) {
var scope = {vars: Object.create(null), prev: prev, type: type};
scopes.push(scope);
return scope;
}
function fnScope(scope) {
while (scope.type != "fn") scope = scope.prev;
return scope;
}
function addVar(scope, name, type, node, deadZone, written) {
if (deadZone && (name in scope.vars))
fail("Duplicate definition of " + name, node.loc);
scope.vars[name] = {type: type, node: node, deadZone: deadZone && scope,
written: written, read: false};
}
function makeCx(scope, binding) {
return {scope: scope, binding: binding};
}
function isBlockScopedDecl(node) {
return node.type == "VariableDeclaration" && node.kind != "var";
}
var topScope = makeScope(null, "fn");
walk.recursive(ast, makeCx(topScope), {
Function: function(node, cx, c) {
var inner = node.scope = node.body.scope = makeScope(cx.scope, "fn");
var innerCx = makeCx(inner, {scope: inner, type: "argument", deadZone: true, written: true});
for (var i = 0; i < node.params.length; ++i)
c(node.params[i], innerCx, "Pattern");
if (node.id) {
var decl = node.type == "FunctionDeclaration";
addVar(decl ? cx.scope : inner, node.id.name,
decl ? "function" : "function name", node.id, false, true);
}
c(node.body, innerCx, node.expression ? "Expression" : "Statement")
},
TryStatement: function(node, cx, c) {
c(node.block, cx, "Statement");
if (node.handler) {
var inner = node.handler.body.scope = makeScope(cx.scope, "block");
if (node.handler.param) addVar(inner, node.handler.param.name, "catch clause", node.handler.param, false, true);
c(node.handler.body, makeCx(inner), "Statement");
}
if (node.finalizer) c(node.finalizer, cx, "Statement");
},
Class: function(node, cx, c) {
if (node.id && node.type == "ClassDeclaration")
addVar(cx.scope, node.id.name, "class name", node, true, true);
if (node.superClass) c(node.superClass, cx, "Expression");
for (var i = 0; i < node.body.body.length; i++)
c(node.body.body[i], cx);
},
ImportDeclaration: function(node, cx) {
for (var i = 0; i < node.specifiers.length; i++) {
var spec = node.specifiers[i].local
addVar(cx.scope, spec.name, "import", spec, false, true)
}
},
Expression: function(node, cx, c) {
if (cx.binding) cx = makeCx(cx.scope)
c(node, cx);
},
VariableDeclaration: function(node, cx, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
c(decl.id, makeCx(cx.scope, {
scope: node.kind == "var" ? fnScope(cx.scope) : cx.scope,
type: node.kind == "const" ? "constant" : "variable",
deadZone: node.kind != "var",
written: !!decl.init
}), "Pattern");
if (decl.init) c(decl.init, cx, "Expression");
}
},
VariablePattern: function(node, cx) {
var b = cx.binding;
if (b) addVar(b.scope, node.name, b.type, node, b.deadZone, b.written);
},
BlockStatement: function(node, cx, c) {
if (!node.scope && node.body.some(isBlockScopedDecl)) {
node.scope = makeScope(cx.scope, "block");
cx = makeCx(node.scope)
}
walk.base.BlockStatement(node, cx, c);
},
ForInStatement: function(node, cx, c) {
if (!node.scope && isBlockScopedDecl(node.left)) {
node.scope = node.body.scope = makeScope(cx.scope, "block");
cx = makeCx(node.scope);
}
walk.base.ForInStatement(node, cx, c);
},
ForStatement: function(node, cx, c) {
if (!node.scope && node.init && isBlockScopedDecl(node.init)) {
node.scope = node.body.scope = makeScope(cx.scope, "block");
cx = makeCx(node.scope);
}
walk.base.ForStatement(node, cx, c);
}
}, null);
return {all: scopes, top: topScope};
};