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