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.internal.jvmci.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 }