interp serialize

Wed Jul 19, 2006, CMcC: A quickie to serialise the variables in a sub-interpreter.

This has undoubtedly already been done, but I can't recall where it is.


nstree returns a flattened trees of all namespaces in the interp.

    proc nstree {interp {prefix ""}} {
        set result {}
        foreach ns [$interp eval [list namespace children $prefix]] {
            lappend result $ns
            lappend result {*}[nstree $interp $ns]
        }
        return $result
    }

serialize returns a valid tcl command to recreate the variables in the interp.

    proc serialize {interp} {
        $interp eval {unset -nocomplain errorCode; unset -nocomplain errorInfo}
        set result {}
        foreach v [$interp eval info globals] {
            if {[string match tcl* $v]} continue
            if {[$interp eval [list array exists $v]]} {
                lappend result [list array set $v [$interp eval [list array get $v]]]
            } else {
                lappend result [list set $v [$interp eval [list set $v]]]
            }
        }

        foreach ns [nstree $interp] {
            set subresult {}
            foreach v [$interp eval [list info vars ${ns}::*]] {
                if {[array exists $v]} {
                    lappend subresult [list array set [namespace tail $v] [$interp eval [list array get $v]]]
                } else {
                    lappend subresult [list set [namespace tail $v] [$interp eval [list set $v]]]
                }
            }
            lappend result [list namespace eval $ns [join $subresult {; }]]
        }
        return [join $result "; "]
    }

Zarutian 14. okt 2008: There is one proplem with this. The sub interp knows that it is being serialized.

Lars H: Yes, that's typical of Tcl; information isn't hidden, we just trust our programs to not poke their noses where they aren't supposed to be. I suppose if you really don't trust the interpreter you want to serialise, the you'll just have to interp hide all the sensitive commands and replace them with suitable aliases, like so:

  foreach cmd {namespace unset info array set} {
     interp hide $interp $cmd
     interp alias $interp $cmd {} $interp invokehidden $cmd
  }

This way, even if the slave interp has tried to "patch" e.g. unset in an attempt to detect the initial unsettings of errorCode and errorInfo, it will only patch the alias, so serialize still has unhindered access. There are however two holes in these precausions:

  • info is a namespace ensemble, so what we really need to hide is the component commands ::tcl::info::globals and ::tcl::info::vars. This, in turn, runs into the problem that interp hide only works for global commands, so first we'd have to rename them…
  • The primary way to discover that something is being done to variables is not to patch set or the like, but to set a trace on the variable. Getting around that mechanism is trickier (it's probably easiest to remove all traces on a variable before serialising it, and then put them back).

The code above also had several quoting errors regarding variable names (assuming that [set $v] and [eval set $v] always did the same thing), but I think I have corrected these.


Related concepts: interp, serializing


AM (15 june 2010) While invoking procedures in another interpreter is easy, exchanging variables between interpreters requires a bit of trickery.