1b5d56e95f27d484ad3098ac18867262e7c8826c1Christian Williams & Phil Goodwinpackage com.xtremelabs.robolectric.bytecode;
26eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
3c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williamsimport android.net.Uri;
46d7cabef7bfbd7f56c3bb2dca3066430b2116985Christian Williamsimport com.xtremelabs.robolectric.internal.DoNotInstrument;
56d7cabef7bfbd7f56c3bb2dca3066430b2116985Christian Williamsimport com.xtremelabs.robolectric.internal.Instrument;
6f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richardimport javassist.*;
76eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
8e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultzimport java.io.IOException;
96eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulzimport java.util.ArrayList;
106eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulzimport java.util.List;
116eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
12e876a8fad71b3098795fc08a08f794795123442bChristian Williams@SuppressWarnings({"UnusedDeclaration"})
136eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulzpublic class AndroidTranslator implements Translator {
1497fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams    /**
1597fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams     * IMPORTANT -- increment this number when the bytecode generated for modified classes changes
1697fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams     * so the cache file can be invalidated.
1797fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams     */
188c52966dbfcf3dfa705f18716a2690c7354d9b84Jon Boekenoogen    public static final int CACHE_VERSION = 21;
1997fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams
20e876a8fad71b3098795fc08a08f794795123442bChristian Williams    private static final List<ClassHandler> CLASS_HANDLERS = new ArrayList<ClassHandler>();
21935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams
226eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private ClassHandler classHandler;
2330291fcbfe19f4959102bacdc65dddb9a3715b74Christian Williams & Phil Goodwin    private ClassCache classCache;
2453c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel    private final List<String> instrumentingList = new ArrayList<String>();
25b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel    private final List<String> instrumentingExcludeList = new ArrayList<String>();
266eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2730291fcbfe19f4959102bacdc65dddb9a3715b74Christian Williams & Phil Goodwin    public AndroidTranslator(ClassHandler classHandler, ClassCache classCache) {
286eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        this.classHandler = classHandler;
2930291fcbfe19f4959102bacdc65dddb9a3715b74Christian Williams & Phil Goodwin        this.classCache = classCache;
3053c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel
31b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel        // Initialize lists
32b9116a1018de7bd622d18742d589eca4b5a12983Kathy Lin        instrumentingList.add("android.");
33b9116a1018de7bd622d18742d589eca4b5a12983Kathy Lin        instrumentingList.add("com.google.android.maps");
34b9116a1018de7bd622d18742d589eca4b5a12983Kathy Lin        instrumentingList.add("org.apache.http.impl.client.DefaultRequestDirector");
35b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel
36a737ba49c3a94d16cc2e4bf7e5335615892cb67fMichael Portuesi        instrumentingExcludeList.add("android.support.v4.app.NotificationCompat");
379f52aa80f4b088107eba7ab628f904290963cdcfTyler Schultz        instrumentingExcludeList.add("android.support.v4.content.LocalBroadcastManager");
388c52966dbfcf3dfa705f18716a2690c7354d9b84Jon Boekenoogen        instrumentingExcludeList.add("android.support.v4.util.LruCache");
396eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4053c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel
41f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard    public AndroidTranslator(ClassHandler classHandler, ClassCache classCache, List<String> customShadowClassNames) {
4253c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        this(classHandler, classCache);
4353c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        if (customShadowClassNames != null && !customShadowClassNames.isEmpty()) {
4453c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            instrumentingList.addAll(customShadowClassNames);
4553c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        }
46b9116a1018de7bd622d18742d589eca4b5a12983Kathy Lin    }
47f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard
48f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard    public void addCustomShadowClass(String customShadowClassName) {
49f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard        if (!instrumentingList.contains(customShadowClassName)) {
50f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard            instrumentingList.add(customShadowClassName);
51f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard        }
52f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard    }
53f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard
54e876a8fad71b3098795fc08a08f794795123442bChristian Williams    public static ClassHandler getClassHandler(int index) {
55e876a8fad71b3098795fc08a08f794795123442bChristian Williams        return CLASS_HANDLERS.get(index);
56e876a8fad71b3098795fc08a08f794795123442bChristian Williams    }
57e876a8fad71b3098795fc08a08f794795123442bChristian Williams
5897fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams    @Override
596eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    public void start(ClassPool classPool) throws NotFoundException, CannotCompileException {
609122a05312e767618ba0545a951df9cab86c8716Christian Williams        injectClassHandlerToInstrumentedClasses(classPool);
619122a05312e767618ba0545a951df9cab86c8716Christian Williams    }
629122a05312e767618ba0545a951df9cab86c8716Christian Williams
639122a05312e767618ba0545a951df9cab86c8716Christian Williams    private void injectClassHandlerToInstrumentedClasses(ClassPool classPool) throws NotFoundException, CannotCompileException {
64e876a8fad71b3098795fc08a08f794795123442bChristian Williams        int index;
65e876a8fad71b3098795fc08a08f794795123442bChristian Williams        synchronized (CLASS_HANDLERS) {
66e876a8fad71b3098795fc08a08f794795123442bChristian Williams            CLASS_HANDLERS.add(classHandler);
67e876a8fad71b3098795fc08a08f794795123442bChristian Williams            index = CLASS_HANDLERS.size() - 1;
68e876a8fad71b3098795fc08a08f794795123442bChristian Williams        }
69e876a8fad71b3098795fc08a08f794795123442bChristian Williams
70e876a8fad71b3098795fc08a08f794795123442bChristian Williams        CtClass robolectricInternalsCtClass = classPool.get(RobolectricInternals.class.getName());
719122a05312e767618ba0545a951df9cab86c8716Christian Williams        robolectricInternalsCtClass.setModifiers(Modifier.PUBLIC);
729122a05312e767618ba0545a951df9cab86c8716Christian Williams
739122a05312e767618ba0545a951df9cab86c8716Christian Williams        robolectricInternalsCtClass.getClassInitializer().insertBefore("{\n" +
74e876a8fad71b3098795fc08a08f794795123442bChristian Williams                "classHandler = " + AndroidTranslator.class.getName() + ".getClassHandler(" + index + ");\n" +
75e876a8fad71b3098795fc08a08f794795123442bChristian Williams                "}");
766eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
776eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
7897fe1bbb3849dbaac8d7243b4c2bf42a99689f07Christian Williams    @Override
796eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    public void onLoad(ClassPool classPool, String className) throws NotFoundException, CannotCompileException {
8030291fcbfe19f4959102bacdc65dddb9a3715b74Christian Williams & Phil Goodwin        if (classCache.isWriting()) {
81a329318a8b2661107af6f9b25ffecf892b3c2c72Christian Williams & Ryan Richard            throw new IllegalStateException("shouldn't be modifying bytecode after we've started writing cache! class=" + className);
82a329318a8b2661107af6f9b25ffecf892b3c2c72Christian Williams & Ryan Richard        }
833fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin
84c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        if (classHasFromAndroidEquivalent(className)) {
85c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            replaceClassWithFromAndroidEquivalent(classPool, className);
86c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            return;
87c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        }
88c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
893ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin        CtClass ctClass;
903ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin        try {
913ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin            ctClass = classPool.get(className);
923ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin        } catch (NotFoundException e) {
93120394e8340f49225bda91512b73e0774f01f3b5Phil Goodwin            throw new IgnorableClassNotFoundException(e);
943ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin        }
9553c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel
9653c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        if (shouldInstrument(ctClass)) {
976eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            int modifiers = ctClass.getModifiers();
986eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            if (Modifier.isFinal(modifiers)) {
996eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                ctClass.setModifiers(modifiers & ~Modifier.FINAL);
1006eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            }
1016eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
1026eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            classHandler.instrument(ctClass);
1035586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
1046eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            fixConstructors(ctClass);
1056eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            fixMethods(ctClass);
106e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultz
107e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultz            try {
10830291fcbfe19f4959102bacdc65dddb9a3715b74Christian Williams & Phil Goodwin                classCache.addClass(className, ctClass.toBytecode());
109e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultz            } catch (IOException e) {
110e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultz                throw new RuntimeException(e);
111e7c2aaf28ca0949241e45085ae056c014c3bbe59Tyler Schultz            }
1126eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
1136eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
1146eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
11553c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel    /* package */ boolean shouldInstrument(CtClass ctClass) {
11653c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        if (ctClass.hasAnnotation(Instrument.class)) {
11753c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            return true;
118b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel        } else if (ctClass.isInterface() || ctClass.hasAnnotation(DoNotInstrument.class)) {
11953c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            return false;
12053c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        } else {
121b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel            for (String klassName : instrumentingExcludeList) {
122b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel                if (ctClass.getName().startsWith(klassName)) {
123b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel                    return false;
124b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel                }
125b28424f3605c0d0eff653a1f3538a1ce56083b7bJan Berkel            }
12653c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            for (String klassName : instrumentingList) {
12753c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel                if (ctClass.getName().startsWith(klassName)) {
12853c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel                    return true;
12953c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel                }
13053c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            }
13153c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel            return false;
13253c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel        }
13353c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel    }
13453c5a5a304aa516f8e088f7bfde25db04053239fJan Berkel
135c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    private boolean classHasFromAndroidEquivalent(String className) {
136c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        return className.startsWith(Uri.class.getName());
137c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    }
138c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
139c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    private void replaceClassWithFromAndroidEquivalent(ClassPool classPool, String className) throws NotFoundException {
140c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        FromAndroidClassNameParts classNameParts = new FromAndroidClassNameParts(className);
141c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        if (classNameParts.isFromAndroid()) return;
142c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
143c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        String from = classNameParts.getNameWithFromAndroid();
144c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        CtClass ctClass = classPool.getAndRename(from, className);
145c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
146c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        ClassMap map = new ClassMap() {
147c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            @Override
148c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            public Object get(Object jvmClassName) {
149c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                FromAndroidClassNameParts classNameParts = new FromAndroidClassNameParts(jvmClassName.toString());
150c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                if (classNameParts.isFromAndroid()) {
151c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                    return classNameParts.getNameWithoutFromAndroid();
152c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                } else {
153c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                    return jvmClassName;
154c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                }
155c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            }
156c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        };
157c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        ctClass.replaceClassName(map);
158c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    }
159c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
160c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    class FromAndroidClassNameParts {
161c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        private static final String TOKEN = "__FromAndroid";
162c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
163c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        private String prefix;
164c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        private String suffix;
165c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
166c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        FromAndroidClassNameParts(String name) {
167c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            int dollarIndex = name.indexOf("$");
168c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            prefix = name;
169c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            suffix = "";
170c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            if (dollarIndex > -1) {
171c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                prefix = name.substring(0, dollarIndex);
172c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams                suffix = name.substring(dollarIndex);
173c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            }
174c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        }
175c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
176c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        public boolean isFromAndroid() {
177c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            return prefix.endsWith(TOKEN);
178c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        }
179c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
180c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        public String getNameWithFromAndroid() {
181c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            return prefix + TOKEN + suffix;
182c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        }
183c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
184c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        public String getNameWithoutFromAndroid() {
185c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams            return prefix.replace(TOKEN, "") + suffix;
186c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams        }
187c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams    }
188c1d4b5222a200d82b1ec9ea56dd0270687ee0920Phil Goodwin & Christian Williams
1895586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams    private void addBypassShadowField(CtClass ctClass, String fieldName) {
1905586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        try {
1915586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            try {
1925586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams                ctClass.getField(fieldName);
1935586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            } catch (NotFoundException e) {
1945586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams                CtField field = new CtField(CtClass.booleanType, fieldName, ctClass);
1955586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams                field.setModifiers(java.lang.reflect.Modifier.PUBLIC | java.lang.reflect.Modifier.STATIC);
1965586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams                ctClass.addField(field);
1975586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            }
1985586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        } catch (CannotCompileException e) {
1995586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            throw new RuntimeException(e);
2005586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        }
2015586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams    }
2025586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
2036eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private void fixConstructors(CtClass ctClass) throws CannotCompileException, NotFoundException {
204f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard
205f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard        if (ctClass.isEnum()) {
206f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard            // skip enum constructors because they are not stubs in android.jar
207f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard            return;
208f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard        }
209f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard
210935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        boolean hasDefault = false;
2116eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
212f567a9e68c435fa0a98191c8ccda08add2f37566Rick Kawala & Ryan Richard        for (CtConstructor ctConstructor : ctClass.getDeclaredConstructors()) {
2131bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams            try {
214935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams                fixConstructor(ctClass, hasDefault, ctConstructor);
215935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams
216935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams                if (ctConstructor.getParameterTypes().length == 0) {
217935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams                    hasDefault = true;
218935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams                }
2191bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams            } catch (Exception e) {
2201bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams                throw new RuntimeException("problem instrumenting " + ctConstructor, e);
2216eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            }
2226eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
2236eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
224935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        if (!hasDefault) {
2250f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz            String methodBody = generateConstructorBody(ctClass, new CtClass[0]);
2265586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            ctClass.addConstructor(CtNewConstructor.make(new CtClass[0], new CtClass[0], "{\n" + methodBody + "}\n", ctClass));
2276eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
2286eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
2296eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2301bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams    private boolean fixConstructor(CtClass ctClass, boolean needsDefault, CtConstructor ctConstructor) throws NotFoundException, CannotCompileException {
2311bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams        String methodBody = generateConstructorBody(ctClass, ctConstructor.getParameterTypes());
2325586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        ctConstructor.setBody("{\n" + methodBody + "}\n");
2331bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams        return needsDefault;
2341bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams    }
2351bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams
2360f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz    private String generateConstructorBody(CtClass ctClass, CtClass[] parameterTypes) throws NotFoundException {
2370f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz        return generateMethodBody(ctClass,
2380f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz                new CtMethod(CtClass.voidType, "<init>", parameterTypes, ctClass),
2390f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz                CtClass.voidType,
2400f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz                Type.VOID,
241537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                false,
2420f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz                false);
2430f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz    }
2440f8821aeb8597334bbe65b809f6dba2dae912a82Christian Williams & Tyler Schultz
2456eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private void fixMethods(CtClass ctClass) throws NotFoundException, CannotCompileException {
2466eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
247537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            fixMethod(ctClass, ctMethod, true);
2481bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams        }
2493fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        CtMethod equalsMethod = ctClass.getMethod("equals", "(Ljava/lang/Object;)Z");
2503fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        CtMethod hashCodeMethod = ctClass.getMethod("hashCode", "()I");
2513fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        CtMethod toStringMethod = ctClass.getMethod("toString", "()Ljava/lang/String;");
2523fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin
253537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin        fixMethod(ctClass, equalsMethod, false);
254537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin        fixMethod(ctClass, hashCodeMethod, false);
255537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin        fixMethod(ctClass, toStringMethod, false);
2561bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams    }
2571bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams
2585586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams    private String describe(CtMethod ctMethod) throws NotFoundException {
2595586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        return Modifier.toString(ctMethod.getModifiers()) + " " + ctMethod.getReturnType().getSimpleName() + " " + ctMethod.getLongName();
2605586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams    }
2615586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
262537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin    private void fixMethod(CtClass ctClass, CtMethod ctMethod, boolean wasFoundInClass) throws NotFoundException {
2633fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        String describeBefore = describe(ctMethod);
2643fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        try {
2653fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            CtClass declaringClass = ctMethod.getDeclaringClass();
2663fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            int originalModifiers = ctMethod.getModifiers();
2675586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
2683fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            boolean wasNative = Modifier.isNative(originalModifiers);
2693fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            boolean wasFinal = Modifier.isFinal(originalModifiers);
2703fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            boolean wasAbstract = Modifier.isAbstract(originalModifiers);
271537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            boolean wasDeclaredInClass = ctClass == declaringClass;
2725586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
2733fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            if (wasFinal && ctClass.isEnum()) {
2743fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                return;
2753fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            }
2766eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2773fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            int newModifiers = originalModifiers;
2783fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            if (wasNative) {
2793fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                newModifiers = Modifier.clear(newModifiers, Modifier.NATIVE);
2803fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            }
2813fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            if (wasFinal) {
2823fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                newModifiers = Modifier.clear(newModifiers, Modifier.FINAL);
2833fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            }
284537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            if (wasFoundInClass) {
2853fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctMethod.setModifiers(newModifiers);
2863fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            }
2876eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2883fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            CtClass returnCtClass = ctMethod.getReturnType();
2893fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            Type returnType = Type.find(returnCtClass);
2906eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2913fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            String methodName = ctMethod.getName();
2923fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            CtClass[] paramTypes = ctMethod.getParameterTypes();
2936eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
2946eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//            if (!isAbstract) {
2956eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                if (methodName.startsWith("set") && paramTypes.length == 1) {
2966eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    String fieldName = "__" + methodName.substring(3);
2976eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    if (declareField(ctClass, fieldName, paramTypes[0])) {
2986eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                        methodBody = fieldName + " = $1;\n" + methodBody;
2996eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    }
3006eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                } else if (methodName.startsWith("get") && paramTypes.length == 0) {
3016eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    String fieldName = "__" + methodName.substring(3);
3026eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    if (declareField(ctClass, fieldName, returnType)) {
3036eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                        methodBody = "return " + fieldName + ";\n";
3046eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                    }
3056eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//                }
3066eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz//            }
3076eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
3083fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            boolean isStatic = Modifier.isStatic(originalModifiers);
309537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            String methodBody = generateMethodBody(ctClass, ctMethod, wasNative, wasAbstract, returnCtClass, returnType, isStatic, !wasFoundInClass);
3103fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin
311537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            if (!wasFoundInClass) {
3123fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                CtMethod newMethod = makeNewMethod(ctClass, ctMethod, returnCtClass, methodName, paramTypes, "{\n" + methodBody + generateCallToSuper(methodName, paramTypes) + "\n}");
3133fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                newMethod.setModifiers(newModifiers);
314537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                if (wasDeclaredInClass) {
315537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                    ctMethod.insertBefore("{\n" + methodBody + "}\n");
316537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                } else {
317537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                    ctClass.addMethod(newMethod);
318537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                }
3193fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            } else if (wasAbstract || wasNative) {
3203fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                CtMethod newMethod = makeNewMethod(ctClass, ctMethod, returnCtClass, methodName, paramTypes, "{\n" + methodBody + "\n}");
3213fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctMethod.setBody(newMethod, null);
3223fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            } else {
3233fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctMethod.insertBefore("{\n" + methodBody + "}\n");
3243fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            }
3253fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        } catch (Exception e) {
3263fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            throw new RuntimeException("problem instrumenting " + describeBefore, e);
3273fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        }
3283fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    }
3296eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
3303fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    private CtMethod makeNewMethod(CtClass ctClass, CtMethod ctMethod, CtClass returnCtClass, String methodName, CtClass[] paramTypes, String methodBody) throws CannotCompileException, NotFoundException {
3313fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        return CtNewMethod.make(
3323fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctMethod.getModifiers(),
3333fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                returnCtClass,
3343fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                methodName,
3353fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                paramTypes,
3363fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctMethod.getExceptionTypes(),
3373fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                methodBody,
3383fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin                ctClass);
3393fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    }
3401bd18692c4958c09a8daa416137a64ef0bb3d96fChristian Williams
3413fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    public String generateCallToSuper(String methodName, CtClass[] paramTypes) {
3423fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        return "return super." + methodName + "(" + makeParameterReplacementList(paramTypes.length) + ");";
3433fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    }
3445586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
3453fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin    public String makeParameterReplacementList(int length) {
3463fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        if (length == 0) {
3473fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            return "";
3485586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        }
3493fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin
3503fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        String parameterReplacementList = "$1";
3513fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        for (int i = 2; i <= length; ++i) {
3523fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin            parameterReplacementList += ", $" + i;
3535586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        }
3543fbe415c50867fd95a2d7c7b1e0b7a08871290b8Christian Williams & Phil Goodwin        return parameterReplacementList;
3556eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
3566eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
357537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin    private String generateMethodBody(CtClass ctClass, CtMethod ctMethod, boolean wasNative, boolean wasAbstract, CtClass returnCtClass, Type returnType, boolean aStatic, boolean shouldGenerateCallToSuper) throws NotFoundException {
358935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        String methodBody;
359935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        if (wasAbstract) {
360935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams            methodBody = returnType.isVoid() ? "" : "return " + returnType.defaultReturnString() + ";";
361935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        } else {
362537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            methodBody = generateMethodBody(ctClass, ctMethod, returnCtClass, returnType, aStatic, shouldGenerateCallToSuper);
363935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        }
364935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams
365935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        if (wasNative) {
366935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams            methodBody += returnType.isVoid() ? "" : "return " + returnType.defaultReturnString() + ";";
367935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        }
368935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams        return methodBody;
369935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams    }
370935342f72e39cbc067cd37c07765ff6dd897db81Christian Williams
371537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin    public String generateMethodBody(CtClass ctClass, CtMethod ctMethod, CtClass returnCtClass, Type returnType, boolean isStatic, boolean shouldGenerateCallToSuper) throws NotFoundException {
372a3533036a35e366c47bad39ac9edc0b48498538aChristian Williams        boolean returnsVoid = returnType.isVoid();
3735586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        String className = ctClass.getName();
374a3533036a35e366c47bad39ac9edc0b48498538aChristian Williams
3756eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        String methodBody;
3766eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        StringBuilder buf = new StringBuilder();
3779122a05312e767618ba0545a951df9cab86c8716Christian Williams        buf.append("if (!");
3789122a05312e767618ba0545a951df9cab86c8716Christian Williams        buf.append(RobolectricInternals.class.getName());
3799122a05312e767618ba0545a951df9cab86c8716Christian Williams        buf.append(".shouldCallDirectly(");
3809122a05312e767618ba0545a951df9cab86c8716Christian Williams        buf.append(isStatic ? className + ".class" : "this");
3819122a05312e767618ba0545a951df9cab86c8716Christian Williams        buf.append(")) {\n");
3825586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
3836eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (!returnsVoid) {
3846eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("Object x = ");
3856eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
386e876a8fad71b3098795fc08a08f794795123442bChristian Williams        buf.append(RobolectricInternals.class.getName());
387e876a8fad71b3098795fc08a08f794795123442bChristian Williams        buf.append(".methodInvoked(\n  ");
3885586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        buf.append(className);
38984a72c253aeabd5617951a4378ec06d91bd2bfa1Christian Williams & Tyler Schulz        buf.append(".class, \"");
3906eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append(ctMethod.getName());
3916eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append("\", ");
3929122a05312e767618ba0545a951df9cab86c8716Christian Williams        if (!isStatic) {
3936eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("this");
3946eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        } else {
3956eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("null");
3966eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
3976eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append(", ");
3986eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
3996eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        appendParamTypeArray(buf, ctMethod);
4006eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append(", ");
40184a72c253aeabd5617951a4378ec06d91bd2bfa1Christian Williams & Tyler Schulz        appendParamArray(buf, ctMethod);
4026eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4036eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append(")");
4046eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        buf.append(";\n");
4056eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4066eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (!returnsVoid) {
4076eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("if (x != null) return ((");
4086eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append(returnType.nonPrimitiveClassName(returnCtClass));
4096eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append(") x)");
4106eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append(returnType.unboxString());
4116eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append(";\n");
412537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin            if (shouldGenerateCallToSuper) {
413cb22b0511708035ce3ecbfff577a74f2e39a76a9Christian Williams & Phil Goodwin                buf.append(generateCallToSuper(ctMethod.getName(), ctMethod.getParameterTypes()));
414cb22b0511708035ce3ecbfff577a74f2e39a76a9Christian Williams & Phil Goodwin            } else {
415cb22b0511708035ce3ecbfff577a74f2e39a76a9Christian Williams & Phil Goodwin                buf.append("return ");
416cb22b0511708035ce3ecbfff577a74f2e39a76a9Christian Williams & Phil Goodwin                buf.append(returnType.defaultReturnString());
417537f38b99433fee0ee08d7c634763e7381942f58Phil Goodwin                buf.append(";\n");
418cb22b0511708035ce3ecbfff577a74f2e39a76a9Christian Williams & Phil Goodwin            }
4195586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        } else {
4205586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams            buf.append("return;\n");
4216eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4226eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4235586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams        buf.append("}\n");
4245586ee2425e7c33e2a123ca5b563f092f66374a6Christian Williams
4256eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        methodBody = buf.toString();
4266eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        return methodBody;
4276eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4286eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4296eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private void appendParamTypeArray(StringBuilder buf, CtMethod ctMethod) throws NotFoundException {
4306eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        CtClass[] parameterTypes = ctMethod.getParameterTypes();
4316eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (parameterTypes.length == 0) {
4326eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("new String[0]");
4336eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        } else {
4346eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("new String[] {");
4356eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            for (int i = 0; i < parameterTypes.length; i++) {
4366eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                if (i > 0) buf.append(", ");
4376eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                buf.append("\"");
4386eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                CtClass parameterType = parameterTypes[i];
4396eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                buf.append(parameterType.getName());
4406eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                buf.append("\"");
4416eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            }
4426eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("}");
4436eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4446eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4456eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
44684a72c253aeabd5617951a4378ec06d91bd2bfa1Christian Williams & Tyler Schulz    private void appendParamArray(StringBuilder buf, CtMethod ctMethod) throws NotFoundException {
4476eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        int parameterCount = ctMethod.getParameterTypes().length;
4486eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (parameterCount == 0) {
4496eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("new Object[0]");
4506eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        } else {
4516eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("new Object[] {");
4526eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            for (int i = 0; i < parameterCount; i++) {
4536eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                if (i > 0) buf.append(", ");
454e876a8fad71b3098795fc08a08f794795123442bChristian Williams                buf.append(RobolectricInternals.class.getName());
4556eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                buf.append(".autobox(");
456329db3eb85b10c4d4cbce8fbbc488af3481ea2f4Christian Williams                buf.append("$").append(i + 1);
4576eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz                buf.append(")");
4586eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            }
4596eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            buf.append("}");
4606eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4616eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4626eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4636eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private boolean declareField(CtClass ctClass, String fieldName, CtClass fieldType) throws CannotCompileException, NotFoundException {
4646eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        CtMethod ctMethod = getMethod(ctClass, "get" + fieldName, "");
4656eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (ctMethod == null) {
4666eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return false;
4676eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4686eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        CtClass getterFieldType = ctMethod.getReturnType();
4696eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4706eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (!getterFieldType.equals(fieldType)) {
4716eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return false;
4726eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4736eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4746eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        if (getField(ctClass, fieldName) == null) {
4756eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            CtField field = new CtField(fieldType, fieldName, ctClass);
4766eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            field.setModifiers(Modifier.PRIVATE);
4776eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            ctClass.addField(field);
4786eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4796eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4806eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        return true;
4816eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4826eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4836eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private CtField getField(CtClass ctClass, String fieldName) {
4846eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        try {
4856eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return ctClass.getField(fieldName);
4866eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        } catch (NotFoundException e) {
4876eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return null;
4886eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4896eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4906eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz
4916eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    private CtMethod getMethod(CtClass ctClass, String methodName, String desc) {
4926eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        try {
4936eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return ctClass.getMethod(methodName, desc);
4946eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        } catch (NotFoundException e) {
4956eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz            return null;
4966eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz        }
4976eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz    }
4983ac42c39c9f4627c52809c9bf6701df7cd651a02Christian Williams & Phil Goodwin
4996eedf728138e6f79fa898ff3e31bf61c9b1151dChristian Williams & Tyler Schulz}
500