blob: 8de64db01097ae43dc264474f877e593e5fd1f4c [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001# Copyright (C) 2008 The Android Open Source Project
2
3
4- Description -
5---------------
6
Deepanshu Gupta88585f42014-04-08 16:16:43 -07007Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform
8layout.
Adam Lesinski282e1812014-01-23 18:17:42 -08009
10
11- Usage -
12---------
13
Deepanshu Gupta88585f42014-04-08 16:16:43 -070014 ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar
Adam Lesinski282e1812014-01-23 18:17:42 -080015
16
17- Design Overview -
18-------------------
19
Deepanshu Gupta88585f42014-04-08 16:16:43 -070020Layoutlib_create uses a few jars from the framework containing the Java code used by Android as
21generated by the Android build, right before the classes are converted to a DEX format.
Adam Lesinski282e1812014-01-23 18:17:42 -080022
Deepanshu Gupta88585f42014-04-08 16:16:43 -070023These jars can't be used directly in Eclipse as:
24- they contains references to native code (which we want to avoid in Eclipse),
25- some classes need to be overridden, for example all the drawing code that is replaced by Java 2D
26 calls in Eclipse.
27- some of the classes that need to be changed are final and/or we need access to their private
28 internal state.
Adam Lesinski282e1812014-01-23 18:17:42 -080029
30Consequently this tool:
31- parses the input JAR,
32- modifies some of the classes directly using some bytecode manipulation,
33- filters some packages and removes those we don't want in the output JAR,
34- injects some new classes,
Deepanshu Gupta88585f42014-04-08 16:16:43 -070035- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform
36 rendering.
Adam Lesinski282e1812014-01-23 18:17:42 -080037
38The ASM library is used to do the bytecode modification using its visitor pattern API.
39
Deepanshu Gupta88585f42014-04-08 16:16:43 -070040The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
41is done in the main() method and the CreateInfo structure is expected to change with the Android
42platform as new classes are added, changed or removed.
Adam Lesinski282e1812014-01-23 18:17:42 -080043
Deepanshu Gupta88585f42014-04-08 16:16:43 -070044The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
45provides all the necessary missing implementation for rendering graphics in Eclipse.
Adam Lesinski282e1812014-01-23 18:17:42 -080046
47
48
49- Implementation Notes -
50------------------------
51
52The tool works in two phases:
53- first analyze the input jar (AsmAnalyzer class)
54- then generate the output jar (AsmGenerator class),
55
56
57- Analyzer
58----------
59
Deepanshu Gupta88585f42014-04-08 16:16:43 -070060The goal of the analyzer is to create a graph of all the classes from the input JAR with their
61dependencies and then only keep the ones we want.
Adam Lesinski282e1812014-01-23 18:17:42 -080062
Deepanshu Gupta88585f42014-04-08 16:16:43 -070063To do that, the analyzer is created with a list of base classes to keep -- everything that derives
64from these is kept. Currently the one such class is android.view.View: since we want to render
65layouts, anything that is sort of a view needs to be kept.
Adam Lesinski282e1812014-01-23 18:17:42 -080066
Deepanshu Gupta88585f42014-04-08 16:16:43 -070067The analyzer is also given a list of class names to keep in the output. This is done using
68shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**"
69("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
70almost but not quite request the inclusion of full packages.
Adam Lesinski282e1812014-01-23 18:17:42 -080071
Deepanshu Gupta88585f42014-04-08 16:16:43 -070072The analyzer is also given a list of classes to exclude. A fake implementation of these classes is
73injected by the Generator.
Deepanshu Gupta03a057c2013-11-06 15:15:32 +053074
Deepanshu Gupta88585f42014-04-08 16:16:43 -070075With this information, the analyzer parses the input zip to find all the classes. All classes
76deriving from the requested bases classes are kept. All classes whose name match the glob pattern
77are kept. The analysis then finds all the dependencies of the classes that are to be kept using an
78ASM visitor on the class, the field types, the method types and annotations types. Classes that
79belong to the current JRE are excluded.
Adam Lesinski282e1812014-01-23 18:17:42 -080080
Deepanshu Gupta88585f42014-04-08 16:16:43 -070081The output of the analyzer is a set of ASM ClassReader instances which are then fed to the
82generator.
Adam Lesinski282e1812014-01-23 18:17:42 -080083
84
85- Generator
86-----------
87
Deepanshu Gupta88585f42014-04-08 16:16:43 -070088The generator is constructed from a CreateInfo struct that acts as a config file and lists:
89- the classes to inject in the output JAR -- these classes are directly implemented in
90 layoutlib_create and will be used to interface with the renderer in Eclipse.
Adam Lesinski282e1812014-01-23 18:17:42 -080091- specific methods to override (see method stubs details below).
92- specific methods for which to delegate calls.
93- specific methods to remove based on their return type.
94- specific classes to rename.
Deepanshu Gupta03a057c2013-11-06 15:15:32 +053095- specific classes to refactor.
Adam Lesinski282e1812014-01-23 18:17:42 -080096
Deepanshu Gupta88585f42014-04-08 16:16:43 -070097Each of these are specific strategies we use to be able to modify the Android code to fit within the
98Eclipse renderer. These strategies are explained beow.
Adam Lesinski282e1812014-01-23 18:17:42 -080099
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700100The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
101to produce a byte array suitable for the final JAR file.
Adam Lesinski282e1812014-01-23 18:17:42 -0800102
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530103The first step of the transformation is to implement the method delegates.
Adam Lesinski282e1812014-01-23 18:17:42 -0800104
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700105The TransformClassAdapter is then used to process the potentially renamed class. All protected or
106private classes are market as public. All classes are made non-final. Interfaces are left as-is.
Adam Lesinski282e1812014-01-23 18:17:42 -0800107
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700108If a method has a return type that must be erased, the whole method is skipped. Methods are also
109changed from protected/private to public. The code of the methods is then kept as-is, except for
110native methods which are replaced by a stub. Methods that are to be overridden are also replaced by
111a stub.
Adam Lesinski282e1812014-01-23 18:17:42 -0800112
Adam Lesinski282e1812014-01-23 18:17:42 -0800113Finally fields are also visited and changed from protected/private to public.
114
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700115The next step of the transformation is changing the name of the class in case we requested the class
116to be renamed. This uses the RenameClassAdapter to also rename all inner classes and references in
117methods and types. Note that other classes are not transformed and keep referencing the original
118name.
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530119
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700120The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the
121references in all classes. This is used to update the references of classes in the java package that
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700122were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are
123modified to update all references to these non-desktop classes. An alternate implementation of
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700124these (com.android.tools.layoutlib.java.*) is injected.
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530125
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700126RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the
127class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the
128class was originally compiled with Java 7 (version 51). This is because we don't currently generate
129the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have
130valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
131Mac has horrible font rendering support.
132
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700133ReplaceMethodCallsAdapter replaces calls to certain methods. Currently, it only rewrites calls to
Deepanshu Guptae1960cc2014-07-10 13:20:42 -0700134specialized versions of java.lang.System.arraycopy(), which are not part of the Desktop VM to call
135the more general method java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V.
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700136
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700137The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
138Transformation chains in the asm user guide, link in the References.) The order of execution of
139these is:
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530140ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700141RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter
Adam Lesinski282e1812014-01-23 18:17:42 -0800142
143- Method stubs
144--------------
145
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700146As indicated above, all native and overridden methods are replaced by a stub. We don't have the
147code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the
148method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register
Adam Lesinski282e1812014-01-23 18:17:42 -0800149listeners from these overridden method calls based on the method signatures.
150
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700151The listeners are currently pretty basic: we only pass the signature of the method being called, its
152caller object and a flag indicating whether the method was native. We do not currently provide the
153parameters. The listener can however specify the return value of the overridden method.
Adam Lesinski282e1812014-01-23 18:17:42 -0800154
155This strategy is now obsolete and replaced by the method delegates.
156
157
158- Strategies
159------------
160
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700161We currently have 6 strategies to deal with overriding the rendering code and make it run in
162Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse)
163and the generator.
Adam Lesinski282e1812014-01-23 18:17:42 -0800164
165
1661- Class Injection
167
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530168This is the easiest: we currently inject the following classes:
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700169- OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to
170 some specific methods that are stubbed out and change their return value.
171- CreateInfo class, which configured the generator. Not used yet, but could in theory help us track
172 what the generator changed.
173- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are
174 injected. The implementation for these classes has been taken from Android's libcore
175 (platform/libcore/luni/src/main/java/java/...).
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700176- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700177 added to the Dalvik VM for performance reasons. An implementation that is very close to the
178 original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees
179 were in part of the java package, where we can't inject classes, all references to these have been
180 updated (See strategy 4- Refactoring Classes).
Adam Lesinski282e1812014-01-23 18:17:42 -0800181
182
1832- Overriding methods
184
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700185As explained earlier, the creator doesn't have any replacement code for methods to override. Instead
186it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The
187bridge then registers a listener on the method signature and can provide an implementation.
Adam Lesinski282e1812014-01-23 18:17:42 -0800188
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700189This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method
190Delegates).
Adam Lesinski282e1812014-01-23 18:17:42 -0800191
192
1933- Renaming classes
194
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700195This simply changes the name of a class in its definition, as well as all its references in internal
196inner classes and methods. Calls from other classes are not modified -- they keep referencing the
197original class name. This allows the bridge to literally replace an implementation.
Adam Lesinski282e1812014-01-23 18:17:42 -0800198
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700199An example will make this easier: android.graphics.Paint is the main drawing class that we need to
200replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own
201replacement version of Paint which will be used by the rest of the Android stack. The replacement
202version of Paint can still use (either by inheritance or delegation) all the original non-native
203code of _original_Paint if it so desires.
Adam Lesinski282e1812014-01-23 18:17:42 -0800204
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700205Some of the Android classes are basically wrappers over native objects and since we don't have the
206native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't
207work as some native methods are static and we don't control object creation.
Adam Lesinski282e1812014-01-23 18:17:42 -0800208
209This won't rename/replace the inner static methods of a given class.
210
211
Deepanshu Gupta03a057c2013-11-06 15:15:32 +05302124- Refactoring classes
213
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700214This is very similar to the Renaming classes except that it also updates the reference in all
215classes. This is done for classes which are added to the Dalvik VM for performance reasons but are
Deepanshu Gupta1160e6d2014-06-09 18:57:18 -0700216not present in the Desktop VM. An implementation for these classes is also injected.
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530217
218
2195- Method erasure based on return type
Adam Lesinski282e1812014-01-23 18:17:42 -0800220
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700221This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some
222inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and
223all the original implementation is native.
Adam Lesinski282e1812014-01-23 18:17:42 -0800224
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700225In this case we have a strategy that tells the generator that anything returning, for example, the
226inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own
227implementation.
Adam Lesinski282e1812014-01-23 18:17:42 -0800228
229
Deepanshu Gupta03a057c2013-11-06 15:15:32 +05302306- Method Delegates
Adam Lesinski282e1812014-01-23 18:17:42 -0800231
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700232This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1
233or 2 methods are generated:
234a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original
235method as-is from the reader. This step is omitted if the method is native, since it has no Java
236implementation.
237b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method
238named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in
Deepanshu Guptac5a58432014-07-10 16:16:29 -0700239layoutlib_bridge.
Adam Lesinski282e1812014-01-23 18:17:42 -0800240
Deepanshu Gupta88585f42014-04-08 16:16:43 -0700241The delegate method is a static method. If the original method is non-static, the delegate method
242receives the original 'this' as its first argument. If the original method is an inner non-static
243method, it also receives the inner 'this' as the second argument.
Adam Lesinski282e1812014-01-23 18:17:42 -0800244
245
246
247- References -
248--------------
249
250
251The JVM Specification 2nd edition:
252 http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
253
254Understanding bytecode:
255 http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
256
257Bytecode opcode list:
258 http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
259
260ASM user guide:
Deepanshu Gupta03a057c2013-11-06 15:15:32 +0530261 http://download.forge.objectweb.org/asm/asm4-guide.pdf
Adam Lesinski282e1812014-01-23 18:17:42 -0800262
263
264--
265end