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 }