diff options
author | Raphael Moll <ralf@android.com> | 2011-06-17 17:12:52 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2011-06-17 19:07:13 -0700 |
commit | 865c3bef54228a353fd449a093b0c8d155618296 (patch) | |
tree | c93e90facdad4fbabb288960427874bd6e7dd2bf /tools | |
parent | 94062517ae5573f9a1a877a83fe7d8e6d1a2c350 (diff) | |
download | frameworks_base-865c3bef54228a353fd449a093b0c8d155618296.zip frameworks_base-865c3bef54228a353fd449a093b0c8d155618296.tar.gz frameworks_base-865c3bef54228a353fd449a093b0c8d155618296.tar.bz2 |
Laoutlib_creator: keep original of delegate methods.
For specific methods, Layoublib_create can rewrite the implementation
of a method to invoke a delegate instead of the original code. This
allows layoutlib to implement native code or override existing behavior.
This patch also 'saves' the original implementation of a rewritten
method so that the delegate can access the original implementation
as needed. Obviously this is only done for non-native methods.
Given a non-native SomeClass.MethodName, we generate 2 methods:
- A copy of the original method named "SomeClass.MethodName_original".
The content is the original method as-is from the reader.
- A brand new implementation of SomeClass.MethodName which calls to a
non-existing method named "SomeClass_delegate.MethodName".
The implementation of this 'delegate' method is done in layoutlib_brigde.
Change-Id: I5ca2cd3ac55991a8e8a51c417e75ee447bf9e9e6
Diffstat (limited to 'tools')
-rw-r--r-- | tools/layoutlib/create/README.txt | 34 | ||||
-rw-r--r-- | tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java | 3 | ||||
-rw-r--r-- | tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java | 62 | ||||
-rw-r--r-- | tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java (renamed from tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java) | 255 | ||||
-rw-r--r-- | tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java | 13 | ||||
-rw-r--r-- | tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java | 58 | ||||
-rw-r--r-- | tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java | 7 | ||||
-rw-r--r-- | tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java | 4 |
8 files changed, 319 insertions, 117 deletions
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt index 65a64cd..894611b 100644 --- a/tools/layoutlib/create/README.txt +++ b/tools/layoutlib/create/README.txt @@ -30,9 +30,9 @@ The Android JAR can't be used directly in Eclipse: Consequently this tool: - parses the input JAR, - modifies some of the classes directly using some bytecode manipulation, -- filters some packages and removes some that we don't want to end in the output JAR, +- filters some packages and removes those we don't want in the output JAR, - injects some new classes, -- and generates a modified JAR file that is suitable for the Android plugin +- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform rendering. The ASM library is used to do the bytecode modification using its visitor pattern API. @@ -63,7 +63,7 @@ with their dependencies and then only keep the ones we want. To do that, the analyzer is created with a list of base classes to keep -- everything that derives from these is kept. Currently the one such class is android.view.View: -since we want to render layouts, anything that is sort of the view needs to be kept. +since we want to render layouts, anything that is sort of a view needs to be kept. The analyzer is also given a list of class names to keep in the output. This is done using shell-like glob patterns that filter on the fully-qualified @@ -90,6 +90,7 @@ and lists: - the classes to inject in the output JAR -- these classes are directly implemented in layoutlib_create and will be used to interface with the renderer in Eclipse. - specific methods to override (see method stubs details below). +- specific methods for which to delegate calls. - specific methods to remove based on their return type. - specific classes to rename. @@ -114,6 +115,9 @@ Methods are also changed from protected/private to public. The code of the methods is then kept as-is, except for native methods which are replaced by a stub. Methods that are to be overridden are also replaced by a stub. +The transformed class is then fed through the DelegateClassAdapter to implement +method delegates. + Finally fields are also visited and changed from protected/private to public. @@ -131,8 +135,7 @@ method being called, its caller object and a flag indicating whether the method was native. We do not currently provide the parameters. The listener can however specify the return value of the overridden method. -An extension being worked on is to actually replace these listeners by -direct calls to a delegate class, complete with parameters. +This strategy is now obsolete and replaced by the method delegates. - Strategies @@ -160,6 +163,9 @@ methods to override. Instead it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The bridge then registers a listener on the method signature and can provide an implementation. +This strategy is now obsolete and replaced by the method delegates. +See strategy 5 below. + 3- Renaming classes @@ -195,6 +201,24 @@ example, the inner class Paint$Style in the Paint class should be discarded and bridge will provide its own implementation. +5- Method Delegates + +This strategy is used to override method implementations. +Given a method SomeClass.MethodName(), 1 or 2 methods are generated: +a- A copy of the original method named SomeClass.MethodName_Original(). + The content is the original method as-is from the reader. + This step is omitted if the method is native, since it has no Java implementation. +b- A brand new implementation of SomeClass.MethodName() which calls to a + non-existing static method named SomeClass_Delegate.MethodName(). + The implementation of this 'delegate' method is done in layoutlib_brigde. + +The delegate method is a static method. +If the original method is non-static, the delegate method receives the original 'this' +as its first argument. If the original method is an inner non-static method, it also +receives the inner 'this' as the second argument. + + + - References - -------------- diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 4b62e43..85e6c3f 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -51,6 +51,8 @@ public final class CreateInfo implements ICreateInfo { * 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. + * <p/> + * This usage is deprecated. Please use method 'delegates' instead. */ public String[] getOverriddenMethods() { return OVERRIDDEN_METHODS; @@ -153,6 +155,7 @@ public final class CreateInfo implements ICreateInfo { /** * The list of methods to stub out. Each entry must be in the form * "package.package.OuterClass$InnerClass#MethodName". + * This usage is deprecated. Please use method 'delegates' instead. */ private final static String[] OVERRIDDEN_METHODS = new String[] { }; diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java index 9cba8a0..49ddf1d 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java @@ -31,6 +31,11 @@ import java.util.Set; */ public class DelegateClassAdapter extends ClassAdapter { + /** Suffix added to original methods. */ + private static final String ORIGINAL_SUFFIX = "_Original"; + private static String CONSTRUCTOR = "<init>"; + private static String CLASS_INIT = "<clinit>"; + public final static String ALL_NATIVES = "<<all_natives>>"; private final String mClassName; @@ -73,22 +78,55 @@ public class DelegateClassAdapter extends ClassAdapter { boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) || mDelegateMethods.contains(name); - if (useDelegate) { - // remove native - access = access & ~Opcodes.ACC_NATIVE; + if (!useDelegate) { + // Not creating a delegate for this method, pass it as-is from the reader + // to the writer. + return super.visitMethod(access, name, desc, signature, exceptions); } - 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; + if (CONSTRUCTOR.equals(name) || CLASS_INIT.equals(name)) { + // We don't currently support generating delegates for constructors. + throw new UnsupportedOperationException( + String.format( + "Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", //$NON-NLS-1$ + mClassName, name, desc)); } } - return mw; + + if (isNative) { + // Remove native flag + access = access & ~Opcodes.ACC_NATIVE; + MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions); + + DelegateMethodAdapter2 a = new DelegateMethodAdapter2( + mLog, null /*mwOriginal*/, mwDelegate, mClassName, name, desc, isStatic); + + // A native has no code to visit, so we need to generate it directly. + a.generateDelegateCode(); + + return mwDelegate; + } + + // Given a non-native SomeClass.MethodName(), we want to generate 2 methods: + // - A copy of the original method named SomeClass.MethodName_Original(). + // The content is the original method as-is from the reader. + // - A brand new implementation of SomeClass.MethodName() which calls to a + // non-existing method named SomeClass_Delegate.MethodName(). + // The implementation of this 'delegate' method is done in layoutlib_brigde. + + int accessDelegate = access; + // change access to public for the original one + access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE); + access |= Opcodes.ACC_PUBLIC; + + MethodVisitor mwOriginal = super.visitMethod(access, name + ORIGINAL_SUFFIX, + desc, signature, exceptions); + MethodVisitor mwDelegate = super.visitMethod(accessDelegate, name, + desc, signature, exceptions); + + DelegateMethodAdapter2 a = new DelegateMethodAdapter2( + mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic); + return a; } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java index 8d7f016..ac4ae6d 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -30,36 +30,57 @@ import org.objectweb.asm.Type; import java.util.ArrayList; /** - * This method adapter rewrites a method by discarding the original code and generating - * a call to a delegate. Original annotations are passed along unchanged. + * This method adapter generates delegate methods. * <p/> - * Calls are delegated to a class named <code><className>_Delegate</code> with - * static methods matching the methods to be overridden here. The methods have the - * same return type. The argument type list is the same except the "this" reference is - * passed first for non-static methods. + * Given a method {@code SomeClass.MethodName()}, this generates 1 or 2 methods: + * <ul> + * <li> A copy of the original method named {@code SomeClass.MethodName_Original()}. + * The content is the original method as-is from the reader. + * This step is omitted if the method is native, since it has no Java implementation. + * <li> A brand new implementation of {@code SomeClass.MethodName()} which calls to a + * non-existing method named {@code SomeClass_Delegate.MethodName()}. + * The implementation of this 'delegate' method is done in layoutlib_brigde. + * </ul> + * A method visitor is generally constructed to generate a single method; however + * here we might want to generate one or two depending on the context. To achieve + * that, the visitor here generates the 'original' method and acts as a no-op if + * no such method exists (e.g. when the original is a native method). + * The delegate method is generated after the {@code visitEnd} of the original method + * or by having the class adapter <em>directly</em> call {@link #generateDelegateCode()} + * for native methods. * <p/> - * A new annotation is added. + * When generating the 'delegate', the implementation generates a call to a class + * class named <code><className>_Delegate</code> with static methods matching + * the methods to be overridden here. The methods have the same return type. + * The argument type list is the same except the "this" reference is passed first + * for non-static methods. * <p/> - * Note that native methods have, by definition, no code so there's nothing a visitor - * can visit. That means the caller must call {@link #generateCode()} directly for + * A new annotation is added to these 'delegate' methods so that we can easily find them + * for automated testing. + * <p/> + * This class isn't intended to be generic or reusable. + * It is called by {@link DelegateClassAdapter}, which takes care of properly initializing + * the two method writers for the original and the delegate class, as needed, with their + * expected names. + * <p/> + * The class adapter also takes care of calling {@link #generateDelegateCode()} directly for * a native and use the visitor pattern for non-natives. + * Note that native methods have, by definition, no code so there's nothing a visitor + * can visit. * <p/> - * Instances of this class are not re-usable. You need a new instance for each method. + * Instances of this class are not re-usable. + * The class adapter creates a new instance for each method. */ -class DelegateMethodAdapter implements MethodVisitor { +class DelegateMethodAdapter2 implements MethodVisitor { - /** - * Suffix added to delegate classes. - */ + /** Suffix added to delegate classes. */ public static final String DELEGATE_SUFFIX = "_Delegate"; - private static String CONSTRUCTOR = "<init>"; - private static String CLASS_INIT = "<clinit>"; - - /** The parent method writer */ - private MethodVisitor mParentVisitor; - /** Flag to output the first line number. */ - private boolean mOutputFirstLineNumber = true; + /** The parent method writer to copy of the original method. + * Null when dealing with a native original method. */ + private MethodVisitor mOrgWriter; + /** The parent method writer to generate the delegating method. Never null. */ + private MethodVisitor mDelWriter; /** The original method descriptor (return type + argument types.) */ private String mDesc; /** True if the original method is static. */ @@ -70,17 +91,22 @@ class DelegateMethodAdapter implements MethodVisitor { private final String mMethodName; /** Logger object. */ private final Log mLog; - /** True if {@link #visitCode()} has been invoked. */ - private boolean mVisitCodeCalled; + + /** Array used to capture the first line number information from the original method + * and duplicate it in the delegate. */ + private Object[] mDelegateLineNumber; /** - * Creates a new {@link DelegateMethodAdapter} that will transform this method + * Creates a new {@link DelegateMethodAdapter2} that will transform this method * into a delegate call. * <p/> - * See {@link DelegateMethodAdapter} for more details. + * See {@link DelegateMethodAdapter2} for more details. * * @param log The logger object. Must not be null. - * @param mv the method visitor to which this adapter must delegate calls. + * @param mvOriginal The parent method writer to copy of the original method. + * Must be {@code null} when dealing with a native original method. + * @param mvDelegate The parent method writer to generate the delegating method. + * Must never be null. * @param className The internal class name of the class to visit, * e.g. <code>com/android/SomeClass$InnerClass</code>. * @param methodName The simple name of the method. @@ -88,28 +114,20 @@ class DelegateMethodAdapter implements MethodVisitor { * {@link Type#getArgumentTypes(String)}) * @param isStatic True if the method is declared static. */ - public DelegateMethodAdapter(Log log, - MethodVisitor mv, + public DelegateMethodAdapter2(Log log, + MethodVisitor mvOriginal, + MethodVisitor mvDelegate, String className, String methodName, String desc, boolean isStatic) { mLog = log; - mParentVisitor = mv; + mOrgWriter = mvOriginal; + mDelWriter = mvDelegate; mClassName = className; mMethodName = methodName; mDesc = desc; mIsStatic = isStatic; - - if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) { - // We're going to simplify by not supporting constructors. - // The only trick with a constructor is to find the proper super constructor - // and call it (and deciding if we should mirror the original method call to - // a custom constructor or call a default one.) - throw new UnsupportedOperationException( - String.format("Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", - className, methodName, desc)); - } } /** @@ -119,25 +137,25 @@ class DelegateMethodAdapter implements MethodVisitor { * (since they have no code to visit). * <p/> * Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to - * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern + * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern * invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then * this method will be invoked from {@link MethodVisitor#visitEnd()}. */ - public void generateCode() { + public void generateDelegateCode() { /* * The goal is to generate a call to a static delegate method. * If this method is non-static, the first parameter will be 'this'. * All the parameters must be passed and then the eventual return type returned. * * Example, let's say we have a method such as - * public void method_1(int a, Object b, ArrayList<String> c) { ... } + * public void myMethod(int a, Object b, ArrayList<String> c) { ... } * * We'll want to create a body that calls a delegate method like this: - * TheClass_Delegate.method_1(this, a, b, c); + * TheClass_Delegate.myMethod(this, a, b, c); * * If the method is non-static and the class name is an inner class (e.g. has $ in its * last segment), we want to push the 'this' of the outer class first: - * OuterClass_InnerClass_Delegate.method_1( + * OuterClass_InnerClass_Delegate.myMethod( * OuterClass.this, * OuterClass$InnerClass.this, * a, b, c); @@ -147,20 +165,22 @@ class DelegateMethodAdapter implements MethodVisitor { * * The generated class name is the current class name with "_Delegate" appended to it. * One thing to realize is that we don't care about generics -- since generic types - * are erased at runtime, they have no influence on the method name being called. + * are erased at build time, they have no influence on the method name being called. */ // Add our annotation - AnnotationVisitor aw = mParentVisitor.visitAnnotation( + AnnotationVisitor aw = mDelWriter.visitAnnotation( Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(), true); // visible at runtime - aw.visitEnd(); + if (aw != null) { + aw.visitEnd(); + } + + mDelWriter.visitCode(); - if (!mVisitCodeCalled) { - // If this is a direct call to generateCode() as done by DelegateClassAdapter - // for natives, visitCode() hasn't been called yet. - mParentVisitor.visitCode(); - mVisitCodeCalled = true; + if (mDelegateLineNumber != null) { + Object[] p = mDelegateLineNumber; + mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]); } ArrayList<Type> paramTypes = new ArrayList<Type>(); @@ -186,8 +206,8 @@ class DelegateMethodAdapter implements MethodVisitor { // that points to the outer class. // Push this.getField("this$0") on the call stack. - mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this - mParentVisitor.visitFieldInsn(Opcodes.GETFIELD, + mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this + mDelWriter.visitFieldInsn(Opcodes.GETFIELD, mClassName, // class where the field is defined "this$0", // field name outerType.getDescriptor()); // type of the field @@ -196,7 +216,7 @@ class DelegateMethodAdapter implements MethodVisitor { } // Push "this" for the instance method, which is always ALOAD 0 - mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); + mDelWriter.visitVarInsn(Opcodes.ALOAD, 0); maxStack++; pushedArg0 = true; paramTypes.add(Type.getObjectType(mClassName)); @@ -207,7 +227,7 @@ class DelegateMethodAdapter implements MethodVisitor { int maxLocals = pushedArg0 ? 1 : 0; for (Type t : argTypes) { int size = t.getSize(); - mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); + mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); maxLocals += size; maxStack += size; paramTypes.add(t); @@ -220,16 +240,16 @@ class DelegateMethodAdapter implements MethodVisitor { paramTypes.toArray(new Type[paramTypes.size()])); // Invoke the static delegate - mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, + mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC, delegateClassName, mMethodName, desc); Type returnType = Type.getReturnType(mDesc); - mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); + mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); - mParentVisitor.visitMaxs(maxStack, maxLocals); - mParentVisitor.visitEnd(); + mDelWriter.visitMaxs(maxStack, maxLocals); + mDelWriter.visitEnd(); // For debugging now. Maybe we should collect these and store them in // a text file for helping create the delegates. We could also compare @@ -241,42 +261,60 @@ class DelegateMethodAdapter implements MethodVisitor { /* Pass down to visitor writer. In this implementation, either do nothing. */ public void visitCode() { - mVisitCodeCalled = true; - mParentVisitor.visitCode(); + if (mOrgWriter != null) { + mOrgWriter.visitCode(); + } } /* * visitMaxs is called just before visitEnd if there was any code to rewrite. - * Skip the original. */ public void visitMaxs(int maxStack, int maxLocals) { + if (mOrgWriter != null) { + mOrgWriter.visitMaxs(maxStack, maxLocals); + } } - /** - * End of visiting. Generate the messaging code. - */ + /** End of visiting. Generate the delegating code. */ public void visitEnd() { - generateCode(); + if (mOrgWriter != null) { + mOrgWriter.visitEnd(); + } + generateDelegateCode(); } /* Writes all annotation from the original method. */ public AnnotationVisitor visitAnnotation(String desc, boolean visible) { - return mParentVisitor.visitAnnotation(desc, visible); + if (mOrgWriter != null) { + return mOrgWriter.visitAnnotation(desc, visible); + } else { + return null; + } } /* Writes all annotation default values from the original method. */ public AnnotationVisitor visitAnnotationDefault() { - return mParentVisitor.visitAnnotationDefault(); + if (mOrgWriter != null) { + return mOrgWriter.visitAnnotationDefault(); + } else { + return null; + } } public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { - return mParentVisitor.visitParameterAnnotation(parameter, desc, visible); + if (mOrgWriter != null) { + return mOrgWriter.visitParameterAnnotation(parameter, desc, visible); + } else { + return null; + } } /* Writes all attributes from the original method. */ public void visitAttribute(Attribute attr) { - mParentVisitor.visitAttribute(attr); + if (mOrgWriter != null) { + mOrgWriter.visitAttribute(attr); + } } /* @@ -284,75 +322,110 @@ class DelegateMethodAdapter implements MethodVisitor { * viewers can direct to the correct method, even if the content doesn't match. */ public void visitLineNumber(int line, Label start) { - if (mOutputFirstLineNumber) { - mParentVisitor.visitLineNumber(line, start); - mOutputFirstLineNumber = false; + // Capture the first line values for the new delegate method + if (mDelegateLineNumber == null) { + mDelegateLineNumber = new Object[] { line, start }; + } + if (mOrgWriter != null) { + mOrgWriter.visitLineNumber(line, start); } } public void visitInsn(int opcode) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitInsn(opcode); + } } public void visitLabel(Label label) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitLabel(label); + } } public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitTryCatchBlock(start, end, handler, type); + } } public void visitMethodInsn(int opcode, String owner, String name, String desc) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitMethodInsn(opcode, owner, name, desc); + } } public void visitFieldInsn(int opcode, String owner, String name, String desc) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitFieldInsn(opcode, owner, name, desc); + } } public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitFrame(type, nLocal, local, nStack, stack); + } } public void visitIincInsn(int var, int increment) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitIincInsn(var, increment); + } } public void visitIntInsn(int opcode, int operand) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitIntInsn(opcode, operand); + } } public void visitJumpInsn(int opcode, Label label) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitJumpInsn(opcode, label); + } } public void visitLdcInsn(Object cst) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitLdcInsn(cst); + } } public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitLocalVariable(name, desc, signature, start, end, index); + } } public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitLookupSwitchInsn(dflt, keys, labels); + } } public void visitMultiANewArrayInsn(String desc, int dims) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitMultiANewArrayInsn(desc, dims); + } } public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitTableSwitchInsn(min, max, dflt, labels); + } } public void visitTypeInsn(int opcode, String type) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitTypeInsn(opcode, type); + } } public void visitVarInsn(int opcode, int var) { - // Skip original code. + if (mOrgWriter != null) { + mOrgWriter.visitVarInsn(opcode, var); + } } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java index 9a57a4a..d70d028 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java @@ -31,7 +31,7 @@ class StubMethodAdapter implements MethodVisitor { private static String CONSTRUCTOR = "<init>"; private static String CLASS_INIT = "<clinit>"; - + /** The parent method writer */ private MethodVisitor mParentVisitor; /** The method return type. Can be null. */ @@ -40,7 +40,7 @@ class StubMethodAdapter implements MethodVisitor { private String mInvokeSignature; /** Flag to output the first line number. */ private boolean mOutputFirstLineNumber = true; - /** Flag that is true when implementing a constructor, to accept all original + /** Flag that is true when implementing a constructor, to accept all original * code calling the original super constructor. */ private boolean mIsInitMethod = false; @@ -55,12 +55,12 @@ class StubMethodAdapter implements MethodVisitor { mInvokeSignature = invokeSignature; mIsStatic = isStatic; mIsNative = isNative; - + if (CONSTRUCTOR.equals(methodName) || CLASS_INIT.equals(methodName)) { mIsInitMethod = true; } } - + private void generateInvoke() { /* Generates the code: * OverrideMethod.invoke("signature", mIsNative ? true : false, null or this); @@ -188,7 +188,7 @@ class StubMethodAdapter implements MethodVisitor { } mParentVisitor.visitMaxs(maxStack, maxLocals); } - + /** * End of visiting. * For non-constructor, generate the messaging code and the return statement @@ -250,6 +250,7 @@ class StubMethodAdapter implements MethodVisitor { generatePop(); generateInvoke(); mMessageGenerated = true; + //$FALL-THROUGH$ default: mParentVisitor.visitInsn(opcode); } @@ -346,5 +347,5 @@ class StubMethodAdapter implements MethodVisitor { mParentVisitor.visitVarInsn(opcode, var); } } - + } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java index e8b3ea8..6e120ce 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java @@ -130,7 +130,7 @@ public class DelegateClassAdapterTest { } /** - * {@link DelegateMethodAdapter} does not support overriding constructors yet, + * {@link DelegateMethodAdapter2} does not support overriding constructors yet, * so this should fail with an {@link UnsupportedOperationException}. * * Although not tested here, the message of the exception should contain the @@ -202,6 +202,7 @@ public class DelegateClassAdapterTest { // We'll delegate the "get" method of both the inner and outer class. HashSet<String> delegateMethods = new HashSet<String>(); delegateMethods.add("get"); + delegateMethods.add("privateMethod"); // Generate the delegate for the outer class. ClassWriter cwOuter = new ClassWriter(0 /*flags*/); @@ -234,6 +235,25 @@ public class DelegateClassAdapterTest { // The original Outer.get returns 1+10+20, // but the delegate makes it return 4+10+20 assertEquals(4+10+20, callGet(o2, 10, 20)); + assertEquals(1+10+20, callGet_Original(o2, 10, 20)); + + // The original Outer has a private method that is + // delegated. We should be able to call both the delegate + // and the original (which is now public). + assertEquals("outerPrivateMethod", + callMethod(o2, "privateMethod_Original", false /*makePublic*/)); + + // The original method is private, so by default we can't access it + boolean gotIllegalAccessException = false; + try { + callMethod(o2, "privateMethod", false /*makePublic*/); + } catch(IllegalAccessException e) { + gotIllegalAccessException = true; + } + assertTrue(gotIllegalAccessException); + // Try again, but now making it accessible + assertEquals("outerPrivate_Delegate", + callMethod(o2, "privateMethod", true /*makePublic*/)); // Check the inner class. Since it's not a static inner class, we need // to use the hidden constructor that takes the outer class as first parameter. @@ -246,6 +266,7 @@ public class DelegateClassAdapterTest { // The original Inner.get returns 3+10+20, // but the delegate makes it return 6+10+20 assertEquals(6+10+20, callGet(i2, 10, 20)); + assertEquals(3+10+20, callGet_Original(i2, 10, 20)); } }; cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); @@ -319,7 +340,7 @@ public class DelegateClassAdapterTest { } /** - * Accesses {@link OuterClass#get()} or {@link InnerClass#get() }via reflection. + * Accesses {@link OuterClass#get} or {@link InnerClass#get}via reflection. */ public int callGet(Object instance, int a, long b) throws Exception { Method m = instance.getClass().getMethod("get", @@ -330,6 +351,39 @@ public class DelegateClassAdapterTest { } /** + * Accesses the "_Original" methods for {@link OuterClass#get} + * or {@link InnerClass#get}via reflection. + */ + public int callGet_Original(Object instance, int a, long b) throws Exception { + Method m = instance.getClass().getMethod("get_Original", + new Class<?>[] { int.class, long.class } ); + + Object result = m.invoke(instance, new Object[] { a, b }); + return ((Integer) result).intValue(); + } + + /** + * Accesses the any declared method that takes no parameter via reflection. + */ + @SuppressWarnings("unchecked") + public <T> T callMethod(Object instance, String methodName, boolean makePublic) throws Exception { + Method m = instance.getClass().getDeclaredMethod(methodName, (Class<?>[])null); + + boolean wasAccessible = m.isAccessible(); + if (makePublic && !wasAccessible) { + m.setAccessible(true); + } + + Object result = m.invoke(instance, (Object[])null); + + if (makePublic && !wasAccessible) { + m.setAccessible(false); + } + + return (T) result; + } + + /** * Accesses {@link ClassWithNative#add(int, int)} via reflection. */ public int callAdd(Object instance, int a, int b) throws Exception { diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java index 9dc2f69..f083e76 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java @@ -39,10 +39,15 @@ public class OuterClass { public InnerClass() { } - // Inner.get returns 1+2=3 + a + b + // Inner.get returns 2 + 1 + a + b public int get(int a, long b) { return 2 + mOuterValue + a + (int) b; } } + + @SuppressWarnings("unused") + private String privateMethod() { + return "outerPrivateMethod"; + } } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java index 3252d87..774be8e 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java @@ -26,5 +26,9 @@ public class OuterClass_Delegate { public static int get(OuterClass instance, int a, long b) { return 4 + a + (int) b; } + + public static String privateMethod(OuterClass instance) { + return "outerPrivate_Delegate"; + } } |