You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@flex.apache.org by Harbs <ha...@gmail.com> on 2016/07/19 11:19:44 UTC

FlexJS Closure Compiler optimizations

I finally got my app to compile a release version by eliminating my circular dependencies. So far so good.

FYI, I fixed this by doing two things: 1. I added a className property and instead of doing if(this is Sub), I’m using if(this.className == “Sub”). 2. I enabled -js-output-optimization=skipAsCoercions (two of my circulars were due to an “as” statement which caused a circular dependency chain through a number of different classes).

I have some observations / problems with the optimizations for the release build:

1. It’s optimized to the point where it does not work. If I run MyApp.start() in index.html, I get an error that MyApp is not defined. Looking in the JS, I see that Em=‘MyApp’. Is there something special I need to do to prevent this from happening?

2. Prototype definitions are not being optimized as much as they could. The pattern is MyClass.prototype.foo = “some value”; for each one. If that’s replaced with MyClass.prototype = {foo:”somevalue”,…} It’ll save “MyClass.prototype” getting written for every property.

3. Static constants are not being optimized as much as they should. I’m not sure what’s responsible for this, but instead of literals or compact variables, it’s using the fully qualified static name every time.

4. Full class paths are not needed. The class paths could easily be shortened to a few unique characters for significant optimization. I just tried a search and replace on a 900KB file, and just replacing the package paths reduced the file size by 20KB.

There’s other optimizations which could probably be taken advantage of, but that’s my list for now.

Harbs

Re: FlexJS Closure Compiler optimizations

Posted by Harbs <ha...@gmail.com>.
I’m not complaining about the output. I think the work you guys have done with the compiler is superb. :-)

However, I do think we should strive to get the output as minified as possible and there’s probably some strategies which we can employ to get the output more compact.

I’m not totally clear on your reflection and module issues and use cases. Can you give some examples where there would be problems?

In my #2 below, this is an optimization which should not effect either one of your cases. It’s simply an alternate pattern for declaring the prototype which should be more compact. Is there a reason not to use it?

#3 should not be a problem either. As long as the qualified name is declared in one place, the shortened version could be used elsewhere by clients.

As far as #4 and exports go, I have a few thoughts:

1. We could have meta-tags in the source code which could tell the FalconJX compiler how to rename packages and classes. Possibly something like this:
[Export(name="a", package="$fx.c")]
public class Application extends ApplicationBase implements IStrand, IParent, IEventDispatcher
This would export all references to “org.apache.flex.core.Application" as “$fx.c.a” instead.

2. An optional tag to specify which pieces should be exported (exposed? I’m not clear on the difference between the two)
Maybe something like this:

To expose the whole class:
[Export(name="a", package="$fx.c”,type=“external")]
public class Application extends ApplicationBase implements IStrand, IParent, IEventDispatcher

To not expose the whole class:
[Export(name="a", package="$fx.c”,type=“internal")]
public class Application extends ApplicationBase implements IStrand, IParent, IEventDispatcher

To expose only pieces:
[Export(name="a", package="$fx.c”,type=“mixed")]
public class Application extends ApplicationBase implements IStrand, IParent, IEventDispatcher

[Export]
public function doSomething()

[Hide]
public function doSomethingElse()

