Friday, December 20, 2013

Re: Code splitting issue

I created an Eclipse project that exhibits similar behavior and uploaded it to GitHub:

https://github.com/adamh-basis/code-splitting-bug.git

If you ensure the compiler settings include -draftCompile and check the resulting deferredjs/<id>/1.js, you won't see any references to a classes Dispatcher2$Class0..3 .  If you remove -draftCompile, the size of the 2.js and 3.js files decrease significantly, the size of the 1.js files increase, and Dispatcher2$Class0...3 begin to appear inside 1.js.

Again, if there's any hints about how to work around this bug, I'd love to hear them.


On Friday, December 20, 2013 3:13:20 PM UTC-5, ahawtho wrote:
This is a follow up to Jim Douglas's question about the change in 2.6.0 to GWT.runAsync() here:


We started working towards avoiding multiple calls to runAsync() for a particular code splitting point.  Our application provides a framework for dynamically creating displays using GWT.  We don't know what controls our customers will use on a given display until they actually create the controls.  To decrease the size of the initial download, we have partitioned some of the lesser-used controls into groups that are downloaded on demand. What we've done below is how we worked around the difficulty of using the Command pattern with code splitting.

Now at first, I was able to successfully change our code to avoid multiple calls to runAsync().  I was iterating quickly, so I used -draftCompile.  When I removed the -draftCompile (no other changes), the GWT compiler began to include everything in our first split point.  Here's an example of what we're doing.  The following classes are generated using annotations and a GWT Generator:

MessageReconstructor.java:

public class MessageReconstructor {
    private Dispatcher m_group1;
    private Dispatcher m_group2;
    ... // there are 6 total.

    public void dispatch(final MessageData p_msg, ...) {
        int msgType = p_msg.getMsgType();
        if (msgType <= GROUP1_LAST) {
            if (m_group1 == null) {
                GWT.runAsync(Dispatcher1.class,
                             new RunAsyncCallback() {
                    @Override public void onSuccess() {
                        m_group1 = new Dispatcher1();
                        m_group1.dispatch(p_msg, ...);
                    }
                    @Override public void onFailure() {
                        // fail...
                    }
                });
            } else {
                m_group1.dispatch(p_msg, ...);
            }
        } else if (msgType <= GROUP2_LAST) {
            if (m_group2 == null) {
                GWT.runAsync(Dispatcher2.class,
                             new RunAsyncCallback() {
                    @Override public void onSuccess() {
                        m_group2 = new Dispatcher2();
                        m_group2.dispatch(p_msg, ...);
                    }
                    @Override public void onFailure() {
                        // fail...
                    }
                });
            } else {
                m_group2.dispatch(p_msg, ...);
            }
        }
    }

In each of Dispatcher1 and Dispatcher2, we have something like this:

public class Dispatcher1 implements Dispatcher {
    @Override public void dispatch(MessageData p_msg, ...) {
        int msgType = p_msg.getMsgType();
        switch (msgType) {
            case MSG1: {
                String_String_Int data = (String_String_Int)p_msg;
                Group1SpecificMessage1 msg = new Group1SpecificMessage1(data.string1, data.string2, data.int1);
                msg.exec(...);
                break;
            }
            case MSG2: {
                String_Int data = (String_Int)p_msg;
                Group1SpecificMessage2 msg = new Group1SpecificMessage2(data.string1, data.int1);
                msg.exec(...);
                break;
            }
            ... /// lots of messages.
        }
    }
}

One interesting thing I've found is that if I change the type of the Dispatcher objects in the MessageReconstructor from 'Dispatcher' to 'Dispatcher1', 'Dispatcher2', etc., then the compiler also fails to put them in the appropriate code fragments even when I specify -draftCompile.

This looks to me like a bug, but I'm not sure where to start looking.  Does anyone know for certain whether we should expect this to work or not?  If not, is there a way we can make it work to avoid running a runAsync() on every single MessageData, but still obtain the benefits of code splitting when compiling for production?



I'll try to anticipate some questions with some preemptive answers:

1.  If I eliminate the block:
            } else {
                m_group1.dispatch(p_msg, ...);
            }

the compiler places the code in the appropriate code fragment regardless of whether '-draftCompile' is specified.

2.  The Group1SpecificMessage* and Group2SpecificMessage* all refer either to code shared in one of our initial code fragments or to disjoint sets of classes.

Any help is appreciated.  Thanks!

Adam

--
You received this message because you are subscribed to the Google Groups "Google Web Toolkit" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.

No comments:

Post a Comment