on d87bc08 help sign in
elk/runtime/Debugger.js
204 lines 4.5 kB history raw
2(function() {
3'use strict';
5var staticDebugger = null;
7Elm.debuggerInit = function(module) {
8 return function(elm) {
9 staticDebugger = debuggerInit(module, elm);
10 return staticDebugger.moduleRetValue;
11 };
12};
14Elm.debuggerReset = function() {
15 staticDebugger.reset();
16};
18Elm.debuggerRestart = function() {
19 staticDebugger.restart();
20};
22Elm.debuggerGetMaxSteps = function() {
23 return staticDebugger.getMaxSteps();
24};
26Elm.debuggerStepTo = function(index) {
27 staticDebugger.stepTo(index);
28};
30function debuggerInit(module, elm) {
32 var programPaused = false;
33 var allNodes = null;
35 var recordedEvents = [];
36 var eventCounter = 0;
37 var asyncTimers = [];
38 var initialProgramState = {
39 nodeValues: [],
40 asyncTimers: []
41 };
43 function wrapNotify(id, v) {
44 var timestep = Date.now();
46 if (programPaused) {
47 // ignore notify because we are stepping
48 }
49 else {
50 elm.notify(id, v, timestep);
51 recordEvent(id, v, timestep);
52 }
53 };
55 function wrapRunDelayed(func, delayMs) {
56 return safeSetTimeout(func, delayMs);
57 };
59 function safeSetTimeout(func, delayMs) {
60 var timerId = setTimeout(func, delayMs);
61 asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
62 return timerId;
63 }
65 function recordEvent(id, v, timestep) {
66 recordedEvents.push({ id:id, value:v, timestep:timestep });
67 }
69 function resetProgram() {
70 clearAsyncEvents();
71 restoreNodeValues(allNodes, initialProgramState.nodeValues);
72 redrawGraphics();
73 }
75 function restartProgram() {
76 resetProgram();
77 recordedEvents = [];
78 initialProgramState.asyncTimers.forEach(function(timer) {
79 var func = timer.func;
80 func();
81 });
82 }
84 function gotoStep(index) {
85 if (!programPaused) {
86 programPaused = true;
87 resetProgram();
88 }
90 if (index < 0 || index >= recordedEvents.length) {
91 throw "Index out of bounds: " + index;
92 }
94 if (index < eventCounter) {
95 resetProgram();
96 eventCounter = 0;
97 }
99 assert(index >= eventCounter, "index must be bad");
100 while (eventCounter < index) {
101 var nextEvent = recordedEvents[eventCounter];
102 elm.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
104 eventCounter += 1;
105 }
106 assert(eventCounter == index, "while loop didnt' work");
107 }
109 function getMaxSteps() {
110 return recordedEvents.length - 1;
111 }
113 function clearAsyncEvents() {
114 asyncTimers.forEach(function(timer) {
115 clearTimeout(timer.timerId);
116 });
117 }
119 function doPlayback(eventList) {
120 var x = eventList.shift();
121 var time = x[2];
122 elm.notify(x[0], x[1], x[2]);
124 if (eventList.length > 0) {
125 var delta = eventList[0][2] - time;
126 setTimeout(function() { doPlayback(eventList); }, delta);
127 }
128 }
130 function redrawGraphics() {
131 var graphicsFoldp = moduleRetValue.main.kids[0];
132 graphicsFoldp.recv(Date.now(), true, moduleRetValue.main.id);
133 }
135 var wrappedElm = {
136 notify: wrapNotify,
137 runDelayed: wrapRunDelayed,
138 node: elm.node,
139 display: elm.display,
140 id: elm.id,
141 addListener: elm.addListener,
142 inputs: elm.inputs
143 };
145 var moduleRetValue = module(wrappedElm);
147 var elmDebugger = {
148 moduleRetValue: moduleRetValue,
150 // debugger functions
151 reset: resetProgram,
152 restart: restartProgram,
153 getMaxSteps: getMaxSteps,
154 stepTo: gotoStep
155 };
157 allNodes = flattenNodes(wrappedElm.inputs);
158 initialProgramState.nodeValues = saveNodeValues(allNodes);
159 initialProgramState.asyncTimers = asyncTimers.slice();
161 return elmDebugger;
162};
166function assert(bool, msg) {
167 if (!bool) {
168 throw "Assertion error: " + msg;
169 }
172function saveNodeValues(allNodes) {
173 var nodeValues = [];
175 allNodes.forEach(function(node) {
176 nodeValues.push({ value: node.value, id: node.id });
177 });
179 return nodeValues;
180};
183function restoreNodeValues(allNodes, nodeStates) {
184 assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
185 for (var i=0; i < allNodes.length; i++) {
186 var node = allNodes[i];
187 var state = nodeStates[i];
188 assert(node.id == state.id, "the nodes moved position");
190 node.value = state.value;
191 }
194function flattenNodes(nodes) {
195 var treeReduce = function(prev, cur, i, arr) {
196 var flatKids = cur.kids.reduce(treeReduce, []);
197 flatKids.push(cur);
198 return prev.concat(flatKids);
199 }
201 return nodes.reduce(treeReduce, []);
202};
204}());