Merge "Fix delegation of methods within inner static classes" into mnc-ub-dev am: 603544d4c4
am: 14a826380d
* commit '14a826380d12f4a5c48c1f9fb97b3b24ebddf0c7':
Fix delegation of methods within inner static classes
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
//-------
|
||||
|
||||
/**
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user