on 73e6bc2 help sign in
elk/runtime/Debugger.js
183 lines 4.0 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.debuggerStep = function() {
23 staticDebugger.step();
24};
26function debuggerInit(module, elm) {
28 var programPaused = false;
29 var allNodes = null;
31 var recordedEvents = [];
32 var asyncTimers = [];
33 var initialProgramState = {
34 nodeValues: [],
35 asyncTimers: []
36 };
38 function wrapNotify(id, v) {
39 var timestep = Date.now();
41 if (programPaused) {
42 // ignore notify because we are stepping
43 }
44 else {
45 elm.notify(id, v, timestep);
46 recordEvent(id, v, timestep);
47 }
48 };
50 function wrapRunDelayed(func, delayMs) {
51 return safeSetTimeout(func, delayMs);
52 };
54 function safeSetTimeout(func, delayMs) {
55 var timerId = setTimeout(func, delayMs);
56 asyncTimers.push({ func:func, delayMs:delayMs, timerId:timerId });
57 return timerId;
58 }
60 function recordEvent(id, v, timestep) {
61 recordedEvents.push({ id:id, value:v, timestep:timestep });
62 }
64 function resetProgram() {
65 clearAsyncEvents();
66 restoreNodeValues(allNodes, initialProgramState.nodeValues);
67 redrawGraphics();
68 }
70 function restartProgram() {
71 resetProgram();
72 recordedEvents = [];
73 initialProgramState.asyncTimers.forEach(function(timer) {
74 var func = timer.func;
75 func();
76 });
77 }
79 function stepRecording() {
80 if (programPaused) {
81 if (recordedEvents.length > 0) {
82 var nextEvent = recordedEvents.shift();
83 elm.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
84 }
85 }
86 else {
87 // move into stepping mode
88 programPaused = true;
89 resetProgram();
90 }
91 }
93 function clearAsyncEvents() {
94 asyncTimers.forEach(function(timer) {
95 clearTimeout(timer.timerId);
96 });
97 }
99 function doPlayback(eventList) {
100 var x = eventList.shift();
101 var time = x[2];
102 elm.notify(x[0], x[1], x[2]);
104 if (eventList.length > 0) {
105 var delta = eventList[0][2] - time;
106 setTimeout(function() { doPlayback(eventList); }, delta);
107 }
108 }
110 function redrawGraphics() {
111 var graphicsFoldp = moduleRetValue.main.kids[0];
112 graphicsFoldp.recv(Date.now(), true, moduleRetValue.main.id);
113 }
115 var wrappedElm = {
116 notify: wrapNotify,
117 runDelayed: wrapRunDelayed,
118 node: elm.node,
119 display: elm.display,
120 id: elm.id,
121 addListener: elm.addListener,
122 inputs: elm.inputs
123 };
125 var moduleRetValue = module(wrappedElm);
127 var elmDebugger = {
128 moduleRetValue: moduleRetValue,
130 // debugger functions
131 reset: resetProgram,
132 restart: restartProgram,
133 step: stepRecording
134 };
136 allNodes = flattenNodes(wrappedElm.inputs);
137 initialProgramState.nodeValues = saveNodeValues(allNodes);
138 initialProgramState.asyncTimers = asyncTimers.slice();
140 return elmDebugger;
141};
145function assert(bool, msg) {
146 if (!bool) {
147 throw "Assertion error: " + msg;
148 }
151function saveNodeValues(allNodes) {
152 var nodeValues = [];
154 allNodes.forEach(function(node) {
155 nodeValues.push({ value: node.value, id: node.id });
156 });
158 return nodeValues;
159};
162function restoreNodeValues(allNodes, nodeStates) {
163 assert(allNodes.length == nodeStates.length, "saved program state has wrong length");
164 for (var i=0; i < allNodes.length; i++) {
165 var node = allNodes[i];
166 var state = nodeStates[i];
167 assert(node.id == state.id, "the nodes moved position");
169 node.value = state.value;
170 }
173function flattenNodes(nodes) {
174 var treeReduce = function(prev, cur, i, arr) {
175 var flatKids = cur.kids.reduce(treeReduce, []);
176 flatKids.push(cur);
177 return prev.concat(flatKids);
178 }
180 return nodes.reduce(treeReduce, []);
181};
183}());