@@ -221,11 +221,51 @@ const createApp = (url: string) => {
221221 updateView ( state ) ;
222222 } ;
223223
224+ const getTooltip = ( ) : HTMLElement => {
225+ let tooltip = document . getElementById ( 'mem-slot-tooltip' ) ;
226+ if ( ! tooltip ) {
227+ tooltip = document . createElement ( 'div' ) ;
228+ tooltip . id = 'mem-slot-tooltip' ;
229+ document . body . appendChild ( tooltip ) ;
230+ }
231+ return tooltip ;
232+ }
233+
234+ const getTooltipArrow = ( ) : HTMLElement => {
235+ let arrow = document . getElementById ( 'mem-slot-tooltip-arrow' ) ;
236+ if ( ! arrow ) {
237+ arrow = document . createElement ( 'div' ) ;
238+ arrow . id = 'mem-slot-tooltip-arrow' ;
239+ document . body . appendChild ( arrow ) ;
240+ }
241+ return arrow ;
242+ }
243+
224244 const handleMouseOver = ( e : MouseEvent ) : void => {
225245 const hoveredElement = e . target as HTMLElement ;
226246 const memSlot = hoveredElement . closest ( '.mem-slot' ) ;
227247 if ( memSlot ) {
248+ const tooltip = getTooltip ( ) ;
249+ const arrow = getTooltipArrow ( ) ;
250+ const idx = contentLineIdx ( memSlot . parentElement ) ;
251+ const displayValue = memSlotDisplayValue ( state , memSlot . id , idx ) ;
228252 memSlot . classList . add ( 'hovered-mem-slot' ) ;
253+ if ( displayValue ) {
254+ // Text needs to be set first, so that position is calculated correctly
255+ tooltip . innerHTML = displayValue ;
256+ tooltip . style . display = 'block' ;
257+ const rect = memSlot . getBoundingClientRect ( ) ;
258+ const tooltipLeft = Math . max ( 0 , rect . left - tooltip . offsetWidth / 2 + rect . width / 2 ) ;
259+ tooltip . style . left = `${ tooltipLeft } px` ;
260+ tooltip . style . top = `${ rect . bottom + 5 } px` ;
261+ arrow . style . display = 'block' ;
262+ const arrowLeft = Math . max ( 0 , rect . left + rect . width / 2 ) ;
263+ arrow . style . left = `${ arrowLeft } px` ;
264+ arrow . style . top = `${ rect . bottom } px` ;
265+ } else {
266+ tooltip . style . display = 'none' ;
267+ arrow . style . display = 'none' ;
268+ }
229269 }
230270 } ;
231271
@@ -234,6 +274,10 @@ const createApp = (url: string) => {
234274 const memSlot = hoveredElement . closest ( '.mem-slot' ) ;
235275 if ( memSlot ) {
236276 memSlot . classList . remove ( 'hovered-mem-slot' ) ;
277+ const tooltip = getTooltip ( ) ;
278+ const arrow = getTooltipArrow ( ) ;
279+ tooltip . style . display = 'none' ;
280+ arrow . style . display = 'none' ;
237281 }
238282 } ;
239283
@@ -500,52 +544,65 @@ const createApp = (url: string) => {
500544 updateLineNumbers ( state ) ;
501545 }
502546
547+ const memSlotDisplayValue = ( state : AppState , memSlotId : string , idx : number ) : string => {
548+ const { state : bpfState , idx : bpfStateIdx } = mostRecentBpfState ( state , idx ) ;
549+ const prevBpfState = mostRecentBpfState ( state , bpfStateIdx - 1 ) . state ;
550+ const prevValue = prevBpfState . values . get ( memSlotId ) ;
551+ const value = bpfState . values . get ( memSlotId ) ;
552+ const ins = state . lines [ idx ] . bpfIns ;
553+ let content = '' ;
554+ switch ( value ?. effect ) {
555+ case Effect . WRITE :
556+ case Effect . UPDATE :
557+ if ( memSlotId == 'MEM' ) {
558+ // show the value of register that was stored
559+ const reg = ins ?. alu ?. src . id ;
560+ if ( reg ) {
561+ const regValue = bpfState . values . get ( reg ) ;
562+ content = `${ RIGHT_ARROW } ${ regValue ?. value } ` ;
563+ }
564+ break ;
565+ }
566+ let newVal = value ?. value ;
567+ let oldVal = prevValue ?. value || '' ;
568+ if ( newVal === oldVal )
569+ content = newVal ;
570+ else if ( newVal )
571+ content = `${ oldVal } ${ RIGHT_ARROW } ${ newVal } ` ;
572+ else
573+ content = `${ oldVal } <span style="color:grey">-> scratched</span>` ;
574+ break ;
575+ case Effect . READ :
576+ case Effect . NONE :
577+ default :
578+ content = value ?. value || '' ;
579+ break ;
580+ }
581+ return content ;
582+ }
583+
503584 const RIGHT_ARROW = '->' ;
504585
505586 const updateStatePanel = async ( state : AppState ) : Promise < void > => {
506587 const { state : bpfState , idx } = mostRecentBpfState ( state , state . selectedLineIdx ) ;
507- const ins = state . lines [ idx ] . bpfIns ;
508- const prevBpfState = mostRecentBpfState ( state , idx - 1 ) . state ;
509-
510588 const statePanel = document . getElementById ( 'state-panel' ) as HTMLElement ;
511589 const table = statePanel . querySelector ( 'table' ) ;
512590 table . innerHTML = '' ;
513591
514592 const addRow = ( id : string ) => {
515- const value = bpfState . values . get ( id ) ;
516- const prevValue = prevBpfState . values . get ( id ) ;
593+ let content = memSlotDisplayValue ( state , id , idx ) ;
517594 const row = document . createElement ( 'tr' ) ;
518- let content = '' ;
595+ const value = bpfState . values . get ( id ) ;
519596 switch ( value ?. effect ) {
520597 case Effect . WRITE :
521598 case Effect . UPDATE :
522599 row . classList . add ( 'effect-write' ) ;
523- if ( id == 'MEM' ) {
524- // show the value of register that was stored
525- const reg = ins ?. alu ?. src . id ;
526- if ( reg ) {
527- const regValue = bpfState . values . get ( reg ) ;
528- content = `${ regValue ?. value } ${ RIGHT_ARROW } ` ;
529- }
530- break ;
531- }
532-
533- let newVal = value ?. value ;
534- let oldVal = prevValue ?. value || '' ;
535- if ( newVal === oldVal )
536- content = newVal ;
537- else if ( newVal )
538- content = `${ oldVal } ${ RIGHT_ARROW } ${ newVal } ` ;
539- else
540- content = `${ oldVal } <span style="color:grey">-> scratched</span>` ;
541600 break ;
542601 case Effect . READ :
543602 row . classList . add ( 'effect-read' ) ;
544- content = value ?. value || '' ;
545603 break ;
546604 case Effect . NONE :
547605 default :
548- content = value ?. value || '' ;
549606 break ;
550607 }
551608 const nameCell = document . createElement ( 'td' ) ;
0 commit comments