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