1/*
2 * Copyright 2013, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package org.jf.dexlib2.analysis;
33
34import com.google.common.collect.ImmutableSet;
35import com.google.common.collect.Lists;
36import junit.framework.Assert;
37import org.jf.dexlib2.Opcodes;
38import org.jf.dexlib2.iface.ClassDef;
39import org.jf.dexlib2.immutable.ImmutableDexFile;
40import org.junit.Test;
41
42import java.io.IOException;
43
44public class CommonSuperclassTest {
45    // object tree:
46    // object
47    //   one
48    //     onetwo
49    //       onetwothree
50    //     onethree
51    // five (undefined class)
52    //   fivetwo
53    //     fivetwothree
54    //   fivethree
55
56    private final ClassPath oldClassPath;
57    private final ClassPath newClassPath;
58
59
60    public CommonSuperclassTest() throws IOException {
61        ImmutableSet<ClassDef> classes = ImmutableSet.of(
62                TestUtils.makeClassDef("Ljava/lang/Object;", null),
63                TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"),
64                TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"),
65                TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"),
66                TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"),
67                TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"),
68                TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"),
69                TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"),
70                TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"),
71                TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"),
72                TestUtils.makeInterfaceDef("Ljava/io/Serializable;"),
73
74                // basic class and interface
75                TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"),
76                TestUtils.makeInterfaceDef("Liface/iface1;"),
77
78                // a more complex interface tree
79                TestUtils.makeInterfaceDef("Liface/base1;"),
80                // implements undefined interface
81                TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"),
82                // this implements sub1, so that its interfaces can't be fully resolved either
83                TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"),
84                TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"),
85                TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"),
86                TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"),
87                TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"),
88                TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;",
89                        "Liface/base;"),
90                TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;",
91                        "Liface/sub4;"),
92                TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"),
93                TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;",
94                        "Liface/sub2;", "Liface/sub3;", "Liface/sub4;"));
95
96        oldClassPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.getDefault(), classes)));
97        newClassPath = new ClassPath(Lists.newArrayList(new DexClassProvider(
98                new ImmutableDexFile(Opcodes.forArtVersion(72), classes))), true, 72);
99    }
100
101    public void superclassTest(ClassPath classPath, String commonSuperclass,
102                               String type1, String type2) {
103        TypeProto commonSuperclassProto = classPath.getClass(commonSuperclass);
104        TypeProto type1Proto = classPath.getClass(type1);
105        TypeProto type2Proto = classPath.getClass(type2);
106
107        Assert.assertSame(commonSuperclassProto, type1Proto.getCommonSuperclass(type2Proto));
108        Assert.assertSame(commonSuperclassProto, type2Proto.getCommonSuperclass(type1Proto));
109    }
110
111    public void superclassTest(String commonSuperclass, String type1, String type2) {
112        superclassTest(oldClassPath, commonSuperclass, type1, type2);
113        superclassTest(newClassPath, commonSuperclass, type1, type2);
114    }
115
116    @Test
117    public void testGetCommonSuperclass() throws IOException {
118        String object = "Ljava/lang/Object;";
119        String unknown = "Ujava/lang/Object;";
120        String one = "Ltest/one;";
121        String two = "Ltest/two;";
122        String onetwo = "Ltest/onetwo;";
123        String onetwothree = "Ltest/onetwothree;";
124        String onethree = "Ltest/onethree;";
125        String five = "Ltest/five;";
126        String fivetwo = "Ltest/fivetwo;";
127        String fivetwothree = "Ltest/fivetwothree;";
128        String fivethree = "Ltest/fivethree;";
129
130        // same object
131        superclassTest(object, object, object);
132        superclassTest(unknown, unknown, unknown);
133        superclassTest(one, one, one);
134        superclassTest(onetwo, onetwo, onetwo);
135        superclassTest(onetwothree, onetwothree, onetwothree);
136        superclassTest(onethree, onethree, onethree);
137        superclassTest(five, five, five);
138        superclassTest(fivetwo, fivetwo, fivetwo);
139        superclassTest(fivetwothree, fivetwothree, fivetwothree);
140        superclassTest(fivethree, fivethree, fivethree);
141
142        // same value, but different object
143        Assert.assertEquals(
144                onetwo,
145                oldClassPath.getClass(onetwo).getCommonSuperclass(new ClassProto(oldClassPath, onetwo)).getType());
146
147        Assert.assertEquals(
148                onetwo,
149                newClassPath.getClass(onetwo).getCommonSuperclass(new ClassProto(newClassPath, onetwo)).getType());
150
151        // other object is superclass
152        superclassTest(object, object, one);
153
154        // other object is superclass two levels up
155        superclassTest(object, object, onetwo);
156
157        // unknown and non-object class
158        superclassTest(unknown, one, unknown);
159
160        // unknown and object class
161        superclassTest(object, object, unknown);
162
163        // siblings
164        superclassTest(one, onetwo, onethree);
165
166        // nephew
167        superclassTest(one, onethree, onetwothree);
168
169        // unrelated
170        superclassTest(object, one, two);
171
172        // undefined superclass and object
173        superclassTest(object, fivetwo, object);
174
175        // undefined class and unrelated type
176        superclassTest(unknown, one, five);
177
178        // undefined superclass and unrelated type
179        superclassTest(unknown, one, fivetwo);
180
181        // undefined ancestor and unrelated type
182        superclassTest(unknown, one, fivetwothree);
183
184        // undefined class and direct subclass
185        superclassTest(five, five, fivetwo);
186
187        // undefined class and descendent
188        superclassTest(five, five, fivetwothree);
189
190        // undefined superclass and direct subclass
191        superclassTest(fivetwo, fivetwo, fivetwothree);
192
193        // siblings with undefined superclass
194        superclassTest(five, fivetwo, fivethree);
195
196        // undefined superclass and nephew
197        superclassTest(five, fivethree, fivetwothree);
198    }
199
200    @Test
201    public void testGetCommonSuperclass_interfaces() {
202        String classiface1 = "Liface/classiface1;";
203        String iface1 = "Liface/iface1;";
204        String base1 = "Liface/base1;";
205        String base2 = "Liface/base2;";
206        String sub1 = "Liface/sub1;";
207        String sub2 = "Liface/sub2;";
208        String sub3 = "Liface/sub3;";
209        String sub4 = "Liface/sub4;";
210        String classsub1 = "Liface/classsub1;";
211        String classsub2 = "Liface/classsub2;";
212        String classsub3 = "Liface/classsub3;";
213        String classsub4 = "Liface/classsub4;";
214        String classsubsub4 = "Liface/classsubsub4;";
215        String classsub1234 = "Liface/classsub1234;";
216        String object = "Ljava/lang/Object;";
217        String unknown = "Ujava/lang/Object;";
218
219        superclassTest(iface1, classiface1, iface1);
220
221        superclassTest(base1, base1, base1);
222        superclassTest(base1, base1, sub1);
223        superclassTest(base1, base1, classsub1);
224        superclassTest(base1, base1, sub2);
225        superclassTest(base1, base1, classsub2);
226        superclassTest(base1, base1, sub3);
227        superclassTest(base1, base1, classsub3);
228        superclassTest(base1, base1, sub4);
229        superclassTest(base1, base1, classsub4);
230        superclassTest(base1, base1, classsubsub4);
231        superclassTest(base1, base1, classsub1234);
232
233        superclassTest(object, sub3, iface1);
234        superclassTest(unknown, sub2, iface1);
235        superclassTest(unknown, sub1, iface1);
236
237        superclassTest(base2, base2, sub1);
238        superclassTest(base2, base2, classsub1);
239        superclassTest(base2, base2, sub2);
240        superclassTest(base2, base2, classsub2);
241        superclassTest(base2, base2, classsub1234);
242
243        superclassTest(unknown, iface1, classsub1234);
244
245        superclassTest(sub1, sub1, classsub1);
246
247        superclassTest(sub2, sub2, classsub2);
248        superclassTest(sub1, sub1, classsub2);
249
250        superclassTest(sub3, sub3, classsub3);
251
252        superclassTest(sub4, sub4, classsub4);
253        superclassTest(sub3, sub3, classsub4);
254        superclassTest(object, sub2, classsub4);
255        superclassTest(object, sub1, classsub4);
256
257        superclassTest(sub1, sub2, sub1);
258
259        superclassTest(sub1, sub1, classsub1234);
260        superclassTest(sub2, sub2, classsub1234);
261        superclassTest(sub3, sub3, classsub1234);
262        superclassTest(sub4, sub4, classsub1234);
263
264        superclassTest(unknown, sub3, classsub1);
265        superclassTest(unknown, sub4, classsub1);
266        superclassTest(unknown, sub3, classsub2);
267        superclassTest(unknown, sub4, classsub2);
268
269        superclassTest(unknown, sub4, base2);
270        superclassTest(unknown, classsub4, base2);
271    }
272
273    @Test
274    public void testGetCommonSuperclass_arrays() throws IOException {
275        String object = "Ljava/lang/Object;";
276        String one = "Ltest/one;";
277        String unknown = "Ujava/lang/Object;";
278
279        String cloneable = "Ljava/lang/Cloneable;";
280        String serializable = "Ljava/io/Serializable;";
281
282        String object1 = "[Ljava/lang/Object;";
283        String one1 = "[Ltest/one;";
284        String one2 = "[[Ltest/one;";
285        String two1 = "[Ltest/two;";
286        String onetwo1 = "[Ltest/onetwo;";
287        String onetwo2 = "[[Ltest/onetwo;";
288        String onethree1 = "[Ltest/onethree;";
289        String onethree2 = "[[Ltest/onethree;";
290        String five = "Ltest/five;";
291        String five1 = "[Ltest/five;";
292        String unknown1 = "[Ujava/lang/Object;";
293
294        String int1 = "[I";
295        String int2 = "[[I";
296        String float1 = "[F";
297
298        superclassTest(one1, one1, one1);
299        superclassTest(object1, object1, one1);
300        superclassTest(one1, onetwo1, onethree1);
301        superclassTest(one1, one1, onethree1);
302        superclassTest(object1, one1, two1);
303
304        superclassTest(one2, one2, one2);
305        superclassTest(one2, one2, onetwo2);
306        superclassTest(one2, onetwo2, onethree2);
307        superclassTest(object1, one1, one2);
308        superclassTest(object1, two1, one2);
309
310        superclassTest(unknown1, five1, one1);
311        superclassTest(object1, five1, one2);
312
313        superclassTest(unknown1, one1, unknown1);
314
315        superclassTest(object, one1, one);
316        superclassTest(object, object1, one);
317        superclassTest(object, onetwo1, one);
318        superclassTest(object, five1, one);
319        superclassTest(object, one2, one);
320
321        superclassTest(object, one1, unknown);
322        superclassTest(object, unknown1, unknown);
323
324        superclassTest(cloneable, one1, cloneable);
325        superclassTest(serializable, one1, serializable);
326
327        superclassTest(object, one1, five);
328
329        superclassTest(int1, int1, int1);
330        superclassTest(object, int1, float1);
331        superclassTest(object, int1, int2);
332    }
333}
334