Loading tools/layoutlib/create/README.txt +17 −0 Original line number Original line Diff line number Diff line Loading @@ -195,5 +195,22 @@ example, the inner class Paint$Style in the Paint class should be discarded and bridge will provide its own implementation. bridge will provide its own implementation. - References - -------------- The JVM Specification 2nd edition: http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html Understanding bytecode: http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ Bytecode opcode list: http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings ASM user guide: http://download.forge.objectweb.org/asm/asm-guide.pdf -- -- end end tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java 0 → 100644 +27 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.layoutlib.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Denotes a method that has been converted to a delegate by layoutlib_create. */ @Retention(RetentionPolicy.RUNTIME) public @interface LayoutlibDelegate { } tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +91 −62 Original line number Original line Diff line number Diff line Loading @@ -28,9 +28,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.Map; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Set; import java.util.TreeMap; import java.util.TreeMap; import java.util.Map.Entry; import java.util.jar.JarEntry; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream; Loading Loading @@ -60,38 +60,55 @@ public class AsmGenerator { * old-FQCN to rename and they get erased as they get renamed. At the end, classes still * old-FQCN to rename and they get erased as they get renamed. At the end, classes still * left here are not in the code base anymore and thus were not renamed. */ * left here are not in the code base anymore and thus were not renamed. */ private HashSet<String> mClassesNotRenamed; private HashSet<String> mClassesNotRenamed; /** A map { FQCN => map { list of return types to delete from the FQCN } }. */ /** A map { FQCN => set { list of return types to delete from the FQCN } }. */ private HashMap<String, Set<String>> mDeleteReturns; private HashMap<String, Set<String>> mDeleteReturns; /** A map { FQCN => set { method names } } of methods to rewrite as delegates. * The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */ private final HashMap<String, Set<String>> mDelegateMethods; /** /** * Creates a new generator that can generate the output JAR with the stubbed classes. * Creates a new generator that can generate the output JAR with the stubbed classes. * * * @param log Output logger. * @param log Output logger. * @param osDestJar The path of the destination JAR to create. * @param osDestJar The path of the destination JAR to create. * @param injectClasses The list of class from layoutlib_create to inject in layoutlib. * @param createInfo Creation parameters. Must not be null. * @param stubMethods The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * @param deleteReturns List of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. */ */ public AsmGenerator(Log log, String osDestJar, public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) { Class<?>[] injectClasses, String[] stubMethods, String[] renameClasses, String[] deleteReturns) { mLog = log; mLog = log; mOsDestJar = osDestJar; mOsDestJar = osDestJar; mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0]; mInjectClasses = createInfo.getInjectedClasses(); mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) : mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods())); new HashSet<String>(); // Create the map/set of methods to change to delegates mDelegateMethods = new HashMap<String, Set<String>>(); for (String signature : createInfo.getDelegateMethods()) { int pos = signature.indexOf('#'); if (pos <= 0 || pos >= signature.length() - 1) { continue; } String className = binaryToInternalClassName(signature.substring(0, pos)); String methodName = signature.substring(pos + 1); Set<String> methods = mDelegateMethods.get(className); if (methods == null) { methods = new HashSet<String>(); mDelegateMethods.put(className, methods); } methods.add(methodName); } for (String className : createInfo.getDelegateClassNatives()) { Set<String> methods = mDelegateMethods.get(className); if (methods == null) { methods = new HashSet<String>(); mDelegateMethods.put(className, methods); } methods.add(DelegateClassAdapter.ALL_NATIVES); } // Create the map of classes to rename. // Create the map of classes to rename. mRenameClasses = new HashMap<String, String>(); mRenameClasses = new HashMap<String, String>(); mClassesNotRenamed = new HashSet<String>(); mClassesNotRenamed = new HashSet<String>(); int n = renameClasses == null ? 0 : renameClasses.length; String[] renameClasses = createInfo.getRenamedClasses(); int n = renameClasses.length; for (int i = 0; i < n; i += 2) { for (int i = 0; i < n; i += 2) { assert i + 1 < n; assert i + 1 < n; // The ASM class names uses "/" separators, whereas regular FQCN use "." // The ASM class names uses "/" separators, whereas regular FQCN use "." Loading @@ -103,7 +120,7 @@ public class AsmGenerator { // create the map of renamed class -> return type of method to delete. // create the map of renamed class -> return type of method to delete. mDeleteReturns = new HashMap<String, Set<String>>(); mDeleteReturns = new HashMap<String, Set<String>>(); if (deleteReturns != null) { String[] deleteReturns = createInfo.getDeleteReturns(); Set<String> returnTypes = null; Set<String> returnTypes = null; String renamedClass = null; String renamedClass = null; for (String className : deleteReturns) { for (String className : deleteReturns) { Loading @@ -130,7 +147,6 @@ public class AsmGenerator { returnTypes.add(binaryToInternalClassName(className)); returnTypes.add(binaryToInternalClassName(className)); } } } } } /** /** * Returns the list of classes that have not been renamed yet. * Returns the list of classes that have not been renamed yet. Loading Loading @@ -270,6 +286,8 @@ public class AsmGenerator { byte[] transform(ClassReader cr, boolean stubNativesOnly) { byte[] transform(ClassReader cr, boolean stubNativesOnly) { boolean hasNativeMethods = hasNativeMethods(cr); boolean hasNativeMethods = hasNativeMethods(cr); // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass) String className = cr.getClassName(); String className = cr.getClassName(); String newName = transformName(className); String newName = transformName(className); Loading @@ -294,6 +312,17 @@ public class AsmGenerator { rv = new RenameClassAdapter(cw, className, newName); rv = new RenameClassAdapter(cw, className, newName); } } Set<String> delegateMethods = mDelegateMethods.get(className); if (delegateMethods != null && !delegateMethods.isEmpty()) { // If delegateMethods only contains one entry ALL_NATIVES and the class is // known to have no native methods, just skip this step. if (hasNativeMethods || !(delegateMethods.size() == 1 && delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) { rv = new DelegateClassAdapter(mLog, rv, className, delegateMethods); } } TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className), mDeleteReturns.get(className), newName, rv, newName, rv, Loading tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +85 −8 Original line number Original line Diff line number Diff line Loading @@ -16,22 +16,99 @@ package com.android.tools.layoutlib.create; package com.android.tools.layoutlib.create; public class CreateInfo { /** * Describes the work to be done by {@link AsmGenerator}. */ public final class CreateInfo implements ICreateInfo { /** * Returns the list of class from layoutlib_create to inject in layoutlib. * The list can be empty but must not be null. */ public Class<?>[] getInjectedClasses() { return INJECTED_CLASSES; } /** * Returns the list of methods to rewrite as delegates. * The list can be empty but must not be null. */ public String[] getDelegateMethods() { return DELEGATE_METHODS; } /** * Returns the list of classes on which to delegate all native methods. * The list can be empty but must not be null. */ public String[] getDelegateClassNatives() { return DELEGATE_CLASS_NATIVES; } /** * Returns The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * The list can be empty but must not be null. */ public String[] getOverriddenMethods() { return OVERRIDDEN_METHODS; } /** * Returns the list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * The list can be empty but must not be null. */ public String[] getRenamedClasses() { return RENAMED_CLASSES; } /** * Returns the list of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. * The list can be empty but must not be null. */ public String[] getDeleteReturns() { return DELETE_RETURNS; } //----- /** /** * The list of class from layoutlib_create to inject in layoutlib. * The list of class from layoutlib_create to inject in layoutlib. */ */ public final static Class<?>[] INJECTED_CLASSES = new Class<?>[] { private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] { OverrideMethod.class, OverrideMethod.class, MethodListener.class, MethodListener.class, MethodAdapter.class, MethodAdapter.class, CreateInfo.class CreateInfo.class }; }; /** * The list of methods to rewrite as delegates. */ private final static String[] DELEGATE_METHODS = new String[] { // TODO: comment out once DelegateClass is working // "android.view.View#isInEditMode", // "android.content.res.Resources$Theme#obtainStyledAttributes", }; /** * The list of classes on which to delegate all native methods. */ private final static String[] DELEGATE_CLASS_NATIVES = new String[] { // TODO: comment out once DelegateClass is working // "android.graphics.Paint" }; /** /** * The list of methods to stub out. Each entry must be in the form * The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * "package.package.OuterClass$InnerClass#MethodName". */ */ public final static String[] OVERRIDDEN_METHODS = new String[] { private final static String[] OVERRIDDEN_METHODS = new String[] { // TODO: remove once DelegateClass is working "android.view.View#isInEditMode", "android.view.View#isInEditMode", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#obtainStyledAttributes", }; }; Loading @@ -40,7 +117,7 @@ public class CreateInfo { * The list of classes to rename, must be an even list: the binary FQCN * The list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * of class to replace followed by the new FQCN. */ */ public final static String[] RENAMED_CLASSES = private final static String[] RENAMED_CLASSES = new String[] { new String[] { "android.graphics.Bitmap", "android.graphics._Original_Bitmap", "android.graphics.Bitmap", "android.graphics._Original_Bitmap", "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", Loading Loading @@ -69,7 +146,7 @@ public class CreateInfo { * to rename in which the methods are deleted, followed by a list of return types identifying * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. * the methods to delete. */ */ public final static String[] REMOVED_METHODS = private final static String[] DELETE_RETURNS = new String[] { new String[] { "android.graphics.Paint", // class to delete methods from "android.graphics.Paint", // class to delete methods from "android.graphics.Paint$Align", // list of type identifying methods to delete "android.graphics.Paint$Align", // list of type identifying methods to delete Loading tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java 0 → 100644 +94 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.layoutlib.create; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.Set; /** * A {@link DelegateClassAdapter} can transform some methods from a class into * delegates that defer the call to an associated delegate class. * <p/> * This is used to override specific methods and or all native methods in classes. */ public class DelegateClassAdapter extends ClassAdapter { public final static String ALL_NATIVES = "<<all_natives>>"; private final String mClassName; private final Set<String> mDelegateMethods; private final Log mLog; /** * Creates a new {@link DelegateClassAdapter} that can transform some methods * from a class into delegates that defer the call to an associated delegate class. * <p/> * This is used to override specific methods and or all native methods in classes. * * @param log The logger object. Must not be null. * @param cv the class visitor to which this adapter must delegate calls. * @param className The internal class name of the class to visit, * e.g. <code>com/android/SomeClass$InnerClass</code>. * @param delegateMethods The set of method names to modify and/or the * special constant {@link #ALL_NATIVES} to convert all native methods. */ public DelegateClassAdapter(Log log, ClassVisitor cv, String className, Set<String> delegateMethods) { super(cv); mLog = log; mClassName = className; mDelegateMethods = delegateMethods; } //---------------------------------- // Methods from the ClassAdapter @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) || mDelegateMethods.contains(name); if (useDelegate) { // remove native access = access & ~Opcodes.ACC_NATIVE; } MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); if (useDelegate) { DelegateMethodAdapter a = new DelegateMethodAdapter(mLog, mw, mClassName, name, desc, isStatic); if (isNative) { // A native has no code to visit, so we need to generate it directly. a.generateCode(); } else { return a; } } return mw; } } Loading
tools/layoutlib/create/README.txt +17 −0 Original line number Original line Diff line number Diff line Loading @@ -195,5 +195,22 @@ example, the inner class Paint$Style in the Paint class should be discarded and bridge will provide its own implementation. bridge will provide its own implementation. - References - -------------- The JVM Specification 2nd edition: http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html Understanding bytecode: http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ Bytecode opcode list: http://en.wikipedia.org/wiki/Java_bytecode_instruction_listings ASM user guide: http://download.forge.objectweb.org/asm/asm-guide.pdf -- -- end end
tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/LayoutlibDelegate.java 0 → 100644 +27 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.layoutlib.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Denotes a method that has been converted to a delegate by layoutlib_create. */ @Retention(RetentionPolicy.RUNTIME) public @interface LayoutlibDelegate { }
tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java +91 −62 Original line number Original line Diff line number Diff line Loading @@ -28,9 +28,9 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.Map; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Set; import java.util.TreeMap; import java.util.TreeMap; import java.util.Map.Entry; import java.util.jar.JarEntry; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.JarOutputStream; Loading Loading @@ -60,38 +60,55 @@ public class AsmGenerator { * old-FQCN to rename and they get erased as they get renamed. At the end, classes still * old-FQCN to rename and they get erased as they get renamed. At the end, classes still * left here are not in the code base anymore and thus were not renamed. */ * left here are not in the code base anymore and thus were not renamed. */ private HashSet<String> mClassesNotRenamed; private HashSet<String> mClassesNotRenamed; /** A map { FQCN => map { list of return types to delete from the FQCN } }. */ /** A map { FQCN => set { list of return types to delete from the FQCN } }. */ private HashMap<String, Set<String>> mDeleteReturns; private HashMap<String, Set<String>> mDeleteReturns; /** A map { FQCN => set { method names } } of methods to rewrite as delegates. * The special name {@link DelegateClassAdapter#ALL_NATIVES} can be used as in internal set. */ private final HashMap<String, Set<String>> mDelegateMethods; /** /** * Creates a new generator that can generate the output JAR with the stubbed classes. * Creates a new generator that can generate the output JAR with the stubbed classes. * * * @param log Output logger. * @param log Output logger. * @param osDestJar The path of the destination JAR to create. * @param osDestJar The path of the destination JAR to create. * @param injectClasses The list of class from layoutlib_create to inject in layoutlib. * @param createInfo Creation parameters. Must not be null. * @param stubMethods The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * @param renameClasses The list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * @param deleteReturns List of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. */ */ public AsmGenerator(Log log, String osDestJar, public AsmGenerator(Log log, String osDestJar, ICreateInfo createInfo) { Class<?>[] injectClasses, String[] stubMethods, String[] renameClasses, String[] deleteReturns) { mLog = log; mLog = log; mOsDestJar = osDestJar; mOsDestJar = osDestJar; mInjectClasses = injectClasses != null ? injectClasses : new Class<?>[0]; mInjectClasses = createInfo.getInjectedClasses(); mStubMethods = stubMethods != null ? new HashSet<String>(Arrays.asList(stubMethods)) : mStubMethods = new HashSet<String>(Arrays.asList(createInfo.getOverriddenMethods())); new HashSet<String>(); // Create the map/set of methods to change to delegates mDelegateMethods = new HashMap<String, Set<String>>(); for (String signature : createInfo.getDelegateMethods()) { int pos = signature.indexOf('#'); if (pos <= 0 || pos >= signature.length() - 1) { continue; } String className = binaryToInternalClassName(signature.substring(0, pos)); String methodName = signature.substring(pos + 1); Set<String> methods = mDelegateMethods.get(className); if (methods == null) { methods = new HashSet<String>(); mDelegateMethods.put(className, methods); } methods.add(methodName); } for (String className : createInfo.getDelegateClassNatives()) { Set<String> methods = mDelegateMethods.get(className); if (methods == null) { methods = new HashSet<String>(); mDelegateMethods.put(className, methods); } methods.add(DelegateClassAdapter.ALL_NATIVES); } // Create the map of classes to rename. // Create the map of classes to rename. mRenameClasses = new HashMap<String, String>(); mRenameClasses = new HashMap<String, String>(); mClassesNotRenamed = new HashSet<String>(); mClassesNotRenamed = new HashSet<String>(); int n = renameClasses == null ? 0 : renameClasses.length; String[] renameClasses = createInfo.getRenamedClasses(); int n = renameClasses.length; for (int i = 0; i < n; i += 2) { for (int i = 0; i < n; i += 2) { assert i + 1 < n; assert i + 1 < n; // The ASM class names uses "/" separators, whereas regular FQCN use "." // The ASM class names uses "/" separators, whereas regular FQCN use "." Loading @@ -103,7 +120,7 @@ public class AsmGenerator { // create the map of renamed class -> return type of method to delete. // create the map of renamed class -> return type of method to delete. mDeleteReturns = new HashMap<String, Set<String>>(); mDeleteReturns = new HashMap<String, Set<String>>(); if (deleteReturns != null) { String[] deleteReturns = createInfo.getDeleteReturns(); Set<String> returnTypes = null; Set<String> returnTypes = null; String renamedClass = null; String renamedClass = null; for (String className : deleteReturns) { for (String className : deleteReturns) { Loading @@ -130,7 +147,6 @@ public class AsmGenerator { returnTypes.add(binaryToInternalClassName(className)); returnTypes.add(binaryToInternalClassName(className)); } } } } } /** /** * Returns the list of classes that have not been renamed yet. * Returns the list of classes that have not been renamed yet. Loading Loading @@ -270,6 +286,8 @@ public class AsmGenerator { byte[] transform(ClassReader cr, boolean stubNativesOnly) { byte[] transform(ClassReader cr, boolean stubNativesOnly) { boolean hasNativeMethods = hasNativeMethods(cr); boolean hasNativeMethods = hasNativeMethods(cr); // Get the class name, as an internal name (e.g. com/android/SomeClass$InnerClass) String className = cr.getClassName(); String className = cr.getClassName(); String newName = transformName(className); String newName = transformName(className); Loading @@ -294,6 +312,17 @@ public class AsmGenerator { rv = new RenameClassAdapter(cw, className, newName); rv = new RenameClassAdapter(cw, className, newName); } } Set<String> delegateMethods = mDelegateMethods.get(className); if (delegateMethods != null && !delegateMethods.isEmpty()) { // If delegateMethods only contains one entry ALL_NATIVES and the class is // known to have no native methods, just skip this step. if (hasNativeMethods || !(delegateMethods.size() == 1 && delegateMethods.contains(DelegateClassAdapter.ALL_NATIVES))) { rv = new DelegateClassAdapter(mLog, rv, className, delegateMethods); } } TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, TransformClassAdapter cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className), mDeleteReturns.get(className), newName, rv, newName, rv, Loading
tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +85 −8 Original line number Original line Diff line number Diff line Loading @@ -16,22 +16,99 @@ package com.android.tools.layoutlib.create; package com.android.tools.layoutlib.create; public class CreateInfo { /** * Describes the work to be done by {@link AsmGenerator}. */ public final class CreateInfo implements ICreateInfo { /** * Returns the list of class from layoutlib_create to inject in layoutlib. * The list can be empty but must not be null. */ public Class<?>[] getInjectedClasses() { return INJECTED_CLASSES; } /** * Returns the list of methods to rewrite as delegates. * The list can be empty but must not be null. */ public String[] getDelegateMethods() { return DELEGATE_METHODS; } /** * Returns the list of classes on which to delegate all native methods. * The list can be empty but must not be null. */ public String[] getDelegateClassNatives() { return DELEGATE_CLASS_NATIVES; } /** * Returns The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * The list can be empty but must not be null. */ public String[] getOverriddenMethods() { return OVERRIDDEN_METHODS; } /** * Returns the list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * The list can be empty but must not be null. */ public String[] getRenamedClasses() { return RENAMED_CLASSES; } /** * Returns the list of classes for which the methods returning them should be deleted. * The array contains a list of null terminated section starting with the name of the class * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. * The list can be empty but must not be null. */ public String[] getDeleteReturns() { return DELETE_RETURNS; } //----- /** /** * The list of class from layoutlib_create to inject in layoutlib. * The list of class from layoutlib_create to inject in layoutlib. */ */ public final static Class<?>[] INJECTED_CLASSES = new Class<?>[] { private final static Class<?>[] INJECTED_CLASSES = new Class<?>[] { OverrideMethod.class, OverrideMethod.class, MethodListener.class, MethodListener.class, MethodAdapter.class, MethodAdapter.class, CreateInfo.class CreateInfo.class }; }; /** * The list of methods to rewrite as delegates. */ private final static String[] DELEGATE_METHODS = new String[] { // TODO: comment out once DelegateClass is working // "android.view.View#isInEditMode", // "android.content.res.Resources$Theme#obtainStyledAttributes", }; /** * The list of classes on which to delegate all native methods. */ private final static String[] DELEGATE_CLASS_NATIVES = new String[] { // TODO: comment out once DelegateClass is working // "android.graphics.Paint" }; /** /** * The list of methods to stub out. Each entry must be in the form * The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". * "package.package.OuterClass$InnerClass#MethodName". */ */ public final static String[] OVERRIDDEN_METHODS = new String[] { private final static String[] OVERRIDDEN_METHODS = new String[] { // TODO: remove once DelegateClass is working "android.view.View#isInEditMode", "android.view.View#isInEditMode", "android.content.res.Resources$Theme#obtainStyledAttributes", "android.content.res.Resources$Theme#obtainStyledAttributes", }; }; Loading @@ -40,7 +117,7 @@ public class CreateInfo { * The list of classes to rename, must be an even list: the binary FQCN * The list of classes to rename, must be an even list: the binary FQCN * of class to replace followed by the new FQCN. * of class to replace followed by the new FQCN. */ */ public final static String[] RENAMED_CLASSES = private final static String[] RENAMED_CLASSES = new String[] { new String[] { "android.graphics.Bitmap", "android.graphics._Original_Bitmap", "android.graphics.Bitmap", "android.graphics._Original_Bitmap", "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", "android.graphics.BitmapFactory", "android.graphics._Original_BitmapFactory", Loading Loading @@ -69,7 +146,7 @@ public class CreateInfo { * to rename in which the methods are deleted, followed by a list of return types identifying * to rename in which the methods are deleted, followed by a list of return types identifying * the methods to delete. * the methods to delete. */ */ public final static String[] REMOVED_METHODS = private final static String[] DELETE_RETURNS = new String[] { new String[] { "android.graphics.Paint", // class to delete methods from "android.graphics.Paint", // class to delete methods from "android.graphics.Paint$Align", // list of type identifying methods to delete "android.graphics.Paint$Align", // list of type identifying methods to delete Loading
tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java 0 → 100644 +94 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.layoutlib.create; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.util.Set; /** * A {@link DelegateClassAdapter} can transform some methods from a class into * delegates that defer the call to an associated delegate class. * <p/> * This is used to override specific methods and or all native methods in classes. */ public class DelegateClassAdapter extends ClassAdapter { public final static String ALL_NATIVES = "<<all_natives>>"; private final String mClassName; private final Set<String> mDelegateMethods; private final Log mLog; /** * Creates a new {@link DelegateClassAdapter} that can transform some methods * from a class into delegates that defer the call to an associated delegate class. * <p/> * This is used to override specific methods and or all native methods in classes. * * @param log The logger object. Must not be null. * @param cv the class visitor to which this adapter must delegate calls. * @param className The internal class name of the class to visit, * e.g. <code>com/android/SomeClass$InnerClass</code>. * @param delegateMethods The set of method names to modify and/or the * special constant {@link #ALL_NATIVES} to convert all native methods. */ public DelegateClassAdapter(Log log, ClassVisitor cv, String className, Set<String> delegateMethods) { super(cv); mLog = log; mClassName = className; mDelegateMethods = delegateMethods; } //---------------------------------- // Methods from the ClassAdapter @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; boolean isNative = (access & Opcodes.ACC_NATIVE) != 0; boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) || mDelegateMethods.contains(name); if (useDelegate) { // remove native access = access & ~Opcodes.ACC_NATIVE; } MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions); if (useDelegate) { DelegateMethodAdapter a = new DelegateMethodAdapter(mLog, mw, mClassName, name, desc, isStatic); if (isNative) { // A native has no code to visit, so we need to generate it directly. a.generateCode(); } else { return a; } } return mw; } }