How to disable *specific* pruning/inlining with GWT compiler?

95 views
Skip to first unread message

Sebastien Diot

unread,
May 2, 2014, 9:51:03 AM5/2/14
to google-we...@googlegroups.com
I have been trying to create an API, such that it would "look and feel" the same, both from GWT Java code, and from manually written JavaScript code. After much Googling, I came to gwt-exporter, which is the "universal answer" used, for anyone trying to do such a thing. So I gave it a try; unfortunately, things did not go well, as explained in this post to the gwt-exporter forum: Is there an ETA for GWT 2.6 support?

Having received no answer in 3 days, I have decided to try and do it "manually". I haven't had any success here either, but I think I have found the true problem: GWT just kills (some of) my code, because it thinks it's "not being used" (or perhaps, "inline" some methods, causing then to not be available anymore). In any case, the methods are *gone*. Looking for a solution to *that problem*, I get back to posts saying to use gwt-exporter there too. So I'm stuck in an infinite loop with no way out.

One critical difference to the "usual case", is that the manually written JavaScript code does not come from a JSNI call, but from an "external source", and so is *not available at compile time*. A compiler flag telling the compiler not to "prune" something would not be useful in this case; the flag/marker needs to be specific to classes and methods, instead of applying globally.

As I did not get any answer on the gwt-exporter forum in 3 days, and have therefore no way of knowing if, and when, gwt-exporter will be updated, I was hoping someone could point me to a solution that would provide the following benefits, even if it involves much boiler-plate GWT/JSNI code:

1) Instances of "marked" types can be created from JavaScript (in or out of JSNI, like from dynamically generated scripts, or scripts typed directly in the app UI), using the fully-qualified, non-obfuscated, Java class name. This should still work, if no code refers to that class, either from Java or JSNI.

2) All the public (at least) methods on the types selected in 1) will be available in JavaScript, using their simple, non-obfuscated, name (this probably preclude method overloading). It should work for both instance, and static methods. This should still work, if no code refers to that method, either from Java or JSNI.

3) (Optional) Same as methods, but for fields too.

The "end goal" is to allow users some limited ability to "customize" the application by "injecting user scripts" into it.

I would be grateful for any help.

Regards,
Sebastien Diot

Thomas Broyer

unread,
May 2, 2014, 6:01:06 PM5/2/14
to google-we...@googlegroups.com
To expose Java/GWT classes to JS, you have to wrap them, assigning there methods to Jav/GWT methods. E.g.

$wnd.com = $wnd.com || {};
$wnd.com.example = $wnd.com.example || {};

var _ = $wnd.com.example.MyClass = $entry(function(a, b) {
  this._real = @com.example.MyClass:new(II)(a, b);
};
_.prototype.myMethod = $entry(function(a, b) {
  this._real @ com.example.MyClass:myMethod(Lcom/example/MyClass;Z)(a._real, b);
});

Note how in myMethod I "unwrap" the Java/GWT MyClass instance from within the JS MyClass instance.
Methods that return a MyClass need to wrap it too (and ideally re-use the same JS wrapper as was already used for the same Java/GWT instance)

Because the class/constructor/method has to be referenced from JSNI to be exported, it won't be pruned by the compiler.

I don't know the internals of GWT-Exporter, but (I suppose) the goal is to free you of all this error-prone boilerplate.

3) (Optional) Same as methods, but for fields too.

Sebastien Diot

unread,
May 3, 2014, 5:07:08 AM5/3/14
to google-we...@googlegroups.com
Thank you. I have in the mean time got some simple example working, but with a somewhat different "design"; I basically wrote my type in JavaScript, and used JavaScriptObject overlay types to make it look like a Java Type, so I"duplicated" the methods in the Java code, using JSNI, rather then "wrapping" it, but idk if this "scales" (from your example, I see a lot of potential to reduce the size of doExportType()):

public class SomeDataType extends JavaScriptObject {
    /** Are we export yet? */
    private static boolean typeExported;

    /** "Registers" (export) this type in JavaScript, so it can be used from there too. */
    private static final native void doExportType() /*-{
        if (!$wnd.com) {
            $wnd.com = {};
        }

        var com = $wnd.com
        if (!com.company) {
            com.company= {};
        }
        if (!com.company.util) {
            com.company.util = {};
        }
        if (!com.company.util.client) {
            com.company.util.client = {};
        }

        function SomeDataType() {
            this.age = 42;
        }
        SomeDataType.prototype.name = function() {
            return "John";
        }
        SomeDataType.prototype.setAge = function(newAge) {
            this.age = newAge;
        }
        SomeDataType.prototype.getAge = function() {
            return this.age;
        }

        com.company.util.client.SomeDataType = SomeDataType;
    }-*/;

    /** "Registers" (export) this type in JavaScript, so it can be used from there too. */
    public static void exportType() {
        if (!typeExported) {
            typeExported = true;
            doExportType();
        }
    }

    public static SomeDataType create() {
        exportType();
        return (SomeDataType) doCreate().cast();
    }

    /** Does the instance initialization. */
    private static final native JavaScriptObject doCreate() /*-{
        return new $wnd.com.company.util.client.SomeDataType();
    }-*/;

    /** Constructor; must always be protected without parameter for overlay types. */
    protected SomeDataType() {
        // NOP
    }

    public final native String name() /*-{
        return this.name();
    }-*/;

    public final native int getAge() /*-{
        return this.getAge();
    }-*/;

    public final native void setAge(int newAge) /*-{
        this.setAge(newAge);
    }-*/;
}.

I am hoping that I could reuse the same code to allow cross-application communication, as would be required, for example, if using WebWorkers.

Note that since my "domain" Java code itself will be generated from Xtend (but not in this example), I might just be able to eventually generate the boiler-plate JSNI code myself, if gwt-exporter takes to long to be updated.
Reply all
Reply to author
Forward
0 new messages