GWT/JSNI-“;DataCloneError-无法克隆对象";-如何调试?

GWT/JSNI-“;DataCloneError-无法克隆对象";-如何调试?,gwt,jsni,Gwt,Jsni,我试图通过JSNI调用parallels.js。Parallels为web工作者提供了一个很好的API,我编写了一些轻量级包装器代码,它为来自GWT的工作者提供了比Elemental更方便的接口。然而,我遇到了一个让我难堪的错误: com.google.gwt.core.client.JavaScriptException:(DataCloneError)@io.mywrapper.workers.Parallel::runParallel([Ljava/lang/String;Lcom/goo

我试图通过JSNI调用parallels.js。Parallels为web工作者提供了一个很好的API,我编写了一些轻量级包装器代码,它为来自GWT的工作者提供了比Elemental更方便的接口。然而,我遇到了一个让我难堪的错误:

com.google.gwt.core.client.JavaScriptException:(DataCloneError)@io.mywrapper.workers.Parallel::runParallel([Ljava/lang/String;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)([Java object:[Ljava.lang.String;@1922352522,JavaScript object(3006),JavaScript object(3008)]):无法克隆对象

在托管模式下,这来自:

com.google.gwt.dev.shell.BrowserChannelServer.invokeJavascript(BrowserChannelServer.java:249)com.google.gwt.dev.shell.modulespaceophm.doInvoke(modulespaceophm.java:136)com.google.gwt.dev.shell.ModuleSpace.invokenactive(ModuleSpace.java:571)com.google.gwt.shell.ModuleSpace.invokenativeoid(ModuleSpace.java:299)com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:107),io.mywrapper.workers.Parallel.runParallel(Parallel.java)

这是我的密码:

创建工作进程的客户端调用示例:

    Workers.spawnWorker(new String[]{"hello"}, new Worker() {
        @Override
        public String[] work(String[] data) {
            return data;
        }

        @Override
        public void done(String[] data) {
            int i = data.length;
        }
    });
提供通用接口的API:

public class Workers {

    public static void spawnWorker(String[] data, Worker worker) {
        Parallel.runParallel(data, workFunction(worker), callbackFunction(worker));
    }

    /**
     * Create a reference to the work function.
     */
    public static native JavaScriptObject workFunction(Worker worker) /*-{
        return worker == null ? null : $entry(function(x) {
            worker.@io.mywrapper.workers.Worker::work([Ljava/lang/String;)(x);
        });
    }-*/;

    /**
     * Create a reference to the done function.
     */
    public static native JavaScriptObject callbackFunction(Worker worker) /*-{
        return worker == null ? null : $entry(function(x) {
            worker.@io.mywrapper.workers.Worker::done([Ljava/lang/String;)(x);
        });
    }-*/;        
}
工人:

public interface Worker extends Serializable {

    /**
     * Called to perform the work.
     * @param data
     * @return
     */
    public String[] work(String[] data);

    /**
     * Called with the result of the work.
     * @param data
     */
    public void done(String[] data);

}
最后是Parallels包装器:

public class Parallel {

    /**
     * @param data Data to be passed to the function
     * @param work Function to perform the work, given the data
     * @param callback Function to be called with result
     * @return
     */
    public static native void runParallel(String[] data, JavaScriptObject work, JavaScriptObject callback) /*-{
        var p =  new $wnd.Parallel(data);
        p.spawn(work).then(callback);
    }-*/;
}
这是什么原因造成的

JSNI文档说,关于阵列:

opaque value that can only be passed back into Java code
这非常简洁,但最终我的数组会被传递回Java代码,所以我认为这些都是可以的

编辑-好的,错误的假设。尽管数组表面上只是被传递回Java代码,但它导致了错误(这很奇怪,因为DataCloneError上几乎没有googleability)将它们更改为字符串可以工作;但是,字符串不足以满足我在这里的需要。看起来对象与数组面临相同的问题;我在另一个StackOverflow线程中看到Thomas对JSArrayUtils的引用,但我不知道如何使用字符串数组调用它(它需要一个JavaScriptObjects数组作为非原语类型的输入,这对我没有好处。)有没有一个简单的解决方法

编辑2-更改为在我使用字符串[]的地方使用JSArrayString。新问题;这次没有stacktrace,但在控制台中我得到错误:Uncaught ReferenceError:_gwt_makeJavaInvoke未定义。当我在开发工具中单击生成脚本的url时,我得到以下代码段:

self.onmessage = function(e) {self.postMessage((function (){
    try {
      return __gwt_makeJavaInvoke(3)(null, 65626, jsFunction, this, arguments);
    }
     catch (e) {
      throw e;
    }
  })(e.data))}
我看到_gwt_makeJavaInvoke是JSNI类的一部分;那么为什么找不到它呢?

如果您从未使用开发模式,那么现在可以安全地假装Java
字符串[]
是一个包含字符串的JS数组。这将在开发模式下中断,因为数组必须在Java中可用,并且字符串是经过特殊处理的,如果编译器以不同方式优化数组,将来可能会中断

未来可能出现错误的情况:

Java数组和JavaScript数组的语义是不同的-Java数组不能调整大小,并且根据组件类型(数组中的数据)使用特定值初始化.由于您正在编写Java代码,编译器可能会根据您如何创建和使用该数组的详细信息做出假设,这些假设可能会被JS代码破坏,而JS代码不知道永远不要修改该数组

一些基本类型的数组可以优化为JavaScript中的TypeDarray,在大小调整方面更接近Java语义,在分配方面更接近Java行为。这也会提高性能,但可能会破坏
int[]
double[]
等的任何使用


相反,您应该将数据复制到
JsArrayString
,或者只使用js数组来保存数据,而不是来回移动,这取决于您的用例。各种JsArray类型可以调整大小,并且已经作为js外部可以理解和使用的JavaScript对象存在


对编辑2的答复:

据猜测,parallel.js脚本正试图从webworker中的另一个作用域运行代码(这就是代码的要点,对吧),而您的GWT代码不存在。因此,它无法调用makeJavaInvoke,这是返回到开发模式的桥梁(对于已编译的js来说可能是另一个失败)。根据需要,传递的回调必须满足特定的要求才能传递到
spawn
,或者
then
——您的匿名函数肯定不满足这些要求,并且可能无法维护java语义

在我深入了解之前,先看看我刚才回答的关于webworkers和gwt/java的基本问题的答案:

如上所述,WebWorkers实际上是新的进程,与原始进程没有共享代码或共享状态。Parallel.js代码试图用一点技巧来掩盖这一点-共享状态仅以传递给原始并行构造函数的内容的形式提供,但您正试图传递ins“java”对象及其调用方法的实例。这些java实例具有自己的状态,并且可能通过Worker实例中的字段链接回java应用程序的其余部分。如果我正在实现Worker并执行引用其他数据而不是传入的数据的操作,那么我将看到更多奇怪的失败

因此,传入的函数必须是完全独立的-它们不能以任何方式引用外部代码,因为这样就不能将函数传递给webworker或几个webworker,每个webworker都不知道彼此的存在。例如,请参阅:

这是不可能的,因为它会

  • 需要在工作人员之间共享状态
  • 要求我们传输所有作用域变量(我认为甚至不可能读取可用的作用域)
唯一可能的事