Variable objects: automagically update values whenever changed.
authorJan Vrany <jan.vrany@fit.cvut.cz>
Sat, 03 Feb 2018 22:37:17 +0000
changeset 104 4add55336dfe
parent 103 56bf65352505
child 105 1d4ca4370d05
Variable objects: automagically update values whenever changed. `GDBVariableObject >> value would always return an up-to-date value. To check whether a varobject changed since last time it has been "looked at", use `#hasChanged`. Internally, this is implemented by issuing `-var-update` whenever a value or `#hasChanged` is requested and inferior state changed. All this is done lazily to minimize communication overhead.
GDBDebugger.st
GDBFrame.st
GDBMI_var_set_format.st
GDBMI_var_update.st
GDBThread.st
GDBThreadInfo.st
GDBVariable.st
GDBVariableObject.st
GDBVariableObjectChange.st
Make.proto
Make.spec
abbrev.stc
bc.mak
jv_libgdbs.st
libInit.cc
tests/GDBDebuggerTestsR.st
tests/GDBMIParserTests.st
tests/c/variables.c
--- a/GDBDebugger.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBDebugger.st	Sat Feb 03 22:37:17 2018 +0000
@@ -417,6 +417,28 @@
 
     "Created: / 05-06-2017 / 17:05:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 25-01-2018 / 23:28:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+selectFrame: aGDBFrame
