FileDocCategorySizeDatePackage
DelegateMethodAdapter.javaAPI DocAndroid 5.1 API17212Thu Mar 12 22:22:44 GMT 2015com.android.tools.layoutlib.create

DelegateMethodAdapter

public class DelegateMethodAdapter extends org.objectweb.asm.MethodVisitor
This method adapter generates delegate methods.

Given a method {@code SomeClass.MethodName()}, this generates 1 or 2 methods:

  • 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.
  • 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.
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 directly call {@link #generateDelegateCode()} for native methods.

When generating the 'delegate', the implementation generates a call to a class class named <className>_Delegate 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.

A new annotation is added to these 'delegate' methods so that we can easily find them for automated testing.

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.

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.

Instances of this class are not re-usable. The class adapter creates a new instance for each method.

Fields Summary
public static final String
DELEGATE_SUFFIX
Suffix added to delegate classes.
private org.objectweb.asm.MethodVisitor
mOrgWriter
The parent method writer to copy of the original method. Null when dealing with a native original method.
private org.objectweb.asm.MethodVisitor
mDelWriter
The parent method writer to generate the delegating method. Never null.
private String
mDesc
The original method descriptor (return type + argument types.)
private final boolean
mIsStatic
True if the original method is static.
private final String
mClassName
The internal class name (e.g. com/android/SomeClass$InnerClass.)
private final String
mMethodName
The method name.
private final Log
mLog
Logger object.
private Object[]
mDelegateLineNumber
Array used to capture the first line number information from the original method and duplicate it in the delegate.
Constructors Summary
public DelegateMethodAdapter(Log log, org.objectweb.asm.MethodVisitor mvOriginal, org.objectweb.asm.MethodVisitor mvDelegate, String className, String methodName, String desc, boolean isStatic)
Creates a new {@link DelegateMethodAdapter} that will transform this method into a delegate call.

See {@link DelegateMethodAdapter} for more details.

param
log The logger object. Must not be null.
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. com/android/SomeClass$InnerClass.
param
methodName The simple name of the method.
param
desc A method descriptor (c.f. {@link Type#getReturnType(String)} + {@link Type#getArgumentTypes(String)})
param
isStatic True if the method is declared static.


                                                                                                                                                          
      
             
             
             
             
             
              
        super(Opcodes.ASM4);
        mLog = log;
        mOrgWriter = mvOriginal;
        mDelWriter = mvDelegate;
        mClassName = className;
        mMethodName = methodName;
        mDesc = desc;
        mIsStatic = isStatic;
    
Methods Summary
public voidgenerateDelegateCode()
Generates the new code for the method.

For native methods, this must be invoked directly by {@link DelegateClassAdapter} (since they have no code to visit).

Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to return this instance of {@link DelegateMethodAdapter} 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()}.

        /*
         * 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 myMethod(int a, Object b, ArrayList<String> c) { ... }
         *
         * We'll want to create a body that calls a delegate method like this:
         *   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.myMethod(
         *     OuterClass.this,
         *     OuterClass$InnerClass.this,
         *     a, b, c);
         *
         * Only one level of inner class is supported right now, for simplicity and because
         * we don't need more.
         *
         * 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 build time, they have no influence on the method name being called.
         */

        // Add our annotation
        AnnotationVisitor aw = mDelWriter.visitAnnotation(
                Type.getObjectType(Type.getInternalName(LayoutlibDelegate.class)).toString(),
                true); // visible at runtime
        if (aw != null) {
            aw.visitEnd();
        }

        mDelWriter.visitCode();

        if (mDelegateLineNumber != null) {
            Object[] p = mDelegateLineNumber;
            mDelWriter.visitLineNumber((Integer) p[0], (Label) p[1]);
        }

        ArrayList<Type> paramTypes = new ArrayList<Type>();
        String delegateClassName = mClassName + DELEGATE_SUFFIX;
        boolean pushedArg0 = false;
        int maxStack = 0;

        // Check if the last segment of the class name has inner an class.
        // Right now we only support one level of inner classes.
        Type outerType = null;
        int slash = mClassName.lastIndexOf('/");
        int dol = mClassName.lastIndexOf('$");
        if (dol != -1 && dol > slash && dol == mClassName.indexOf('$")) {
            String outerClass = mClassName.substring(0, dol);
            outerType = Type.getObjectType(outerClass);

            // Change a delegate class name to "com/foo/Outer_Inner_Delegate"
            delegateClassName = delegateClassName.replace('$", '_");
        }

        // For an instance method (e.g. non-static), push the 'this' preceded
        // by the 'this' of any outer class, if any.
        if (!mIsStatic) {

            if (outerType != null) {
                // The first-level inner class has a package-protected member called 'this$0'
                // that points to the outer class.

                // Push this.getField("this$0") on the call stack.
                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
                maxStack++;
                paramTypes.add(outerType);

            }

            // Push "this" for the instance method, which is always ALOAD 0
            mDelWriter.visitVarInsn(Opcodes.ALOAD, 0);
            maxStack++;
            pushedArg0 = true;
            paramTypes.add(Type.getObjectType(mClassName));
        }

        // Push all other arguments. Start at arg 1 if we already pushed 'this' above.
        Type[] argTypes = Type.getArgumentTypes(mDesc);
        int maxLocals = pushedArg0 ? 1 : 0;
        for (Type t : argTypes) {
            int size = t.getSize();
            mDelWriter.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals);
            maxLocals += size;
            maxStack += size;
            paramTypes.add(t);
        }

        // Construct the descriptor of the delegate based on the parameters
        // we pushed on the call stack. The return type remains unchanged.
        String desc = Type.getMethodDescriptor(
                Type.getReturnType(mDesc),
                paramTypes.toArray(new Type[paramTypes.size()]));

        // Invoke the static delegate
        mDelWriter.visitMethodInsn(Opcodes.INVOKESTATIC,
                delegateClassName,
                mMethodName,
                desc);

        Type returnType = Type.getReturnType(mDesc);
        mDelWriter.visitInsn(returnType.getOpcode(Opcodes.IRETURN));

        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
        // the text file to a golden and break the build on unsupported changes
        // or regressions. Even better we could fancy-print something that looks
        // like the expected Java method declaration.
        mLog.debug("Delegate: %1$s # %2$s %3$s", delegateClassName, mMethodName, desc);
    
public org.objectweb.asm.AnnotationVisitorvisitAnnotation(java.lang.String desc, boolean visible)

        if (mOrgWriter != null) {
            return mOrgWriter.visitAnnotation(desc, visible);
        } else {
            return null;
        }
    
public org.objectweb.asm.AnnotationVisitorvisitAnnotationDefault()

        if (mOrgWriter != null) {
            return mOrgWriter.visitAnnotationDefault();
        } else {
            return null;
        }
    
public voidvisitAttribute(org.objectweb.asm.Attribute attr)

        if (mOrgWriter != null) {
            mOrgWriter.visitAttribute(attr);
        }
    
public voidvisitCode()

        if (mOrgWriter != null) {
            mOrgWriter.visitCode();
        }
    
public voidvisitEnd()
End of visiting. Generate the delegating code.

        if (mOrgWriter != null) {
            mOrgWriter.visitEnd();
        }
        generateDelegateCode();
    
public voidvisitFieldInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String desc)

        if (mOrgWriter != null) {
            mOrgWriter.visitFieldInsn(opcode, owner, name, desc);
        }
    
public voidvisitFrame(int type, int nLocal, java.lang.Object[] local, int nStack, java.lang.Object[] stack)

        if (mOrgWriter != null) {
            mOrgWriter.visitFrame(type, nLocal, local, nStack, stack);
        }
    
public voidvisitIincInsn(int var, int increment)

        if (mOrgWriter != null) {
            mOrgWriter.visitIincInsn(var, increment);
        }
    
public voidvisitInsn(int opcode)

        if (mOrgWriter != null) {
            mOrgWriter.visitInsn(opcode);
        }
    
public voidvisitIntInsn(int opcode, int operand)

        if (mOrgWriter != null) {
            mOrgWriter.visitIntInsn(opcode, operand);
        }
    
public voidvisitJumpInsn(int opcode, org.objectweb.asm.Label label)

        if (mOrgWriter != null) {
            mOrgWriter.visitJumpInsn(opcode, label);
        }
    
public voidvisitLabel(org.objectweb.asm.Label label)

        if (mOrgWriter != null) {
            mOrgWriter.visitLabel(label);
        }
    
public voidvisitLdcInsn(java.lang.Object cst)

        if (mOrgWriter != null) {
            mOrgWriter.visitLdcInsn(cst);
        }
    
public voidvisitLineNumber(int line, org.objectweb.asm.Label start)

        // 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 voidvisitLocalVariable(java.lang.String name, java.lang.String desc, java.lang.String signature, org.objectweb.asm.Label start, org.objectweb.asm.Label end, int index)

        if (mOrgWriter != null) {
            mOrgWriter.visitLocalVariable(name, desc, signature, start, end, index);
        }
    
public voidvisitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, org.objectweb.asm.Label[] labels)

        if (mOrgWriter != null) {
            mOrgWriter.visitLookupSwitchInsn(dflt, keys, labels);
        }
    
public voidvisitMaxs(int maxStack, int maxLocals)

        if (mOrgWriter != null) {
            mOrgWriter.visitMaxs(maxStack, maxLocals);
        }
    
public voidvisitMethodInsn(int opcode, java.lang.String owner, java.lang.String name, java.lang.String desc)

        if (mOrgWriter != null) {
            mOrgWriter.visitMethodInsn(opcode, owner, name, desc);
        }
    
public voidvisitMultiANewArrayInsn(java.lang.String desc, int dims)

        if (mOrgWriter != null) {
            mOrgWriter.visitMultiANewArrayInsn(desc, dims);
        }
    
public org.objectweb.asm.AnnotationVisitorvisitParameterAnnotation(int parameter, java.lang.String desc, boolean visible)

        if (mOrgWriter != null) {
            return mOrgWriter.visitParameterAnnotation(parameter, desc, visible);
        } else {
            return null;
        }
    
public voidvisitTableSwitchInsn(int min, int max, org.objectweb.asm.Label dflt, org.objectweb.asm.Label[] labels)

        if (mOrgWriter != null) {
            mOrgWriter.visitTableSwitchInsn(min, max, dflt, labels);
        }
    
public voidvisitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label handler, java.lang.String type)

        if (mOrgWriter != null) {
            mOrgWriter.visitTryCatchBlock(start, end, handler, type);
        }
    
public voidvisitTypeInsn(int opcode, java.lang.String type)

        if (mOrgWriter != null) {
            mOrgWriter.visitTypeInsn(opcode, type);
        }
    
public voidvisitVarInsn(int opcode, int var)

        if (mOrgWriter != null) {
            mOrgWriter.visitVarInsn(opcode, var);
        }