001/*
002 * Copyright (c) 2011, 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 jdk.internal.jvmci.meta;
024
025import java.lang.invoke.*;
026import java.util.*;
027
028/**
029 * Class for recording assumptions made during compilation.
030 */
031public final class Assumptions implements Iterable<Assumptions.Assumption> {
032
033    /**
034     * Abstract base class for assumptions. An assumption assumes a property of the runtime that may
035     * be invalidated by subsequent execution (e.g., that a class has no subclasses implementing
036     * {@link NoFinalizableSubclass Object.finalize()}).
037     */
038    public abstract static class Assumption {
039    }
040
041    /**
042     * A class for providing information that is only valid in association with a set of
043     * {@link Assumption}s.
044     *
045     * @param <T>
046     */
047    public static class AssumptionResult<T> {
048        Assumption[] assumptions;
049        final T result;
050
051        private static final Assumption[] EMPTY = new Assumption[0];
052
053        public AssumptionResult(T result, Assumption... assumptions) {
054            this.result = result;
055            this.assumptions = assumptions;
056        }
057
058        public AssumptionResult(T result) {
059            this(result, EMPTY);
060        }
061
062        public T getResult() {
063            return result;
064        }
065
066        public boolean isAssumptionFree() {
067            return assumptions.length == 0;
068        }
069
070        public void add(AssumptionResult<T> other) {
071            Assumption[] newAssumptions = Arrays.copyOf(this.assumptions, this.assumptions.length + other.assumptions.length);
072            System.arraycopy(other.assumptions, 0, newAssumptions, this.assumptions.length, other.assumptions.length);
073            this.assumptions = newAssumptions;
074        }
075    }
076
077    /**
078     * An assumption that a given class has no subclasses implementing {@link Object#finalize()}).
079     */
080    public static final class NoFinalizableSubclass extends Assumption {
081
082        private ResolvedJavaType receiverType;
083
084        public NoFinalizableSubclass(ResolvedJavaType receiverType) {
085            this.receiverType = receiverType;
086        }
087
088        @Override
089        public int hashCode() {
090            return 31 + receiverType.hashCode();
091        }
092
093        @Override
094        public boolean equals(Object obj) {
095            if (obj instanceof NoFinalizableSubclass) {
096                NoFinalizableSubclass other = (NoFinalizableSubclass) obj;
097                return other.receiverType.equals(receiverType);
098            }
099            return false;
100        }
101
102        @Override
103        public String toString() {
104            return "NoFinalizableSubclass[receiverType=" + receiverType.toJavaName() + "]";
105        }
106
107    }
108
109    /**
110     * An assumption that a given abstract or interface type has one direct concrete subtype. There
111     * is no requirement that the subtype is a leaf type.
112     */
113    public static final class ConcreteSubtype extends Assumption {
114
115        /**
116         * Type the assumption is made about.
117         */
118        public final ResolvedJavaType context;
119
120        /**
121         * Assumed concrete sub-type of the context type.
122         */
123        public final ResolvedJavaType subtype;
124
125        public ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
126            this.context = context;
127            this.subtype = subtype;
128            assert context.isAbstract();
129            assert subtype.isConcrete() || context.isInterface() : subtype.toString() + " : " + context.toString();
130            assert !subtype.isArray() || subtype.getElementalType().isFinalFlagSet() : subtype.toString() + " : " + context.toString();
131        }
132
133        @Override
134        public int hashCode() {
135            final int prime = 31;
136            int result = 1;
137            result = prime * result + context.hashCode();
138            result = prime * result + subtype.hashCode();
139            return result;
140        }
141
142        @Override
143        public boolean equals(Object obj) {
144            if (obj instanceof ConcreteSubtype) {
145                ConcreteSubtype other = (ConcreteSubtype) obj;
146                return other.context.equals(context) && other.subtype.equals(subtype);
147            }
148            return false;
149        }
150
151        @Override
152        public String toString() {
153            return "ConcreteSubtype[context=" + context.toJavaName() + ", subtype=" + subtype.toJavaName() + "]";
154        }
155    }
156
157    /**
158     * An assumption that a given type has no subtypes.
159     */
160    public static final class LeafType extends Assumption {
161
162        /**
163         * Type the assumption is made about.
164         */
165        public final ResolvedJavaType context;
166
167        public LeafType(ResolvedJavaType context) {
168            this.context = context;
169        }
170
171        @Override
172        public int hashCode() {
173            final int prime = 31;
174            int result = 1;
175            result = prime * result + context.hashCode();
176            return result;
177        }
178
179        @Override
180        public boolean equals(Object obj) {
181            if (obj instanceof LeafType) {
182                LeafType other = (LeafType) obj;
183                return other.context.equals(context);
184            }
185            return false;
186        }
187
188        @Override
189        public String toString() {
190            return "LeafSubtype[context=" + context.toJavaName() + "]";
191        }
192    }
193
194    /**
195     * An assumption that a given virtual method has a given unique implementation.
196     */
197    public static final class ConcreteMethod extends Assumption {
198
199        /**
200         * A virtual (or interface) method whose unique implementation for the receiver type in
201         * {@link #context} is {@link #impl}.
202         */
203        public final ResolvedJavaMethod method;
204
205        /**
206         * A receiver type.
207         */
208        public final ResolvedJavaType context;
209
210        /**
211         * The unique implementation of {@link #method} for {@link #context}.
212         */
213        public final ResolvedJavaMethod impl;
214
215        public ConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
216            this.method = method;
217            this.context = context;
218            this.impl = impl;
219        }
220
221        @Override
222        public int hashCode() {
223            final int prime = 31;
224            int result = 1;
225            result = prime * result + method.hashCode();
226            result = prime * result + context.hashCode();
227            result = prime * result + impl.hashCode();
228            return result;
229        }
230
231        @Override
232        public boolean equals(Object obj) {
233            if (obj instanceof ConcreteMethod) {
234                ConcreteMethod other = (ConcreteMethod) obj;
235                return other.method.equals(method) && other.context.equals(context) && other.impl.equals(impl);
236            }
237            return false;
238        }
239
240        @Override
241        public String toString() {
242            return "ConcreteMethod[method=" + method.format("%H.%n(%p)%r") + ", context=" + context.toJavaName() + ", impl=" + impl.format("%H.%n(%p)%r") + "]";
243        }
244    }
245
246    /**
247     * An assumption that a given call site's method handle did not change.
248     */
249    public static final class CallSiteTargetValue extends Assumption {
250
251        public final CallSite callSite;
252        public final MethodHandle methodHandle;
253
254        public CallSiteTargetValue(CallSite callSite, MethodHandle methodHandle) {
255            this.callSite = callSite;
256            this.methodHandle = methodHandle;
257        }
258
259        @Override
260        public int hashCode() {
261            final int prime = 31;
262            int result = 1;
263            result = prime * result + callSite.hashCode();
264            result = prime * result + methodHandle.hashCode();
265            return result;
266        }
267
268        @Override
269        public boolean equals(Object obj) {
270            if (obj instanceof CallSiteTargetValue) {
271                CallSiteTargetValue other = (CallSiteTargetValue) obj;
272                return callSite.equals(other.callSite) && methodHandle.equals(other.methodHandle);
273            }
274            return false;
275        }
276
277        @Override
278        public String toString() {
279            return "CallSiteTargetValue[callSite=" + callSite + ", methodHandle=" + methodHandle + "]";
280        }
281    }
282
283    private final Set<Assumption> assumptions = new HashSet<>();
284
285    /**
286     * Returns whether any assumptions have been registered.
287     *
288     * @return {@code true} if at least one assumption has been registered, {@code false} otherwise.
289     */
290    public boolean isEmpty() {
291        return assumptions.isEmpty();
292    }
293
294    @Override
295    public int hashCode() {
296        throw new UnsupportedOperationException("hashCode");
297    }
298
299    @Override
300    public boolean equals(Object obj) {
301        if (this == obj) {
302            return true;
303        }
304        if (obj instanceof Assumptions) {
305            Assumptions that = (Assumptions) obj;
306            if (!this.assumptions.equals(that.assumptions)) {
307                return false;
308            }
309            return true;
310        }
311        return false;
312    }
313
314    @Override
315    public Iterator<Assumption> iterator() {
316        return assumptions.iterator();
317    }
318
319    /**
320     * Records an assumption that the specified type has no finalizable subclasses.
321     *
322     * @param receiverType the type that is assumed to have no finalizable subclasses
323     */
324    public void recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType) {
325        record(new NoFinalizableSubclass(receiverType));
326    }
327
328    /**
329     * Records that {@code subtype} is the only concrete subtype in the class hierarchy below
330     * {@code context}.
331     *
332     * @param context the root of the subtree of the class hierarchy that this assumptions is about
333     * @param subtype the one concrete subtype
334     */
335    public void recordConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
336        record(new ConcreteSubtype(context, subtype));
337    }
338
339    /**
340     * Records that {@code impl} is the only possible concrete target for a virtual call to
341     * {@code method} with a receiver of type {@code context}.
342     *
343     * @param method a method that is the target of a virtual call
344     * @param context the receiver type of a call to {@code method}
345     * @param impl the concrete method that is the only possible target for the virtual call
346     */
347    public void recordConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
348        record(new ConcreteMethod(method, context, impl));
349    }
350
351    public void record(AssumptionResult<?> result) {
352        for (Assumption assumption : result.assumptions) {
353            record(assumption);
354        }
355    }
356
357    public void record(Assumption assumption) {
358        assumptions.add(assumption);
359    }
360
361    /**
362     * Gets a copy of the assumptions recorded in this object as an array.
363     */
364    public Assumption[] toArray() {
365        return assumptions.toArray(new Assumption[assumptions.size()]);
366    }
367
368    /**
369     * Copies assumptions recorded by another {@link Assumptions} object into this object.
370     */
371    public void record(Assumptions other) {
372        assert other != this;
373        assumptions.addAll(other.assumptions);
374    }
375
376    @Override
377    public String toString() {
378        return "Assumptions[" + assumptions + "]";
379    }
380}