art: Refactor RuntimeOptions/ParsedOptions

Refactor the RuntimeOptions to be a
type-safe map (VariantMap, see runtime_options.h) and the ParsedOptions
to delegate the parsing to CmdlineParser (see cmdline/cmdline_parser.h).

This is the start of a command line parsing refactor, and may include
more in the future (dex2oat, patchoat, etc).

For more details of the command line parsing generator usage see cmdline/README.md

Change-Id: Ic67c6bca5e1f33bf2ec60e2e3ff8c366bab91563
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b4eb0c0..06d258d 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -81,6 +81,7 @@
 LOCAL_PATH := art
 
 RUNTIME_GTEST_COMMON_SRC_FILES := \
+  cmdline/cmdline_parser_test.cc \
   imgdiag/imgdiag_test.cc \
   runtime/arch/arch_test.cc \
   runtime/arch/instruction_set_test.cc \
@@ -103,6 +104,7 @@
   runtime/base/scoped_flock_test.cc \
   runtime/base/stringprintf_test.cc \
   runtime/base/timing_logger_test.cc \
+  runtime/base/variant_map_test.cc \
   runtime/base/unix_file/fd_file_test.cc \
   runtime/class_linker_test.cc \
   runtime/dex_file_test.cc \
diff --git a/cmdline/README.md b/cmdline/README.md
new file mode 100644
index 0000000..8cac77f
--- /dev/null
+++ b/cmdline/README.md
@@ -0,0 +1,245 @@
+Cmdline
+===================
+
+Introduction
+-------------
+This directory contains the classes that do common command line tool initialization and parsing. The
+long term goal is eventually for all `art` command-line tools to be using these helpers.
+
+----------
+
+
+## Cmdline Parser
+-------------
+
+The `CmdlineParser` class provides a fluent interface using a domain-specific language to quickly
+generate a type-safe value parser that process a user-provided list of strings (`argv`). Currently,
+it can parse a string into a `VariantMap`, although in the future it might be desirable to parse
+into any struct of any field.
+
+To use, create a `CmdlineParser::Builder` and then chain the `Define` methods together with
+`WithType` and `IntoXX` methods.
+
+### Quick Start
+For example, to save the values into a user-defined variant map:
+
+```
+struct FruitVariantMap : VariantMap {
+  static const Key<int> Apple;
+  static const Key<double> Orange;
+  static const Key<bool> Help;
+};
+// Note that some template boilerplate has been avoided for clarity.
+// See variant_map_test.cc for how to completely define a custom map.
+
+using FruitParser = CmdlineParser<FruitVariantMap, FruitVariantMap::Key>;
+
+FruitParser MakeParser() {
+  auto&& builder = FruitParser::Builder();
+  builder.
+   .Define("--help")
+      .IntoKey(FruitVariantMap::Help)
+    Define("--apple:_")
+      .WithType<int>()
+      .IntoKey(FruitVariantMap::Apple)
+   .Define("--orange:_")
+      .WithType<double>()
+      .WithRange(0.0, 1.0)
+      .IntoKey(FruitVariantMap::Orange);
+
+  return builder.Build();
+}
+
+int main(char** argv, int argc) {
+  auto parser = MakeParser();
+  auto result = parser.parse(argv, argc));
+  if (result.isError()) {
+     std::cerr << result.getMessage() << std::endl;
+     return EXIT_FAILURE;
+  }
+  auto map = parser.GetArgumentsMap();
+  std::cout << "Help? " << map.GetOrDefault(FruitVariantMap::Help) << std::endl;
+  std::cout << "Apple? " << map.GetOrDefault(FruitVariantMap::Apple) << std::endl;
+  std::cout << "Orange? " << map.GetOrDefault(FruitVariantMap::Orange) << std::endl;
+
+  return EXIT_SUCCESS;
+}
+```
+
+In the above code sample, we define a parser which is capable of parsing something like `--help
+--apple:123 --orange:0.456` . It will error out automatically if invalid flags are given, or if the
+appropriate flags are given but of the the wrong type/range. So for example, `--foo` will not parse
+(invalid argument), neither will `--apple:fruit` (fruit is not an int) nor `--orange:1234` (1234 is
+out of range of [0.0, 1.0])
+
+### Argument Definitions in Detail
+#### Define method
+The 'Define' method takes one or more aliases for the argument. Common examples might be `{"-h",
+"--help"}` where both `--help` and `-h` are aliases for the same argument.
+
+The simplest kind of argument just tests for presence, but we often want to parse out a particular
+type of value (such as an int or double as in the above `FruitVariantMap` example). To do that, a
+_wildcard_ must be used to denote the location within the token that the type will be parsed out of.
+
+For example with `-orange:_` the parse would know to check all tokens in an `argv` list for the
+`-orange:` prefix and then strip it, leaving only the remains to be parsed.
+
+#### WithType method (optional)
+After an argument definition is provided, the parser builder needs to know what type the argument
+will be in order to provide the type safety and make sure the rest of the argument definition is
+correct as early as possible (in essence, everything but the parsing of the argument name is done at
+compile time).
+
+Everything that follows a `WithType<T>()` call is thus type checked to only take `T` values.
+
+If this call is omitted, the parser generator assumes you are building a `Unit` type (i.e. an
+argument that only cares about presence).
+
+#### WithRange method (optional)
+Some values will not make sense outside of a `[min, max]` range, so this is an option to quickly add
+a range check without writing custom code. The range check is performed after the main parsing
+happens and happens for any type implementing the `<=` operators.
+
+#### WithValueMap (optional)
+When parsing an enumeration, it might be very convenient to map a list of possible argument string
+values into its runtime value.
+
+With something like
+```
+    .Define("-hello:_")
+      .WithValueMap({"world", kWorld},
+                    {"galaxy", kGalaxy})
+```
+It will parse either `-hello:world` or `-hello:galaxy` only (and error out on other variations of
+`-hello:whatever`), converting it to the type-safe value of `kWorld` or `kGalaxy` respectively.
+
+This is meant to be another shorthand (like `WithRange`) to avoid writing a custom type parser. In
+general it takes a variadic number of `pair<const char* /*arg name*/, T /*value*/>`.
+
+#### WithValues (optional)
+When an argument definition has multiple aliases with no wildcards, it might be convenient to
+quickly map them into discrete values.
+
+For example:
+```
+  .Define({"-xinterpret", "-xnointerpret"})
+    .WithValues({true, false}
+```
+It will parse `-xinterpret` as `true` and `-xnointerpret` as `false`.
+
+In general, it uses the position of the argument alias to map into the WithValues position value.
+
+(Note that this method will not work when the argument definitions have a wildcard because there is
+no way to position-ally match that).
+
+#### AppendValues (optional)
+By default, the argument is assumed to appear exactly once, and if the user specifies it more than
+once, only the latest value is taken into account (and all previous occurrences of the argument are
+ignored).
+
+In some situations, we may want to accumulate the argument values instead of discarding the previous
+ones.
+
+For example
+```
+  .Define("-D")
+     .WithType<std::vector<std::string>)()
+     .AppendValues()
+```
+Will parse something like `-Dhello -Dworld -Dbar -Dbaz` into `std::vector<std::string>{"hello",
+"world", "bar", "baz"}`.
+
+### Setting an argument parse target (required)
+To complete an argument definition, the parser generator also needs to know where to save values.
+Currently, only `IntoKey` is supported, but that may change in the future.
+
+#### IntoKey (required)
+This specifies that when a value is parsed, it will get saved into a variant map using the specific
+key.
+
+For example,
+```
+   .Define("-help")
+     .IntoKey(Map::Help)
+```
+will save occurrences of the `-help` argument by doing a `Map.Set(Map::Help, ParsedValue("-help"))`
+where `ParsedValue` is an imaginary function that parses the `-help` argment into a specific type
+set by `WithType`.
+
+### Ignoring unknown arguments
+This is highly discouraged, but for compatibility with `JNI` which allows argument ignores, there is
+an option to ignore any argument tokens that are not known to the parser. This is done with the
+`Ignore` function which takes a list of argument definition names.
+
+It's semantically equivalent to making a series of argument definitions that map to `Unit` but don't
+get saved anywhere. Values will still get parsed as normal, so it will *not* ignore known arguments
+with invalid values, only user-arguments for which it could not find a matching argument definition.
+
+### Parsing custom types
+Any type can be parsed from a string by specializing the `CmdlineType` class and implementing the
+static interface provided by `CmdlineTypeParser`. It is recommended to inherit from
+`CmdlineTypeParser` since it already provides default implementations for every method.
+
+The `Parse` method should be implemented for most types. Some types will allow appending (such as an
+`std::vector<std::string>` and are meant to be used with `AppendValues` in which case the
+`ParseAndAppend` function should be implemented.
+
+For example:
+```
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+  Result Parse(const std::string& str) {
+    char* end = nullptr;
+    errno = 0;
+    double value = strtod(str.c_str(), &end);
+
+    if (*end != '\0') {
+      return Result::Failure("Failed to parse double from " + str);
+    }
+    if (errno == ERANGE) {
+      return Result::OutOfRange(
+          "Failed to parse double from " + str + "; overflow/underflow occurred");
+    }
+
+    return Result::Success(value);
+  }
+
+  static const char* Name() { return "double"; }
+  // note: Name() is just here for more user-friendly errors,
+  // but in the future we will use non-standard ways of getting the type name
+  // at compile-time and this will no longer be required
+};
+```
+Will parse any non-append argument definitions with a type of `double`.
+
+For an appending example:
+```
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<std::string>& existing_value) {
+    existing_value.push_back(args);
+    return Result::SuccessNoValue();
+  }
+  static const char* Name() { return "std::vector<std::string>"; }
+};
+```
+Will parse multiple instances of the same argument repeatedly into the `existing_value` (which will
+be default-constructed to `T{}` for the first occurrence of the argument).
+
+#### What is a `Result`?
+`Result` is a typedef for `CmdlineParseResult<T>` and it acts similar to a poor version of
+`Either<Left, Right>` in Haskell. In particular, it would be similar to `Either< int ErrorCode,
+Maybe<T> >`.
+
+There are helpers like `Result::Success(value)`, `Result::Failure(string message)` and so on to
+quickly construct these without caring about the type.
+
+When successfully parsing a single value, `Result::Success(value)` should be used, and when
+successfully parsing an appended value, use `Result::SuccessNoValue()` and write back the new value
+into `existing_value` as an out-parameter.
+
+When many arguments are parsed, the result is collapsed down to a `CmdlineResult` which acts as a
+`Either<int ErrorCode, Unit>` where the right side simply indicates success. When values are
+successfully stored, the parser will automatically save it into the target destination as a side
+effect.
diff --git a/cmdline/cmdline_parse_result.h b/cmdline/cmdline_parse_result.h
new file mode 100644
index 0000000..d6ac341
--- /dev/null
+++ b/cmdline/cmdline_parse_result.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
+
+#include "cmdline_result.h"
+#include "detail/cmdline_parser_detail.h"
+
+namespace art {
+// Result of a type-parsing attempt. If successful holds the strongly-typed value,
+// otherwise it holds either a usage or a failure string message that should be displayed back
+// to the user.
+//
+// CmdlineType::Parse/CmdlineType::ParseAndAppend must return this type.
+template <typename T>
+struct CmdlineParseResult : CmdlineResult {
+  using CmdlineResult::CmdlineResult;
+
+  // Create an error result with the usage error code and the specified message.
+  static CmdlineParseResult Usage(const std::string& message) {
+    return CmdlineParseResult(kUsage, message);
+  }
+
+  // Create an error result with the failure error code and no message.
+  static CmdlineParseResult<T> Failure()  {
+    return CmdlineParseResult(kFailure);
+  }
+
+  // Create an error result with the failure error code and no message.
+  static CmdlineParseResult<T> Failure(const std::string& message) {
+    return CmdlineParseResult(kFailure, message);
+  }
+
+  // Create a successful result which holds the specified value.
+  static CmdlineParseResult<T> Success(const T& value) {
+    return CmdlineParseResult(value);
+  }
+
+  // Create a successful result, taking over the value.
+  static CmdlineParseResult<T> Success(T&& value) {
+    return CmdlineParseResult(std::forward<T>(value));
+  }
+
+  // Create succesful result, without any values. Used when a value was successfully appended
+  // into an existing object.
+  static CmdlineParseResult<T> SuccessNoValue() {
+    return CmdlineParseResult(T {});
+  }
+
+  // Create an error result with the OutOfRange error and the specified message.
+  static CmdlineParseResult<T> OutOfRange(const std::string& message) {
+    return CmdlineParseResult(kOutOfRange, message);
+  }
+
+  // Create an error result with the OutOfRange code and a custom message
+  // which is printed from the actual/min/max values.
+  // Values are converted to string using the ostream<< operator.
+  static CmdlineParseResult<T> OutOfRange(const T& value,
+                                          const T& min,
+                                          const T& max) {
+    return CmdlineParseResult(kOutOfRange,
+                              "actual: " + art::detail::ToStringAny(value) +
+                              ", min: " + art::detail::ToStringAny(min) +
+                              ", max: " + art::detail::ToStringAny(max));
+  }
+
+  // Get a read-only reference to the underlying value.
+  // The result must have been successful and must have a value.
+  const T& GetValue() const {
+    assert(IsSuccess());
+    assert(has_value_);
+    return value_;
+  }
+
+  // Get a mutable reference to the underlying value.
+  // The result must have been successful and must have a value.
+  T& GetValue() {
+    assert(IsSuccess());
+    assert(has_value_);
+    return value_;
+  }
+
+  // Take over the value.
+  // The result must have been successful and must have a value.
+  T&& ReleaseValue() {
+    assert(IsSuccess());
+    assert(has_value_);
+    return std::move(value_);
+  }
+
+  // Whether or not the result has a value (e.g. created with Result::Success).
+  // Error results never have values, success results commonly, but not always, have values.
+  bool HasValue() const {
+    return has_value_;
+  }
+
+  // Cast an error-result from type T2 to T1.
+  // Safe since error-results don't store a typed value.
+  template <typename T2>
+  static CmdlineParseResult<T> CastError(const CmdlineParseResult<T2>& other) {
+    assert(other.IsError());
+    return CmdlineParseResult<T>(other.GetStatus());
+  }
+
+  // Make sure copying is allowed
+  CmdlineParseResult(const CmdlineParseResult& other) = default;
+  // Make sure moving is cheap
+  CmdlineParseResult(CmdlineParseResult&& other) = default;
+
+ private:
+  explicit CmdlineParseResult(const T& value)
+    : CmdlineResult(kSuccess), value_(value), has_value_(true) {}
+  explicit CmdlineParseResult(T&& value)
+    : CmdlineResult(kSuccess), value_(std::forward<T>(value)), has_value_(true) {}
+  explicit CmdlineParseResult()
+    : CmdlineResult(kSuccess), value_(), has_value_(false) {}
+
+  T value_;
+  bool has_value_ = false;
+};
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_PARSE_RESULT_H_
diff --git a/cmdline/cmdline_parser.h b/cmdline/cmdline_parser.h
new file mode 100644
index 0000000..a555356
--- /dev/null
+++ b/cmdline/cmdline_parser.h
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_PARSER_H_
+
+#define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
+
+#include "cmdline/detail/cmdline_parser_detail.h"
+#include "cmdline/detail/cmdline_parse_argument_detail.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+
+#include "cmdline_type_parser.h"
+#include "token_range.h"
+#include "cmdline_types.h"
+#include "cmdline_result.h"
+#include "cmdline_parse_result.h"
+
+#include "runtime/base/variant_map.h"
+#include "utils.h"
+
+#include <vector>
+#include <memory>
+
+namespace art {
+// Build a parser for command line arguments with a small domain specific language.
+// Each parsed type must have a specialized CmdlineType<T> in order to do the string->T parsing.
+// Each argument must also have a VariantMap::Key<T> in order to do the T storage.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser {
+  template <typename TArg>
+  struct ArgumentBuilder;
+
+  struct Builder;  // Build the parser.
+  struct UntypedArgumentBuilder;  // Build arguments which weren't yet given a type.
+
+ private:
+  // Forward declare some functions that we need to use before fully-defining structs.
+  template <typename TArg>
+  static ArgumentBuilder<TArg> CreateArgumentBuilder(Builder& parent);
+  static void AppendCompletedArgument(Builder& builder, detail::CmdlineParseArgumentAny* arg);
+
+  // Allow argument definitions to save their values when they are parsed,
+  // without having a dependency on CmdlineParser or any of the builders.
+  //
+  // A shared pointer to the save destination is saved into the load/save argument callbacks.
+  //
+  // This also allows the underlying storage (i.e. a variant map) to be released
+  // to the user, without having to recreate all of the callbacks.
+  struct SaveDestination {
+    SaveDestination() : variant_map_(new TVariantMap()) {}
+
+    // Save value to the variant map.
+    template <typename TArg>
+    void SaveToMap(const TVariantMapKey<TArg>& key, TArg& value) {
+      variant_map_->Set(key, value);
+    }
+
+    // Get the existing value from a map, creating the value if it did not already exist.
+    template <typename TArg>
+    TArg& GetOrCreateFromMap(const TVariantMapKey<TArg>& key) {
+      auto* ptr = variant_map_->Get(key);
+      if (ptr == nullptr) {
+        variant_map_->Set(key, TArg());
+        ptr = variant_map_->Get(key);
+        assert(ptr != nullptr);
+      }
+
+      return *ptr;
+    }
+
+   protected:
+    // Release the map, clearing it as a side-effect.
+    // Future saves will be distinct from previous saves.
+    TVariantMap&& ReleaseMap() {
+      return std::move(*variant_map_);
+    }
+
+    // Get a read-only reference to the variant map.
+    const TVariantMap& GetMap() {
+      return *variant_map_;
+    }
+
+    // Clear all potential save targets.
+    void Clear() {
+      variant_map_->Clear();
+    }
+
+   private:
+    // Don't try to copy or move this. Just don't.
+    SaveDestination(const SaveDestination&) = delete;
+    SaveDestination(SaveDestination&&) = delete;
+    SaveDestination& operator=(const SaveDestination&) = delete;
+    SaveDestination& operator=(SaveDestination&&) = delete;
+
+    std::shared_ptr<TVariantMap> variant_map_;
+
+    // Allow the parser to change the underlying pointers when we release the underlying storage.
+    friend struct CmdlineParser;
+  };
+
+ public:
+  // Builder for the argument definition of type TArg. Do not use this type directly,
+  // it is only a separate type to provide compile-time enforcement against doing
+  // illegal builds.
+  template <typename TArg>
+  struct ArgumentBuilder {
+    // Add a range check to this argument.
+    ArgumentBuilder<TArg>& WithRange(const TArg& min, const TArg& max) {
+      argument_info_.has_range_ = true;
+      argument_info_.min_ = min;
+      argument_info_.max_ = max;
+
+      return *this;
+    }
+
+    // Map the list of names into the list of values. List of names must not have
+    // any wildcards '_' in it.
+    //
+    // Do not use if a value map has already been set.
+    ArgumentBuilder<TArg>& WithValues(std::initializer_list<TArg> value_list) {
+      SetValuesInternal(value_list);
+      return *this;
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Map the parsed string values (from _) onto a concrete value. If no wildcard
+    // has been specified, then map the value directly from the arg name (i.e.
+    // if there are multiple aliases, then use the alias to do the mapping).
+    //
+    // Do not use if a values list has already been set.
+    ArgumentBuilder<TArg>& WithValueMap(
+        std::initializer_list<std::pair<const char*, TArg>> key_value_list) {
+      assert(!argument_info_.has_value_list_);
+
+      argument_info_.has_value_map_ = true;
+      argument_info_.value_map_ = key_value_list;
+
+      return *this;
+    }
+
+    // If this argument is seen multiple times, successive arguments mutate the same value
+    // instead of replacing it with a new value.
+    ArgumentBuilder<TArg>& AppendValues() {
+      argument_info_.appending_values_ = true;
+
+      return *this;
+    }
+
+    // Convenience type alias for the variant map key type definition.
+    using MapKey = TVariantMapKey<TArg>;
+
+    // Write the results of this argument into the key.
+    // To look up the parsed arguments, get the map and then use this key with VariantMap::Get
+    CmdlineParser::Builder& IntoKey(const MapKey& key) {
+      // Only capture save destination as a pointer.
+      // This allows the parser to later on change the specific save targets.
+      auto save_destination = save_destination_;
+      save_value_ = [save_destination, &key](TArg& value) {
+        save_destination->SaveToMap(key, value);
+        CMDLINE_DEBUG_LOG << "Saved value into map '"
+            << detail::ToStringAny(value) << "'" << std::endl;
+      };
+
+      load_value_ = [save_destination, &key]() -> TArg& {
+        TArg& value = save_destination->GetOrCreateFromMap(key);
+        CMDLINE_DEBUG_LOG << "Loaded value from map '" << detail::ToStringAny(value) << "'"
+            << std::endl;
+
+        return value;
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    // Ensure we always move this when returning a new builder.
+    ArgumentBuilder(ArgumentBuilder&&) = default;
+
+   protected:
+    // Used by builder to internally ignore arguments by dropping them on the floor after parsing.
+    CmdlineParser::Builder& IntoIgnore() {
+      save_value_ = [](TArg& value) {
+        CMDLINE_DEBUG_LOG << "Ignored value '" << detail::ToStringAny(value) << "'" << std::endl;
+      };
+      load_value_ = []() -> TArg& {
+        assert(false && "Should not be appending values to ignored arguments");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+
+      save_value_specified_ = true;
+      load_value_specified_ = true;
+
+      CompleteArgument();
+      return parent_;
+    }
+
+    void SetValuesInternal(const std::vector<TArg>&& value_list) {
+      assert(!argument_info_.has_value_map_);
+
+      argument_info_.has_value_list_ = true;
+      argument_info_.value_list_ = value_list;
+    }
+
+    void SetNames(std::vector<const char*>&& names) {
+      argument_info_.names_ = names;
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      argument_info_.names_ = names;
+    }
+
+   private:
+    // Copying is bad. Move only.
+    ArgumentBuilder(const ArgumentBuilder&) = delete;
+
+    // Called by any function that doesn't chain back into this builder.
+    // Completes the argument builder and save the information into the main builder.
+    void CompleteArgument() {
+      assert(save_value_specified_ &&
+             "No Into... function called, nowhere to save parsed values to");
+      assert(load_value_specified_ &&
+             "No Into... function called, nowhere to load parsed values from");
+
+      argument_info_.CompleteArgument();
+
+      // Appending the completed argument is destructive. The object is no longer
+      // usable since all the useful information got moved out of it.
+      AppendCompletedArgument(parent_,
+                              new detail::CmdlineParseArgument<TArg>(
+                                  std::move(argument_info_),
+                                  std::move(save_value_),
+                                  std::move(load_value_)));
+    }
+
+    friend struct CmdlineParser;
+    friend struct CmdlineParser::Builder;
+    friend struct CmdlineParser::UntypedArgumentBuilder;
+
+    ArgumentBuilder(CmdlineParser::Builder& parser,
+                    std::shared_ptr<SaveDestination> save_destination)
+        : parent_(parser),
+          save_value_specified_(false),
+          load_value_specified_(false),
+          save_destination_(save_destination) {
+      save_value_ = [](TArg&) {
+        assert(false && "No save value function defined");
+      };
+
+      load_value_ = []() -> TArg& {
+        assert(false && "No load value function defined");
+        return *reinterpret_cast<TArg*>(0);  // Blow up.
+      };
+    }
+
+    CmdlineParser::Builder& parent_;
+    std::function<void(TArg&)> save_value_;
+    std::function<TArg&(void)> load_value_;
+    bool save_value_specified_;
+    bool load_value_specified_;
+    detail::CmdlineParserArgumentInfo<TArg> argument_info_;
+
+    std::shared_ptr<SaveDestination> save_destination_;
+  };
+
+  struct UntypedArgumentBuilder {
+    // Set a type for this argument. The specific subcommand parser is looked up by the type.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithType() {
+      return CreateTypedBuilder<TArg>();
+    }
+
+    // When used with multiple aliases, map the position of the alias to the value position.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValues(std::initializer_list<TArg> values) {
+      auto&& a = CreateTypedBuilder<TArg>();
+      a.WithValues(values);
+      return std::move(a);
+    }
+
+    // When used with a single alias, map the alias into this value.
+    // Same as 'WithValues({value})' , but allows the omission of the curly braces {}.
+    template <typename TArg>
+    ArgumentBuilder<TArg> WithValue(const TArg& value) {
+      return WithValues({ value });
+    }
+
+    // Set the current building argument to target this key.
+    // When this command line argument is parsed, it can be fetched with this key.
+    Builder& IntoKey(const TVariantMapKey<Unit>& key) {
+      return CreateTypedBuilder<Unit>().IntoKey(key);
+    }
+
+    // Ensure we always move this when returning a new builder.
+    UntypedArgumentBuilder(UntypedArgumentBuilder&&) = default;
+
+   protected:
+    void SetNames(std::vector<const char*>&& names) {
+      names_ = std::move(names);
+    }
+
+    void SetNames(std::initializer_list<const char*> names) {
+      names_ = names;
+    }
+
+   private:
+    // No copying. Move instead.
+    UntypedArgumentBuilder(const UntypedArgumentBuilder&) = delete;
+
+    template <typename TArg>
+    ArgumentBuilder<TArg> CreateTypedBuilder() {
+      auto&& b = CreateArgumentBuilder<TArg>(parent_);
+      InitializeTypedBuilder(&b);  // Type-specific initialization
+      b.SetNames(std::move(names_));
+      return std::move(b);
+    }
+
+    template <typename TArg = Unit>
+    typename std::enable_if<std::is_same<TArg, Unit>::value>::type
+    InitializeTypedBuilder(ArgumentBuilder<TArg>* arg_builder) {
+      // Every Unit argument implicitly maps to a runtime value of Unit{}
+      std::vector<Unit> values(names_.size(), Unit{});  // NOLINT [whitespace/braces] [5]
+      arg_builder->SetValuesInternal(std::move(values));
+    }
+
+    // No extra work for all other types
+    void InitializeTypedBuilder(void*) {}
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct Builder;
+
+    explicit UntypedArgumentBuilder(CmdlineParser::Builder& parent) : parent_(parent) {}
+    // UntypedArgumentBuilder(UntypedArgumentBuilder&& other) = default;
+
+    CmdlineParser::Builder& parent_;
+    std::vector<const char*> names_;
+  };
+
+  // Build a new parser given a chain of calls to define arguments.
+  struct Builder {
+    Builder() : save_destination_(new SaveDestination()) {}
+
+    // Define a single argument. The default type is Unit.
+    UntypedArgumentBuilder Define(const char* name) {
+      return Define({name});
+    }
+
+    // Define a single argument with multiple aliases.
+    UntypedArgumentBuilder Define(std::initializer_list<const char*> names) {
+      auto&& b = UntypedArgumentBuilder(*this);
+      b.SetNames(names);
+      return std::move(b);
+    }
+
+    // Whether the parser should give up on unrecognized arguments. Not recommended.
+    Builder& IgnoreUnrecognized(bool ignore_unrecognized) {
+      ignore_unrecognized_ = ignore_unrecognized;
+      return *this;
+    }
+
+    // Provide a list of arguments to ignore for backwards compatibility.
+    Builder& Ignore(std::initializer_list<const char*> ignore_list) {
+      for (auto&& ignore_name : ignore_list) {
+        std::string ign = ignore_name;
+
+        // Ignored arguments are just like a regular definition which have very
+        // liberal parsing requirements (no range checks, no value checks).
+        // Unlike regular argument definitions, when a value gets parsed into its
+        // stronger type, we just throw it away.
+
+        if (ign.find("_") != std::string::npos) {  // Does the arg-def have a wildcard?
+          // pretend this is a string, e.g. -Xjitconfig:<anythinggoeshere>
+          auto&& builder = Define(ignore_name).template WithType<std::string>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        } else {
+          // pretend this is a unit, e.g. -Xjitblocking
+          auto&& builder = Define(ignore_name).template WithType<Unit>().IntoIgnore();
+          assert(&builder == this);
+          (void)builder;  // Ignore pointless unused warning, it's used in the assert.
+        }
+      }
+      ignore_list_ = ignore_list;
+      return *this;
+    }
+
+    // Finish building the parser; performs sanity checks. Return value is moved, not copied.
+    // Do not call this more than once.
+    CmdlineParser Build() {
+      assert(!built_);
+      built_ = true;
+
+      auto&& p = CmdlineParser(ignore_unrecognized_,
+                               std::move(ignore_list_),
+                               save_destination_,
+                               std::move(completed_arguments_));
+
+      return std::move(p);
+    }
+
+   protected:
+    void AppendCompletedArgument(detail::CmdlineParseArgumentAny* arg) {
+      auto smart_ptr = std::unique_ptr<detail::CmdlineParseArgumentAny>(arg);
+      completed_arguments_.push_back(std::move(smart_ptr));
+    }
+
+   private:
+    // No copying now!
+    Builder(const Builder& other) = delete;
+
+    template <typename TArg>
+    friend struct ArgumentBuilder;
+    friend struct UntypedArgumentBuilder;
+    friend struct CmdlineParser;
+
+    bool built_ = false;
+    bool ignore_unrecognized_ = false;
+    std::vector<const char*> ignore_list_;
+    std::shared_ptr<SaveDestination> save_destination_;
+
+    std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+  };
+
+  CmdlineResult Parse(const std::string& argv) {
+    std::vector<std::string> tokenized;
+    Split(argv, ' ', &tokenized);
+
+    return Parse(TokenRange(std::move(tokenized)));
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  CmdlineResult Parse(const char* argv) {
+    return Parse(std::string(argv));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<const char*>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments; storing the results into the arguments map. Returns success value.
+  // Assumes that argv[0] is a valid argument (i.e. not the program name).
+  CmdlineResult Parse(const std::vector<std::string>& argv) {
+    return Parse(TokenRange(argv.begin(), argv.end()));
+  }
+
+  // Parse the arguments (directly from an int main(argv,argc)). Returns success value.
+  // Assumes that argv[0] is the program name, and ignores it.
+  CmdlineResult Parse(const char* argv[], int argc) {
+    return Parse(TokenRange(&argv[1], argc - 1));  // ignore argv[0] because it's the program name
+  }
+
+  // Look up the arguments that have been parsed; use the target keys to lookup individual args.
+  const TVariantMap& GetArgumentsMap() const {
+    return save_destination_->GetMap();
+  }
+
+  // Release the arguments map that has been parsed; useful for move semantics.
+  TVariantMap&& ReleaseArgumentsMap() {
+    return save_destination_->ReleaseMap();
+  }
+
+  // How many arguments were defined?
+  size_t CountDefinedArguments() const {
+    return completed_arguments_.size();
+  }
+
+  // Ensure we have a default move constructor.
+  CmdlineParser(CmdlineParser&& other) = default;
+  // Ensure we have a default move assignment operator.
+  CmdlineParser& operator=(CmdlineParser&& other) = default;
+
+ private:
+  friend struct Builder;
+
+  // Construct a new parser from the builder. Move all the arguments.
+  explicit CmdlineParser(bool ignore_unrecognized,
+                         std::vector<const char*>&& ignore_list,
+                         std::shared_ptr<SaveDestination> save_destination,
+                         std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>>&&
+                             completed_arguments)
+    : ignore_unrecognized_(ignore_unrecognized),
+      ignore_list_(std::move(ignore_list)),
+      save_destination_(save_destination),
+      completed_arguments_(std::move(completed_arguments)) {
+    assert(save_destination != nullptr);
+  }
+
+  // Parse the arguments; storing results into the arguments map. Returns success value.
+  // The parsing will fail on the first non-success parse result and return that error.
+  //
+  // All previously-parsed arguments are cleared out.
+  // Otherwise, all parsed arguments will be stored into SaveDestination as a side-effect.
+  // A partial parse will result only in a partial save of the arguments.
+  CmdlineResult Parse(TokenRange&& arguments_list) {
+    save_destination_->Clear();
+
+    for (size_t i = 0; i < arguments_list.Size(); ) {
+      TokenRange possible_name = arguments_list.Slice(i);
+
+      size_t best_match_size = 0;  // How many tokens were matched in the best case.
+      size_t best_match_arg_idx = 0;
+      bool matched = false;  // At least one argument definition has been matched?
+
+      // Find the closest argument definition for the remaining token range.
+      size_t arg_idx = 0;
+      for (auto&& arg : completed_arguments_) {
+        size_t local_match = arg->MaybeMatches(possible_name);
+
+        if (local_match > best_match_size) {
+          best_match_size = local_match;
+          best_match_arg_idx = arg_idx;
+          matched = true;
+        }
+        arg_idx++;
+      }
+
+      // Saw some kind of unknown argument
+      if (matched == false) {
+        if (UNLIKELY(ignore_unrecognized_)) {  // This is usually off, we only need it for JNI.
+          // Consume 1 token and keep going, hopefully the next token is a good one.
+          ++i;
+          continue;
+        }
+        // Common case:
+        // Bail out on the first unknown argument with an error.
+        return CmdlineResult(CmdlineResult::kUnknown,
+                             std::string("Unknown argument: ") + possible_name[0]);
+      }
+
+      // Look at the best-matched argument definition and try to parse against that.
+      auto&& arg = completed_arguments_[best_match_arg_idx];
+
+      assert(arg->MaybeMatches(possible_name) == best_match_size);
+
+      // Try to parse the argument now, if we have enough tokens.
+      std::pair<size_t, size_t> num_tokens = arg->GetNumTokens();
+      size_t min_tokens;
+      size_t max_tokens;
+
+      std::tie(min_tokens, max_tokens) = num_tokens;
+
+      if ((i + min_tokens) > arguments_list.Size()) {
+        // expected longer command line but it was too short
+        // e.g. if the argv was only "-Xms" without specifying a memory option
+        CMDLINE_DEBUG_LOG << "Parse failure, i = " << i << ", arg list " << arguments_list.Size() <<
+            " num tokens in arg_def: " << min_tokens << "," << max_tokens << std::endl;
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") +
+                             possible_name[0] + ": incomplete command line arguments, expected "
+                             + std::to_string(size_t(i + min_tokens) - arguments_list.Size()) +
+                             " more tokens");
+      }
+
+      if (best_match_size > max_tokens || best_match_size < min_tokens) {
+        // Even our best match was out of range, so parsing would fail instantly.
+        return CmdlineResult(CmdlineResult::kFailure,
+                             std::string("Argument ") + possible_name[0] + ": too few tokens "
+                             "matched " + std::to_string(best_match_size)
+                             + " but wanted " + std::to_string(num_tokens.first));
+      }
+
+      // We have enough tokens to begin exact parsing.
+      TokenRange exact_range = possible_name.Slice(0, max_tokens);
+
+      size_t consumed_tokens = 1;  // At least 1 if we ever want to try to resume parsing on error
+      CmdlineResult parse_attempt = arg->ParseArgument(exact_range, &consumed_tokens);
+
+      if (parse_attempt.IsError()) {
+        // We may also want to continue parsing the other tokens to gather more errors.
+        return parse_attempt;
+      }  // else the value has been successfully stored into the map
+
+      assert(consumed_tokens > 0);  // Don't hang in an infinite loop trying to parse
+      i += consumed_tokens;
+
+      // TODO: also handle ignoring arguments for backwards compatibility
+    }  // for
+
+    return CmdlineResult(CmdlineResult::kSuccess);
+  }
+
+  bool ignore_unrecognized_ = false;
+  std::vector<const char*> ignore_list_;
+  std::shared_ptr<SaveDestination> save_destination_;
+  std::vector<std::unique_ptr<detail::CmdlineParseArgumentAny>> completed_arguments_;
+};
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+template <typename TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>
+CmdlineParser<TVariantMap, TVariantMapKey>::CreateArgumentBuilder(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& parent) {
+  return CmdlineParser<TVariantMap, TVariantMapKey>::ArgumentBuilder<TArg>(
+      parent, parent.save_destination_);
+}
+
+// This has to be defined after everything else, since we want the builders to call this.
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+void CmdlineParser<TVariantMap, TVariantMapKey>::AppendCompletedArgument(
+    CmdlineParser<TVariantMap, TVariantMapKey>::Builder& builder,
+    detail::CmdlineParseArgumentAny* arg) {
+  builder.AppendCompletedArgument(arg);
+}
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_PARSER_H_
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
new file mode 100644
index 0000000..a875641
--- /dev/null
+++ b/cmdline/cmdline_parser_test.cc
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cmdline_parser.h"
+#include "runtime/runtime_options.h"
+#include "runtime/parsed_options.h"
+
+#include "utils.h"
+#include <numeric>
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+                                        reinterpret_cast<void*>(NULL));
+
+namespace art {
+  bool UsuallyEquals(double expected, double actual);
+
+  // This has a gtest dependency, which is why it's in the gtest only.
+  bool operator==(const TestProfilerOptions& lhs, const TestProfilerOptions& rhs) {
+    return lhs.enabled_ == rhs.enabled_ &&
+        lhs.output_file_name_ == rhs.output_file_name_ &&
+        lhs.period_s_ == rhs.period_s_ &&
+        lhs.duration_s_ == rhs.duration_s_ &&
+        lhs.interval_us_ == rhs.interval_us_ &&
+        UsuallyEquals(lhs.backoff_coefficient_, rhs.backoff_coefficient_) &&
+        UsuallyEquals(lhs.start_immediately_, rhs.start_immediately_) &&
+        UsuallyEquals(lhs.top_k_threshold_, rhs.top_k_threshold_) &&
+        UsuallyEquals(lhs.top_k_change_threshold_, rhs.top_k_change_threshold_) &&
+        lhs.profile_type_ == rhs.profile_type_ &&
+        lhs.max_stack_depth_ == rhs.max_stack_depth_;
+  }
+
+  bool UsuallyEquals(double expected, double actual) {
+    using FloatingPoint = ::testing::internal::FloatingPoint<double>;
+
+    FloatingPoint exp(expected);
+    FloatingPoint act(actual);
+
+    // Compare with ULPs instead of comparing with ==
+    return exp.AlmostEquals(act);
+  }
+
+  template <typename T>
+  bool UsuallyEquals(const T& expected, const T& actual,
+                     typename std::enable_if<
+                         detail::SupportsEqualityOperator<T>::value>::type* = 0) {
+    return expected == actual;
+  }
+
+  // Try to use memcmp to compare simple plain-old-data structs.
+  //
+  // This should *not* generate false positives, but it can generate false negatives.
+  // This will mostly work except for fields like float which can have different bit patterns
+  // that are nevertheless equal.
+  // If a test is failing because the structs aren't "equal" when they really are
+  // then it's recommended to implement operator== for it instead.
+  template <typename T, typename ... Ignore>
+  bool UsuallyEquals(const T& expected, const T& actual,
+                     const Ignore& ... more ATTRIBUTE_UNUSED,
+                     typename std::enable_if<std::is_pod<T>::value>::type* = 0,
+                     typename std::enable_if<!detail::SupportsEqualityOperator<T>::value>::type* = 0
+                     ) {
+    return memcmp(std::addressof(expected), std::addressof(actual), sizeof(T)) == 0;
+  }
+
+  bool UsuallyEquals(const XGcOption& expected, const XGcOption& actual) {
+    return memcmp(std::addressof(expected), std::addressof(actual), sizeof(expected)) == 0;
+  }
+
+  bool UsuallyEquals(const char* expected, std::string actual) {
+    return std::string(expected) == actual;
+  }
+
+  template <typename TMap, typename TKey, typename T>
+  ::testing::AssertionResult IsExpectedKeyValue(const T& expected,
+                                                const TMap& map,
+                                                const TKey& key) {
+    auto* actual = map.Get(key);
+    if (actual != nullptr) {
+      if (!UsuallyEquals(expected, *actual)) {
+        return ::testing::AssertionFailure()
+          << "expected " << detail::ToStringAny(expected) << " but got "
+          << detail::ToStringAny(*actual);
+      }
+      return ::testing::AssertionSuccess();
+    }
+
+    return ::testing::AssertionFailure() << "key was not in the map";
+  }
+
+class CmdlineParserTest : public ::testing::Test {
+ public:
+  CmdlineParserTest() = default;
+  ~CmdlineParserTest() = default;
+
+ protected:
+  using M = RuntimeArgumentMap;
+  using RuntimeParser = ParsedOptions::RuntimeParser;
+
+  static void SetUpTestCase() {
+    art::InitLogging(nullptr);  // argv = null
+  }
+
+  virtual void SetUp() {
+    parser_ = ParsedOptions::MakeParser(false);  // do not ignore unrecognized options
+  }
+
+  static ::testing::AssertionResult IsResultSuccessful(CmdlineResult result) {
+    if (result.IsSuccess()) {
+      return ::testing::AssertionSuccess();
+    } else {
+      return ::testing::AssertionFailure()
+        << result.GetStatus() << " with: " << result.GetMessage();
+    }
+  }
+
+  static ::testing::AssertionResult IsResultFailure(CmdlineResult result,
+                                                    CmdlineResult::Status failure_status) {
+    if (result.IsSuccess()) {
+      return ::testing::AssertionFailure() << " got success but expected failure: "
+          << failure_status;
+    } else if (result.GetStatus() == failure_status) {
+      return ::testing::AssertionSuccess();
+    }
+
+    return ::testing::AssertionFailure() << " expected failure " << failure_status
+        << " but got " << result.GetStatus();
+  }
+
+  std::unique_ptr<RuntimeParser> parser_;
+};
+
+#define EXPECT_KEY_EXISTS(map, key) EXPECT_TRUE((map).Exists(key))
+#define EXPECT_KEY_VALUE(map, key, expected) EXPECT_TRUE(IsExpectedKeyValue(expected, map, key))
+
+#define EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv)               \
+  do {                                                        \
+    EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv)));    \
+    EXPECT_EQ(0u, parser_->GetArgumentsMap().Size());         \
+  } while (false)
+
+#define _EXPECT_SINGLE_PARSE_EXISTS(argv, key)                \
+  do {                                                        \
+    EXPECT_TRUE(IsResultSuccessful(parser_->Parse(argv)));    \
+    RuntimeArgumentMap args = parser_->ReleaseArgumentsMap(); \
+    EXPECT_EQ(1u, args.Size());                               \
+    EXPECT_KEY_EXISTS(args, key);                             \
+
+#define EXPECT_SINGLE_PARSE_EXISTS(argv, key)                 \
+    _EXPECT_SINGLE_PARSE_EXISTS(argv, key);                   \
+  } while (false)
+
+#define EXPECT_SINGLE_PARSE_VALUE(expected, argv, key)        \
+    _EXPECT_SINGLE_PARSE_EXISTS(argv, key);                   \
+    EXPECT_KEY_VALUE(args, key, expected);                    \
+  } while (false)                                             // NOLINT [readability/namespace] [5]
+
+#define EXPECT_SINGLE_PARSE_VALUE_STR(expected, argv, key)    \
+  EXPECT_SINGLE_PARSE_VALUE(std::string(expected), argv, key)
+
+#define EXPECT_SINGLE_PARSE_FAIL(argv, failure_status)         \
+    do {                                                       \
+      EXPECT_TRUE(IsResultFailure(parser_->Parse(argv), failure_status));\
+      RuntimeArgumentMap args = parser_->ReleaseArgumentsMap();\
+      EXPECT_EQ(0u, args.Size());                              \
+    } while (false)
+
+TEST_F(CmdlineParserTest, TestSimpleSuccesses) {
+  auto& parser = *parser_;
+
+  EXPECT_LT(0u, parser.CountDefinedArguments());
+
+  {
+    // Test case 1: No command line arguments
+    EXPECT_TRUE(IsResultSuccessful(parser.Parse("")));
+    RuntimeArgumentMap args = parser.ReleaseArgumentsMap();
+    EXPECT_EQ(0u, args.Size());
+  }
+
+  EXPECT_SINGLE_PARSE_EXISTS("-Xzygote", M::Zygote);
+  EXPECT_SINGLE_PARSE_VALUE_STR("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+  EXPECT_SINGLE_PARSE_VALUE("/hello/world", "-Xbootclasspath:/hello/world", M::BootClassPath);
+  EXPECT_SINGLE_PARSE_VALUE(false, "-Xverify:none", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:remote", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-Xverify:all", M::Verify);
+  EXPECT_SINGLE_PARSE_VALUE(Memory<1>(234), "-Xss234", M::StackSize);
+  EXPECT_SINGLE_PARSE_VALUE(MemoryKiB(1234*MB), "-Xms1234m", M::MemoryInitialSize);
+  EXPECT_SINGLE_PARSE_VALUE(true, "-XX:EnableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+  EXPECT_SINGLE_PARSE_VALUE(false, "-XX:DisableHSpaceCompactForOOM", M::EnableHSpaceCompactForOOM);
+  EXPECT_SINGLE_PARSE_VALUE(0.5, "-XX:HeapTargetUtilization=0.5", M::HeapTargetUtilization);
+  EXPECT_SINGLE_PARSE_VALUE(5u, "-XX:ParallelGCThreads=5", M::ParallelGCThreads);
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestSimpleFailures) {
+  // Test argument is unknown to the parser
+  EXPECT_SINGLE_PARSE_FAIL("abcdefg^%@#*(@#", CmdlineResult::kUnknown);
+  // Test value map substitution fails
+  EXPECT_SINGLE_PARSE_FAIL("-Xverify:whatever", CmdlineResult::kFailure);
+  // Test value type parsing failures
+  EXPECT_SINGLE_PARSE_FAIL("-Xsswhatever", CmdlineResult::kFailure);  // invalid memory value
+  EXPECT_SINGLE_PARSE_FAIL("-Xms123", CmdlineResult::kFailure);       // memory value too small
+  EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=0.0", CmdlineResult::kOutOfRange);  // toosmal
+  EXPECT_SINGLE_PARSE_FAIL("-XX:HeapTargetUtilization=2.0", CmdlineResult::kOutOfRange);  // toolarg
+  EXPECT_SINGLE_PARSE_FAIL("-XX:ParallelGCThreads=-5", CmdlineResult::kOutOfRange);  // too small
+  EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage);  // not a valid suboption
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestLogVerbosity) {
+  {
+    const char* log_args = "-verbose:"
+        "class,compiler,gc,heap,jdwp,jni,monitor,profiler,signals,startup,third-party-jni,"
+        "threads,verifier";
+
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.class_linker = true;
+    log_verbosity.compiler = true;
+    log_verbosity.gc = true;
+    log_verbosity.heap = true;
+    log_verbosity.jdwp = true;
+    log_verbosity.jni = true;
+    log_verbosity.monitor = true;
+    log_verbosity.profiler = true;
+    log_verbosity.signals = true;
+    log_verbosity.startup = true;
+    log_verbosity.third_party_jni = true;
+    log_verbosity.threads = true;
+    log_verbosity.verifier = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
+
+  {
+    const char* log_args = "-verbose:"
+        "class,compiler,gc,heap,jdwp,jni,monitor";
+
+    LogVerbosity log_verbosity = LogVerbosity();
+    log_verbosity.class_linker = true;
+    log_verbosity.compiler = true;
+    log_verbosity.gc = true;
+    log_verbosity.heap = true;
+    log_verbosity.jdwp = true;
+    log_verbosity.jni = true;
+    log_verbosity.monitor = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(log_verbosity, log_args, M::Verbose);
+  }
+
+  EXPECT_SINGLE_PARSE_FAIL("-verbose:blablabla", CmdlineResult::kUsage);  // invalid verbose opt
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestXGcOption) {
+  /*
+   * Test success
+   */
+  {
+    XGcOption option_all_true{};  // NOLINT [readability/braces] [4]
+    option_all_true.collector_type_ = gc::CollectorType::kCollectorTypeCMS;
+    option_all_true.verify_pre_gc_heap_ = true;
+    option_all_true.verify_pre_sweeping_heap_ = true;
+    option_all_true.verify_post_gc_heap_ = true;
+    option_all_true.verify_pre_gc_rosalloc_ = true;
+    option_all_true.verify_pre_sweeping_rosalloc_ = true;
+    option_all_true.verify_post_gc_rosalloc_ = true;
+
+    const char * xgc_args_all_true = "-Xgc:concurrent,"
+        "preverify,presweepingverify,postverify,"
+        "preverify_rosalloc,presweepingverify_rosalloc,"
+        "postverify_rosalloc,precise,"
+        "verifycardtable";
+
+    EXPECT_SINGLE_PARSE_VALUE(option_all_true, xgc_args_all_true, M::GcOption);
+
+    XGcOption option_all_false{};  // NOLINT [readability/braces] [4]
+    option_all_false.collector_type_ = gc::CollectorType::kCollectorTypeMS;
+    option_all_false.verify_pre_gc_heap_ = false;
+    option_all_false.verify_pre_sweeping_heap_ = false;
+    option_all_false.verify_post_gc_heap_ = false;
+    option_all_false.verify_pre_gc_rosalloc_ = false;
+    option_all_false.verify_pre_sweeping_rosalloc_ = false;
+    option_all_false.verify_post_gc_rosalloc_ = false;
+
+    const char* xgc_args_all_false = "-Xgc:nonconcurrent,"
+        "nopreverify,nopresweepingverify,nopostverify,nopreverify_rosalloc,"
+        "nopresweepingverify_rosalloc,nopostverify_rosalloc,noprecise,noverifycardtable";
+
+    EXPECT_SINGLE_PARSE_VALUE(option_all_false, xgc_args_all_false, M::GcOption);
+
+    XGcOption option_all_default{};  // NOLINT [readability/braces] [4]
+
+    option_all_default.collector_type_ = gc::kCollectorTypeDefault;
+    option_all_default.verify_pre_gc_heap_ = false;
+    option_all_default.verify_pre_sweeping_heap_ = kIsDebugBuild;
+    option_all_default.verify_post_gc_heap_ = false;
+    option_all_default.verify_pre_gc_rosalloc_ = kIsDebugBuild;
+    option_all_default.verify_pre_sweeping_rosalloc_ = false;
+    option_all_default.verify_post_gc_rosalloc_ = false;
+
+    const char* xgc_args_blank = "-Xgc:";
+    EXPECT_SINGLE_PARSE_VALUE(option_all_default, xgc_args_blank, M::GcOption);
+  }
+
+  /*
+   * Test failures
+   */
+  EXPECT_SINGLE_PARSE_FAIL("-Xgc:blablabla", CmdlineResult::kUsage);  // invalid Xgc opt
+}  // TEST_F
+
+/*
+ * {"-Xrunjdwp:_", "-agentlib:jdwp=_"}
+ */
+TEST_F(CmdlineParserTest, TestJdwpOptions) {
+  /*
+   * Test success
+   */
+  {
+    /*
+     * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+    opt.port = 8000;
+    opt.server = true;
+
+    const char *opt_args = "-Xrunjdwp:transport=dt_socket,address=8000,server=y";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+  }
+
+  {
+    /*
+     * "Example: -agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n\n");
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    opt.transport = JDWP::JdwpTransportType::kJdwpTransportSocket;
+    opt.host = "localhost";
+    opt.port = 6500;
+    opt.server = false;
+
+    const char *opt_args = "-agentlib:jdwp=transport=dt_socket,address=localhost:6500,server=n";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, opt_args, M::JdwpOptions);
+  }
+
+  /*
+   * Test failures
+   */
+  EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:help", CmdlineResult::kUsage);  // usage for help only
+  EXPECT_SINGLE_PARSE_FAIL("-Xrunjdwp:blabla", CmdlineResult::kFailure);  // invalid subarg
+  EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=help", CmdlineResult::kUsage);  // usage for help only
+  EXPECT_SINGLE_PARSE_FAIL("-agentlib:jdwp=blabla", CmdlineResult::kFailure);  // invalid subarg
+}  // TEST_F
+
+/*
+ * -D_ -D_ -D_ ...
+ */
+TEST_F(CmdlineParserTest, TestPropertiesList) {
+  /*
+   * Test successes
+   */
+  {
+    std::vector<std::string> opt = {"hello"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello", M::PropertiesList);
+  }
+
+  {
+    std::vector<std::string> opt = {"hello", "world"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Dhello -Dworld", M::PropertiesList);
+  }
+
+  {
+    std::vector<std::string> opt = {"one", "two", "three"};
+
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Done -Dtwo -Dthree", M::PropertiesList);
+  }
+}  // TEST_F
+
+/*
+* -Xcompiler-option foo -Xcompiler-option bar ...
+*/
+TEST_F(CmdlineParserTest, TestCompilerOption) {
+ /*
+  * Test successes
+  */
+  {
+    std::vector<std::string> opt = {"hello"};
+    EXPECT_SINGLE_PARSE_VALUE(opt, "-Xcompiler-option hello", M::CompilerOptions);
+  }
+
+  {
+    std::vector<std::string> opt = {"hello", "world"};
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xcompiler-option hello -Xcompiler-option world",
+                              M::CompilerOptions);
+  }
+
+  {
+    std::vector<std::string> opt = {"one", "two", "three"};
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xcompiler-option one -Xcompiler-option two -Xcompiler-option three",
+                              M::CompilerOptions);
+  }
+}  // TEST_F
+
+/*
+* -X-profile-*
+*/
+TEST_F(CmdlineParserTest, TestProfilerOptions) {
+ /*
+  * Test successes
+  */
+
+  {
+    TestProfilerOptions opt;
+    opt.enabled_ = true;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xenable-profiler",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt;
+    // also need to test 'enabled'
+    opt.output_file_name_ = "hello_world.txt";
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-filename:hello_world.txt ",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt = TestProfilerOptions();
+    // also need to test 'enabled'
+    opt.output_file_name_ = "output.txt";
+    opt.period_s_ = 123u;
+    opt.duration_s_ = 456u;
+    opt.interval_us_ = 789u;
+    opt.backoff_coefficient_ = 2.0;
+    opt.start_immediately_ = true;
+    opt.top_k_threshold_ = 50.0;
+    opt.top_k_change_threshold_ = 60.0;
+    opt.profile_type_ = kProfilerMethod;
+    opt.max_stack_depth_ = 1337u;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-filename:output.txt "
+                              "-Xprofile-period:123 "
+                              "-Xprofile-duration:456 "
+                              "-Xprofile-interval:789 "
+                              "-Xprofile-backoff:2.0 "
+                              "-Xprofile-start-immediately "
+                              "-Xprofile-top-k-threshold:50.0 "
+                              "-Xprofile-top-k-change-threshold:60.0 "
+                              "-Xprofile-type:method "
+                              "-Xprofile-max-stack-depth:1337",
+                              M::ProfilerOpts);
+  }
+
+  {
+    TestProfilerOptions opt = TestProfilerOptions();
+    opt.profile_type_ = kProfilerBoundedStack;
+
+    EXPECT_SINGLE_PARSE_VALUE(opt,
+                              "-Xprofile-type:stack",
+                              M::ProfilerOpts);
+  }
+}  // TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
+  RuntimeParser::Builder parserBuilder;
+
+  parserBuilder
+      .Define("-help")
+          .IntoKey(M::Help)
+      .IgnoreUnrecognized(true);
+
+  parser_.reset(new RuntimeParser(parserBuilder.Build()));
+
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option");
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS("-non-existent-option1 --non-existent-option-2");
+}  //  TEST_F
+
+TEST_F(CmdlineParserTest, TestIgnoredArguments) {
+  std::initializer_list<const char*> ignored_args = {
+      "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+      "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:abdef",
+      "-Xdexopt:foobar", "-Xnoquithandler", "-Xjnigreflimit:ixnay", "-Xgenregmap", "-Xnogenregmap",
+      "-Xverifyopt:never", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:noop",
+      "-Xincludeselectedmethod", "-Xjitthreshold:123", "-Xjitcodecachesize:12345",
+      "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:nosuchluck", "-Xjitoffset:none",
+      "-Xjitconfig:yes", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+      "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=1337"
+  };
+
+  // Check they are ignored when parsed one at a time
+  for (auto&& arg : ignored_args) {
+    SCOPED_TRACE(arg);
+    EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(arg);
+  }
+
+  // Check they are ignored when we pass it all together at once
+  std::vector<const char*> argv = ignored_args;
+  EXPECT_SINGLE_PARSE_EMPTY_SUCCESS(argv);
+}  //  TEST_F
+
+TEST_F(CmdlineParserTest, MultipleArguments) {
+  EXPECT_TRUE(IsResultSuccessful(parser_->Parse(
+      "-help -XX:ForegroundHeapGrowthMultiplier=0.5 "
+      "-Xnodex2oat -Xmethod-trace -XX:LargeObjectSpace=map")));
+
+  auto&& map = parser_->ReleaseArgumentsMap();
+  EXPECT_EQ(5u, map.Size());
+  EXPECT_KEY_VALUE(map, M::Help, Unit{});  // NOLINT [whitespace/braces] [5]
+  EXPECT_KEY_VALUE(map, M::ForegroundHeapGrowthMultiplier, 0.5);
+  EXPECT_KEY_VALUE(map, M::Dex2Oat, false);
+  EXPECT_KEY_VALUE(map, M::MethodTrace, Unit{});  // NOLINT [whitespace/braces] [5]
+  EXPECT_KEY_VALUE(map, M::LargeObjectSpace, gc::space::LargeObjectSpaceType::kMap);
+}  //  TEST_F
+}  // namespace art
diff --git a/cmdline/cmdline_result.h b/cmdline/cmdline_result.h
new file mode 100644
index 0000000..bf3a85d
--- /dev/null
+++ b/cmdline/cmdline_result.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_RESULT_H_
+#define ART_CMDLINE_CMDLINE_RESULT_H_
+
+#include <assert.h>
+#include <utils.h>
+
+namespace art {
+  // Result of an attempt to process the command line arguments. If fails, specifies
+  // the specific error code and an error message.
+  // Use the value-carrying CmdlineParseResult<T> to get an additional value out in a success case.
+  struct CmdlineResult {
+    enum Status {
+      kSuccess,
+      // Error codes:
+      kUsage,
+      kFailure,
+      kOutOfRange,
+      kUnknown,
+    };
+
+    // Short-hand for checking if the result was successful.
+    operator bool() const {
+      return IsSuccess();
+    }
+
+    // Check if the operation has succeeded.
+    bool IsSuccess() const { return status_ == kSuccess; }
+    // Check if the operation was not a success.
+    bool IsError() const { return status_ != kSuccess; }
+    // Get the specific status, regardless of whether it's failure or success.
+    Status GetStatus() const { return status_; }
+
+    // Get the error message, *must* only be called for error status results.
+    const std::string& GetMessage() const { assert(IsError()); return message_; }
+
+    // Constructor any status. No message.
+    explicit CmdlineResult(Status status) : status_(status) {}
+
+    // Constructor with an error status, copying the message.
+    CmdlineResult(Status status, const std::string& message)
+      : status_(status), message_(message) {
+      assert(status != kSuccess);
+    }
+
+    // Constructor with an error status, taking over the message.
+    CmdlineResult(Status status, std::string&& message)
+      : status_(status), message_(message) {
+      assert(status != kSuccess);
+    }
+
+    // Make sure copying exists
+    CmdlineResult(const CmdlineResult& other) = default;
+    // Make sure moving is cheap
+    CmdlineResult(CmdlineResult&& other) = default;
+
+  private:
+    const Status status_;
+    const std::string message_;
+  };
+
+  // TODO: code-generate this
+  static inline std::ostream& operator<<(std::ostream& stream, CmdlineResult::Status status) {
+    switch (status) {
+      case CmdlineResult::kSuccess:
+        stream << "kSuccess";
+        break;
+      case CmdlineResult::kUsage:
+        stream << "kUsage";
+        break;
+      case CmdlineResult::kFailure:
+        stream << "kFailure";
+        break;
+      case CmdlineResult::kOutOfRange:
+        stream << "kOutOfRange";
+        break;
+      case CmdlineResult::kUnknown:
+        stream << "kUnknown";
+        break;
+      default:
+        UNREACHABLE();
+    }
+    return stream;
+  }
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_RESULT_H_
diff --git a/cmdline/cmdline_type_parser.h b/cmdline/cmdline_type_parser.h
new file mode 100644
index 0000000..fa5cdaf
--- /dev/null
+++ b/cmdline/cmdline_type_parser.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
+#define ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
+
+#include "cmdline_parse_result.h"
+
+namespace art {
+
+// Base class for user-defined CmdlineType<T> specializations.
+//
+// Not strictly necessary, but if the specializations fail to Define all of these functions
+// the compilation will fail.
+template <typename T>
+struct CmdlineTypeParser {
+  // Return value of parsing attempts. Represents a Success(T value) or an Error(int code)
+  using Result = CmdlineParseResult<T>;
+
+  // Parse a single value for an argument definition out of the wildcard component.
+  //
+  // e.g. if the argument definition was "foo:_", and the user-provided input was "foo:bar",
+  // then args is "bar".
+  Result Parse(const std::string& args ATTRIBUTE_UNUSED) {
+    assert(false);
+    return Result::Failure("Missing type specialization and/or value map");
+  }
+
+  // Parse a value and append it into the existing value so far, for argument
+  // definitions which are marked with AppendValues().
+  //
+  // The value is parsed out of the wildcard component as in Parse.
+  //
+  // If the initial value does not exist yet, a default value is created by
+  // value-initializing with 'T()'.
+  Result ParseAndAppend(const std::string& args ATTRIBUTE_UNUSED,
+                        T& existing_value ATTRIBUTE_UNUSED) {
+    assert(false);
+    return Result::Failure("Missing type specialization and/or value map");
+  }
+
+  // Runtime type name of T, so that we can print more useful error messages.
+  static const char* Name() { assert(false); return "UnspecializedType"; }
+
+  // Whether or not your type can parse argument definitions defined without a "_"
+  // e.g. -Xenable-profiler just mutates the existing profiler struct in-place
+  // so it doesn't need to do any parsing other than token recognition.
+  //
+  // If this is false, then either the argument definition has a _, from which the parsing
+  // happens, or the tokens get mapped to a value list/map from which a 1:1 matching occurs.
+  //
+  // This should almost *always* be false!
+  static constexpr bool kCanParseBlankless = false;
+
+ protected:
+  // Don't accidentally initialize instances of this directly; they will assert at runtime.
+  CmdlineTypeParser() = default;
+};
+
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_CMDLINE_TYPE_PARSER_H_
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
new file mode 100644
index 0000000..9221023
--- /dev/null
+++ b/cmdline/cmdline_types.h
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ART_CMDLINE_CMDLINE_TYPES_H_
+#define ART_CMDLINE_CMDLINE_TYPES_H_
+
+#define CMDLINE_NDEBUG 1  // Do not output any debugging information for parsing.
+
+#include "cmdline/memory_representation.h"
+#include "cmdline/detail/cmdline_debug_detail.h"
+#include "cmdline_type_parser.h"
+
+// Includes for the types that are being specialized
+#include <string>
+#include "unit.h"
+#include "jdwp/jdwp.h"
+#include "runtime/base/logging.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+
+namespace art {
+
+// The default specialization will always fail parsing the type from a string.
+// Provide your own specialization that inherits from CmdlineTypeParser<T>
+// and implements either Parse or ParseAndAppend
+// (only if the argument was defined with ::AppendValues()) but not both.
+template <typename T>
+struct CmdlineType : CmdlineTypeParser<T> {
+};
+
+// Specializations for CmdlineType<T> follow:
+
+// Parse argument definitions for Unit-typed arguments.
+template <>
+struct CmdlineType<Unit> : CmdlineTypeParser<Unit> {
+  Result Parse(const std::string& args) {
+    if (args == "") {
+      return Result::Success(Unit{});  // NOLINT [whitespace/braces] [5]
+    }
+    return Result::Failure("Unexpected extra characters " + args);
+  }
+};
+
+template <>
+struct CmdlineType<JDWP::JdwpOptions> : CmdlineTypeParser<JDWP::JdwpOptions> {
+  Result Parse(const std::string& options) {
+    VLOG(jdwp) << "ParseJdwpOptions: " << options;
+
+    if (options == "help") {
+      return Result::Usage(
+          "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+          "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
+    }
+
+    const std::string s;
+
+    std::vector<std::string> pairs;
+    Split(options, ',', &pairs);
+
+    JDWP::JdwpOptions jdwp_options = JDWP::JdwpOptions();
+    std::stringstream error_stream;
+
+    for (size_t i = 0; i < pairs.size(); ++i) {
+      std::string::size_type equals = pairs[i].find('=');
+      if (equals == std::string::npos) {
+        return Result::Failure(s +
+            "Can't parse JDWP option '" + pairs[i] + "' in '" + options + "'");
+      }
+
+      if (!ParseJdwpOption(pairs[i].substr(0, equals),
+                           pairs[i].substr(equals + 1),
+                           error_stream,
+                           jdwp_options)) {
+        return Result::Failure(error_stream.str());
+      }
+    }
+
+    if (jdwp_options.transport == JDWP::kJdwpTransportUnknown) {
+      return Result::Failure(s + "Must specify JDWP transport: " + options);
+    }
+    if (!jdwp_options.server && (jdwp_options.host.empty() || jdwp_options.port == 0)) {
+      return Result::Failure(s + "Must specify JDWP host and port when server=n: " + options);
+    }
+
+    return Result::Success(std::move(jdwp_options));
+  }
+
+  bool ParseJdwpOption(const std::string& name, const std::string& value,
+                       std::ostream& error_stream,
+                       JDWP::JdwpOptions& jdwp_options) {
+    if (name == "transport") {
+      if (value == "dt_socket") {
+        jdwp_options.transport = JDWP::kJdwpTransportSocket;
+      } else if (value == "dt_android_adb") {
+        jdwp_options.transport = JDWP::kJdwpTransportAndroidAdb;
+      } else {
+        error_stream << "JDWP transport not supported: " << value;
+        return false;
+      }
+    } else if (name == "server") {
+      if (value == "n") {
+        jdwp_options.server = false;
+      } else if (value == "y") {
+        jdwp_options.server = true;
+      } else {
+        error_stream << "JDWP option 'server' must be 'y' or 'n'";
+        return false;
+      }
+    } else if (name == "suspend") {
+      if (value == "n") {
+        jdwp_options.suspend = false;
+      } else if (value == "y") {
+        jdwp_options.suspend = true;
+      } else {
+        error_stream << "JDWP option 'suspend' must be 'y' or 'n'";
+        return false;
+      }
+    } else if (name == "address") {
+      /* this is either <port> or <host>:<port> */
+      std::string port_string;
+      jdwp_options.host.clear();
+      std::string::size_type colon = value.find(':');
+      if (colon != std::string::npos) {
+        jdwp_options.host = value.substr(0, colon);
+        port_string = value.substr(colon + 1);
+      } else {
+        port_string = value;
+      }
+      if (port_string.empty()) {
+        error_stream << "JDWP address missing port: " << value;
+        return false;
+      }
+      char* end;
+      uint64_t port = strtoul(port_string.c_str(), &end, 10);
+      if (*end != '\0' || port > 0xffff) {
+        error_stream << "JDWP address has junk in port field: " << value;
+        return false;
+      }
+      jdwp_options.port = port;
+    } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+      /* valid but unsupported */
+      LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+    } else {
+      LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+    }
+
+    return true;
+  }
+
+  static const char* Name() { return "JdwpOptions"; }
+};
+
+template <size_t Divisor>
+struct CmdlineType<Memory<Divisor>> : CmdlineTypeParser<Memory<Divisor>> {
+  using typename CmdlineTypeParser<Memory<Divisor>>::Result;
+
+  Result Parse(const std::string arg) {
+    CMDLINE_DEBUG_LOG << "Parsing memory: " << arg << std::endl;
+    size_t val = ParseMemoryOption(arg.c_str(), Divisor);
+    CMDLINE_DEBUG_LOG << "Memory parsed to size_t value: " << val << std::endl;
+
+    if (val == 0) {
+      return Result::Failure(std::string("not a valid memory value, or not divisible by ")
+                             + std::to_string(Divisor));
+    }
+
+    return Result::Success(Memory<Divisor>(val));
+  }
+
+  // Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
+  // memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
+  // [gG] gigabytes.
+  //
+  // "s" should point just past the "-Xm?" part of the string.
+  // "div" specifies a divisor, e.g. 1024 if the value must be a multiple
+  // of 1024.
+  //
+  // The spec says the -Xmx and -Xms options must be multiples of 1024.  It
+  // doesn't say anything about -Xss.
+  //
+  // Returns 0 (a useless size) if "s" is malformed or specifies a low or
+  // non-evenly-divisible value.
+  //
+  static size_t ParseMemoryOption(const char* s, size_t div) {
+    // strtoul accepts a leading [+-], which we don't want,
+    // so make sure our string starts with a decimal digit.
+    if (isdigit(*s)) {
+      char* s2;
+      size_t val = strtoul(s, &s2, 10);
+      if (s2 != s) {
+        // s2 should be pointing just after the number.
+        // If this is the end of the string, the user
+        // has specified a number of bytes.  Otherwise,
+        // there should be exactly one more character
+        // that specifies a multiplier.
+        if (*s2 != '\0') {
+          // The remainder of the string is either a single multiplier
+          // character, or nothing to indicate that the value is in
+          // bytes.
+          char c = *s2++;
+          if (*s2 == '\0') {
+            size_t mul;
+            if (c == '\0') {
+              mul = 1;
+            } else if (c == 'k' || c == 'K') {
+              mul = KB;
+            } else if (c == 'm' || c == 'M') {
+              mul = MB;
+            } else if (c == 'g' || c == 'G') {
+              mul = GB;
+            } else {
+              // Unknown multiplier character.
+              return 0;
+            }
+
+            if (val <= std::numeric_limits<size_t>::max() / mul) {
+              val *= mul;
+            } else {
+              // Clamp to a multiple of 1024.
+              val = std::numeric_limits<size_t>::max() & ~(1024-1);
+            }
+          } else {
+            // There's more than one character after the numeric part.
+            return 0;
+          }
+        }
+        // The man page says that a -Xm value must be a multiple of 1024.
+        if (val % div == 0) {
+          return val;
+        }
+      }
+    }
+    return 0;
+  }
+
+  static const char* Name() { return Memory<Divisor>::Name(); }
+};
+
+template <>
+struct CmdlineType<double> : CmdlineTypeParser<double> {
+  Result Parse(const std::string& str) {
+    char* end = nullptr;
+    errno = 0;
+    double value = strtod(str.c_str(), &end);
+
+    if (*end != '\0') {
+      return Result::Failure("Failed to parse double from " + str);
+    }
+    if (errno == ERANGE) {
+      return Result::OutOfRange(
+          "Failed to parse double from " + str + "; overflow/underflow occurred");
+    }
+
+    return Result::Success(value);
+  }
+
+  static const char* Name() { return "double"; }
+};
+
+template <>
+struct CmdlineType<unsigned int> : CmdlineTypeParser<unsigned int> {
+  Result Parse(const std::string& str) {
+    const char* begin = str.c_str();
+    char* end;
+
+    // Parse into a larger type (long long) because we can't use strtoul
+    // since it silently converts negative values into unsigned long and doesn't set errno.
+    errno = 0;
+    long long int result = strtoll(begin, &end, 10);  // NOLINT [runtime/int] [4]
+    if (begin == end || *end != '\0' || errno == EINVAL) {
+      return Result::Failure("Failed to parse integer from " + str);
+    } else if ((errno == ERANGE) ||  // NOLINT [runtime/int] [4]
+        result < std::numeric_limits<int>::min()
+        || result > std::numeric_limits<unsigned int>::max() || result < 0) {
+      return Result::OutOfRange(
+          "Failed to parse integer from " + str + "; out of unsigned int range");
+    }
+
+    return Result::Success(static_cast<unsigned int>(result));
+  }
+
+  static const char* Name() { return "unsigned integer"; }
+};
+
+// Lightweight nanosecond value type. Allows parser to convert user-input from milliseconds
+// to nanoseconds automatically after parsing.
+//
+// All implicit conversion from uint64_t uses nanoseconds.
+struct MillisecondsToNanoseconds {
+  // Create from nanoseconds.
+  MillisecondsToNanoseconds(uint64_t nanoseconds) : nanoseconds_(nanoseconds) {  // NOLINT [runtime/explicit] [5]
+  }
+
+  // Create from milliseconds.
+  static MillisecondsToNanoseconds FromMilliseconds(unsigned int milliseconds) {
+    return MillisecondsToNanoseconds(MsToNs(milliseconds));
+  }
+
+  // Get the underlying nanoseconds value.
+  uint64_t GetNanoseconds() const {
+    return nanoseconds_;
+  }
+
+  // Get the milliseconds value [via a conversion]. Loss of precision will occur.
+  uint64_t GetMilliseconds() const {
+    return NsToMs(nanoseconds_);
+  }
+
+  // Get the underlying nanoseconds value.
+  operator uint64_t() const {
+    return GetNanoseconds();
+  }
+
+  // Default constructors/copy-constructors.
+  MillisecondsToNanoseconds() : nanoseconds_(0ul) {}
+  MillisecondsToNanoseconds(const MillisecondsToNanoseconds& rhs) = default;
+  MillisecondsToNanoseconds(MillisecondsToNanoseconds&& rhs) = default;
+
+ private:
+  uint64_t nanoseconds_;
+};
+
+template <>
+struct CmdlineType<MillisecondsToNanoseconds> : CmdlineTypeParser<MillisecondsToNanoseconds> {
+  Result Parse(const std::string& str) {
+    CmdlineType<unsigned int> uint_parser;
+    CmdlineParseResult<unsigned int> res = uint_parser.Parse(str);
+
+    if (res.IsSuccess()) {
+      return Result::Success(MillisecondsToNanoseconds::FromMilliseconds(res.GetValue()));
+    } else {
+      return Result::CastError(res);
+    }
+  }
+
+  static const char* Name() { return "MillisecondsToNanoseconds"; }
+};
+
+template <>
+struct CmdlineType<std::string> : CmdlineTypeParser<std::string> {
+  Result Parse(const std::string& args) {
+    return Result::Success(args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::string& existing_value) {
+    if (existing_value.empty()) {
+      existing_value = args;
+    } else {
+      existing_value += ' ';
+      existing_value += args;
+    }
+    return Result::SuccessNoValue();
+  }
+};
+
+template <>
+struct CmdlineType<std::vector<std::string>> : CmdlineTypeParser<std::vector<std::string>> {
+  Result Parse(const std::string& args) {
+    assert(false && "Use AppendValues() for a string vector type");
+    return Result::Failure("Unconditional failure: string vector must be appended: " + args);
+  }
+
+  Result ParseAndAppend(const std::string& args,
+                        std::vector<std::string>& existing_value) {
+    existing_value.push_back(args);
+    return Result::SuccessNoValue();
+  }
+
+  static const char* Name() { return "std::vector<std::string>"; }
+};
+
+template <char Separator>
+struct ParseStringList {
+  explicit ParseStringList(std::vector<std::string>&& list) : list_(list) {}
+
+  operator std::vector<std::string>() const {
+    return list_;
+  }
+
+  operator std::vector<std::string>&&() && {
+    return std::move(list_);
+  }
+
+  size_t Size() const {
+    return list_.size();
+  }
+
+  std::string Join() const {
+    return art::Join(list_, Separator);
+  }
+
+  static ParseStringList<Separator> Split(const std::string& str) {
+    std::vector<std::string> list;
+    art::Split(str, Separator, &list);
+    return ParseStringList<Separator>(std::move(list));
+  }
+
+  ParseStringList() = default;
+  ParseStringList(const ParseStringList& rhs) = default;
+  ParseStringList(ParseStringList&& rhs) = default;
+
+ private:
+  std::vector<std::string> list_;
+};
+
+template <char Separator>
+struct CmdlineType<ParseStringList<Separator>> : CmdlineTypeParser<ParseStringList<Separator>> {
+  using Result = CmdlineParseResult<ParseStringList<Separator>>;
+
+  Result Parse(const std::string& args) {
+    return Result::Success(ParseStringList<Separator>::Split(args));
+  }
+
+  static const char* Name() { return "ParseStringList<Separator>"; }
+};
+
+static gc::CollectorType ParseCollectorType(const std::string& option) {
+  if (option == "MS" || option == "nonconcurrent") {
+    return gc::kCollectorTypeMS;
+  } else if (option == "CMS" || option == "concurrent") {
+    return gc::kCollectorTypeCMS;
+  } else if (option == "SS") {
+    return gc::kCollectorTypeSS;
+  } else if (option == "GSS") {
+    return gc::kCollectorTypeGSS;
+  } else if (option == "CC") {
+    return gc::kCollectorTypeCC;
+  } else if (option == "MC") {
+    return gc::kCollectorTypeMC;
+  } else {
+    return gc::kCollectorTypeNone;
+  }
+}
+
+struct XGcOption {
+  // These defaults are used when the command line arguments for -Xgc:
+  // are either omitted completely or partially.
+  gc::CollectorType collector_type_ =  kUseReadBarrier ?
+                                           // If RB is enabled (currently a build-time decision),
+                                           // use CC as the default GC.
+                                           gc::kCollectorTypeCC :
+                                           gc::kCollectorTypeDefault;
+  bool verify_pre_gc_heap_ = false;
+  bool verify_pre_sweeping_heap_ = kIsDebugBuild;
+  bool verify_post_gc_heap_ = false;
+  bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
+  bool verify_pre_sweeping_rosalloc_ = false;
+  bool verify_post_gc_rosalloc_ = false;
+};
+
+template <>
+struct CmdlineType<XGcOption> : CmdlineTypeParser<XGcOption> {
+  Result Parse(const std::string& option) {  // -Xgc: already stripped
+    XGcOption xgc{};  // NOLINT [readability/braces] [4]
+
+    std::vector<std::string> gc_options;
+    Split(option, ',', &gc_options);
+    for (const std::string& gc_option : gc_options) {
+      gc::CollectorType collector_type = ParseCollectorType(gc_option);
+      if (collector_type != gc::kCollectorTypeNone) {
+        xgc.collector_type_ = collector_type;
+      } else if (gc_option == "preverify") {
+        xgc.verify_pre_gc_heap_ = true;
+      } else if (gc_option == "nopreverify") {
+        xgc.verify_pre_gc_heap_ = false;
+      }  else if (gc_option == "presweepingverify") {
+        xgc.verify_pre_sweeping_heap_ = true;
+      } else if (gc_option == "nopresweepingverify") {
+        xgc.verify_pre_sweeping_heap_ = false;
+      } else if (gc_option == "postverify") {
+        xgc.verify_post_gc_heap_ = true;
+      } else if (gc_option == "nopostverify") {
+        xgc.verify_post_gc_heap_ = false;
+      } else if (gc_option == "preverify_rosalloc") {
+        xgc.verify_pre_gc_rosalloc_ = true;
+      } else if (gc_option == "nopreverify_rosalloc") {
+        xgc.verify_pre_gc_rosalloc_ = false;
+      } else if (gc_option == "presweepingverify_rosalloc") {
+        xgc.verify_pre_sweeping_rosalloc_ = true;
+      } else if (gc_option == "nopresweepingverify_rosalloc") {
+        xgc.verify_pre_sweeping_rosalloc_ = false;
+      } else if (gc_option == "postverify_rosalloc") {
+        xgc.verify_post_gc_rosalloc_ = true;
+      } else if (gc_option == "nopostverify_rosalloc") {
+        xgc.verify_post_gc_rosalloc_ = false;
+      } else if ((gc_option == "precise") ||
+                 (gc_option == "noprecise") ||
+                 (gc_option == "verifycardtable") ||
+                 (gc_option == "noverifycardtable")) {
+        // Ignored for backwards compatibility.
+      } else {
+        return Result::Usage(std::string("Unknown -Xgc option ") + gc_option);
+      }
+    }
+
+    return Result::Success(std::move(xgc));
+  }
+
+  static const char* Name() { return "XgcOption"; }
+};
+
+struct BackgroundGcOption {
+  // If background_collector_type_ is kCollectorTypeNone, it defaults to the
+  // XGcOption::collector_type_ after parsing options. If you set this to
+  // kCollectorTypeHSpaceCompact then we will do an hspace compaction when
+  // we transition to background instead of a normal collector transition.
+  gc::CollectorType background_collector_type_;
+
+  BackgroundGcOption(gc::CollectorType background_collector_type)  // NOLINT [runtime/explicit] [5]
+    : background_collector_type_(background_collector_type) {}
+  BackgroundGcOption()
+    : background_collector_type_(gc::kCollectorTypeNone) {
+
+    if (kUseReadBarrier) {
+      background_collector_type_ = gc::kCollectorTypeCC;  // Disable background compaction for CC.
+    }
+  }
+
+  operator gc::CollectorType() const { return background_collector_type_; }
+};
+
+template<>
+struct CmdlineType<BackgroundGcOption>
+  : CmdlineTypeParser<BackgroundGcOption>, private BackgroundGcOption {
+  Result Parse(const std::string& substring) {
+    // Special handling for HSpaceCompact since this is only valid as a background GC type.
+    if (substring == "HSpaceCompact") {
+      background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
+    } else {
+      gc::CollectorType collector_type = ParseCollectorType(substring);
+      if (collector_type != gc::kCollectorTypeNone) {
+        background_collector_type_ = collector_type;
+      } else {
+        return Result::Failure();
+      }
+    }
+
+    BackgroundGcOption res = *this;
+    return Result::Success(res);
+  }
+
+  static const char* Name() { return "BackgroundGcOption"; }
+};
+
+template <>
+struct CmdlineType<LogVerbosity> : CmdlineTypeParser<LogVerbosity> {
+  Result Parse(const std::string& options) {
+    LogVerbosity log_verbosity = LogVerbosity();
+
+    std::vector<std::string> verbose_options;
+    Split(options, ',', &verbose_options);
+    for (size_t j = 0; j < verbose_options.size(); ++j) {
+      if (verbose_options[j] == "class") {
+        log_verbosity.class_linker = true;
+      } else if (verbose_options[j] == "compiler") {
+        log_verbosity.compiler = true;
+      } else if (verbose_options[j] == "gc") {
+        log_verbosity.gc = true;
+      } else if (verbose_options[j] == "heap") {
+        log_verbosity.heap = true;
+      } else if (verbose_options[j] == "jdwp") {
+        log_verbosity.jdwp = true;
+      } else if (verbose_options[j] == "jni") {
+        log_verbosity.jni = true;
+      } else if (verbose_options[j] == "monitor") {
+        log_verbosity.monitor = true;
+      } else if (verbose_options[j] == "profiler") {
+        log_verbosity.profiler = true;
+      } else if (verbose_options[j] == "signals") {
+        log_verbosity.signals = true;
+      } else if (verbose_options[j] == "startup") {
+        log_verbosity.startup = true;
+      } else if (verbose_options[j] == "third-party-jni") {
+        log_verbosity.third_party_jni = true;
+      } else if (verbose_options[j] == "threads") {
+        log_verbosity.threads = true;
+      } else if (verbose_options[j] == "verifier") {
+        log_verbosity.verifier = true;
+      } else {
+        return Result::Usage(std::string("Unknown -verbose option ") + verbose_options[j]);
+      }
+    }
+
+    return Result::Success(log_verbosity);
+  }
+
+  static const char* Name() { return "LogVerbosity"; }
+};
+
+// TODO: Replace with art::ProfilerOptions for the real thing.
+struct TestProfilerOptions {
+  // Whether or not the applications should be profiled.
+  bool enabled_;
+  // Destination file name where the profiling data will be saved into.
+  std::string output_file_name_;
+  // Generate profile every n seconds.
+  uint32_t period_s_;
+  // Run profile for n seconds.
+  uint32_t duration_s_;
+  // Microseconds between samples.
+  uint32_t interval_us_;
+  // Coefficient to exponential backoff.
+  double backoff_coefficient_;
+  // Whether the profile should start upon app startup or be delayed by some random offset.
+  bool start_immediately_;
+  // Top K% of samples that are considered relevant when deciding if the app should be recompiled.
+  double top_k_threshold_;
+  // How much the top K% samples needs to change in order for the app to be recompiled.
+  double top_k_change_threshold_;
+  // The type of profile data dumped to the disk.
+  ProfileDataType profile_type_;
+  // The max depth of the stack collected by the profiler
+  uint32_t max_stack_depth_;
+
+  TestProfilerOptions() :
+    enabled_(false),
+    output_file_name_(),
+    period_s_(0),
+    duration_s_(0),
+    interval_us_(0),
+    backoff_coefficient_(0),
+    start_immediately_(0),
+    top_k_threshold_(0),
+    top_k_change_threshold_(0),
+    profile_type_(ProfileDataType::kProfilerMethod),
+    max_stack_depth_(0) {
+  }
+
+  TestProfilerOptions(const TestProfilerOptions& other) = default;
+  TestProfilerOptions(TestProfilerOptions&& other) = default;
+};
+
+static inline std::ostream& operator<<(std::ostream& stream, const TestProfilerOptions& options) {
+  stream << "TestProfilerOptions {" << std::endl;
+
+#define PRINT_TO_STREAM(field) \
+  stream << #field << ": '" << options.field << "'" << std::endl;
+
+  PRINT_TO_STREAM(enabled_);
+  PRINT_TO_STREAM(output_file_name_);
+  PRINT_TO_STREAM(period_s_);
+  PRINT_TO_STREAM(duration_s_);
+  PRINT_TO_STREAM(interval_us_);
+  PRINT_TO_STREAM(backoff_coefficient_);
+  PRINT_TO_STREAM(start_immediately_);
+  PRINT_TO_STREAM(top_k_threshold_);
+  PRINT_TO_STREAM(top_k_change_threshold_);
+  PRINT_TO_STREAM(profile_type_);
+  PRINT_TO_STREAM(max_stack_depth_);
+
+  stream << "}";
+
+  return stream;
+#undef PRINT_TO_STREAM
+}
+
+template <>
+struct CmdlineType<TestProfilerOptions> : CmdlineTypeParser<TestProfilerOptions> {
+  using Result = CmdlineParseResult<TestProfilerOptions>;
+
+ private:
+  using StringResult = CmdlineParseResult<std::string>;
+  using DoubleResult = CmdlineParseResult<double>;
+
+  template <typename T>
+  static Result ParseInto(TestProfilerOptions& options,
+                          T TestProfilerOptions::*pField,
+                          CmdlineParseResult<T>&& result) {
+    assert(pField != nullptr);
+
+    if (result.IsSuccess()) {
+      options.*pField = result.ReleaseValue();
+      return Result::SuccessNoValue();
+    }
+
+    return Result::CastError(result);
+  }
+
+  template <typename T>
+  static Result ParseIntoRangeCheck(TestProfilerOptions& options,
+                                    T TestProfilerOptions::*pField,
+                                    CmdlineParseResult<T>&& result,
+                                    T min,
+                                    T max) {
+    if (result.IsSuccess()) {
+      const T& value = result.GetValue();
+
+      if (value < min || value > max) {
+        CmdlineParseResult<T> out_of_range = CmdlineParseResult<T>::OutOfRange(value, min, max);
+        return Result::CastError(out_of_range);
+      }
+    }
+
+    return ParseInto(options, pField, std::forward<CmdlineParseResult<T>>(result));
+  }
+
+  static StringResult ParseStringAfterChar(const std::string& s, char c) {
+    std::string parsed_value;
+
+    std::string::size_type colon = s.find(c);
+    if (colon == std::string::npos) {
+      return StringResult::Usage(std::string() + "Missing char " + c + " in option " + s);
+    }
+    // Add one to remove the char we were trimming until.
+    parsed_value = s.substr(colon + 1);
+    return StringResult::Success(parsed_value);
+  }
+
+  static std::string RemovePrefix(const std::string& source) {
+    size_t prefix_idx = source.find(":");
+
+    if (prefix_idx == std::string::npos) {
+      return "";
+    }
+
+    return source.substr(prefix_idx + 1);
+  }
+
+ public:
+  Result ParseAndAppend(const std::string& option, TestProfilerOptions& existing) {
+    // Special case which doesn't include a wildcard argument definition.
+    // We pass-it through as-is.
+    if (option == "-Xenable-profiler") {
+      existing.enabled_ = true;
+      return Result::SuccessNoValue();
+    }
+
+    // The rest of these options are always the wildcard from '-Xprofile-*'
+    std::string suffix = RemovePrefix(option);
+
+    if (StartsWith(option, "filename:")) {
+      CmdlineType<std::string> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::output_file_name_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "period:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::period_s_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "duration:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::duration_s_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "interval:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::interval_us_,
+                       type_parser.Parse(suffix));
+    } else if (StartsWith(option, "backoff:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::backoff_coefficient_,
+                                 type_parser.Parse(suffix),
+                                 1.0,
+                                 10.0);
+
+    } else if (option == "start-immediately") {
+      existing.start_immediately_ = true;
+      return Result::SuccessNoValue();
+    } else if (StartsWith(option, "top-k-threshold:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::top_k_threshold_,
+                                 type_parser.Parse(suffix),
+                                 0.0,
+                                 100.0);
+    } else if (StartsWith(option, "top-k-change-threshold:")) {
+      CmdlineType<double> type_parser;
+
+      return ParseIntoRangeCheck(existing,
+                                 &TestProfilerOptions::top_k_change_threshold_,
+                                 type_parser.Parse(suffix),
+                                 0.0,
+                                 100.0);
+    } else if (option == "type:method") {
+      existing.profile_type_ = kProfilerMethod;
+      return Result::SuccessNoValue();
+    } else if (option == "type:stack") {
+      existing.profile_type_ = kProfilerBoundedStack;
+      return Result::SuccessNoValue();
+    } else if (StartsWith(option, "max-stack-depth:")) {
+      CmdlineType<unsigned int> type_parser;
+
+      return ParseInto(existing,
+                       &TestProfilerOptions::max_stack_depth_,
+                       type_parser.Parse(suffix));
+    } else {
+      return Result::Failure(std::string("Invalid suboption '") + option + "'");
+    }
+  }
+
+  static const char* Name() { return "TestProfilerOptions"; }
+  static constexpr bool kCanParseBlankless = true;
+};
+
+
+}  // namespace art
+#endif  // ART_CMDLINE_CMDLINE_TYPES_H_
diff --git a/cmdline/detail/cmdline_debug_detail.h b/cmdline/detail/cmdline_debug_detail.h
new file mode 100644
index 0000000..79028f4
--- /dev/null
+++ b/cmdline/detail/cmdline_debug_detail.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
+
+#include <iostream>
+#ifndef CMDLINE_NDEBUG
+#define CMDLINE_DEBUG_LOG std::cerr
+#else
+#define CMDLINE_DEBUG_LOG ::art::detail::debug_log_ignore()
+#endif
+
+namespace art {
+  // Implementation details for some template querying. Don't look inside if you hate templates.
+  namespace detail {
+    struct debug_log_ignore {
+      // Ignore most of the normal operator<< usage.
+      template <typename T>
+      debug_log_ignore& operator<<(const T&) { return *this; }
+      // Ignore std::endl and the like.
+      debug_log_ignore& operator<<(std::ostream& (*)(std::ostream&) ) { return *this; }
+    };
+  }  // namespace detail  // NOLINT [readability/namespace] [5]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_DEBUG_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
new file mode 100644
index 0000000..81ef36b
--- /dev/null
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
+
+#include <type_traits>
+#include <assert.h>
+#include <functional>
+#include <vector>
+#include <algorithm>
+#include <numeric>
+#include <memory>
+
+#include "cmdline/cmdline_parse_result.h"
+#include "cmdline/token_range.h"
+#include "cmdline/unit.h"
+#include "cmdline/cmdline_types.h"
+
+namespace art {
+  // Implementation details for the parser. Do not look inside if you hate templates.
+  namespace detail {
+    // A non-templated base class for argument parsers. Used by the general parser
+    // to parse arguments, without needing to know the argument type at compile time.
+    //
+    // This is an application of the type erasure idiom.
+    struct CmdlineParseArgumentAny {
+      virtual ~CmdlineParseArgumentAny() {}
+
+      // Attempt to parse this argument starting at arguments[position].
+      // If the parsing succeeds, the parsed value will be saved as a side-effect.
+      //
+      // In most situations, the parsing will not match by returning kUnknown. In this case,
+      // no tokens were consumed and the position variable will not be updated.
+      //
+      // At other times, parsing may fail due to validation but the initial token was still matched
+      // (for example an out of range value, or passing in a string where an int was expected).
+      // In this case the tokens are still consumed, and the position variable will get incremented
+      // by all the consumed tokens.
+      //
+      // The # of tokens consumed by the parse attempt will be set as an out-parameter into
+      // consumed_tokens. The parser should skip this many tokens before parsing the next
+      // argument.
+      virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) = 0;
+      // How many tokens should be taken off argv for parsing this argument.
+      // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+      //
+      // A [min,max] range is returned to represent argument definitions with multiple
+      // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+      virtual std::pair<size_t, size_t> GetNumTokens() const = 0;
+      // Get the run-time typename of the argument type.
+      virtual const char* GetTypeName() const = 0;
+      // Try to do a close match, returning how many tokens were matched against this argument
+      // definition. More tokens is better.
+      //
+      // Do a quick match token-by-token, and see if they match.
+      // Any tokens with a wildcard in them are only matched up until the wildcard.
+      // If this is true, then the wildcard matching later on can still fail, so this is not
+      // a guarantee that the argument is correct, it's more of a strong hint that the
+      // user-provided input *probably* was trying to match this argument.
+      //
+      // Returns how many tokens were either matched (or ignored because there was a
+      // wildcard present). 0 means no match. If the Size() tokens are returned.
+      virtual size_t MaybeMatches(const TokenRange& tokens) = 0;
+    };
+
+    template <typename T>
+    using EnableIfNumeric = std::enable_if<std::is_arithmetic<T>::value>;
+
+    template <typename T>
+    using DisableIfNumeric = std::enable_if<!std::is_arithmetic<T>::value>;
+
+    // Argument definition information, created by an ArgumentBuilder and an UntypedArgumentBuilder.
+    template <typename TArg>
+    struct CmdlineParserArgumentInfo {
+      // This version will only be used if TArg is arithmetic and thus has the <= operators.
+      template <typename T = TArg>  // Necessary to get SFINAE to kick in.
+      bool CheckRange(const TArg& value, typename EnableIfNumeric<T>::type* = 0) {
+        if (has_range_) {
+          return min_ <= value && value <= max_;
+        }
+        return true;
+      }
+
+      // This version will be used at other times when TArg is not arithmetic.
+      template <typename T = TArg>
+      bool CheckRange(const TArg&, typename DisableIfNumeric<T>::type* = 0) {
+        assert(!has_range_);
+        return true;
+      }
+
+      // Do a quick match token-by-token, and see if they match.
+      // Any tokens with a wildcard in them only match the prefix up until the wildcard.
+      //
+      // If this is true, then the wildcard matching later on can still fail, so this is not
+      // a guarantee that the argument is correct, it's more of a strong hint that the
+      // user-provided input *probably* was trying to match this argument.
+      size_t MaybeMatches(TokenRange token_list) const {
+        auto best_match = FindClosestMatch(token_list);
+
+        return best_match.second;
+      }
+
+      // Attempt to find the closest match (see MaybeMatches).
+      //
+      // Returns the token range that was the closest match and the # of tokens that
+      // this range was matched up until.
+      std::pair<const TokenRange*, size_t> FindClosestMatch(TokenRange token_list) const {
+        const TokenRange* best_match_ptr = nullptr;
+
+        size_t best_match = 0;
+        for (auto&& token_range : tokenized_names_) {
+          size_t this_match = token_range.MaybeMatches(token_list, std::string("_"));
+
+          if (this_match > best_match) {
+            best_match_ptr = &token_range;
+            best_match = this_match;
+          }
+        }
+
+        return std::make_pair(best_match_ptr, best_match);
+      }
+
+      // Mark the argument definition as completed, do not mutate the object anymore after this
+      // call is done.
+      //
+      // Performs several sanity checks and token calculations.
+      void CompleteArgument() {
+        assert(names_.size() >= 1);
+        assert(!is_completed_);
+
+        is_completed_ = true;
+
+        size_t blank_count = 0;
+        size_t token_count = 0;
+
+        size_t global_blank_count = 0;
+        size_t global_token_count = 0;
+        for (auto&& name : names_) {
+          std::string s(name);
+
+          size_t local_blank_count = std::count(s.begin(), s.end(), '_');
+          size_t local_token_count = std::count(s.begin(), s.end(), ' ');
+
+          if (global_blank_count != 0) {
+            assert(local_blank_count == global_blank_count
+                   && "Every argument descriptor string must have same amount of blanks (_)");
+          }
+
+          if (local_blank_count != 0) {
+            global_blank_count = local_blank_count;
+            blank_count++;
+
+            assert(local_blank_count == 1 && "More than one blank is not supported");
+            assert(s.back() == '_' && "The blank character must only be at the end of the string");
+          }
+
+          if (global_token_count != 0) {
+            assert(local_token_count == global_token_count
+                   && "Every argument descriptor string must have same amount of tokens (spaces)");
+          }
+
+          if (local_token_count != 0) {
+            global_token_count = local_token_count;
+            token_count++;
+          }
+
+          // Tokenize every name, turning it from a string to a token list.
+          tokenized_names_.clear();
+          for (auto&& name1 : names_) {
+            // Split along ' ' only, removing any duplicated spaces.
+            tokenized_names_.push_back(
+                TokenRange::Split(name1, {' '}).RemoveToken(" "));
+          }
+
+          // remove the _ character from each of the token ranges
+          // we will often end up with an empty token (i.e. ["-XX", "_"] -> ["-XX", ""]
+          // and this is OK because we still need an empty token to simplify
+          // range comparisons
+          simple_names_.clear();
+
+          for (auto&& tokenized_name : tokenized_names_) {
+            simple_names_.push_back(tokenized_name.RemoveCharacter('_'));
+          }
+        }
+
+        if (token_count != 0) {
+          assert(("Every argument descriptor string must have equal amount of tokens (spaces)" &&
+              token_count == names_.size()));
+        }
+
+        if (blank_count != 0) {
+          assert(("Every argument descriptor string must have an equal amount of blanks (_)" &&
+              blank_count == names_.size()));
+        }
+
+        using_blanks_ = blank_count > 0;
+        {
+          size_t smallest_name_token_range_size =
+              std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), ~(0u),
+                              [](size_t min, const TokenRange& cur) {
+                                return std::min(min, cur.Size());
+                              });
+          size_t largest_name_token_range_size =
+              std::accumulate(tokenized_names_.begin(), tokenized_names_.end(), 0u,
+                              [](size_t max, const TokenRange& cur) {
+                                return std::max(max, cur.Size());
+                              });
+
+          token_range_size_ = std::make_pair(smallest_name_token_range_size,
+                                             largest_name_token_range_size);
+        }
+
+        if (has_value_list_) {
+          assert(names_.size() == value_list_.size()
+                 && "Number of arg descriptors must match number of values");
+          assert(!has_value_map_);
+        }
+        if (has_value_map_) {
+          if (!using_blanks_) {
+            assert(names_.size() == value_map_.size() &&
+                   "Since no blanks were specified, each arg is mapped directly into a mapped "
+                   "value without parsing; sizes must match");
+          }
+
+          assert(!has_value_list_);
+        }
+
+        if (!using_blanks_ && !CmdlineType<TArg>::kCanParseBlankless) {
+          assert((has_value_map_ || has_value_list_) &&
+                 "Arguments without a blank (_) must provide either a value map or a value list");
+        }
+
+        TypedCheck();
+      }
+
+      // List of aliases for a single argument definition, e.g. {"-Xdex2oat", "-Xnodex2oat"}.
+      std::vector<const char*> names_;
+      // Is there at least 1 wildcard '_' in the argument definition?
+      bool using_blanks_ = false;
+      // [min, max] token counts in each arg def
+      std::pair<size_t, size_t> token_range_size_;
+
+      // contains all the names in a tokenized form, i.e. as a space-delimited list
+      std::vector<TokenRange> tokenized_names_;
+
+      // contains the tokenized names, but with the _ character stripped
+      std::vector<TokenRange> simple_names_;
+
+      // For argument definitions created with '.AppendValues()'
+      // Meaning that parsing should mutate the existing value in-place if possible.
+      bool appending_values_ = false;
+
+      // For argument definitions created with '.WithRange(min, max)'
+      bool has_range_ = false;
+      TArg min_;
+      TArg max_;
+
+      // For argument definitions created with '.WithValueMap'
+      bool has_value_map_ = false;
+      std::vector<std::pair<const char*, TArg>> value_map_;
+
+      // For argument definitions created with '.WithValues'
+      bool has_value_list_ = false;
+      std::vector<TArg> value_list_;
+
+      // Make sure there's a default constructor.
+      CmdlineParserArgumentInfo() = default;
+
+      // Ensure there's a default move constructor.
+      CmdlineParserArgumentInfo(CmdlineParserArgumentInfo&&) = default;
+
+     private:
+      // Perform type-specific checks at runtime.
+      template <typename T = TArg>
+      void TypedCheck(typename std::enable_if<std::is_same<Unit, T>::value>::type* = 0) {
+        assert(!using_blanks_ &&
+               "Blanks are not supported in Unit arguments; since a Unit has no parse-able value");
+      }
+
+      void TypedCheck() {}
+
+      bool is_completed_ = false;
+    };
+
+    // A virtual-implementation of the necessary argument information in order to
+    // be able to parse arguments.
+    template <typename TArg>
+    struct CmdlineParseArgument : CmdlineParseArgumentAny {
+      explicit CmdlineParseArgument(CmdlineParserArgumentInfo<TArg>&& argument_info,
+                                    std::function<void(TArg&)>&& save_argument,
+                                    std::function<TArg&(void)>&& load_argument)
+          : argument_info_(std::forward<decltype(argument_info)>(argument_info)),
+            save_argument_(std::forward<decltype(save_argument)>(save_argument)),
+            load_argument_(std::forward<decltype(load_argument)>(load_argument)) {
+      }
+
+      using UserTypeInfo = CmdlineType<TArg>;
+
+      virtual CmdlineResult ParseArgument(const TokenRange& arguments, size_t* consumed_tokens) {
+        assert(arguments.Size() > 0);
+        assert(consumed_tokens != nullptr);
+
+        auto closest_match_res = argument_info_.FindClosestMatch(arguments);
+        size_t best_match_size = closest_match_res.second;
+        const TokenRange* best_match_arg_def = closest_match_res.first;
+
+        if (best_match_size > arguments.Size()) {
+          // The best match has more tokens than were provided.
+          // Shouldn't happen in practice since the outer parser does this check.
+          return CmdlineResult(CmdlineResult::kUnknown, "Size mismatch");
+        }
+
+        assert(best_match_arg_def != nullptr);
+        *consumed_tokens = best_match_arg_def->Size();
+
+        if (!argument_info_.using_blanks_) {
+          return ParseArgumentSingle(arguments.Join(' '));
+        }
+
+        // Extract out the blank value from arguments
+        // e.g. for a def of "foo:_" and input "foo:bar", blank_value == "bar"
+        std::string blank_value = "";
+        size_t idx = 0;
+        for (auto&& def_token : *best_match_arg_def) {
+          auto&& arg_token = arguments[idx];
+
+          // Does this definition-token have a wildcard in it?
+          if (def_token.find('_') == std::string::npos) {
+            // No, regular token. Match 1:1 against the argument token.
+            bool token_match = def_token == arg_token;
+
+            if (!token_match) {
+              return CmdlineResult(CmdlineResult::kFailure,
+                                   std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+                                   + " at token " + std::to_string(idx));
+            }
+          } else {
+            // This is a wild-carded token.
+            TokenRange def_split_wildcards = TokenRange::Split(def_token, {'_'});
+
+            // Extract the wildcard contents out of the user-provided arg_token.
+            std::unique_ptr<TokenRange> arg_matches =
+                def_split_wildcards.MatchSubstrings(arg_token, "_");
+            if (arg_matches == nullptr) {
+              return CmdlineResult(CmdlineResult::kFailure,
+                                   std::string("Failed to parse ") + best_match_arg_def->GetToken(0)
+                                   + ", with a wildcard pattern " + def_token
+                                   + " at token " + std::to_string(idx));
+            }
+
+            // Get the corresponding wildcard tokens from arg_matches,
+            // and concatenate it to blank_value.
+            for (size_t sub_idx = 0;
+                sub_idx < def_split_wildcards.Size() && sub_idx < arg_matches->Size(); ++sub_idx) {
+              if (def_split_wildcards[sub_idx] == "_") {
+                blank_value += arg_matches->GetToken(sub_idx);
+              }
+            }
+          }
+
+          ++idx;
+        }
+
+        return ParseArgumentSingle(blank_value);
+      }
+
+     private:
+      virtual CmdlineResult ParseArgumentSingle(const std::string& argument) {
+        // TODO: refactor to use LookupValue for the value lists/maps
+
+        // Handle the 'WithValueMap(...)' argument definition
+        if (argument_info_.has_value_map_) {
+          for (auto&& value_pair : argument_info_.value_map_) {
+            const char* name = value_pair.first;
+
+            if (argument == name) {
+              return SaveArgument(value_pair.second);
+            }
+          }
+
+          // Error case: Fail, telling the user what the allowed values were.
+          std::vector<std::string> allowed_values;
+          for (auto&& value_pair : argument_info_.value_map_) {
+            const char* name = value_pair.first;
+            allowed_values.push_back(name);
+          }
+
+          std::string allowed_values_flat = Join(allowed_values, ',');
+          return CmdlineResult(CmdlineResult::kFailure,
+                               "Argument value '" + argument + "' does not match any of known valid"
+                                "values: {" + allowed_values_flat + "}");
+        }
+
+        // Handle the 'WithValues(...)' argument definition
+        if (argument_info_.has_value_list_) {
+          size_t arg_def_idx = 0;
+          for (auto&& value : argument_info_.value_list_) {
+            auto&& arg_def_token = argument_info_.names_[arg_def_idx];
+
+            if (arg_def_token == argument) {
+              return SaveArgument(value);
+            }
+            ++arg_def_idx;
+          }
+
+          assert(arg_def_idx + 1 == argument_info_.value_list_.size() &&
+                 "Number of named argument definitions must match number of values defined");
+
+          // Error case: Fail, telling the user what the allowed values were.
+          std::vector<std::string> allowed_values;
+          for (auto&& arg_name : argument_info_.names_) {
+            allowed_values.push_back(arg_name);
+          }
+
+          std::string allowed_values_flat = Join(allowed_values, ',');
+          return CmdlineResult(CmdlineResult::kFailure,
+                               "Argument value '" + argument + "' does not match any of known valid"
+                                "values: {" + allowed_values_flat + "}");
+        }
+
+        // Handle the regular case where we parsed an unknown value from a blank.
+        UserTypeInfo type_parser;
+
+        if (argument_info_.appending_values_) {
+          TArg& existing = load_argument_();
+          CmdlineParseResult<TArg> result = type_parser.ParseAndAppend(argument, existing);
+
+          assert(!argument_info_.has_range_);
+
+          return result;
+        }
+
+        CmdlineParseResult<TArg> result = type_parser.Parse(argument);
+
+        if (result.IsSuccess()) {
+          TArg& value = result.GetValue();
+
+          // Do a range check for 'WithRange(min,max)' argument definition.
+          if (!argument_info_.CheckRange(value)) {
+            return CmdlineParseResult<TArg>::OutOfRange(
+                value, argument_info_.min_, argument_info_.max_);
+          }
+
+          return SaveArgument(value);
+        }
+
+        // Some kind of type-specific parse error. Pass the result as-is.
+        CmdlineResult raw_result = std::move(result);
+        return raw_result;
+      }
+
+     public:
+      virtual const char* GetTypeName() const {
+        // TODO: Obviate the need for each type specialization to hardcode the type name
+        return UserTypeInfo::Name();
+      }
+
+      // How many tokens should be taken off argv for parsing this argument.
+      // For example "--help" is just 1, "-compiler-option _" would be 2 (since there's a space).
+      //
+      // A [min,max] range is returned to represent argument definitions with multiple
+      // value tokens. (e.g. {"-h", "-h " } would return [1,2]).
+      virtual std::pair<size_t, size_t> GetNumTokens() const {
+        return argument_info_.token_range_size_;
+      }
+
+      // See if this token range might begin the same as the argument definition.
+      virtual size_t MaybeMatches(const TokenRange& tokens) {
+        return argument_info_.MaybeMatches(tokens);
+      }
+
+     private:
+      CmdlineResult SaveArgument(const TArg& value) {
+        assert(!argument_info_.appending_values_
+               && "If the values are being appended, then the updated parse value is "
+                   "updated by-ref as a side effect and shouldn't be stored directly");
+        TArg val = value;
+        save_argument_(val);
+        return CmdlineResult(CmdlineResult::kSuccess);
+      }
+
+      CmdlineParserArgumentInfo<TArg> argument_info_;
+      std::function<void(TArg&)> save_argument_;
+      std::function<TArg&(void)> load_argument_;
+    };
+  } // namespace detail // NOLINT [readability/namespace] [5] [whitespace/comments] [2]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_PARSE_ARGUMENT_DETAIL_H_
diff --git a/cmdline/detail/cmdline_parser_detail.h b/cmdline/detail/cmdline_parser_detail.h
new file mode 100644
index 0000000..9b43bb0
--- /dev/null
+++ b/cmdline/detail/cmdline_parser_detail.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+#define ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
+
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace art {
+  // Implementation details for some template querying. Don't look inside if you hate templates.
+  namespace detail {
+    template <typename T>
+    typename std::remove_reference<T>::type& FakeReference();
+
+    // SupportsInsertionOperator<T, TStream>::value will evaluate to a boolean,
+    // whose value is true if the TStream class supports the << operator against T,
+    // and false otherwise.
+    template <typename T2, typename TStream2 = std::ostream>
+    struct SupportsInsertionOperator {
+     private:
+      template <typename TStream, typename T>
+      static std::true_type InsertionOperatorTest(TStream& os, const T& value,
+                                                  std::remove_reference<decltype(os << value)>* = 0); // NOLINT [whitespace/operators] [3]
+
+      template <typename TStream, typename ... T>
+      static std::false_type InsertionOperatorTest(TStream& os, const T& ... args);
+
+     public:
+      static constexpr bool value =
+          decltype(InsertionOperatorTest(FakeReference<TStream2>(), std::declval<T2>()))::value;
+    };
+
+    template <typename TLeft, typename TRight = TLeft, bool IsFloatingPoint = false>
+    struct SupportsEqualityOperatorImpl;
+
+    template <typename TLeft, typename TRight>
+    struct SupportsEqualityOperatorImpl<TLeft, TRight, false> {
+     private:
+      template <typename TL, typename TR>
+      static std::true_type EqualityOperatorTest(const TL& left, const TR& right,
+                                                 std::remove_reference<decltype(left == right)>* = 0); // NOLINT [whitespace/operators] [3]
+
+      template <typename TL, typename ... T>
+      static std::false_type EqualityOperatorTest(const TL& left, const T& ... args);
+
+     public:
+      static constexpr bool value =
+          decltype(EqualityOperatorTest(std::declval<TLeft>(), std::declval<TRight>()))::value;
+    };
+
+    // Partial specialization when TLeft/TRight are both floating points.
+    // This is a work-around because decltype(floatvar1 == floatvar2)
+    // will not compile with clang:
+    // error: comparing floating point with == or != is unsafe [-Werror,-Wfloat-equal]
+    template <typename TLeft, typename TRight>
+    struct SupportsEqualityOperatorImpl<TLeft, TRight, true> {
+      static constexpr bool value = true;
+    };
+
+    // SupportsEqualityOperatorImpl<T1, T2>::value will evaluate to a boolean,
+    // whose value is true if T1 can be compared against T2 with ==,
+    // and false otherwise.
+    template <typename TLeft, typename TRight = TLeft>
+    struct SupportsEqualityOperator :
+        SupportsEqualityOperatorImpl<TLeft, TRight,
+                                     std::is_floating_point<TLeft>::value
+                                     && std::is_floating_point<TRight>::value> {
+    };
+
+    // Convert any kind of type to an std::string, even if there's no
+    // serialization support for it. Unknown types get converted to an
+    // an arbitrary value.
+    //
+    // Meant for printing user-visible errors or unit test failures only.
+    template <typename T>
+    std::string ToStringAny(const T& value,
+                            typename std::enable_if<
+                                SupportsInsertionOperator<T>::value>::type* = 0) {
+      std::stringstream stream;
+      stream << value;
+      return stream.str();
+    }
+
+    template <typename T>
+    std::string ToStringAny(const std::vector<T> value,
+                            typename std::enable_if<
+                                SupportsInsertionOperator<T>::value>::type* = 0) {
+      std::stringstream stream;
+      stream << "vector{";
+
+      for (size_t i = 0; i < value.size(); ++i) {
+        stream << ToStringAny(value[i]);
+
+        if (i != value.size() - 1) {
+          stream << ',';
+        }
+      }
+
+      stream << "}";
+      return stream.str();
+    }
+
+    template <typename T>
+    std::string ToStringAny(const T&,
+                            typename std::enable_if<
+                                !SupportsInsertionOperator<T>::value>::type* = 0
+    ) {
+      return std::string("(unknown type [no operator<< implemented] for )");
+    }
+  }  // namespace detail  // NOLINT [readability/namespace] [5]
+}  // namespace art
+
+#endif  // ART_CMDLINE_DETAIL_CMDLINE_PARSER_DETAIL_H_
diff --git a/cmdline/memory_representation.h b/cmdline/memory_representation.h
new file mode 100644
index 0000000..93387de
--- /dev/null
+++ b/cmdline/memory_representation.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_MEMORY_REPRESENTATION_H_
+#define ART_CMDLINE_MEMORY_REPRESENTATION_H_
+
+#include <string>
+#include <assert.h>
+#include <ostream>
+#include "utils.h"
+
+namespace art {
+
+// An integral representation of bytes of memory.
+// The underlying runtime size_t value is guaranteed to be a multiple of Divisor.
+template <size_t Divisor = 1024>
+struct Memory {
+  static_assert(IsPowerOfTwo(Divisor), "Divisor must be a power of 2");
+
+  static Memory<Divisor> FromBytes(size_t bytes) {
+    assert(bytes % Divisor == 0);
+    return Memory<Divisor>(bytes);
+  }
+
+  Memory() : Value(0u) {}
+  Memory(size_t value) : Value(value) {  // NOLINT [runtime/explicit] [5]
+    assert(value % Divisor == 0);
+  }
+  operator size_t() const { return Value; }
+
+  size_t ToBytes() const {
+    return Value;
+  }
+
+  static constexpr size_t kDivisor = Divisor;
+
+  static const char* Name() {
+    static std::string str;
+    if (str.empty()) {
+      str = "Memory<" + std::to_string(Divisor) + '>';
+    }
+
+    return str.c_str();
+  }
+
+  size_t Value;
+};
+
+template <size_t Divisor>
+std::ostream& operator<<(std::ostream& stream, Memory<Divisor> memory) {
+  return stream << memory.Value << '*' << Divisor;
+}
+
+using MemoryKiB = Memory<1024>;
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_MEMORY_REPRESENTATION_H_
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
new file mode 100644
index 0000000..50c54fe
--- /dev/null
+++ b/cmdline/token_range.h
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_TOKEN_RANGE_H_
+#define ART_CMDLINE_TOKEN_RANGE_H_
+
+#include <assert.h>
+#include <vector>
+#include <string>
+#include <algorithm>
+#include <memory>
+
+namespace art {
+// A range of tokens to make token matching algorithms easier.
+//
+// We try really hard to avoid copying and store only a pointer and iterators to the
+// interiors of the vector, so a typical copy constructor never ends up doing a deep copy.
+// It is up to the user to play nice and not to mutate the strings in-place.
+//
+// Tokens are only copied if a mutating operation is performed (and even then only
+// if it *actually* mutates the token).
+struct TokenRange {
+  // Short-hand for a vector of strings. A single string and a token is synonymous.
+  using TokenList = std::vector<std::string>;
+
+  // Copying-from-vector constructor.
+  explicit TokenRange(const TokenList& token_list)
+    : token_list_(new TokenList(token_list)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Copying-from-iterator constructor
+  template <typename ForwardIterator>
+  explicit TokenRange(ForwardIterator it_begin, ForwardIterator it_end)
+    : token_list_(new TokenList(it_begin, it_end)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+#if 0
+  // Copying-from-vector constructor.
+  TokenRange(const TokenList& token_list ATTRIBUTE_UNUSED,
+             TokenList::const_iterator it_begin,
+             TokenList::const_iterator it_end)
+    : token_list_(new TokenList(it_begin, it_end)),
+      begin_(token_list_->begin()),
+      end_(token_list_->end()) {
+    assert(it_begin >= token_list.begin());
+    assert(it_end <= token_list.end());
+  }
+#endif
+
+  // Copying from char array constructor, convertings into tokens (strings) along the way.
+  TokenRange(const char* token_list[], size_t length)
+    : token_list_(new TokenList(&token_list[0], &token_list[length])),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Non-copying move-from-vector constructor. Takes over the token vector.
+  explicit TokenRange(TokenList&& token_list)
+    : token_list_(new TokenList(std::forward<TokenList>(token_list))),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Non-copying constructor. Retain reference to existing list of tokens.
+  TokenRange(std::shared_ptr<TokenList> token_list,
+             TokenList::const_iterator it_begin,
+             TokenList::const_iterator it_end)
+    : token_list_(token_list),
+      begin_(it_begin),
+      end_(it_end) {
+    assert(it_begin >= token_list->begin());
+    assert(it_end <= token_list->end());
+  }
+
+  // Non-copying copy constructor.
+  TokenRange(const TokenRange& other) = default;
+
+  // Non-copying move constructor.
+  TokenRange(TokenRange&& other) = default;
+
+  // Non-copying constructor. Retains reference to an existing list of tokens, with offset.
+  explicit TokenRange(std::shared_ptr<TokenList> token_list)
+    : token_list_(token_list),
+      begin_(token_list_->begin()),
+      end_(token_list_->end())
+  {}
+
+  // Iterator type for begin() and end(). Guaranteed to be a RandomAccessIterator.
+  using iterator = TokenList::const_iterator;
+
+  // Iterator type for const begin() and const end(). Guaranteed to be a RandomAccessIterator.
+  using const_iterator = iterator;
+
+  // Create a token range by splitting a string. Each separator gets their own token.
+  // Since the separator are retained as tokens, it might be useful to call
+  // RemoveToken afterwards.
+  static TokenRange Split(const std::string& string, std::initializer_list<char> separators) {
+    TokenList new_token_list;
+
+    std::string tok;
+    for (auto&& c : string) {
+      for (char sep : separators) {
+        if (c == sep) {
+          // We spotted a separator character.
+          // Push back everything before the last separator as a new token.
+          // Push back the separator as a token.
+          if (!tok.empty()) {
+            new_token_list.push_back(tok);
+            tok = "";
+          }
+          new_token_list.push_back(std::string() + sep);
+        } else {
+          // Build up the token with another character.
+          tok += c;
+        }
+      }
+    }
+
+    if (!tok.empty()) {
+      new_token_list.push_back(tok);
+    }
+
+    return TokenRange(std::move(new_token_list));
+  }
+
+  // A RandomAccessIterator to the first element in this range.
+  iterator begin() const {
+    return begin_;
+  }
+
+  // A RandomAccessIterator to one past the last element in this range.
+  iterator end() const {
+    return end_;
+  }
+
+  // The size of the range, i.e. how many tokens are in it.
+  size_t Size() const {
+    return std::distance(begin_, end_);
+  }
+
+  // Are there 0 tokens in this range?
+  bool IsEmpty() const {
+    return Size() > 0;
+  }
+
+  // Look up a token by it's offset.
+  const std::string& GetToken(size_t offset) const {
+    assert(offset < Size());
+    return *(begin_ + offset);
+  }
+
+  // Does this token range equal the other range?
+  // Equality is defined as having both the same size, and
+  // each corresponding token being equal.
+  bool operator==(const TokenRange& other) const {
+    if (this == &other) {
+      return true;
+    }
+
+    if (Size() != other.Size()) {
+      return false;
+    }
+
+    return std::equal(begin(), end(), other.begin());
+  }
+
+  // Look up the token at the requested index.
+  const std::string& operator[](int index) const {
+    assert(index >= 0 && static_cast<size_t>(index) < Size());
+    return *(begin() + index);
+  }
+
+  // Does this current range start with the other range?
+  bool StartsWith(const TokenRange& other) const {
+    if (this == &other) {
+      return true;
+    }
+
+    if (Size() < other.Size()) {
+      return false;
+    }
+
+    auto& smaller = Size() < other.Size() ? *this : other;
+    auto& greater = Size() < other.Size() ? other : *this;
+
+    return std::equal(smaller.begin(), smaller.end(), greater.begin());
+  }
+
+  // Remove all characters 'c' from each token, potentially copying the underlying tokens.
+  TokenRange RemoveCharacter(char c) const {
+    TokenList new_token_list(begin(), end());
+
+    bool changed = false;
+    for (auto&& token : new_token_list) {
+      auto it = std::remove_if(token.begin(), token.end(), [&](char ch) {
+        if (ch == c) {
+          changed = true;
+          return true;
+        }
+        return false;
+      });
+      token.erase(it, token.end());
+    }
+
+    if (!changed) {
+      return *this;
+    }
+
+    return TokenRange(std::move(new_token_list));
+  }
+
+  // Remove all tokens matching this one, potentially copying the underlying tokens.
+  TokenRange RemoveToken(const std::string& token) {
+    return RemoveIf([&](const std::string& tok) { return tok == token; });
+  }
+
+  // Discard all empty tokens, potentially copying the underlying tokens.
+  TokenRange DiscardEmpty() const {
+    return RemoveIf([](const std::string& token) { return token.empty(); });
+  }
+
+  // Create a non-copying subset of this range.
+  // Length is trimmed so that the Slice does not go out of range.
+  TokenRange Slice(size_t offset, size_t length = std::string::npos) const {
+    assert(offset < Size());
+
+    if (length != std::string::npos && offset + length > Size()) {
+      length = Size() - offset;
+    }
+
+    iterator it_end;
+    if (length == std::string::npos) {
+      it_end = end();
+    } else {
+      it_end = begin() + offset + length;
+    }
+
+    return TokenRange(token_list_, begin() + offset, it_end);
+  }
+
+  // Try to match the string with tokens from this range.
+  // Each token is used to match exactly once (after which the next token is used, and so on).
+  // The matching happens from left-to-right in a non-greedy fashion.
+  // If the currently-matched token is the wildcard, then the new outputted token will
+  // contain as much as possible until the next token is matched.
+  //
+  // For example, if this == ["a:", "_", "b:] and "_" is the match string, then
+  // MatchSubstrings on "a:foob:" will yield: ["a:", "foo", "b:"]
+  //
+  // Since the string matching can fail (e.g. ["foo"] against "bar"), then this
+  // function can fail, in which cause it will return null.
+  std::unique_ptr<TokenRange> MatchSubstrings(const std::string& string,
+                                              const std::string& wildcard) const {
+    TokenList new_token_list;
+
+    size_t wildcard_idx = std::string::npos;
+    size_t string_idx = 0;
+
+    // Function to push all the characters matched as a wildcard so far
+    // as a brand new token. It resets the wildcard matching.
+    // Empty wildcards are possible and ok, but only if wildcard matching was on.
+    auto maybe_push_wildcard_token = [&]() {
+      if (wildcard_idx != std::string::npos) {
+        size_t wildcard_length = string_idx - wildcard_idx;
+        std::string wildcard_substr = string.substr(wildcard_idx, wildcard_length);
+        new_token_list.push_back(std::move(wildcard_substr));
+
+        wildcard_idx = std::string::npos;
+      }
+    };
+
+    for (iterator it = begin(); it != end(); ++it) {
+      const std::string& tok = *it;
+
+      if (tok == wildcard) {
+        maybe_push_wildcard_token();
+        wildcard_idx = string_idx;
+        continue;
+      }
+
+      size_t next_token_idx = string.find(tok);
+      if (next_token_idx == std::string::npos) {
+        // Could not find token at all
+        return nullptr;
+      } else if (next_token_idx != string_idx && wildcard_idx == std::string::npos) {
+        // Found the token at a non-starting location, and we weren't
+        // trying to parse the wildcard.
+        return nullptr;
+      }
+
+      new_token_list.push_back(string.substr(next_token_idx, tok.size()));
+      maybe_push_wildcard_token();
+      string_idx += tok.size();
+    }
+
+    size_t remaining = string.size() - string_idx;
+    if (remaining > 0) {
+      if (wildcard_idx == std::string::npos) {
+        // Some characters were still remaining in the string,
+        // but it wasn't trying to match a wildcard.
+        return nullptr;
+      }
+    }
+
+    // If some characters are remaining, the rest must be a wildcard.
+    string_idx += remaining;
+    maybe_push_wildcard_token();
+
+    return std::unique_ptr<TokenRange>(new TokenRange(std::move(new_token_list)));
+  }
+
+  // Do a quick match token-by-token, and see if they match.
+  // Any tokens with a wildcard in them are only matched up until the wildcard.
+  // If this is true, then the wildcard matching later on can still fail, so this is not
+  // a guarantee that the argument is correct, it's more of a strong hint that the
+  // user-provided input *probably* was trying to match this argument.
+  //
+  // Returns how many tokens were either matched (or ignored because there was a
+  // wildcard present). 0 means no match. If the size() tokens are returned.
+  size_t MaybeMatches(const TokenRange& token_list, const std::string& wildcard) const {
+    auto token_it = token_list.begin();
+    auto token_end = token_list.end();
+    auto name_it = begin();
+    auto name_end = end();
+
+    size_t matched_tokens = 0;
+
+    while (token_it != token_end && name_it != name_end) {
+      // Skip token matching when the corresponding name has a wildcard in it.
+      const std::string& name = *name_it;
+
+      size_t wildcard_idx = name.find(wildcard);
+      if (wildcard_idx == std::string::npos) {  // No wildcard present
+        // Did the definition token match the user token?
+        if (name != *token_it) {
+          return matched_tokens;
+        }
+      } else {
+        std::string name_prefix = name.substr(0, wildcard_idx);
+
+        // Did the user token start with the up-to-the-wildcard prefix?
+        if (!StartsWith(*token_it, name_prefix)) {
+          return matched_tokens;
+        }
+      }
+
+      ++token_it;
+      ++name_it;
+      ++matched_tokens;
+    }
+
+    // If we got this far, it's either a full match or the token list was too short.
+    return matched_tokens;
+  }
+
+  // Flatten the token range by joining every adjacent token with the separator character.
+  // e.g. ["hello", "world"].join('$') == "hello$world"
+  std::string Join(char separator) const {
+    TokenList tmp(begin(), end());
+    return art::Join(tmp, separator);
+    // TODO: Join should probably take an offset or iterators
+  }
+
+ private:
+  static bool StartsWith(const std::string& larger, const std::string& smaller) {
+    if (larger.size() >= smaller.size()) {
+      return std::equal(smaller.begin(), smaller.end(), larger.begin());
+    }
+
+    return false;
+  }
+
+  template <typename TPredicate>
+  TokenRange RemoveIf(const TPredicate& predicate) const {
+    // If any of the tokens in the token lists are empty, then
+    // we need to remove them and compress the token list into a smaller one.
+    bool remove = false;
+    for (auto it = begin_; it != end_; ++it) {
+      auto&& token = *it;
+
+      if (predicate(token)) {
+        remove = true;
+        break;
+      }
+    }
+
+    // Actually copy the token list and remove the tokens that don't match our predicate.
+    if (remove) {
+      auto token_list = std::make_shared<TokenList>(begin(), end());
+      TokenList::iterator new_end =
+          std::remove_if(token_list->begin(), token_list->end(), predicate);
+      token_list->erase(new_end, token_list->end());
+
+      assert(token_list_->size() > token_list->size() && "Nothing was actually removed!");
+
+      return TokenRange(token_list);
+    }
+
+    return *this;
+  }
+
+  const std::shared_ptr<std::vector<std::string>> token_list_;
+  const iterator begin_;
+  const iterator end_;
+};
+}  // namespace art
+
+#endif  // ART_CMDLINE_TOKEN_RANGE_H_
diff --git a/cmdline/unit.h b/cmdline/unit.h
new file mode 100644
index 0000000..6b53b18
--- /dev/null
+++ b/cmdline/unit.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_CMDLINE_UNIT_H_
+#define ART_CMDLINE_UNIT_H_
+
+namespace art {
+
+// Used for arguments that simply indicate presence (e.g. "-help") without any values.
+struct Unit {
+  // Avoid 'Conditional jump or move depends on uninitialised value(s)' errors
+  // when running valgrind by specifying a user-defined constructor.
+  Unit() {}
+  ~Unit() {}
+  bool operator==(Unit) const {
+    return true;
+  }
+};
+
+}  // namespace art
+
+#endif  // ART_CMDLINE_UNIT_H_
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 30cf580..907d884 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -139,6 +139,7 @@
   reference_table.cc \
   reflection.cc \
   runtime.cc \
+  runtime_options.cc \
   signal_catcher.cc \
   stack.cc \
   thread.cc \
@@ -457,7 +458,9 @@
   endif
 
   LOCAL_C_INCLUDES += $$(ART_C_INCLUDES)
+  LOCAL_C_INCLUDES += art/cmdline
   LOCAL_C_INCLUDES += art/sigchainlib
+  LOCAL_C_INCLUDES += art
 
   LOCAL_SHARED_LIBRARIES := libnativehelper libnativebridge libsigchain
   LOCAL_SHARED_LIBRARIES += libbacktrace
diff --git a/runtime/base/variant_map.h b/runtime/base/variant_map.h
new file mode 100644
index 0000000..cf7977e
--- /dev/null
+++ b/runtime/base/variant_map.h
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_VARIANT_MAP_H_
+#define ART_RUNTIME_BASE_VARIANT_MAP_H_
+
+#include <memory.h>
+#include <map>
+#include <utility>
+
+namespace art {
+
+//
+// A variant map is a heterogenous, type safe key->value map. It allows
+// for multiple different value types to be stored dynamically in the same map.
+//
+// It provides the following interface in a nutshell:
+//
+// struct VariantMap {
+//   template <typename TValue>
+//   TValue* Get(Key<T> key);  // nullptr if the value was never set, otherwise the value.
+//
+//   template <typename TValue>
+//   void Set(Key<T> key, TValue value);
+// };
+//
+// Since the key is strongly typed at compile-time, it is impossible to accidentally
+// read/write a value with a different type than the key at either compile-time or run-time.
+//
+// Do not use VariantMap/VariantMapKey directly. Instead subclass each of them and use
+// the subclass, for example:
+//
+// template <typename TValue>
+// struct FruitMapKey : VariantMapKey<TValue> {
+//   FruitMapKey() {}
+// };
+//
+// struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+//   // This 'using' line is necessary to inherit the variadic constructor.
+//   using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+//
+//   // Make the next '4' usages of Key slightly shorter to type.
+//   template <typename TValue>
+//   using Key = FruitMapKey<TValue>;
+//
+//   static const Key<int> Apple;
+//   static const Key<double> Orange;
+//   static const Key<std::string> Banana;
+// };
+//
+// const FruitMap::Key<int> FruitMap::Apple;
+// const FruitMap::Key<double> FruitMap::Orange;
+// const FruitMap::Key<std::string> Banana;
+//
+// See variant_map_test.cc for more examples.
+//
+
+// Implementation details for VariantMap.
+namespace detail {
+  // Allocate a unique counter value each time it's called.
+  struct VariantMapKeyCounterAllocator {
+    static size_t AllocateCounter() {
+      static size_t counter = 0;
+      counter++;
+
+      return counter;
+    }
+  };
+
+  // Type-erased version of VariantMapKey<T>
+  struct VariantMapKeyRaw {
+    // TODO: this may need to call a virtual function to support string comparisons
+    bool operator<(const VariantMapKeyRaw& other) const {
+      return key_counter_ < other.key_counter_;
+    }
+
+    // The following functions need to be virtual since we don't know the compile-time type anymore:
+
+    // Clone the key, creating a copy of the contents.
+    virtual VariantMapKeyRaw* Clone() const = 0;
+
+    // Delete a value whose runtime type is that of the non-erased key's TValue.
+    virtual void ValueDelete(void* value) const = 0;
+
+    // Clone a value whose runtime type is that of the non-erased key's TValue.
+    virtual void* ValueClone(void* value) const = 0;
+
+    // Compare one key to another (same as operator<).
+    virtual bool Compare(const VariantMapKeyRaw* other) const {
+      if (other == nullptr) {
+        return false;
+      }
+      return key_counter_ < other->key_counter_;
+    }
+
+    virtual ~VariantMapKeyRaw() {}
+
+   protected:
+    VariantMapKeyRaw()
+        : key_counter_(VariantMapKeyCounterAllocator::AllocateCounter()) {}
+    // explicit VariantMapKeyRaw(size_t counter)
+    //     : key_counter_(counter) {}
+
+    size_t GetCounter() const {
+      return key_counter_;
+    }
+
+   protected:
+    // Avoid the object slicing problem; use Clone() instead.
+    VariantMapKeyRaw(const VariantMapKeyRaw& other) = default;
+    VariantMapKeyRaw(VariantMapKeyRaw&& other) = default;
+
+   private:
+    size_t key_counter_;  // Runtime type ID. Unique each time a new type is reified.
+  };
+}  // namespace detail
+
+// The base type for keys used by the VariantMap. Users must subclass this type.
+template <typename TValue>
+struct VariantMapKey : detail::VariantMapKeyRaw {
+  // Instantiate a default value for this key. If an explicit default value was provided
+  // then that is used. Otherwise, the default value for the type TValue{} is returned.
+  TValue CreateDefaultValue() const {
+    if (default_value_ == nullptr) {
+      return TValue{};  // NOLINT [readability/braces] [4]
+    } else {
+      return TValue(*default_value_);
+    }
+  }
+
+ protected:
+  // explicit VariantMapKey(size_t counter) : detail::VariantMapKeyRaw(counter) {}
+  explicit VariantMapKey(const TValue& default_value)
+    : default_value_(std::make_shared<TValue>(default_value)) {}
+  explicit VariantMapKey(TValue&& default_value)
+    : default_value_(std::make_shared<TValue>(default_value)) {}
+  VariantMapKey() {}
+  virtual ~VariantMapKey() {}
+
+ private:
+  virtual VariantMapKeyRaw* Clone() const {
+    return new VariantMapKey<TValue>(*this);
+  }
+
+  virtual void* ValueClone(void* value) const {
+    if (value == nullptr) {
+      return nullptr;
+    }
+
+    TValue* strong_value = reinterpret_cast<TValue*>(value);
+    return new TValue(*strong_value);
+  }
+
+  virtual void ValueDelete(void* value) const {
+    if (value == nullptr) {
+      return;
+    }
+
+    // Smartly invoke the proper delete/delete[]/etc
+    const std::default_delete<TValue> deleter = std::default_delete<TValue>();
+    deleter(reinterpret_cast<TValue*>(value));
+  }
+
+  VariantMapKey(const VariantMapKey& other) = default;
+  VariantMapKey(VariantMapKey&& other) = default;
+
+  template <typename Base, template <typename TV> class TKey> friend struct VariantMap;
+
+  // Store a prototype of the key's default value, for usage with VariantMap::GetOrDefault
+  std::shared_ptr<TValue> default_value_;
+};
+
+// Implementation details for a stringified VariantMapStringKey.
+namespace detail {
+  struct VariantMapStringKeyRegistry {
+    // TODO
+  };
+}  // namespace detail
+
+// Alternative base type for all keys used by VariantMap, supports runtime strings as the name.
+template <typename TValue>
+struct VariantMapStringKey : VariantMapKey<TValue> {
+  explicit VariantMapStringKey(const char* name)
+      :   // VariantMapKey(/*std::hash<std::string>()(name)*/),
+        name_(name) {
+  }
+
+ private:
+  const char* name_;
+};
+
+// A variant map allows type-safe heteregeneous key->value mappings.
+// All possible key types must be specified at compile-time. Values may be added/removed
+// at runtime.
+template <typename Base, template <typename TV> class TKey>
+struct VariantMap {
+  // Allow users of this static interface to use the key type.
+  template <typename TValue>
+  using Key = TKey<TValue>;
+
+  // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+  // A null value is returned only when the key does not exist in this map.
+  template <typename TValue>
+  const TValue* Get(const TKey<TValue>& key) const {
+    return GetValuePtr(key);
+  }
+
+  // Look up the value from the key. The pointer becomes invalid if this key is overwritten/removed.
+  // A null value is returned only when the key does not exist in this map.
+  template <typename TValue>
+  TValue* Get(const TKey<TValue>& key) {
+    return GetValuePtr(key);
+  }
+
+  // Lookup the value from the key. If it was not set in the map, return the default value.
+  // The default value is either the key's default, or TValue{} if the key doesn't have a default.
+  template <typename TValue>
+  TValue GetOrDefault(const TKey<TValue>& key) const {
+    auto* ptr = Get(key);
+    return (ptr == nullptr) ? key.CreateDefaultValue() : *ptr;
+  }
+
+ private:
+  // TODO: move to detail, or make it more generic like a ScopeGuard(function)
+  template <typename TValue>
+  struct ScopedRemove {
+    ScopedRemove(VariantMap& map, const TKey<TValue>& key) : map_(map), key_(key) {}
+    ~ScopedRemove() {
+      map_.Remove(key_);
+    }
+
+    VariantMap& map_;
+    const TKey<TValue>& key_;
+  };
+
+ public:
+  // Release the value from the key. If it was not set in the map, returns the default value.
+  // If the key was set, it is removed as a side effect.
+  template <typename TValue>
+  TValue ReleaseOrDefault(const TKey<TValue>& key) {
+    ScopedRemove<TValue> remove_on_return(*this, key);
+
+    TValue* ptr = Get(key);
+    if (ptr != nullptr) {
+      return std::move(*ptr);
+    } else {
+      TValue default_value = key.CreateDefaultValue();
+      return std::move(default_value);
+    }
+  }
+
+  // See if a value is stored for this key.
+  template <typename TValue>
+  bool Exists(const TKey<TValue>& key) const {
+    return GetKeyValueIterator(key) != storage_map_.end();
+  }
+
+  // Set a value for a given key, overwriting the previous value if any.
+  template <typename TValue>
+  void Set(const TKey<TValue>& key, const TValue& value) {
+    Remove(key);
+    storage_map_.insert({{key.Clone(), new TValue(value)}});
+  }
+
+  // Set a value for a given key, only if there was no previous value before.
+  // Returns true if the value was set, false if a previous value existed.
+  template <typename TValue>
+  bool SetIfMissing(const TKey<TValue>& key, const TValue& value) {
+    TValue* ptr = Get(key);
+    if (ptr == nullptr) {
+      Set(key, value);
+      return true;
+    }
+    return false;
+  }
+
+  // Remove the value for a given key, or a no-op if there was no previously set value.
+  template <typename TValue>
+  void Remove(const TKey<TValue>& key) {
+    StaticAssertKeyType<TValue>();
+
+    auto&& it = GetKeyValueIterator(key);
+    if (it != storage_map_.end()) {
+      key.ValueDelete(it->second);
+      delete it->first;
+      storage_map_.erase(it);
+    }
+  }
+
+  // Remove all key/value pairs.
+  void Clear() {
+    DeleteStoredValues();
+    storage_map_.clear();
+  }
+
+  // How many key/value pairs are stored in this map.
+  size_t Size() const {
+    return storage_map_.size();
+  }
+
+  // Construct an empty map.
+  explicit VariantMap() {}
+
+  template <typename ... TKeyValue>
+  explicit VariantMap(const TKeyValue& ... key_value_list) {
+    static_assert(sizeof...(TKeyValue) % 2 == 0, "Must be an even number of key/value elements");
+    InitializeParameters(key_value_list...);
+  }
+
+  // Create a new map from an existing map, copying all the key/value pairs.
+  VariantMap(const VariantMap& other) {
+    operator=(other);
+  }
+
+  // Copy the key/value pairs from the other map into this one. Existing key/values are cleared.
+  VariantMap& operator=(const VariantMap& other) {
+    if (this == &other) {
+      return *this;
+    }
+
+    Clear();
+
+    for (auto&& kv_pair : other.storage_map_) {
+      const detail::VariantMapKeyRaw* raw_key_other = kv_pair.first;
+      void* value = kv_pair.second;
+
+      detail::VariantMapKeyRaw* cloned_raw_key = raw_key_other->Clone();
+      void* cloned_value = raw_key_other->ValueClone(value);
+
+      storage_map_.insert({{ cloned_raw_key, cloned_value }});
+    }
+
+    return *this;
+  }
+
+  // Create a new map by moving an existing map into this one. The other map becomes empty.
+  VariantMap(VariantMap&& other) {
+    operator=(std::forward<VariantMap>(other));
+  }
+
+  // Move the existing map's key/value pairs into this one. The other map becomes empty.
+  VariantMap& operator=(VariantMap&& other) {
+    if (this != &other) {
+      Clear();
+      storage_map_.swap(other.storage_map_);
+      other.storage_map_.clear();
+    }
+    return *this;
+  }
+
+  ~VariantMap() {
+    DeleteStoredValues();
+  }
+
+ private:
+  void InitializeParameters() {}
+
+  template <typename TK, typename TValue, typename ... Rest>
+  void InitializeParameters(const TK& key, const TValue& value, const Rest& ... rest) {
+    static_assert(
+        std::is_same<TK, TKey<TValue>>::value, "The 0th/2nd/4th/etc parameters must be a key");
+
+    const TKey<TValue>& key_refined = key;
+
+    Set(key_refined, value);
+    InitializeParameters(rest...);
+  }
+
+  // Custom key comparator for std::map, needed since we are storing raw pointers as the keys.
+  struct KeyComparator {
+    bool operator()(const detail::VariantMapKeyRaw* lhs,
+                    const detail::VariantMapKeyRaw* rhs) const {
+      if (lhs == nullptr) {
+        return lhs != rhs;
+      }
+
+      return lhs->Compare(rhs);
+    }
+  };
+
+  // Map of key pointers to value pointers. Pointers are never null.
+  using StorageMap = std::map<const detail::VariantMapKeyRaw*, void*, KeyComparator>;
+
+  template <typename TValue>
+  typename StorageMap::iterator GetKeyValueIterator(const TKey<TValue>& key) {
+    StaticAssertKeyType<TValue>();
+
+    const TKey<TValue>* key_ptr = &key;
+    const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+    return storage_map_.find(raw_ptr);
+  }
+
+  template <typename TValue>
+  typename StorageMap::const_iterator GetKeyValueIterator(const TKey<TValue>& key) const {
+    StaticAssertKeyType<TValue>();
+
+    const TKey<TValue>* key_ptr = &key;
+    const detail::VariantMapKeyRaw* raw_ptr = key_ptr;
+    return storage_map_.find(raw_ptr);
+  }
+
+  template <typename TValue>
+  TValue* GetValuePtr(const TKey<TValue>& key) {
+    return const_cast<TValue*>(GetValueConstPtr(key));
+  }
+
+  template <typename TValue>
+  const TValue* GetValuePtr(const TKey<TValue>& key) const {
+    return GetValueConstPtr(key);
+  }
+
+  template <typename TValue>
+  const TValue* GetValueConstPtr(const TKey<TValue>& key) const {
+    auto&& it = GetKeyValueIterator(key);
+    if (it == storage_map_.end()) {
+      return nullptr;
+    }
+
+    return reinterpret_cast<const TValue*>(it->second);
+  }
+
+  template <typename TValue>
+  static void StaticAssertKeyType() {
+    static_assert(std::is_base_of<VariantMapKey<TValue>, TKey<TValue>>::value,
+                  "The provided key type (TKey) must be a subclass of VariantMapKey");
+  }
+
+  void DeleteStoredValues() {
+    for (auto&& kv_pair : storage_map_) {
+      kv_pair.first->ValueDelete(kv_pair.second);
+      delete kv_pair.first;
+    }
+  }
+
+  StorageMap storage_map_;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_BASE_VARIANT_MAP_H_
diff --git a/runtime/base/variant_map_test.cc b/runtime/base/variant_map_test.cc
new file mode 100644
index 0000000..827de46
--- /dev/null
+++ b/runtime/base/variant_map_test.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "variant_map.h"
+#include "gtest/gtest.h"
+
+#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
+                                        reinterpret_cast<void*>(NULL));
+
+namespace art {
+
+namespace {
+  template <typename TValue>
+  struct FruitMapKey : VariantMapKey<TValue> {
+    FruitMapKey() {}
+  };
+
+  struct FruitMap : VariantMap<FruitMap, FruitMapKey> {
+    // This 'using' line is necessary to inherit the variadic constructor.
+    using VariantMap<FruitMap, FruitMapKey>::VariantMap;
+
+    // Make the next '4' usages of Key slightly shorter to type.
+    template <typename TValue>
+    using Key = FruitMapKey<TValue>;
+
+    static const Key<int> Apple;
+    static const Key<double> Orange;
+  };
+
+  const FruitMap::Key<int> FruitMap::Apple;
+  const FruitMap::Key<double> FruitMap::Orange;
+}  // namespace
+
+TEST(VariantMaps, BasicReadWrite) {
+  FruitMap fm;
+
+  EXPECT_NULL(fm.Get(FruitMap::Apple));
+  EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+
+  fm.Set(FruitMap::Apple, 1);
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_EQ(1, *fm.Get(FruitMap::Apple));
+  EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+  fm.Set(FruitMap::Apple, 5);
+  EXPECT_NULL(fm.Get(FruitMap::Orange));
+  EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+  EXPECT_TRUE(fm.Exists(FruitMap::Apple));
+
+  fm.Set(FruitMap::Orange, 555.0);
+  EXPECT_EQ(5, *fm.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(555.0, *fm.Get(FruitMap::Orange));
+  EXPECT_EQ(size_t(2), fm.Size());
+
+  fm.Remove(FruitMap::Apple);
+  EXPECT_FALSE(fm.Exists(FruitMap::Apple));
+
+  fm.Clear();
+  EXPECT_EQ(size_t(0), fm.Size());
+  EXPECT_FALSE(fm.Exists(FruitMap::Orange));
+}
+
+TEST(VariantMaps, RuleOfFive) {
+  // Test empty constructor
+  FruitMap fmEmpty;
+  EXPECT_EQ(size_t(0), fmEmpty.Size());
+
+  // Test empty constructor
+  FruitMap fmFilled;
+  fmFilled.Set(FruitMap::Apple, 1);
+  fmFilled.Set(FruitMap::Orange, 555.0);
+  EXPECT_EQ(size_t(2), fmFilled.Size());
+
+  // Test copy constructor
+  FruitMap fmEmptyCopy(fmEmpty);
+  EXPECT_EQ(size_t(0), fmEmptyCopy.Size());
+
+  // Test copy constructor
+  FruitMap fmFilledCopy(fmFilled);
+  EXPECT_EQ(size_t(2), fmFilledCopy.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy.Get(FruitMap::Orange));
+
+  // Test operator=
+  FruitMap fmFilledCopy2;
+  fmFilledCopy2 = fmFilled;
+  EXPECT_EQ(size_t(2), fmFilledCopy2.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmFilledCopy2.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmFilledCopy2.Get(FruitMap::Orange));
+
+  // Test move constructor
+  FruitMap fmMoved(std::move(fmFilledCopy));
+  EXPECT_EQ(size_t(0), fmFilledCopy.Size());
+  EXPECT_EQ(size_t(2), fmMoved.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved.Get(FruitMap::Orange));
+
+  // Test operator= move
+  FruitMap fmMoved2;
+  fmMoved2.Set(FruitMap::Apple, 12345);  // This value will be clobbered after the move
+
+  fmMoved2 = std::move(fmFilledCopy2);
+  EXPECT_EQ(size_t(0), fmFilledCopy2.Size());
+  EXPECT_EQ(size_t(2), fmMoved2.Size());
+  EXPECT_EQ(*fmFilled.Get(FruitMap::Apple), *fmMoved2.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(*fmFilled.Get(FruitMap::Orange), *fmMoved2.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, VariadicConstructors) {
+  // Variadic constructor, 1 kv/pair
+  FruitMap fmApple(FruitMap::Apple, 12345);
+  EXPECT_EQ(size_t(1), fmApple.Size());
+  EXPECT_EQ(12345, *fmApple.Get(FruitMap::Apple));
+
+  // Variadic constructor, 2 kv/pair
+  FruitMap fmAppleAndOrange(FruitMap::Apple,   12345,
+                            FruitMap::Orange,  100.0);
+  EXPECT_EQ(size_t(2), fmAppleAndOrange.Size());
+  EXPECT_EQ(12345, *fmAppleAndOrange.Get(FruitMap::Apple));
+  EXPECT_DOUBLE_EQ(100.0, *fmAppleAndOrange.Get(FruitMap::Orange));
+}
+
+TEST(VariantMaps, ReleaseOrDefault) {
+  FruitMap fmAppleAndOrange(FruitMap::Apple,   12345,
+                            FruitMap::Orange,  100.0);
+
+  int apple = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+  EXPECT_EQ(12345, apple);
+
+  // Releasing will also remove the Apple key.
+  EXPECT_EQ(size_t(1), fmAppleAndOrange.Size());
+
+  // Releasing again yields a default value.
+  int apple2 = fmAppleAndOrange.ReleaseOrDefault(FruitMap::Apple);
+  EXPECT_EQ(0, apple2);
+}
+
+TEST(VariantMaps, GetOrDefault) {
+  FruitMap fm(FruitMap::Apple,   12345);
+
+  // Apple gives the expected value we set.
+  int apple = fm.GetOrDefault(FruitMap::Apple);
+  EXPECT_EQ(12345, apple);
+
+  // Map is still 1.
+  EXPECT_EQ(size_t(1), fm.Size());
+
+  // Orange gives back a default value, since it's not in the map.
+  double orange = fm.GetOrDefault(FruitMap::Orange);
+  EXPECT_DOUBLE_EQ(0.0, orange);
+}
+
+}  // namespace art
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index d48ac9d..b7ffd60 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -224,6 +224,7 @@
   options.push_back(std::make_pair(max_heap_string, nullptr));
   options.push_back(std::make_pair("compilercallbacks", callbacks_.get()));
   SetUpRuntimeOptions(&options);
+
   if (!Runtime::Create(options, false)) {
     LOG(FATAL) << "Failed to create runtime";
     return;
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 38a9733..9efea84 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -76,6 +76,9 @@
   CommonRuntimeTest();
   ~CommonRuntimeTest();
 
+  // Gets the path of the libcore dex file.
+  static std::string GetLibCoreDexFileName();
+
  protected:
   static bool IsHost() {
     return !kIsTargetBuild;
@@ -98,11 +101,8 @@
 
   virtual void TearDown();
 
-  // Gets the path of the libcore dex file.
-  std::string GetLibCoreDexFileName();
-
   // Gets the path of the specified dex file for host or target.
-  std::string GetDexFileName(const std::string& jar_prefix);
+  static std::string GetDexFileName(const std::string& jar_prefix);
 
   std::string GetTestAndroidRoot();
 
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index ef5d56e..9c62097 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -46,6 +46,19 @@
 };
 std::ostream& operator<<(std::ostream& os, const CollectorType& collector_type);
 
+static constexpr CollectorType kCollectorTypeDefault =
+#if ART_DEFAULT_GC_TYPE_IS_CMS
+    kCollectorTypeCMS
+#elif ART_DEFAULT_GC_TYPE_IS_SS
+    kCollectorTypeSS
+#elif ART_DEFAULT_GC_TYPE_IS_GSS
+    kCollectorTypeGSS
+#else
+    gc::kCollectorTypeCMS
+#error "ART default GC type must be set"
+#endif
+    ;  // NOLINT [whitespace/semicolon] [5]
+
 }  // namespace gc
 }  // namespace art
 
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 5a60c87..dc42510 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -364,11 +364,11 @@
   CHECK(non_moving_space_ != nullptr);
   CHECK(!non_moving_space_->CanMoveObjects());
   // Allocate the large object space.
-  if (large_object_space_type == space::kLargeObjectSpaceTypeFreeList) {
+  if (large_object_space_type == space::LargeObjectSpaceType::kFreeList) {
     large_object_space_ = space::FreeListSpace::Create("free list large object space", nullptr,
                                                        capacity_);
     CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
-  } else if (large_object_space_type == space::kLargeObjectSpaceTypeMap) {
+  } else if (large_object_space_type == space::LargeObjectSpaceType::kMap) {
     large_object_space_ = space::LargeObjectMapSpace::Create("mem map large object space");
     CHECK(large_object_space_ != nullptr) << "Failed to create large object space";
   } else {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 9aced81..0beb20c 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -152,7 +152,7 @@
       space::kLargeObjectSpaceTypeFreeList;
 #else
   static constexpr space::LargeObjectSpaceType kDefaultLargeObjectSpaceType =
-      space::kLargeObjectSpaceTypeMap;
+      space::LargeObjectSpaceType::kMap;
 #endif
   // Used so that we don't overflow the allocation time atomic integer.
   static constexpr size_t kTimeAdjust = 1024;
diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h
index 850a006..847f575 100644
--- a/runtime/gc/space/large_object_space.h
+++ b/runtime/gc/space/large_object_space.h
@@ -31,10 +31,10 @@
 
 class AllocationInfo;
 
-enum LargeObjectSpaceType {
-  kLargeObjectSpaceTypeDisabled,
-  kLargeObjectSpaceTypeMap,
-  kLargeObjectSpaceTypeFreeList,
+enum class LargeObjectSpaceType {
+  kDisabled,
+  kMap,
+  kFreeList,
 };
 
 // Abstraction implemented by all large object spaces.
diff --git a/runtime/globals.h b/runtime/globals.h
index 0756a73..0845475 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -110,16 +110,16 @@
 #endif
 
 // Kinds of tracing clocks.
-enum TraceClockSource {
-  kTraceClockSourceThreadCpu,
-  kTraceClockSourceWall,
-  kTraceClockSourceDual,  // Both wall and thread CPU clocks.
+enum class TraceClockSource {
+  kThreadCpu,
+  kWall,
+  kDual,  // Both wall and thread CPU clocks.
 };
 
 #if defined(__linux__)
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceDual;
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kDual;
 #else
-static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall;
+static constexpr TraceClockSource kDefaultTraceClockSource = TraceClockSource::kWall;
 #endif
 
 static constexpr bool kDefaultMustRelocate = true;
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index 40417d8..ea7c192 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -32,6 +32,7 @@
 #include "java_vm_ext.h"
 #include "parsed_options.h"
 #include "runtime-inl.h"
+#include "runtime_options.h"
 #include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
@@ -357,14 +358,15 @@
   JII::AttachCurrentThreadAsDaemon
 };
 
-JavaVMExt::JavaVMExt(Runtime* runtime, ParsedOptions* options)
+JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
     : runtime_(runtime),
       check_jni_abort_hook_(nullptr),
       check_jni_abort_hook_data_(nullptr),
       check_jni_(false),  // Initialized properly in the constructor body below.
-      force_copy_(options->force_copy_),
-      tracing_enabled_(!options->jni_trace_.empty() || VLOG_IS_ON(third_party_jni)),
-      trace_(options->jni_trace_),
+      force_copy_(runtime_options.Exists(RuntimeArgumentMap::JniOptsForceCopy)),
+      tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
+                       || VLOG_IS_ON(third_party_jni)),
+      trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
       globals_lock_("JNI global reference table lock"),
       globals_(gGlobalsInitial, gGlobalsMax, kGlobal),
       libraries_(new Libraries),
@@ -374,9 +376,7 @@
       allow_new_weak_globals_(true),
       weak_globals_add_condition_("weak globals add condition", weak_globals_lock_) {
   functions = unchecked_functions_;
-  if (options->check_jni_) {
-    SetCheckJniEnabled(true);
-  }
+  SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni));
 }
 
 JavaVMExt::~JavaVMExt() {
diff --git a/runtime/java_vm_ext.h b/runtime/java_vm_ext.h
index c3f0a82..037fbe5 100644
--- a/runtime/java_vm_ext.h
+++ b/runtime/java_vm_ext.h
@@ -34,10 +34,11 @@
 class Libraries;
 class ParsedOptions;
 class Runtime;
+struct RuntimeArgumentMap;
 
 class JavaVMExt : public JavaVM {
  public:
-  JavaVMExt(Runtime* runtime, ParsedOptions* options);
+  JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
   ~JavaVMExt();
 
   bool ForceCopy() const {
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index 9309ab5..6464a62 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -108,6 +108,8 @@
   uint16_t port;
 };
 
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs);
+
 struct JdwpEvent;
 class JdwpNetStateBase;
 struct ModBasket;
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index 40211de..b04aa6e 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -619,6 +619,18 @@
   return !(lhs == rhs);
 }
 
+bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs) {
+  if (&lhs == &rhs) {
+    return true;
+  }
+
+  return lhs.transport == rhs.transport &&
+      lhs.server == rhs.server &&
+      lhs.suspend == rhs.suspend &&
+      lhs.host == rhs.host &&
+      lhs.port == rhs.port;
+}
+
 }  // namespace JDWP
 
 }  // namespace art
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 96430a0..b27bd6b 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -26,240 +26,327 @@
 #include "trace.h"
 #include "utils.h"
 
+#include "cmdline_parser.h"
+#include "runtime_options.h"
+
 namespace art {
 
+using MemoryKiB = Memory<1024>;
+
 ParsedOptions::ParsedOptions()
-    :
-    check_jni_(kIsDebugBuild),                      // -Xcheck:jni is off by default for regular
-                                                    // builds but on by default in debug builds.
-    force_copy_(false),
-    compiler_callbacks_(nullptr),
-    is_zygote_(false),
-    must_relocate_(kDefaultMustRelocate),
-    dex2oat_enabled_(true),
-    image_dex2oat_enabled_(true),
-    interpreter_only_(kPoisonHeapReferences),       // kPoisonHeapReferences currently works with
-                                                    // the interpreter only.
-                                                    // TODO: make it work with the compiler.
-    is_explicit_gc_disabled_(false),
-    use_tlab_(false),
-    verify_pre_gc_heap_(false),
-    verify_pre_sweeping_heap_(kIsDebugBuild),       // Pre sweeping is the one that usually fails
-                                                    // if the GC corrupted the heap.
-    verify_post_gc_heap_(false),
-    verify_pre_gc_rosalloc_(kIsDebugBuild),
-    verify_pre_sweeping_rosalloc_(false),
-    verify_post_gc_rosalloc_(false),
-    long_pause_log_threshold_(gc::Heap::kDefaultLongPauseLogThreshold),
-    long_gc_log_threshold_(gc::Heap::kDefaultLongGCLogThreshold),
-    dump_gc_performance_on_shutdown_(false),
-    ignore_max_footprint_(false),
-    heap_initial_size_(gc::Heap::kDefaultInitialSize),
-    heap_maximum_size_(gc::Heap::kDefaultMaximumSize),
-    heap_growth_limit_(0),                          // 0 means no growth limit.
-    heap_min_free_(gc::Heap::kDefaultMinFree),
-    heap_max_free_(gc::Heap::kDefaultMaxFree),
-    heap_non_moving_space_capacity_(gc::Heap::kDefaultNonMovingSpaceCapacity),
-    large_object_space_type_(gc::Heap::kDefaultLargeObjectSpaceType),
-    large_object_threshold_(gc::Heap::kDefaultLargeObjectThreshold),
-    heap_target_utilization_(gc::Heap::kDefaultTargetUtilization),
-    foreground_heap_growth_multiplier_(gc::Heap::kDefaultHeapGrowthMultiplier),
-    parallel_gc_threads_(1),
-    conc_gc_threads_(0),                            // Only the main GC thread, no workers.
-    collector_type_(                                // The default GC type is set in makefiles.
-#if ART_DEFAULT_GC_TYPE_IS_CMS
-        gc::kCollectorTypeCMS),
-#elif ART_DEFAULT_GC_TYPE_IS_SS
-        gc::kCollectorTypeSS),
-#elif ART_DEFAULT_GC_TYPE_IS_GSS
-    gc::kCollectorTypeGSS),
-#else
-    gc::kCollectorTypeCMS),
-#error "ART default GC type must be set"
-#endif
-    background_collector_type_(gc::kCollectorTypeNone),
-                                                    // If background_collector_type_ is
-                                                    // kCollectorTypeNone, it defaults to the
-                                                    // collector_type_ after parsing options. If
-                                                    // you set this to kCollectorTypeHSpaceCompact
-                                                    // then we will do an hspace compaction when
-                                                    // we transition to background instead of a
-                                                    // normal collector transition.
-    stack_size_(0),                                 // 0 means default.
-    max_spins_before_thin_lock_inflation_(Monitor::kDefaultMaxSpinsBeforeThinLockInflation),
-    low_memory_mode_(false),
-    lock_profiling_threshold_(0),
-    method_trace_(false),
-    method_trace_file_("/data/method-trace-file.bin"),
-    method_trace_file_size_(10 * MB),
-    hook_is_sensitive_thread_(nullptr),
+  : hook_is_sensitive_thread_(nullptr),
     hook_vfprintf_(vfprintf),
     hook_exit_(exit),
-    hook_abort_(nullptr),                           // We don't call abort(3) by default; see
-                                                    // Runtime::Abort.
-    profile_clock_source_(kDefaultTraceClockSource),
-    verify_(true),
-    image_isa_(kRuntimeISA),
-    use_homogeneous_space_compaction_for_oom_(true),  // Enable hspace compaction on OOM by default.
-    min_interval_homogeneous_space_compaction_by_oom_(MsToNs(100 * 1000)) {  // 100s.
-  if (kUseReadBarrier) {
-    // If RB is enabled (currently a build-time decision), use CC as the default GC.
-    collector_type_ = gc::kCollectorTypeCC;
-    background_collector_type_ = gc::kCollectorTypeCC;  // Disable background compaction for CC.
-    interpreter_only_ = true;  // Disable the compiler for CC (for now).
-    // use_tlab_ = true;
-  }
+    hook_abort_(nullptr) {                          // We don't call abort(3) by default; see
+                                                    // Runtime::Abort
 }
 
-ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized) {
+ParsedOptions* ParsedOptions::Create(const RuntimeOptions& options, bool ignore_unrecognized,
+                                     RuntimeArgumentMap* runtime_options) {
+  CHECK(runtime_options != nullptr);
+
   std::unique_ptr<ParsedOptions> parsed(new ParsedOptions());
-  if (parsed->Parse(options, ignore_unrecognized)) {
+  if (parsed->Parse(options, ignore_unrecognized, runtime_options)) {
     return parsed.release();
   }
   return nullptr;
 }
 
-// Parse a string of the form /[0-9]+[kKmMgG]?/, which is used to specify
-// memory sizes.  [kK] indicates kilobytes, [mM] megabytes, and
-// [gG] gigabytes.
-//
-// "s" should point just past the "-Xm?" part of the string.
-// "div" specifies a divisor, e.g. 1024 if the value must be a multiple
-// of 1024.
-//
-// The spec says the -Xmx and -Xms options must be multiples of 1024.  It
-// doesn't say anything about -Xss.
-//
-// Returns 0 (a useless size) if "s" is malformed or specifies a low or
-// non-evenly-divisible value.
-//
-size_t ParseMemoryOption(const char* s, size_t div) {
-  // strtoul accepts a leading [+-], which we don't want,
-  // so make sure our string starts with a decimal digit.
-  if (isdigit(*s)) {
-    char* s2;
-    size_t val = strtoul(s, &s2, 10);
-    if (s2 != s) {
-      // s2 should be pointing just after the number.
-      // If this is the end of the string, the user
-      // has specified a number of bytes.  Otherwise,
-      // there should be exactly one more character
-      // that specifies a multiplier.
-      if (*s2 != '\0') {
-        // The remainder of the string is either a single multiplier
-        // character, or nothing to indicate that the value is in
-        // bytes.
-        char c = *s2++;
-        if (*s2 == '\0') {
-          size_t mul;
-          if (c == '\0') {
-            mul = 1;
-          } else if (c == 'k' || c == 'K') {
-            mul = KB;
-          } else if (c == 'm' || c == 'M') {
-            mul = MB;
-          } else if (c == 'g' || c == 'G') {
-            mul = GB;
-          } else {
-            // Unknown multiplier character.
-            return 0;
-          }
+using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
 
-          if (val <= std::numeric_limits<size_t>::max() / mul) {
-            val *= mul;
-          } else {
-            // Clamp to a multiple of 1024.
-            val = std::numeric_limits<size_t>::max() & ~(1024-1);
-          }
-        } else {
-          // There's more than one character after the numeric part.
-          return 0;
-        }
-      }
-      // The man page says that a -Xm value must be a multiple of 1024.
-      if (val % div == 0) {
-        return val;
-      }
-    }
-  }
-  return 0;
+// Yes, the stack frame is huge. But we get called super early on (and just once)
+// to pass the command line arguments, so we'll probably be ok.
+// Ideas to avoid suppressing this diagnostic are welcome!
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+std::unique_ptr<RuntimeParser> ParsedOptions::MakeParser(bool ignore_unrecognized) {
+  using M = RuntimeArgumentMap;
+
+  std::unique_ptr<RuntimeParser::Builder> parser_builder =
+      std::unique_ptr<RuntimeParser::Builder>(new RuntimeParser::Builder());
+
+  parser_builder->
+       Define("-Xzygote")
+          .IntoKey(M::Zygote)
+      .Define("-help")
+          .IntoKey(M::Help)
+      .Define("-showversion")
+          .IntoKey(M::ShowVersion)
+      .Define("-Xbootclasspath:_")
+          .WithType<std::string>()
+          .IntoKey(M::BootClassPath)
+      .Define("-Xbootclasspath-locations:_")
+          .WithType<ParseStringList<':'>>()  // std::vector<std::string>, split by :
+          .IntoKey(M::BootClassPathLocations)
+      .Define({"-classpath _", "-cp _"})
+          .WithType<std::string>()
+          .IntoKey(M::ClassPath)
+      .Define("-Ximage:_")
+          .WithType<std::string>()
+          .IntoKey(M::Image)
+      .Define("-Xcheck:jni")
+          .IntoKey(M::CheckJni)
+      .Define("-Xjniopts:forcecopy")
+          .IntoKey(M::JniOptsForceCopy)
+      .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
+          .WithType<JDWP::JdwpOptions>()
+          .IntoKey(M::JdwpOptions)
+      .Define("-Xms_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::MemoryInitialSize)
+      .Define("-Xmx_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::MemoryMaximumSize)
+      .Define("-XX:HeapGrowthLimit=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapGrowthLimit)
+      .Define("-XX:HeapMinFree=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapMinFree)
+      .Define("-XX:HeapMaxFree=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::HeapMaxFree)
+      .Define("-XX:NonMovingSpaceCapacity=_")
+          .WithType<MemoryKiB>()
+          .IntoKey(M::NonMovingSpaceCapacity)
+      .Define("-XX:HeapTargetUtilization=_")
+          .WithType<double>().WithRange(0.1, 0.9)
+          .IntoKey(M::HeapTargetUtilization)
+      .Define("-XX:ForegroundHeapGrowthMultiplier=_")
+          .WithType<double>().WithRange(0.1, 1.0)
+          .IntoKey(M::ForegroundHeapGrowthMultiplier)
+      .Define("-XX:ParallelGCThreads=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::ParallelGCThreads)
+      .Define("-XX:ConcGCThreads=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::ConcGCThreads)
+      .Define("-Xss_")
+          .WithType<Memory<1>>()
+          .IntoKey(M::StackSize)
+      .Define("-XX:MaxSpinsBeforeThinLockInflation=_")
+          .WithType<unsigned int>()
+          .IntoKey(M::MaxSpinsBeforeThinLockInflation)
+      .Define("-XX:LongPauseLogThreshold=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::LongPauseLogThreshold)
+      .Define("-XX:LongGCLogThreshold=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::LongGCLogThreshold)
+      .Define("-XX:DumpGCPerformanceOnShutdown")
+          .IntoKey(M::DumpGCPerformanceOnShutdown)
+      .Define("-XX:IgnoreMaxFootprint")
+          .IntoKey(M::IgnoreMaxFootprint)
+      .Define("-XX:LowMemoryMode")
+          .IntoKey(M::LowMemoryMode)
+      .Define("-XX:UseTLAB")
+          .IntoKey(M::UseTLAB)
+      .Define({"-XX:EnableHSpaceCompactForOOM", "-XX:DisableHSpaceCompactForOOM"})
+          .WithValues({true, false})
+          .IntoKey(M::EnableHSpaceCompactForOOM)
+      .Define("-XX:HspaceCompactForOOMMinIntervalMs=_")  // in ms
+          .WithType<MillisecondsToNanoseconds>()  // store as ns
+          .IntoKey(M::HSpaceCompactForOOMMinIntervalsMs)
+      .Define("-D_")
+          .WithType<std::vector<std::string>>().AppendValues()
+          .IntoKey(M::PropertiesList)
+      .Define("-Xjnitrace:_")
+          .WithType<std::string>()
+          .IntoKey(M::JniTrace)
+      .Define("-Xpatchoat:_")
+          .WithType<std::string>()
+          .IntoKey(M::PatchOat)
+      .Define({"-Xrelocate", "-Xnorelocate"})
+          .WithValues({true, false})
+          .IntoKey(M::Relocate)
+      .Define({"-Xdex2oat", "-Xnodex2oat"})
+          .WithValues({true, false})
+          .IntoKey(M::Dex2Oat)
+      .Define({"-Ximage-dex2oat", "-Xnoimage-dex2oat"})
+          .WithValues({true, false})
+          .IntoKey(M::ImageDex2Oat)
+      .Define("-Xint")
+          .WithValue(true)
+          .IntoKey(M::Interpret)
+      .Define("-Xgc:_")
+          .WithType<XGcOption>()
+          .IntoKey(M::GcOption)
+      .Define("-XX:LargeObjectSpace=_")
+          .WithType<gc::space::LargeObjectSpaceType>()
+          .WithValueMap({{"disabled", gc::space::LargeObjectSpaceType::kDisabled},
+                         {"freelist", gc::space::LargeObjectSpaceType::kFreeList},
+                         {"map",      gc::space::LargeObjectSpaceType::kMap}})
+          .IntoKey(M::LargeObjectSpace)
+      .Define("-XX:LargeObjectThreshold=_")
+          .WithType<Memory<1>>()
+          .IntoKey(M::LargeObjectThreshold)
+      .Define("-XX:BackgroundGC=_")
+          .WithType<BackgroundGcOption>()
+          .IntoKey(M::BackgroundGc)
+      .Define("-XX:+DisableExplicitGC")
+          .IntoKey(M::DisableExplicitGC)
+      .Define("-verbose:_")
+          .WithType<LogVerbosity>()
+          .IntoKey(M::Verbose)
+      .Define("-Xlockprofthreshold:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::LockProfThreshold)
+      .Define("-Xstacktracefile:_")
+          .WithType<std::string>()
+          .IntoKey(M::StackTraceFile)
+      .Define("-Xmethod-trace")
+          .IntoKey(M::MethodTrace)
+      .Define("-Xmethod-trace-file:_")
+          .WithType<std::string>()
+          .IntoKey(M::MethodTraceFile)
+      .Define("-Xmethod-trace-file-size:_")
+          .WithType<unsigned int>()
+          .IntoKey(M::MethodTraceFileSize)
+      .Define("-Xprofile:_")
+          .WithType<TraceClockSource>()
+          .WithValueMap({{"threadcpuclock", TraceClockSource::kThreadCpu},
+                         {"wallclock",      TraceClockSource::kWall},
+                         {"dualclock",      TraceClockSource::kDual}})
+          .IntoKey(M::ProfileClock)
+      .Define("-Xenable-profiler")
+          .WithType<TestProfilerOptions>()
+          .AppendValues()
+          .IntoKey(M::ProfilerOpts)  // NOTE: Appends into same key as -Xprofile-*
+      .Define("-Xprofile-_")  // -Xprofile-<key>:<value>
+          .WithType<TestProfilerOptions>()
+          .AppendValues()
+          .IntoKey(M::ProfilerOpts)  // NOTE: Appends into same key as -Xenable-profiler
+      .Define("-Xcompiler:_")
+          .WithType<std::string>()
+          .IntoKey(M::Compiler)
+      .Define("-Xcompiler-option _")
+          .WithType<std::vector<std::string>>()
+          .AppendValues()
+          .IntoKey(M::CompilerOptions)
+      .Define("-Ximage-compiler-option _")
+          .WithType<std::vector<std::string>>()
+          .AppendValues()
+          .IntoKey(M::ImageCompilerOptions)
+      .Define("-Xverify:_")
+          .WithType<bool>()
+          .WithValueMap({{"none", false},
+                         {"remote", true},
+                         {"all", true}})
+          .IntoKey(M::Verify)
+      .Define("-XX:NativeBridge=_")
+          .WithType<std::string>()
+          .IntoKey(M::NativeBridge)
+      .Ignore({
+          "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
+          "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
+          "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
+          "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
+          "-Xincludeselectedmethod", "-Xjitthreshold:_", "-Xjitcodecachesize:_",
+          "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
+          "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
+          "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
+      .IgnoreUnrecognized(ignore_unrecognized);
+
+  // TODO: Move Usage information into this DSL.
+
+  return std::unique_ptr<RuntimeParser>(new RuntimeParser(parser_builder->Build()));
 }
 
-static gc::CollectorType ParseCollectorType(const std::string& option) {
-  if (option == "MS" || option == "nonconcurrent") {
-    return gc::kCollectorTypeMS;
-  } else if (option == "CMS" || option == "concurrent") {
-    return gc::kCollectorTypeCMS;
-  } else if (option == "SS") {
-    return gc::kCollectorTypeSS;
-  } else if (option == "GSS") {
-    return gc::kCollectorTypeGSS;
-  } else if (option == "CC") {
-    return gc::kCollectorTypeCC;
-  } else if (option == "MC") {
-    return gc::kCollectorTypeMC;
-  } else {
-    return gc::kCollectorTypeNone;
-  }
-}
+#pragma GCC diagnostic pop
 
-bool ParsedOptions::ParseXGcOption(const std::string& option) {
-  std::vector<std::string> gc_options;
-  Split(option.substr(strlen("-Xgc:")), ',', &gc_options);
-  for (const std::string& gc_option : gc_options) {
-    gc::CollectorType collector_type = ParseCollectorType(gc_option);
-    if (collector_type != gc::kCollectorTypeNone) {
-      collector_type_ = collector_type;
-    } else if (gc_option == "preverify") {
-      verify_pre_gc_heap_ = true;
-    } else if (gc_option == "nopreverify") {
-      verify_pre_gc_heap_ = false;
-    }  else if (gc_option == "presweepingverify") {
-      verify_pre_sweeping_heap_ = true;
-    } else if (gc_option == "nopresweepingverify") {
-      verify_pre_sweeping_heap_ = false;
-    } else if (gc_option == "postverify") {
-      verify_post_gc_heap_ = true;
-    } else if (gc_option == "nopostverify") {
-      verify_post_gc_heap_ = false;
-    } else if (gc_option == "preverify_rosalloc") {
-      verify_pre_gc_rosalloc_ = true;
-    } else if (gc_option == "nopreverify_rosalloc") {
-      verify_pre_gc_rosalloc_ = false;
-    } else if (gc_option == "presweepingverify_rosalloc") {
-      verify_pre_sweeping_rosalloc_ = true;
-    } else if (gc_option == "nopresweepingverify_rosalloc") {
-      verify_pre_sweeping_rosalloc_ = false;
-    } else if (gc_option == "postverify_rosalloc") {
-      verify_post_gc_rosalloc_ = true;
-    } else if (gc_option == "nopostverify_rosalloc") {
-      verify_post_gc_rosalloc_ = false;
-    } else if ((gc_option == "precise") ||
-               (gc_option == "noprecise") ||
-               (gc_option == "verifycardtable") ||
-               (gc_option == "noverifycardtable")) {
-      // Ignored for backwards compatibility.
+// Remove all the special options that have something in the void* part of the option.
+// If runtime_options is not null, put the options in there.
+// As a side-effect, populate the hooks from options.
+bool ParsedOptions::ProcessSpecialOptions(const RuntimeOptions& options,
+                                          RuntimeArgumentMap* runtime_options,
+                                          std::vector<std::string>* out_options) {
+  using M = RuntimeArgumentMap;
+
+  // TODO: Move the below loop into JNI
+  // Handle special options that set up hooks
+  for (size_t i = 0; i < options.size(); ++i) {
+    const std::string option(options[i].first);
+      // TODO: support -Djava.class.path
+    if (option == "bootclasspath") {
+      auto boot_class_path
+          = reinterpret_cast<const std::vector<const DexFile*>*>(options[i].second);
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::BootClassPathDexList, boot_class_path);
+      }
+    } else if (option == "compilercallbacks") {
+      CompilerCallbacks* compiler_callbacks =
+          reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::CompilerCallbacksPtr, compiler_callbacks);
+      }
+    } else if (option == "imageinstructionset") {
+      const char* isa_str = reinterpret_cast<const char*>(options[i].second);
+      auto&& image_isa = GetInstructionSetFromString(isa_str);
+      if (image_isa == kNone) {
+        Usage("%s is not a valid instruction set.", isa_str);
+        return false;
+      }
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::ImageInstructionSet, image_isa);
+      }
+    } else if (option == "sensitiveThread") {
+      const void* hook = options[i].second;
+      bool (*hook_is_sensitive_thread)() = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookIsSensitiveThread, hook_is_sensitive_thread);
+      }
+    } else if (option == "vfprintf") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("vfprintf argument was NULL");
+        return false;
+      }
+      int (*hook_vfprintf)(FILE *, const char*, va_list) =
+          reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
+
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookVfprintf, hook_vfprintf);
+      }
+      hook_vfprintf_ = hook_vfprintf;
+    } else if (option == "exit") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("exit argument was NULL");
+        return false;
+      }
+      void(*hook_exit)(jint) = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookExit, hook_exit);
+      }
+      hook_exit_ = hook_exit;
+    } else if (option == "abort") {
+      const void* hook = options[i].second;
+      if (hook == nullptr) {
+        Usage("abort was NULL\n");
+        return false;
+      }
+      void(*hook_abort)() = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
+      if (runtime_options != nullptr) {
+        runtime_options->Set(M::HookAbort, hook_abort);
+      }
+      hook_abort_ = hook_abort;
     } else {
-      Usage("Unknown -Xgc option %s\n", gc_option.c_str());
-      return false;
+      // It is a regular option, that doesn't have a known 'second' value.
+      // Push it on to the regular options which will be parsed by our parser.
+      if (out_options != nullptr) {
+        out_options->push_back(option);
+      }
     }
   }
+
   return true;
 }
 
-bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized) {
-  const char* boot_class_path_string = getenv("BOOTCLASSPATH");
-  if (boot_class_path_string != NULL) {
-    boot_class_path_string_ = boot_class_path_string;
-  }
-  const char* class_path_string = getenv("CLASSPATH");
-  if (class_path_string != NULL) {
-    class_path_string_ = class_path_string;
-  }
-
-  // Default to number of processors minus one since the main GC thread also does work.
-  parallel_gc_threads_ = sysconf(_SC_NPROCESSORS_CONF) - 1;
-
+bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognized,
+                          RuntimeArgumentMap* runtime_options) {
 //  gLogVerbosity.class_linker = true;  // TODO: don't check this in!
 //  gLogVerbosity.compiler = true;  // TODO: don't check this in!
 //  gLogVerbosity.gc = true;  // TODO: don't check this in!
@@ -279,435 +366,110 @@
       LOG(INFO) << "option[" << i << "]=" << options[i].first;
     }
   }
-  for (size_t i = 0; i < options.size(); ++i) {
-    const std::string option(options[i].first);
-    if (StartsWith(option, "-help")) {
-      Usage(nullptr);
-      return false;
-    } else if (StartsWith(option, "-showversion")) {
-      UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+
+  auto parser = MakeParser(ignore_unrecognized);
+
+  // Convert to a simple string list (without the magic pointer options)
+  std::vector<std::string> argv_list;
+  if (!ProcessSpecialOptions(options, nullptr, &argv_list)) {
+    return false;
+  }
+
+  CmdlineResult parse_result = parser->Parse(argv_list);
+
+  // Handle parse errors by displaying the usage and potentially exiting.
+  if (parse_result.IsError()) {
+    if (parse_result.GetStatus() == CmdlineResult::kUsage) {
+      UsageMessage(stdout, "%s\n", parse_result.GetMessage().c_str());
       Exit(0);
-    } else if (StartsWith(option, "-Xbootclasspath:")) {
-      boot_class_path_string_ = option.substr(strlen("-Xbootclasspath:")).data();
-      LOG(INFO) << "setting boot class path to " << boot_class_path_string_;
-    } else if (StartsWith(option, "-Xbootclasspath-locations:")) {
-      boot_class_path_locations_string_ = option.substr(
-          strlen("-Xbootclasspath-locations:")).data();
-    } else if (option == "-classpath" || option == "-cp") {
-      // TODO: support -Djava.class.path
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required class path value for %s\n", option.c_str());
-        return false;
-      }
-      const StringPiece& value = options[i].first;
-      class_path_string_ = value.data();
-    } else if (StartsWith(option, "-Ximage:")) {
-      if (!ParseStringAfterChar(option, ':', &image_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xcheck:jni")) {
-      check_jni_ = true;
-    } else if (StartsWith(option, "-Xjniopts:forcecopy")) {
-      force_copy_ = true;
-    } else if (StartsWith(option, "-Xrunjdwp:") || StartsWith(option, "-agentlib:jdwp=")) {
-      std::string tail(option.substr(option[1] == 'X' ? 10 : 15));
-      // TODO: move parsing logic out of Dbg
-      if (tail == "help" || !Dbg::ParseJdwpOptions(tail)) {
-        if (tail != "help") {
-          UsageMessage(stderr, "Failed to parse JDWP option %s\n", tail.c_str());
-        }
-        Usage("Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
-              "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n");
-        return false;
-      }
-    } else if (StartsWith(option, "-Xms")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xms")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_initial_size_ = size;
-    } else if (StartsWith(option, "-Xmx")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xmx")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_maximum_size_ = size;
-    } else if (StartsWith(option, "-XX:HeapGrowthLimit=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapGrowthLimit=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_growth_limit_ = size;
-    } else if (StartsWith(option, "-XX:HeapMinFree=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMinFree=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_min_free_ = size;
-    } else if (StartsWith(option, "-XX:HeapMaxFree=")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-XX:HeapMaxFree=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_max_free_ = size;
-    } else if (StartsWith(option, "-XX:NonMovingSpaceCapacity=")) {
-      size_t size = ParseMemoryOption(
-          option.substr(strlen("-XX:NonMovingSpaceCapacity=")).c_str(), 1024);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      heap_non_moving_space_capacity_ = size;
-    } else if (StartsWith(option, "-XX:HeapTargetUtilization=")) {
-      if (!ParseDouble(option, '=', 0.1, 0.9, &heap_target_utilization_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ForegroundHeapGrowthMultiplier=")) {
-      if (!ParseDouble(option, '=', 0.1, 10.0, &foreground_heap_growth_multiplier_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ParallelGCThreads=")) {
-      if (!ParseUnsignedInteger(option, '=', &parallel_gc_threads_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:ConcGCThreads=")) {
-      if (!ParseUnsignedInteger(option, '=', &conc_gc_threads_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xss")) {
-      size_t size = ParseMemoryOption(option.substr(strlen("-Xss")).c_str(), 1);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      stack_size_ = size;
-    } else if (StartsWith(option, "-XX:MaxSpinsBeforeThinLockInflation=")) {
-      if (!ParseUnsignedInteger(option, '=', &max_spins_before_thin_lock_inflation_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LongPauseLogThreshold=")) {
-      unsigned int value;
-      if (!ParseUnsignedInteger(option, '=', &value)) {
-        return false;
-      }
-      long_pause_log_threshold_ = MsToNs(value);
-    } else if (StartsWith(option, "-XX:LongGCLogThreshold=")) {
-      unsigned int value;
-      if (!ParseUnsignedInteger(option, '=', &value)) {
-        return false;
-      }
-      long_gc_log_threshold_ = MsToNs(value);
-    } else if (option == "-XX:DumpGCPerformanceOnShutdown") {
-      dump_gc_performance_on_shutdown_ = true;
-    } else if (option == "-XX:IgnoreMaxFootprint") {
-      ignore_max_footprint_ = true;
-    } else if (option == "-XX:LowMemoryMode") {
-      low_memory_mode_ = true;
-      // TODO Might want to turn off must_relocate here.
-    } else if (option == "-XX:UseTLAB") {
-      use_tlab_ = true;
-    } else if (option == "-XX:EnableHSpaceCompactForOOM") {
-      use_homogeneous_space_compaction_for_oom_ = true;
-    } else if (option == "-XX:DisableHSpaceCompactForOOM") {
-      use_homogeneous_space_compaction_for_oom_ = false;
-    } else if (StartsWith(option, "-XX:HspaceCompactForOOMMinIntervalMs=")) {
-      unsigned int value;
-      if (!ParseUnsignedInteger(option, '=', &value)) {
-        return false;
-      }
-      min_interval_homogeneous_space_compaction_by_oom_ = MsToNs(value);
-    } else if (StartsWith(option, "-D")) {
-      properties_.push_back(option.substr(strlen("-D")));
-    } else if (StartsWith(option, "-Xjnitrace:")) {
-      jni_trace_ = option.substr(strlen("-Xjnitrace:"));
-    } else if (option == "compilercallbacks") {
-      compiler_callbacks_ =
-          reinterpret_cast<CompilerCallbacks*>(const_cast<void*>(options[i].second));
-    } else if (option == "imageinstructionset") {
-      const char* isa_str = reinterpret_cast<const char*>(options[i].second);
-      image_isa_ = GetInstructionSetFromString(isa_str);
-      if (image_isa_ == kNone) {
-        Usage("%s is not a valid instruction set.", isa_str);
-        return false;
-      }
-    } else if (option == "-Xzygote") {
-      is_zygote_ = true;
-    } else if (StartsWith(option, "-Xpatchoat:")) {
-      if (!ParseStringAfterChar(option, ':', &patchoat_executable_)) {
-        return false;
-      }
-    } else if (option == "-Xrelocate") {
-      must_relocate_ = true;
-    } else if (option == "-Xnorelocate") {
-      must_relocate_ = false;
-    } else if (option == "-Xnodex2oat") {
-      dex2oat_enabled_ = false;
-    } else if (option == "-Xdex2oat") {
-      dex2oat_enabled_ = true;
-    } else if (option == "-Xnoimage-dex2oat") {
-      image_dex2oat_enabled_ = false;
-    } else if (option == "-Ximage-dex2oat") {
-      image_dex2oat_enabled_ = true;
-    } else if (option == "-Xint") {
-      interpreter_only_ = true;
-    } else if (StartsWith(option, "-Xgc:")) {
-      if (!ParseXGcOption(option)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LargeObjectSpace=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      if (substring == "disabled") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeDisabled;
-      } else if (substring == "freelist") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeFreeList;
-      } else if (substring == "map") {
-        large_object_space_type_ = gc::space::kLargeObjectSpaceTypeMap;
-      } else {
-        Usage("Unknown -XX:LargeObjectSpace= option %s\n", substring.c_str());
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:LargeObjectThreshold=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      size_t size = ParseMemoryOption(substring.c_str(), 1);
-      if (size == 0) {
-        Usage("Failed to parse memory option %s\n", option.c_str());
-        return false;
-      }
-      large_object_threshold_ = size;
-    } else if (StartsWith(option, "-XX:BackgroundGC=")) {
-      std::string substring;
-      if (!ParseStringAfterChar(option, '=', &substring)) {
-        return false;
-      }
-      // Special handling for HSpaceCompact since this is only valid as a background GC type.
-      if (substring == "HSpaceCompact") {
-        background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact;
-      } else {
-        gc::CollectorType collector_type = ParseCollectorType(substring);
-        if (collector_type != gc::kCollectorTypeNone) {
-          background_collector_type_ = collector_type;
-        } else {
-          Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str());
-          return false;
-        }
-      }
-    } else if (option == "-XX:+DisableExplicitGC") {
-      is_explicit_gc_disabled_ = true;
-    } else if (StartsWith(option, "-verbose:")) {
-      std::vector<std::string> verbose_options;
-      Split(option.substr(strlen("-verbose:")), ',', &verbose_options);
-      for (size_t j = 0; j < verbose_options.size(); ++j) {
-        if (verbose_options[j] == "class") {
-          gLogVerbosity.class_linker = true;
-        } else if (verbose_options[j] == "compiler") {
-          gLogVerbosity.compiler = true;
-        } else if (verbose_options[j] == "gc") {
-          gLogVerbosity.gc = true;
-        } else if (verbose_options[j] == "heap") {
-          gLogVerbosity.heap = true;
-        } else if (verbose_options[j] == "jdwp") {
-          gLogVerbosity.jdwp = true;
-        } else if (verbose_options[j] == "jni") {
-          gLogVerbosity.jni = true;
-        } else if (verbose_options[j] == "monitor") {
-          gLogVerbosity.monitor = true;
-        } else if (verbose_options[j] == "profiler") {
-          gLogVerbosity.profiler = true;
-        } else if (verbose_options[j] == "signals") {
-          gLogVerbosity.signals = true;
-        } else if (verbose_options[j] == "startup") {
-          gLogVerbosity.startup = true;
-        } else if (verbose_options[j] == "third-party-jni") {
-          gLogVerbosity.third_party_jni = true;
-        } else if (verbose_options[j] == "threads") {
-          gLogVerbosity.threads = true;
-        } else if (verbose_options[j] == "verifier") {
-          gLogVerbosity.verifier = true;
-        } else {
-          Usage("Unknown -verbose option %s\n", verbose_options[j].c_str());
-          return false;
-        }
-      }
-    } else if (StartsWith(option, "-Xlockprofthreshold:")) {
-      if (!ParseUnsignedInteger(option, ':', &lock_profiling_threshold_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xstacktracefile:")) {
-      if (!ParseStringAfterChar(option, ':', &stack_trace_file_)) {
-        return false;
-      }
-    } else if (option == "sensitiveThread") {
-      const void* hook = options[i].second;
-      hook_is_sensitive_thread_ = reinterpret_cast<bool (*)()>(const_cast<void*>(hook));
-    } else if (option == "vfprintf") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("vfprintf argument was NULL");
-        return false;
-      }
-      hook_vfprintf_ =
-          reinterpret_cast<int (*)(FILE *, const char*, va_list)>(const_cast<void*>(hook));
-    } else if (option == "exit") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("exit argument was NULL");
-        return false;
-      }
-      hook_exit_ = reinterpret_cast<void(*)(jint)>(const_cast<void*>(hook));
-    } else if (option == "abort") {
-      const void* hook = options[i].second;
-      if (hook == nullptr) {
-        Usage("abort was NULL\n");
-        return false;
-      }
-      hook_abort_ = reinterpret_cast<void(*)()>(const_cast<void*>(hook));
-    } else if (option == "-Xmethod-trace") {
-      method_trace_ = true;
-    } else if (StartsWith(option, "-Xmethod-trace-file:")) {
-      method_trace_file_ = option.substr(strlen("-Xmethod-trace-file:"));
-    } else if (StartsWith(option, "-Xmethod-trace-file-size:")) {
-      if (!ParseUnsignedInteger(option, ':', &method_trace_file_size_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile:threadcpuclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceThreadCpu);
-    } else if (option == "-Xprofile:wallclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceWall);
-    } else if (option == "-Xprofile:dualclock") {
-      Trace::SetDefaultClockSource(kTraceClockSourceDual);
-    } else if (option == "-Xenable-profiler") {
-      profiler_options_.enabled_ = true;
-    } else if (StartsWith(option, "-Xprofile-filename:")) {
-      if (!ParseStringAfterChar(option, ':', &profile_output_filename_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-period:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.period_s_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-duration:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.duration_s_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-interval:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.interval_us_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-backoff:")) {
-      if (!ParseDouble(option, ':', 1.0, 10.0, &profiler_options_.backoff_coefficient_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile-start-immediately") {
-      profiler_options_.start_immediately_ = true;
-    } else if (StartsWith(option, "-Xprofile-top-k-threshold:")) {
-      if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_threshold_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xprofile-top-k-change-threshold:")) {
-      if (!ParseDouble(option, ':', 0.0, 100.0, &profiler_options_.top_k_change_threshold_)) {
-        return false;
-      }
-    } else if (option == "-Xprofile-type:method") {
-      profiler_options_.profile_type_ = kProfilerMethod;
-    } else if (option == "-Xprofile-type:stack") {
-      profiler_options_.profile_type_ = kProfilerBoundedStack;
-    } else if (StartsWith(option, "-Xprofile-max-stack-depth:")) {
-      if (!ParseUnsignedInteger(option, ':', &profiler_options_.max_stack_depth_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-Xcompiler:")) {
-      if (!ParseStringAfterChar(option, ':', &compiler_executable_)) {
-        return false;
-      }
-    } else if (option == "-Xcompiler-option") {
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required compiler option for %s\n", option.c_str());
-        return false;
-      }
-      compiler_options_.push_back(options[i].first);
-    } else if (option == "-Ximage-compiler-option") {
-      i++;
-      if (i == options.size()) {
-        Usage("Missing required compiler option for %s\n", option.c_str());
-        return false;
-      }
-      image_compiler_options_.push_back(options[i].first);
-    } else if (StartsWith(option, "-Xverify:")) {
-      std::string verify_mode = option.substr(strlen("-Xverify:"));
-      if (verify_mode == "none") {
-        verify_ = false;
-      } else if (verify_mode == "remote" || verify_mode == "all") {
-        verify_ = true;
-      } else {
-        Usage("Unknown -Xverify option %s\n", verify_mode.c_str());
-        return false;
-      }
-    } else if (StartsWith(option, "-XX:NativeBridge=")) {
-      if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) {
-        return false;
-      }
-    } else if (StartsWith(option, "-ea") ||
-               StartsWith(option, "-da") ||
-               StartsWith(option, "-enableassertions") ||
-               StartsWith(option, "-disableassertions") ||
-               (option == "--runtime-arg") ||
-               (option == "-esa") ||
-               (option == "-dsa") ||
-               (option == "-enablesystemassertions") ||
-               (option == "-disablesystemassertions") ||
-               (option == "-Xrs") ||
-               StartsWith(option, "-Xint:") ||
-               StartsWith(option, "-Xdexopt:") ||
-               (option == "-Xnoquithandler") ||
-               StartsWith(option, "-Xjnigreflimit:") ||
-               (option == "-Xgenregmap") ||
-               (option == "-Xnogenregmap") ||
-               StartsWith(option, "-Xverifyopt:") ||
-               (option == "-Xcheckdexsum") ||
-               (option == "-Xincludeselectedop") ||
-               StartsWith(option, "-Xjitop:") ||
-               (option == "-Xincludeselectedmethod") ||
-               StartsWith(option, "-Xjitthreshold:") ||
-               StartsWith(option, "-Xjitcodecachesize:") ||
-               (option == "-Xjitblocking") ||
-               StartsWith(option, "-Xjitmethod:") ||
-               StartsWith(option, "-Xjitclass:") ||
-               StartsWith(option, "-Xjitoffset:") ||
-               StartsWith(option, "-Xjitconfig:") ||
-               (option == "-Xjitcheckcg") ||
-               (option == "-Xjitverbose") ||
-               (option == "-Xjitprofile") ||
-               (option == "-Xjitdisableopt") ||
-               (option == "-Xjitsuspendpoll") ||
-               StartsWith(option, "-XX:mainThreadStackSize=")) {
-      // Ignored for backwards compatibility.
-    } else if (!ignore_unrecognized) {
-      Usage("Unrecognized option %s\n", option.c_str());
+    } else if (parse_result.GetStatus() == CmdlineResult::kUnknown && !ignore_unrecognized) {
+      Usage("%s\n", parse_result.GetMessage().c_str());
       return false;
+    } else {
+      Usage("%s\n", parse_result.GetMessage().c_str());
+      Exit(0);
+    }
+
+    UNREACHABLE();
+    return false;
+  }
+
+  using M = RuntimeArgumentMap;
+  RuntimeArgumentMap args = parser->ReleaseArgumentsMap();
+
+  // -help, -showversion, etc.
+  if (args.Exists(M::Help)) {
+    Usage(nullptr);
+    return false;
+  } else if (args.Exists(M::ShowVersion)) {
+    UsageMessage(stdout, "ART version %s\n", Runtime::GetVersion());
+    Exit(0);
+  } else if (args.Exists(M::BootClassPath)) {
+    LOG(INFO) << "setting boot class path to " << *args.Get(M::BootClassPath);
+  }
+
+  // Set a default boot class path if we didn't get an explicit one via command line.
+  if (getenv("BOOTCLASSPATH") != nullptr) {
+    args.SetIfMissing(M::BootClassPath, std::string(getenv("BOOTCLASSPATH")));
+  }
+
+  // Set a default class path if we didn't get an explicit one via command line.
+  if (getenv("CLASSPATH") != nullptr) {
+    args.SetIfMissing(M::ClassPath, std::string(getenv("CLASSPATH")));
+  }
+
+  // Default to number of processors minus one since the main GC thread also does work.
+  args.SetIfMissing(M::ParallelGCThreads,
+                    static_cast<unsigned int>(sysconf(_SC_NPROCESSORS_CONF) - 1u));
+
+  // -Xverbose:
+  {
+    LogVerbosity *log_verbosity = args.Get(M::Verbose);
+    if (log_verbosity != nullptr) {
+      gLogVerbosity = *log_verbosity;
     }
   }
-  // If not set, background collector type defaults to homogeneous compaction.
-  // If foreground is GSS, use GSS as background collector.
-  // If not low memory mode, semispace otherwise.
-  if (background_collector_type_ == gc::kCollectorTypeNone) {
-    if (collector_type_ != gc::kCollectorTypeGSS) {
-      background_collector_type_ = low_memory_mode_ ?
-          gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
-    } else {
-      background_collector_type_ = collector_type_;
+
+  // -Xprofile:
+  Trace::SetDefaultClockSource(args.GetOrDefault(M::ProfileClock));
+
+  if (!ProcessSpecialOptions(options, &args, nullptr)) {
+      return false;
+  }
+
+  {
+    // If not set, background collector type defaults to homogeneous compaction.
+    // If foreground is GSS, use GSS as background collector.
+    // If not low memory mode, semispace otherwise.
+
+    gc::CollectorType background_collector_type_;
+    gc::CollectorType collector_type_ = (XGcOption{}).collector_type_;  // NOLINT [whitespace/braces] [5]
+    bool low_memory_mode_ = args.Exists(M::LowMemoryMode);
+
+    background_collector_type_ = args.GetOrDefault(M::BackgroundGc);
+    {
+      XGcOption* xgc = args.Get(M::GcOption);
+      if (xgc != nullptr && xgc->collector_type_ != gc::kCollectorTypeNone) {
+        collector_type_ = xgc->collector_type_;
+      }
+    }
+
+    if (background_collector_type_ == gc::kCollectorTypeNone) {
+      if (collector_type_ != gc::kCollectorTypeGSS) {
+        background_collector_type_ = low_memory_mode_ ?
+            gc::kCollectorTypeSS : gc::kCollectorTypeHomogeneousSpaceCompact;
+      } else {
+        background_collector_type_ = collector_type_;
+      }
+    }
+
+    args.Set(M::BackgroundGc, BackgroundGcOption { background_collector_type_ });
+    {
+      XGcOption* xgc = args.Get(M::GcOption);
+      if (xgc != nullptr) {
+        xgc->collector_type_ = collector_type_;
+        args.Set(M::GcOption, *xgc);
+      }
     }
   }
 
@@ -723,38 +485,45 @@
   std::string core_jar("/core-hostdex.jar");
   std::string core_libart_jar("/core-libart-hostdex.jar");
 #endif
-  size_t core_jar_pos = boot_class_path_string_.find(core_jar);
+  auto boot_class_path_string = args.GetOrDefault(M::BootClassPath);
+
+  size_t core_jar_pos = boot_class_path_string.find(core_jar);
   if (core_jar_pos != std::string::npos) {
-    boot_class_path_string_.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+    boot_class_path_string.replace(core_jar_pos, core_jar.size(), core_libart_jar);
+    args.Set(M::BootClassPath, boot_class_path_string);
   }
 
-  if (!boot_class_path_locations_string_.empty()) {
-    std::vector<std::string> files;
-    Split(boot_class_path_string_, ':', &files);
+  {
+    auto&& boot_class_path = args.GetOrDefault(M::BootClassPath);
+    auto&& boot_class_path_locations = args.GetOrDefault(M::BootClassPathLocations);
+    if (args.Exists(M::BootClassPathLocations)) {
+      size_t boot_class_path_count = ParseStringList<':'>::Split(boot_class_path).Size();
 
-    std::vector<std::string> locations;
-    Split(boot_class_path_locations_string_, ':', &locations);
-
-    if (files.size() != locations.size()) {
-      Usage("The number of boot class path files does not match"
-          " the number of boot class path locations given\n"
-          "  boot class path files     (%zu): %s\n"
-          "  boot class path locations (%zu): %s\n",
-          files.size(), boot_class_path_string_.c_str(),
-          locations.size(), boot_class_path_locations_string_.c_str());
-      return false;
+      if (boot_class_path_count != boot_class_path_locations.Size()) {
+        Usage("The number of boot class path files does not match"
+            " the number of boot class path locations given\n"
+            "  boot class path files     (%zu): %s\n"
+            "  boot class path locations (%zu): %s\n",
+            boot_class_path.size(), boot_class_path_string.c_str(),
+            boot_class_path_locations.Size(), boot_class_path_locations.Join().c_str());
+        return false;
+      }
     }
   }
 
-  if (compiler_callbacks_ == nullptr && image_.empty()) {
-    image_ += GetAndroidRoot();
-    image_ += "/framework/boot.art";
+  if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
+    std::string image = GetAndroidRoot();
+    image += "/framework/boot.art";
+    args.Set(M::Image, image);
   }
-  if (heap_growth_limit_ == 0) {
-    heap_growth_limit_ = heap_maximum_size_;
+
+  if (args.GetOrDefault(M::HeapGrowthLimit) == 0u) {  // 0 means no growth limit
+    args.Set(M::HeapGrowthLimit, args.GetOrDefault(M::MemoryMaximumSize));
   }
+
+  *runtime_options = std::move(args);
   return true;
-}  // NOLINT(readability/fn_size)
+}
 
 void ParsedOptions::Exit(int status) {
   hook_exit_(status);
@@ -831,7 +600,7 @@
   UsageMessage(stream, "  -Xgc:[no]presweepingverify\n");
   UsageMessage(stream, "  -Ximage:filename\n");
   UsageMessage(stream, "  -Xbootclasspath-locations:bootclasspath\n"
-      "     (override the dex locations of the -Xbootclasspath files)\n");
+                       "     (override the dex locations of the -Xbootclasspath files)\n");
   UsageMessage(stream, "  -XX:+DisableExplicitGC\n");
   UsageMessage(stream, "  -XX:ParallelGCThreads=integervalue\n");
   UsageMessage(stream, "  -XX:ConcGCThreads=integervalue\n");
@@ -907,73 +676,4 @@
   Exit((error) ? 1 : 0);
 }
 
-bool ParsedOptions::ParseStringAfterChar(const std::string& s, char c, std::string* parsed_value) {
-  std::string::size_type colon = s.find(c);
-  if (colon == std::string::npos) {
-    Usage("Missing char %c in option %s\n", c, s.c_str());
-    return false;
-  }
-  // Add one to remove the char we were trimming until.
-  *parsed_value = s.substr(colon + 1);
-  return true;
-}
-
-bool ParsedOptions::ParseInteger(const std::string& s, char after_char, int* parsed_value) {
-  std::string::size_type colon = s.find(after_char);
-  if (colon == std::string::npos) {
-    Usage("Missing char %c in option %s\n", after_char, s.c_str());
-    return false;
-  }
-  const char* begin = &s[colon + 1];
-  char* end;
-  size_t result = strtoul(begin, &end, 10);
-  if (begin == end || *end != '\0') {
-    Usage("Failed to parse integer from %s\n", s.c_str());
-    return false;
-  }
-  *parsed_value = result;
-  return true;
-}
-
-bool ParsedOptions::ParseUnsignedInteger(const std::string& s, char after_char,
-                                         unsigned int* parsed_value) {
-  int i;
-  if (!ParseInteger(s, after_char, &i)) {
-    return false;
-  }
-  if (i < 0) {
-    Usage("Negative value %d passed for unsigned option %s\n", i, s.c_str());
-    return false;
-  }
-  *parsed_value = i;
-  return true;
-}
-
-bool ParsedOptions::ParseDouble(const std::string& option, char after_char,
-                                double min, double max, double* parsed_value) {
-  std::string substring;
-  if (!ParseStringAfterChar(option, after_char, &substring)) {
-    return false;
-  }
-  bool sane_val = true;
-  double value;
-  if ((false)) {
-    // TODO: this doesn't seem to work on the emulator.  b/15114595
-    std::stringstream iss(substring);
-    iss >> value;
-    // Ensure that we have a value, there was no cruft after it and it satisfies a sensible range.
-    sane_val = iss.eof() && (value >= min) && (value <= max);
-  } else {
-    char* end = nullptr;
-    value = strtod(substring.c_str(), &end);
-    sane_val = *end == '\0' && value >= min && value <= max;
-  }
-  if (!sane_val) {
-    Usage("Invalid double value %s for option %s\n", substring.c_str(), option.c_str());
-    return false;
-  }
-  *parsed_value = value;
-  return true;
-}
-
 }  // namespace art
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index c7162b8..529dd5c 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -27,92 +27,44 @@
 #include "gc/space/large_object_space.h"
 #include "arch/instruction_set.h"
 #include "profiler_options.h"
+#include "runtime_options.h"
 
 namespace art {
 
 class CompilerCallbacks;
 class DexFile;
+struct RuntimeArgumentMap;
 
 typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
 
+template <typename TVariantMap,
+          template <typename TKeyValue> class TVariantMapKey>
+struct CmdlineParser;
+
 class ParsedOptions {
  public:
-  // returns null if problem parsing and ignore_unrecognized is false
-  static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized);
+  using RuntimeParser = CmdlineParser<RuntimeArgumentMap, RuntimeArgumentMap::Key>;
+  // Create a parser that can turn user-defined input into a RuntimeArgumentMap.
+  // This visibility is effectively for testing-only, and normal code does not
+  // need to create its own parser.
+  static std::unique_ptr<RuntimeParser> MakeParser(bool ignore_unrecognized);
 
-  std::string boot_class_path_string_;
-  std::string boot_class_path_locations_string_;
-  std::string class_path_string_;
-  std::string image_;
-  bool check_jni_;
-  bool force_copy_;
-  std::string jni_trace_;
-  std::string native_bridge_library_filename_;
-  CompilerCallbacks* compiler_callbacks_;
-  bool is_zygote_;
-  bool must_relocate_;
-  bool dex2oat_enabled_;
-  bool image_dex2oat_enabled_;
-  std::string patchoat_executable_;
-  bool interpreter_only_;
-  bool is_explicit_gc_disabled_;
-  bool use_tlab_;
-  bool verify_pre_gc_heap_;
-  bool verify_pre_sweeping_heap_;
-  bool verify_post_gc_heap_;
-  bool verify_pre_gc_rosalloc_;
-  bool verify_pre_sweeping_rosalloc_;
-  bool verify_post_gc_rosalloc_;
-  unsigned int long_pause_log_threshold_;
-  unsigned int long_gc_log_threshold_;
-  bool dump_gc_performance_on_shutdown_;
-  bool ignore_max_footprint_;
-  size_t heap_initial_size_;
-  size_t heap_maximum_size_;
-  size_t heap_growth_limit_;
-  size_t heap_min_free_;
-  size_t heap_max_free_;
-  size_t heap_non_moving_space_capacity_;
-  gc::space::LargeObjectSpaceType large_object_space_type_;
-  size_t large_object_threshold_;
-  double heap_target_utilization_;
-  double foreground_heap_growth_multiplier_;
-  unsigned int parallel_gc_threads_;
-  unsigned int conc_gc_threads_;
-  gc::CollectorType collector_type_;
-  gc::CollectorType background_collector_type_;
-  size_t stack_size_;
-  unsigned int max_spins_before_thin_lock_inflation_;
-  bool low_memory_mode_;
-  unsigned int lock_profiling_threshold_;
-  std::string stack_trace_file_;
-  bool method_trace_;
-  std::string method_trace_file_;
-  unsigned int method_trace_file_size_;
+  // returns true if parsing succeeds, and stores the resulting options into runtime_options
+  static ParsedOptions* Create(const RuntimeOptions& options, bool ignore_unrecognized,
+                               RuntimeArgumentMap* runtime_options);
+
   bool (*hook_is_sensitive_thread_)();
   jint (*hook_vfprintf_)(FILE* stream, const char* format, va_list ap);
   void (*hook_exit_)(jint status);
   void (*hook_abort_)();
-  std::vector<std::string> properties_;
-  std::string compiler_executable_;
-  std::vector<std::string> compiler_options_;
-  std::vector<std::string> image_compiler_options_;
-  ProfilerOptions profiler_options_;
-  std::string profile_output_filename_;
-  TraceClockSource profile_clock_source_;
-  bool verify_;
-  InstructionSet image_isa_;
-
-  // Whether or not we use homogeneous space compaction to avoid OOM errors. If enabled,
-  // the heap will attempt to create an extra space which enables compacting from a malloc space to
-  // another malloc space when we are about to throw OOM.
-  bool use_homogeneous_space_compaction_for_oom_;
-  // Minimal interval allowed between two homogeneous space compactions caused by OOM.
-  uint64_t min_interval_homogeneous_space_compaction_by_oom_;
 
  private:
   ParsedOptions();
 
+  bool ProcessSpecialOptions(const RuntimeOptions& options,
+                             RuntimeArgumentMap* runtime_options,
+                             std::vector<std::string>* out_options);
+
   void Usage(const char* fmt, ...);
   void UsageMessage(FILE* stream, const char* fmt, ...);
   void UsageMessageV(FILE* stream, const char* fmt, va_list ap);
@@ -120,13 +72,8 @@
   void Exit(int status);
   void Abort();
 
-  bool Parse(const RuntimeOptions& options,  bool ignore_unrecognized);
-  bool ParseXGcOption(const std::string& option);
-  bool ParseStringAfterChar(const std::string& option, char after_char, std::string* parsed_value);
-  bool ParseInteger(const std::string& option, char after_char, int* parsed_value);
-  bool ParseUnsignedInteger(const std::string& option, char after_char, unsigned int* parsed_value);
-  bool ParseDouble(const std::string& option, char after_char, double min, double max,
-                   double* parsed_value);
+  bool Parse(const RuntimeOptions& options,  bool ignore_unrecognized,
+             RuntimeArgumentMap* runtime_options);
 };
 
 }  // namespace art
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 61481b1..f68b632 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -22,7 +22,7 @@
 
 namespace art {
 
-class ParsedOptionsTest : public CommonRuntimeTest {};
+class ParsedOptionsTest : public ::testing::Test {};
 
 TEST_F(ParsedOptionsTest, ParsedOptions) {
   void* test_vfprintf = reinterpret_cast<void*>(0xa);
@@ -30,7 +30,7 @@
   void* test_exit = reinterpret_cast<void*>(0xc);
   void* null = reinterpret_cast<void*>(NULL);
 
-  std::string lib_core(GetLibCoreDexFileName());
+  std::string lib_core(CommonRuntimeTest::GetLibCoreDexFileName());
 
   std::string boot_class_path;
   boot_class_path += "-Xbootclasspath:";
@@ -54,20 +54,28 @@
   options.push_back(std::make_pair("vfprintf", test_vfprintf));
   options.push_back(std::make_pair("abort", test_abort));
   options.push_back(std::make_pair("exit", test_exit));
-  std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false));
-  ASSERT_TRUE(parsed.get() != NULL);
 
-  EXPECT_EQ(lib_core, parsed->boot_class_path_string_);
-  EXPECT_EQ(lib_core, parsed->class_path_string_);
-  EXPECT_EQ(std::string("boot_image"), parsed->image_);
-  EXPECT_EQ(true, parsed->check_jni_);
-  EXPECT_EQ(2048U, parsed->heap_initial_size_);
-  EXPECT_EQ(4 * KB, parsed->heap_maximum_size_);
-  EXPECT_EQ(1 * MB, parsed->stack_size_);
-  EXPECT_DOUBLE_EQ(0.75, parsed->heap_target_utilization_);
-  EXPECT_TRUE(test_vfprintf == parsed->hook_vfprintf_);
-  EXPECT_TRUE(test_exit == parsed->hook_exit_);
-  EXPECT_TRUE(test_abort == parsed->hook_abort_);
+  RuntimeArgumentMap map;
+  std::unique_ptr<ParsedOptions> parsed(ParsedOptions::Create(options, false, &map));
+  ASSERT_TRUE(parsed.get() != NULL);
+  ASSERT_NE(0u, map.Size());
+
+  using Opt = RuntimeArgumentMap;
+
+#define EXPECT_PARSED_EQ(expected, actual_key) EXPECT_EQ(expected, map.GetOrDefault(actual_key))
+#define EXPECT_PARSED_EXISTS(actual_key) EXPECT_TRUE(map.Exists(actual_key))
+
+  EXPECT_PARSED_EQ(lib_core, Opt::BootClassPath);
+  EXPECT_PARSED_EQ(lib_core, Opt::ClassPath);
+  EXPECT_PARSED_EQ(std::string("boot_image"), Opt::Image);
+  EXPECT_PARSED_EXISTS(Opt::CheckJni);
+  EXPECT_PARSED_EQ(2048U, Opt::MemoryInitialSize);
+  EXPECT_PARSED_EQ(4 * KB, Opt::MemoryMaximumSize);
+  EXPECT_PARSED_EQ(1 * MB, Opt::StackSize);
+  EXPECT_DOUBLE_EQ(0.75, map.GetOrDefault(Opt::HeapTargetUtilization));
+  EXPECT_TRUE(test_vfprintf == map.GetOrDefault(Opt::HookVfprintf));
+  EXPECT_TRUE(test_exit == map.GetOrDefault(Opt::HookExit));
+  EXPECT_TRUE(test_abort == map.GetOrDefault(Opt::HookAbort));
   EXPECT_TRUE(VLOG_IS_ON(class_linker));
   EXPECT_FALSE(VLOG_IS_ON(compiler));
   EXPECT_FALSE(VLOG_IS_ON(heap));
@@ -78,9 +86,11 @@
   EXPECT_FALSE(VLOG_IS_ON(startup));
   EXPECT_FALSE(VLOG_IS_ON(third_party_jni));
   EXPECT_FALSE(VLOG_IS_ON(threads));
-  ASSERT_EQ(2U, parsed->properties_.size());
-  EXPECT_EQ("foo=bar", parsed->properties_[0]);
-  EXPECT_EQ("baz=qux", parsed->properties_[1]);
+
+  auto&& properties_list = map.GetOrDefault(Opt::PropertiesList);
+  ASSERT_EQ(2U, properties_list.size());
+  EXPECT_EQ("foo=bar", properties_list[0]);
+  EXPECT_EQ("baz=qux", properties_list[1]);
 }
 
 }  // namespace art
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 3acac3a..4bb1741 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -29,7 +29,7 @@
 #include <cstdio>
 #include <cstdlib>
 #include <limits>
-#include <memory>
+#include <memory_representation.h>
 #include <vector>
 #include <fcntl.h>
 
@@ -106,6 +106,8 @@
 #include "profiler.h"
 #include "quick/quick_method_frame_info.h"
 #include "reflection.h"
+#include "runtime_options.h"
+#include "ScopedLocalRef.h"
 #include "scoped_thread_state_change.h"
 #include "sigchain.h"
 #include "signal_catcher.h"
@@ -711,8 +713,11 @@
 
   MemMap::Init();
 
-  std::unique_ptr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
-  if (options.get() == nullptr) {
+  using Opt = RuntimeArgumentMap;
+  RuntimeArgumentMap runtime_options;
+  std::unique_ptr<ParsedOptions> parsed_options(
+      ParsedOptions::Create(raw_options, ignore_unrecognized, &runtime_options));
+  if (parsed_options.get() == nullptr) {
     LOG(ERROR) << "Failed to parse options";
     return false;
   }
@@ -720,76 +725,79 @@
 
   QuasiAtomic::Startup();
 
-  Monitor::Init(options->lock_profiling_threshold_, options->hook_is_sensitive_thread_);
+  Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),
+                runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));
 
-  boot_class_path_string_ = options->boot_class_path_string_;
-  class_path_string_ = options->class_path_string_;
-  properties_ = options->properties_;
+  boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);
+  class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);
+  properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);
 
-  compiler_callbacks_ = options->compiler_callbacks_;
-  patchoat_executable_ = options->patchoat_executable_;
-  must_relocate_ = options->must_relocate_;
-  is_zygote_ = options->is_zygote_;
-  is_explicit_gc_disabled_ = options->is_explicit_gc_disabled_;
-  dex2oat_enabled_ = options->dex2oat_enabled_;
-  image_dex2oat_enabled_ = options->image_dex2oat_enabled_;
+  compiler_callbacks_ = runtime_options.GetOrDefault(Opt::CompilerCallbacksPtr);
+  patchoat_executable_ = runtime_options.ReleaseOrDefault(Opt::PatchOat);
+  must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate);
+  is_zygote_ = runtime_options.Exists(Opt::Zygote);
+  is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
+  dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);
+  image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
 
-  vfprintf_ = options->hook_vfprintf_;
-  exit_ = options->hook_exit_;
-  abort_ = options->hook_abort_;
+  vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);
+  exit_ = runtime_options.GetOrDefault(Opt::HookExit);
+  abort_ = runtime_options.GetOrDefault(Opt::HookAbort);
 
-  default_stack_size_ = options->stack_size_;
-  stack_trace_file_ = options->stack_trace_file_;
+  default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize);
+  stack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile);
 
-  compiler_executable_ = options->compiler_executable_;
-  compiler_options_ = options->compiler_options_;
-  image_compiler_options_ = options->image_compiler_options_;
-  image_location_ = options->image_;
+  compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);
+  compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
+  image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);
+  image_location_ = runtime_options.GetOrDefault(Opt::Image);
 
-  max_spins_before_thin_lock_inflation_ = options->max_spins_before_thin_lock_inflation_;
+  max_spins_before_thin_lock_inflation_ =
+      runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);
 
   monitor_list_ = new MonitorList;
   monitor_pool_ = MonitorPool::Create();
   thread_list_ = new ThreadList;
   intern_table_ = new InternTable;
 
-  verify_ = options->verify_;
+  verify_ = runtime_options.GetOrDefault(Opt::Verify);
 
-  if (options->interpreter_only_) {
+  if (runtime_options.Exists(Opt::Interpret)) {
     GetInstrumentation()->ForceInterpretOnly();
   }
 
-  heap_ = new gc::Heap(options->heap_initial_size_,
-                       options->heap_growth_limit_,
-                       options->heap_min_free_,
-                       options->heap_max_free_,
-                       options->heap_target_utilization_,
-                       options->foreground_heap_growth_multiplier_,
-                       options->heap_maximum_size_,
-                       options->heap_non_moving_space_capacity_,
-                       options->image_,
-                       options->image_isa_,
-                       options->collector_type_,
-                       options->background_collector_type_,
-                       options->large_object_space_type_,
-                       options->large_object_threshold_,
-                       options->parallel_gc_threads_,
-                       options->conc_gc_threads_,
-                       options->low_memory_mode_,
-                       options->long_pause_log_threshold_,
-                       options->long_gc_log_threshold_,
-                       options->ignore_max_footprint_,
-                       options->use_tlab_,
-                       options->verify_pre_gc_heap_,
-                       options->verify_pre_sweeping_heap_,
-                       options->verify_post_gc_heap_,
-                       options->verify_pre_gc_rosalloc_,
-                       options->verify_pre_sweeping_rosalloc_,
-                       options->verify_post_gc_rosalloc_,
-                       options->use_homogeneous_space_compaction_for_oom_,
-                       options->min_interval_homogeneous_space_compaction_by_oom_);
+  XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
+  heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
+                       runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
+                       runtime_options.GetOrDefault(Opt::HeapMinFree),
+                       runtime_options.GetOrDefault(Opt::HeapMaxFree),
+                       runtime_options.GetOrDefault(Opt::HeapTargetUtilization),
+                       runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier),
+                       runtime_options.GetOrDefault(Opt::MemoryMaximumSize),
+                       runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),
+                       runtime_options.GetOrDefault(Opt::Image),
+                       runtime_options.GetOrDefault(Opt::ImageInstructionSet),
+                       xgc_option.collector_type_,
+                       runtime_options.GetOrDefault(Opt::BackgroundGc),
+                       runtime_options.GetOrDefault(Opt::LargeObjectSpace),
+                       runtime_options.GetOrDefault(Opt::LargeObjectThreshold),
+                       runtime_options.GetOrDefault(Opt::ParallelGCThreads),
+                       runtime_options.GetOrDefault(Opt::ConcGCThreads),
+                       runtime_options.Exists(Opt::LowMemoryMode),
+                       runtime_options.GetOrDefault(Opt::LongPauseLogThreshold),
+                       runtime_options.GetOrDefault(Opt::LongGCLogThreshold),
+                       runtime_options.Exists(Opt::IgnoreMaxFootprint),
+                       runtime_options.Exists(Opt::UseTLAB),
+                       xgc_option.verify_pre_gc_heap_,
+                       xgc_option.verify_pre_sweeping_heap_,
+                       xgc_option.verify_post_gc_heap_,
+                       xgc_option.verify_pre_gc_rosalloc_,
+                       xgc_option.verify_pre_sweeping_rosalloc_,
+                       xgc_option.verify_post_gc_rosalloc_,
+                       runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),
+                       runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));
 
-  dump_gc_performance_on_shutdown_ = options->dump_gc_performance_on_shutdown_;
+  dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);
 
   BlockSignals();
   InitPlatformSignalHandlers();
