@@ -254,6 +254,171 @@ function isOutputLineVisible(lineNo) {
254254
255255
256256
257+ // Pre-compute the layout of top-level heap objects for ALL execution
258+ // points as soon as a trace is first loaded. The reason why we want to
259+ // do this is so that when the user steps through execution points, the
260+ // heap objects don't "jiggle around" (i.e., preserving positional
261+ // invariance). Also, if we set up the layout objects properly, then we
262+ // can take full advantage of d3 to perform rendering and transitions.
263+
264+
265+ // curTraceLayouts is a list of top-level heap layout "objects" with the
266+ // same length as curTrace after it's been fully initialized. Each
267+ // element of curTraceLayouts is computed from the contents of its
268+ // immediate predecessor, thus ensuring that objects don't "jiggle
269+ // around" between consecutive execution points.
270+ //
271+ // Each top-level heap layout "object" is itself a LIST of LISTS of
272+ // object IDs, where each element of the outer list represents a row,
273+ // and each element of the inner list represents columns within a
274+ // particular row. Each row can have a different number of columns. Most
275+ // rows have exactly ONE column (representing ONE object ID), but rows
276+ // containing 1-D linked data structures have multiple columns. Each
277+ // inner list element looks something like ['row1', 3, 2, 1] where the
278+ // first element is a unique row ID tag, which is used as a key for d3 to
279+ // preserve "object constancy" for updates, transitions, etc. The row ID
280+ // is derived from the FIRST object ID inserted into the row. Since all
281+ // object IDs are unique, all row IDs will also be unique.
282+ var curTraceLayouts = null ;
283+
284+ /* This is a good, simple example to test whether objects "jiggle"
285+
286+ x = [1, [2, [3, None]]]
287+ y = [4, [5, [6, None]]]
288+
289+ x[1][1] = y[1]
290+
291+ */
292+
293+
294+ function precomputeCurTraceLayouts ( ) {
295+ curTraceLayouts = [ ] ;
296+ curTraceLayouts . push ( [ ] ) ; // pre-seed with an empty sentinel to simplify the code
297+
298+ assert ( curTrace . length > 0 ) ;
299+
300+
301+ $ . each ( curTrace , function ( i , elt ) {
302+ var prevLayout = curTraceLayouts [ curTraceLayouts . length - 1 ] ;
303+
304+ // make a DEEP COPY of prevLayout to use as the basis for curLine
305+ var curLayout = $ . extend ( true /* deep copy */ , [ ] , prevLayout ) ;
306+
307+ // initialize with all IDs from curLayout
308+ var idsToRemove = d3 . map ( ) ;
309+ $ . each ( curLayout , function ( i , row ) {
310+ for ( var j = 1 /* ignore row ID tag */ ; j < row . length ; j ++ ) {
311+ idsToRemove . set ( row [ j ] , 1 ) ;
312+ }
313+ } ) ;
314+
315+
316+ function curLayoutIndexOf ( id ) {
317+ for ( var i = 0 ; i < curLayout . length ; i ++ ) {
318+ var row = curLayout [ i ] ;
319+ var index = row . indexOf ( id ) ;
320+ if ( index > 0 ) { // index of 0 is impossible since it's the row ID tag
321+ return { row : row , index : index }
322+ }
323+ }
324+ return null ;
325+ }
326+
327+
328+ // a krazy function!
329+ // id - the new object ID to be inserted somewhere in curLayout
330+ // (if it's not already in there)
331+ // curRow - a row within curLayout where new linked list
332+ // elements can be appended onto (might be null)
333+ // newRow - a new row that might be spliced into curRow or appended
334+ // as a new row in curLayout
335+ function updateCurLayout ( id , curRow , newRow ) {
336+ var curLayoutLoc = curLayoutIndexOf ( id ) ;
337+
338+ // if id is already in curLayout ...
339+ if ( curLayoutLoc ) {
340+ var foundRow = curLayoutLoc . row ;
341+ var foundIndex = curLayoutLoc . index ;
342+
343+ // splice the contents of newRow right BEFORE foundIndex.
344+ // (Think about when you're trying to insert in id=3 into ['row1', 2, 1]
345+ // to represent a linked list 3->2->1. You want to splice the 3
346+ // entry right before the 2 to form ['row1', 3, 2, 1])
347+ if ( newRow . length > 1 ) {
348+ var args = [ foundIndex - 1 , 0 ] ;
349+ for ( var i = 1 ; i < newRow . length ; i ++ ) { // ignore row ID tag
350+ args . push ( newRow [ i ] ) ;
351+ idsToRemove . remove ( newRow [ i ] ) ;
352+ }
353+ foundRow . splice . apply ( args ) ;
354+
355+ // remove ALL elements from newRow since they've all been accounted for
356+ // (but don't reassign it away to an empty list, since the
357+ // CALLER checks its value. TODO: get rid of this gross hack?!?)
358+ newRow . splice ( 0 , newRow . length ) ;
359+ }
360+
361+ // recurse to find more top-level linked entries to append onto curRow
362+ // updateCurLayout(child ID, foundRow, [])
363+
364+ }
365+ else {
366+ // push id into newRow ...
367+ if ( newRow . length == 0 ) {
368+ newRow . push ( 'row' + id ) ; // unique row ID (since IDs are unique)
369+ }
370+ newRow . push ( id ) ;
371+
372+ // RECURSE to find possible top-level linked entries
373+ // updateCurLayout(child ID, curRow, newRow)
374+
375+
376+ // if newRow hasn't been spliced into an existing row yet during
377+ // a child recursive call ...
378+ if ( newRow . length > 0 ) {
379+ if ( curRow && curRow . length > 0 ) {
380+ // append onto the END of curRow if it exists
381+ for ( var i = 1 ; i < newRow . length ; i ++ ) { // ignore row ID tag
382+ curRow . push ( newRow [ i ] ) ;
383+ }
384+ }
385+ else {
386+ // otherwise push to curLayout as a new row
387+ curLayout . push ( newRow ) ;
388+ }
389+
390+ // regardless, newRow is now accounted for, so clear it
391+ for ( var i = 1 ; i < newRow . length ; i ++ ) { // ignore row ID tag
392+ idsToRemove . remove ( newRow [ i ] ) ;
393+ }
394+ newRow . splice ( 0 , newRow . length ) ;
395+ }
396+
397+ }
398+ }
399+
400+
401+ // iterate through all globals and ordered stack frames
402+ // and then call updateCurLayout(id, null, []);
403+
404+
405+ // iterate through remaining elements of idsToRemove and REMOVE them
406+ // from curLayout
407+ idsToRemove . forEach ( function ( id , xxx ) {
408+ var ind = row . indexOf ( id ) ;
409+ if ( ind > 0 ) { // remember that index 0 of the row is the row ID tag
410+ row . splice ( ind , 1 ) ;
411+ }
412+ } ) ;
413+
414+ curTraceLayouts . push ( curLayout ) ;
415+ } ) ;
416+
417+ curTraceLayouts . splice ( 0 , 1 ) ; // remove seeded empty sentinel element
418+ assert ( curTrace . length == curTraceLayouts . length ) ;
419+ }
420+
421+
257422// relies on curTrace and curInstr globals
258423function updateOutput ( ) {
259424 if ( ! curTrace ) {
0 commit comments