Posts in Category "RSLs"

New compiler option to support monkey patching RSLs

Setting up the monkey patch RSL took a bit of work. First
you needed to create an externs file from a link report. Then you needed to
figure out the inheritance dependencies and remove them from the externs file.
Wouldn’t it be nice to have the compiler to this for you? The compiler knows
what the inheritance dependencies are, right?

It turns out that writing the prototype for the new compiler option wasn’t all that hard. I called the new compiler option “include-inheritance-dependencies-only”.
It only works with compc and the -include-classes option. Normally compc
will include all the dependences of classes linked into a swc. But when the new
option is used only the inheritance dependences will be linked in. So the
command to build the RSL swc for the mx.collections.ListCollectionView monkey
patch becomes:

compc
-include-classes mx.collections.ListCollectionView -include-inheritance-dependencies-only
-source-path . -output lcv2 -directory

The new option is just a prototype. It may never see the
light of day. If you think this option would be useful please leave a comment
to let me know.

 

Monkey Patching ListCollectionView

The last in this monkey patch series is mx.collections.ListCollectionView.
I was going to skip the write up on monkey patching this class but I had already
said I was going to do it and there was one interesting twist along the way. I
chose to demonstrate this class because there was a real world case of it
being monkey patched. I haven’t actually patched the class myself I’m just going
to demonstrate how to make a patch work when using RSLs.

This monkey patch is going to follow the same pattern as the
previous two. First I’m going to create the RSL and then add the RSL into the
configuration.

Create an RSL

As usual I compile the monkey patched files into a library
and create a link report. The link report becomes the basis of my externs file.
The size of the monkey patch RSL without externalizing any classes is 38,260
bytes.

Next I start removing classes from the externs file. First I
remove ListCollectionView itself. ListCollectionView extends flash.utils.Proxy.
Proxy is already externalized in playerglobal.swc so nothing to do. Next remove
the three classes ListCollectionView implements, ICollectionView, IList, and
IMXMLObject. ICollectionView and IList both implement IEventDispatcher.
IEventDispatcher is also in playerglobals.swc so nothing to do. With all these
classes removed from the externs file, recompile the RSL.

Add the RSL to the configuration

Since we are monkey patching the framework RSL, add the
monkey patch RSL to load before the framework RSL.

At this point I thought I was done and ran the application.
But I got a VerifyError on mx.collections.CursorBookmark. I couldn’t see how
ListCollectionView extended or implemented CursorBookmark so I opened the catalog
in framework.swc to double check. Sure enough CursorBookmark was an inheritance
dependency of ListCollectionView. Inheritance dependencies are in the catalog
as type=”i”.

 

        <dep
id=”mx.collections:CursorBookmark” type=”i” />

I didn’t understand why so I searched ListCollectionView for
CursorBookmark. I found a private class that extends CursorBookmark:

class ListCollectionViewBookmark extends
CursorBookmark

and another private class that implements IViewCursor:

class ListCollectionViewCursor extends EventDispatcher
implements IViewCursor

With the mystery solved I removed CursorBookmark and
IViewCursor from the externs file and recompiled. This time the application loaded
without error although the application does not actually use ListCollectionView.
The final size of the monkey patch RSL was 8,047 bytes.

Download Source

Monkey Patching the Spark Button

This time around I’m going to monkey patch the Spark Button.
Like last time I’ll patch the label property setter. The label property setter
for the Spark Button is in spark.components.supportClasses.ButtonBase, not in
spark.components.Button as you might expect. I’m going to create an RSL
containing the ButtonBase class and its inheritance dependencies and then slide
that RSL into the RSL configuration.

Create an RSL

The ButtonBase class is compiled into spark.swc. I want to externalize
as many classes as possible in my custom RSL so I’ll externalize all the
libraries that spark.swc externalizes. That means I can externalize osmf.swc,
textLayout.swc, and framework.swc. My application is not using any classes from
osmf.swc so I won’t externalize that library and I won’t load that RSL either.

How do you know what libraries spark.swc is dependent on?
One way is to look at the build file for spark.swc and see what libraries are
on the external-library-path. Another way is to use the swcdepends tool. This
tool is helpful in looking at dependencies between swcs and finding out what
classes are causing those dependencies. I usually run it from the command line
like this:

>swcdepends
-locale=

The locale is set empty so that resource bundles are removed
from the output. The swcdepends tool behaves like mxmlc in that it
uses flex-config.xml and accepts the same configuration options. The output is
a list of all the swcs found in dependency order. The swc with the least
dependencies are at the top and the most dependencies are at the bottom. For
each swc in the list, there is an indented list to show the actually swcs it is
dependent on. Here an excerpt from the output showing the entry for spark.swc:

C:\opensource\sdk\branches\4.0.0\frameworks\libs\spark.swc:

      C:\opensource\sdk\branches\4.0.0\frameworks\libs\player\10.0\playerglobal.swc

      C:\opensource\sdk\branches\4.0.0\frameworks\libs\osmf.swc

      C:\opensource\sdk\branches\4.0.0\frameworks\libs\textLayout.swc

      C:\opensource\sdk\branches\4.0.0\frameworks\libs\framework.swc

This shows that spark.swc is dependent on playerglobal.swc,
osmf.swc, textLayout.swc, and framework.swc. If you want to show what classes
are causing spark.swc to be dependent on the other libraries add the
-show-external-classes to the command line.

