robertkrimen / otto

A JavaScript interpreter in Go (golang)
http://godoc.org/github.com/robertkrimen/otto
MIT License
8.01k stars 584 forks source link

Is there a way serialize *otto.Otto and save it to disk? #133

Open jclohmann opened 9 years ago

jclohmann commented 9 years ago

Hi! Is there a way to serialize the Otto-object (or some other context-containing-object)? I would like to save javascripts this (with all dynamicly added fields) to disk and be able to load it after a restart.

Simply pushing it into a gob.Encoder (or json.Encoder) seems not to work :-)

deoxxa commented 8 years ago

I feel like there are two "levels" for this kind of feature.

  1. being able to serialise the global state of the vm - things like defined functions, variables, etc. For this, _runtime.clone would probably be a good place to start investigating.
  2. being able to serialise the current execution context of the vm - basically the entire call stack. This would probably be really tricky, especially if there are native functions on the stack.
darkliquid commented 8 years ago

Agreed. Some kind of global state serialisation based on clone seems a good area to investigate and could be useful.

I think point 2 might be doable if you make some assumptions:

If all the assumptions there are true, I would guess you could have a special serialisation format for native functions that annotates the full package path, object references, etc to be able to find and replumb in the function call. However, thats not trivial by any stretch of the imagination. I'm not entirely convinced of the utility of this though, it seems like a lot of work for some very limited use-cases.

Arguably it would be better to write a book-keeping wrapper around your runtime that tracks native functions being attached and has it's own serialisation system that works in tandem with the stuff in point 1 to rebuild your execution context, as typically the functiosn you want to expose are going to be from all over the place and otto can't be expected to have the required domain knowledge to work in a generic sense.

alexandru-eftimie commented 2 years ago

I use a simple-ish way to export and save most of what I actually need.

var $exclude = "$exclude,console,..."// replace ... with other functions to ignore
$exclude = $exclude.split(",")
function exportGlobal(src) {

    var dst = {}
    var keys = Object.keys(src)
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i]

        // skip exluded keys
        if ($exclude.indexOf(key) != -1) {
            continue;
        }

        var element = src[key];
        if (typeof element == "function") {
            dst[key] = "func://" + element.toString()
        } else {
            dst[key] = element
        }
    }
    return JSON.stringify(dst)
}
function importGlobal(dst, src) {

    var keys = Object.keys(src)

    for (var i = 0; i < keys.length; i++) {
        var key = keys[i]

        // skip exluded keys
        if ($exclude.indexOf(key) != -1) {
            continue;
        }

        var element = src[key];
        if (typeof element == "string" && element.substring(0, 7) == "func://") {
            eval("dst[key] = " + element.substring(7))
        } else {
            dst[key] = element
        }
    }
}

And I run on save:

o.Run("exportGlobal(this)")

On load:

o.Run("importGlobal(this, " + old + ")")

It saves global functions but not nested ones.