@@ -840,7 +848,7 @@
     }
   }
 
-  java_vm_ = new JavaVMExt(this, options.get());
+  java_vm_ = new JavaVMExt(this, runtime_options);
 
   Thread::Startup();
 
@@ -879,16 +887,20 @@
     Split(boot_class_path_string_, ':', &dex_filenames);
 
     std::vector<std::string> dex_locations;
-    if (options->boot_class_path_locations_string_.empty()) {
+    if (!runtime_options.Exists(Opt::BootClassPathLocations)) {
       dex_locations = dex_filenames;
     } else {
-      Split(options->boot_class_path_locations_string_, ':', &dex_locations);
+      dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);
       CHECK_EQ(dex_filenames.size(), dex_locations.size());
     }
 
     std::vector<std::unique_ptr<const DexFile>> boot_class_path;
-    OpenDexFiles(dex_filenames, dex_locations, options->image_, &boot_class_path);
+    OpenDexFiles(dex_filenames,
+                 dex_locations,
+                 runtime_options.GetOrDefault(Opt::Image),
+                 &boot_class_path);
     class_linker_->InitWithoutImage(std::move(boot_class_path));
+
     // TODO: Should we move the following to InitWithoutImage?
     SetInstructionSet(kRuntimeISA);
     for (int i = 0; i < Runtime::kLastCalleeSaveType; i++) {
@@ -907,20 +919,42 @@
 
   verifier::MethodVerifier::Init();
 
-  method_trace_ = options->method_trace_;
-  method_trace_file_ = options->method_trace_file_;
-  method_trace_file_size_ = options->method_trace_file_size_;
+  method_trace_ = runtime_options.Exists(Opt::MethodTrace);
+  method_trace_file_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFile);
+  method_trace_file_size_ = runtime_options.ReleaseOrDefault(Opt::MethodTraceFileSize);
 
