Redex Synth Pass Example
The code and artifacts for this example are on GitHub.
The synth optimizations attempt to remove synthetic methods and wrappers. This improves performance by making access to field values faster and it also reduces code size because the definition and invocation of synthetic methods is eliminated.
Removing synthetic methods for accessing static fieldsβ
This directory contains an Android Studio 1.5 project that illustrates how a wrapper synthetic method is removed by Redex. The contrived example is a simple Android application which makes use of this class:
package com.facebook.redex.examples.synth;
public class Alpha {
private static int alpha;
public Alpha(int initialValue) {
alpha = initialValue;
}
public class Beta {
public int doubleAlpha() {
return 2 * alpha;
}
}
}
The key thing to note here is that there is an inner class Beta
which
has a method doubleAlpha
which accesses a private static field alpha
of
its outer class Alpha
. A dump of the Dex bytecode for the Alpha
class
confirms that the alpha
field is private
and static
.
Static fields -
#0 : (in Lcom/facebook/redex/examples/synth/Alpha;)
name : 'alpha'
type : 'I'
access : 0x000a (PRIVATE STATIC)
Java compilers will implement accesses] to this field from inner classes
by generating a synthetic getter method to allow the inner class to access
the private field of the outer
class. We can see the automatically generated synthetic method access$000
if we examine the Dex bytecode for the Alpha
class:
Class #597 -
Class descriptor : 'Lcom/facebook/redex/examples/synth/Alpha;'
...
#1 : (in Lcom/facebook/redex/examples/synth/Alpha;)
name : 'access$000'
type : '()I'
access : 0x1008 (STATIC SYNTHETIC)
code -
registers : 1
ins : 0
outs : 0
insns size : 3 16-bit code units
065f14: |[065f14] com.facebook.redex.examples.synth.Alpha.access$000:()I
065f24: 6000 070b |0000: sget v0, Lcom/facebook/redex/examples/synth/Alpha;.alpha:I // field@0b07
065f28: 0f00 |0002: return v0
This generated synthetic method can be accessed by the inner class
which keeps the JVM happy. All it does is to read the state field
value using an sget
instruction and return the read value.
This synthetic wrapper is used in the implementation of doubleAlpha
:
Virtual methods -
#0 : (in Lcom/facebook/redex/examples/synth/Alpha$Beta;)
name : 'doubleAlpha'
...
065ed8: |[065ed8] com.facebook.redex.examples.synth.Alpha.Beta.doubleAlpha:()I
065ee8: 7100 1118 0000 |0000: invoke-static {}, Lcom/facebook/redex/examples/synth/Alpha;.access$000:()I // method@1811
065eee: 0a00 |0003: move-result v0
065ef0: da00 0002 |0004: mul-int/lit8 v0, v0, #int 2 // #02
065ef4: 0f00 |0006: return v0
For the doubleAlpha
method in the inner class Beta
to access the
private static field of the enclosing class an invoke-static
call is made
to the synthetic wrapper method access$000
.
Here is an example of the doubleAlpha
method being used from a simple
Android application:
package com.facebook.redex.examples.synth;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.message);
textView.setText("Redex Synth Example\n");
Alpha a = new Alpha(12);
Alpha.Beta b = a.new Beta();
textView.append("Double Alpha(12) = " + b.doubleAlpha() + "\n");
}
}
If we examined the Dex bytecode for the call to doubleAlpha
we would
see something like this which shows a virtual call to the doubleAlpha
method:
Direct methods -
#0 : (in Lcom/facebook/redex/examples/synth/MainActivity;)
name : 'onCreate'
...
065fb6: 6e10 0f18 0100 |0031: invoke-virtual {v1}, Lcom/facebook/redex/examples/synth/Alpha$Beta;.doubleAlpha:()I // method@180f
...
After running Redex the synthetic wrapper method will be removed and
the code for accessing the alpha
static field will be inlined into
the code for the main activity:
Direct methods -
#0 : (in Lcom/facebook/redex/examples/synth/MainActivity;)
name : 'onCreate'
...
07f246: 6005 4d06 |0031: sget v5, Lcom/facebook/redex/examples/synth/Alpha;.alpha:I // field@064d
07f24a: da05 0502 |0033: mul-int/lit8 v5, v5, #int 2 // #02
...
To make the access of the alpha
field legal for the Dalvik VM we have
mutated the access permissions for this field:
Static fields -
#0 : (in Lcom/facebook/redex/examples/synth/Alpha;)
name : 'alpha'
type : 'I'
access : 0x0009 (PUBLIC STATIC)
Similar optimizations exist for other synthetic wrapper scenarios e.g. for instance fields.
Example Codeβ
The project in the synth-example
directory can be opened with Android Studio 1.5 and
contains the sample here that illustrates the removal of synthetic
wrappers for static private fields.
The Makefile
in this directory can be used once you have build a signed:
APK (Build : Generate Signed APK...
) to produce the following items:
synth-example-release-redex.apk
: A Redex optimized version of the original APK.classes.dump
: A dump of the Dex bytecode for the input APKsynth-example-release.apk
.classes-redex.dump
: A dump of the Redex optimizd APKsynth-example-release-redex.apk
.
The environment variable ANDROID_TOOLS
should be set to the location
of your Android SDK tools directory.
To produce these items:
$ make clean all