001/*
002 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.compiler.common.type;
024
025import java.util.*;
026
027import jdk.internal.jvmci.meta.*;
028
029/**
030 * Type describing all pointers to Java objects.
031 */
032public abstract class AbstractObjectStamp extends AbstractPointerStamp {
033
034    private final ResolvedJavaType type;
035    private final boolean exactType;
036
037    protected AbstractObjectStamp(ResolvedJavaType type, boolean exactType, boolean nonNull, boolean alwaysNull) {
038        super(nonNull, alwaysNull);
039        this.type = type;
040        if (!exactType && type != null && type.isLeaf()) {
041            this.exactType = true;
042        } else {
043            this.exactType = exactType;
044        }
045    }
046
047    protected abstract AbstractObjectStamp copyWith(ResolvedJavaType newType, boolean newExactType, boolean newNonNull, boolean newAlwaysNull);
048
049    @Override
050    protected final AbstractPointerStamp copyWith(boolean newNonNull, boolean newAlwaysNull) {
051        return copyWith(type, exactType, newNonNull, newAlwaysNull);
052    }
053
054    @Override
055    public Stamp unrestricted() {
056        return copyWith(null, false, false, false);
057    }
058
059    @Override
060    public Stamp empty() {
061        return copyWith(null, true, true, false);
062    }
063
064    @Override
065    public Stamp constant(Constant c, MetaAccessProvider meta) {
066        JavaConstant jc = (JavaConstant) c;
067        ResolvedJavaType constType = jc.isNull() ? null : meta.lookupJavaType(jc);
068        return copyWith(constType, jc.isNonNull(), jc.isNonNull(), jc.isNull());
069    }
070
071    @Override
072    public boolean hasValues() {
073        return !exactType || (type != null && (isConcreteType(type)));
074    }
075
076    @Override
077    public Kind getStackKind() {
078        return Kind.Object;
079    }
080
081    @Override
082    public ResolvedJavaType javaType(MetaAccessProvider metaAccess) {
083        if (type != null) {
084            return type;
085        }
086        return metaAccess.lookupJavaType(Object.class);
087    }
088
089    public ResolvedJavaType type() {
090        return type;
091    }
092
093    public boolean isExactType() {
094        return exactType;
095    }
096
097    protected void appendString(StringBuilder str) {
098        if (this.isEmpty()) {
099            str.append(" empty");
100        } else {
101            str.append(nonNull() ? "!" : "").append(exactType ? "#" : "").append(' ').append(type == null ? "-" : type.getName()).append(alwaysNull() ? " NULL" : "");
102        }
103    }
104
105    @Override
106    public Stamp meet(Stamp otherStamp) {
107        if (this == otherStamp) {
108            return this;
109        }
110        AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
111        if (isEmpty()) {
112            return other;
113        } else if (other.isEmpty()) {
114            return this;
115        }
116        ResolvedJavaType meetType;
117        boolean meetExactType;
118        boolean meetNonNull;
119        boolean meetAlwaysNull;
120        if (other.alwaysNull()) {
121            meetType = type();
122            meetExactType = exactType;
123            meetNonNull = false;
124            meetAlwaysNull = alwaysNull();
125        } else if (alwaysNull()) {
126            meetType = other.type();
127            meetExactType = other.exactType;
128            meetNonNull = false;
129            meetAlwaysNull = other.alwaysNull();
130        } else {
131            meetType = meetTypes(type(), other.type());
132            meetExactType = exactType && other.exactType;
133            if (meetExactType && type != null && other.type != null) {
134                // meeting two valid exact types may result in a non-exact type
135                meetExactType = Objects.equals(meetType, type) && Objects.equals(meetType, other.type);
136            }
137            meetNonNull = nonNull() && other.nonNull();
138            meetAlwaysNull = false;
139        }
140
141        if (Objects.equals(meetType, type) && meetExactType == exactType && meetNonNull == nonNull() && meetAlwaysNull == alwaysNull()) {
142            return this;
143        } else if (Objects.equals(meetType, other.type) && meetExactType == other.exactType && meetNonNull == other.nonNull() && meetAlwaysNull == other.alwaysNull()) {
144            return other;
145        } else {
146            return copyWith(meetType, meetExactType, meetNonNull, meetAlwaysNull);
147        }
148    }
149
150    @Override
151    public Stamp join(Stamp otherStamp) {
152        return join0(otherStamp, false);
153    }
154
155    /**
156     * Returns the stamp representing the type of this stamp after a cast to the type represented by
157     * the {@code to} stamp. While this is very similar to a {@link #join} operation, in the case
158     * where both types are not obviously related, the cast operation will prefer the type of the
159     * {@code to} stamp. This is necessary as long as ObjectStamps are not able to accurately
160     * represent intersection types.
161     *
162     * For example when joining the {@link RandomAccess} type with the {@link AbstractList} type,
163     * without intersection types, this would result in the most generic type ({@link Object} ). For
164     * this reason, in some cases a {@code castTo} operation is preferable in order to keep at least
165     * the {@link AbstractList} type.
166     *
167     * @param other the stamp this stamp should be casted to
168     * @return the new improved stamp or {@code null} if this stamp cannot be improved
169     */
170    @Override
171    public Stamp improveWith(Stamp other) {
172        return join0(other, true);
173    }
174
175    private Stamp join0(Stamp otherStamp, boolean improve) {
176        if (this == otherStamp) {
177            return this;
178        }
179        AbstractObjectStamp other = (AbstractObjectStamp) otherStamp;
180        if (isEmpty()) {
181            return this;
182        } else if (other.isEmpty()) {
183            return other;
184        }
185
186        ResolvedJavaType joinType;
187        boolean joinAlwaysNull = alwaysNull() || other.alwaysNull();
188        boolean joinNonNull = nonNull() || other.nonNull();
189        boolean joinExactType = exactType || other.exactType;
190        if (Objects.equals(type, other.type)) {
191            joinType = type;
192        } else if (type == null && other.type == null) {
193            joinType = null;
194        } else if (type == null) {
195            joinType = other.type;
196        } else if (other.type == null) {
197            joinType = type;
198        } else {
199            // both types are != null and different
200            if (type.isAssignableFrom(other.type)) {
201                joinType = other.type;
202                if (exactType) {
203                    joinAlwaysNull = true;
204                }
205            } else if (other.type.isAssignableFrom(type)) {
206                joinType = type;
207                if (other.exactType) {
208                    joinAlwaysNull = true;
209                }
210            } else {
211                if (improve) {
212                    joinType = type;
213                    joinExactType = exactType;
214                } else {
215                    joinType = null;
216                }
217
218                if (joinExactType || (!isInterfaceOrArrayOfInterface(type) && !isInterfaceOrArrayOfInterface(other.type))) {
219                    joinAlwaysNull = true;
220                }
221            }
222        }
223        if (joinAlwaysNull) {
224            joinType = null;
225            joinExactType = false;
226        }
227        if (joinExactType && joinType == null) {
228            return empty();
229        }
230        if (joinAlwaysNull && joinNonNull) {
231            return empty();
232        } else if (joinExactType && !isConcreteType(joinType)) {
233            return empty();
234        }
235        if (Objects.equals(joinType, type) && joinExactType == exactType && joinNonNull == nonNull() && joinAlwaysNull == alwaysNull()) {
236            return this;
237        } else if (Objects.equals(joinType, other.type) && joinExactType == other.exactType && joinNonNull == other.nonNull() && joinAlwaysNull == other.alwaysNull()) {
238            return other;
239        } else {
240            return copyWith(joinType, joinExactType, joinNonNull, joinAlwaysNull);
241        }
242    }
243
244    private static boolean isInterfaceOrArrayOfInterface(ResolvedJavaType t) {
245        return t.isInterface() || (t.isArray() && t.getElementalType().isInterface());
246    }
247
248    public static boolean isConcreteType(ResolvedJavaType type) {
249        return !(type.isAbstract() && !type.isArray());
250    }
251
252    private static ResolvedJavaType meetTypes(ResolvedJavaType a, ResolvedJavaType b) {
253        if (Objects.equals(a, b)) {
254            return a;
255        } else if (a == null || b == null) {
256            return null;
257        } else {
258            return a.findLeastCommonAncestor(b);
259        }
260    }
261
262    @Override
263    public int hashCode() {
264        final int prime = 31;
265        int result = 1;
266        result = prime * result + super.hashCode();
267        result = prime * result + (exactType ? 1231 : 1237);
268        result = prime * result + ((type == null || type.isJavaLangObject()) ? 0 : type.hashCode());
269        return result;
270    }
271
272    @Override
273    public boolean equals(Object obj) {
274        if (this == obj) {
275            return true;
276        }
277        if (obj == null || getClass() != obj.getClass()) {
278            return false;
279        }
280        AbstractObjectStamp other = (AbstractObjectStamp) obj;
281        if (exactType != other.exactType) {
282            return false;
283        }
284        // null == java.lang.Object
285        if (type == null) {
286            if (other.type != null && !other.type.isJavaLangObject()) {
287                return false;
288            }
289        } else if (other.type == null) {
290            if (type != null && !type.isJavaLangObject()) {
291                return false;
292            }
293        } else if (!type.equals(other.type)) {
294            return false;
295        }
296        return super.equals(other);
297    }
298}