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