11160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta/*
21160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
31160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta *
41160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
51160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * you may not use this file except in compliance with the License.
61160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * You may obtain a copy of the License at
71160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta *
81160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
91160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta *
101160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
111160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
121160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * See the License for the specific language governing permissions and
141160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta * limitations under the License.
151160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta */
161160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
171160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Guptapackage com.android.tools.layoutlib.create;
181160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
191330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Guptaimport com.android.tools.layoutlib.java.LinkedHashMap_Delegate;
2017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Guptaimport com.android.tools.layoutlib.java.System_Delegate;
2117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
221160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Guptaimport org.objectweb.asm.ClassVisitor;
231160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Guptaimport org.objectweb.asm.MethodVisitor;
241160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Guptaimport org.objectweb.asm.Opcodes;
25b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Guptaimport org.objectweb.asm.Type;
261160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
275cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Guptaimport java.util.ArrayList;
28e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Guptaimport java.util.Arrays;
29e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Guptaimport java.util.HashSet;
301330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Guptaimport java.util.LinkedHashMap;
315cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Guptaimport java.util.List;
32b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Guptaimport java.util.Locale;
331330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Guptaimport java.util.Map;
34e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Guptaimport java.util.Set;
35e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta
361160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta/**
375cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta * Replaces calls to certain methods that do not exist in the Desktop VM. Useful for methods in the
385cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta * "java" package.
391160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta */
401160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Guptapublic class ReplaceMethodCallsAdapter extends ClassVisitor {
41e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta
42e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta    /**
43e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta     * Descriptors for specialized versions {@link System#arraycopy} that are not present on the
44e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta     * Desktop VM.
45e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta     */
4623e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta    private static Set<String> ARRAYCOPY_DESCRIPTORS = new HashSet<>(Arrays.asList(
47e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta            "([CI[CII)V", "([BI[BII)V", "([SI[SII)V", "([II[III)V",
48e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta            "([JI[JII)V", "([FI[FII)V", "([DI[DII)V", "([ZI[ZII)V"));
49e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta
5023e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta    private static final List<MethodReplacer> METHOD_REPLACERS = new ArrayList<>(5);
515cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
52b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta    private static final String ANDROID_LOCALE_CLASS =
53b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            "com/android/layoutlib/bridge/android/AndroidLocale";
54b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
5517a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta    private static final String JAVA_LOCALE_CLASS = Type.getInternalName(java.util.Locale.class);
56b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta    private static final Type STRING = Type.getType(String.class);
57b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
5817a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta    private static final String JAVA_LANG_SYSTEM = Type.getInternalName(System.class);
5917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
605cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    // Static initialization block to initialize METHOD_REPLACERS.
615cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    static {
625cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        // Case 1: java.lang.System.arraycopy()
635cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
645cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            @Override
65f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
6617a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                return JAVA_LANG_SYSTEM.equals(owner) && "arraycopy".equals(name) &&
675cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta                        ARRAYCOPY_DESCRIPTORS.contains(desc);
685cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            }
695cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
705cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            @Override
7117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            public void replace(MethodInformation mi) {
7217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.desc = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
735cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            }
745cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        });
755cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
76b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta        // Case 2: java.util.Locale.toLanguageTag() and java.util.Locale.getScript()
775cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
78b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
791330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            private final String LOCALE_TO_STRING =
801330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                    Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
81b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
82b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            @Override
83f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
84b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta                return JAVA_LOCALE_CLASS.equals(owner) && "()Ljava/lang/String;".equals(desc) &&
85b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta                        ("toLanguageTag".equals(name) || "getScript".equals(name));
86b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            }
87b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
88b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            @Override
8917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            public void replace(MethodInformation mi) {
9017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.opcode = Opcodes.INVOKESTATIC;
9117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.owner = ANDROID_LOCALE_CLASS;
9217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.desc = LOCALE_TO_STRING;
93b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            }
94b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta        });
95b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
9682c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta        // Case 3: java.util.Locale.adjustLanguageCode() or java.util.Locale.forLanguageTag() or
9782c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta        // java.util.Locale.getDefault()
98b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
99b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
100b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING);
101b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta            private final String STRING_TO_LOCALE = Type.getMethodDescriptor(
102b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta                    Type.getType(Locale.class), STRING);
10382c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta            private final String VOID_TO_LOCALE =
10482c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta                    Type.getMethodDescriptor(Type.getType(Locale.class));
105b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta
1065cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            @Override
107f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
108b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta                return JAVA_LOCALE_CLASS.equals(owner) &&
109b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta                        ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
11082c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta                        "forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE) ||
11182c7fdb1f6346862de373c95c618e370f81d8df6Deepanshu Gupta                        "getDefault".equals(name) && desc.equals(VOID_TO_LOCALE));
1125cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            }
1135cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
1145cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            @Override
11517a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            public void replace(MethodInformation mi) {
11617a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.owner = ANDROID_LOCALE_CLASS;
11717a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            }
11817a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        });
11917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
12017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        // Case 4: java.lang.System.log?()
12117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
12217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            @Override
123f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
12417a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                return JAVA_LANG_SYSTEM.equals(owner) && name.length() == 4
12517a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                        && name.startsWith("log");
12617a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            }
12717a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
12817a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            @Override
12917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            public void replace(MethodInformation mi) {
13017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                assert mi.desc.equals("(Ljava/lang/String;Ljava/lang/Throwable;)V")
13117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                        || mi.desc.equals("(Ljava/lang/String;)V");
13217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.name = "log";
13317a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                mi.owner = Type.getInternalName(System_Delegate.class);
1345cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            }
1355cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        });
1361330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta
1377c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        // Case 5: java.lang.System time calls
1387c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
1397c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            @Override
1407c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
1417c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("nanoTime");
1427c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            }
1437c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta
1447c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            @Override
1457c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            public void replace(MethodInformation mi) {
1467c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                mi.name = "nanoTime";
1477c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                mi.owner = Type.getInternalName(System_Delegate.class);
1487c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            }
1497c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        });
1507c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
1517c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            @Override
1527c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
1537c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                return JAVA_LANG_SYSTEM.equals(owner) && name.equals("currentTimeMillis");
1547c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            }
1557c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta
1567c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            @Override
1577c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            public void replace(MethodInformation mi) {
1587c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                mi.name = "currentTimeMillis";
1597c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta                mi.owner = Type.getInternalName(System_Delegate.class);
1607c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta            }
1617c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        });
1627c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta
1637c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        // Case 6: java.util.LinkedHashMap.eldest()
1641330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
1651330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta
1661330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            private final String VOID_TO_MAP_ENTRY =
1671330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                    Type.getMethodDescriptor(Type.getType(Map.Entry.class));
1681330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            private final String LINKED_HASH_MAP = Type.getInternalName(LinkedHashMap.class);
1691330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta
1701330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            @Override
171f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
1721330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                return LINKED_HASH_MAP.equals(owner) &&
1731330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                        "eldest".equals(name) &&
1741330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                        VOID_TO_MAP_ENTRY.equals(desc);
1751330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            }
1761330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta
1771330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            @Override
1781330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            public void replace(MethodInformation mi) {
1791330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                mi.opcode = Opcodes.INVOKESTATIC;
1801330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                mi.owner = Type.getInternalName(LinkedHashMap_Delegate.class);
1811330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                mi.desc = Type.getMethodDescriptor(
1821330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta                        Type.getType(Map.Entry.class), Type.getType(LinkedHashMap.class));
1831330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta            }
1841330f79f95fd14b53c393402fbcbf7b7bbdcbc60Deepanshu Gupta        });
185f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta
1867c4420bd04f8f56f6044f88e34616d8c5d96c7e9Deepanshu Gupta        // Case 7: android.content.Context.getClassLoader() in LayoutInflater
187f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta        METHOD_REPLACERS.add(new MethodReplacer() {
188f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // When LayoutInflater asks for a class loader, we must return the class loader that
189f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // cannot return app's custom views/classes. This is so that in case of any failure
190f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // or exception when instantiating the views, the IDE can replace it with a mock view
191f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // and have proper error handling. However, if a custom view asks for the class
192f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // loader, we must return a class loader that can find app's custom views as well.
193f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // Thus, we rewrite the call to get class loader in LayoutInflater to
194f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // getFrameworkClassLoader and inject a new method in Context. This leaves the normal
195f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            // method: Context.getClassLoader() free to be used by the apps.
196f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            private final String VOID_TO_CLASS_LOADER =
197f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                    Type.getMethodDescriptor(Type.getType(ClassLoader.class));
198f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta
199f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            @Override
200f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public boolean isNeeded(String owner, String name, String desc, String sourceClass) {
201f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                return owner.equals("android/content/Context") &&
202f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                        sourceClass.equals("android/view/LayoutInflater") &&
203f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                        name.equals("getClassLoader") &&
204f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                        desc.equals(VOID_TO_CLASS_LOADER);
205f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            }
206f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta
207f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            @Override
208f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            public void replace(MethodInformation mi) {
209f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                mi.name = "getFrameworkClassLoader";
210f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            }
211f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta        });
2125cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    }
2135cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
214f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta    /**
215f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * If a method some.package.Class.Method(args) is called from some.other.Class,
216f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * @param owner some/package/Class
217f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * @param name Method
218f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * @param desc (args)returnType
219f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * @param sourceClass some/other/Class
220f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     * @return if the method invocation needs to be replaced by some other class.
221f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta     */
222f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta    public static boolean isReplacementNeeded(String owner, String name, String desc,
223f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            String sourceClass) {
2245cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        for (MethodReplacer replacer : METHOD_REPLACERS) {
225f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta            if (replacer.isNeeded(owner, name, desc, sourceClass)) {
2265cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta                return true;
2275cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            }
2285cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        }
2295cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        return false;
2305cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    }
2315cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
232f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta    private final String mOriginalClassName;
233f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta
234f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta    public ReplaceMethodCallsAdapter(ClassVisitor cv, String originalClassName) {
23523e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta        super(Main.ASM_VERSION, cv);
236f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta        mOriginalClassName = originalClassName;
2371160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    }
2381160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
2391160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    @Override
2401160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
2411160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta            String[] exceptions) {
2421160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
2431160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    }
2441160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
2451160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    private class MyMethodVisitor extends MethodVisitor {
2461160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
2471160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        public MyMethodVisitor(MethodVisitor mv) {
24823e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta            super(Main.ASM_VERSION, mv);
2491160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        }
2501160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta
2511160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        @Override
25223e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta        public void visitMethodInsn(int opcode, String owner, String name, String desc,
25323e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta                boolean itf) {
2545cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta            for (MethodReplacer replacer : METHOD_REPLACERS) {
255f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta                if (replacer.isNeeded(owner, name, desc, mOriginalClassName)) {
25617a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    MethodInformation mi = new MethodInformation(opcode, owner, name, desc);
25717a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    replacer.replace(mi);
25817a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    opcode = mi.opcode;
25917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    owner = mi.owner;
26017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    name = mi.name;
26117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta                    desc = mi.desc;
2625cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta                    break;
263e1960cc0b541cda93db94de5bef42dff922b9ec3Deepanshu Gupta                }
2641160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta            }
26523e47f5621271db9b84f53f15a3e3d81d8b8b48dDeepanshu Gupta            super.visitMethodInsn(opcode, owner, name, desc, itf);
2661160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta        }
2671160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta    }
2685cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
26917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta    private static class MethodInformation {
27017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        public int opcode;
27117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        public String owner;
27217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        public String name;
27317a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        public String desc;
27417a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
27517a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        public MethodInformation(int opcode, String owner, String name, String desc) {
27617a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            this.opcode = opcode;
27717a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            this.owner = owner;
27817a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            this.name = name;
27917a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta            this.desc = desc;
28017a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta        }
28117a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta    }
28217a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta
2835cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    private interface MethodReplacer {
284f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta        boolean isNeeded(String owner, String name, String desc, String sourceClass);
2855cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta
2865cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta        /**
28717a6170c62e6f74f2881623a9c16f0b6fea54721Deepanshu Gupta         * Updates the MethodInformation with the new values of the method attributes -
288b80e42f93ff26984c410da0235dd10f463e5722aDeepanshu Gupta         * opcode, owner, name and desc.
2895cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta         */
290f8ea750455eec81e4e6d877b3e18e29a86d4ec95Deepanshu Gupta        void replace(MethodInformation mi);
2915cd9dde5a2a77c5095f985186d8f03147fd22870Deepanshu Gupta    }
2921160e6d2f7018117b0c29a7e2adba9ece36faec1Deepanshu Gupta}
293