1 /* 2 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.core.common.type; 26 27 import java.util.AbstractList; 28 import java.util.Objects; 29 import java.util.RandomAccess; 30 31 import jdk.vm.ci.meta.Constant; 32 import jdk.vm.ci.meta.JavaConstant; 33 import jdk.vm.ci.meta.JavaKind; 34 import jdk.vm.ci.meta.MetaAccessProvider; 35 import jdk.vm.ci.meta.ResolvedJavaType; 36 37 /** 38 * Type describing all pointers to Java objects. 39 */ 40 public abstract class AbstractObjectStamp extends AbstractPointerStamp { 41 42 private final ResolvedJavaType type; 43 private final boolean exactType; 44 45 protected AbstractObjectStamp(ResolvedJavaType type, boolean exactType, boolean nonNull, boolean alwaysNull) { 46 super(nonNull, alwaysNull); 47 this.type = type; 48 this.exactType = exactType; 49 } 50 51 @Override 52 public void accept(Visitor v) { 53 super.accept(v); 54 v.visitObject(type); 55 v.visitBoolean(exactType); 56 } 57 58 protected abstract AbstractObjectStamp copyWith(ResolvedJavaType newType, boolean newExactType, boolean newNonNull, boolean newAlwaysNull); 59 60 @Override 61 protected final AbstractPointerStamp copyWith(boolean newNonNull, boolean newAlwaysNull) { 62 return copyWith(type, exactType, newNonNull, newAlwaysNull); 63 } 64 65 @Override 66 public Stamp unrestricted() { 67 return copyWith(null, false, false, false); 68 } 69 70 @Override 71 public Stamp empty() { 72 return copyWith(null, true, true, false); 73 } 74 75 @Override 76 public Stamp constant(Constant c, MetaAccessProvider meta) { 77 JavaConstant jc = (JavaConstant) c; 78 ResolvedJavaType constType = jc.isNull() ? null : meta.lookupJavaType(jc); 79 return copyWith(constType, jc.isNonNull(), jc.isNonNull(), jc.isNull()); 80 } 81 82 @Override 83 public boolean hasValues() { 84 return !exactType || (type != null && (isConcreteType(type))); 85 } 86 87 @Override 88 public JavaKind getStackKind() { 89 return JavaKind.Object; 90 } 91 92 @Override 93 public ResolvedJavaType javaType(MetaAccessProvider metaAccess) { 94 if (type != null) { 95 return type; 96 } 97 return metaAccess.lookupJavaType(Object.class); 98 } 99 100 public ResolvedJavaType type() { 101 return type; 102 } 103 104 public boolean isExactType() { 105 return exactType && type != null; 106 } 107 108 protected void appendString(StringBuilder str) { 109 if (this.isEmpty()) { 110 str.append(" empty"); 111 } else { 112 str.append(nonNull() ? "!" : "").append(exactType ? "#" : "").append(' ').append(type == null ? "-" : type.getName()).append(alwaysNull() ? " NULL" : ""); 113 } 114 } 115 116 @Override 117 public Stamp meet(Stamp otherStamp) { 118 if (this == otherStamp) { 119 return this; 120 } 121 AbstractObjectStamp other = (AbstractObjectStamp) otherStamp; 122 if (isEmpty()) { 123 return other; 124 } else if (other.isEmpty()) { 125 return this; 126 } 127 ResolvedJavaType meetType; 128 boolean meetExactType; 129 boolean meetNonNull; 130 boolean meetAlwaysNull; 131 if (other.alwaysNull()) { 132 meetType = type(); 133 meetExactType = exactType; 134 meetNonNull = false; 135 meetAlwaysNull = alwaysNull(); 136 } else if (alwaysNull()) { 137 meetType = other.type(); 138 meetExactType = other.exactType; 139 meetNonNull = false; 140 meetAlwaysNull = other.alwaysNull(); 141 } else { 142 meetType = meetTypes(type(), other.type()); 143 meetExactType = exactType && other.exactType; 144 if (meetExactType && type != null && other.type != null) { 145 // meeting two valid exact types may result in a non-exact type 146 meetExactType = Objects.equals(meetType, type) && Objects.equals(meetType, other.type); 147 } 148 meetNonNull = nonNull() && other.nonNull(); 149 meetAlwaysNull = false; 150 } 151 152 if (Objects.equals(meetType, type) && meetExactType == exactType && meetNonNull == nonNull() && meetAlwaysNull == alwaysNull()) { 153 return this; 154 } else if (Objects.equals(meetType, other.type) && meetExactType == other.exactType && meetNonNull == other.nonNull() && meetAlwaysNull == other.alwaysNull()) { 155 return other; 156 } else { 157 return copyWith(meetType, meetExactType, meetNonNull, meetAlwaysNull); 158 } 159 } 160 161 @Override 162 public Stamp join(Stamp otherStamp) { 163 return join0(otherStamp, false); 164 } 165 166 /** 167 * Returns the stamp representing the type of this stamp after a cast to the type represented by 168 * the {@code to} stamp. While this is very similar to a {@link #join} operation, in the case 169 * where both types are not obviously related, the cast operation will prefer the type of the 170 * {@code to} stamp. This is necessary as long as ObjectStamps are not able to accurately 171 * represent intersection types. 172 * 173 * For example when joining the {@link RandomAccess} type with the {@link AbstractList} type, 174 * without intersection types, this would result in the most generic type ({@link Object} ). For 175 * this reason, in some cases a {@code castTo} operation is preferable in order to keep at least 176 * the {@link AbstractList} type. 177 * 178 * @param other the stamp this stamp should be casted to 179 * @return the new improved stamp or {@code null} if this stamp cannot be improved 180 */ 181 @Override 182 public Stamp improveWith(Stamp other) { 183 return join0(other, true); 184 } 185 186 private Stamp join0(Stamp otherStamp, boolean improve) { 187 if (this == otherStamp) { 188 return this; 189 } 190 AbstractObjectStamp other = (AbstractObjectStamp) otherStamp; 191 if (isEmpty()) { 192 return this; 193 } else if (other.isEmpty()) { 194 return other; 195 } 196 197 ResolvedJavaType joinType; 198 boolean joinAlwaysNull = alwaysNull() || other.alwaysNull(); 199 boolean joinNonNull = nonNull() || other.nonNull(); 200 boolean joinExactType = exactType || other.exactType; 201 if (Objects.equals(type, other.type)) { 202 joinType = type; 203 } else if (type == null) { 204 joinType = other.type; 205 } else if (other.type == null) { 206 joinType = type; 207 } else { 208 // both types are != null and different 209 if (type.isAssignableFrom(other.type)) { 210 joinType = other.type; 211 if (exactType) { 212 joinAlwaysNull = true; 213 } 214 } else if (other.type.isAssignableFrom(type)) { 215 joinType = type; 216 if (other.exactType) { 217 joinAlwaysNull = true; 218 } 219 } else { 220 if (improve) { 221 joinType = type; 222 joinExactType = exactType; 223 } else { 224 joinType = null; 225 } 226 227 if (joinExactType || (!isInterfaceOrArrayOfInterface(type) && !isInterfaceOrArrayOfInterface(other.type))) { 228 joinAlwaysNull = true; 229 } 230 } 231 } 232 if (joinAlwaysNull) { 233 joinType = null; 234 joinExactType = false; 235 } 236 if (joinExactType && joinType == null) { 237 return empty(); 238 } 239 if (joinAlwaysNull && joinNonNull) { 240 return empty(); 241 } else if (joinExactType && !isConcreteType(joinType)) { 242 return empty(); 243 } 244 if (Objects.equals(joinType, type) && joinExactType == exactType && joinNonNull == nonNull() && joinAlwaysNull == alwaysNull()) { 245 return this; 246 } else if (Objects.equals(joinType, other.type) && joinExactType == other.exactType && joinNonNull == other.nonNull() && joinAlwaysNull == other.alwaysNull()) { 247 return other; 248 } else { 249 return copyWith(joinType, joinExactType, joinNonNull, joinAlwaysNull); 250 } 251 } 252 253 private static boolean isInterfaceOrArrayOfInterface(ResolvedJavaType t) { 254 return t.isInterface() || (t.isArray() && t.getElementalType().isInterface()); 255 } 256 257 public static boolean isConcreteType(ResolvedJavaType type) { 258 return !(type.isAbstract() && !type.isArray()); 259 } 260 261 private static ResolvedJavaType meetTypes(ResolvedJavaType a, ResolvedJavaType b) { 262 if (Objects.equals(a, b)) { 263 return a; 264 } else if (a == null || b == null) { 265 return null; 266 } else { 267 // The `meetTypes` operation must be commutative. One way to achieve this is to totally 268 // order the types and always call `meetOrderedNonNullTypes` in the same order. We 269 // establish the order by first comparing the hash-codes for performance reasons, and 270 // then comparing the internal names of the types. 271 int hashA = a.getName().hashCode(); 272 int hashB = b.getName().hashCode(); 273 if (hashA < hashB) { 274 return meetOrderedNonNullTypes(a, b); 275 } else if (hashB < hashA) { 276 return meetOrderedNonNullTypes(b, a); 277 } else { 278 int diff = a.getName().compareTo(b.getName()); 279 if (diff <= 0) { 280 return meetOrderedNonNullTypes(a, b); 281 } else { 282 return meetOrderedNonNullTypes(b, a); 283 } 284 } 285 } 286 } 287 288 private static ResolvedJavaType meetOrderedNonNullTypes(ResolvedJavaType a, ResolvedJavaType b) { 289 ResolvedJavaType result = a.findLeastCommonAncestor(b); 290 if (result.isJavaLangObject() && a.isInterface() && b.isInterface()) { 291 // Both types are incompatible interfaces => search for first possible common 292 // ancestor match among super interfaces. 293 ResolvedJavaType[] interfacesA = a.getInterfaces(); 294 ResolvedJavaType[] interfacesB = b.getInterfaces(); 295 for (int i = 0; i < interfacesA.length; ++i) { 296 ResolvedJavaType interface1 = interfacesA[i]; 297 for (int j = 0; j < interfacesB.length; ++j) { 298 ResolvedJavaType interface2 = interfacesB[j]; 299 ResolvedJavaType leastCommon = meetTypes(interface1, interface2); 300 if (leastCommon.isInterface()) { 301 return leastCommon; 302 } 303 } 304 } 305 } 306 return result; 307 } 308 309 @Override 310 public int hashCode() { 311 final int prime = 31; 312 int result = 1; 313 result = prime * result + super.hashCode(); 314 result = prime * result + (exactType ? 1231 : 1237); 315 result = prime * result + ((type == null || type.isJavaLangObject()) ? 0 : type.hashCode()); 316 return result; 317 } 318 319 @Override 320 public boolean equals(Object obj) { 321 if (this == obj) { 322 return true; 323 } 324 if (obj == null || getClass() != obj.getClass()) { 325 return false; 326 } 327 AbstractObjectStamp other = (AbstractObjectStamp) obj; 328 if (exactType != other.exactType) { 329 return false; 330 } 331 // null == java.lang.Object 332 if (type == null) { 333 if (other.type != null && !other.type.isJavaLangObject()) { 334 return false; 335 } 336 } else if (other.type == null) { 337 if (type != null && !type.isJavaLangObject()) { 338 return false; 339 } 340 } else if (!type.equals(other.type)) { 341 return false; 342 } 343 return super.equals(other); 344 } 345 }