-
Notifications
You must be signed in to change notification settings - Fork 1
/
lib.js
139 lines (127 loc) · 4.3 KB
/
lib.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
var generateLabels = require("./label");
var basic = require("./basic_control");
var continue_and_break = require("./continue_break_control");
var hoist = require("./hoist");
var switchControl = require("./switch_control");
var immutable = require("immutable");
/**
* Creates a new flow given the node list, all the flows, and specifically which
* flow this object should represent and the type of edge
*/
function Flow(nodeList, flows, node, type) {
this.nodeList = nodeList;
this.flows = flows;
this.node = node;
this.type = type;
}
/**
* Gets the node, this is similar to an AST node, but with all inner nodes
* replaced by numbers to make the structure easier to persist to disk
*/
Flow.prototype.getNode = function() {
return this.nodeList.get(this.node.node);
}
/**
* Gets a unique identifier for this flow, this will be an integer for quick
* comparison where the number of AST nodes is small enough
*/
Flow.prototype.getId = function() {
if (this.node.node > ((Number.MAX_SAFE_INTEGER - 2) / 3) - 1) {
return this.node.node + "." + this.node.type;
}
var id = 3 * (this.node.node + 1);
switch(this.node.type) {
case "start":
id += 0;
break;
case "end":
id += 1;
break;
case "hoist":
id += 2;
break;
}
return id;
}
/**
* Gets whether we are entering this node. For example:
* > { statement1; }
* We would first enter the BlockStatement, then enter statement1, then exit
* statement1, then exit the BlockStatement. So we would visit both nodes twice
* once in their entrance capacity and once in their exit capacity.
*/
Flow.prototype.isEnter = function() {
return this.node.type == "start"
}
/**
* Gets whether we are exiting this node. For example:
* > { statement1; }
* We would first enter the BlockStatement, then enter statement1, then exit
* statement1, then exit the BlockStatement. So we would visit both nodes twice
* once in their entrance capacity and once in their exit capacity.
*/
Flow.prototype.isExit = function() {
return this.node.type == "end"
}
/**
* Gets whether we are hoisting this node. For example:
* > { var x; statement1; }
* We would first hoist the variable declaration for "x", then we would enter
* the BlockStatement, then enter the variable declaration, etc
* There's more on this here: https://www.w3schools.com/js/js_hoisting.asp
* Note that this implements Chrome/IE/Safari hoisting, not Firefox
*/
Flow.prototype.isHoist = function() {
return this.node.type == "hoist"
}
/**
* Get all the statements which flow into this one. I.e. given:
* > statement1; statement2; statement3;
* If we call statement2.getBackwardsFlows, we'd get [statement1]. Note the
* output is a list, as multiple edges can come into a statement
*/
Flow.prototype.getBackwardsFlows = function() {
return this.flows.filter((value) => {
return this.node.type == value.end.type && this.node.node == value.end.node;
}).map((flow) => {
return new Flow(this.nodeList, this.flows, flow.start, flow.type)
}).toJS();
}
/**
* Get all the statements which flow out of this one. I.e. given:
* > statement1; statement2; statement3;
* If we call statement2.getForwardFlows, we'd get [statement3]. Note the
* output is a list, as this node could go on to multiple statements
*/
Flow.prototype.getForwardFlows = function() {
return this.flows.filter((value) => {
return this.node.type == value.start.type && this.node.node == value.start.node;
}).map((flow) => {
return new Flow(this.nodeList, this.flows, flow.end, flow.type)
}).toJS();
}
/**
* Gets the flows from a given AST. Call .getStartOfFlow() or .getEndOfFlow() to
* grab the start or end statements. Call .getNode(n) to get a node from a
* specific index
*/
module.exports = function(ast) {
var out = generateLabels(ast);
var basic_control = basic(out);
var continue_and_break_control = continue_and_break(out);
var hoist_control = hoist(out);
var switch_control = switchControl(out);
var flows = immutable.List(hoist_control)
.concat(basic_control)
.concat(continue_and_break_control)
.concat(switch_control);
return {
getStartOfFlow: () => (new Flow(
out, flows, {node: 0, type: "hoist"}, "hoist"
)),
getEndOfFlow: () => (new Flow(
out, flows, {node: 0, type: "end"}, "end"
)),
getNode: (node) => (out.get(node)),
}
}