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     protected abstract AbstractObjectStamp copyWith(ResolvedJavaType newType, boolean newExactType, boolean newNonNull, boolean newAlwaysNull);
  52 
  53     @Override
  54     protected final AbstractPointerStamp copyWith(boolean newNonNull, boolean newAlwaysNull) {
  55         return copyWith(type, exactType, newNonNull, newAlwaysNull);
  56     }
  57 
  58     @Override
  59     public Stamp unrestricted() {
  60         return copyWith(null, false, false, false);
  61     }
  62 
  63     @Override
  64     public Stamp empty() {
  65         return copyWith(null, true, true, false);
  66     }
  67 
  68     @Override
  69     public Stamp constant(Constant c, MetaAccessProvider meta) {
  70         JavaConstant jc = (JavaConstant) c;
  71         ResolvedJavaType constType = jc.isNull() ? null : meta.lookupJavaType(jc);
  72         return copyWith(constType, jc.isNonNull(), jc.isNonNull(), jc.isNull());
  73     }
  74 
  75     @Override
  76     public boolean hasValues() {
  77         return !exactType || (type != null && (isConcreteType(type)));
  78     }
  79 
  80     @Override
  81     public JavaKind getStackKind() {
  82         return JavaKind.Object;
  83     }
  84 
  85     @Override
  86     public ResolvedJavaType javaType(MetaAccessProvider metaAccess) {
  87         if (type != null) {
  88             return type;
  89         }
  90         return metaAccess.lookupJavaType(Object.class);
  91     }
  92 
  93     public ResolvedJavaType type() {
  94         return type;
  95     }
  96 
  97     public boolean isExactType() {
  98         return exactType && type != null;
  99     }
 100 
 101     protected void appendString(StringBuilder str) {
 102         if (this.isEmpty()) {
 103             str.append(" empty");
 104         } else {
 105             str.append(nonNull() ? "!" : "").append(exactType ? "#" : "").append(' ').append(type == null ? "-" : type.getName()).append(alwaysNull() ? " NULL" : "");
 106         }
 107     }
 108 
 109     @Override
 110     public Stamp meet(Stamp otherStamp) {
 111         if (this == otherStamp) {
 112             return this;
 113         }
 114         AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
 115         if (isEmpty()) {
 116             return other;
 117         } else if (other.isEmpty()) {
 118             return this;
 119         }
 120         ResolvedJavaType meetType;
 121         boolean meetExactType;
 122         boolean meetNonNull;
 123         boolean meetAlwaysNull;
 124         if (other.alwaysNull()) {
 125             meetType = type();
 126             meetExactType = exactType;
 127             meetNonNull = false;
 128             meetAlwaysNull = alwaysNull();
 129         } else if (alwaysNull()) {
 130             meetType = other.type();
 131             meetExactType = other.exactType;
 132             meetNonNull = false;
 133             meetAlwaysNull = other.alwaysNull();
 134         } else {
 135             meetType = meetTypes(type(), other.type());
 136             meetExactType = exactType && other.exactType;
 137             if (meetExactType && type != null && other.type != null) {
 138                 // meeting two valid exact types may result in a non-exact type
 139                 meetExactType = Objects.equals(meetType, type) && Objects.equals(meetType, other.type);
 140             }
 141             meetNonNull = nonNull() && other.nonNull();
 142             meetAlwaysNull = false;
 143         }
 144 
 145         if (Objects.equals(meetType, type) && meetExactType == exactType && meetNonNull == nonNull() && meetAlwaysNull == alwaysNull()) {
 146             return this;
 147         } else if (Objects.equals(meetType, other.type) && meetExactType == other.exactType && meetNonNull == other.nonNull() && meetAlwaysNull == other.alwaysNull()) {
 148             return other;
 149         } else {
 150             return copyWith(meetType, meetExactType, meetNonNull, meetAlwaysNull);
 151         }
 152     }
 153 
 154     @Override
 155     public Stamp join(Stamp otherStamp) {
 156         return join0(otherStamp, false);
 157     }
 158 
 159     /**
 160      * Returns the stamp representing the type of this stamp after a cast to the type represented by
 161      * the {@code to} stamp. While this is very similar to a {@link #join} operation, in the case
 162      * where both types are not obviously related, the cast operation will prefer the type of the
 163      * {@code to} stamp. This is necessary as long as ObjectStamps are not able to accurately
 164      * represent intersection types.
 165      *
 166      * For example when joining the {@link RandomAccess} type with the {@link AbstractList} type,
 167      * without intersection types, this would result in the most generic type ({@link Object} ). For
 168      * this reason, in some cases a {@code castTo} operation is preferable in order to keep at least
 169      * the {@link AbstractList} type.
 170      *
 171      * @param other the stamp this stamp should be casted to
 172      * @return the new improved stamp or {@code null} if this stamp cannot be improved
 173      */
 174     @Override
 175     public Stamp improveWith(Stamp other) {
 176         return join0(other, true);
 177     }
 178 
 179     private Stamp join0(Stamp otherStamp, boolean improve) {
 180         if (this == otherStamp) {
 181             return this;
 182         }
 183         AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
 184         if (isEmpty()) {
 185             return this;
 186         } else if (other.isEmpty()) {
 187             return other;
 188         }
 189 
 190         ResolvedJavaType joinType;
 191         boolean joinAlwaysNull = alwaysNull() || other.alwaysNull();
 192         boolean joinNonNull = nonNull() || other.nonNull();
 193         boolean joinExactType = exactType || other.exactType;
 194         if (Objects.equals(type, other.type)) {
 195             joinType = type;
 196         } else if (type == null) {
 197             joinType = other.type;
 198         } else if (other.type == null) {
 199             joinType = type;
 200         } else {
 201             // both types are != null and different
 202             if (type.isAssignableFrom(other.type)) {
 203                 joinType = other.type;
 204                 if (exactType) {
 205                     joinAlwaysNull = true;
 206                 }
 207             } else if (other.type.isAssignableFrom(type)) {
 208                 joinType = type;
 209                 if (other.exactType) {
 210                     joinAlwaysNull = true;
 211                 }
 212             } else {
 213                 if (improve) {
 214                     joinType = type;
 215                     joinExactType = exactType;
 216                 } else {
 217                     joinType = null;
 218                 }
 219 
 220                 if (joinExactType || (!isInterfaceOrArrayOfInterface(type) && !isInterfaceOrArrayOfInterface(other.type))) {
 221                     joinAlwaysNull = true;
 222                 }
 223             }
 224         }
 225         if (joinAlwaysNull) {
 226             joinType = null;
 227             joinExactType = false;
 228         }
 229         if (joinExactType && joinType == null) {
 230             return empty();
 231         }
 232         if (joinAlwaysNull && joinNonNull) {
 233             return empty();
 234         } else if (joinExactType && !isConcreteType(joinType)) {
 235             return empty();
 236         }
 237         if (Objects.equals(joinType, type) && joinExactType == exactType && joinNonNull == nonNull() && joinAlwaysNull == alwaysNull()) {
 238             return this;
 239         } else if (Objects.equals(joinType, other.type) && joinExactType == other.exactType && joinNonNull == other.nonNull() && joinAlwaysNull == other.alwaysNull()) {
 240             return other;
 241         } else {
 242             return copyWith(joinType, joinExactType, joinNonNull, joinAlwaysNull);
 243         }
 244     }
 245 
 246     private static boolean isInterfaceOrArrayOfInterface(ResolvedJavaType t) {
 247         return t.isInterface() || (t.isArray() && t.getElementalType().isInterface());
 248     }
 249 
 250     public static boolean isConcreteType(ResolvedJavaType type) {
 251         return !(type.isAbstract() && !type.isArray());
 252     }
 253 
 254     private static ResolvedJavaType meetTypes(ResolvedJavaType a, ResolvedJavaType b) {
 255         if (Objects.equals(a, b)) {
 256             return a;
 257         } else if (a == null || b == null) {
 258             return null;
 259         } else {
 260             // The `meetTypes` operation must be commutative. One way to achieve this is to totally
 261             // order the types and always call `meetOrderedNonNullTypes` in the same order. We
 262             // establish the order by first comparing the hash-codes for performance reasons, and
 263             // then comparing the internal names of the types.
 264             int hashA = a.getName().hashCode();
 265             int hashB = b.getName().hashCode();
 266             if (hashA < hashB) {
 267                 return meetOrderedNonNullTypes(a, b);
 268             } else if (hashB < hashA) {
 269                 return meetOrderedNonNullTypes(b, a);
 270             } else {
 271                 int diff = a.getName().compareTo(b.getName());
 272                 if (diff <= 0) {
 273                     return meetOrderedNonNullTypes(a, b);
 274                 } else {
 275                     return meetOrderedNonNullTypes(b, a);
 276                 }
 277             }
 278         }
 279     }
 280 
 281     private static ResolvedJavaType meetOrderedNonNullTypes(ResolvedJavaType a, ResolvedJavaType b) {
 282         ResolvedJavaType result = a.findLeastCommonAncestor(b);
 283         if (result.isJavaLangObject() && a.isInterface() && b.isInterface()) {
 284             // Both types are incompatible interfaces => search for first possible common
 285             // ancestor match among super interfaces.
 286             ResolvedJavaType[] interfacesA = a.getInterfaces();
 287             ResolvedJavaType[] interfacesB = b.getInterfaces();
 288             for (int i = 0; i < interfacesA.length; ++i) {
 289                 ResolvedJavaType interface1 = interfacesA[i];
 290                 for (int j = 0; j < interfacesB.length; ++j) {
 291                     ResolvedJavaType interface2 = interfacesB[j];
 292                     ResolvedJavaType leastCommon = meetTypes(interface1, interface2);
 293                     if (leastCommon.isInterface()) {
 294                         return leastCommon;
 295                     }
 296                 }
 297             }
 298         }
 299         return result;
 300     }
 301 
 302     @Override
 303     public int hashCode() {
 304         final int prime = 31;
 305         int result = 1;
 306         result = prime * result + super.hashCode();
 307         result = prime * result + (exactType ? 1231 : 1237);
 308         result = prime * result + ((type == null || type.isJavaLangObject()) ? 0 : type.hashCode());
 309         return result;
 310     }
 311 
 312     @Override
 313     public boolean equals(Object obj) {
 314         if (this == obj) {
 315             return true;
 316         }
 317         if (obj == null || getClass() != obj.getClass()) {
 318             return false;
 319         }
 320         AbstractObjectStamp other = (AbstractObjectStamp) obj;
 321         if (exactType != other.exactType) {
 322             return false;
 323         }
 324         // null == java.lang.Object
 325         if (type == null) {
 326             if (other.type != null && !other.type.isJavaLangObject()) {
 327                 return false;
 328             }
 329         } else if (other.type == null) {
 330             if (type != null && !type.isJavaLangObject()) {
 331                 return false;
 332             }
 333         } else if (!type.equals(other.type)) {
 334             return false;
 335         }
 336         return super.equals(other);
 337     }
 338 }