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&lt;OptionValue, Object&gt; overrides = new HashMap&lt;&gt;();
  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 }