1 /* 2 * Copyright (c) 2013, 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.options; 24 25 import java.io.PrintStream; 26 import java.util.ArrayList; 27 import java.util.Collection; 28 import java.util.Collections; 29 import java.util.Comparator; 30 import java.util.HashMap; 31 import java.util.Map; 32 import java.util.Map.Entry; 33 import java.util.Objects; 34 35 /** 36 * An option value. 37 */ 38 public class OptionValue<T> { 39 /** 40 * Temporarily changes the value for an option. The {@linkplain OptionValue#getValue() value} of 41 * {@code option} is set to {@code value} until {@link OverrideScope#close()} is called on the 42 * object returned by this method. 43 * <p> 44 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 45 * used: 46 * 47 * <pre> 48 * try (OverrideScope s = OptionValue.override(myOption, myValue) { 49 * // code that depends on myOption == myValue 50 * } 51 * </pre> 52 */ 53 public static OverrideScope override(OptionValue<?> option, Object value) { 54 OverrideScope current = getOverrideScope(); 55 if (current == null) { 56 if (!value.equals(option.getValue())) { 57 return new SingleOverrideScope(option, value); 58 } 59 Map<OptionValue<?>, Object> overrides = Collections.emptyMap(); 60 return new MultipleOverridesScope(current, overrides); 61 } 62 return new MultipleOverridesScope(current, option, value); 63 } 64 65 /** 66 * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() 67 * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} 68 * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by 69 * this method. 70 * <p> 71 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 72 * used: 73 * 74 * <pre> 75 * Map<OptionValue, Object> overrides = new HashMap<>(); 76 * overrides.put(myOption1, myValue1); 77 * overrides.put(myOption2, myValue2); 78 * try (OverrideScope s = OptionValue.override(overrides) { 79 * // code that depends on myOption == myValue 80 * } 81 * </pre> 82 */ 83 public static OverrideScope override(Map<OptionValue<?>, Object> overrides) { 84 OverrideScope current = getOverrideScope(); 85 if (current == null && overrides.size() == 1) { 86 Entry<OptionValue<?>, Object> single = overrides.entrySet().iterator().next(); 87 OptionValue<?> option = single.getKey(); 88 Object overrideValue = single.getValue(); 89 if (!overrideValue.equals(option.getValue())) { 90 return new SingleOverrideScope(option, overrideValue); 91 } 92 } 93 return new MultipleOverridesScope(current, overrides); 94 } 95 96 /** 97 * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() 98 * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} 99 * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by 100 * this method. 101 * <p> 102 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 103 * used: 104 * 105 * <pre> 106 * try (OverrideScope s = OptionValue.override(myOption1, myValue1, myOption2, myValue2) { 107 * // code that depends on myOption == myValue 108 * } 109 * </pre> 110 * 111 * @param overrides overrides in the form {@code [option1, override1, option2, override2, ...]} 112 */ 113 public static OverrideScope override(Object... overrides) { 114 OverrideScope current = getOverrideScope(); 115 if (current == null && overrides.length == 2) { 116 OptionValue<?> option = (OptionValue<?>) overrides[0]; 117 Object overrideValue = overrides[1]; 118 if (!overrideValue.equals(option.getValue())) { 119 return new SingleOverrideScope(option, overrideValue); 120 } 121 } 122 Map<OptionValue<?>, Object> map = Collections.emptyMap(); 123 for (int i = 0; i < overrides.length; i += 2) { 124 OptionValue<?> option = (OptionValue<?>) overrides[i]; 125 Object overrideValue = overrides[i + 1]; 126 if (!overrideValue.equals(option.getValue())) { 127 if (map.isEmpty()) { 128 map = new HashMap<>(); 129 } 130 map.put(option, overrideValue); 131 } 132 } 133 return new MultipleOverridesScope(current, map); 134 } 135 136 private static final ThreadLocal<OverrideScope> overrideScopeTL = new ThreadLocal<>(); 137 138 protected static OverrideScope getOverrideScope() { 139 return overrideScopeTL.get(); 140 } 141 142 protected static void setOverrideScope(OverrideScope overrideScope) { 143 overrideScopeTL.set(overrideScope); 144 } 145 146 private T defaultValue; 147 148 /** 149 * The raw option value. 150 */ 151 protected T value; 152 153 private OptionDescriptor descriptor; 154 155 private long reads; 156 private OptionValue<?> next; 157 private static OptionValue<?> head; 158 159 private static final boolean ShowReadsHistogram = Boolean.getBoolean("jvmci.showOptionValueReadsHistogram"); 160 161 private static void addToHistogram(OptionValue<?> option) { 162 if (ShowReadsHistogram) { 163 synchronized (OptionValue.class) { 164 option.next = head; 165 head = option; 166 } 167 } 168 } 169 170 @SuppressWarnings("unchecked") 171 public OptionValue(T value) { 172 this.defaultValue = value; 173 this.value = (T) DEFAULT; 174 addToHistogram(this); 175 } 176 177 private static final Object DEFAULT = "DEFAULT"; 178 private static final Object UNINITIALIZED = "UNINITIALIZED"; 179 180 /** 181 * Creates an uninitialized option value for a subclass that initializes itself 182 * {@link #defaultValue() lazily}. 183 */ 184 @SuppressWarnings("unchecked") 185 protected OptionValue() { 186 this.defaultValue = (T) UNINITIALIZED; 187 this.value = (T) DEFAULT; 188 addToHistogram(this); 189 } 190 191 /** 192 * Lazy initialization of default value. 193 */ 194 protected T defaultValue() { 195 throw new InternalError("Option without a default value value must override defaultValue()"); 196 } 197 198 /** 199 * Sets the descriptor for this option. 200 */ 201 public void setDescriptor(OptionDescriptor descriptor) { 202 assert this.descriptor == null : "Overwriting existing descriptor"; 203 this.descriptor = descriptor; 204 } 205 206 /** 207 * Returns the descriptor for this option, if it has been set by 208 * {@link #setDescriptor(OptionDescriptor)}. 209 */ 210 public OptionDescriptor getDescriptor() { 211 return descriptor; 212 } 213 214 /** 215 * Gets the name of this option. The name for an option value with a null 216 * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is the value of 217 * {@link Object#toString()}. 218 */ 219 public String getName() { 220 return descriptor == null ? super.toString() : (descriptor.getDeclaringClass().getName() + "." + descriptor.getName()); 221 } 222 223 @Override 224 public String toString() { 225 return getName() + "=" + getValue(); 226 } 227 228 /** 229 * The initial value specified in source code. The returned value is not affected by calls to 230 * {@link #setValue(Object)} or registering {@link OverrideScope}s. Therefore, it is also not 231 * affected by options set on the command line. 232 */ 233 public T getDefaultValue() { 234 if (defaultValue == UNINITIALIZED) { 235 defaultValue = defaultValue(); 236 } 237 return defaultValue; 238 } 239 240 /** 241 * Returns true if the option has the same value that was set in the source code. 242 */ 243 public boolean hasDefaultValue() { 244 if (!(this instanceof StableOptionValue)) { 245 getValue(); // ensure initialized 246 } 247 return value == DEFAULT || Objects.equals(value, getDefaultValue()); 248 } 249 250 /** 251 * Gets the value of this option. 252 */ 253 public T getValue() { 254 if (ShowReadsHistogram) { 255 reads++; 256 } 257 if (!(this instanceof StableOptionValue)) { 258 OverrideScope overrideScope = getOverrideScope(); 259 if (overrideScope != null) { 260 T override = overrideScope.getOverride(this); 261 if (override != null) { 262 return override; 263 } 264 } 265 } 266 if (value != DEFAULT) { 267 return value; 268 } else { 269 return getDefaultValue(); 270 } 271 } 272 273 /** 274 * Gets the values of this option including overridden values. 275 * 276 * @param c the collection to which the values are added. If null, one is allocated. 277 * @return the collection to which the values were added in order from most overridden to 278 * current value 279 */ 280 @SuppressWarnings("unchecked") 281 public Collection<T> getValues(Collection<T> c) { 282 Collection<T> values = c == null ? new ArrayList<>() : c; 283 if (!(this instanceof StableOptionValue)) { 284 OverrideScope overrideScope = getOverrideScope(); 285 if (overrideScope != null) { 286 overrideScope.getOverrides(this, (Collection<Object>) values); 287 } 288 } 289 if (value != DEFAULT) { 290 values.add(value); 291 } else { 292 values.add(getDefaultValue()); 293 } 294 return values; 295 } 296 297 /** 298 * Sets the value of this option. 299 */ 300 @SuppressWarnings("unchecked") 301 public void setValue(Object v) { 302 this.value = (T) v; 303 } 304 305 /** 306 * An object whose {@link #close()} method reverts the option value overriding initiated by 307 * {@link OptionValue#override(OptionValue, Object)} or {@link OptionValue#override(Map)}. 308 */ 309 public abstract static class OverrideScope implements AutoCloseable { 310 311 private Map<DerivedOptionValue<?>, Object> derivedCache = null; 312 313 public <T> T getDerived(DerivedOptionValue<T> key) { 314 if (derivedCache == null) { 315 derivedCache = new HashMap<>(); 316 } 317 @SuppressWarnings("unchecked") 318 T ret = (T) derivedCache.get(key); 319 if (ret == null) { 320 ret = key.createValue(); 321 derivedCache.put(key, ret); 322 } 323 return ret; 324 } 325 326 abstract void addToInherited(Map<OptionValue<?>, Object> inherited); 327 328 abstract <T> T getOverride(OptionValue<T> option); 329 330 abstract void getOverrides(OptionValue<?> option, Collection<Object> c); 331 332 public abstract void close(); 333 } 334 335 static class SingleOverrideScope extends OverrideScope { 336 337 private final OptionValue<?> option; 338 private final Object value; 339 340 public SingleOverrideScope(OptionValue<?> option, Object value) { 341 if (option instanceof StableOptionValue) { 342 throw new IllegalArgumentException("Cannot override stable option " + option); 343 } 344 this.option = option; 345 this.value = value; 346 setOverrideScope(this); 347 } 348 349 @Override 350 void addToInherited(Map<OptionValue<?>, Object> inherited) { 351 inherited.put(option, value); 352 } 353 354 @SuppressWarnings("unchecked") 355 @Override 356 <T> T getOverride(OptionValue<T> key) { 357 if (key == this.option) { 358 return (T) value; 359 } 360 return null; 361 } 362 363 @Override 364 void getOverrides(OptionValue<?> key, Collection<Object> c) { 365 if (key == this.option) { 366 c.add(value); 367 } 368 } 369 370 @Override 371 public void close() { 372 setOverrideScope(null); 373 } 374 } 375 376 static class MultipleOverridesScope extends OverrideScope { 377 final OverrideScope parent; 378 final Map<OptionValue<?>, Object> overrides; 379 380 public MultipleOverridesScope(OverrideScope parent, OptionValue<?> option, Object value) { 381 this.parent = parent; 382 this.overrides = new HashMap<>(); 383 if (parent != null) { 384 parent.addToInherited(overrides); 385 } 386 if (option instanceof StableOptionValue) { 387 throw new IllegalArgumentException("Cannot override stable option " + option); 388 } 389 if (!value.equals(option.getValue())) { 390 this.overrides.put(option, value); 391 } 392 if (!overrides.isEmpty()) { 393 setOverrideScope(this); 394 } 395 } 396 397 MultipleOverridesScope(OverrideScope parent, Map<OptionValue<?>, Object> overrides) { 398 this.parent = parent; 399 if (overrides.isEmpty() && parent == null) { 400 this.overrides = Collections.emptyMap(); 401 return; 402 } 403 this.overrides = new HashMap<>(); 404 if (parent != null) { 405 parent.addToInherited(this.overrides); 406 } 407 for (Map.Entry<OptionValue<?>, Object> e : overrides.entrySet()) { 408 OptionValue<?> option = e.getKey(); 409 if (option instanceof StableOptionValue) { 410 throw new IllegalArgumentException("Cannot override stable option " + option); 411 } 412 if (!e.getValue().equals(option.getValue())) { 413 this.overrides.put(option, e.getValue()); 414 } 415 } 416 if (!this.overrides.isEmpty()) { 417 setOverrideScope(this); 418 } 419 } 420 421 @Override 422 void addToInherited(Map<OptionValue<?>, Object> inherited) { 423 if (parent != null) { 424 parent.addToInherited(inherited); 425 } 426 inherited.putAll(overrides); 427 } 428 429 @SuppressWarnings("unchecked") 430 @Override 431 <T> T getOverride(OptionValue<T> option) { 432 return (T) overrides.get(option); 433 } 434 435 @Override 436 void getOverrides(OptionValue<?> option, Collection<Object> c) { 437 Object v = overrides.get(option); 438 if (v != null) { 439 c.add(v); 440 } 441 if (parent != null) { 442 parent.getOverrides(option, c); 443 } 444 } 445 446 @Override 447 public void close() { 448 if (!overrides.isEmpty()) { 449 setOverrideScope(parent); 450 } 451 } 452 } 453 454 static { 455 if (ShowReadsHistogram) { 456 Runtime.getRuntime().addShutdownHook(new Thread() { 457 @Override 458 public void run() { 459 ArrayList<OptionValue<?>> options = new ArrayList<>(); 460 for (OptionValue<?> option = head; option != null; option = option.next) { 461 options.add(option); 462 } 463 Collections.sort(options, new Comparator<OptionValue<?>>() { 464 465 public int compare(OptionValue<?> o1, OptionValue<?> o2) { 466 if (o1.reads < o2.reads) { 467 return -1; 468 } else if (o1.reads > o2.reads) { 469 return 1; 470 } else { 471 return o1.getName().compareTo(o2.getName()); 472 } 473 } 474 }); 475 PrintStream out = System.out; 476 out.println("=== OptionValue reads histogram ==="); 477 for (OptionValue<?> option : options) { 478 out.println(option.reads + "\t" + option); 479 } 480 } 481 }); 482 } 483 } 484 }