1 /*
2  * Copyright (c) 2011, 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 package jdk.vm.ci.meta;
24 
25 import java.util.Arrays;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Set;
29 
30 /**
31  * Class for recording assumptions made during compilation.
32  */
33 public final class Assumptions implements Iterable<Assumptions.Assumption> {
34 
35     /**
36      * Abstract base class for assumptions. An assumption assumes a property of the runtime that may
37      * be invalidated by subsequent execution (e.g., that a class has no subclasses implementing
38      * {@link NoFinalizableSubclass Object.finalize()}).
39      */
40     public abstract static class Assumption {
41     }
42 
43     /**
44      * A class for providing information that is only valid in association with a set of
45      * {@link Assumption}s. It is permissible for AssumptionResults to have no assumptions at all.
46      * For instance, if {@link ResolvedJavaType#isLeaf()} returns true for a type
47      * {@link ResolvedJavaType#findLeafConcreteSubtype()} can return an AssumptionResult with no
48      * assumptions since the leaf information is statically true.
49      *
50      * @param <T>
51      */
52     public static class AssumptionResult<T> {
53         Assumption[] assumptions;
54         final T result;
55 
56         private static final Assumption[] EMPTY = new Assumption[0];
57 
AssumptionResult(T result, Assumption... assumptions)58         public AssumptionResult(T result, Assumption... assumptions) {
59             this.result = result;
60             this.assumptions = assumptions;
61         }
62 
AssumptionResult(T result)63         public AssumptionResult(T result) {
64             this(result, EMPTY);
65         }
66 
getResult()67         public T getResult() {
68             return result;
69         }
70 
isAssumptionFree()71         public boolean isAssumptionFree() {
72             return assumptions.length == 0;
73         }
74 
add(AssumptionResult<T> other)75         public void add(AssumptionResult<T> other) {
76             Assumption[] newAssumptions = Arrays.copyOf(this.assumptions, this.assumptions.length + other.assumptions.length);
77             System.arraycopy(other.assumptions, 0, newAssumptions, this.assumptions.length, other.assumptions.length);
78             this.assumptions = newAssumptions;
79         }
80 
canRecordTo(Assumptions target)81         public boolean canRecordTo(Assumptions target) {
82             /*
83              * We can use the result if it is either assumption free, or if we have a valid
84              * Assumptions object where we can record assumptions.
85              */
86             return assumptions.length == 0 || target != null;
87         }
88 
recordTo(Assumptions target)89         public void recordTo(Assumptions target) {
90             assert canRecordTo(target);
91 
92             if (assumptions.length > 0) {
93                 for (Assumption assumption : assumptions) {
94                     target.record(assumption);
95                 }
96             }
97         }
98     }
99 
100     /**
101      * An assumption that a given class has no subclasses implementing {@code Object#finalize()}).
102      */
103     public static final class NoFinalizableSubclass extends Assumption {
104 
105         private ResolvedJavaType receiverType;
106 
NoFinalizableSubclass(ResolvedJavaType receiverType)107         public NoFinalizableSubclass(ResolvedJavaType receiverType) {
108             this.receiverType = receiverType;
109         }
110 
111         @Override
hashCode()112         public int hashCode() {
113             return 31 + receiverType.hashCode();
114         }
115 
116         @Override
equals(Object obj)117         public boolean equals(Object obj) {
118             if (obj instanceof NoFinalizableSubclass) {
119                 NoFinalizableSubclass other = (NoFinalizableSubclass) obj;
120                 return other.receiverType.equals(receiverType);
121             }
122             return false;
123         }
124 
125         @Override
toString()126         public String toString() {
127             return "NoFinalizableSubclass[receiverType=" + receiverType.toJavaName() + "]";
128         }
129 
130     }
131 
132     /**
133      * An assumption that a given abstract or interface type has one direct concrete subtype. There
134      * is no requirement that the subtype is a leaf type.
135      */
136     public static final class ConcreteSubtype extends Assumption {
137 
138         /**
139          * Type the assumption is made about.
140          */
141         public final ResolvedJavaType context;
142 
143         /**
144          * Assumed concrete sub-type of the context type.
145          */
146         public final ResolvedJavaType subtype;
147 
ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype)148         public ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
149             this.context = context;
150             this.subtype = subtype;
151             assert context.isAbstract();
152             assert subtype.isConcrete() || context.isInterface() : subtype.toString() + " : " + context.toString();
153             assert !subtype.isArray() || subtype.getElementalType().isFinalFlagSet() : subtype.toString() + " : " + context.toString();
154         }
155 
156         @Override
hashCode()157         public int hashCode() {
158             final int prime = 31;
159             int result = 1;
160             result = prime * result + context.hashCode();
161             result = prime * result + subtype.hashCode();
162             return result;
163         }
164 
165         @Override
equals(Object obj)166         public boolean equals(Object obj) {
167             if (obj instanceof ConcreteSubtype) {
168                 ConcreteSubtype other = (ConcreteSubtype) obj;
169                 return other.context.equals(context) && other.subtype.equals(subtype);
170             }
171             return false;
172         }
173 
174         @Override
toString()175         public String toString() {
176             return "ConcreteSubtype[context=" + context.toJavaName() + ", subtype=" + subtype.toJavaName() + "]";
177         }
178     }
179 
180     /**
181      * An assumption that a given type has no subtypes.
182      */
183     public static final class LeafType extends Assumption {
184 
185         /**
186          * Type the assumption is made about.
187          */
188         public final ResolvedJavaType context;
189 
LeafType(ResolvedJavaType context)190         public LeafType(ResolvedJavaType context) {
191             assert !context.isLeaf() : "assumption isn't required for leaf types";
192             this.context = context;
193         }
194 
195         @Override
hashCode()196         public int hashCode() {
197             final int prime = 31;
198             int result = 1;
199             result = prime * result + context.hashCode();
200             return result;
201         }
202 
203         @Override
equals(Object obj)204         public boolean equals(Object obj) {
205             if (obj instanceof LeafType) {
206                 LeafType other = (LeafType) obj;
207                 return other.context.equals(context);
208             }
209             return false;
210         }
211 
212         @Override
toString()213         public String toString() {
214             return "LeafSubtype[context=" + context.toJavaName() + "]";
215         }
216     }
217 
218     /**
219      * An assumption that a given virtual method has a given unique implementation.
220      */
221     public static final class ConcreteMethod extends Assumption {
222 
223         /**
224          * A virtual (or interface) method whose unique implementation for the receiver type in
225          * {@link #context} is {@link #impl}.
226          */
227         public final ResolvedJavaMethod method;
228 
229         /**
230          * A receiver type.
231          */
232         public final ResolvedJavaType context;
233 
234         /**
235          * The unique implementation of {@link #method} for {@link #context}.
236          */
237         public final ResolvedJavaMethod impl;
238 
ConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl)239         public ConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
240             this.method = method;
241             this.context = context;
242             this.impl = impl;
243         }
244 
245         @Override
hashCode()246         public int hashCode() {
247             final int prime = 31;
248             int result = 1;
249             result = prime * result + method.hashCode();
250             result = prime * result + context.hashCode();
251             result = prime * result + impl.hashCode();
252             return result;
253         }
254 
255         @Override
equals(Object obj)256         public boolean equals(Object obj) {
257             if (obj instanceof ConcreteMethod) {
258                 ConcreteMethod other = (ConcreteMethod) obj;
259                 return other.method.equals(method) && other.context.equals(context) && other.impl.equals(impl);
260             }
261             return false;
262         }
263 
264         @Override
toString()265         public String toString() {
266             return "ConcreteMethod[method=" + method.format("%H.%n(%p)%r") + ", context=" + context.toJavaName() + ", impl=" + impl.format("%H.%n(%p)%r") + "]";
267         }
268     }
269 
270     /**
271      * An assumption that a given call site's method handle did not change.
272      */
273     public static final class CallSiteTargetValue extends Assumption {
274 
275         public final JavaConstant callSite;
276         public final JavaConstant methodHandle;
277 
CallSiteTargetValue(JavaConstant callSite, JavaConstant methodHandle)278         public CallSiteTargetValue(JavaConstant callSite, JavaConstant methodHandle) {
279             this.callSite = callSite;
280             this.methodHandle = methodHandle;
281         }
282 
283         @Override
hashCode()284         public int hashCode() {
285             final int prime = 31;
286             int result = 1;
287             result = prime * result + callSite.hashCode();
288             result = prime * result + methodHandle.hashCode();
289             return result;
290         }
291 
292         @Override
equals(Object obj)293         public boolean equals(Object obj) {
294             if (obj instanceof CallSiteTargetValue) {
295                 CallSiteTargetValue other = (CallSiteTargetValue) obj;
296                 return callSite.equals(other.callSite) && methodHandle.equals(other.methodHandle);
297             }
298             return false;
299         }
300 
301         @Override
toString()302         public String toString() {
303             return "CallSiteTargetValue[callSite=" + callSite + ", methodHandle=" + methodHandle + "]";
304         }
305     }
306 
307     private final Set<Assumption> assumptions = new HashSet<>();
308 
309     /**
310      * Returns whether any assumptions have been registered.
311      *
312      * @return {@code true} if at least one assumption has been registered, {@code false} otherwise.
313      */
isEmpty()314     public boolean isEmpty() {
315         return assumptions.isEmpty();
316     }
317 
318     @Override
hashCode()319     public int hashCode() {
320         throw new UnsupportedOperationException("hashCode");
321     }
322 
323     @Override
equals(Object obj)324     public boolean equals(Object obj) {
325         if (this == obj) {
326             return true;
327         }
328         if (obj instanceof Assumptions) {
329             Assumptions that = (Assumptions) obj;
330             if (!this.assumptions.equals(that.assumptions)) {
331                 return false;
332             }
333             return true;
334         }
335         return false;
336     }
337 
338     @Override
iterator()339     public Iterator<Assumption> iterator() {
340         return assumptions.iterator();
341     }
342 
343     /**
344      * Records an assumption that the specified type has no finalizable subclasses.
345      *
346      * @param receiverType the type that is assumed to have no finalizable subclasses
347      */
recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType)348     public void recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType) {
349         record(new NoFinalizableSubclass(receiverType));
350     }
351 
352     /**
353      * Records that {@code subtype} is the only concrete subtype in the class hierarchy below
354      * {@code context}.
355      *
356      * @param context the root of the subtree of the class hierarchy that this assumptions is about
357      * @param subtype the one concrete subtype
358      */
recordConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype)359     public void recordConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) {
360         record(new ConcreteSubtype(context, subtype));
361     }
362 
363     /**
364      * Records that {@code impl} is the only possible concrete target for a virtual call to
365      * {@code method} with a receiver of type {@code context}.
366      *
367      * @param method a method that is the target of a virtual call
368      * @param context the receiver type of a call to {@code method}
369      * @param impl the concrete method that is the only possible target for the virtual call
370      */
recordConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl)371     public void recordConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
372         record(new ConcreteMethod(method, context, impl));
373     }
374 
record(Assumption assumption)375     public void record(Assumption assumption) {
376         assumptions.add(assumption);
377     }
378 
379     /**
380      * Gets a copy of the assumptions recorded in this object as an array.
381      */
toArray()382     public Assumption[] toArray() {
383         return assumptions.toArray(new Assumption[assumptions.size()]);
384     }
385 
386     /**
387      * Copies assumptions recorded by another {@link Assumptions} object into this object.
388      */
record(Assumptions other)389     public void record(Assumptions other) {
390         assert other != this;
391         assumptions.addAll(other.assumptions);
392     }
393 
394     @Override
toString()395     public String toString() {
396         return "Assumptions[" + assumptions + "]";
397     }
398 }
399