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