-  profile_output_filename_ = options->profile_output_filename_;
-  profiler_options_ = options->profiler_options_;
+  {
+    auto&& profiler_options = runtime_options.ReleaseOrDefault(Opt::ProfilerOpts);
+    profile_output_filename_ = profiler_options.output_file_name_;
+
+    // TODO: Don't do this, just change ProfilerOptions to include the output file name?
+    ProfilerOptions other_options(
+        profiler_options.enabled_,
+        profiler_options.period_s_,
+        profiler_options.duration_s_,
+        profiler_options.interval_us_,
+        profiler_options.backoff_coefficient_,
+        profiler_options.start_immediately_,
+        profiler_options.top_k_threshold_,
+        profiler_options.top_k_change_threshold_,
+        profiler_options.profile_type_,
+        profiler_options.max_stack_depth_);
+
+    profiler_options_ = other_options;
+  }
 
   // TODO: move this to just be an Trace::Start argument
-  Trace::SetDefaultClockSource(options->profile_clock_source_);
+  Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
 
-  if (options->method_trace_) {
+  if (method_trace_) {
     ScopedThreadStateChange tsc(self, kWaitingForMethodTracingStart);
-    Trace::Start(options->method_trace_file_.c_str(), -1, options->method_trace_file_size_, 0,
-                 false, false, 0);
+    Trace::Start(method_trace_file_.c_str(),
+                 -1,
+                 static_cast<int>(method_trace_file_size_),
+                 0,
+                 false,
+                 false,
+                 0);
   }
 
   // Pre-allocate an OutOfMemoryError for the double-OOME case.
@@ -964,7 +998,10 @@
   // Runtime::Start():
   //   DidForkFromZygote(kInitialize) -> try to initialize any native bridge given.
   //   No-op wrt native bridge.
-  is_native_bridge_loaded_ = LoadNativeBridge(options->native_bridge_library_filename_);
+  {
+    std::string native_bridge_file_name = runtime_options.ReleaseOrDefault(Opt::NativeBridge);
+    is_native_bridge_loaded_ = LoadNativeBridge(native_bridge_file_name);
+  }
 
   VLOG(startup) << "Runtime::Init exiting";
   return true;
diff --git a/runtime/runtime_options.cc b/runtime/runtime_options.cc
new file mode 100644
index 0000000..c54461e
--- /dev/null
+++ b/runtime/runtime_options.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "runtime_options.h"
+
+#include "gc/heap.h"
+#include "monitor.h"
+#include "runtime.h"
+#include "trace.h"
+#include "utils.h"
+#include "debugger.h"
+
+namespace art {
+
+// Specify storage for the RuntimeOptions keys.
+
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) const RuntimeArgumentMap::Key<Type> RuntimeArgumentMap::Name {__VA_ARGS__};  // NOLINT [readability/braces] [4]
+#include "runtime_options.def"
+
+}  // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
new file mode 100644
index 0000000..022a2a5
--- /dev/null
+++ b/runtime/runtime_options.def
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RUNTIME_OPTIONS_KEY
+#error "Please #define RUNTIME_OPTIONS_KEY before #including this file"
+#define RUNTIME_OPTIONS_KEY(...)  // Don't display errors in this file in IDEs.
+#endif
+
+// This file defines the list of keys for RuntimeOptions.
+// These can be used with RuntimeOptions.Get/Set/etc, for example:
+//         RuntimeOptions opt; bool* dex2oat_enabled = opt.Get(RuntimeOptions::Dex2Oat);
+//
+// Column Descriptions:
+//                   <<Type>>             <<Key Name>>                  <<Default Value>>
+//
+// Default values are only used by Map::GetOrDefault(K<T>).
+// If a default value is omitted here, T{} is used as the default value, which is
+// almost-always the value of the type as if it was memset to all 0.
+//
+
+// Parse-able keys from the command line.
+RUNTIME_OPTIONS_KEY (Unit,                Zygote)
+RUNTIME_OPTIONS_KEY (Unit,                Help)
+RUNTIME_OPTIONS_KEY (Unit,                ShowVersion)
+RUNTIME_OPTIONS_KEY (std::string,         BootClassPath)
+RUNTIME_OPTIONS_KEY (ParseStringList<':'>,BootClassPathLocations)  // std::vector<std::string>
+RUNTIME_OPTIONS_KEY (std::string,         ClassPath)
+RUNTIME_OPTIONS_KEY (std::string,         Image)
+RUNTIME_OPTIONS_KEY (Unit,                CheckJni)
+RUNTIME_OPTIONS_KEY (Unit,                JniOptsForceCopy)
+RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions,   JdwpOptions)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryMaximumSize,              gc::Heap::kDefaultMaximumSize)  // -Xmx
+RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryInitialSize,              gc::Heap::kDefaultInitialSize)  // -Xms
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapGrowthLimit)                // Default is 0 for unlimited
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMinFree,                    gc::Heap::kDefaultMinFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapMaxFree,                    gc::Heap::kDefaultMaxFree)
+RUNTIME_OPTIONS_KEY (MemoryKiB,           NonMovingSpaceCapacity,         gc::Heap::kDefaultNonMovingSpaceCapacity)
+RUNTIME_OPTIONS_KEY (double,              HeapTargetUtilization,          gc::Heap::kDefaultTargetUtilization)
+RUNTIME_OPTIONS_KEY (double,              ForegroundHeapGrowthMultiplier, gc::Heap::kDefaultHeapGrowthMultiplier)
+RUNTIME_OPTIONS_KEY (unsigned int,        ParallelGCThreads,              1u)
+RUNTIME_OPTIONS_KEY (unsigned int,        ConcGCThreads)
+RUNTIME_OPTIONS_KEY (Memory<1>,           StackSize)  // -Xss
+RUNTIME_OPTIONS_KEY (unsigned int,        MaxSpinsBeforeThinLockInflation,Monitor::kDefaultMaxSpinsBeforeThinLockInflation)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          LongPauseLogThreshold,          gc::Heap::kDefaultLongPauseLogThreshold)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          LongGCLogThreshold,             gc::Heap::kDefaultLongGCLogThreshold)
+RUNTIME_OPTIONS_KEY (Unit,                DumpGCPerformanceOnShutdown)
+RUNTIME_OPTIONS_KEY (Unit,                IgnoreMaxFootprint)
+RUNTIME_OPTIONS_KEY (Unit,                LowMemoryMode)
+RUNTIME_OPTIONS_KEY (Unit,                UseTLAB)
+RUNTIME_OPTIONS_KEY (bool,                EnableHSpaceCompactForOOM,      true)
+RUNTIME_OPTIONS_KEY (MillisecondsToNanoseconds, \
+                                          HSpaceCompactForOOMMinIntervalsMs,\
+                                                                          MsToNs(100 * 1000))  // 100s
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          PropertiesList)  // -D<whatever> -D<whatever> ...
+RUNTIME_OPTIONS_KEY (std::string,         JniTrace)
+RUNTIME_OPTIONS_KEY (std::string,         PatchOat)
+RUNTIME_OPTIONS_KEY (bool,                Relocate,                       kDefaultMustRelocate)
+RUNTIME_OPTIONS_KEY (bool,                Dex2Oat,                        true)
+RUNTIME_OPTIONS_KEY (bool,                ImageDex2Oat,                   true)
+                                                        // kPoisonHeapReferences currently works with
+                                                        // the interpreter only.
+                                                        // TODO: make it work with the compiler.
+RUNTIME_OPTIONS_KEY (bool,                Interpret,                      (kPoisonHeapReferences || kUseReadBarrier)) // -Xint
+                                                        // Disable the compiler for CC (for now).
+RUNTIME_OPTIONS_KEY (XGcOption,           GcOption)  // -Xgc:
+RUNTIME_OPTIONS_KEY (gc::space::LargeObjectSpaceType, \
+                                          LargeObjectSpace,               gc::Heap::kDefaultLargeObjectSpaceType)
+RUNTIME_OPTIONS_KEY (Memory<1>,           LargeObjectThreshold,           gc::Heap::kDefaultLargeObjectThreshold)
+RUNTIME_OPTIONS_KEY (BackgroundGcOption,  BackgroundGc)
+
+RUNTIME_OPTIONS_KEY (Unit,                DisableExplicitGC)
+RUNTIME_OPTIONS_KEY (LogVerbosity,        Verbose)
+RUNTIME_OPTIONS_KEY (unsigned int,        LockProfThreshold)
+RUNTIME_OPTIONS_KEY (std::string,         StackTraceFile)
+RUNTIME_OPTIONS_KEY (Unit,                MethodTrace)
+RUNTIME_OPTIONS_KEY (std::string,         MethodTraceFile,                "/data/method-trace-file.bin")
+RUNTIME_OPTIONS_KEY (unsigned int,        MethodTraceFileSize,            10 * MB)
+RUNTIME_OPTIONS_KEY (TraceClockSource,    ProfileClock,                   kDefaultTraceClockSource)  // -Xprofile:
+RUNTIME_OPTIONS_KEY (TestProfilerOptions, ProfilerOpts)  // -Xenable-profiler, -Xprofile-*
+RUNTIME_OPTIONS_KEY (std::string,         Compiler)
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          CompilerOptions)  // -Xcompiler-option ...
+RUNTIME_OPTIONS_KEY (std::vector<std::string>, \
+                                          ImageCompilerOptions)  // -Ximage-compiler-option ...
+RUNTIME_OPTIONS_KEY (bool,                Verify,                         true)
+RUNTIME_OPTIONS_KEY (std::string,         NativeBridge)
+
+// Not parse-able from command line, but can be provided explicitly.
+RUNTIME_OPTIONS_KEY (const std::vector<const DexFile*>*, \
+                                          BootClassPathDexList)  // TODO: make unique_ptr
+RUNTIME_OPTIONS_KEY (InstructionSet,      ImageInstructionSet,            kRuntimeISA)
+RUNTIME_OPTIONS_KEY (CompilerCallbacks*,  CompilerCallbacksPtr)  // TDOO: make unique_ptr
+RUNTIME_OPTIONS_KEY (bool (*)(),          HookIsSensitiveThread)
+RUNTIME_OPTIONS_KEY (int32_t (*)(FILE* stream, const char* format, va_list ap), \
+                                          HookVfprintf,                   vfprintf)
+RUNTIME_OPTIONS_KEY (void (*)(int32_t status), \
+                                          HookExit,                       exit)
+                                                                          // We don't call abort(3) by default; see
+                                                                          // Runtime::Abort.
+RUNTIME_OPTIONS_KEY (void (*)(),          HookAbort,                      nullptr)
+
+
+#undef RUNTIME_OPTIONS_KEY
diff --git a/runtime/runtime_options.h b/runtime/runtime_options.h
new file mode 100644
index 0000000..ebd52d7
--- /dev/null
+++ b/runtime/runtime_options.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_OPTIONS_H_
+#define ART_RUNTIME_RUNTIME_OPTIONS_H_
+
+#include "runtime/base/variant_map.h"
+#include "cmdline/cmdline_types.h"  // TODO: don't need to include this file here
+
+// Map keys
+#include <vector>
+#include <string>
+#include "runtime/base/logging.h"
+#include "cmdline/unit.h"
+#include "jdwp/jdwp.h"
+#include "gc/collector_type.h"
+#include "gc/space/large_object_space.h"
+#include "profiler_options.h"
+#include "arch/instruction_set.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+namespace art {
+
+class CompilerCallbacks;
+class DexFile;
+struct XGcOption;
+struct BackgroundGcOption;
+struct TestProfilerOptions;
+
+#define DECLARE_KEY(Type, Name) static const Key<Type> Name
+
+  // Define a key that is usable with a RuntimeArgumentMap.
+  // This key will *not* work with other subtypes of VariantMap.
+  template <typename TValue>
+  struct RuntimeArgumentMapKey : VariantMapKey<TValue> {
+    RuntimeArgumentMapKey() {}
+    explicit RuntimeArgumentMapKey(TValue default_value)
+      : VariantMapKey<TValue>(std::move(default_value)) {}
+    // Don't ODR-use constexpr default values, which means that Struct::Fields
+    // that are declared 'static constexpr T Name = Value' don't need to have a matching definition.
+  };
+
+  // Defines a type-safe heterogeneous key->value map.
+  // Use the VariantMap interface to look up or to store a RuntimeArgumentMapKey,Value pair.
+  //
+  // Example:
+  //    auto map = RuntimeArgumentMap();
+  //    map.Set(RuntimeArgumentMap::HeapTargetUtilization, 5.0);
+  //    double *target_utilization = map.Get(RuntimeArgumentMap);
+  //
+  struct RuntimeArgumentMap : VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey> {
+    // This 'using' line is necessary to inherit the variadic constructor.
+    using VariantMap<RuntimeArgumentMap, RuntimeArgumentMapKey>::VariantMap;
+
+    // Make the next many usages of Key slightly shorter to type.
+    template <typename TValue>
+    using Key = RuntimeArgumentMapKey<TValue>;
+
+    // List of key declarations, shorthand for 'static const Key<T> Name'
+#define RUNTIME_OPTIONS_KEY(Type, Name, ...) static const Key<Type> Name;
+#include "runtime_options.def"
+  };
+
+#undef DECLARE_KEY
+
+  // using RuntimeOptions = RuntimeArgumentMap;
+}  // namespace art
+
+#endif  // ART_RUNTIME_RUNTIME_OPTIONS_H_
diff --git a/runtime/trace.cc b/runtime/trace.cc
index 5066e03..0950abeb 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -153,30 +153,30 @@
 #if defined(__linux__)
   default_clock_source_ = clock_source;
 #else
