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