hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java
changeset 46344 694c102fd8ed
parent 43972 1ade39b8381b
child 46371 0337d0617e7b
--- a/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java	Mon Dec 12 16:16:27 2016 +0300
+++ b/hotspot/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.options/src/org/graalvm/compiler/options/OptionsParser.java	Wed Mar 22 13:42:45 2017 -0700
@@ -22,16 +22,14 @@
  */
 package org.graalvm.compiler.options;
 
-import java.io.PrintStream;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Formatter;
 import java.util.List;
-import java.util.Map;
 import java.util.ServiceLoader;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
+
+import org.graalvm.util.CollectionsUtil;
+import org.graalvm.util.EconomicMap;
+import org.graalvm.util.MapCursor;
 
 /**
  * This class contains methods for parsing Graal options and matching them against a set of
@@ -39,34 +37,50 @@
  */
 public class OptionsParser {
 
-    public interface OptionConsumer {
-        void set(OptionDescriptor desc, Object value);
+    /**
+     * Gets an iterable composed of the {@link ServiceLoader}s to be used when looking for
+     * {@link OptionDescriptors} providers.
+     */
+    public static Iterable<OptionDescriptors> getOptionsLoader() {
+        ServiceLoader<OptionDescriptors> graalLoader = ServiceLoader.load(OptionDescriptors.class, OptionDescriptors.class.getClassLoader());
+        boolean java8OrEarlier = System.getProperty("java.specification.version").compareTo("1.9") < 0;
+        if (java8OrEarlier) {
+            return graalLoader;
+        } else {
+            /*
+             * The Graal module (i.e., jdk.internal.vm.compiler) is loaded by the platform class
+             * loader on JDK 9. Other modules that extend Graal or are Graal dependencies (such as
+             * Truffle) are supplied via --module-path which means they are loaded by the app class
+             * loader. As such, we need to search the app class loader path as well.
+             */
+            ServiceLoader<OptionDescriptors> truffleLoader = ServiceLoader.load(OptionDescriptors.class, ClassLoader.getSystemClassLoader());
+            return CollectionsUtil.concat(graalLoader, truffleLoader);
+        }
     }
 
     /**
      * Parses a map representing assignments of values to options.
      *
      * @param optionSettings option settings (i.e., assignments of values to options)
-     * @param setter the object to notify of the parsed option and value
-     * @param loader the loader for {@linkplain #lookup(ServiceLoader, String) looking} up
-     *            {@link OptionDescriptor}s
+     * @param values the object in which to store the parsed values
+     * @param loader source of the available {@link OptionDescriptors}
      * @throws IllegalArgumentException if there's a problem parsing {@code option}
      */
-    public static void parseOptions(Map<String, String> optionSettings, OptionConsumer setter, ServiceLoader<OptionDescriptors> loader) {
+    public static void parseOptions(EconomicMap<String, String> optionSettings, EconomicMap<OptionKey<?>, Object> values, Iterable<OptionDescriptors> loader) {
         if (optionSettings != null && !optionSettings.isEmpty()) {
-
-            for (Map.Entry<String, String> e : optionSettings.entrySet()) {
-                parseOption(e.getKey(), e.getValue(), setter, loader);
+            MapCursor<String, String> cursor = optionSettings.getEntries();
+            while (cursor.advance()) {
+                parseOption(cursor.getKey(), cursor.getValue(), values, loader);
             }
         }
     }
 
     /**
-     * Parses a given option setting string to a map of settings.
+     * Parses a given option setting string and adds the parsed key and value {@code dst}.
      *
      * @param optionSetting a string matching the pattern {@code <name>=<value>}
      */
-    public static void parseOptionSettingTo(String optionSetting, Map<String, String> dst) {
+    public static void parseOptionSettingTo(String optionSetting, EconomicMap<String, String> dst) {
         int eqIndex = optionSetting.indexOf('=');
         if (eqIndex == -1) {
             throw new InternalError("Option setting has does not match the pattern <name>=<value>: " + optionSetting);
@@ -77,12 +91,12 @@
     /**
      * Looks up an {@link OptionDescriptor} based on a given name.
      *
-     * @param loader provides the available {@link OptionDescriptor}s
+     * @param loader source of the available {@link OptionDescriptors}
      * @param name the name of the option to look up
      * @return the {@link OptionDescriptor} whose name equals {@code name} or null if not such
      *         descriptor is available
      */
-    private static OptionDescriptor lookup(ServiceLoader<OptionDescriptors> loader, String name) {
+    private static OptionDescriptor lookup(Iterable<OptionDescriptors> loader, String name) {
         for (OptionDescriptors optionDescriptors : loader) {
             OptionDescriptor desc = optionDescriptors.get(name);
             if (desc != null) {
@@ -96,13 +110,12 @@
      * Parses a given option name and value.
      *
      * @param name the option name
-     * @param valueString the option value as a string
-     * @param setter the object to notify of the parsed option and value
-     * @param loader the loader for {@linkplain #lookup(ServiceLoader, String) looking} up
-     *            {@link OptionDescriptor}s
+     * @param uncheckedValue the unchecked value for the option
+     * @param values the object in which to store the parsed option and value
+     * @param loader source of the available {@link OptionDescriptors}
      * @throws IllegalArgumentException if there's a problem parsing {@code option}
      */
-    private static void parseOption(String name, String valueString, OptionConsumer setter, ServiceLoader<OptionDescriptors> loader) {
+    static void parseOption(String name, Object uncheckedValue, EconomicMap<OptionKey<?>, Object> values, Iterable<OptionDescriptors> loader) {
 
         OptionDescriptor desc = lookup(loader, name);
         if (desc == null) {
@@ -120,41 +133,49 @@
 
         Class<?> optionType = desc.getType();
         Object value;
-        if (optionType == Boolean.class) {
-            if ("true".equals(valueString)) {
-                value = Boolean.TRUE;
-            } else if ("false".equals(valueString)) {
-                value = Boolean.FALSE;
-            } else {
-                throw new IllegalArgumentException("Boolean option '" + name + "' must have value \"true\" or \"false\", not \"" + valueString + "\"");
-            }
-        } else if (optionType == String.class || Enum.class.isAssignableFrom(optionType)) {
-            value = valueString;
-        } else {
-            if (valueString.isEmpty()) {
-                throw new IllegalArgumentException("Non empty value required for option '" + name + "'");
+        if (!(uncheckedValue instanceof String)) {
+            if (optionType != uncheckedValue.getClass()) {
+                String type = optionType.getSimpleName();
+                throw new IllegalArgumentException(type + " option '" + name + "' must have " + type + " value, not " + uncheckedValue.getClass() + " [toString: " + uncheckedValue + "]");
             }
-            try {
-                if (optionType == Float.class) {
-                    value = Float.parseFloat(valueString);
-                } else if (optionType == Double.class) {
-                    value = Double.parseDouble(valueString);
-                } else if (optionType == Integer.class) {
-                    value = Integer.valueOf((int) parseLong(valueString));
-                } else if (optionType == Long.class) {
-                    value = Long.valueOf(parseLong(valueString));
+            value = uncheckedValue;
+        } else {
+            String valueString = (String) uncheckedValue;
+            if (optionType == Boolean.class) {
+                if ("true".equals(valueString)) {
+                    value = Boolean.TRUE;
+                } else if ("false".equals(valueString)) {
+                    value = Boolean.FALSE;
                 } else {
-                    throw new IllegalArgumentException("Wrong value for option '" + name + "'");
+                    throw new IllegalArgumentException("Boolean option '" + name + "' must have value \"true\" or \"false\", not \"" + uncheckedValue + "\"");
+                }
+            } else if (optionType == String.class) {
+                value = valueString;
+            } else if (Enum.class.isAssignableFrom(optionType)) {
+                value = ((EnumOptionKey<?>) desc.getOptionKey()).valueOf(valueString);
+            } else {
+                if (valueString.isEmpty()) {
+                    throw new IllegalArgumentException("Non empty value required for option '" + name + "'");
                 }
-            } catch (NumberFormatException nfe) {
-                throw new IllegalArgumentException("Value for option '" + name + "' has invalid number format: " + valueString);
+                try {
+                    if (optionType == Float.class) {
+                        value = Float.parseFloat(valueString);
+                    } else if (optionType == Double.class) {
+                        value = Double.parseDouble(valueString);
+                    } else if (optionType == Integer.class) {
+                        value = Integer.valueOf((int) parseLong(valueString));
+                    } else if (optionType == Long.class) {
+                        value = Long.valueOf(parseLong(valueString));
+                    } else {
+                        throw new IllegalArgumentException("Wrong value for option '" + name + "'");
+                    }
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalArgumentException("Value for option '" + name + "' has invalid number format: " + valueString);
+                }
             }
         }
-        if (setter == null) {
-            desc.getOptionValue().setValue(value);
-        } else {
-            setter.set(desc, value);
-        }
+
+        desc.optionKey.update(values, value);
     }
 
     private static long parseLong(String v) {
@@ -179,96 +200,6 @@
     }
 
     /**
-     * Wraps some given text to one or more lines of a given maximum width.
-     *
-     * @param text text to wrap
-     * @param width maximum width of an output line, exception for words in {@code text} longer than
-     *            this value
-     * @return {@code text} broken into lines
-     */
-    private static List<String> wrap(String text, int width) {
-        List<String> lines = Collections.singletonList(text);
-        if (text.length() > width) {
-            String[] chunks = text.split("\\s+");
-            lines = new ArrayList<>();
-            StringBuilder line = new StringBuilder();
-            for (String chunk : chunks) {
-                if (line.length() + chunk.length() > width) {
-                    lines.add(line.toString());
-                    line.setLength(0);
-                }
-                if (line.length() != 0) {
-                    line.append(' ');
-                }
-                String[] embeddedLines = chunk.split("%n", -2);
-                if (embeddedLines.length == 1) {
-                    line.append(chunk);
-                } else {
-                    for (int i = 0; i < embeddedLines.length; i++) {
-                        line.append(embeddedLines[i]);
-                        if (i < embeddedLines.length - 1) {
-                            lines.add(line.toString());
-                            line.setLength(0);
-                        }
-                    }
-                }
-            }
-            if (line.length() != 0) {
-                lines.add(line.toString());
-            }
-        }
-        return lines;
-    }
-
-    private static final int PROPERTY_LINE_WIDTH = 80;
-    private static final int PROPERTY_HELP_INDENT = 10;
-
-    public static void printFlags(ServiceLoader<OptionDescriptors> loader, PrintStream out, Set<String> explicitlyAssigned, String namePrefix) {
-        SortedMap<String, OptionDescriptor> sortedOptions = new TreeMap<>();
-        for (OptionDescriptors opts : loader) {
-            for (OptionDescriptor desc : opts) {
-                String name = desc.getName();
-                OptionDescriptor existing = sortedOptions.put(name, desc);
-                assert existing == null : "Option named \"" + name + "\" has multiple definitions: " + existing.getLocation() + " and " + desc.getLocation();
-            }
-        }
-        for (Map.Entry<String, OptionDescriptor> e : sortedOptions.entrySet()) {
-            OptionDescriptor desc = e.getValue();
-            Object value = desc.getOptionValue().getValue();
-            if (value instanceof String) {
-                value = '"' + String.valueOf(value) + '"';
-            }
-            String help = desc.getHelp();
-            if (desc.getOptionValue() instanceof EnumOptionValue) {
-                EnumOptionValue<?> eoption = (EnumOptionValue<?>) desc.getOptionValue();
-                String evalues = eoption.getOptionValues().toString();
-                if (help.length() > 0 && !help.endsWith(".")) {
-                    help += ".";
-                }
-                help += " Valid values are: " + evalues.substring(1, evalues.length() - 1);
-            }
-            String name = namePrefix + e.getKey();
-            String assign = explicitlyAssigned.contains(name) ? ":=" : "=";
-            String typeName = desc.getOptionValue() instanceof EnumOptionValue ? "String" : desc.getType().getSimpleName();
-            String linePrefix = String.format("%s %s %s ", name, assign, value);
-            int typeStartPos = PROPERTY_LINE_WIDTH - typeName.length();
-            int linePad = typeStartPos - linePrefix.length();
-            if (linePad > 0) {
-                out.printf("%s%-" + linePad + "s[%s]%n", linePrefix, "", typeName);
-            } else {
-                out.printf("%s[%s]%n", linePrefix, typeName);
-            }
-
-            if (help.length() != 0) {
-                List<String> helpLines = wrap(help, PROPERTY_LINE_WIDTH - PROPERTY_HELP_INDENT);
-                for (int i = 0; i < helpLines.size(); i++) {
-                    out.printf("%" + PROPERTY_HELP_INDENT + "s%s%n", "", helpLines.get(i));
-                }
-            }
-        }
-    }
-
-    /**
      * Compute string similarity based on Dice's coefficient.
      *
      * Ported from str_similar() in globals.cpp.
@@ -291,7 +222,7 @@
     /**
      * Returns the set of options that fuzzy match a given option name.
      */
-    private static List<OptionDescriptor> fuzzyMatch(ServiceLoader<OptionDescriptors> loader, String optionName) {
+    private static List<OptionDescriptor> fuzzyMatch(Iterable<OptionDescriptors> loader, String optionName) {
         List<OptionDescriptor> matches = new ArrayList<>();
         for (OptionDescriptors options : loader) {
             for (OptionDescriptor option : options) {