# HG changeset patch # User Doug Simon # Date 1383652101 -3600 # Node ID 38bf986ce231c4d52da10d8c282b1dcb3a9acfc0 # Parent 582b3d24c6adeff19cfba0da39c2e549fe923f45 added support for scoped overriding of OptionValues (GRAAL-27) diff -r 582b3d24c6ad -r 38bf986ce231 graal/com.oracle.graal.options.test/src/com/oracle/graal/options/test/TestOptionValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.options.test/src/com/oracle/graal/options/test/TestOptionValue.java Tue Nov 05 12:48:21 2013 +0100 @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.options.test; + +import static com.oracle.graal.options.test.TestOptionValue.Options.*; +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.graal.options.*; +import com.oracle.graal.options.OptionValue.OverrideScope; + +public class TestOptionValue { + + public static class Options { + public static final OptionValue Stable = new StableOptionValue<>(true); + public static final OptionValue Mutable = new OptionValue<>("original"); + public static final OptionValue SecondMutable = new OptionValue<>("second"); + } + + static final OptionDescriptor stable = new OptionDescriptor("Stable", Boolean.class, "", Options.class, "Stable", Stable); + static final OptionDescriptor mutable = new OptionDescriptor("Mutable", String.class, "", Options.class, "Mutable", Mutable); + static final OptionDescriptor secondMutable = new OptionDescriptor("SecondMutable", String.class, "", Options.class, "SecondMutable", SecondMutable); + + @Test + public void testMutable() { + assertEquals("original", Mutable.getValue()); + try (OverrideScope s1 = OptionValue.override(Mutable, "override1")) { + assertEquals("override1", Mutable.getValue()); + try (OverrideScope s2 = OptionValue.override(Mutable, "override2")) { + assertEquals("override2", Mutable.getValue()); + } + assertEquals("override1", Mutable.getValue()); + try (OverrideScope s3 = OptionValue.override(Mutable, "override3")) { + assertEquals("override3", Mutable.getValue()); + } + assertEquals("override1", Mutable.getValue()); + } + assertEquals("original", Mutable.getValue()); + try (OverrideScope s1 = OptionValue.override(Mutable, "original")) { + assertEquals("original", Mutable.getValue()); + } + } + + @Test + public void testMultiple() { + assertEquals("original", Mutable.getValue()); + assertEquals("second", SecondMutable.getValue()); + try (OverrideScope s1 = OptionValue.override(Mutable, "override1", SecondMutable, "secondOverride1")) { + assertEquals("override1", Mutable.getValue()); + assertEquals("secondOverride1", SecondMutable.getValue()); + try (OverrideScope s2 = OptionValue.override(Mutable, "override2", SecondMutable, "secondOverride2")) { + assertEquals("override2", Mutable.getValue()); + assertEquals("secondOverride2", SecondMutable.getValue()); + } + assertEquals("override1", Mutable.getValue()); + assertEquals("secondOverride1", SecondMutable.getValue()); + try (OverrideScope s3 = OptionValue.override(Mutable, "override3", SecondMutable, "secondOverride3")) { + assertEquals("override3", Mutable.getValue()); + assertEquals("secondOverride3", SecondMutable.getValue()); + } + assertEquals("override1", Mutable.getValue()); + assertEquals("secondOverride1", SecondMutable.getValue()); + } + assertEquals("original", Mutable.getValue()); + assertEquals("second", SecondMutable.getValue()); + try (OverrideScope s1 = OptionValue.override(Mutable, "original", SecondMutable, "second")) { + assertEquals("original", Mutable.getValue()); + assertEquals("second", SecondMutable.getValue()); + } + } + + @Test + public void testStable() { + assertTrue(Stable.getValue()); + try (OverrideScope s = OptionValue.override(Stable, false)) { + fail("cannot override stable option"); + } catch (IllegalArgumentException e) { + // expected + } + } +} diff -r 582b3d24c6ad -r 38bf986ce231 graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionValue.java --- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionValue.java Tue Nov 05 11:14:42 2013 +0100 +++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionValue.java Tue Nov 05 12:48:21 2013 +0100 @@ -22,12 +22,114 @@ */ package com.oracle.graal.options; +import java.util.*; +import java.util.Map.Entry; + /** * An option value. */ public class OptionValue { /** + * Temporarily changes the value for an option. The {@linkplain OptionValue#getValue() value} of + * {@code option} is set to {@code value} until {@link OverrideScope#close()} is called on the + * object returned by this method. + *

+ * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be + * used: + * + *

