@@ -20,6 +20,9 @@ Elm.debuggerAttach = function(module, hotSwapState /* =undefined */) {
23+var EVENTS_PER_SAVE = 100;
23function dispatchElmDebuggerInit() {
26function dispatchElmDebuggerInit() {
24 if (parent.window) {
27 if (parent.window) {
25 var dispatch = function() {
28 var dispatch = function() {
@@ -38,8 +41,10 @@ function debugModule(module, runtime) {
38 var programPaused = false;
41 var programPaused = false;
39 var recordedEvents = [];
42 var recordedEvents = [];
40 var asyncCallbacks = [];
43 var asyncCallbacks = [];
41 var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
45 var watchTracker = Elm.Native.Debug.make(runtime).watchTracker;
42 var pauseTime = 0;
46 var pauseTime = 0;
47+ var eventsUntilSnapshot = EVENTS_PER_SAVE;
44 // runtime is the prototype of wrappedRuntime
49 // runtime is the prototype of wrappedRuntime
45 // so we can access all runtime properties too
50 // so we can access all runtime properties too
@@ -66,6 +71,8 @@ function debugModule(module, runtime) {
66 var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
71 var signalGraphNodes = flattenSignalGraph(wrappedRuntime.inputs);
67 var tracePath = tracePathInit(runtime, debuggedModule.main);
72 var tracePath = tracePathInit(runtime, debuggedModule.main);
74+ snapshots.push(snapshotSignalGraph(signalGraphNodes));
69 function wrapNotify(id, v) {
76 function wrapNotify(id, v) {
70 var timestep = runtime.timer.now();
77 var timestep = runtime.timer.now();
@@ -75,8 +82,9 @@ function debugModule(module, runtime) {
75 return false;
82 return false;
78− var changed = runtime.notify(id, v, timestep);
79 recordEvent(id, v, timestep);
85 recordEvent(id, v, timestep);
86+ var changed = runtime.notify(id, v, timestep);
87+ snapshotOnCheckpoint();
80 if (parent.window) {
88 if (parent.window) {
81 parent.window.postMessage("elmNotify", window.location.origin);
89 parent.window.postMessage("elmNotify", window.location.origin);
@@ -103,6 +111,7 @@ function debugModule(module, runtime) {
103 function recordEvent(id, v, timestep) {
111 function recordEvent(id, v, timestep) {
104 watchTracker.pushFrame();
112 watchTracker.pushFrame();
105 recordedEvents.push({ id:id, value:v, timestep:timestep });
113 recordedEvents.push({ id:id, value:v, timestep:timestep });
108 function clearAsyncCallbacks() {
117 function clearAsyncCallbacks() {
@@ -115,6 +124,7 @@ function debugModule(module, runtime) {
116 function clearRecordedEvents() {
125 function clearRecordedEvents() {
117 recordedEvents = [];
126 recordedEvents = [];
120 function getRecordedEventsLength() {
130 function getRecordedEventsLength() {
@@ -133,6 +143,26 @@ function debugModule(module, runtime) {
133 recordedEvents = events.slice();
143 recordedEvents = events.slice();
146+ function clearSnapshots() {
147+ snapshots = [snapshotSignalGraph(signalGraphNodes)];
150+ function getSnapshotAt(i) {
151+ var snapshotEvent = Math.floor(i / EVENTS_PER_SAVE);
152+ assert(snapshotEvent < snapshots.length && snapshotEvent >= 0,
153+ "Out of bounds index: " + snapshotEvent);
154+ return snapshots[snapshotEvent];
157+ function snapshotOnCheckpoint() {
158+ if (eventsUntilSnapshot === 1) {
159+ snapshots.push(snapshotSignalGraph(signalGraphNodes));
160+ eventsUntilSnapshot = EVENTS_PER_SAVE;
162+ eventsUntilSnapshot -= 1;
136 function setPaused() {
166 function setPaused() {
137 programPaused = true;
167 programPaused = true;
138 clearAsyncCallbacks();
168 clearAsyncCallbacks();
@@ -147,7 +177,12 @@ function debugModule(module, runtime) {
147 if (position > 0) {
177 if (position > 0) {
148 // If we're not unpausing at the head, then we need to dump the
178 // If we're not unpausing at the head, then we need to dump the
149 // events that are ahead of where we're continuing.
179 // events that are ahead of where we're continuing.
180+ var lastSnapshotPosition = Math.floor(position / EVENTS_PER_SAVE);
181+ eventsUntilSnapshot = EVENTS_PER_SAVE - (position % EVENTS_PER_SAVE);
182+ snapshots = snapshots.slice(0, lastSnapshotPosition + 1);
150 recordedEvents = recordedEvents.slice(0, position);
183 recordedEvents = recordedEvents.slice(0, position);
184+ tracePath.clearTracesAfter(position);
185+ eventCounter = position;
151 executeCallbacks(asyncCallbacks, false);
186 executeCallbacks(asyncCallbacks, false);
153 tracePath.startRecording();
188 tracePath.startRecording();
@@ -169,6 +204,9 @@ function debugModule(module, runtime) {
169 getRecordedEventAt: getRecordedEventAt,
204 getRecordedEventAt: getRecordedEventAt,
170 copyRecordedEvents: copyRecordedEvents,
205 copyRecordedEvents: copyRecordedEvents,
171 loadRecordedEvents: loadRecordedEvents,
206 loadRecordedEvents: loadRecordedEvents,
207+ clearSnapshots: clearSnapshots,
208+ getSnapshotAt: getSnapshotAt,
209+ snapshotOnCheckpoint: snapshotOnCheckpoint,
172 getPaused: getPaused,
210 getPaused: getPaused,
173 setPaused: setPaused,
211 setPaused: setPaused,
174 setContinue: setContinue,
212 setContinue: setContinue,
@@ -180,17 +218,19 @@ function debugModule(module, runtime) {
180function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
218function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
181 var currentEventIndex = 0;
219 var currentEventIndex = 0;
183− function resetProgram() {
221+ function resetProgram(position) {
222+ var closestSnapshot = debugModule.getSnapshotAt(position);
184 debugModule.clearAsyncCallbacks();
223 debugModule.clearAsyncCallbacks();
185− restoreSnapshot(debugModule.signalGraphNodes, debugModule.initialSnapshot);
224+ restoreSnapshot(debugModule.signalGraphNodes, closestSnapshot);
186 redrawGraphics();
225 redrawGraphics();
189 function restartProgram() {
228 function restartProgram() {
190− resetProgram();
229+ resetProgram(0);
191 debugModule.watchTracker.clear();
230 debugModule.watchTracker.clear();
192 debugModule.tracePath.clearTraces();
231 debugModule.tracePath.clearTraces();
193 debugModule.clearRecordedEvents();
232 debugModule.clearRecordedEvents();
233+ debugModule.clearSnapshots();
194 debugModule.setContinue(0);
234 debugModule.setContinue(0);
195 executeCallbacks(debugModule.initialAsyncCallbacks, true);
235 executeCallbacks(debugModule.initialAsyncCallbacks, true);
@@ -207,11 +247,11 @@ function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
207 restartProgram();
247 restartProgram();
250+ var closestSnapshotIndex =
251+ Math.floor(currentEventIndex / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
210− resetProgram();
252+ resetProgram(currentEventIndex);
211 var continueIndex = currentEventIndex;
253 var continueIndex = currentEventIndex;
212− currentEventIndex = 0;
254+ currentEventIndex = closestSnapshotIndex;
213− debugModule.tracePath.clearTraces();
214− debugModule.tracePath.startRecording();
215 stepTo(continueIndex);
255 stepTo(continueIndex);
216 debugModule.setContinue(currentEventIndex);
256 debugModule.setContinue(currentEventIndex);
@@ -228,8 +268,9 @@ function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
230 if (index < currentEventIndex) {
270 if (index < currentEventIndex) {
271+ var closestSnapshotIndex = Math.floor(index / EVENTS_PER_SAVE) * EVENTS_PER_SAVE;
231− resetProgram();
272+ resetProgram(index);
232− currentEventIndex = 0;
273+ currentEventIndex = closestSnapshotIndex;
235 while (currentEventIndex < index) {
276 while (currentEventIndex < index) {
@@ -277,10 +318,19 @@ function debuggerInit(debugModule, runtime, hotSwapState /* =undefined */) {
277 var paused = top.debug.paused;
318 var paused = top.debug.paused;
278 debugModule.setPaused();
319 debugModule.setPaused();
279 debugModule.loadRecordedEvents(hotSwapState.recordedEvents);
320 debugModule.loadRecordedEvents(hotSwapState.recordedEvents);
321+ var index = getMaxSteps();
323+ debugModule.tracePath.clearTraces();
281 // draw new trace path
325 // draw new trace path
282 debugModule.tracePath.startRecording();
326 debugModule.tracePath.startRecording();
283− stepTo(getMaxSteps());
327+ while(currentEventIndex < index) {
328+ var nextEvent = debugModule.getRecordedEventAt(currentEventIndex);
330+ runtime.notify(nextEvent.id, nextEvent.value, nextEvent.timestep);
331+ debugModule.snapshotOnCheckpoint();
332+ currentEventIndex += 1;
284 debugModule.tracePath.stopRecording();
334 debugModule.tracePath.stopRecording();
286 stepTo(hotSwapState.currentEventIndex);
336 stepTo(hotSwapState.currentEventIndex);
@@ -356,14 +406,20 @@ function tracePathInit(runtime, signalGraphMain) {
356 for (var id in positions) {
406 for (var id in positions) {
357 var pos = positions[id];
407 var pos = positions[id];
358 if (tracePositions.hasOwnProperty(id)) {
408 if (tracePositions.hasOwnProperty(id)) {
359− var points = tracePositions[id];
409+ tracePositions[id].push(pos);
360− if (!pos.equals(points[points.length-1])) {
365 tracePositions[id] = [pos];
412 tracePositions[id] = [pos];
414+ if (tracePositions[id].length < eventCounter) {
415+ var padCount = eventCounter - tracePositions[id].length;
416+ var lastTracePosition = tracePositions[id][tracePositions[id].length - 1];
417+ for (var i = padCount; i--;) {
418+ tracePositions[id].push(lastTracePosition)
421+ assert(tracePositions[id].length === eventCounter,
422+ "We don't have a 1-1 mapping of trace positions to events");
@@ -421,10 +477,19 @@ function tracePathInit(runtime, signalGraphMain) {
421 recordingTraces = true;
477 recordingTraces = true;
480+ function clearTracesAfter(position) {
482+ for (var id in tracePositions) {
483+ newTraces[id] = tracePositions[id].slice(0,position);
485+ tracePositions = newTraces;
425 graphicsUpdate: graphicsUpdate,
489 graphicsUpdate: graphicsUpdate,
426 canvas: tracePathCanvas,
490 canvas: tracePathCanvas,
427 clearTraces: clearTraces,
491 clearTraces: clearTraces,
492+ clearTracesAfter: clearTracesAfter,
428 stopRecording: stopRecording,
493 stopRecording: stopRecording,
429 startRecording: startRecording
494 startRecording: startRecording
@@ -468,7 +533,8 @@ function snapshotSignalGraph(signalGraphNodes) {
470function restoreSnapshot(signalGraphNodes, snapshot) {
535function restoreSnapshot(signalGraphNodes, snapshot) {
471− assert(signalGraphNodes.length == snapshot.length, "saved program state has wrong length");
536+ assert(signalGraphNodes.length == snapshot.length,
537+ "saved program state has wrong length");
472 for (var i=0; i < signalGraphNodes.length; i++) {
538 for (var i=0; i < signalGraphNodes.length; i++) {
473 var node = signalGraphNodes[i];
539 var node = signalGraphNodes[i];
474 var state = snapshot[i];
540 var state = snapshot[i];