diff options
author | Raphael <raphael@google.com> | 2011-01-27 16:48:27 -0800 |
---|---|---|
committer | Raphael <raphael@google.com> | 2011-01-31 13:16:04 -0800 |
commit | 811820f440b24db200e66874d42331023b7cd389 (patch) | |
tree | 474c8f6c1d1f5df3a593c8cebe4bfd8c4dbf9258 /tools | |
parent | c1eb127f4e19c1f86e4b18c2c1daebe2404eeb93 (diff) | |
download | frameworks_base-811820f440b24db200e66874d42331023b7cd389.zip frameworks_base-811820f440b24db200e66874d42331023b7cd389.tar.gz frameworks_base-811820f440b24db200e66874d42331023b7cd389.tar.bz2 |
LayoutLib.Create: support Outer_Inner_Delegate renaming.
When generating delegates, LayoutLib.Create support renaming
inner classes. Only one level of inner class is supported.
The method Outer$Inner#get(...) generates a call to:
static Outer_Inner_Delegate#get(Outer instance, Outer$Inner instance, ...)
Change-Id: Ie70f2b8e5e5f311ed9c7f26b7f64637ae6157a51
Diffstat (limited to 'tools')
7 files changed, 446 insertions, 115 deletions
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java index c7968a4..8d7f016 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java @@ -27,6 +27,8 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; 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. @@ -124,7 +126,7 @@ class DelegateMethodAdapter implements MethodVisitor { public void generateCode() { /* * The goal is to generate a call to a static delegate method. - * If this method is not-static, the first parameter will be this. + * 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 @@ -133,9 +135,19 @@ class DelegateMethodAdapter implements MethodVisitor { * We'll want to create a body that calls a delegate method like this: * TheClass_Delegate.method_1(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.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 runtime, they have no influence on the method being called. + * are erased at runtime, they have no influence on the method name being called. */ // Add our annotation @@ -151,34 +163,61 @@ class DelegateMethodAdapter implements MethodVisitor { mVisitCodeCalled = true; } - int numVars = 0; + ArrayList<Type> paramTypes = new ArrayList<Type>(); + String delegateClassName = mClassName + DELEGATE_SUFFIX; + boolean pushedArg0 = false; + int maxStack = 0; - // Push "this" for an instance method, which is always ALOAD 0 + // For an instance method (e.g. non-static), push the 'this' preceded + // by the 'this' of any outer class, if any. if (!mIsStatic) { - mParentVisitor.visitVarInsn(Opcodes.ALOAD, numVars++); + // Check if the last segment of the class name has inner an class. + // Right now we only support one level of inner classes. + int slash = mClassName.lastIndexOf('/'); + int dol = mClassName.lastIndexOf('$'); + if (dol != -1 && dol > slash && dol == mClassName.indexOf('$')) { + String outerClass = mClassName.substring(0, dol); + Type outerType = Type.getObjectType(outerClass); + + // Change a delegate class name to "com/foo/Outer_Inner_Delegate" + delegateClassName = delegateClassName.replace('$', '_'); + + // 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. + mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); // var 0 = this + mParentVisitor.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 + mParentVisitor.visitVarInsn(Opcodes.ALOAD, 0); + maxStack++; + pushedArg0 = true; + paramTypes.add(Type.getObjectType(mClassName)); } - // Push all other arguments + // 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(); - mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), numVars); - numVars += size; - } - - // Construct the descriptor of the delegate. For a static method, it's the same - // however for an instance method we need to pass the 'this' reference first - String desc = mDesc; - if (!mIsStatic) { - Type[] argTypes2 = new Type[argTypes.length + 1]; - - argTypes2[0] = Type.getObjectType(mClassName); - System.arraycopy(argTypes, 0, argTypes2, 1, argTypes.length); - - desc = Type.getMethodDescriptor(Type.getReturnType(mDesc), argTypes2); + mParentVisitor.visitVarInsn(t.getOpcode(Opcodes.ILOAD), maxLocals); + maxLocals += size; + maxStack += size; + paramTypes.add(t); } - String delegateClassName = mClassName + DELEGATE_SUFFIX; + // 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 mParentVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, @@ -189,7 +228,7 @@ class DelegateMethodAdapter implements MethodVisitor { Type returnType = Type.getReturnType(mDesc); mParentVisitor.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); - mParentVisitor.visitMaxs(numVars, numVars); + mParentVisitor.visitMaxs(maxStack, maxLocals); mParentVisitor.visitEnd(); // For debugging now. Maybe we should collect these and store them in diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java index 7d80796..e8b3ea8 100644 --- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java @@ -24,25 +24,38 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; 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 org.junit.Before; import org.junit.Test; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; public class DelegateClassAdapterTest { private MockLog mLog; - private static final String CLASS_NAME = - DelegateClassAdapterTest.class.getCanonicalName() + "$" + - ClassWithNative.class.getSimpleName(); + private static final String NATIVE_CLASS_NAME = ClassWithNative.class.getCanonicalName(); + private static final String OUTER_CLASS_NAME = OuterClass.class.getCanonicalName(); + private static final String INNER_CLASS_NAME = OuterClass.class.getCanonicalName() + "$" + + InnerClass.class.getSimpleName(); @Before public void setUp() throws Exception { @@ -55,11 +68,11 @@ public class DelegateClassAdapterTest { */ @SuppressWarnings("unchecked") @Test - public void testNoOp() throws Exception { + public void testNoOp() throws Throwable { // create an instance of the class that will be modified // (load the class in a distinct class loader so that we can trash its definition later) ClassLoader cl1 = new ClassLoader(this.getClass().getClassLoader()) { }; - Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(CLASS_NAME); + Class<ClassWithNative> clazz1 = (Class<ClassWithNative>) cl1.loadClass(NATIVE_CLASS_NAME); ClassWithNative instance1 = clazz1.newInstance(); assertEquals(42, instance1.add(20, 22)); try { @@ -73,42 +86,47 @@ public class DelegateClassAdapterTest { ClassWriter cw = new ClassWriter(0 /*flags*/); HashSet<String> delegateMethods = new HashSet<String>(); - String internalClassName = CLASS_NAME.replace('.', '/'); + String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); DelegateClassAdapter cv = new DelegateClassAdapter( mLog, cw, internalClassName, delegateMethods); - ClassReader cr = new ClassReader(CLASS_NAME); + ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); cr.accept(cv, 0 /* flags */); // Load the generated class in a different class loader and try it again - final byte[] bytes = cw.toByteArray(); - ClassLoader2 cl2 = new ClassLoader2(bytes) { - @Override - public void testModifiedInstance() throws Exception { - Class<?> clazz2 = loadClass(CLASS_NAME); - Object i2 = clazz2.newInstance(); - assertNotNull(i2); - assertEquals(42, callAdd(i2, 20, 22)); + ClassLoader2 cl2 = null; + try { + cl2 = new ClassLoader2() { + @Override + public void testModifiedInstance() throws Exception { + Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME); + Object i2 = clazz2.newInstance(); + assertNotNull(i2); + assertEquals(42, callAdd(i2, 20, 22)); - try { - callCallNativeInstance(i2, 10, 3.1415, new Object[0]); - fail("Test should have failed to invoke callTheNativeMethod [2]"); - } catch (InvocationTargetException e) { - // This is expected to fail since the native method has NOT been - // overridden here. - assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass()); + try { + callCallNativeInstance(i2, 10, 3.1415, new Object[0]); + fail("Test should have failed to invoke callTheNativeMethod [2]"); + } catch (InvocationTargetException e) { + // This is expected to fail since the native method has NOT been + // overridden here. + assertEquals(UnsatisfiedLinkError.class, e.getCause().getClass()); + } + + // Check that the native method does NOT have the new annotation + Method[] m = clazz2.getDeclaredMethods(); + assertEquals("native_instance", m[2].getName()); + assertTrue(Modifier.isNative(m[2].getModifiers())); + Annotation[] a = m[2].getAnnotations(); + assertEquals(0, a.length); } - - // Check that the native method does NOT have the new annotation - Method[] m = clazz2.getDeclaredMethods(); - assertEquals("native_instance", m[2].getName()); - assertTrue(Modifier.isNative(m[2].getModifiers())); - Annotation[] a = m[2].getAnnotations(); - assertEquals(0, a.length); - } - }; - cl2.testModifiedInstance(); + }; + cl2.add(NATIVE_CLASS_NAME, cw); + cl2.testModifiedInstance(); + } catch (Throwable t) { + throw dumpGeneratedClass(t, cl2); + } } /** @@ -122,38 +140,37 @@ public class DelegateClassAdapterTest { public void testConstructorsNotSupported() throws IOException { ClassWriter cw = new ClassWriter(0 /*flags*/); - String internalClassName = CLASS_NAME.replace('.', '/'); + String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); HashSet<String> delegateMethods = new HashSet<String>(); delegateMethods.add("<init>"); DelegateClassAdapter cv = new DelegateClassAdapter( mLog, cw, internalClassName, delegateMethods); - ClassReader cr = new ClassReader(CLASS_NAME); + ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); cr.accept(cv, 0 /* flags */); } @Test - public void testDelegateNative() throws Exception { + public void testDelegateNative() throws Throwable { ClassWriter cw = new ClassWriter(0 /*flags*/); - String internalClassName = CLASS_NAME.replace('.', '/'); + String internalClassName = NATIVE_CLASS_NAME.replace('.', '/'); HashSet<String> delegateMethods = new HashSet<String>(); delegateMethods.add(DelegateClassAdapter.ALL_NATIVES); DelegateClassAdapter cv = new DelegateClassAdapter( mLog, cw, internalClassName, delegateMethods); - ClassReader cr = new ClassReader(CLASS_NAME); + ClassReader cr = new ClassReader(NATIVE_CLASS_NAME); cr.accept(cv, 0 /* flags */); // Load the generated class in a different class loader and try it - final byte[] bytes = cw.toByteArray(); - + ClassLoader2 cl2 = null; try { - ClassLoader2 cl2 = new ClassLoader2(bytes) { + cl2 = new ClassLoader2() { @Override public void testModifiedInstance() throws Exception { - Class<?> clazz2 = loadClass(CLASS_NAME); + Class<?> clazz2 = loadClass(NATIVE_CLASS_NAME); Object i2 = clazz2.newInstance(); assertNotNull(i2); @@ -173,48 +190,105 @@ public class DelegateClassAdapterTest { assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName()); } }; + cl2.add(NATIVE_CLASS_NAME, cw); cl2.testModifiedInstance(); + } catch (Throwable t) { + throw dumpGeneratedClass(t, cl2); + } + } + + @Test + public void testDelegateInner() 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 inner class. + ClassWriter cwInner = new ClassWriter(0 /*flags*/); + String innerClassName = INNER_CLASS_NAME.replace('.', '/'); + DelegateClassAdapter cvInner = new DelegateClassAdapter( + mLog, cwInner, innerClassName, delegateMethods); + cr = new ClassReader(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); + + // 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)); + + // 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(INNER_CLASS_NAME); + Constructor<?> innerCons = innerClazz2.getConstructor( + new Class<?>[] { outerClazz2 }); + Object i2 = innerCons.newInstance(new Object[] { o2 }); + assertNotNull(i2); - // This code block is useful for debugging. However to make it work you need to - // pull in the org.objectweb.asm.util.TraceClassVisitor class and associated - // utilities which are found in the ASM source jar. - // - // } catch (Throwable t) { - // For debugging, dump the bytecode of the class in case of unexpected error. - // StringWriter sw = new StringWriter(); - // PrintWriter pw = new PrintWriter(sw); - // TraceClassVisitor tcv = new TraceClassVisitor(pw); - // ClassReader cr2 = new ClassReader(bytes); - // cr2.accept(tcv, 0 /* flags */); - // String msg = "\n" + t.getClass().getCanonicalName(); - // if (t.getMessage() != null) { - // msg += ": " + t.getMessage(); - // } - // msg = msg + "\nBytecode dump:\n" + sw.toString(); - // // Re-throw exception with new message - // RuntimeException ex = new RuntimeException(msg, t); - // throw ex; - } finally { + // 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)); + } + }; + cl2.add(OUTER_CLASS_NAME, cwOuter.toByteArray()); + cl2.add(INNER_CLASS_NAME, cwInner.toByteArray()); + cl2.testModifiedInstance(); + } catch (Throwable t) { + throw dumpGeneratedClass(t, cl2); } } //------- /** - * A class loader than can define and instantiate our dummy {@link ClassWithNative}. + * A class loader than can define and instantiate our modified classes. + * <p/> + * The trick here is that this class loader will test our <em>modified</em> version + * of the classes, the one with the delegate calls. * <p/> - * The trick here is that this class loader will test our modified version of ClassWithNative. * Trying to do so in the original class loader generates all sort of link issues because * there are 2 different definitions of the same class name. This class loader will * define and load the class when requested by name and provide helpers to access the * instance methods via reflection. */ private abstract class ClassLoader2 extends ClassLoader { - private final byte[] mClassWithNative; - public ClassLoader2(byte[] classWithNative) { + private final Map<String, byte[]> mClassDefs = new HashMap<String, byte[]>(); + + public ClassLoader2() { super(null); - mClassWithNative = classWithNative; + } + + public ClassLoader2 add(String className, byte[] definition) { + mClassDefs.put(className, definition); + return this; + } + + public ClassLoader2 add(String className, ClassWriter rewrittenClass) { + mClassDefs.put(className, rewrittenClass.toByteArray()); + return this; + } + + private Set<Entry<String, byte[]>> getByteCode() { + return mClassDefs.entrySet(); } @SuppressWarnings("unused") @@ -224,9 +298,10 @@ public class DelegateClassAdapterTest { return super.findClass(name); } catch (ClassNotFoundException e) { - if (CLASS_NAME.equals(name)) { + byte[] def = mClassDefs.get(name); + if (def != null) { // Load the modified ClassWithNative from its bytes representation. - return defineClass(CLASS_NAME, mClassWithNative, 0, mClassWithNative.length); + return defineClass(name, def, 0, def.length); } try { @@ -244,6 +319,17 @@ public class DelegateClassAdapterTest { } /** + * 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", + new Class<?>[] { int.class, long.class } ); + + Object result = m.invoke(instance, new Object[] { a, b }); + return ((Integer) result).intValue(); + } + + /** * Accesses {@link ClassWithNative#add(int, int)} via reflection. */ public int callAdd(Object instance, int a, int b) throws Exception { @@ -271,34 +357,53 @@ public class DelegateClassAdapterTest { } /** - * Dummy test class with a native method. - * The native method is not defined and any attempt to invoke it will - * throw an {@link UnsatisfiedLinkError}. + * For debugging, it's useful to dump the content of the generated classes + * along with the exception that was generated. + * + * However to make it work you need to pull in the org.objectweb.asm.util.TraceClassVisitor + * class and associated utilities which are found in the ASM source jar. Since we don't + * want that dependency in the source code, we only put it manually for development and + * access the TraceClassVisitor via reflection if present. + * + * @param t The exception thrown by {@link ClassLoader2#testModifiedInstance()} + * @param cl2 The {@link ClassLoader2} instance with the generated bytecode. + * @return Either original {@code t} or a new wrapper {@link Throwable} */ - public static class ClassWithNative { - public ClassWithNative() { - } - - public int add(int a, int b) { - return a + b; - } + private Throwable dumpGeneratedClass(Throwable t, ClassLoader2 cl2) { + try { + // For debugging, dump the bytecode of the class in case of unexpected error + // if we can find the TraceClassVisitor class. + Class<?> tcvClass = Class.forName("org.objectweb.asm.util.TraceClassVisitor"); + + StringBuilder sb = new StringBuilder(); + sb.append('\n').append(t.getClass().getCanonicalName()); + if (t.getMessage() != null) { + sb.append(": ").append(t.getMessage()); + } + + for (Entry<String, byte[]> entry : cl2.getByteCode()) { + String className = entry.getKey(); + byte[] bytes = entry.getValue(); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + // next 2 lines do: TraceClassVisitor tcv = new TraceClassVisitor(pw); + Constructor<?> cons = tcvClass.getConstructor(new Class<?>[] { pw.getClass() }); + Object tcv = cons.newInstance(new Object[] { pw }); + ClassReader cr2 = new ClassReader(bytes); + cr2.accept((ClassVisitor) tcv, 0 /* flags */); + + sb.append("\nBytecode dump: <").append(className).append(">:\n") + .append(sw.toString()); + } - public int callNativeInstance(int a, double d, Object[] o) { - return native_instance(a, d, o); + // Re-throw exception with new message + RuntimeException ex = new RuntimeException(sb.toString(), t); + return ex; + } catch (Throwable ignore) { + // In case of problem, just throw the original exception as-is. + return t; } - - private native int native_instance(int a, double d, Object[] o); } - /** - * The delegate that receives the call to {@link ClassWithNative}'s overridden methods. - */ - public static class ClassWithNative_Delegate { - public static int native_instance(ClassWithNative instance, int a, double d, Object[] o) { - if (o != null && o.length > 0) { - o[0] = instance; - } - return (int)(a + d); - } - } } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java new file mode 100644 index 0000000..c314853 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative.java @@ -0,0 +1,45 @@ +/* + * 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. + * 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; + +/** + * Dummy test class with a native method. + * The native method is not defined and any attempt to invoke it will + * throw an {@link UnsatisfiedLinkError}. + * + * Used by {@link DelegateClassAdapterTest}. + */ +public class ClassWithNative { + public ClassWithNative() { + } + + public int add(int a, int b) { + return a + b; + } + + // Note: it's good to have a long or double for testing parameters since they take + // 2 slots in the stack/locals maps. + + public int callNativeInstance(int a, double d, Object[] o) { + return native_instance(a, d, o); + } + + private native int native_instance(int a, double d, Object[] o); +} + diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java new file mode 100644 index 0000000..a3d4dc6 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/ClassWithNative_Delegate.java @@ -0,0 +1,34 @@ +/* + * 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. + * 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; + +/** + * The delegate that receives the call to {@link ClassWithNative_Delegate}'s overridden methods. + * + * Used by {@link DelegateClassAdapterTest}. + */ +public class ClassWithNative_Delegate { + public static int native_instance(ClassWithNative instance, int a, double d, Object[] o) { + if (o != null && o.length > 0) { + o[0] = instance; + } + return (int)(a + d); + } +} + diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java new file mode 100644 index 0000000..9dc2f69 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 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; + +/** + * Test class with an inner class. + * + * Used by {@link DelegateClassAdapterTest}. + */ +public class OuterClass { + private int mOuterValue = 1; + public OuterClass() { + } + + // Outer.get returns 1 + a + b + // Note: it's good to have a long or double for testing parameters since they take + // 2 slots in the stack/locals maps. + public int get(int a, long b) { + return mOuterValue + a + (int) b; + } + + public class InnerClass { + public InnerClass() { + } + + // Inner.get returns 1+2=3 + a + b + public int get(int a, long b) { + return 2 + mOuterValue + a + (int) b; + } + } +} + diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java new file mode 100644 index 0000000..3252d87 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_Delegate.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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; + +/** + * Used by {@link DelegateClassAdapterTest}. + */ +public class OuterClass_Delegate { + // The delegate override of Outer.get returns 4 + a + b + public static int get(OuterClass instance, int a, long b) { + return 4 + a + (int) b; + } +} + diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java new file mode 100644 index 0000000..b472220 --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/dataclass/OuterClass_InnerClass_Delegate.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.InnerClass; + +/** + * Used by {@link DelegateClassAdapterTest}. + */ +public class OuterClass_InnerClass_Delegate { + // The delegate override of Inner.get return 6 + a + b + public static int get(OuterClass outer, InnerClass inner, int a, long b) { + return 6 + a + (int) b; + } +} |