克隆整个JavaScript ScriptEngine

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

我试过的

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

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

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

为什么我需要这样做

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

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


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

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

除了“边缘情况”之外,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中没有豆类或泡菜或任何防腐剂重的食物。

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

所以上述对象关系的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。 实现看似简单的“撤消”方法听起来真是太多了。

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

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

Java很慢,但你已经知道了。
因为它很容易显示出来,就像它一样快。

我可以提出一个不同的方法。

  • 不要尝试克隆ScriptEngine。 它没有实现Serializable / Externalizable,API也不支持克隆。 尝试强制克隆将是一些可能在将来的某些Java版本中破解的变通方法。

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

    至于我可以告诉cycle.js没有序列化函数,但可以使用Function.toString()自己添加函数序列化(参见下面和示例 )

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

 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 })