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 }