1 # Copyright (C) 2008 The Android Open Source Project 2 3 4 - Description - 5 --------------- 6 7 Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor 8 to perform layout. 9 10 11 - Usage - 12 --------- 13 14 ./layoutlib_create path/to/android.jar destination.jar 15 16 17 - Design Overview - 18 ------------------- 19 20 Layoutlib_create uses the "android.jar" containing all the Java code used by Android 21 as generated by the Android build, right before the classes are converted to a DEX format. 22 23 The Android JAR can't be used directly in Eclipse: 24 - it 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 26 replaced by Java 2D calls in Eclipse. 27 - some of the classes that need to be changed are final and/or we need access 28 to their private internal state. 29 30 Consequently 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 36 for Eclipse to perform rendering. 37 38 The ASM library is used to do the bytecode modification using its visitor pattern API. 39 40 The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the 41 configuration is done in the main() method and the CreateInfo structure is expected to 42 change with the Android platform as new classes are added, changed or removed. 43 44 The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the 45 platform, that provides all the necessary missing implementation for rendering graphics 46 in Eclipse. 47 48 49 50 - Implementation Notes - 51 ------------------------ 52 53 The 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 61 The goal of the analyzer is to create a graph of all the classes from the input JAR 62 with their dependencies and then only keep the ones we want. 63 64 To do that, the analyzer is created with a list of base classes to keep -- everything 65 that derives from these is kept. Currently the one such class is android.view.View: 66 since we want to render layouts, anything that is sort of a view needs to be kept. 67 68 The analyzer is also given a list of class names to keep in the output. 69 This is done using shell-like glob patterns that filter on the fully-qualified 70 class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does, 71 and "." and "$" are interpreted as-is). 72 In practice we almost but not quite request the inclusion of full packages. 73 74 The analyzer is also given a list of classes to exclude. A fake implementation of these 75 classes is injected by the Generator. 76 77 With this information, the analyzer parses the input zip to find all the classes. 78 All classes deriving from the requested bases classes are kept. 79 All classes which name matched the glob pattern are kept. 80 The analysis then finds all the dependencies of the classes that are to be kept 81 using an ASM visitor on the class, the field types, the method types and annotations types. 82 Classes that belong to the current JRE are excluded. 83 84 The output of the analyzer is a set of ASM ClassReader instances which are then 85 fed to the generator. 86 87 88 - Generator 89 ----------- 90 91 The generator is constructed from a CreateInfo struct that acts as a config file 92 and lists: 93 - the classes to inject in the output JAR -- these classes are directly implemented 94 in layoutlib_create and will be used to interface with the renderer in Eclipse. 95 - specific methods to override (see method stubs details below). 96 - specific methods for which to delegate calls. 97 - specific methods to remove based on their return type. 98 - specific classes to rename. 99 - specific classes to refactor. 100 101 Each of these are specific strategies we use to be able to modify the Android code 102 to fit within the Eclipse renderer. These strategies are explained beow. 103 104 The core method of the generator is transform(): it takes an input ASM ClassReader 105 and modifies it to produce a byte array suitable for the final JAR file. 106 107 The first step of the transformation is to implement the method delegates. 108 109 The TransformClassAdapter is then used to process the potentially renamed class. 110 All protected or private classes are market as public. 111 All classes are made non-final. 112 Interfaces are left as-is. 113 114 If a method has a return type that must be erased, the whole method is skipped. 115 Methods are also changed from protected/private to public. 116 The code of the methods is then kept as-is, except for native methods which are 117 replaced by a stub. Methods that are to be overridden are also replaced by a stub. 118 119 Finally fields are also visited and changed from protected/private to public. 120 121 The next step of the transformation is changing the name of the class in case 122 we requested the class to be renamed. This uses the RenameClassAdapter to also rename 123 all inner classes and references in methods and types. Note that other classes are 124 not transformed and keep referencing the original name. 125 126 The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but 127 updates the references in all classes. This is used to update the references of classes 128 in the java package that were added in the Dalvik VM but are not a part of the standard 129 JVM. The existing classes are modified to update all references to these non-standard 130 classes. An alternate implementation of these (com.android.tools.layoutlib.java.*) is 131 injected. 132 133 The ClassAdapters are chained together to achieve the desired output. (Look at section 134 2.2.7 Transformation chains in the asm user guide, link in the References.) The order of 135 execution of these is: 136 ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] -> 137 RefactorClassAdapter -> ClassWriter 138 139 - Method stubs 140 -------------- 141 142 As indicated above, all native and overridden methods are replaced by a stub. 143 We don't have the code to replace with in layoutlib_create. 144 Instead the StubMethodAdapter replaces the code of the method by a call to 145 OverrideMethod.invokeX(). When using the final JAR, the bridge can register 146 listeners from these overridden method calls based on the method signatures. 147 148 The listeners are currently pretty basic: we only pass the signature of the 149 method being called, its caller object and a flag indicating whether the 150 method was native. We do not currently provide the parameters. The listener 151 can however specify the return value of the overridden method. 152 153 This strategy is now obsolete and replaced by the method delegates. 154 155 156 - Strategies 157 ------------ 158 159 We currently have 6 strategies to deal with overriding the rendering code 160 and make it run in Eclipse. Most of these strategies are implemented hand-in-hand 161 by the bridge (which runs in Eclipse) and the generator. 162 163 164 1- Class Injection 165 166 This is the easiest: we currently inject the following classes: 167 - OverrideMethod and its associated MethodListener and MethodAdapter are used 168 to intercept calls to some specific methods that are stubbed out and change 169 their return value. 170 - CreateInfo class, which configured the generator. Not used yet, but could 171 in theory help us track what the generator changed. 172 - AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new 173 classes are injected. The implementation for these classes has been taken from 174 Android's libcore (platform/libcore/luni/src/main/java/java/...). 175 - Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM. 176 They are added to the Dalvik VM for performance reasons. An implementation that is very 177 close to the original (which is at platform/libcore/luni/src/main/java/...) is injected. 178 Since these classees were in part of the java package, where we can't inject classes, 179 all references to these have been updated (See strategy 4- Refactoring Classes). 180 181 182 2- Overriding methods 183 184 As explained earlier, the creator doesn't have any replacement code for 185 methods to override. Instead it removes the original code and replaces it 186 by a call to a specific OveriddeMethod.invokeX(). The bridge then registers 187 a listener on the method signature and can provide an implementation. 188 189 This strategy is now obsolete and replaced by the method delegates. 190 See strategy 5 below. 191 192 193 3- Renaming classes 194 195 This simply changes the name of a class in its definition, as well as all its 196 references in internal inner classes and methods. 197 Calls from other classes are not modified -- they keep referencing the original 198 class name. This allows the bridge to literally replace an implementation. 199 200 An example will make this easier: android.graphics.Paint is the main drawing 201 class that we need to replace. To do so, the generator renames Paint to _original_Paint. 202 Later the bridge provides its own replacement version of Paint which will be used 203 by the rest of the Android stack. The replacement version of Paint can still use 204 (either by inheritance or delegation) all the original non-native code of _original_Paint 205 if it so desires. 206 207 Some of the Android classes are basically wrappers over native objects and since 208 we don't have the native code in Eclipse, we need to provide a full alternate 209 implementation. Sub-classing doesn't work as some native methods are static and 210 we don't control object creation. 211 212 This won't rename/replace the inner static methods of a given class. 213 214 215 4- Refactoring classes 216 217 This is very similar to the Renaming classes except that it also updates the reference in 218 all classes. This is done for classes which are added to the Dalvik VM for performance 219 reasons but are not present in the Standard Java VM. An implementation for these classes 220 is also injected. 221 222 223 5- Method erasure based on return type 224 225 This is mostly an implementation detail of the bridge: in the Paint class 226 mentioned above, some inner static classes are used to pass around 227 attributes (e.g. FontMetrics, or the Style enum) and all the original implementation 228 is native. 229 230 In this case we have a strategy that tells the generator that anything returning, for 231 example, the inner class Paint$Style in the Paint class should be discarded and the 232 bridge will provide its own implementation. 233 234 235 6- Method Delegates 236 237 This strategy is used to override method implementations. 238 Given a method SomeClass.MethodName(), 1 or 2 methods are generated: 239 a- A copy of the original method named SomeClass.MethodName_Original(). 240 The content is the original method as-is from the reader. 241 This step is omitted if the method is native, since it has no Java implementation. 242 b- A brand new implementation of SomeClass.MethodName() which calls to a 243 non-existing static method named SomeClass_Delegate.MethodName(). 244 The implementation of this 'delegate' method is done in layoutlib_brigde. 245 246 The delegate method is a static method. 247 If the original method is non-static, the delegate method receives the original 'this' 248 as its first argument. If the original method is an inner non-static method, it also 249 receives the inner 'this' as the second argument. 250 251 252 253 - References - 254 -------------- 255 256 257 The JVM Specification 2nd edition: 258 http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html 259 260 Understanding bytecode: 261 http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ 262 263 Bytecode opcode list: 264 http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings 265 266 ASM user guide: 267 http://download.forge.objectweb.org/asm/asm4-guide.pdf 268 269 270 -- 271 end 272