克隆整个JavaScript ScriptEngine

时间:2012-06-20 09:02:01

标签: javascript java clone rhino scriptengine

我需要以某种方式深度克隆我的ScriptEngine对象的整个绑定集。

我尝试了什么

  • 到目前为止,我已尝试Cloner library来克隆整个Bindings结构。如果它有用,那将是很好的,因为它可以确保精确的副本,包括私有变量。但这会导致jvm堆损坏(jvm只是崩溃,退出代码为-1073740940)。有时它不会崩溃但会发生奇怪的事情,比如System.out.println()停止工作......

  • 我还研究了在ScriptEngine中使用js代码克隆对象,这样我就可以将它们作为NativeObjects来获取并在一些java地图中管理它们。但是我发现的所有克隆方法都存在缺陷。我想要一个精确的对象快照。例如,如果两个对象a和b中的每一个都包含引用相同对象c的字段(例如a.fa和b.fb),则使用jQuery.extend()(例如)克隆字段a.fa和{克隆的a和b的{1}}将引用c的不同克隆,而不是引用同一个克隆。还有许多其他边缘问题。

  • 我还尝试使用Cloner克隆整个ScriptEngine(不仅仅是绑定),我还尝试使用Rhino的js引擎并克隆整个范围(而不是Bundeled ScriptEngine包装器)。但堆腐败问题仍然存在。

为什么我需要这样做

我需要这个,因为我必须能够将整个ScriptEngine绑定的值恢复到之前的某个点。我需要对绑定进行精确的快照。

该应用程序是我的博士研究项目的一部分,该项目包括运行状态机和节点(在java中实现),其中附带了js代码。 js代码由最终用户输入,并且在运行时被逐出。当无法通过路径到达最终状态时,算法会向后退步,尝试查找备用路径。在每一步向后,它必须撤消js引擎绑定中可能发生的任何更改。


所有全局变量名称在js evaling之前都是已知的,并且是对象(用户在节点的代码中键入,然后将其组织(在java中)到具有特定名称模式的js对象中)。但它们的内容可以是任何内容,因为它由用户js代码控制。

所以我想我现在唯一的解决办法是使用js代码克隆js对象。

2 个答案:

答案 0 :(得分:3)

除了"边缘情况",jQuery.extend可以按照你提到的方式使用。 a b及其克隆将引用同一个对象c

var c = { f:'see' };
var a = { fa: c };
var b = { fb: c };
var cloneA = $.extend({}, a);
var cloneB = $.extend({}, b);
console.log(a.fa === b.fb, cloneA.fa === cloneB.fb, a.fa === cloneB.fb);
// true true true

但似乎你想要克隆所有对象(包括c),同时跟踪对象'关系。为此,最好使用对象关系表。

我通过嵌套的javascript对象和JSON看到了很多,因为人们往往会忘记JSON纯粹是一种文本格式。除了单个文本字符串instanceof String之外,JSON文件中没有实际的javascript对象。 javascript中没有豆类或泡菜或任何防腐剂重的食物。

在对象关系表中,每个表'只是一个" flat"只有原始值属性的对象和指向表(或另一个表)中其他对象的指针(不是引用)。指针可以只是目标对象的索引。

因此上述对象关系的JSON版本可能类似于

{
    "table-1":[
        { "a": { "fa":["table-2",0] } },
        { "b": { "fb":["table-2",0] } }
    ],
    "table-2":[
        { "c": { "name":"see" } },
        { "d": { "name":"dee" } },
        { "e": { "name":"eh.."} }
    ]
}

解析器可能看起来像

var tables = JSON.parse(jsonString);
for(var key in tables){
    var table = tables[key];
    for(var i = 0; i < table.length; i++){
        var name = Object.keys(table[i])
        var obj = table[i][name];
        for(var key2 in obj){
            if(obj[key2] instanceof Array && obj[key2][0] in tables){
                var refTable = obj[key2][0];
                var refID    = obj[key2][1];
                var refObj   = tables[refTable][refID];
                var refKey   = Object.keys(refObj)[0];
                obj[key2] = refObj[refKey];
            }
        }
        this[name] = obj;
    }
}

console.log(a.fa === b.fb, b.fb === c);
// true true

我意识到对象关系映射有它的垮台,但拍摄脚本引擎的快照确实听起来有点疯狂。特别是因为你的目的是能够回忆起前面的每一步,因为那时你需要为每一步创建一个新的快照...这将很快占用大量的磁盘空间..除非你只是跟踪快照在每个步骤之间进行区分,就像git repo一样。这听起来像是一项非常艰巨的工作来实现一个看似简单的&#34;撤消&#34;方法

该死的......想一想,为什么不将每一步存储在历史文件中?然后,如果您需要后退,只需截断上一步中的历史记录文件,然后在新环境中再次运行每个步骤。

不确定使用java有多么实用(性能明智)。 Nodejs(现在存在)比任何java脚本引擎都要快。事实上,从现在开始我就把它称为ECMAscript。对不起那个咆哮,只是那个

Java很慢,但你已经知道了 因为它很容易表明,它的速度和它一样快。

答案 1 :(得分:2)

我可以建议采用不同的方法。

  • 不要尝试克隆ScriptEngine。它没有实现Serializable / Externalizable,API也不支持克隆。尝试强制克隆将是未来Java版本可能会破坏的解决方法。

  • 使用cycle.js将绑定序列化为JSON。它将以{$ref: PATH}形式编码对象引用。反序列化时,它将恢复引用。

    据我所知, cycle.js 不会序列化函数,但可以使用Function.toString()自行添加函数序列化(参见下文和Example

或者,如果使用库不是一个选项,则可以非常直接地实现自己的序列化以满足您的需求:

var jsonString = JSON.stringify(obj, function(key, val) {
    if (typeof(value) === 'function')
        return val.toString();
    // or do something else with value like detect a reference
    return val
})