【问题标题】:Clone Entire JavaScript ScriptEngine克隆整个 JavaScript ScriptEngine
【发布时间】:2012-06-22 09:17:00
【问题描述】:

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

我尝试过的

  • 到目前为止,我已经尝试使用 Cloner library 克隆整个 Bindings 结构。如果它有效,那就太好了,因为它可以确保精确的副本,包括私有变量。但这会导致 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 引擎并克隆整个范围(而不是捆绑的 ScriptEngine 包装器)。但是堆损坏问题仍然存在。

为什么我需要这样做

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

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


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

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

【问题讨论】:

    标签: javascript java clone rhino scriptengine


    【解决方案1】:

    我可以建议一种不同的方法吗?

    • 不要试图克隆 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
    })
    

    【讨论】:

      【解决方案2】:

      除了“边缘情况”,jQuery.extend 可以按照您提到的方式使用。 ab 及其克隆都将引用同一个对象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 是纯粹的文本格式。 JSON 文件中没有实际的 javascript 对象,除了单个文本字符串 instanceof String。 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 很慢,但你已经知道了。
      因为它很容易显示,所以速度很快。

      【讨论】:

      • 感谢您的详细回复。正如你所说,我最终使用 js 克隆了对象并在 js 中维护了克隆历史。克隆方法在我的回答stackoverflow.com/questions/728360/…中有详细描述。当一个新对象被克隆时,一个唯一的 id 添加到原始对象中,并且克隆被添加到地图中。当必须再次克隆同一个对象时,将使用映射中的克隆,因为原始对象具有唯一的 id。最终项目是sourceforge.net/projects/ggs
      猜你喜欢
      • 2014-08-09
      • 1970-01-01
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      • 2013-01-03
      • 2015-02-12
      • 2017-08-16
      • 2017-12-01
      相关资源
      最近更新 更多