1# Copyright (C) 2008 The Android Open Source Project 2 3 4- Description - 5--------------- 6 7Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform 8layout. 9 10 11- Usage - 12--------- 13 14 ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar 15 16 17- Design Overview - 18------------------- 19 20Layoutlib_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. 22 23These 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. 29 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, 35- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform 36 rendering. 37 38The ASM library is used to do the bytecode modification using its visitor pattern API. 39 40The 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. Some configuration that may be platform 43dependent is also present elsewhere in code. 44 45The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that 46provides all the necessary missing implementation for rendering graphics in Eclipse. 47 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 61The 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. 63 64To 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. 67 68The 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**" 70("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we 71almost but not quite request the inclusion of full packages. 72 73The analyzer is also given a list of classes to exclude. A fake implementation of these classes is 74injected by the Generator. 75 76With this information, the analyzer parses the input zip to find all the classes. All classes 77deriving from the requested bases classes are kept. All classes whose name match the glob pattern 78are 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. 81 82The output of the analyzer is a set of ASM ClassReader instances which are then fed to the 83generator. 84 85 86- Generator 87----------- 88 89The 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 91 layoutlib_create and will be used to interface with the renderer in Eclipse. 92- 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. 96- specific classes to refactor. 97 98Each of these are specific strategies we use to be able to modify the Android code to fit within the 99Eclipse renderer. These strategies are explained below. 100 101The 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. 103 104The first step of the transformation is to implement the method delegates. 105 106The TransformClassAdapter is then used to process the potentially renamed class. All protected or 107private classes are market as public. All classes are made non-final. Interfaces are left as-is. 108 109If 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. 113 114Finally fields are also visited and changed from protected/private to public. 115 116The 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. 120 121The 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 123were 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 125these (com.android.tools.layoutlib.java.*) is injected. 126 127RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the 128class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the 129class was originally compiled with Java 7 (version 51). This is because we don't currently generate 130the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have 131valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on 132Mac has horrible font rendering support. 133 134ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the 135DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly 136changes the calls to a method in each class instead of changing the implementation of the method. 137This is useful for methods in the Java namespace where we cannot add delegates. The configuration 138for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter. 139 140The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7 141Transformation chains in the asm user guide, link in the References.) The order of execution of 142these is: 143ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] -> 144RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter 145 146- Method stubs 147-------------- 148 149As indicated above, all native and overridden methods are replaced by a stub. We don't have the 150code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the 151method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register 152listeners from these overridden method calls based on the method signatures. 153 154The listeners are currently pretty basic: we only pass the signature of the method being called, its 155caller object and a flag indicating whether the method was native. We do not currently provide the 156parameters. The listener can however specify the return value of the overridden method. 157 158This strategy is now obsolete and replaced by the method delegates. 159 160 161- Strategies 162------------ 163 164We currently have 6 strategies to deal with overriding the rendering code and make it run in 165Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse) 166and the generator. 167 168 1691- Class Injection 170 171This is the easiest: we currently inject the following classes: 172- OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to 173 some specific methods that are stubbed out and change their return value. 174- CreateInfo class, which configured the generator. Not used yet, but could in theory help us track 175 what the generator changed. 176- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are 177 injected. The implementation for these classes has been taken from Android's libcore 178 (platform/libcore/luni/src/main/java/java/...). 179- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are 180 added to the Dalvik VM for performance reasons. An implementation that is very close to the 181 original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees 182 were in part of the java package, where we can't inject classes, all references to these have been 183 updated (See strategy 4- Refactoring Classes). 184 185 1862- Overriding methods 187 188As explained earlier, the creator doesn't have any replacement code for methods to override. Instead 189it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The 190bridge then registers a listener on the method signature and can provide an implementation. 191 192This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method 193Delegates). 194 195 1963- Renaming classes 197 198This simply changes the name of a class in its definition, as well as all its references in internal 199inner classes and methods. Calls from other classes are not modified -- they keep referencing the 200original class name. This allows the bridge to literally replace an implementation. 201 202An example will make this easier: android.graphics.Paint is the main drawing class that we need to 203replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own 204replacement version of Paint which will be used by the rest of the Android stack. The replacement 205version of Paint can still use (either by inheritance or delegation) all the original non-native 206code of _original_Paint if it so desires. 207 208Some of the Android classes are basically wrappers over native objects and since we don't have the 209native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't 210work as some native methods are static and we don't control object creation. 211 212This won't rename/replace the inner static methods of a given class. 213 214 2154- Refactoring classes 216 217This is very similar to the Renaming classes except that it also updates the reference in all 218classes. This is done for classes which are added to the Dalvik VM for performance reasons but are 219not present in the Desktop VM. An implementation for these classes is also injected. 220 221 2225- Method erasure based on return type 223 224This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some 225inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and 226all the original implementation is native. 227 228In this case we have a strategy that tells the generator that anything returning, for example, the 229inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own 230implementation. 231 232 2336- Method Delegates 234 235This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1 236or 2 methods are generated: 237a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original 238method as-is from the reader. This step is omitted if the method is native, since it has no Java 239implementation. 240b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method 241named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in 242layoutlib_bridge. 243 244The delegate method is a static method. If the original method is non-static, the delegate method 245receives the original 'this' as its first argument. If the original method is an inner non-static 246method, it also receives the inner 'this' as the second argument. 247 248 249 250- References - 251-------------- 252 253 254The JVM Specification 2nd edition: 255 http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html 256 257Understanding bytecode: 258 http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ 259 260Bytecode opcode list: 261 http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings 262 263ASM user guide: 264 http://download.forge.objectweb.org/asm/asm4-guide.pdf 265 266 267-- 268end 269