001/* 002 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package jdk.internal.jvmci.options; 024 025import java.io.*; 026import java.util.*; 027import java.util.Map.Entry; 028 029/** 030 * An option value. 031 */ 032public class OptionValue<T> { 033 /** 034 * Temporarily changes the value for an option. The {@linkplain OptionValue#getValue() value} of 035 * {@code option} is set to {@code value} until {@link OverrideScope#close()} is called on the 036 * object returned by this method. 037 * <p> 038 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 039 * used: 040 * 041 * <pre> 042 * try (OverrideScope s = OptionValue.override(myOption, myValue) { 043 * // code that depends on myOption == myValue 044 * } 045 * </pre> 046 */ 047 public static OverrideScope override(OptionValue<?> option, Object value) { 048 OverrideScope current = getOverrideScope(); 049 if (current == null) { 050 if (!value.equals(option.getValue())) { 051 return new SingleOverrideScope(option, value); 052 } 053 Map<OptionValue<?>, Object> overrides = Collections.emptyMap(); 054 return new MultipleOverridesScope(current, overrides); 055 } 056 return new MultipleOverridesScope(current, option, value); 057 } 058 059 /** 060 * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() 061 * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} 062 * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by 063 * this method. 064 * <p> 065 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 066 * used: 067 * 068 * <pre> 069 * Map<OptionValue, Object> overrides = new HashMap<>(); 070 * overrides.put(myOption1, myValue1); 071 * overrides.put(myOption2, myValue2); 072 * try (OverrideScope s = OptionValue.override(overrides) { 073 * // code that depends on myOption == myValue 074 * } 075 * </pre> 076 */ 077 public static OverrideScope override(Map<OptionValue<?>, Object> overrides) { 078 OverrideScope current = getOverrideScope(); 079 if (current == null && overrides.size() == 1) { 080 Entry<OptionValue<?>, Object> single = overrides.entrySet().iterator().next(); 081 OptionValue<?> option = single.getKey(); 082 Object overrideValue = single.getValue(); 083 if (!overrideValue.equals(option.getValue())) { 084 return new SingleOverrideScope(option, overrideValue); 085 } 086 } 087 return new MultipleOverridesScope(current, overrides); 088 } 089 090 /** 091 * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() 092 * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} 093 * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by 094 * this method. 095 * <p> 096 * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be 097 * used: 098 * 099 * <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 this.descriptor = descriptor; 197 } 198 199 /** 200 * Returns the descriptor for this option, if it has been set by 201 * {@link #setDescriptor(OptionDescriptor)}. 202 */ 203 public OptionDescriptor getDescriptor() { 204 return descriptor; 205 } 206 207 /** 208 * Gets the name of this option. The name for an option value with a null 209 * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is the value of 210 * {@link Object#toString()}. 211 */ 212 public String getName() { 213 return descriptor == null ? super.toString() : (descriptor.getDeclaringClass().getName() + "." + descriptor.getName()); 214 } 215 216 @Override 217 public String toString() { 218 return getName() + "=" + getValue(); 219 } 220 221 /** 222 * The initial value specified in source code. The returned value is not affected by calls to 223 * {@link #setValue(Object)} or registering {@link OverrideScope}s. Therefore, it is also not 224 * affected by options set on the command line. 225 */ 226 public T getDefaultValue() { 227 if (defaultValue == UNINITIALIZED) { 228 defaultValue = defaultValue(); 229 } 230 return defaultValue; 231 } 232 233 /** 234 * Returns true if the option has the same value that was set in the source code. 235 */ 236 public boolean hasDefaultValue() { 237 getValue(); // ensure initialized 238 return value == DEFAULT || Objects.equals(value, getDefaultValue()); 239 } 240 241 /** 242 * Gets the value of this option. 243 */ 244 public T getValue() { 245 if (ShowReadsHistogram) { 246 reads++; 247 } 248 if (!(this instanceof StableOptionValue)) { 249 OverrideScope overrideScope = getOverrideScope(); 250 if (overrideScope != null) { 251 T override = overrideScope.getOverride(this); 252 if (override != null) { 253 return override; 254 } 255 } 256 } 257 if (value != DEFAULT) { 258 return value; 259 } else { 260 return getDefaultValue(); 261 } 262 } 263 264 /** 265 * Gets the values of this option including overridden values. 266 * 267 * @param c the collection to which the values are added. If null, one is allocated. 268 * @return the collection to which the values were added in order from most overridden to 269 * current value 270 */ 271 @SuppressWarnings("unchecked") 272 public Collection<T> getValues(Collection<T> c) { 273 Collection<T> values = c == null ? new ArrayList<>() : c; 274 if (!(this instanceof StableOptionValue)) { 275 OverrideScope overrideScope = getOverrideScope(); 276 if (overrideScope != null) { 277 overrideScope.getOverrides(this, (Collection<Object>) values); 278 } 279 } 280 if (value != DEFAULT) { 281 values.add(value); 282 } else { 283 values.add(getDefaultValue()); 284 } 285 return values; 286 } 287 288 /** 289 * Sets the value of this option. 290 */ 291 @SuppressWarnings("unchecked") 292 public void setValue(Object v) { 293 this.value = (T) v; 294 } 295 296 /** 297 * An object whose {@link #close()} method reverts the option value overriding initiated by 298 * {@link OptionValue#override(OptionValue, Object)} or {@link OptionValue#override(Map)}. 299 */ 300 public abstract static class OverrideScope implements AutoCloseable { 301 302 private Map<DerivedOptionValue<?>, Object> derivedCache = null; 303 304 public <T> T getDerived(DerivedOptionValue<T> key) { 305 if (derivedCache == null) { 306 derivedCache = new HashMap<>(); 307 } 308 @SuppressWarnings("unchecked") 309 T ret = (T) derivedCache.get(key); 310 if (ret == null) { 311 ret = key.createValue(); 312 derivedCache.put(key, ret); 313 } 314 return ret; 315 } 316 317 abstract void addToInherited(Map<OptionValue<?>, Object> inherited); 318 319 abstract <T> T getOverride(OptionValue<T> option); 320 321 abstract void getOverrides(OptionValue<?> option, Collection<Object> c); 322 323 public abstract void close(); 324 } 325 326 static class SingleOverrideScope extends OverrideScope { 327 328 private final OptionValue<?> option; 329 private final Object value; 330 331 public SingleOverrideScope(OptionValue<?> option, Object value) { 332 if (option instanceof StableOptionValue) { 333 throw new IllegalArgumentException("Cannot override stable option " + option); 334 } 335 this.option = option; 336 this.value = value; 337 setOverrideScope(this); 338 } 339 340 @Override 341 void addToInherited(Map<OptionValue<?>, Object> inherited) { 342 inherited.put(option, value); 343 } 344 345 @SuppressWarnings("unchecked") 346 @Override 347 <T> T getOverride(OptionValue<T> key) { 348 if (key == this.option) { 349 return (T) value; 350 } 351 return null; 352 } 353 354 @Override 355 void getOverrides(OptionValue<?> key, Collection<Object> c) { 356 if (key == this.option) { 357 c.add(value); 358 } 359 } 360 361 @Override 362 public void close() { 363 setOverrideScope(null); 364 } 365 } 366 367 static class MultipleOverridesScope extends OverrideScope { 368 final OverrideScope parent; 369 final Map<OptionValue<?>, Object> overrides; 370 371 public MultipleOverridesScope(OverrideScope parent, OptionValue<?> option, Object value) { 372 this.parent = parent; 373 this.overrides = new HashMap<>(); 374 if (parent != null) { 375 parent.addToInherited(overrides); 376 } 377 if (option instanceof StableOptionValue) { 378 throw new IllegalArgumentException("Cannot override stable option " + option); 379 } 380 if (!value.equals(option.getValue())) { 381 this.overrides.put(option, value); 382 } 383 if (!overrides.isEmpty()) { 384 setOverrideScope(this); 385 } 386 } 387 388 MultipleOverridesScope(OverrideScope parent, Map<OptionValue<?>, Object> overrides) { 389 this.parent = parent; 390 if (overrides.isEmpty() && parent == null) { 391 this.overrides = Collections.emptyMap(); 392 return; 393 } 394 this.overrides = new HashMap<>(); 395 if (parent != null) { 396 parent.addToInherited(this.overrides); 397 } 398 for (Map.Entry<OptionValue<?>, Object> e : overrides.entrySet()) { 399 OptionValue<?> option = e.getKey(); 400 if (option instanceof StableOptionValue) { 401 throw new IllegalArgumentException("Cannot override stable option " + option); 402 } 403 if (!e.getValue().equals(option.getValue())) { 404 this.overrides.put(option, e.getValue()); 405 } 406 } 407 if (!this.overrides.isEmpty()) { 408 setOverrideScope(this); 409 } 410 } 411 412 @Override 413 void addToInherited(Map<OptionValue<?>, Object> inherited) { 414 if (parent != null) { 415 parent.addToInherited(inherited); 416 } 417 inherited.putAll(overrides); 418 } 419 420 @SuppressWarnings("unchecked") 421 @Override 422 <T> T getOverride(OptionValue<T> option) { 423 return (T) overrides.get(option); 424 } 425 426 @Override 427 void getOverrides(OptionValue<?> option, Collection<Object> c) { 428 Object v = overrides.get(option); 429 if (v != null) { 430 c.add(v); 431 } 432 if (parent != null) { 433 parent.getOverrides(option, c); 434 } 435 } 436 437 @Override 438 public void close() { 439 if (!overrides.isEmpty()) { 440 setOverrideScope(parent); 441 } 442 } 443 } 444 445 static { 446 if (ShowReadsHistogram) { 447 Runtime.getRuntime().addShutdownHook(new Thread() { 448 @Override 449 public void run() { 450 ArrayList<OptionValue<?>> options = new ArrayList<>(); 451 for (OptionValue<?> option = head; option != null; option = option.next) { 452 options.add(option); 453 } 454 Collections.sort(options, new Comparator<OptionValue<?>>() { 455 456 public int compare(OptionValue<?> o1, OptionValue<?> o2) { 457 if (o1.reads < o2.reads) { 458 return -1; 459 } else if (o1.reads > o2.reads) { 460 return 1; 461 } else { 462 return o1.getName().compareTo(o2.getName()); 463 } 464 } 465 }); 466 PrintStream out = System.out; 467 out.println("=== OptionValue reads histogram ==="); 468 for (OptionValue<?> option : options) { 469 out.println(option.reads + "\t" + option); 470 } 471 } 472 }); 473 } 474 } 475}