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