[Export(name=“voodoo")]
public var someImportantData

Maybe there’s better ways to do this, but this might be a way to control how output gets optimized (and give control over obfuscation as well).

Thoughts?
 
On Jul 19, 2016, at 4:50 PM, Alex Harui <ah...@adobe.com> wrote:

>> 
>> 2. Prototype definitions are not being optimized as much as they could.
>> The pattern is MyClass.prototype.foo = “some value”; for each one. If
>> that’s replaced with MyClass.prototype = {foo:”somevalue”,…} It’ll save
>> “MyClass.prototype” getting written for every property.
>> 
>> 3. Static constants are not being optimized as much as they should. I’m
>> not sure what’s responsible for this, but instead of literals or compact
>> variables, it’s using the fully qualified static name every time.
>> 
>> 4. Full class paths are not needed. The class paths could easily be
>> shortened to a few unique characters for significant optimization. I just
>> tried a search and replace on a 900KB file, and just replacing the
>> package paths reduced the file size by 20KB.
> 
> We have currently chosen to export every public property in our
> cross-compiled output.  That is done to support Reflection, and also to
> support independently built modules someday.  The optimizer only knows how
> to optimize the total set of sources given to it.  If a module developed
> elsewhere gets loaded at runtime and makes a call, it won't know the
> optimized variable used and needs to resolve the "old-fashioned" way.  We
> could add new compiler options to get better optimization some day.
> 
> HTH,
> -Alex


Re: FlexJS Closure Compiler optimizations

Posted by Harbs <ha...@gmail.com>.
Yes. It helped a lot!

I feel kind of dumb…

I was ignoring a runtime error that I thought did not matter. It seems I had a static constant value which relied on a class which was not yet loaded. I made it an instance const instead, and the error went away.

The app now loads when minified. What a relief! :-)

It’s late for me. I’ll respond to the other parts of the discussions tomorrow.

Thanks!
Harbs

On Jul 20, 2016, at 12:00 AM, Alex Harui <ah...@adobe.com> wrote:

> 
> On 7/19/16, 1:31 PM, "Harbs" <ha...@gmail.com> wrote:
> 
>> I was not getting any errors, but I was getting a lot of warnings.
>> 
>> I eliminated virtually all the warnings in my code with the exception of
>> two "WARNING - unreachable code” errors. Eliminating the first one causes
>> a Falcon compiler error, and the second appears to be a bug in the
>> Closure compiler.
>> 
>> The remaining warnings I’m getting are the following ones. I have no idea
>> why I’m getting them or how to eliminate them. I also don’t know whether
>> this is causing my problems of the main app class not being defined.
>> 
>> Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager
>> println
>> WARNING: /Users/harbs/Documents/Apache Flex
>> Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.
>> js:619: WARNING - actual parameter 1 of goog.array.contains does not
>> match formal parameter
>> found   : (Array<T>|null)
>> required: (IArrayLike<?>|null|string)
>> if (!goog.array.contains(arr, obj)) {
>>                          ^
> 
> These look like compile time warnings.  If you still got a main app .js
> file with minified code in it, then you can ignore these warnings.  I
> noticed you are using a different version of Google Closure Library than I
> am.  Sometimes we have to upgrade GCC to handle warnings like this.  I'll
> add it to my list of things to try to remember to do.
> 
> If you have minified .js, then you want to run it in the browser and see
> what kinds of exceptions are being thrown at runtime and reported in the
> console.  Those are the errors that tend to block full initialization of
> all of the symbols in the app.
> 
> HTH,
> -Alex


Re: FlexJS Closure Compiler optimizations

Posted by Alex Harui <ah...@adobe.com>.
On 7/19/16, 1:31 PM, "Harbs" <ha...@gmail.com> wrote:

>I was not getting any errors, but I was getting a lot of warnings.
>
>I eliminated virtually all the warnings in my code with the exception of
>two "WARNING - unreachable code” errors. Eliminating the first one causes
>a Falcon compiler error, and the second appears to be a bug in the
>Closure compiler.
>
>The remaining warnings I’m getting are the following ones. I have no idea
>why I’m getting them or how to eliminate them. I also don’t know whether
>this is causing my problems of the main app class not being defined.
>
>Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager
>println
>WARNING: /Users/harbs/Documents/Apache Flex
>Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.
>js:619: WARNING - actual parameter 1 of goog.array.contains does not
>match formal parameter
>found   : (Array<T>|null)
>required: (IArrayLike<?>|null|string)
>  if (!goog.array.contains(arr, obj)) {
>                           ^

These look like compile time warnings.  If you still got a main app .js
file with minified code in it, then you can ignore these warnings.  I
noticed you are using a different version of Google Closure Library than I
am.  Sometimes we have to upgrade GCC to handle warnings like this.  I'll
add it to my list of things to try to remember to do.

If you have minified .js, then you want to run it in the browser and see
what kinds of exceptions are being thrown at runtime and reported in the
console.  Those are the errors that tend to block full initialization of
all of the symbols in the app.

HTH,
-Alex


Re: FlexJS Closure Compiler optimizations

Posted by Harbs <ha...@gmail.com>.
I was not getting any errors, but I was getting a lot of warnings.

I eliminated virtually all the warnings in my code with the exception of two "WARNING - unreachable code” errors. Eliminating the first one causes a Falcon compiler error, and the second appears to be a bug in the Closure compiler.

The remaining warnings I’m getting are the following ones. I have no idea why I’m getting them or how to eliminate them. I also don’t know whether this is causing my problems of the main app class not being defined.

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.js:619: WARNING - actual parameter 1 of goog.array.contains does not match formal parameter
found   : (Array<T>|null)
required: (IArrayLike<?>|null|string)
  if (!goog.array.contains(arr, obj)) {
                           ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.js:659: WARNING - actual parameter 1 of goog.array.indexOf does not match formal parameter
found   : (Array<T>|null)
required: (IArrayLike<?>|null|string)
  if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) {
                                                       ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.js:662: WARNING - actual parameter 1 of goog.array.insertAt does not match formal parameter
found   : (Array<T>|null)
required: (IArrayLike<?>|null)
    goog.array.insertAt(arr, obj, i);
                        ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/array/array.js:1643: WARNING - actual parameter 1 of goog.array.forEach does not match formal parameter
found   : Array<number>
required: (IArrayLike<?>|null|string)
  goog.array.forEach(index_arr, function(index) { result.push(arr[index]); });
                     ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/events/listenermap.js:149: WARNING - actual parameter 1 of goog.array.removeAt does not match formal parameter
found   : Array<goog.events.Listener>
required: (IArrayLike<?>|null)
    goog.array.removeAt(listenerArray, index);
                        ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/events/listenermap.js:171: WARNING - actual parameter 1 of goog.array.remove does not match formal parameter
found   : Array<goog.events.Listener>
required: (IArrayLike<goog.events.ListenableKey>|null)
  var removed = goog.array.remove(this.listeners[type], listener);
                                  ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/labs/useragent/browser.js:233: WARNING - actual parameter 1 of goog.array.forEach does not match formal parameter
found   : Array<Array<string>>
required: (IArrayLike<?>|null|string)
  goog.array.forEach(versionTuples, function(tuple) {
                     ^

Jul 19, 2016 11:22:39 PM com.google.javascript.jscomp.LoggerErrorManager println
WARNING: /Users/harbs/Documents/Apache Flex Dist/FlexJSNightly/js/lib/google/closure-library/closure/goog/labs/useragent/engine.js:153: WARNING - actual parameter 1 of goog.array.find does not match formal parameter
found   : Array<Array<string>>
required: (IArrayLike<?>|null|string)
  var pair = goog.array.find(tuples, function(pair) { return key == pair[0]; });
 
On Jul 19, 2016, at 4:50 PM, Alex Harui <ah...@adobe.com> wrote:

>> 1. It’s optimized to the point where it does not work. If I run
>> MyApp.start() in index.html, I get an error that MyApp is not defined.
>> Looking in the JS, I see that Em=‘MyApp’. Is there something special I
>> need to do to prevent this from happening?
> 
> This usually means some exception was thrown at init time and MyApp never
> got set as a exported symbol.  You have to eliminate all runtime
> exceptions during class definition and prototype setup before MyApp is
> defined.


Re: FlexJS Closure Compiler optimizations

Posted by Alex Harui <ah...@adobe.com>.

On 7/19/16, 4:19 AM, "Harbs" <ha...@gmail.com> wrote:

>I finally got my app to compile a release version by eliminating my
>circular dependencies. So far so good.
>
>FYI, I fixed this by doing two things: 1. I added a className property
>and instead of doing if(this is Sub), I’m using if(this.className ==
>“Sub”). 2. I enabled -js-output-optimization=skipAsCoercions (two of my
>circulars were due to an “as” statement which caused a circular
>dependency chain through a number of different classes).
>
>I have some observations / problems with the optimizations for the
>release build:
>
>1. It’s optimized to the point where it does not work. If I run
>MyApp.start() in index.html, I get an error that MyApp is not defined.
>Looking in the JS, I see that Em=‘MyApp’. Is there something special I
>need to do to prevent this from happening?

This usually means some exception was thrown at init time and MyApp never
got set as a exported symbol.  You have to eliminate all runtime
exceptions during class definition and prototype setup before MyApp is
defined.

>
>2. Prototype definitions are not being optimized as much as they could.
>The pattern is MyClass.prototype.foo = “some value”; for each one. If
>that’s replaced with MyClass.prototype = {foo:”somevalue”,…} It’ll save
>“MyClass.prototype” getting written for every property.
>
>3. Static constants are not being optimized as much as they should. I’m
>not sure what’s responsible for this, but instead of literals or compact
>variables, it’s using the fully qualified static name every time.
>
>4. Full class paths are not needed. The class paths could easily be
>shortened to a few unique characters for significant optimization. I just
>tried a search and replace on a 900KB file, and just replacing the
>package paths reduced the file size by 20KB.

We have currently chosen to export every public property in our
cross-compiled output.  That is done to support Reflection, and also to
support independently built modules someday.  The optimizer only knows how
to optimize the total set of sources given to it.  If a module developed
elsewhere gets loaded at runtime and makes a call, it won't know the
optimized variable used and needs to resolve the "old-fashioned" way.  We
could add new compiler options to get better optimization some day.

HTH,
-Alex