blob: 5625675c6a2ef576acc066ad415fea62568594c7 [file] [log] [blame]
Adam Lesinskiab775ec2014-01-23 18:17:42 -08001# Copyright (C) 2008 The Android Open Source Project
2
3
4- Description -
5---------------
6
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +01007Layoutlib_create generates a JAR library used by the Android Studio graphical layout editors to perform
8layout and rendering.
Adam Lesinskiab775ec2014-01-23 18:17:42 -08009
10
11- Usage -
12---------
13
Deepanshu Guptaf9644532014-04-08 16:16:43 -070014 ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar
Adam Lesinskiab775ec2014-01-23 18:17:42 -080015
16
17- Design Overview -
18-------------------
19
Deepanshu Guptaf9644532014-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 Lesinskiab775ec2014-01-23 18:17:42 -080022
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010023These jars can't be used directly in Android Studio as:
24- they contains references to native code (which we cannot support in Android Studio at the moment, but working on it),
Deepanshu Guptaf9644532014-04-08 16:16:43 -070025- some classes need to be overridden, for example all the drawing code that is replaced by Java 2D
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010026 calls.
Deepanshu Guptaf9644532014-04-08 16:16:43 -070027- some of the classes that need to be changed are final and/or we need access to their private
28 internal state.
Adam Lesinskiab775ec2014-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,
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010035- generates a modified JAR file that is suitable for the Android Studio to perform
Deepanshu Guptaf9644532014-04-08 16:16:43 -070036 rendering.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080037
38The ASM library is used to do the bytecode modification using its visitor pattern API.
39
Deepanshu Guptaf9644532014-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
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010042platform as new classes are added, changed or removed. See src/com/android/tools/layoutlib/create/CreateInfo.java
43for more details. Some configuration that may be platform dependent is also present elsewhere in code.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080044
Deepanshu Guptaf9644532014-04-08 16:16:43 -070045The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010046provides all the necessary missing implementation for rendering graphics in Android Studio.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080047
48
49
50- Implementation Notes -
51------------------------
52
53The tool works in two phases:
54- first analyze the input jar (AsmAnalyzer class)
55- then generate the output jar (AsmGenerator class),
56
57
58- Analyzer
59----------
60
Deepanshu Guptaf9644532014-04-08 16:16:43 -070061The goal of the analyzer is to create a graph of all the classes from the input JAR with their
62dependencies and then only keep the ones we want.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080063
Deepanshu Guptaf9644532014-04-08 16:16:43 -070064To do that, the analyzer is created with a list of base classes to keep -- everything that derives
65from these is kept. Currently the one such class is android.view.View: since we want to render
66layouts, anything that is sort of a view needs to be kept.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080067
Deepanshu Guptaf9644532014-04-08 16:16:43 -070068The analyzer is also given a list of class names to keep in the output. This is done using
69shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**"
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010070("*" does not match dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
Deepanshu Guptaf9644532014-04-08 16:16:43 -070071almost but not quite request the inclusion of full packages.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080072
Deepanshu Guptaf9644532014-04-08 16:16:43 -070073The analyzer is also given a list of classes to exclude. A fake implementation of these classes is
74injected by the Generator.
Deepanshu Gupta60b7c022013-11-06 15:15:32 +053075
Deepanshu Guptaf9644532014-04-08 16:16:43 -070076With this information, the analyzer parses the input zip to find all the classes. All classes
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010077deriving from the requested base classes are kept. All classes whose name match the glob pattern
Deepanshu Guptaf9644532014-04-08 16:16:43 -070078are kept. The analysis then finds all the dependencies of the classes that are to be kept using an
79ASM visitor on the class, the field types, the method types and annotations types. Classes that
80belong to the current JRE are excluded.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080081
Deepanshu Guptaf9644532014-04-08 16:16:43 -070082The output of the analyzer is a set of ASM ClassReader instances which are then fed to the
83generator.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080084
85
86- Generator
87-----------
88
Deepanshu Guptaf9644532014-04-08 16:16:43 -070089The generator is constructed from a CreateInfo struct that acts as a config file and lists:
90- the classes to inject in the output JAR -- these classes are directly implemented in
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010091 layoutlib_create and will be used to interface with the Java 2D renderer.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080092- specific methods to override (see method stubs details below).
93- specific methods for which to delegate calls.
94- specific methods to remove based on their return type.
95- specific classes to rename.
Deepanshu Gupta60b7c022013-11-06 15:15:32 +053096- specific classes to refactor.
Adam Lesinskiab775ec2014-01-23 18:17:42 -080097
Deepanshu Guptaf9644532014-04-08 16:16:43 -070098Each of these are specific strategies we use to be able to modify the Android code to fit within the
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +010099Java 2D renderer. These strategies are explained below.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800100
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700101The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
102to produce a byte array suitable for the final JAR file.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800103
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530104The first step of the transformation is to implement the method delegates.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800105
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700106The TransformClassAdapter is then used to process the potentially renamed class. All protected or
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +0100107private classes are marked as public. All classes are made non-final. Interfaces are left as-is.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800108
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700109If a method has a return type that must be erased, the whole method is skipped. Methods are also
110changed from protected/private to public. The code of the methods is then kept as-is, except for
111native methods which are replaced by a stub. Methods that are to be overridden are also replaced by
112a stub.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800113
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800114Finally fields are also visited and changed from protected/private to public.
115
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700116The next step of the transformation is changing the name of the class in case we requested the class
117to be renamed. This uses the RenameClassAdapter to also rename all inner classes and references in
118methods and types. Note that other classes are not transformed and keep referencing the original
119name.
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530120
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700121The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the
122references in all classes. This is used to update the references of classes in the java package that
Deepanshu Guptabc3738e2014-06-09 18:57:18 -0700123were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are
124modified to update all references to these non-desktop classes. An alternate implementation of
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700125these (com.android.tools.layoutlib.java.*) is injected.
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530126
Deepanshu Guptac9557c32014-07-14 15:50:49 -0700127ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
128DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
129changes the calls to a method in each class instead of changing the implementation of the method.
130This is useful for methods in the Java namespace where we cannot add delegates. The configuration
131for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter.
Deepanshu Guptabc3738e2014-06-09 18:57:18 -0700132
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700133The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
134Transformation chains in the asm user guide, link in the References.) The order of execution of
135these is:
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530136ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
Deepanshu Guptabc3738e2014-06-09 18:57:18 -0700137RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800138
139- Method stubs
140--------------
141
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700142As indicated above, all native and overridden methods are replaced by a stub. We don't have the
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +0100143code to replace with in layoutlib_create. Instead the StubCallMethodAdapter replaces the code of the
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700144method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800145listeners from these overridden method calls based on the method signatures.
146
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700147The listeners are currently pretty basic: we only pass the signature of the method being called, its
148caller object and a flag indicating whether the method was native. We do not currently provide the
149parameters. The listener can however specify the return value of the overridden method.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800150
151This strategy is now obsolete and replaced by the method delegates.
152
153
154- Strategies
155------------
156
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700157We currently have 6 strategies to deal with overriding the rendering code and make it run in
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +0100158Android Studio. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Android Studio)
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700159and the generator.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800160
161
1621- Class Injection
163
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530164This is the easiest: we currently inject the following classes:
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700165- OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to
166 some specific methods that are stubbed out and change their return value.
167- CreateInfo class, which configured the generator. Not used yet, but could in theory help us track
168 what the generator changed.
169- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are
170 injected. The implementation for these classes has been taken from Android's libcore
171 (platform/libcore/luni/src/main/java/java/...).
Deepanshu Guptabc3738e2014-06-09 18:57:18 -0700172- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700173 added to the Dalvik VM for performance reasons. An implementation that is very close to the
174 original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees
175 were in part of the java package, where we can't inject classes, all references to these have been
176 updated (See strategy 4- Refactoring Classes).
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800177
178
1792- Overriding methods
180
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700181As explained earlier, the creator doesn't have any replacement code for methods to override. Instead
182it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The
183bridge then registers a listener on the method signature and can provide an implementation.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800184
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700185This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method
186Delegates).
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800187
188
1893- Renaming classes
190
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700191This simply changes the name of a class in its definition, as well as all its references in internal
192inner classes and methods. Calls from other classes are not modified -- they keep referencing the
193original class name. This allows the bridge to literally replace an implementation.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800194
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700195An example will make this easier: android.graphics.Paint is the main drawing class that we need to
196replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own
197replacement version of Paint which will be used by the rest of the Android stack. The replacement
198version of Paint can still use (either by inheritance or delegation) all the original non-native
199code of _original_Paint if it so desires.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800200
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700201Some of the Android classes are basically wrappers over native objects and since we don't have the
Fedor Kudasov0c5db0f2019-05-29 11:31:14 +0100202native code in Android Studio, we need to provide a full alternate implementation. Sub-classing doesn't
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700203work as some native methods are static and we don't control object creation.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800204
205This won't rename/replace the inner static methods of a given class.
206
207
Deepanshu Gupta60b7c022013-11-06 15:15:32 +05302084- Refactoring classes
209
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700210This is very similar to the Renaming classes except that it also updates the reference in all
211classes. This is done for classes which are added to the Dalvik VM for performance reasons but are
Deepanshu Guptabc3738e2014-06-09 18:57:18 -0700212not present in the Desktop VM. An implementation for these classes is also injected.
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530213
214
2155- Method erasure based on return type
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800216
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700217This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some
218inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and
219all the original implementation is native.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800220
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700221In this case we have a strategy that tells the generator that anything returning, for example, the
222inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own
223implementation.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800224
225
Deepanshu Gupta60b7c022013-11-06 15:15:32 +05302266- Method Delegates
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800227
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700228This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1
229or 2 methods are generated:
230a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original
231method as-is from the reader. This step is omitted if the method is native, since it has no Java
232implementation.
233b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method
234named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in
Deepanshu Gupta544d3252014-07-10 16:16:29 -0700235layoutlib_bridge.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800236
Deepanshu Guptaf9644532014-04-08 16:16:43 -0700237The delegate method is a static method. If the original method is non-static, the delegate method
238receives the original 'this' as its first argument. If the original method is an inner non-static
239method, it also receives the inner 'this' as the second argument.
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800240
241
242
243- References -
244--------------
245
246
247The JVM Specification 2nd edition:
248 http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html
249
250Understanding bytecode:
251 http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/
252
253Bytecode opcode list:
254 http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings
255
256ASM user guide:
Deepanshu Gupta60b7c022013-11-06 15:15:32 +0530257 http://download.forge.objectweb.org/asm/asm4-guide.pdf
Adam Lesinskiab775ec2014-01-23 18:17:42 -0800258
259
260--
261end