furnace-xray 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +60 -0
- data/Rakefile +1 -0
- data/bin/furnace-xray +23 -0
- data/furnace-xray.gemspec +33 -0
- data/lib/furnace-xray.rb +1 -0
- data/lib/furnace-xray/app/app.rb +56 -0
- data/lib/furnace-xray/app/assets/images/chosen-sprite.png +0 -0
- data/lib/furnace-xray/app/assets/images/evil_martians_bw_logo.svg +3 -0
- data/lib/furnace-xray/app/assets/images/evil_martians_logo.svg +3 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_diagonal-maze_20_6e4f1c_10x10.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_diagonal-maze_40_000000_10x10.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_10_eceadf_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_10_f8f7f6_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_15_eceadf_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_15_f7f3de_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_15_ffffff_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_65_654b24_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-bg_fine-grain_68_b83400_60x60.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_222222_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_3572ac_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_8c291d_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_b83400_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_fbdb93_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/images/ui/ui-icons_ffffff_256x240.png +0 -0
- data/lib/furnace-xray/app/assets/javascripts/application.js.coffee +219 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/drawer.js.coffee +206 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/graph.js.coffee +26 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/input.js.coffee +134 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/input_state.js.coffee +8 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/map.js.coffee +45 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/argument.js.coffee +7 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/block.js.coffee +76 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/function.js.coffee +15 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/instruction.js.coffee +33 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/operand.js.coffee +21 -0
- data/lib/furnace-xray/app/assets/javascripts/lib/nodes/type.js.coffee +12 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/chosen.jquery.js +1090 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/d3.v3.js +7806 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/dagre.js +4053 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/hotkeys.jquery.js +106 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/jquery.js +9472 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/mustache.js +532 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/stacktrace-0.4.js +424 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/sugar-1.3.8.js +8615 -0
- data/lib/furnace-xray/app/assets/javascripts/vendor/ui.jquery.js +14850 -0
- data/lib/furnace-xray/app/assets/stylesheets/app.css.sass +199 -0
- data/lib/furnace-xray/app/assets/stylesheets/chosen.jquery.css +413 -0
- data/lib/furnace-xray/app/assets/stylesheets/elements.css.sass +31 -0
- data/lib/furnace-xray/app/assets/stylesheets/graph.css.sass +29 -0
- data/lib/furnace-xray/app/assets/stylesheets/ui.jquery.css +1174 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.afm +170 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.eot +0 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.otf +0 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.svg +553 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.ttf +0 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke.woff +0 -0
- data/lib/furnace-xray/app/public/fonts/iconic_stroke_demo.html +1 -0
- data/lib/furnace-xray/app/views/index.haml +52 -0
- data/lib/furnace-xray/app/views/nodes/argument.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/block.jst.mustache +4 -0
- data/lib/furnace-xray/app/views/nodes/diff/added_line.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/diff/changed_line.jst.mustache +2 -0
- data/lib/furnace-xray/app/views/nodes/diff/removed_line.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/diff/unchanged_line.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/function.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/function_removed.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/instruction_typed.jst.mustache +3 -0
- data/lib/furnace-xray/app/views/nodes/instruction_void.jst.mustache +3 -0
- data/lib/furnace-xray/app/views/nodes/operand_argument.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/operand_basic_block.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/operand_constant.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/operand_constant_function.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/operand_instruction.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/type_constant.jst.mustache +1 -0
- data/lib/furnace-xray/app/views/nodes/type_parametric.jst.mustache +3 -0
- data/lib/furnace-xray/lib/jst_pages.rb +226 -0
- data/lib/furnace-xray/version.rb +5 -0
- data/sample.json +1 -0
- metadata +251 -0
@@ -0,0 +1,206 @@
|
|
1
|
+
#
|
2
|
+
# Graph drawing routines.
|
3
|
+
# Drawer manages SVG canvas exclusively – avoid interferention
|
4
|
+
#
|
5
|
+
class @Drawer
|
6
|
+
|
7
|
+
@canvasPadding = 20
|
8
|
+
|
9
|
+
@attach: (selector) ->
|
10
|
+
@svg = d3.select(selector)
|
11
|
+
@group = @svg.append("g").attr("class", "container")
|
12
|
+
@zoom = d3.behavior.zoom()
|
13
|
+
|
14
|
+
@zoom.on "zoom", =>
|
15
|
+
@group.attr("transform", "translate(#{d3.event.translate}) scale(#{d3.event.scale})")
|
16
|
+
|
17
|
+
@svg.call(@zoom).on("dblclick.zoom", null)
|
18
|
+
|
19
|
+
@zoomTo: (t, s) ->
|
20
|
+
@zoom.translate(t)
|
21
|
+
@zoom.scale(s) if s
|
22
|
+
@group.attr("transform", "translate(#{t}) scale(#{s || @zoom.scale()})")
|
23
|
+
|
24
|
+
@clear: ->
|
25
|
+
@group?.select("g").remove()
|
26
|
+
|
27
|
+
@reset: ->
|
28
|
+
@zoomTo [@canvasPadding, @canvasPadding]
|
29
|
+
|
30
|
+
constructor: (@graph, padding=10) ->
|
31
|
+
@padding = padding
|
32
|
+
|
33
|
+
@setupEntities()
|
34
|
+
@draw()
|
35
|
+
|
36
|
+
repaint: ->
|
37
|
+
@container.attr("transform", "translate(0,1)")
|
38
|
+
@container.attr("transform", "translate(0,0)")
|
39
|
+
|
40
|
+
fit: (width, height) ->
|
41
|
+
@zoomValue = [@constructor.zoom.translate(), @constructor.zoom.scale()]
|
42
|
+
|
43
|
+
width = (width-@constructor.canvasPadding*2) / @width()
|
44
|
+
height = (height-@constructor.canvasPadding*2) / @height()
|
45
|
+
ratio = [width, height].min()
|
46
|
+
|
47
|
+
@constructor.zoomTo([@constructor.canvasPadding,@constructor.canvasPadding], ratio)
|
48
|
+
|
49
|
+
unfit: ->
|
50
|
+
@constructor.zoomTo @zoomValue...
|
51
|
+
|
52
|
+
zoomNode: (width, height, name) ->
|
53
|
+
node = @container.select("#node-#{name}")
|
54
|
+
return if node.empty()
|
55
|
+
|
56
|
+
padding = @constructor.canvasPadding
|
57
|
+
width = width-padding*2
|
58
|
+
height = height-padding*2
|
59
|
+
|
60
|
+
zoom = @constructor.zoom.scale()
|
61
|
+
bbox = node[0][0].getBBox()
|
62
|
+
|
63
|
+
if width < bbox.width*zoom || height < bbox.height*zoom
|
64
|
+
zoomTo = zoom = [width/bbox.width, height/bbox.height].min()
|
65
|
+
|
66
|
+
x = (-node.attr("x").toNumber()*zoom+width/2-bbox.width*zoom/2)+padding
|
67
|
+
y = (-node.attr("y").toNumber()*zoom+height/2-bbox.height*zoom/2)+padding
|
68
|
+
|
69
|
+
@constructor.zoomTo [x, y], zoomTo
|
70
|
+
|
71
|
+
setupEntities: ->
|
72
|
+
@container = @constructor.group.append("g")
|
73
|
+
|
74
|
+
# `nodes` is center positioned for easy layout later
|
75
|
+
@nodes = @container.selectAll("g .node")
|
76
|
+
.data(@graph.nodes)
|
77
|
+
.enter()
|
78
|
+
.append("g")
|
79
|
+
.attr("class", "node")
|
80
|
+
.attr("id", (d) -> "node-" + d.label)
|
81
|
+
|
82
|
+
@edges = @container.selectAll("g .edge")
|
83
|
+
.data(@graph.edges)
|
84
|
+
.enter()
|
85
|
+
.append("g")
|
86
|
+
.attr("class", "edge")
|
87
|
+
|
88
|
+
@edges
|
89
|
+
.append("path")
|
90
|
+
.attr("marker-end", "url(#arrowhead)")
|
91
|
+
|
92
|
+
@assignHtmlLabels(@nodes)
|
93
|
+
@assignTextLabels(@edges, 1)
|
94
|
+
|
95
|
+
assignTextLabels: (items, padding) ->
|
96
|
+
groups = items.append("g").attr("class", "label")
|
97
|
+
rects = groups.append("rect")
|
98
|
+
labels = groups.append("text")
|
99
|
+
|
100
|
+
labels.attr("text-anchor", "left")
|
101
|
+
.append("tspan")
|
102
|
+
.attr("dy", "1em")
|
103
|
+
.text((d) -> d.data)
|
104
|
+
|
105
|
+
@setupEntitiesSizes(rects, labels, padding)
|
106
|
+
|
107
|
+
assignHtmlLabels: (items, padding) ->
|
108
|
+
groups = items.append("g").attr("class", "label")
|
109
|
+
rects = groups.append("rect")
|
110
|
+
labels = groups.append("foreignObject")
|
111
|
+
texts = labels.append("xhtml:div").style
|
112
|
+
"float": "left"
|
113
|
+
"white-space": "nowrap"
|
114
|
+
|
115
|
+
texts.html((d) -> d.data).each (d) ->
|
116
|
+
if @clientWidth > 900
|
117
|
+
d3.select(@).style
|
118
|
+
"white-space": "normal"
|
119
|
+
"width": '900px'
|
120
|
+
|
121
|
+
texts
|
122
|
+
.each (d) ->
|
123
|
+
d.width = @clientWidth
|
124
|
+
d.height = @clientHeight
|
125
|
+
d.nodePadding = 0
|
126
|
+
|
127
|
+
labels
|
128
|
+
.attr("width", (d) -> d.width)
|
129
|
+
.attr("height", (d) -> d.height)
|
130
|
+
|
131
|
+
@setupEntitiesSizes(rects, labels, padding)
|
132
|
+
|
133
|
+
setupEntitiesSizes: (rects, labels, padding) ->
|
134
|
+
padding = @padding unless padding
|
135
|
+
|
136
|
+
# We need width and height for layout.
|
137
|
+
labels.each (d) ->
|
138
|
+
bbox = @getBBox()
|
139
|
+
d.bbox = bbox
|
140
|
+
d.width = bbox.width + 2 * padding
|
141
|
+
d.height = bbox.height + 2 * padding
|
142
|
+
|
143
|
+
rects
|
144
|
+
.attr("width", (d) -> d.width)
|
145
|
+
.attr("height", (d) -> d.height)
|
146
|
+
|
147
|
+
labels
|
148
|
+
.attr("x", (d) -> padding)
|
149
|
+
.attr("y", (d) -> padding)
|
150
|
+
|
151
|
+
width: -> @container[0][0].getBBox().width
|
152
|
+
height: -> @container[0][0].getBBox().height
|
153
|
+
|
154
|
+
draw: ->
|
155
|
+
dagre.layout().nodeSep(50).edgeSep(50).rankSep(50)
|
156
|
+
.nodes(@graph.nodes)
|
157
|
+
.edges(@graph.edges)
|
158
|
+
.run()
|
159
|
+
|
160
|
+
# Positioning nodes
|
161
|
+
@nodes.attr "transform", (d, i) =>
|
162
|
+
x = d.dagre.x - d.width/2
|
163
|
+
y = d.dagre.y - d.height/2
|
164
|
+
|
165
|
+
@nodes[0][i].setAttribute 'x', x
|
166
|
+
@nodes[0][i].setAttribute 'y', y
|
167
|
+
|
168
|
+
"translate(#{x},#{y})"
|
169
|
+
|
170
|
+
# Ensure that we have at least two points between source and target
|
171
|
+
@edges.each (d) ->
|
172
|
+
points = d.dagre.points
|
173
|
+
unless points.length
|
174
|
+
s = d.source.dagre
|
175
|
+
t = d.target.dagre
|
176
|
+
points.push
|
177
|
+
x: (s.x + t.x) / 2
|
178
|
+
y: (s.y + t.y) / 2
|
179
|
+
|
180
|
+
if points.length is 1
|
181
|
+
points.push
|
182
|
+
x: points[0].x
|
183
|
+
y: points[0].y
|
184
|
+
|
185
|
+
# Setting correct path coordinates
|
186
|
+
@edges.selectAll("path").attr "d", (e) ->
|
187
|
+
points = e.dagre.points.slice(0)
|
188
|
+
source = dagre.util.intersectRect(e.source.dagre, points[0])
|
189
|
+
target = dagre.util.intersectRect(e.target.dagre, points[points.length - 1])
|
190
|
+
|
191
|
+
points.unshift source
|
192
|
+
points.push target
|
193
|
+
|
194
|
+
d3.svg.line()
|
195
|
+
.x((d) -> d.x)
|
196
|
+
.y((d) -> d.y)
|
197
|
+
.interpolate("linear")(points)
|
198
|
+
|
199
|
+
# Positioning edges labels
|
200
|
+
@edges.selectAll("g.label")
|
201
|
+
.attr("transform", (d) ->
|
202
|
+
points = d.dagre.points;
|
203
|
+
x = (points[0].x + points[1].x) / 2;
|
204
|
+
y = (points[0].y + points[1].y) / 2;
|
205
|
+
return "translate(" + (-d.bbox.width / 2 + x) + "," + (-d.bbox.height / 2 + y) + ")";
|
206
|
+
)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#
|
2
|
+
# Maps internal JSON representation to a flat
|
3
|
+
# graph structure consumable by `Drawer`
|
4
|
+
#
|
5
|
+
class @Graph
|
6
|
+
constructor: (@input) ->
|
7
|
+
|
8
|
+
@edges = []
|
9
|
+
blockNodes = Object.extended()
|
10
|
+
|
11
|
+
@input.blocks.each (i, b) =>
|
12
|
+
blockNodes[b.name] =
|
13
|
+
edges: b.references()
|
14
|
+
label: b.name
|
15
|
+
data: b.title(@input.previousState?.blocks[b.name])
|
16
|
+
|
17
|
+
blockNodes.each (i, n) =>
|
18
|
+
edges = []
|
19
|
+
|
20
|
+
n.edges.each (x) =>
|
21
|
+
edges.add {source: n, target: blockNodes[x], data: blockNodes[x].label}
|
22
|
+
@edges.add edges.last()
|
23
|
+
|
24
|
+
n.edges = edges
|
25
|
+
|
26
|
+
@nodes = blockNodes.values()
|
@@ -0,0 +1,134 @@
|
|
1
|
+
#
|
2
|
+
# Internal representation of input JSON
|
3
|
+
#
|
4
|
+
class @Input
|
5
|
+
@normalize: (data) ->
|
6
|
+
data.map (x) -> new Input(x)
|
7
|
+
|
8
|
+
constructor: (@source) ->
|
9
|
+
@source.events.each (x) -> x.event = x.event.camelize(false)
|
10
|
+
|
11
|
+
# Scan for effective events
|
12
|
+
@events = []
|
13
|
+
reducer = []
|
14
|
+
transforms = Object.extended()
|
15
|
+
|
16
|
+
@source.events.each (x, i) =>
|
17
|
+
switch x.event
|
18
|
+
when 'addInstruction'
|
19
|
+
reducer.add x.name
|
20
|
+
@events.add i
|
21
|
+
when 'removeInstruction'
|
22
|
+
reducer.exclude x.name
|
23
|
+
@events.add i
|
24
|
+
when 'updateInstruction'
|
25
|
+
@events.add i if reducer.any(x.name)
|
26
|
+
when 'type', 'transformStart'
|
27
|
+
else
|
28
|
+
@events.add i
|
29
|
+
|
30
|
+
if x.event == 'transformStart'
|
31
|
+
id = @events.length-1
|
32
|
+
transforms[id] = {id: id, label: x.name}
|
33
|
+
|
34
|
+
@transforms = transforms.values()
|
35
|
+
@transforms = @transforms.filter (x, i) =>
|
36
|
+
x.length = (@transforms[i+1]?.id || @events.length) - x.id
|
37
|
+
x.id < @events.length-1
|
38
|
+
|
39
|
+
@reset()
|
40
|
+
|
41
|
+
reset: ->
|
42
|
+
@types = Object.extended()
|
43
|
+
@blocks = Object.extended()
|
44
|
+
@instructions = Object.extended()
|
45
|
+
@blocksMap = new Map 'blocks'
|
46
|
+
@instructionsMap = new Map 'instructions'
|
47
|
+
|
48
|
+
@function = new FunctionNode(@source.name, @source.present)
|
49
|
+
@cursor = 0
|
50
|
+
|
51
|
+
# run first step at initialization
|
52
|
+
i = -1; @run(i) while (i+=1) <= (@events[1] || @source.events.length-1)
|
53
|
+
|
54
|
+
rewind: (to) ->
|
55
|
+
return if to == @cursor
|
56
|
+
|
57
|
+
if to < @cursor
|
58
|
+
delete @previousState
|
59
|
+
@reset()
|
60
|
+
else
|
61
|
+
@previousState = new InputState(@)
|
62
|
+
|
63
|
+
@reset()
|
64
|
+
@increment(to)
|
65
|
+
|
66
|
+
increment: (to) ->
|
67
|
+
to = @events.length-1 unless to?
|
68
|
+
stop = [to, @events.length-1].min()
|
69
|
+
stop = 0 if stop < 0
|
70
|
+
|
71
|
+
i = @events[@cursor]; @run(i) while (i+=1) <= @events[stop]
|
72
|
+
@cursor = stop
|
73
|
+
|
74
|
+
run: (step) ->
|
75
|
+
event = @source.events[step]
|
76
|
+
|
77
|
+
if event.event == 'type'
|
78
|
+
@types[event.id] = new TypeNode(event.kind, event.name, event.parameters)
|
79
|
+
else
|
80
|
+
@[event.event]?(event)
|
81
|
+
console.log "UNKNOWN EVENT: #{event.event}" unless @[event.event]?
|
82
|
+
|
83
|
+
type: (id) ->
|
84
|
+
return undefined unless id?
|
85
|
+
|
86
|
+
if type = @types[id]
|
87
|
+
return type
|
88
|
+
else
|
89
|
+
@reset()
|
90
|
+
throw "Type #{id} not found in #{@types.keys().join(',')}"
|
91
|
+
|
92
|
+
setReturnType: (event) ->
|
93
|
+
@function.setReturnType @type(event.return_type)
|
94
|
+
|
95
|
+
setArguments: (event) ->
|
96
|
+
@function.setArguments(event.arguments.map (x) =>
|
97
|
+
new ArgumentNode(x.name, @type(x.type)))
|
98
|
+
|
99
|
+
addBasicBlock: (event) ->
|
100
|
+
@blocksMap.add event.name, (id) =>
|
101
|
+
@blocks[id] = new BlockNode(event.name)
|
102
|
+
|
103
|
+
removeBasicBlock: (event) ->
|
104
|
+
@blocksMap.remove event.name, (id) =>
|
105
|
+
delete @blocks[id]
|
106
|
+
|
107
|
+
renameBasicBlock: (event) ->
|
108
|
+
@blocksMap.rename event.name, event.new_name, (id) =>
|
109
|
+
@blocks[id].setName(event.new_name)
|
110
|
+
|
111
|
+
updateInstruction: (event) ->
|
112
|
+
id = @instructionsMap.add event.name, (id) =>
|
113
|
+
@instructions[id] = new InstructionNode
|
114
|
+
|
115
|
+
if Object.isArray(event.operands)
|
116
|
+
operands = event.operands.map (x) =>
|
117
|
+
new OperandNode x.kind, @type(x.type), x.name, x.value
|
118
|
+
else
|
119
|
+
operands = []
|
120
|
+
Object.each event.operands, (key, x) =>
|
121
|
+
operands.push [key, new OperandNode(x.kind, @type(x.type), x.name, x.value)]
|
122
|
+
|
123
|
+
@instructions[id].update event.opcode, event.name, event.parameters, operands, @type(event.type)
|
124
|
+
|
125
|
+
addInstruction: (event) ->
|
126
|
+
@instructionsMap.locate event.name, (i) =>
|
127
|
+
@blocksMap.locate event.basic_block, (b) =>
|
128
|
+
@instructions[i].link @blocks[b], event.index
|
129
|
+
|
130
|
+
removeInstruction: (event) ->
|
131
|
+
@instructionsMap.locate event.name, (i) =>
|
132
|
+
@instructions[i].unlink()
|
133
|
+
|
134
|
+
transformStart: (event) ->
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#
|
2
|
+
# Failsafe Map container
|
3
|
+
#
|
4
|
+
class @Map
|
5
|
+
constructor: (@title) ->
|
6
|
+
@data = []
|
7
|
+
@map = Object.extended()
|
8
|
+
|
9
|
+
each: (block) -> @map.each block
|
10
|
+
|
11
|
+
add: (name, block) ->
|
12
|
+
id = @data.findIndex(name)
|
13
|
+
|
14
|
+
if id < 0
|
15
|
+
@data.add name
|
16
|
+
id = @data.length-1
|
17
|
+
block(id) if block
|
18
|
+
@map[name] = id
|
19
|
+
|
20
|
+
id
|
21
|
+
|
22
|
+
find: (name) ->
|
23
|
+
id = @map[name]
|
24
|
+
|
25
|
+
if id < 0
|
26
|
+
error = "Map '#{@title}': '#{name}' element not found"
|
27
|
+
throw error
|
28
|
+
|
29
|
+
id
|
30
|
+
|
31
|
+
remove: (name, block) ->
|
32
|
+
id = @find(name)
|
33
|
+
@data.splice id, 1
|
34
|
+
block(id) if block
|
35
|
+
delete @map[name]
|
36
|
+
|
37
|
+
rename: (name, newName, block) ->
|
38
|
+
id = @find(name)
|
39
|
+
@data[id] = newName
|
40
|
+
block(id) if block
|
41
|
+
delete @map[name]
|
42
|
+
@map[newName] = id
|
43
|
+
|
44
|
+
locate: (name, block) ->
|
45
|
+
block(@find(name))
|
@@ -0,0 +1,76 @@
|
|
1
|
+
class @BlockNode
|
2
|
+
constructor: (@name, @instructions) ->
|
3
|
+
@instructions ||= []
|
4
|
+
|
5
|
+
title: (previousState) ->
|
6
|
+
JST['nodes/block']
|
7
|
+
name: @name
|
8
|
+
instructions: @titleizeInstructions(previousState)
|
9
|
+
|
10
|
+
titleizeInstructions: (previousState) ->
|
11
|
+
unless previousState
|
12
|
+
result = ""
|
13
|
+
result += JST['nodes/diff/unchanged_line'] line: x.title() for x in @instructions
|
14
|
+
result
|
15
|
+
else
|
16
|
+
result = ""
|
17
|
+
removed = Object.extended()
|
18
|
+
added = Object.extended()
|
19
|
+
changed = Object.extended()
|
20
|
+
unchanged = Object.extended()
|
21
|
+
|
22
|
+
for cs, i in @instructions
|
23
|
+
previous = previousState.find (ps) -> ps.name == cs.name
|
24
|
+
currentTitle = cs.title()
|
25
|
+
|
26
|
+
if previous && previous.title == currentTitle
|
27
|
+
unchanged[i] = previous.title
|
28
|
+
previous.newPosition = i
|
29
|
+
else if previous
|
30
|
+
changed[i] = [previous.title, currentTitle]
|
31
|
+
previous.newPosition = i
|
32
|
+
else
|
33
|
+
added[i] = currentTitle
|
34
|
+
|
35
|
+
for ps, i in previousState
|
36
|
+
if !ps.newPosition?
|
37
|
+
position = 0
|
38
|
+
|
39
|
+
while --i >= 0
|
40
|
+
if previousState[i].newPosition?
|
41
|
+
position = previousState[i].newPosition+1
|
42
|
+
break
|
43
|
+
|
44
|
+
removed[position] ||= []
|
45
|
+
removed[position].push ps.title
|
46
|
+
|
47
|
+
[@instructions.length, previousState.length].max().times (i) ->
|
48
|
+
if removed[i]
|
49
|
+
result += JST['nodes/diff/removed_line'](line: l) for l in removed[i]
|
50
|
+
|
51
|
+
result += JST['nodes/diff/added_line'](line: added[i]) if added[i]
|
52
|
+
result += JST['nodes/diff/changed_line'](before: changed[i][0], after:changed[i][1]) if changed[i]
|
53
|
+
result += JST['nodes/diff/unchanged_line'](line: unchanged[i]) if unchanged[i]
|
54
|
+
|
55
|
+
result
|
56
|
+
|
57
|
+
previous: ->
|
58
|
+
try
|
59
|
+
previous = @input.previous.blocks[@input.previous.blocksMap.find(@name)]
|
60
|
+
catch error
|
61
|
+
console.log error
|
62
|
+
|
63
|
+
previous
|
64
|
+
|
65
|
+
setName: (name) ->
|
66
|
+
@name = name
|
67
|
+
|
68
|
+
addInstruction: (instruction, index) ->
|
69
|
+
@instructions.splice index, 0, instruction
|
70
|
+
|
71
|
+
removeInstruction: (instruction) ->
|
72
|
+
index = @instructions.findIndex(instruction)
|
73
|
+
@instructions.splice index, 1 unless index < 0
|
74
|
+
|
75
|
+
references: ->
|
76
|
+
@instructions.last()?.operands.findAll((x) -> x.kind == "basic_block").map((x) -> x.name) || []
|