on 139feef help sign in
elk/runtime/Debugger.js
189 lines 4.2 kB history raw
2(function() {
3'use strict';
5Elm.Debugger = null;
6Elm.debuggerInit = function(module) {
7 return {
8 make: function(runtime) {
9 Elm.Debugger = debuggerInit(module, runtime);
10 return Elm.Debugger.moduleInstance;
11 }
12 };
13};
15function debuggerInit(module, runtime) {
17 var programPaused = false;
18 var allNodes = null;
20 var recordedEvents = [];
21 var eventCounter = 0;
22 var asyncTimers = [];
23 var initialProgramState = {
24 nodeValues: [],
25 asyncTimers: []
26 };
28 function wrapNotify(id, v) {
29 var timestep = Date.now();
31 if (programPaused) {
32 // ignore notify because we are stepping
33 }
34 else {
35 runtime.notify(id, v, timestep);
36 recordEvent(id, v, timestep);
37 }
38 };
40 function wrapRunDelayed(func, delayMs) {
41 return safeSetTimeout(func, delayMs);
42 };
44 function safeSetTimeout(func, delayMs) {
45 var timerId = setTimeout(func, delayMs);
46 asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
47 return timerId;
48 }
50 function recordEvent(id, v, timestep) {
51 recordedEvents.push({ id:id, value:v, timestep:timestep });
52 }
54 function resetProgram() {
55 clearAsyncEvents();
56 restoreNodeValues(allNodes, initialProgramState.nodeValues);
57 redrawGraphics();
58 }
60 function restartProgram() {
61 resetProgram();
62 recordedEvents = [];
63 initialProgramState.asyncTimers.forEach(function(timer) {
64 var func = timer.func;
65 func();
66 });
67 }
69 function stepTo(index) {
70 if (!programPaused) {
71 programPaused = true;
72 resetProgram();
73 }
75 if (index < 0 || index >= recordedEvents.length) {
76 throw "Index out of bounds: " + index;
77 }
79 if (index < eventCounter) {
80 resetProgram();
81 eventCounter = 0;
82 }
84 assert(index >= eventCounter, "index must be bad");
85 while (eventCounter < index) {
86 var nextEvent = recordedEvents[eventCounter];
87 runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
89 eventCounter += 1;
90 }
91 assert(eventCounter == index, "while loop didnt' work");
92 }
94 function getMaxSteps() {
95 return recordedEvents.length - 1;
96 }
98 function clearAsyncEvents() {
99 asyncTimers.forEach(function(timer) {
100 clearTimeout(timer.timerId);
101 });
102 }
104 function doPlayback(eventList) {
105 var x = eventList.shift();
106 var time = x[2];
107 runtime.notify(x[0], x[1], x[2]);
109 if (eventList.length > 0) {
110 var delta = eventList[0][2] - time;
111 setTimeout(function() { doPlayback(eventList); }, delta);
112 }
113 }
115 function redrawGraphics() {
116 var graphicsFoldp = moduleInstance.main.kids[0];
117 graphicsFoldp.recv(Date.now(), true, moduleInstance.main.id);
118 }
120 var wrappedRuntime = {
121 notify: wrapNotify,
122 runDelayed: wrapRunDelayed,
123 node: runtime.node,
124 display: runtime.display,
125 id: runtime.id,
126 addListener: runtime.addListener,
127 inputs: runtime.inputs
128 };
130 var moduleInstance = module.make(wrappedRuntime);
132 var elmDebugger = {
133 moduleInstance: moduleInstance,
135 // debugger functions
136 reset: resetProgram,
137 restart: restartProgram,
138 getMaxSteps: getMaxSteps,
139 stepTo: stepTo
140 };
142 allNodes = flattenNodes(wrappedRuntime.inputs);
143 initialProgramState.nodeValues = saveNodeValues(allNodes);
144 initialProgramState.asyncTimers = asyncTimers.slice();
146 return elmDebugger;
147};
151function assert(bool, msg) {
152 if (!bool) {
153 throw "Assertion error: " + msg;
154 }
157function saveNodeValues(allNodes) {
158 var nodeValues = [];
160 allNodes.forEach(function(node) {
161 nodeValues.push({ value: node.value, id: node.id });
162 });
164 return nodeValues;
165};
168function restoreNodeValues(allNodes, nodeStates) {
169 assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
170 for (var i=0; i < allNodes.length; i++) {
171 var node = allNodes[i];
172 var state = nodeStates[i];
173 assert(node.id == state.id, "the nodes moved position");
175 node.value = state.value;
176 }
179function flattenNodes(nodes) {
180 var treeReduce = function(prev, cur, i, arr) {
181 var flatKids = cur.kids.reduce(treeReduce, []);
182 flatKids.push(cur);
183 return prev.concat(flatKids);
184 }
186 return nodes.reduce(treeReduce, []);
187};
189}());