Creating the custom library for ButtonBase was pretty easy.
First I compiled with a list report and no externs file. The size of the RSL
was 10,801 bytes. This was already much smaller than the mx Button RSL and I
hadn’t even created an externs file yet. I copied the list report to the
externs file and added a “load-externs=externs.xml” to the compc ant task. Next
I edited the externs.xml file with the intent of removing ButtonBase and its
inheritance dependencies. I removed ButtonBase and the SkinnableComponent class
that ButtonBase extends. SkinnableComponent extends UIComponent but UIComponent
is in framework.swc and was already externalized. ButtonBase implements
IFocusManagerComponent which is also in framework.swc and does not need to be
excluded. So were done after removing just two classes.

Compile again and check the link report. The link report
shows just ButtonBase, SkinnableComponent and the root class so we are done.
The custom RSL’s final size is 6,198 bytes.

Add your RSL to the configuration

The custom RSL entry needs to be loaded after the framework
RSL and before the spark RSL. The framework RSL will provide UIComponent and
other classes we excluded. The custom RSL must be loaded before the spark RSL
so the monkey patched class will override the spark class.

Running the application shows that the monkey patched
version of ButtonBase is being used.

Download Source

 

How to Monkey Patch when using Flex RSLs

Monkey patchers run into problems when using Flex RSLs. In this post I’ll explain why monkey patching does not work when using RSLs and provide details of how to make it work. I’ll monkey patch the mx.controls.Button class in this post and ListCollectionView and Spark Button in later posts.

Why doesn’t monkey patching work when using RSLs? To answer this question it is important to understand two things:

  1. The Flash Player has a first-in-wins rule when loading classes.
  2. Flex applications contain two frames. The first frame contains few classes and loads the RSLs. The second frame contains the rest of the application’s classes.

Say you’ve monkey patched mx.controls.Button, which will end up in frame 2 of your application. In frame 1 of the application, the framework RSL will be loaded which contains the mx.controls.Button class. When frame 2 of the application loads the monkey patched version of mx.controls.Button is ignored because of the Flash Player first-in-wins rule.

When you monkey patch a class that is in frame 1, then you are done, you don’t need an RSL.

The basic steps I’ll use to monkey patch are:

  1. Create an RSL with just the monkey-patched file and its inheritance dependencies.
  2. Add your RSL to the configuration to get it loaded.

To demonstrate the process I created a project with a simple application that contains one button.

<mx:Button label=”My Button”/>

The project was developed using a pre-release version of Flex4, build 4.0.0.13672.

 

Create an RSL

My modification of Button is just a change to set label() so I can see if a Button in my application is a real or patched Button. When the Button’s label setter is called I just prepend “Monkey patched ” to the original value.

 

_label = “Monkey patched ” + value;

I started by adding Button.as to my source tree using the same package as Flex, “mx.controls”. Next compile your source into an RSL. From the command line it will look something like this:

>compc -directory -output=button -link-report=link.rpt -include-classes=mx.controls.Button -source-path=.

>optimizer button/library.swf –output -button.swf

–keep-as3-metadata=Bindable,Managed,ChangeEvent,NonCommittingChangeEvent,Transient,SkinPart

>digest button.swf -swc-path=button

After running these commands the size of button.swf is 143,596 bytes. Looking at the link report, there are a lot of classes besides mx.controls.Button that we don’t need. Ideally, all we want in the RSL is mx.controls.Button. But the Flash Player requires classes that Button extends or implements to be defined when the Button class is loaded. Since we are loading this RSL before the framework RSL, we need to also include the classes Button extends and implements in framework.swc. For each additional class we include, we also need to include the classes it extends and implements.

Take the link report from the first compile of Button and copy it to a file we will use as our externs file. The externs file is what we will use to remove classes we don’t want in the RSL. I actually seeded my externs.xml file by copying and pasting the script children from framework.swc’s catalog. My thinking was that I wanted to exclude everything in framework.swc except Button and the classes it extends or implements. But now I can see that the externs file is much larger than it would have been if I had just started with the initial link report.

Next edit the externs file by removing the script elements for mx.controls.Button and all of its inheritance dependencies. After recompiling I was able to get the RSL size down to 35,152 bytes. That is still pretty big but it’s the best we can do because we need UIComponent and UICompoment is big. When monkey patching spark components we will be able to remove UIComponent and get a much smaller RSL.

Add your RSL to the configuration

Now that you have your RSL, you just need to add it to the RSL configuration. I created a local configuration file, main-config.xml, with the RSLs I wanted. You can start by copying the <runtime-shared-library-path> elements from flex-config.xml to your local configuration file. Then add the entry for your RSL before the framework RSL.

<!– monkey patch mx.controls.Button from Framework SWC –>

<runtime-shared-library-path>

<path-element>button</path-element>

<rsl-url>button.swf</rsl-url>

<policy-file-url></policy-file-url>

</runtime-shared-library-path>

The order of the runtime-shared-library-path options is important because it determines the load order of the RSLs. The RSL URLs in my configuration file are all local SWF files so before running the application I will copy the RSLs from my SDK frameworks/RSLs directory to where my application lives.

When the new RSL is loaded for the first time you will mostly encounter verify errors because you missed removing an interface from the externs file. As you encounter a verify error, remove that class from the externs file and try again. Eventually the RSL will contain all the classes it needs and load without errors.

Finally, run the application to verify the monkey patched Button is being used.

The postings on this site are my own and don’t necessarily represent Adobe’s positions, views, strategies or opinions.