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

Commit f9281b6d authored by Diego Perez's avatar Diego Perez Committed by android-build-merger
Browse files

Merge "Fix delegation of methods within inner static classes" into mnc-ub-dev...

Merge "Fix delegation of methods within inner static classes" into mnc-ub-dev am: 603544d4 am: 14a82638
am: 0b76d094

* commit '0b76d094':
  Fix delegation of methods within inner static classes
parents 01186abc 0b76d094
Loading
Loading
Loading
Loading
+21 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.tools.layoutlib.create;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

@@ -40,6 +41,7 @@ public class DelegateClassAdapter extends ClassVisitor {
    private final String mClassName;
    private final Set<String> mDelegateMethods;
    private final Log mLog;
    private boolean mIsStaticInnerClass;

    /**
     * Creates a new {@link DelegateClassAdapter} that can transform some methods
@@ -62,16 +64,30 @@ public class DelegateClassAdapter extends ClassVisitor {
        mLog = log;
        mClassName = className;
        mDelegateMethods = delegateMethods;
        // If this is an inner class, by default, we assume it's static. If it's not we will detect
        // by looking at the fields (see visitField)
        mIsStaticInnerClass = className.contains("$");
    }

    //----------------------------------
    // Methods from the ClassAdapter

    @Override
    public FieldVisitor visitField(int access, String name, String desc, String signature,
            Object value) {
        if (mIsStaticInnerClass && "this$0".equals(name)) {
            // Having a "this$0" field, proves that this class is not a static inner class.
            mIsStaticInnerClass = false;
        }

        return super.visitField(access, name, desc, signature, value);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc,
            String signature, String[] exceptions) {

        boolean isStatic = (access & Opcodes.ACC_STATIC) != 0;
        boolean isStaticMethod = (access & Opcodes.ACC_STATIC) != 0;
        boolean isNative = (access & Opcodes.ACC_NATIVE) != 0;

        boolean useDelegate = (isNative && mDelegateMethods.contains(ALL_NATIVES)) ||
@@ -96,7 +112,8 @@ public class DelegateClassAdapter extends ClassVisitor {
            MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);

            DelegateMethodAdapter a = new DelegateMethodAdapter(
                    mLog, null, mwDelegate, mClassName, name, desc, isStatic);
                    mLog, null, mwDelegate, mClassName, name, desc, isStaticMethod,
                    mIsStaticInnerClass);

            // A native has no code to visit, so we need to generate it directly.
            a.generateDelegateCode();
@@ -120,6 +137,7 @@ public class DelegateClassAdapter extends ClassVisitor {
                                                     desc, signature, exceptions);

        return new DelegateMethodAdapter(
                mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
                mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStaticMethod,
                mIsStaticInnerClass);
    }
}
+6 −2
Original line number Diff line number Diff line
@@ -85,6 +85,8 @@ class DelegateMethodAdapter extends MethodVisitor {
    private String mDesc;
    /** True if the original method is static. */
    private final boolean mIsStatic;
    /** True if the method is contained in a static inner class */
    private final boolean mIsStaticInnerClass;
    /** The internal class name (e.g. <code>com/android/SomeClass$InnerClass</code>.) */
    private final String mClassName;
    /** The method name. */
@@ -120,7 +122,8 @@ class DelegateMethodAdapter extends MethodVisitor {
            String className,
            String methodName,
            String desc,
            boolean isStatic) {
            boolean isStatic,
            boolean isStaticClass) {
        super(Opcodes.ASM4);
        mLog = log;
        mOrgWriter = mvOriginal;
@@ -129,6 +132,7 @@ class DelegateMethodAdapter extends MethodVisitor {
        mMethodName = methodName;
        mDesc = desc;
        mIsStatic = isStatic;
        mIsStaticInnerClass = isStaticClass;
    }

    /**
@@ -206,7 +210,7 @@ class DelegateMethodAdapter extends MethodVisitor {
        // by the 'this' of any outer class, if any.
        if (!mIsStatic) {

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

+58 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.junit.Assert.fail;
import com.android.tools.layoutlib.create.dataclass.ClassWithNative;
import com.android.tools.layoutlib.create.dataclass.OuterClass;
import com.android.tools.layoutlib.create.dataclass.OuterClass.InnerClass;
import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;

import org.junit.Before;
import org.junit.Test;
@@ -56,6 +57,8 @@ public class DelegateClassAdapterTest {
    private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName();
    private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" +
                                                   InnerClass.class.getSimpleName();
    private static final String STATIC_INNER_CLASS_NAME =
            OuterClass.class.getCanonicalName() + "$" + StaticInnerClass.class.getSimpleName();

    @Before
    public void setUp() throws Exception {
@@ -294,6 +297,61 @@ public class DelegateClassAdapterTest {
        }
    }

    @Test
    public void testDelegateStaticInner() throws Throwable {
        // We'll delegate the "get" method of both the inner and outer class.
        HashSet<String> delegateMethods = new HashSet<String>();
        delegateMethods.add("get");

        // Generate the delegate for the outer class.
        ClassWriter cwOuter = new ClassWriter(0 /*flags*/);
        String outerClassName = OUTER_CLASS_NAME.replace('.', '/');
        DelegateClassAdapter cvOuter = new DelegateClassAdapter(
                mLog, cwOuter, outerClassName, delegateMethods);
        ClassReader cr = new ClassReader(OUTER_CLASS_NAME);
        cr.accept(cvOuter, 0 /* flags */);

        // Generate the delegate for the static inner class.
        ClassWriter cwInner = new ClassWriter(0 /*flags*/);
        String innerClassName = STATIC_INNER_CLASS_NAME.replace('.', '/');
        DelegateClassAdapter cvInner = new DelegateClassAdapter(
                mLog, cwInner, innerClassName, delegateMethods);
        cr = new ClassReader(STATIC_INNER_CLASS_NAME);
        cr.accept(cvInner, 0 /* flags */);

        // Load the generated classes in a different class loader and try them
        ClassLoader2 cl2 = null;
        try {
            cl2 = new ClassLoader2() {
                @Override
                public void testModifiedInstance() throws Exception {

                    // Check the outer class
                    Class<?> outerClazz2 = loadClass(OUTER_CLASS_NAME);
                    Object o2 = outerClazz2.newInstance();
                    assertNotNull(o2);

                    // 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.
                    Class<?> innerClazz2 = loadClass(STATIC_INNER_CLASS_NAME);
                    Constructor<?> innerCons = innerClazz2.getConstructor();
                    Object i2 = innerCons.newInstance();
                    assertNotNull(i2);

                    // The original StaticInner.get returns 100+10+20,
                    // but the delegate makes it return 6+10+20
                    assertEquals(6+10+20, callGet(i2, 10, 20));
                    assertEquals(100+10+20, callGet_Original(i2, 10, 20));
                }
            };
            cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray());
            cl2.add(STATIC_INNER_CLASS_NAME, cwInner.toByteArray());
            cl2.testModifiedInstance();
        } catch (Throwable t) {
            throw dumpGeneratedClass(t, cl2);
        }
    }

    //-------

    /**
+10 −0
Original line number Diff line number Diff line
@@ -45,6 +45,16 @@ public class OuterClass {
        }
    }

    public static class StaticInnerClass {
        public StaticInnerClass() {
        }

        // StaticInnerClass.get returns 100 + a + b
        public int get(int a, long b) {
            return 100 + a + (int) b;
        }
    }

    @SuppressWarnings("unused")
    private String privateMethod() {
        return "outerPrivateMethod";
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.tools.layoutlib.create.dataclass;

import com.android.tools.layoutlib.create.DelegateClassAdapterTest;
import com.android.tools.layoutlib.create.dataclass.OuterClass.StaticInnerClass;

/**
 * Used by {@link DelegateClassAdapterTest}.
 */
public class OuterClass_StaticInnerClass_Delegate {
    // The delegate override of Inner.get return 6 + a + b
    public static int get(StaticInnerClass inner, int a, long b) {
        return 6 + a + (int) b;
    }
}