Josh, is there a compiler option to prevent the rename of generic object keys?
var myvar:Object = {foo : 1, bar : 2} Renamed by closure to... var myvar:Object = {ad: 1, hb: 2} I'm using Tweener in SpriteFlexJS which receives a generic object of properties for tweening, but I can't use ADVANCED_OPTIMIZATIONS because of the re-name. On Mon, Nov 9, 2020 at 4:16 PM Josh Tynjala <joshtynj...@bowlerhat.dev> wrote: > Hi all, > > Some of you have probably been wondering about my changes to the compiler > over the last year or more. I apologize again for occasionally breaking > things for short periods. It's been quite a challenge getting this stuff > working, but I'm excited to finally be able to report some real > improvements that pretty much anyone should be able to take advantage of > when building a Royale app. > > First some background. A while back, Harbs asked me to look into ways of > reducing the file size of release builds. As you may know, we use Google's > Closure compiler to optimize our generated JavaScript. Closure can be very > aggressive in its optimizations, by renaming symbols (things like variable > and function names) and removing "dead code" that is detected as never > being called. > > Closure's optimizations are good, but they also require developers to be > very careful about how they write their JavaScript code. When you enable > Closure's full optimizations, you are not allowed to use certain JavaScript > features because Closure cannot analyze them properly. For instance, > consider the following code: > > var propName= "myProp"; > var value = obj[propName]; > > When you dynamically access a property with a string, Closure cannot > reliably know that the property exists and will be accessed at runtime. It > may decide to rename or remove that property, which would break things at > runtime. > > ActionScript supports many of the same restricted dynamic features too, so > if you want to support the entire AS3 language, we can't let Closure do its > full optimization. Luckily, Closure also provides a bit of a backdoor: it > allows you to "export" symbols, which means that they won't be renamed and > they won't be removed as dead code. Traditionally, we have made heavy use > of this exporting feature in Royale. > > Harbs wanted to know if we absolutely needed to export everything that we > currently export, and if we could potentially allow developers to turn off > exporting entirely, as long as they follow the stricter rules required by > Closure. > > I won't go into all of the details, but over the last several months, I've > been changing the compiler to give developers more control over release > builds. In particular, control over which symbols get exported, but also > the ability to block Closure from renaming symbols that haven't been > exported. > > Now, for some of the results. I'm going to share the output file size of > the release build for several Royale projects with various different > compiler options. > > For the example projects included with Royale, I built royale-asjs commit > 94f12ed0e564b0b443834400dc2fc06d61b90a8a from October 26, 2020. If you want > to try building these examples yourself, the file sizes of release builds > may be slightly different, if you use a different commit. > > SpectrumBrowser is a project developed by Harbs and his team. I used commit > d25a3def972b15ec029ae838f1a8a677d2d158bd from October 20 for the results > below. Repo: https://github.com/unhurdle/spectrum-royale/ > > To establish a baseline, I built all of these projects with the older > Royale 0.9.7 compiler first. > > ========== > Baseline: royale-compiler 0.9.7 > ========== > > HelloWorld: 68 KB > ASDoc: 231 KB > TourDeJewel: 1074 KB > SpectrumBrowser: 900 KB > > Again, I am building the same AS3/MXML code every time, but these first > numbers are from building with the older compiler. All apps build and run > successfully. > > ----- > > The rest of the results are built with royale-compiler commit > df8bd9f686f1bbf89539e545377b2797c646172c from November 3. > > All results below include the difference in KB and %. These values are > always in comparison to the baseline numbers above. > > ========== > Result 1: 0.9.8 default options > ========== > > HelloWorld: 84 KB (+10 KB / +24%) > ASDoc: 254 KB (+23 KB / +10%) > TourDeJewel: 1105 KB (+31 KB / +3%) > SpectrumBrowser: 936 KB (+36 KB / +4%) > > These examples are slightly larger when built with the newer compiler. > That's expected. It's not ideal, but in the process of testing a multitude > of things to be sure that nothing had broken after my compiler changes, I > discovered some cases where exporting a symbol didn't actually work > correctly in 0.9.7! To properly fix the bug and export these symbols, there > was no choice but to make the file size a bit larger. > > ========== > Result 2: Disable export > ========== > > HelloWorld: 74 KB (+6 KB / +9%) > ASDoc: 227 KB (-4 KB / -2%) > TourDeJewel: 942 KB (-132 KB / -12%) > SpectrumBrowser: 882 KB (-18 KB / -2%) > > In this round, I added the *-export-public-symbols=false* compiler option. > You may recall that I said earlier that I also modified the compiler to > allow a symbol not to be exported, but still prevent it from being renamed. > With that in mind, -export-public-symbols=false basically tells the > compiler that it still can't rename things, but it is allowed to remove > what it perceives as dead code. > > HelloWorld is still slightly larger than 0.9.7, but the three other > examples are now slightly smaller than 0.9.7. > > Most developers should be able to safely add -export-public-symbols=false > to their compiler options when building a Royale app. The only time that > you might still want this exporting is if you have external JavaScript in > your page that isn't part of your Royale app, but it needs to call > functions/classes in your Royale app. > > ========== > Result 3: Allow non-public things to be renamed > ========== > > HelloWorld: 72 KB (+4 KB / +6%) > ASDoc: 221 KB (-10 KB / -4%) > TourDeJewel: 918 KB (-156 KB / -15%) > SpectrumBrowser: 861 KB (-39 KB / -4%) > > In this round, I used the following compiler options: > > -export-public-symbols=false > > > *-prevent-rename-protected-symbols=false-prevent-rename-internal-symbols=false* > > The two new options allow Closure compiler to rename protected and internal > symbols. Once again, HelloWorld is still slightly larger than 0.9.7, but > the other three examples have gotten smaller again. > > While -prevent-rename-public-symbols=false exists too, we cannot use it. > The examples would not work correctly at runtime. This option would > probably work in a pure AS3 app, but our implementation of MXML in Royale > uses dynamic language features that Closure restricts. Unless that is > fixed, we need to avoid renaming certain public symbols. > > Again, most developers should be able to add > -prevent-rename-protected-symbols=false > and -prevent-rename-internal-symbols=false to their Royale app's compiler > options. You might need to prevent renaming of protected/internal symbols > if you access them dynamically. However, in my experience, people are much > more likely to access public symbols dynamically. > > ----- > > ========== > Result 4: Allow public methods to be renamed > ========== > > HelloWorld: 64 KB (-4 KB / -6%) > ASDoc: 206 KB (-25 KB / -11%) > TourDeJewel: 881 KB (-193 KB / -18%) > SpectrumBrowser: 828 KB (-72 KB / -8%) > > In this round, I used the following compiler options: > > -export-public-symbols=false > -prevent-rename-protected-symbols=false > -prevent-rename-internal-symbols=false > > > *-prevent-rename-public-static-methods=false-prevent-rename-public-instance-methods=false > * > > The two new options allow Closure to rename methods that are public. Now, > all four examples are smaller than 0.9.7, and the file size difference is > getting even more dramatic. > > Once again, -prevent-rename-public-static-methods=false and > -prevent-rename-public-instance-methods=false should be safe for most > developers to enable when compiling their Royale app. In my experience, > calling methods dynamically is rare. > > ========== > More new compiler options > ========== > > There are some additional new compiler options available, but using them is > likely to break most Royale apps. > > -prevent-rename-public-static-variables=false > -prevent-rename-public-instance-variables=false > -prevent-rename-public-static-accessors=false > -prevent-rename-public-instance-accessors=false > > These options control whether Closure allows variables or accessors > (getters and setters) to be renamed. There are also similarly-named options > for protected and internal symbols, if you want more control over those > too, instead of using -prevent-rename-protected-symbols=false and > -prevent-rename-internal-symbols=false. > > Unfortunately, renaming public variables/accessors is usually not possible > without breaking the app at runtime. In some apps, you might be able to > allow public static members to be renamed. However, in my experience, > binding to static constants is pretty common, and renaming breaks those > bindings. > > ========== > Next Steps > ========== > > Ideally, I'd like to make it possible for developers to be able to tell > Closure that it's allowed to rename all symbols, including public ones. I > believe that we could see even more file size savings in release builds if > Closure works with full optimizations for all symbols. Obviously, > ActionScript developers would be required to strictly follow Closure's > rules, if they opt into renaming of public symbols, but that's a choice > that they should be allowed to make. > > As I mentioned above, our implementation of MXML and binding uses dynamic > access, which is not compatible with Closure's full optimizations. To > support those optimizations, I will need to explore changes to how we > generate JS for MXML, and how it gets parsed at runtime. > > We previously discussed this subject a bit in this older thread from > January 2020: > > > https://lists.apache.org/thread.html/r843e55252e37967b71b1430a2a904719791d698f3e5e2a79de74e493%40%3Cdev.royale.apache.org%3E > > At the time, I tried out some ideas that we came up with while > brainstorming, but all had various downsides that didn't make for an > obvious winner. In the end, I decided to set further investigation aside > and first focus on exporting/renaming stuff. Now, I'm ready to take a > second look with a fresh perspective, and maybe we'll have some new ideas > to try. > > ----- > > That was really long, so thank you for reading, if you made it to the end! > > TL;DR: By enabling certain, new compiler options, most Royale developers > can make their app release builds smaller. Additionally, I plan to keep > investigating, and I expect to find more ways to reduce file size in the > future. > > -- > Josh Tynjala > Bowler Hat LLC <https://bowlerhat.dev> >