am 2b5f2d01: am e2b41b0d: Merge "Laoutlib_creator: keep original of delegate methods."

* commit '2b5f2d01558ba338042f486c754f63873c4061fe':
  Laoutlib_creator: keep original of delegate methods.
This commit is contained in:
Raphael Moll
2011-06-22 18:05:22 -07:00
committed by Android Git Automerger
8 changed files with 319 additions and 117 deletions

View File

@ -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 -
--------------

View File

@ -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[] {
};

View File

@ -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;
}
}

View File

@ -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();
}
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;
mDelWriter.visitCode();
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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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",
@ -329,6 +350,39 @@ public class DelegateClassAdapterTest {
return ((Integer) result).intValue();
}
/**
* 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.
*/

View File

@ -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";
}
}

View File

@ -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";
}
}