3

I have run into a weird issue with gwt dev mode debugging.

Following is a JSNI wrapper I am writing https://github.com/sillysachin/GWTAMChart

It is fairly small and simple project with lots of JSNI, JavaScriptObject and JSON code. It wraps over popular amcharts charting library. It works well when debugged in SuperDevMode and in Production.

However I am not able to debug the project in Internet Explorer with Dev Mode Debugging.

java.lang.ClassFormatError: Duplicate method name&signature in class file com/google/gwt/core/client/JavaScriptObject$

The main exception thrown is not helping me figure which part of code is breaking !!!!!

java.lang.ClassFormatError: Duplicate method name&signature in class file com/google/gwt/core/client/JavaScriptObject$
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:643)
    at com.google.gwt.dev.shell.CompilingClassLoader.findClass(CompilingClassLoader.java:1142)
    at com.google.gwt.dev.shell.CompilingClassLoader.loadClass(CompilingClassLoader.java:1215)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:270)
    at com.google.gwt.dev.shell.JsValueGlue.set(JsValueGlue.java:220)
    at com.google.gwt.dev.shell.ModuleSpaceOOPHM.doInvoke(ModuleSpaceOOPHM.java:130)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNative(ModuleSpace.java:589)
    at com.google.gwt.dev.shell.ModuleSpace.invokeNativeVoid(ModuleSpace.java:315)
    at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:359)
    at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:200)
    at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:530)
    at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:368)
    at java.lang.Thread.run(Thread.java:745)
appbootup
  • 9,537
  • 3
  • 33
  • 65

2 Answers2

1

The problematic class is AmChartJSO implementing the IsAmChart interface - all the methods are declared twice in JavaScriptObject$. Snippet from the bytecode:

public final synthetic com_amcharts_api_IsAmChart_setVersion(Ljava/lang/String;)V
  ALOAD 0
  ALOAD 1
  INVOKESTATIC com/amcharts/jso/AmChartJSO$.setVersion$ (Lcom/amcharts/jso/AmChartJSO;Ljava/lang/String;)V
  RETURN
  MAXSTACK = 2
  MAXLOCALS = 2

// access flags 0x1011
public final synthetic com_amcharts_api_IsAmChart_setVersion(Ljava/lang/String;)V
  ALOAD 0
  ALOAD 1
  INVOKESTATIC com/amcharts/jso/AmChartJSO$.setVersion$ (Lcom/amcharts/jso/AmChartJSO;Ljava/lang/String;)V
  RETURN
  MAXSTACK = 2
  MAXLOCALS = 2

It seems you've run into a limitation of overlay types - only one JavaScriptObject subtype can implement any given interface:

Practically speaking, this means that only one JavaScriptObject type may implement any given interface, but any number of non-JavaScriptObject types may also implement that interface.

Looking at your code, this restriction is broken: AmChartJSO implements IsAmChart, but AmCoordinateChartJSO implements IsAmCoordinateChart which extends IsAmChart - and thus two JSOs implement the same interface. If I understand this restriction correctly, you can't even subclass a JSO that implements an interface.

I've done a quick test and this code fails too:

public class Test extends JavaScriptObject implements TakesValue<String> {
    protected Test() {
    }

    @Override
    public final void setValue(String value) {
    }

    @Override
    public final String getValue() {
        return null;
    }
}

public class Test2 extends Test {
    protected Test2() {
    }
}

With a similarly useless exception:

java.lang.NullPointerException: null
    at com.google.gwt.dev.shell.CompilingClassLoader$MySingleJsoImplData.findOverloadUsingErasure(CompilingClassLoader.java:703)
    at com.google.gwt.dev.shell.CompilingClassLoader$MySingleJsoImplData.<init>(CompilingClassLoader.java:593)
    at com.google.gwt.dev.shell.CompilingClassLoader.<init>(CompilingClassLoader.java:980)
    at com.google.gwt.dev.shell.ShellModuleSpaceHost.onModuleReady(ShellModuleSpaceHost.java:137)
    at com.google.gwt.dev.shell.ModuleSpace.onLoad(ModuleSpace.java:340)
    at com.google.gwt.dev.shell.OophmSessionHandler.loadModule(OophmSessionHandler.java:200)
    at com.google.gwt.dev.shell.BrowserChannelServer.processConnection(BrowserChannelServer.java:526)
    at com.google.gwt.dev.shell.BrowserChannelServer.run(BrowserChannelServer.java:364)
    at java.lang.Thread.run(Thread.java:745)

Please see this thread on GWT's mailing list for workarounds and general discussion of this problem.


To dump the generated class files yourself

For future reference, you can dump the generated class filed by setting the gwt.dev.classDump system property (-Dgwt.dev.classDump=true). See this wiki page for more information. By default the classes are written to the rewritten-classes folder (in your case it will be war/rewritten-classes). The classes are organized by packages, so finding JavaScriptObject$ is easy: rewritten-classes/com/google/gwt/core/client/JavaScriptObject$.class.

Now, all you need to do is disassemble it - I've used the Bytecode Outline plugin for Eclipse and got the bytecode of JavaScriptObject$.class.

To find out which methods were duplicated, I could just load the class file with a classloader and let the JVM figure it out... But I was feeling lazy so I've just greped for public final synthetic in the bytecode and run uniq -D to see only the duplicated entries.

Igor Klimer
  • 15,321
  • 3
  • 47
  • 57
  • Fantastic explanation. Got it. Do you recommend cleaning up the interface hierarchy or overhaul of the approach i have taken for the client side jso and impl packages. – appbootup Dec 07 '14 at 20:07
  • Curious to know whether my initial assumption of SuperDevMode and Production mode functioning is mis-interpretation. The Dev Mode does not launch where Super Dev Mode launches and functions with this design !!!!! – appbootup Dec 07 '14 at 20:29
  • Yes, it is very interesting that SDM works with this design, while classic DevMode does not. Maybe it's a bug? Or a regression? It might be worthwhile to file a bug report - at least they could improve the error thrown ;) As for the overall design - it definitely needs an overhaul, because even if you understand the limitation now, a new developer, trying to extend your library might not and could easily run into the same issue. There were some promising ideas in the linked thread that you could try. – Igor Klimer Dec 07 '14 at 21:01
  • Sorted out the issue by removing IJavaScriptWrapper implemented repeatedly by sub classes of AmChart. Accepting the answer as it pointed me in the right direction. The solution did not require me to revisit the JSO hierarchy. – appbootup Dec 10 '14 at 06:37
0

This happens in dev mode when using JSNI. Since super dev mode directly debugs in browser with pure java script, there is no strict type casting and produce no errors. Where as in dev mode the java code runs and due to strict type casting you will get class format exception. There is no avail. Same problem with highcharts as well. If it works well in production and super dev mode then you should not worry about it. Hope it helps.

Abhijith Nagaraja
  • 3,370
  • 6
  • 27
  • 55
  • Need it for ensuring GWT Dev Mode debugging as Super Dev Mode is impossible to use with mega projects. Since Chrome and Firefox have dropped dev mode. We are forced to use IE9/11 for Debugging. – appbootup Dec 04 '14 at 11:56