summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
authorRaphael Moll <ralf@android.com>2011-06-23 14:30:25 -0700
committerAndroid Git Automerger <android-git-automerger@android.com>2011-06-23 14:30:25 -0700
commitb02367778d451e38509ca1fc3fbc1d01dce6af1e (patch)
treef12ac14175b927f9bf93b6bd95822c908f5aba97 /tools/layoutlib
parent996bdbaaf0b532c77c10a1efea3b17eb50f416db (diff)
parentf9705ef6d3f1502991dd312bfdbfb93cb07fe672 (diff)
downloadframeworks_base-b02367778d451e38509ca1fc3fbc1d01dce6af1e.zip
frameworks_base-b02367778d451e38509ca1fc3fbc1d01dce6af1e.tar.gz
frameworks_base-b02367778d451e38509ca1fc3fbc1d01dce6af1e.tar.bz2
am f9705ef6: am 2b5f2d01: am e2b41b0d: Merge "Laoutlib_creator: keep original of delegate methods."
* commit 'f9705ef6d3f1502991dd312bfdbfb93cb07fe672': Laoutlib_creator: keep original of delegate methods.
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/create/README.txt34
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java3
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java62
-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.java13
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java58
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java7
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java4
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 5c60318..233f72e 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;
@@ -158,6 +160,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>&lt;className&gt;_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>&lt;className&gt;_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";
+ }
}