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 }