+     * try (OverrideScope s = OptionValue.override(myOption, myValue) {
+     *     // code that depends on myOption == myValue
+     * }
+     * 
+ */ + public static OverrideScope override(OptionValue option, Object value) { + OverrideScope current = overrideScopes.get(); + if (current == null) { + if (!value.equals(option.getValue())) { + return new SingleOverrideScope(option, value); + } + Map, Object> overrides = Collections.emptyMap(); + return new MultipleOverridesScope(current, overrides); + } + return new MultipleOverridesScope(current, option, value); + } + + /** + * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() + * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} + * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by + * this method. + *

+ * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be + * used: + * + *

+     * Map overrides = new HashMap<>();
+     * overrides.put(myOption1, myValue1);
+     * overrides.put(myOption2, myValue2);
+     * try (OverrideScope s = OptionValue.override(overrides) {
+     *     // code that depends on myOption == myValue
+     * }
+     * 
+ */ + public static OverrideScope override(Map, Object> overrides) { + OverrideScope current = overrideScopes.get(); + if (current == null && overrides.size() == 1) { + Entry, Object> single = overrides.entrySet().iterator().next(); + OptionValue option = single.getKey(); + Object overrideValue = single.getValue(); + if (!overrideValue.equals(option.getValue())) { + return new SingleOverrideScope(option, overrideValue); + } + } + return new MultipleOverridesScope(current, overrides); + } + + /** + * Temporarily changes the values for a set of options. The {@linkplain OptionValue#getValue() + * value} of each {@code option} in {@code overrides} is set to the corresponding {@code value} + * in {@code overrides} until {@link OverrideScope#close()} is called on the object returned by + * this method. + *

+ * Since the returned object is {@link AutoCloseable} the try-with-resource construct can be + * used: + * + *

+     * try (OverrideScope s = OptionValue.override(myOption1, myValue1, myOption2, myValue2) {
+     *     // code that depends on myOption == myValue
+     * }
+     * 
+ * + * @param overrides overrides in the form {@code [option1, override1, option2, override2, ...]} + */ + public static OverrideScope override(Object... overrides) { + OverrideScope current = overrideScopes.get(); + if (current == null && overrides.length == 2) { + OptionValue option = (OptionValue) overrides[0]; + Object overrideValue = overrides[1]; + if (!overrideValue.equals(option.getValue())) { + return new SingleOverrideScope(option, overrideValue); + } + } + Map, Object> map = Collections.emptyMap(); + for (int i = 0; i < overrides.length; i += 2) { + OptionValue option = (OptionValue) overrides[i]; + Object overrideValue = overrides[i + 1]; + if (!overrideValue.equals(option.getValue())) { + if (map.isEmpty()) { + map = new HashMap<>(); + } + map.put(option, overrideValue); + } + } + return new MultipleOverridesScope(current, map); + } + + private static ThreadLocal overrideScopes = new ThreadLocal<>(); + + /** * The raw option value. */ protected T value; @@ -80,6 +182,13 @@ * Gets the value of this option. */ public T getValue() { + OverrideScope overrideScope = overrideScopes.get(); + if (overrideScope != null) { + T override = overrideScope.getOverride(this); + if (override != null) { + return override; + } + } if (value == UNINITIALIZED) { value = initialValue(); } @@ -93,4 +202,117 @@ public void setValue(Object v) { this.value = (T) v; } + + /** + * An object whose {@link #close()} method reverts the option value overriding initiated by + * {@link OptionValue#override(OptionValue, Object)} or {@link OptionValue#override(Map)}. + */ + public abstract static class OverrideScope implements AutoCloseable { + abstract void addToInherited(Map inherited); + + abstract T getOverride(OptionValue option); + + public abstract void close(); + } + + static class SingleOverrideScope extends OverrideScope { + + private final OptionValue option; + private final Object value; + + public SingleOverrideScope(OptionValue option, Object value) { + if (option instanceof StableOptionValue) { + throw new IllegalArgumentException("Cannot override stable option " + option); + } + this.option = option; + this.value = value; + overrideScopes.set(this); + } + + @Override + void addToInherited(Map inherited) { + inherited.put(option, value); + } + + @SuppressWarnings("unchecked") + @Override + T getOverride(OptionValue key) { + if (key == this.option) { + return (T) value; + } + return null; + } + + @Override + public void close() { + overrideScopes.set(null); + } + } + + static class MultipleOverridesScope extends OverrideScope { + final OverrideScope parent; + final Map overrides; + + public MultipleOverridesScope(OverrideScope parent, OptionValue option, Object value) { + this.parent = parent; + this.overrides = new HashMap<>(); + if (parent != null) { + parent.addToInherited(overrides); + } + if (option instanceof StableOptionValue) { + throw new IllegalArgumentException("Cannot override stable option " + option); + } + if (!value.equals(option.getValue())) { + this.overrides.put(option, value); + } + if (!overrides.isEmpty()) { + overrideScopes.set(this); + } + } + + MultipleOverridesScope(OverrideScope parent, Map, Object> overrides) { + this.parent = parent; + if (overrides.isEmpty() && parent == null) { + this.overrides = Collections.emptyMap(); + return; + } + this.overrides = new HashMap<>(); + if (parent != null) { + parent.addToInherited(this.overrides); + } + for (Map.Entry, Object> e : overrides.entrySet()) { + OptionValue option = e.getKey(); + if (option instanceof StableOptionValue) { + throw new IllegalArgumentException("Cannot override stable option " + option); + } + if (!e.getValue().equals(option.getValue())) { + this.overrides.put(option, e.getValue()); + } + } + if (!this.overrides.isEmpty()) { + overrideScopes.set(this); + } + } + + @Override + void addToInherited(Map inherited) { + if (parent != null) { + parent.addToInherited(inherited); + } + inherited.putAll(overrides); + } + + @SuppressWarnings("unchecked") + @Override + T getOverride(OptionValue option) { + return (T) overrides.get(option); + } + + @Override + public void close() { + if (!overrides.isEmpty()) { + overrideScopes.set(parent); + } + } + } } diff -r 582b3d24c6ad -r 38bf986ce231 mx/projects --- a/mx/projects Tue Nov 05 11:14:42 2013 +0100 +++ b/mx/projects Tue Nov 05 12:48:21 2013 +0100 @@ -200,6 +200,14 @@ project@com.oracle.graal.options@annotationProcessorForDependents=true project@com.oracle.graal.options@workingSets=Graal,Codegen +# graal.options.test +project@com.oracle.graal.options.test@subDir=graal +project@com.oracle.graal.options.test@sourceDirs=src +project@com.oracle.graal.options.test@dependencies=com.oracle.graal.options,JUNIT +project@com.oracle.graal.options.test@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.options.test@javaCompliance=1.7 +project@com.oracle.graal.options.test@workingSets=Graal + # graal.graph project@com.oracle.graal.graph@subDir=graal project@com.oracle.graal.graph@sourceDirs=src