1/*
2 * Copyright (c) 2016, 2016, 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 */
23package org.graalvm.compiler.core.common.type;
24
25import jdk.vm.ci.meta.Assumptions;
26import jdk.vm.ci.meta.ResolvedJavaType;
27
28/**
29 * This class represents a reference to a Java type and whether this reference is referring only to
30 * the represented type or also to its sub types in the class hierarchy. When creating a type
31 * reference, the following options have to be considered:
32 *
33 * <ul>
34 * <li>The reference should always only refer to the given concrete type. Use
35 * {@link #createExactTrusted(ResolvedJavaType)} for this purpose.</li>
36 * <li>The reference should be created without assumptions about the class hierarchy. The returned
37 * reference is exact only when the type is a leaf type (i.e., it cannot have subclasses). Depending
38 * on whether interface types can be trusted for this type reference use
39 * {@link #createWithoutAssumptions} or {@link #createTrustedWithoutAssumptions}.</li>
40 * <li>The reference should be created using assumptions about the class hierarchy. The returned
41 * reference is also exact, when there is only a single concrete sub type for the given type.
42 * Depending on whether interface types can be trusted for this type reference use {@link #create}
43 * or {@link #createTrusted}.</li>
44 * </ul>
45 *
46 * For the methods with untrusted interface types, a {@code null} reference will be constructed for
47 * untrusted interface types. Examples for interface types that cannot be trusted are types for
48 * parameters, fields, and return values. They are not checked by the Java verifier.
49 *
50 */
51public final class TypeReference {
52    private final ResolvedJavaType type;
53    private final boolean exactReference;
54
55    private TypeReference(ResolvedJavaType type, boolean exactReference) {
56        this.type = type;
57        this.exactReference = exactReference;
58    }
59
60    /**
61     * Creates an exact type reference using the given type.
62     */
63    public static TypeReference createExactTrusted(ResolvedJavaType type) {
64        if (type == null) {
65            return null;
66        }
67        return new TypeReference(type, true);
68    }
69
70    /**
71     * Creates a type reference using the given type without assumptions and without trusting
72     * interface types.
73     */
74    public static TypeReference createWithoutAssumptions(ResolvedJavaType type) {
75        return create(null, type);
76    }
77
78    /**
79     * Creates a type reference using the given type without assumptions and trusting interface
80     * types.
81     */
82    public static TypeReference createTrustedWithoutAssumptions(ResolvedJavaType type) {
83        return createTrusted(null, type);
84    }
85
86    /**
87     * Creates a type reference using the given type with assumptions and without trusting interface
88     * types.
89     */
90    public static TypeReference create(Assumptions assumptions, ResolvedJavaType type) {
91        return createTrusted(assumptions, filterInterfaceTypesOut(type));
92    }
93
94    /**
95     * Create a type reference using the given type with assumptions and trusting interface types.
96     */
97    public static TypeReference createTrusted(Assumptions assumptions, ResolvedJavaType type) {
98        if (type == null) {
99            return null;
100        }
101        ResolvedJavaType exactType = type.isLeaf() ? type : null;
102        if (exactType == null) {
103            Assumptions.AssumptionResult<ResolvedJavaType> leafConcreteSubtype = type.findLeafConcreteSubtype();
104            if (leafConcreteSubtype != null && leafConcreteSubtype.canRecordTo(assumptions)) {
105                leafConcreteSubtype.recordTo(assumptions);
106                exactType = leafConcreteSubtype.getResult();
107            }
108        }
109        if (exactType == null) {
110            return new TypeReference(type, false);
111        }
112        return new TypeReference(exactType, true);
113    }
114
115    /**
116     * The type this reference refers to.
117     */
118    public ResolvedJavaType getType() {
119        return type;
120    }
121
122    /**
123     * @return {@code true} if this reference is exact and only refers to the given type and
124     *         {@code false} if it also refers to its sub types.
125     */
126    public boolean isExact() {
127        return exactReference;
128    }
129
130    /**
131     * @return A new reference that is guaranteed to be exact.
132     */
133    public TypeReference asExactReference() {
134        if (isExact()) {
135            return this;
136        }
137        return new TypeReference(type, true);
138    }
139
140    private static ResolvedJavaType filterInterfaceTypesOut(ResolvedJavaType type) {
141        if (type != null) {
142            if (type.isArray()) {
143                ResolvedJavaType componentType = filterInterfaceTypesOut(type.getComponentType());
144                if (componentType != null) {
145                    return componentType.getArrayClass();
146                }
147                // Returns Object[].class
148                return type.getSuperclass().getArrayClass();
149            }
150            if (type.isInterface()) {
151                return null;
152            }
153        }
154        return type;
155    }
156
157    @Override
158    public String toString() {
159        return (isExact() ? "#" : "") + type;
160    }
161}
162