1 /*
   2  * Copyright (c) 2011, 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 
  58         public AssumptionResult(T result, Assumption... assumptions) {
  59             this.result = result;
  60             this.assumptions = assumptions;
  61         }
  62 
  63         public AssumptionResult(T result) {
  64             this(result, EMPTY);
  65         }
  66 
  67         public T getResult() {
  68             return result;
  69         }
  70 
  71         public boolean isAssumptionFree() {
  72             return assumptions.length == 0;
  73         }
  74 
  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 
  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 
  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 {@link Object#finalize()}).
 102      */
 103     public static final class NoFinalizableSubclass extends Assumption {
 104 
 105         private ResolvedJavaType receiverType;
 106 
 107         public NoFinalizableSubclass(ResolvedJavaType receiverType) {
 108             this.receiverType = receiverType;
 109         }
 110 
 111         @Override
 112         public int hashCode() {
 113             return 31 + receiverType.hashCode();
 114         }
 115 
 116         @Override
 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
 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 
 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
 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
 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
 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 
 190         public LeafType(ResolvedJavaType context) {
 191             assert !context.isLeaf() : "assumption isn't required for leaf types";
 192             this.context = context;
 193         }
 194 
 195         @Override
 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
 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
 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 
 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
 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
 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
 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 
 278         public CallSiteTargetValue(JavaConstant callSite, JavaConstant methodHandle) {
 279             this.callSite = callSite;
 280             this.methodHandle = methodHandle;
 281         }
 282 
 283         @Override
 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
 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
 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      */
 314     public boolean isEmpty() {
 315         return assumptions.isEmpty();
 316     }
 317 
 318     @Override
 319     public int hashCode() {
 320         throw new UnsupportedOperationException("hashCode");
 321     }
 322 
 323     @Override
 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
 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      */
 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      */
 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      */
 371     public void recordConcreteMethod(ResolvedJavaMethod method, ResolvedJavaType context, ResolvedJavaMethod impl) {
 372         record(new ConcreteMethod(method, context, impl));
 373     }
 374 
 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      */
 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      */
 389     public void record(Assumptions other) {
 390         assert other != this;
 391         assumptions.addAll(other.assumptions);
 392     }
 393 
 394     @Override
 395     public String toString() {
 396         return "Assumptions[" + assumptions + "]";
 397     }
 398 }