1 /*
   2  * Copyright (c) 2013, 2018, 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 
  24 
  25 package org.graalvm.compiler.options;
  26 
  27 import java.util.Formatter;
  28 
  29 import jdk.internal.vm.compiler.collections.EconomicMap;
  30 
  31 /**
  32  * A key for an option. The value for an option is obtained from an {@link OptionValues} object.
  33  */
  34 public class OptionKey<T> {
  35 
  36     private final T defaultValue;
  37 
  38     private OptionDescriptor descriptor;
  39 
  40     public OptionKey(T defaultValue) {
  41         this.defaultValue = defaultValue;
  42     }
  43 
  44     /**
  45      * Sets the descriptor for this option.
  46      */
  47     public final void setDescriptor(OptionDescriptor descriptor) {
  48         assert this.descriptor == null : "Overwriting existing descriptor";
  49         this.descriptor = descriptor;
  50     }
  51 
  52     /**
  53      * Returns the descriptor for this option, if it has been set by
  54      * {@link #setDescriptor(OptionDescriptor)}.
  55      */
  56     public final OptionDescriptor getDescriptor() {
  57         return descriptor;
  58     }
  59 
  60     /**
  61      * Checks that a descriptor exists for this key after triggering loading of descriptors.
  62      */
  63     protected boolean checkDescriptorExists() {
  64         OptionKey.Lazy.init();
  65         if (descriptor == null) {
  66             Formatter buf = new Formatter();
  67             buf.format("Could not find a descriptor for an option key. The most likely cause is " +
  68                             "a dependency on the %s annotation without a dependency on the " +
  69                             "org.graalvm.compiler.options.processor.OptionProcessor annotation processor.", Option.class.getName());
  70             StackTraceElement[] stackTrace = new Exception().getStackTrace();
  71             if (stackTrace.length > 2 &&
  72                             stackTrace[1].getClassName().equals(OptionKey.class.getName()) &&
  73                             stackTrace[1].getMethodName().equals("getValue")) {
  74                 String caller = stackTrace[2].getClassName();
  75                 buf.format(" In suite.py, add GRAAL_OPTIONS_PROCESSOR to the \"annotationProcessors\" attribute of the project " +
  76                                 "containing %s.", caller);
  77             }
  78             throw new AssertionError(buf.toString());
  79         }
  80         return true;
  81     }
  82 
  83     /**
  84      * Mechanism for lazily loading all available options which has the side effect of assigning
  85      * names to the options.
  86      */
  87     static class Lazy {
  88         static {
  89             for (OptionDescriptors opts : OptionsParser.getOptionsLoader()) {
  90                 for (OptionDescriptor desc : opts) {
  91                     desc.getName();
  92                 }
  93             }
  94         }
  95 
  96         static void init() {
  97             /* Running the static class initializer does all the initialization. */
  98         }
  99     }
 100 
 101     /**
 102      * Gets the name of this option. The name for an option value with a null
 103      * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is the value of
 104      * {@link Object#toString()}.
 105      */
 106     public final String getName() {
 107         if (descriptor == null) {
 108             // Trigger initialization of OptionsLoader to ensure all option values have
 109             // a descriptor which is required for them to have meaningful names.
 110             Lazy.init();
 111         }
 112         return descriptor == null ? super.toString() : descriptor.getName();
 113     }
 114 
 115     @Override
 116     public String toString() {
 117         return getName();
 118     }
 119 
 120     /**
 121      * The initial value specified in source code.
 122      */
 123     public final T getDefaultValue() {
 124         return defaultValue;
 125     }
 126 
 127     /**
 128      * Returns true if the option has been set in any way. Note that this doesn't mean that the
 129      * current value is different than the default.
 130      */
 131     public boolean hasBeenSet(OptionValues values) {
 132         return values.containsKey(this);
 133     }
 134 
 135     /**
 136      * Gets the value of this option in {@code values}.
 137      */
 138     public T getValue(OptionValues values) {
 139         assert checkDescriptorExists();
 140         return values.get(this);
 141     }
 142 
 143     /**
 144      * Gets the value of this option in {@code values} if it is present, otherwise
 145      * {@link #getDefaultValue()}.
 146      */
 147     @SuppressWarnings("unchecked")
 148     public T getValueOrDefault(EconomicMap<OptionKey<?>, Object> values) {
 149         if (!values.containsKey(this)) {
 150             return defaultValue;
 151         }
 152         return (T) values.get(this);
 153     }
 154 
 155     /**
 156      * Sets the value of this option in a given map. The
 157      * {@link #onValueUpdate(EconomicMap, Object, Object)} method is called once the value is set.
 158      *
 159      * @param values map of option values
 160      * @param v the value to set for this key in {@code map}
 161      */
 162     @SuppressWarnings("unchecked")
 163     public void update(EconomicMap<OptionKey<?>, Object> values, Object v) {
 164         T oldValue = (T) values.put(this, v);
 165         onValueUpdate(values, oldValue, (T) v);
 166     }
 167 
 168     /**
 169      * Sets the value of this option in a given map if it doesn't already have a value. The
 170      * {@link #onValueUpdate(EconomicMap, Object, Object)} method is called once the value is set.
 171      *
 172      * @param values map of option values
 173      * @param v the value to set for this key in {@code map}
 174      */
 175     @SuppressWarnings("unchecked")
 176     public void putIfAbsent(EconomicMap<OptionKey<?>, Object> values, Object v) {
 177         if (!values.containsKey(this)) {
 178             T oldValue = (T) values.put(this, v);
 179             onValueUpdate(values, oldValue, (T) v);
 180         }
 181     }
 182 
 183     /**
 184      * Notifies this object when a value associated with this key is set or updated in
 185      * {@code values}.
 186      *
 187      * @param values
 188      * @param oldValue
 189      * @param newValue
 190      */
 191     protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, T oldValue, T newValue) {
 192     }
 193 }