OptionValue.java revision 12651:6ef01bd40ce2
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 */
23package org.graalvm.compiler.options;
24
25import java.io.PrintStream;
26import java.util.ArrayList;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Comparator;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Map.Entry;
33import java.util.ServiceLoader;
34
35/**
36 * An option value.
37 */
38public 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}
518