Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e2b41b0d authored by Raphael Moll's avatar Raphael Moll Committed by Android Code Review
Browse files

Merge "Laoutlib_creator: keep original of delegate methods."

parents bf5adfa5 865c3bef
Loading
Loading
Loading
Loading
+29 −5
Original line number Diff line number Diff line
@@ -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 -
--------------

+3 −0
Original line number Diff line number Diff line
@@ -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[] {
    };
+50 −12
Original line number Diff line number Diff line
@@ -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 (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));
            }
        }

        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.generateCode();
            } else {
                return a;
            }
            a.generateDelegateCode();

            return mwDelegate;
        }
        return mw;

        // 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;
    }
}
+164 −91

File changed and moved.

Preview size limit exceeded, changes collapsed.

+7 −6
Original line number Diff line number Diff line
@@ -250,6 +250,7 @@ class StubMethodAdapter implements MethodVisitor {
                generatePop();
                generateInvoke();
                mMessageGenerated = true;
                //$FALL-THROUGH$
            default:
                mParentVisitor.visitInsn(opcode);
            }
Loading