+    "
+    Set the context frame to given frame. This frame is then
+    the contex frame for other commands, like finish, info frame
+    and so on
+      "
+    self send:(GDBMI_thread_select new arguments:(Array with:aGDBFrame thread id))andWait:false.
+    self send:(GDBMI_stack_select_frame new arguments:(Array with:aGDBFrame level)) andWait:false.
+
+    "Created: / 01-02-2018 / 22:27:16 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+selectThread: aGDBThread
+    "
+    Set the context thread to given thread. This thread is then
+    the contex thread for other commands (if no thread id is given)
+    "
+    self send:(GDBMI_thread_select new arguments:(Array with:aGDBThread id))andWait:false.
+
+    "Created: / 01-02-2018 / 22:25:32 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBDebugger methodsFor:'evaluating'!
--- a/GDBFrame.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBFrame.st	Sat Feb 03 22:37:17 2018 +0000
@@ -107,19 +107,32 @@
 variables
     self ensureIsValid.
     variables isNil ifTrue:[
-        variables := GDBTransientDataHolder debugger: debugger factory:[ 
-            | result |
+        variables := GDBTransientDataHolder debugger: debugger factory:[ :old |
+            | result new |
+
+            result := debugger send: (GDBMI_stack_list_variables new arguments: (Array with: '--thread' with: thread id with: '--frame' with: level with: '--no-values')).
+            new := (result propertyAt: #variables) ? #().
+            old notNil ifTrue:[ 
+                self assert: old size == new size. "/ for now...
+                1 to: new size do:[:i | 
+                    | oldVar newVar |
 
-            result := debugger send: (GDBMI_stack_list_variables new arguments: (Array with: '--thread' with: thread id with: '--frame' with: level with: '--simple-values')).
-            (result propertyAt: #variables) ? #()
-                do:[ :each | each setFrame: self ];
-                yourself
+                    oldVar := old at: i.
+                    newVar := new at: i.
+                    newVar name = oldVar name ifTrue:[ 
+                        new at: i put: (old at: i)
+                    ].
+                ].
+            ] ifFalse:[ 
+                new do:[:newVar | newVar setFrame: self ]
+            ].
+            new
         ].
     ].
     ^ variables value
 
     "Created: / 27-02-2015 / 14:56:22 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 17-11-2017 / 20:22:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 01-02-2018 / 22:02:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBFrame methodsFor:'initialization'!
@@ -134,6 +147,7 @@
     level := anInteger
 
     "Created: / 15-02-2018 / 08:34:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Created: / 02-02-2018 / 12:16:46 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 setLine: anInteger
--- a/GDBMI_var_set_format.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBMI_var_set_format.st	Sat Feb 03 22:37:17 2018 +0000
@@ -85,3 +85,10 @@
 	^ 'var-set-format'
 ! !
 
+!GDBMI_var_set_format class methodsFor:'documentation'!
+
+version_HG
+
+    ^ '$Changeset: <not expanded> $'
+! !
+
--- a/GDBMI_var_update.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBMI_var_update.st	Sat Feb 03 22:37:17 2018 +0000
@@ -182,3 +182,13 @@
 	^ 'var-update'
 ! !
 
+!GDBMI_var_update methodsFor:'accessing-descriptors'!
+
+resultDescription
+    ^ (super resultDescription)
+        define: #changelist as: Array of: GDBVariableObjectChange;
+        yourself
+
+    "Created: / 29-01-2018 / 20:30:30 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
--- a/GDBThread.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBThread.st	Sat Feb 03 22:37:17 2018 +0000
@@ -115,6 +115,7 @@
                 newFrame addr = oldFrame addr ifTrue:[ 
                     "/ OK, the two frames are really the same thing
                     oldFrame setLevel: newFrame level.
+                    oldFrame setLevel: newFrame level. "/ Update level
                     new at: newFrameIndex put: (old at: oldFrameIndex).
                     newFrameIndex := newFrameIndex - 1.
                     oldFrameIndex := oldFrameIndex - 1.
@@ -154,6 +155,7 @@
 
     "Created: / 09-09-2014 / 00:02:45 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 15-02-2018 / 08:41:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 01-02-2018 / 23:19:50 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 status
--- a/GDBThreadInfo.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBThreadInfo.st	Sat Feb 03 22:37:17 2018 +0000
@@ -24,7 +24,7 @@
 	instanceVariableNames:'id target_id frame state'
 	classVariableNames:''
 	poolDictionaries:''
-	category:'GDB-Private-Model'
+	category:'GDB-Private'
 !
 
 !GDBThreadInfo class methodsFor:'documentation'!
--- a/GDBVariable.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBVariable.st	Sat Feb 03 22:37:17 2018 +0000
@@ -125,6 +125,12 @@
     self debugger: frame debugger.
 
     "Created: / 27-02-2015 / 17:08:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+setValue: aString
+    value := aString
+
+    "Created: / 01-02-2018 / 21:34:17 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBVariable methodsFor:'inspecting'!
--- a/GDBVariableObject.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/GDBVariableObject.st	Sat Feb 03 22:37:17 2018 +0000
@@ -21,7 +21,8 @@
 "{ NameSpace: Smalltalk }"
 
 GDBDebuggerObject subclass:#GDBVariableObject
-	instanceVariableNames:'parent name exp thread_id value type numchild has_more children'
+	instanceVariableNames:'parent name exp thread_id value type numchild has_more children
+		changed'
 	classVariableNames:''
 	poolDictionaries:''
 	category:'GDB-Core'
@@ -111,7 +112,10 @@
 !
 
 value
+    changed value. "/ to force update if necessary
     ^ value
+
+    "Modified (comment): / 01-02-2018 / 22:31:23 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBVariableObject methodsFor:'displaying'!
@@ -159,6 +163,15 @@
 
 !GDBVariableObject methodsFor:'initialization'!
 
+setDebugger: aGDBDebugger
+    super setDebugger: aGDBDebugger.
+    aGDBDebugger notNil ifTrue:[
+        changed := GDBTransientDataHolder debugger: debugger factory: [ self updateChanged ].
+    ].
+
+    "Created: / 01-02-2018 / 09:29:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
 setExpression: aString
     exp := aString
 
@@ -167,9 +180,10 @@
 
 setParent: variableObjectOrNil
     self assert: (variableObjectOrNil isNil or:[ variableObjectOrNil isKindOf: self class ]).
-    parent := variableObjectOrNil
+    self assert: debugger notNil.
 
     "Created: / 27-01-2018 / 22:54:42 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 01-02-2018 / 09:29:00 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 
@@ -188,12 +202,58 @@
 
 !GDBVariableObject methodsFor:'queries'!
 
+hasChanged
+    ^ changed value
+
+    "Created: / 30-01-2018 / 00:27:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
 hasChildren
     ^ numchild > 0
 
     "Created: / 27-01-2018 / 22:47:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
+!GDBVariableObject methodsFor:'updating'!
+
+updateChanged
+    "Check for updates and update all changed variables accordingly."
+
+    parent notNil ifTrue:[ 
+        parent updateChanged
+    ] ifFalse:[
+        | result changelist |
+
+        result := debugger send: (GDBMI_var_update arguments: (Array with: '--all-values' with: name)).
+        changelist := result propertyAt: #changelist.
+        self updateChanged: changelist.
+    ].
+    ^ false
+
+    "Created: / 30-01-2018 / 01:08:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+updateChanged: changelist
+    | change |
+
+    changelist isEmptyOrNil ifTrue:[ ^ self ].
+    change := changelist detect: [ :each | each id = self id ] ifNone:[nil].
+    change notNil ifTrue:[ 
+        value := change value.
+        changed value: true.
+        changelist remove: change.
+        changelist isEmptyOrNil ifTrue:[ ^ self ].
+    ] ifFalse:[ 
+        changed value: false.
+    ].
+    children notEmptyOrNil ifTrue:[ 
+        children do: [ :each | each updateChanged: changelist ]
+    ].
+
+    "Created: / 30-01-2018 / 01:09:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 01-02-2018 / 22:42:49 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
 !GDBVariableObject class methodsFor:'documentation'!
 
 version_HG
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GDBVariableObjectChange.st	Sat Feb 03 22:37:17 2018 +0000
@@ -0,0 +1,89 @@
+"{ Encoding: utf8 }"
+
+"
+jv:libgdbs - GNU Debugger Interface Library
+Copyright (C) 2015-now Jan Vrany
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License. 
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+"
+"{ Package: 'jv:libgdbs' }"
+
+"{ NameSpace: Smalltalk }"
+
+GDBDebuggerObject subclass:#GDBVariableObjectChange
+	instanceVariableNames:'name value in_scope type_changed new_type new_num_children
+		displayhint has_more dynamic new_children'
+	classVariableNames:''
+	poolDictionaries:''
+	category:'GDB-Private'
+!
+
+!GDBVariableObjectChange class methodsFor:'documentation'!
+
+copyright
+"
+jv:libgdbs - GNU Debugger Interface Library
+Copyright (C) 2015-now Jan Vrany
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License. 
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+"
+! !
+
+!GDBVariableObjectChange methodsFor:'accessing'!
+
+id
+    ^ name
+
+    "Created: / 29-01-2018 / 22:01:36 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+value
+    ^ value
+! !
+
+!GDBVariableObjectChange methodsFor:'testing'!
+
+isInvalid
+    "Return true, of the variable object no longer holds a valid value. 
+     This can occur when the executable file being debugged has changed, 
+     either through recompilation or by using the GDB file command. The 
+     front end should normally choose to delete these variable objects."
+
+    ^ in_scope = 'invalid'
+
+    "Created: / 29-01-2018 / 22:06:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+isValid
+    "Return `true`, if the variable object’s current value is valid,
+     `false` otherwise."
+
+    ^ in_scope = 'true'
+
+    "Created: / 29-01-2018 / 22:05:37 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+! !
+
--- a/Make.proto	Thu Feb 15 08:46:03 2018 +0000
+++ b/Make.proto	Sat Feb 03 22:37:17 2018 +0000
@@ -34,7 +34,7 @@
 # add the path(es) here:,
 # ********** OPTIONAL: MODIFY the next lines ***
 # LOCALINCLUDES=-Ifoo -Ibar
-LOCALINCLUDES= -I$(INCLUDE_TOP)/stx/goodies/announcements -I$(INCLUDE_TOP)/stx/goodies/magritte -I$(INCLUDE_TOP)/stx/goodies/sunit -I$(INCLUDE_TOP)/stx/libbasic -I$(INCLUDE_TOP)/stx/libbasic2 -I$(INCLUDE_TOP)/stx/libtool -I$(INCLUDE_TOP)/stx/libview2 -I$(INCLUDE_TOP)/stx/libwidg
+LOCALINCLUDES= -I$(INCLUDE_TOP)/stx/goodies/announcements -I$(INCLUDE_TOP)/stx/goodies/magritte -I$(INCLUDE_TOP)/stx/libbasic -I$(INCLUDE_TOP)/stx/libbasic2 -I$(INCLUDE_TOP)/stx/libtool -I$(INCLUDE_TOP)/stx/libview2 -I$(INCLUDE_TOP)/stx/libwidg
 
 
 # if you need any additional defines for embedded C code,
@@ -297,6 +297,7 @@
 $(OUTDIR)GDBThreadGroup.$(O) GDBThreadGroup.$(C) GDBThreadGroup.$(H): GDBThreadGroup.st $(INCLUDE_TOP)/jv/libgdbs/GDBCommandStatus.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
 $(OUTDIR)GDBTransientObject.$(O) GDBTransientObject.$(C) GDBTransientObject.$(H): GDBTransientObject.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
 $(OUTDIR)GDBVariableObject.$(O) GDBVariableObject.$(C) GDBVariableObject.$(H): GDBVariableObject.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
+$(OUTDIR)GDBVariableObjectChange.$(O) GDBVariableObjectChange.$(C) GDBVariableObjectChange.$(H): GDBVariableObjectChange.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
 $(OUTDIR)GDBVariableObjectExecutor.$(O) GDBVariableObjectExecutor.$(C) GDBVariableObjectExecutor.$(H): GDBVariableObjectExecutor.st $(INCLUDE_TOP)/jv/libgdbs/GDBDebuggerObject.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBObject.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
 $(OUTDIR)GDBBreakpointDeletedEvent.$(O) GDBBreakpointDeletedEvent.$(C) GDBBreakpointDeletedEvent.$(H): GDBBreakpointDeletedEvent.st $(INCLUDE_TOP)/jv/libgdbs/GDBAsyncEvent.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBEvent.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBNotificationEvent.$(H) $(INCLUDE_TOP)/stx/goodies/announcements/Announcement.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
 $(OUTDIR)GDBBreakpointEvent.$(O) GDBBreakpointEvent.$(C) GDBBreakpointEvent.$(H): GDBBreakpointEvent.st $(INCLUDE_TOP)/jv/libgdbs/GDBAsyncEvent.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBEvent.$(H) $(INCLUDE_TOP)/jv/libgdbs/GDBNotificationEvent.$(H) $(INCLUDE_TOP)/stx/goodies/announcements/Announcement.$(H) $(INCLUDE_TOP)/stx/libbasic/Object.$(H) $(STCHDR)
--- a/Make.spec	Thu Feb 15 08:46:03 2018 +0000
+++ b/Make.spec	Sat Feb 03 22:37:17 2018 +0000
@@ -221,6 +221,7 @@
 	GDBThreadGroup \
 	GDBTransientObject \
 	GDBVariableObject \
+	GDBVariableObjectChange \
 	GDBVariableObjectExecutor \
 	GDBBreakpointDeletedEvent \
 	GDBBreakpointEvent \
@@ -417,6 +418,7 @@
     $(OUTDIR)GDBThreadGroup.$(O) \
     $(OUTDIR)GDBTransientObject.$(O) \
     $(OUTDIR)GDBVariableObject.$(O) \
+    $(OUTDIR)GDBVariableObjectChange.$(O) \
     $(OUTDIR)GDBVariableObjectExecutor.$(O) \
     $(OUTDIR)GDBBreakpointDeletedEvent.$(O) \
     $(OUTDIR)GDBBreakpointEvent.$(O) \
--- a/abbrev.stc	Thu Feb 15 08:46:03 2018 +0000
+++ b/abbrev.stc	Sat Feb 03 22:37:17 2018 +0000
@@ -40,7 +40,7 @@
 GDBSessionRecorder GDBSessionRecorder jv:libgdbs 'GDB-Private-Simulator' 0
 GDBStreamOutputEvent GDBStreamOutputEvent jv:libgdbs 'GDB-Core-Events' 0
 GDBThreadGroupTypeProcess GDBThreadGroupTypeProcess jv:libgdbs 'GDB-Core' 1
-GDBThreadInfo GDBThreadInfo jv:libgdbs 'GDB-Private-Model' 0
+GDBThreadInfo GDBThreadInfo jv:libgdbs 'GDB-Private' 0
 GDBThreadStateRunning GDBThreadStateRunning jv:libgdbs 'GDB-Core' 1
 GDBThreadStateStopped GDBThreadStateStopped jv:libgdbs 'GDB-Core' 1
 GDBThreadStateTerminated GDBThreadStateTerminated jv:libgdbs 'GDB-Core' 1
@@ -171,6 +171,7 @@
 GDBThreadGroup GDBThreadGroup jv:libgdbs 'GDB-Core' 0
 GDBTransientObject GDBTransientObject jv:libgdbs 'GDB-Core' 0
 GDBVariableObject GDBVariableObject jv:libgdbs 'GDB-Core' 0
+GDBVariableObjectChange GDBVariableObjectChange jv:libgdbs 'GDB-Private' 0
 GDBVariableObjectExecutor GDBVariableObjectExecutor jv:libgdbs 'GDB-Private' 0
 GDBBreakpointDeletedEvent GDBBreakpointDeletedEvent jv:libgdbs 'GDB-Core-Events' 0
 GDBBreakpointEvent GDBBreakpointEvent jv:libgdbs 'GDB-Core-Events' 0
--- a/bc.mak	Thu Feb 15 08:46:03 2018 +0000
+++ b/bc.mak	Sat Feb 03 22:37:17 2018 +0000
@@ -35,7 +35,7 @@
 
 
 
-LOCALINCLUDES= -I$(INCLUDE_TOP)\stx\goodies\announcements -I$(INCLUDE_TOP)\stx\goodies\magritte -I$(INCLUDE_TOP)\stx\goodies\sunit -I$(INCLUDE_TOP)\stx\libbasic -I$(INCLUDE_TOP)\stx\libbasic2 -I$(INCLUDE_TOP)\stx\libtool -I$(INCLUDE_TOP)\stx\libview2 -I$(INCLUDE_TOP)\stx\libwidg
+LOCALINCLUDES= -I$(INCLUDE_TOP)\stx\goodies\announcements -I$(INCLUDE_TOP)\stx\goodies\magritte -I$(INCLUDE_TOP)\stx\libbasic -I$(INCLUDE_TOP)\stx\libbasic2 -I$(INCLUDE_TOP)\stx\libtool -I$(INCLUDE_TOP)\stx\libview2 -I$(INCLUDE_TOP)\stx\libwidg
 LOCALDEFINES=
 
 STCLOCALOPT=-package=$(PACKAGE) -I. $(LOCALINCLUDES) -headerDir=. $(STCLOCALOPTIMIZATIONS) $(STCWARNINGS) $(LOCALDEFINES)  -varPrefix=$(LIBNAME)
@@ -244,6 +244,7 @@
 $(OUTDIR)GDBThreadGroup.$(O) GDBThreadGroup.$(C) GDBThreadGroup.$(H): GDBThreadGroup.st $(INCLUDE_TOP)\jv\libgdbs\GDBCommandStatus.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
 $(OUTDIR)GDBTransientObject.$(O) GDBTransientObject.$(C) GDBTransientObject.$(H): GDBTransientObject.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
 $(OUTDIR)GDBVariableObject.$(O) GDBVariableObject.$(C) GDBVariableObject.$(H): GDBVariableObject.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
+$(OUTDIR)GDBVariableObjectChange.$(O) GDBVariableObjectChange.$(C) GDBVariableObjectChange.$(H): GDBVariableObjectChange.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
 $(OUTDIR)GDBVariableObjectExecutor.$(O) GDBVariableObjectExecutor.$(C) GDBVariableObjectExecutor.$(H): GDBVariableObjectExecutor.st $(INCLUDE_TOP)\jv\libgdbs\GDBDebuggerObject.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBObject.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
 $(OUTDIR)GDBBreakpointDeletedEvent.$(O) GDBBreakpointDeletedEvent.$(C) GDBBreakpointDeletedEvent.$(H): GDBBreakpointDeletedEvent.st $(INCLUDE_TOP)\jv\libgdbs\GDBAsyncEvent.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBEvent.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBNotificationEvent.$(H) $(INCLUDE_TOP)\stx\goodies\announcements\Announcement.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
 $(OUTDIR)GDBBreakpointEvent.$(O) GDBBreakpointEvent.$(C) GDBBreakpointEvent.$(H): GDBBreakpointEvent.st $(INCLUDE_TOP)\jv\libgdbs\GDBAsyncEvent.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBEvent.$(H) $(INCLUDE_TOP)\jv\libgdbs\GDBNotificationEvent.$(H) $(INCLUDE_TOP)\stx\goodies\announcements\Announcement.$(H) $(INCLUDE_TOP)\stx\libbasic\Object.$(H) $(STCHDR)
--- a/jv_libgdbs.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/jv_libgdbs.st	Sat Feb 03 22:37:17 2018 +0000
@@ -89,7 +89,6 @@
      Please also take a look at the #mandatoryPreRequisites method"
 
     ^ #(
-        #'stx:goodies/sunit'    "TestAsserter - superclass of GDBSimulatorResource"
         #'stx:libbasic2'    "List - referenced by GDBDebugger>>breakpoints"
         #'stx:libtool'    "Tools::Inspector2Tab - referenced by GDBBreakpoint>>inspector2TabCondition"
         #'stx:libview2'    "ApplicationModel - referenced by GDBEventSubscription class>>blockFor:withSelector:"
@@ -287,6 +286,7 @@
         GDBThreadGroup
         GDBTransientObject
         GDBVariableObject
+        GDBVariableObjectChange
         GDBVariableObjectExecutor
         GDBBreakpointDeletedEvent
         GDBBreakpointEvent
--- a/libInit.cc	Thu Feb 15 08:46:03 2018 +0000
+++ b/libInit.cc	Sat Feb 03 22:37:17 2018 +0000
@@ -186,6 +186,7 @@
 extern void _GDBThreadGroup_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
 extern void _GDBTransientObject_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
 extern void _GDBVariableObject_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
+extern void _GDBVariableObjectChange_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
 extern void _GDBVariableObjectExecutor_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
 extern void _GDBBreakpointDeletedEvent_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
 extern void _GDBBreakpointEvent_Init(int pass, struct __vmData__ *__pRT__, OBJ snd);
@@ -391,6 +392,7 @@
     _GDBThreadGroup_Init(pass,__pRT__,snd);
     _GDBTransientObject_Init(pass,__pRT__,snd);
     _GDBVariableObject_Init(pass,__pRT__,snd);
+    _GDBVariableObjectChange_Init(pass,__pRT__,snd);
     _GDBVariableObjectExecutor_Init(pass,__pRT__,snd);
     _GDBBreakpointDeletedEvent_Init(pass,__pRT__,snd);
     _GDBBreakpointEvent_Init(pass,__pRT__,snd);
--- a/tests/GDBDebuggerTestsR.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/tests/GDBDebuggerTestsR.st	Sat Feb 03 22:37:17 2018 +0000
@@ -423,6 +423,80 @@
     "Created: / 30-01-2018 / 10:27:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 
 
+!
+
+test_variables_02
+    "
+    This test ensures that GDBVariable objects are preserved
+    across multiple run-stop cycles
+    "    
+    | variables1 variables2 |
+
+    debugger := GDBDebugger new.
+    debugger executable: GDBDebuggeesResource current binaryVariables.
+
+    debugger send: 'b main'.
+    debugger send: 'r' andWaitFor: GDBStoppedEvent.
+    debugger send: 's' andWaitFor: GDBStoppedEvent.
+
+    variables1 := debugger selectedInferior threads first stack first variables.
+    debugger send: 'next' andWaitFor: GDBStoppedEvent.
+    variables2 := debugger selectedInferior threads first stack first variables.
+
+    self assert: variables1 size = 3.
+    self assert: variables2 size = 3.
+    variables1 with: variables2 do:[:var1 :var2 | 
+        self assert: var1 == var2    
+    ].
+
+    debugger send: 'c' andWaitFor: GDBThreadGroupExitedEvent.
+    debugger send: 'quit' andWait: false.
+
+    "Created: / 01-02-2018 / 21:45:03 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+!
+
+test_variables_03
+    "
+    This test tests GDBVariableObject >> hasChanged
+    " 
+    | variables d d_i |
+
+    debugger := GDBDebugger new.
+    debugger executable: GDBDebuggeesResource current binaryVariables.
+
+    debugger send: 'b set_data_i'.
+    debugger send: 'r' andWaitFor: GDBStoppedEvent.
+
+    self assert: debugger selectedInferior threads first stack second func = 'main'.
+    variables := debugger selectedInferior threads first stack second variables.
+    self assert: variables third name = 'd'.
+    self assert: variables third varobj expression = 'd'.   
+    d := variables third varobj.
+    self assert: d hasChildren.
+    self assert: d expression = 'd'.
+    self assert: d children size = 3.   
+    d_i := d children first.
+    self assert: d_i expression = 'i'.
+    self assert: d_i value = '1'.
+    self assert: d_i hasChanged not.
+    self assert: d_i hasChildren not .   
+
+    debugger selectFrame: debugger selectedInferior threads first stack first.
+    debugger send: 'finish' andWaitFor: GDBStoppedEvent.
+    self assert: debugger selectedInferior threads first stack first func = 'main'.
+    self assert: d_i value = '12'.
+    self assert: d_i hasChanged.
+    self assert: d_i hasChanged.
+    self assert: d_i hasChanged.
+
+    debugger send: 's' andWaitFor: GDBStoppedEvent.
+    self assert: d_i value = '12'.
+    self assert: d_i hasChanged not.
+
+    debugger send: 'c' andWaitFor: GDBThreadGroupExitedEvent.
+    debugger send: 'quit' andWait: false.
+
+    "Created: / 01-02-2018 / 21:57:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBDebuggerTestsR class methodsFor:'documentation'!
--- a/tests/GDBMIParserTests.st	Thu Feb 15 08:46:03 2018 +0000
+++ b/tests/GDBMIParserTests.st	Sat Feb 03 22:37:17 2018 +0000
@@ -108,7 +108,7 @@
     self assert:(GDBMIParser on:'"\H\e\l\l\o" xxx') parseCString = 'Hello'.
     self assert:(GDBMIParser on:'"Hel\nlo" xxx') parseCString = 'Hel
 lo'.
-    self assert:(GDBMIParser on:'"X\xE1X" xxx') parseCString = 'XáX'.
+    self assert:(GDBMIParser on:'"X\xE1X" xxx') parseCString = ('X', (Character codePoint: 16rE1), 'X').
     self 
         assert:(GDBMIParser 
                 on:'"warning: File \"/home/jv/Private/Projects/SmalltalkX/sources/branches/jv1/build/stx/.gdbinit\" auto-loading has been declined by your `auto-load safe-path'' set to \"$debugdir:$datadir/auto-load\".\n"') 
@@ -117,7 +117,7 @@
 '.
 
     "Created: / 28-05-2014 / 00:05:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 03-02-2018 / 21:43:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 15-02-2018 / 08:55:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 ! !
 
 !GDBMIParserTests methodsFor:'tests - commands'!
@@ -325,6 +325,7 @@
 
     "Created: / 19-03-2015 / 07:46:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 03-02-2018 / 21:44:04 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    
 !
 
 test_command_var_create_02
@@ -343,6 +344,32 @@
     self assert: result value isNil.
 
     "Created: / 02-02-2018 / 09:27:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+
+!
+
+test_command_var_update_01
+    | parser events result changelist |
+
+    parser := GDBMIParser 
+            on:('3^done,changelist=[{name="var1",value="3",in_scope="true",
+type_changed="false"}]' asStringCollection asStringWith:'').
+    parser token2CommandMappingBlock:[:token | GDBMI_var_update new ].
+    events := parser parseOutput.
+
+    self assert:events size == 1.
+    result := events first result.
+    changelist := result propertyAt: #changelist.  
+
+    self assert: changelist size == 1.
+    self assert:(changelist first isKindOf: GDBVariableObjectChange).
+    self assert:(changelist first id = 'var1').
+    self assert:(changelist first value = '3').
+    self assert:(changelist first isValid = true).
+    self assert:(changelist first isInvalid = false).
+
+    "Created: / 29-01-2018 / 20:21:29 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 29-01-2018 / 22:01:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+
 ! !
 
 !GDBMIParserTests methodsFor:'tests - examples'!
@@ -705,12 +732,19 @@
 !GDBMIParserTests methodsFor:'tests - values - typed'!
 
 test_typed_boolean
-    self assert:(GDBMIParser on:'"y"') parseValueAsBoolean == true.
-    self assert:(GDBMIParser on:'"n"') parseValueAsBoolean == false.
+    self assert:(GDBMIParser on:'"y"') parseValueAsBoolean = true.
+    self assert:(GDBMIParser on:'"n"') parseValueAsBoolean = false.
+    self assert:(GDBMIParser on:'"yX"') parseValueAsBoolean = 'yX'.
+    self assert:(GDBMIParser on:'"nM"') parseValueAsBoolean = 'nM'.
+    self assert:(GDBMIParser on:'"1"') parseValueAsBoolean = true.
+    self assert:(GDBMIParser on:'"0"') parseValueAsBoolean = false.
+    self assert:(GDBMIParser on:'"1X"') parseValueAsBoolean = '1X'.
+    self assert:(GDBMIParser on:'"0M"') parseValueAsBoolean = '0M'.
     self assert:(GDBMIParser on:'"no"') parseValueAsBoolean = 'no'.
     self assert:(GDBMIParser on:'"12xyz"') parseValueAsBoolean = '12xyz'.
 
     "Created: / 18-06-2014 / 23:38:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified: / 29-01-2018 / 20:28:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
 !
 
 test_typed_integer
--- a/tests/c/variables.c	Thu Feb 15 08:46:03 2018 +0000
+++ b/tests/c/variables.c	Sat Feb 03 22:37:17 2018 +0000
@@ -15,6 +15,15 @@
     };
 } data;
 
+void set_data_i(data *d, int i) {
+    d->i = i;
+}
+
+void set_data_f(data *d, float f) {
+    d->as_f.f = f;
+}
+
+
 int main(int argc, char **argv) {
     data d = {
         1,
@@ -26,5 +35,7 @@
             }
         }
     };
+    set_data_i(&d, 12);
+    set_data_f(&d, 0.1);
     return d.i;
 }