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