• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
32 package org.jf.dexlib2.analysis;
33 
34 import com.google.common.base.Strings;
35 import org.jf.dexlib2.iface.Method;
36 import org.jf.dexlib2.iface.reference.FieldReference;
37 import org.jf.dexlib2.iface.reference.MethodReference;
38 import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
39 import org.jf.dexlib2.util.TypeUtils;
40 import org.jf.util.ExceptionWithContext;
41 
42 import javax.annotation.Nonnull;
43 import javax.annotation.Nullable;
44 
45 public class ArrayProto implements TypeProto {
46     protected final ClassPath classPath;
47     protected final int dimensions;
48     protected final String elementType;
49 
ArrayProto(@onnull ClassPath classPath, @Nonnull String type)50     public ArrayProto(@Nonnull ClassPath classPath, @Nonnull String type) {
51         this.classPath = classPath;
52         int i=0;
53         while (type.charAt(i) == '[') {
54             i++;
55             if (i == type.length()) {
56                 throw new ExceptionWithContext("Invalid array type: %s", type);
57             }
58         }
59 
60         if (i == 0) {
61             throw new ExceptionWithContext("Invalid array type: %s", type);
62         }
63 
64         dimensions = i;
65         elementType = type.substring(i);
66     }
67 
toString()68     @Override public String toString() { return getType(); }
getClassPath()69     @Nonnull @Override public ClassPath getClassPath() { return classPath; }
getType()70     @Nonnull @Override public String getType() { return makeArrayType(elementType, dimensions); }
getDimensions()71     public int getDimensions() { return dimensions; }
isInterface()72     @Override public boolean isInterface() { return false; }
73 
74     /**
75      * @return The base element type of this array. E.g. This would return Ljava/lang/String; for [[Ljava/lang/String;
76      */
getElementType()77     @Nonnull public String getElementType() { return elementType; }
78 
79     /**
80      * @return The immediate element type of this array. E.g. This would return [Ljava/lang/String; for
81      * [[Ljava/lang/String;
82      */
getImmediateElementType()83     @Nonnull public String getImmediateElementType() {
84         if (dimensions > 1) {
85             return makeArrayType(elementType, dimensions-1);
86         }
87         return elementType;
88     }
89 
implementsInterface(@onnull String iface)90     @Override public boolean implementsInterface(@Nonnull String iface) {
91         return iface.equals("Ljava/lang/Cloneable;") || iface.equals("Ljava/io/Serializable;");
92     }
93 
94     @Nullable @Override
getSuperclass()95     public String getSuperclass() {
96         return "Ljava/lang/Object;";
97     }
98 
99     @Nonnull @Override
getCommonSuperclass(@onnull TypeProto other)100     public TypeProto getCommonSuperclass(@Nonnull TypeProto other) {
101         if (other instanceof ArrayProto) {
102             if (TypeUtils.isPrimitiveType(getElementType()) ||
103                     TypeUtils.isPrimitiveType(((ArrayProto)other).getElementType())) {
104                 if (dimensions == ((ArrayProto)other).dimensions &&
105                         getElementType().equals(((ArrayProto)other).getElementType())) {
106                     return this;
107                 }
108                 return classPath.getClass("Ljava/lang/Object;");
109             }
110 
111             if (dimensions == ((ArrayProto)other).dimensions) {
112                 TypeProto thisClass = classPath.getClass(elementType);
113                 TypeProto otherClass = classPath.getClass(((ArrayProto)other).elementType);
114                 TypeProto mergedClass = thisClass.getCommonSuperclass(otherClass);
115                 if (thisClass == mergedClass) {
116                     return this;
117                 }
118                 if (otherClass == mergedClass) {
119                     return other;
120                 }
121                 return classPath.getClass(makeArrayType(mergedClass.getType(), dimensions));
122             }
123 
124             int dimensions = Math.min(this.dimensions, ((ArrayProto)other).dimensions);
125             return classPath.getClass(makeArrayType("Ljava/lang/Object;", dimensions));
126         }
127 
128         if (other instanceof ClassProto) {
129             try {
130                 if (other.isInterface()) {
131                     if (implementsInterface(other.getType())) {
132                         return other;
133                     }
134                 }
135             } catch (UnresolvedClassException ex) {
136                 // ignore
137             }
138             return classPath.getClass("Ljava/lang/Object;");
139         }
140 
141         // otherwise, defer to the other class' getCommonSuperclass
142         return other.getCommonSuperclass(this);
143     }
144 
145     private static final String BRACKETS = Strings.repeat("[", 256);
146 
147     @Nonnull
makeArrayType(@onnull String elementType, int dimensions)148     private static String makeArrayType(@Nonnull String elementType, int dimensions) {
149         return BRACKETS.substring(0, dimensions) + elementType;
150     }
151 
152 
153     @Override
154     @Nullable
getFieldByOffset(int fieldOffset)155     public FieldReference getFieldByOffset(int fieldOffset) {
156         if (fieldOffset==8) {
157             return new ImmutableFieldReference(getType(), "length", "int");
158         }
159         return null;
160     }
161 
162     @Override
163     @Nullable
getMethodByVtableIndex(int vtableIndex)164     public Method getMethodByVtableIndex(int vtableIndex) {
165         return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
166     }
167 
findMethodIndexInVtable(@onnull MethodReference method)168     @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
169         return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
170     }
171 }
172