-  if (clock_source != kTraceClockSourceWall) {
+  if (clock_source != TraceClockSource::kWall) {
     LOG(WARNING) << "Ignoring tracing request to use CPU time.";
   }
 #endif
 }
 
 static uint16_t GetTraceVersion(TraceClockSource clock_source) {
-  return (clock_source == kTraceClockSourceDual) ? kTraceVersionDualClock
+  return (clock_source == TraceClockSource::kDual) ? kTraceVersionDualClock
                                                     : kTraceVersionSingleClock;
 }
 
 static uint16_t GetRecordSize(TraceClockSource clock_source) {
-  return (clock_source == kTraceClockSourceDual) ? kTraceRecordSizeDualClock
+  return (clock_source == TraceClockSource::kDual) ? kTraceRecordSizeDualClock
                                                     : kTraceRecordSizeSingleClock;
 }
 
 bool Trace::UseThreadCpuClock() {
-  return (clock_source_ == kTraceClockSourceThreadCpu) ||
-      (clock_source_ == kTraceClockSourceDual);
+  return (clock_source_ == TraceClockSource::kThreadCpu) ||
+      (clock_source_ == TraceClockSource::kDual);
 }
 
 bool Trace::UseWallClock() {
-  return (clock_source_ == kTraceClockSourceWall) ||
-      (clock_source_ == kTraceClockSourceDual);
+  return (clock_source_ == TraceClockSource::kWall) ||
+      (clock_source_ == TraceClockSource::kDual);
 }
 
 void Trace::MeasureClockOverhead() {
diff --git a/runtime/utils.h b/runtime/utils.h
index b5413e7..1c2576c 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -549,6 +549,13 @@
 template <typename T>
 using UniqueCPtr = std::unique_ptr<T, FreeDelete>;
 
+// C++14 from-the-future import (std::make_unique)
+// Invoke the constructor of 'T' with the provided args, and wrap the result in a unique ptr.
+template <typename T, typename ... Args>
+std::unique_ptr<T> MakeUnique(Args&& ... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_UTILS_H_