This revision was automatically updated to reflect the committed changes.
Closed by commit rL322174: Add Utility/Environment class for handling... 
environments (authored by labath, committed by ).
Herald added a subscriber: llvm-commits.

Changed prior to commit:
  https://reviews.llvm.org/D41359?vs=127384&id=129239#toc

Repository:
  rL LLVM

https://reviews.llvm.org/D41359

Files:
  lldb/trunk/include/lldb/API/SBLaunchInfo.h
  lldb/trunk/include/lldb/Host/Host.h
  lldb/trunk/include/lldb/Interpreter/Args.h
  lldb/trunk/include/lldb/Target/Platform.h
  lldb/trunk/include/lldb/Target/ProcessInfo.h
  lldb/trunk/include/lldb/Target/Target.h
  lldb/trunk/include/lldb/Utility/Environment.h
  
lldb/trunk/packages/Python/lldbsuite/test/python_api/sblaunchinfo/TestSBLaunchInfo.py
  lldb/trunk/source/API/SBLaunchInfo.cpp
  lldb/trunk/source/API/SBPlatform.cpp
  lldb/trunk/source/API/SBProcess.cpp
  lldb/trunk/source/API/SBTarget.cpp
  lldb/trunk/source/Commands/CommandObjectProcess.cpp
  lldb/trunk/source/Host/freebsd/Host.cpp
  lldb/trunk/source/Host/linux/Host.cpp
  lldb/trunk/source/Host/macosx/Host.mm
  lldb/trunk/source/Host/netbsd/Host.cpp
  lldb/trunk/source/Host/openbsd/Host.cpp
  lldb/trunk/source/Host/posix/ProcessLauncherPosixFork.cpp
  lldb/trunk/source/Host/windows/Host.cpp
  lldb/trunk/source/Interpreter/Args.cpp
  lldb/trunk/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
  
lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm
  lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
  lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
  lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp
  lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h
  lldb/trunk/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
  
lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
  lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
  lldb/trunk/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
  lldb/trunk/source/Target/Platform.cpp
  lldb/trunk/source/Target/Process.cpp
  lldb/trunk/source/Target/ProcessInfo.cpp
  lldb/trunk/source/Target/Target.cpp
  lldb/trunk/source/Utility/CMakeLists.txt
  lldb/trunk/source/Utility/Environment.cpp
  lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp
  lldb/trunk/unittests/Host/HostTest.cpp
  lldb/trunk/unittests/Interpreter/TestArgs.cpp
  lldb/trunk/unittests/Utility/CMakeLists.txt
  lldb/trunk/unittests/Utility/EnvironmentTest.cpp
  lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp

Index: lldb/trunk/include/lldb/Target/ProcessInfo.h
===================================================================
--- lldb/trunk/include/lldb/Target/ProcessInfo.h
+++ lldb/trunk/include/lldb/Target/ProcessInfo.h
@@ -13,6 +13,7 @@
 // LLDB headers
 #include "lldb/Interpreter/Args.h"
 #include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/Environment.h"
 #include "lldb/Utility/FileSpec.h"
 
 namespace lldb_private {
@@ -81,18 +82,17 @@
 
   void SetArguments(char const **argv, bool first_arg_is_executable);
 
-  Args &GetEnvironmentEntries() { return m_environment; }
-
-  const Args &GetEnvironmentEntries() const { return m_environment; }
+  Environment &GetEnvironment() { return m_environment; }
+  const Environment &GetEnvironment() const { return m_environment; }
 
 protected:
   FileSpec m_executable;
   std::string m_arg0; // argv[0] if supported. If empty, then use m_executable.
   // Not all process plug-ins support specifying an argv[0]
   // that differs from the resolved platform executable
   // (which is in m_executable)
   Args m_arguments; // All program arguments except argv[0]
-  Args m_environment;
+  Environment m_environment;
   uint32_t m_uid;
   uint32_t m_gid;
   ArchSpec m_arch;
Index: lldb/trunk/include/lldb/Target/Platform.h
===================================================================
--- lldb/trunk/include/lldb/Target/Platform.h
+++ lldb/trunk/include/lldb/Target/Platform.h
@@ -633,7 +633,7 @@
   //----------------------------------------------------------------------
   virtual Status Install(const FileSpec &src, const FileSpec &dst);
 
-  virtual size_t GetEnvironment(StringList &environment);
+  virtual Environment GetEnvironment();
 
   virtual bool GetFileExists(const lldb_private::FileSpec &file_spec);
 
Index: lldb/trunk/include/lldb/Target/Target.h
===================================================================
--- lldb/trunk/include/lldb/Target/Target.h
+++ lldb/trunk/include/lldb/Target/Target.h
@@ -115,9 +115,8 @@
 
   void SetRunArguments(const Args &args);
 
-  size_t GetEnvironmentAsArgs(Args &env) const;
-
-  void SetEnvironmentFromArgs(const Args &env);
+  Environment GetEnvironment() const;
+  void SetEnvironment(Environment env);
 
   bool GetSkipPrologue() const;
 
Index: lldb/trunk/include/lldb/Host/Host.h
===================================================================
--- lldb/trunk/include/lldb/Host/Host.h
+++ lldb/trunk/include/lldb/Host/Host.h
@@ -12,8 +12,8 @@
 
 #include "lldb/Host/File.h"
 #include "lldb/Host/HostThread.h"
+#include "lldb/Utility/Environment.h"
 #include "lldb/Utility/FileSpec.h"
-#include "lldb/Utility/StringList.h"
 #include "lldb/lldb-private-forward.h"
 #include "lldb/lldb-private.h"
 #include <cerrno>
@@ -242,7 +242,7 @@
   static bool OpenFileInExternalEditor(const FileSpec &file_spec,
                                        uint32_t line_no);
 
-  static size_t GetEnvironment(StringList &env);
+  static Environment GetEnvironment();
 
   static std::unique_ptr<Connection>
   CreateDefaultConnection(llvm::StringRef url);
Index: lldb/trunk/include/lldb/API/SBLaunchInfo.h
===================================================================
--- lldb/trunk/include/lldb/API/SBLaunchInfo.h
+++ lldb/trunk/include/lldb/API/SBLaunchInfo.h
@@ -12,6 +12,10 @@
 
 #include "lldb/API/SBDefines.h"
 
+namespace lldb_private {
+class SBLaunchInfoImpl;
+}
+
 namespace lldb {
 
 class SBPlatform;
@@ -141,11 +145,10 @@
   friend class SBPlatform;
   friend class SBTarget;
 
-  lldb_private::ProcessLaunchInfo &ref();
-
   const lldb_private::ProcessLaunchInfo &ref() const;
+  void set_ref(const lldb_private::ProcessLaunchInfo &info);
 
-  ProcessLaunchInfoSP m_opaque_sp;
+  std::shared_ptr<lldb_private::SBLaunchInfoImpl> m_opaque_sp;
 };
 
 } // namespace lldb
Index: lldb/trunk/include/lldb/Utility/Environment.h
===================================================================
--- lldb/trunk/include/lldb/Utility/Environment.h
+++ lldb/trunk/include/lldb/Utility/Environment.h
@@ -0,0 +1,95 @@
+//===-- Environment.h -------------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_ENVIRONMENT_H
+#define LLDB_UTILITY_ENVIRONMENT_H
+
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/FormatProviders.h"
+
+namespace lldb_private {
+
+class Environment : private llvm::StringMap<std::string> {
+  using Base = llvm::StringMap<std::string>;
+
+public:
+  class Envp {
+  public:
+    Envp(Envp &&RHS) = default;
+    Envp &operator=(Envp &&RHS) = default;
+
+    char *const *get() const { return Data; }
+    operator char *const *() const { return get(); }
+
+  private:
+    explicit Envp(const Environment &Env);
+    char *make_entry(llvm::StringRef Key, llvm::StringRef Value);
+    Envp(const Envp &) = delete;
+    Envp &operator=(const Envp &) = delete;
+    friend class Environment;
+
+    llvm::BumpPtrAllocator Allocator;
+    char **Data;
+  };
+
+  using Base::const_iterator;
+  using Base::iterator;
+  using Base::value_type;
+
+  using Base::begin;
+  using Base::clear;
+  using Base::count;
+  using Base::end;
+  using Base::erase;
+  using Base::find;
+  using Base::insert;
+  using Base::lookup;
+  using Base::size;
+  using Base::try_emplace;
+  using Base::operator[];
+
+  Environment() : Base() {}
+  Environment(const Environment &RHS) : Base(RHS) {}
+  Environment(Environment &&RHS) : Base(std::move(RHS)) {}
+  Environment(char *const *Env)
+      : Environment(const_cast<const char *const *>(Env)) {}
+  Environment(const char *const *Env);
+
+  Environment &operator=(Environment RHS) {
+    Base::operator=(std::move(RHS));
+    return *this;
+  }
+
+  std::pair<iterator, bool> insert(llvm::StringRef KeyEqValue) {
+    return insert(KeyEqValue.split('='));
+  }
+
+  void insert(const_iterator first, const_iterator last);
+
+  Envp getEnvp() const { return Envp(*this); }
+
+  static std::string compose(const value_type &KeyValue) {
+    return (KeyValue.first() + "=" + KeyValue.second).str();
+  }
+};
+
+} // namespace lldb_private
+
+namespace llvm {
+template <> struct format_provider<lldb_private::Environment> {
+  static void format(const lldb_private::Environment &Env, raw_ostream &Stream,
+                     StringRef Style) {
+    for (const auto &KV : Env)
+      Stream << "env[" << KV.first() << "] = " << KV.second << "\n";
+  }
+};
+} // namespace llvm
+
+#endif // #ifndef LLDB_UTILITY_ENVIRONMENT_H
Index: lldb/trunk/include/lldb/Interpreter/Args.h
===================================================================
--- lldb/trunk/include/lldb/Interpreter/Args.h
+++ lldb/trunk/include/lldb/Interpreter/Args.h
@@ -21,6 +21,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
 // Project includes
+#include "lldb/Utility/Environment.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/lldb-private-types.h"
 #include "lldb/lldb-types.h"
@@ -95,6 +96,12 @@
   //------------------------------------------------------------------
   ~Args();
 
+  explicit Args(const Environment &env) : Args() {
+    SetArguments(const_cast<const char **>(env.getEnvp().get()));
+  }
+
+  explicit operator Environment() const { return GetConstArgumentVector(); }
+
   //------------------------------------------------------------------
   /// Dump all entries to the stream \a s using label \a label_name.
   ///
@@ -433,38 +440,6 @@
   static std::string EscapeLLDBCommandArgument(const std::string &arg,
                                                char quote_char);
 
-  //------------------------------------------------------------------
-  /// Add or replace an environment variable with the given value.
-  ///
-  /// This command adds the environment variable if it is not already
-  /// present using the given value.  If the environment variable is
-  /// already in the list, it replaces the first such occurrence
-  /// with the new value.
-  //------------------------------------------------------------------
-  void AddOrReplaceEnvironmentVariable(llvm::StringRef env_var_name,
-                                       llvm::StringRef new_value);
-
-  /// Return whether a given environment variable exists.
-  ///
-  /// This command treats Args like a list of environment variables,
-  /// as used in ProcessLaunchInfo.  It treats each argument as
-  /// an {env_var_name}={value} or an {env_var_name} entry.
-  ///
-  /// @param[in] env_var_name
-  ///     Specifies the name of the environment variable to check.
-  ///
-  /// @param[out] argument_index
-  ///     If non-null, then when the environment variable is found,
-  ///     the index of the argument position will be returned in
-  ///     the size_t pointed to by this argument.
-  ///
-  /// @return
-  ///     true if the specified env var name exists in the list in
-  ///     either of the above-mentioned formats; otherwise, false.
-  //------------------------------------------------------------------
-  bool ContainsEnvironmentVariable(llvm::StringRef env_var_name,
-                                   size_t *argument_index = nullptr) const;
-
 private:
   size_t FindArgumentIndexForOption(Option *long_options,
                                     int long_options_index) const;
Index: lldb/trunk/packages/Python/lldbsuite/test/python_api/sblaunchinfo/TestSBLaunchInfo.py
===================================================================
--- lldb/trunk/packages/Python/lldbsuite/test/python_api/sblaunchinfo/TestSBLaunchInfo.py
+++ lldb/trunk/packages/Python/lldbsuite/test/python_api/sblaunchinfo/TestSBLaunchInfo.py
@@ -0,0 +1,31 @@
+"""
+Test SBLaunchInfo
+"""
+
+from __future__ import print_function
+
+
+from lldbsuite.test.lldbtest import *
+
+
+def lookup(info, key):
+    for i in range(info.GetNumEnvironmentEntries()):
+        KeyEqValue = info.GetEnvironmentEntryAtIndex(i)
+        Key, Value = KeyEqValue.split("=")
+        if Key == key:
+            return Value
+    return ""
+
+class TestSBLaunchInfo(TestBase):
+
+    mydir = TestBase.compute_mydir(__file__)
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def test_environment_getset(self):
+        info = lldb.SBLaunchInfo(None)
+        info.SetEnvironmentEntries(["FOO=BAR"], False)
+        self.assertEquals(1, info.GetNumEnvironmentEntries())
+        info.SetEnvironmentEntries(["BAR=BAZ"], True)
+        self.assertEquals(2, info.GetNumEnvironmentEntries())
+        self.assertEquals("BAR", lookup(info, "FOO"))
+        self.assertEquals("BAZ", lookup(info, "BAR"))
Index: lldb/trunk/unittests/Host/HostTest.cpp
===================================================================
--- lldb/trunk/unittests/Host/HostTest.cpp
+++ lldb/trunk/unittests/Host/HostTest.cpp
@@ -20,3 +20,9 @@
   EXPECT_EQ("Exited with status 4",
             formatv("{0}", WaitStatus{WaitStatus::Exit, 4}).str());
 }
+
+TEST(Host, GetEnvironment) {
+  putenv(const_cast<char *>("LLDB_TEST_ENVIRONMENT_VAR=Host::GetEnvironment"));
+  ASSERT_EQ("Host::GetEnvironment",
+            Host::GetEnvironment().lookup("LLDB_TEST_ENVIRONMENT_VAR"));
+}
Index: lldb/trunk/unittests/Utility/EnvironmentTest.cpp
===================================================================
--- lldb/trunk/unittests/Utility/EnvironmentTest.cpp
+++ lldb/trunk/unittests/Utility/EnvironmentTest.cpp
@@ -0,0 +1,49 @@
+//===-- EnvironmentTest.cpp -------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "gtest/gtest.h"
+
+#include "lldb/Utility/Environment.h"
+
+using namespace lldb_private;
+
+TEST(EnvironmentTest, EnvpConstruction) {
+  const char **Envp1 = nullptr;
+  EXPECT_EQ(0u, Environment(Envp1).size());
+
+  const char *Envp2[] = {"FOO=BAR", nullptr};
+  EXPECT_EQ("BAR", Environment(Envp2).lookup("FOO"));
+
+  const char *Envp3[] = {"FOO=BAR", "FOO=BAZ", nullptr};
+  EXPECT_EQ("BAR", Environment(Envp3).lookup("FOO"));
+
+  const char *Envp4[] = {"FOO=", "BAR", nullptr};
+  Environment Env4(Envp4);
+  ASSERT_EQ(2u, Env4.size());
+  EXPECT_EQ("", Environment(Envp4).find("FOO")->second);
+  EXPECT_EQ("", Environment(Envp4).find("BAR")->second);
+
+  const char *Envp5[] = {"FOO=BAR=BAZ", nullptr};
+  EXPECT_EQ("BAR=BAZ", Environment(Envp5).lookup("FOO"));
+}
+
+TEST(EnvironmentTest, EnvpConversion) {
+  std::string FOO_EQ_BAR("FOO=BAR");
+  std::string BAR_EQ_BAZ("BAR=BAZ");
+
+  Environment Env;
+  Env.insert(FOO_EQ_BAR);
+  Env.insert(BAR_EQ_BAZ);
+  Environment::Envp Envp = Env.getEnvp();
+  const char *const *Envp_ = Envp;
+
+  EXPECT_TRUE(FOO_EQ_BAR == Envp_[0] || FOO_EQ_BAR == Envp_[1]);
+  EXPECT_TRUE(BAR_EQ_BAZ == Envp_[0] || BAR_EQ_BAZ == Envp_[1]);
+  EXPECT_EQ(nullptr, Envp_[2]);
+}
Index: lldb/trunk/unittests/Utility/CMakeLists.txt
===================================================================
--- lldb/trunk/unittests/Utility/CMakeLists.txt
+++ lldb/trunk/unittests/Utility/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_lldb_unittest(UtilityTests
   ArchSpecTest.cpp
   ConstStringTest.cpp
+  EnvironmentTest.cpp
   JSONTest.cpp
   LogTest.cpp
   NameMatchesTest.cpp
Index: lldb/trunk/unittests/Interpreter/TestArgs.cpp
===================================================================
--- lldb/trunk/unittests/Interpreter/TestArgs.cpp
+++ lldb/trunk/unittests/Interpreter/TestArgs.cpp
@@ -276,87 +276,3 @@
                                          &success));
   EXPECT_FALSE(success);
 }
-
-TEST(ArgsTest, StringToVersion) {}
-
-// Environment Variable Tests
-
-class EnvVarFixture: public ::testing::Test {
-protected:
-
-    void SetUp() {
-        args.AppendArgument(llvm::StringRef("Arg1=foo"));
-        args.AppendArgument(llvm::StringRef("Arg2"));
-        args.AppendArgument(llvm::StringRef("Arg3=bar"));
-    }
-
-    size_t GetIndexForEnvVar(llvm::StringRef envvar_name) {
-        size_t argument_index = std::numeric_limits<size_t>::max();
-        EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name,
-                                                     &argument_index));
-        EXPECT_LT(argument_index, args.GetArgumentCount());
-        return argument_index;
-    }
-
-    Args  args;
-};
-
-
-TEST_F(EnvVarFixture, TestContainsEnvironmentVariableNoValue) {
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(llvm::StringRef("Arg2")));
-}
-
-TEST_F(EnvVarFixture, TestContainsEnvironmentVariableWithValue) {
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(llvm::StringRef("Arg3")));
-}
-
-TEST_F(EnvVarFixture, TestContainsEnvironmentVariableNonExistentVariable) {
-    auto nonexistent_envvar = llvm::StringRef("ThisEnvVarShouldNotExist");
-    EXPECT_FALSE(args.ContainsEnvironmentVariable(nonexistent_envvar));
-}
-
-TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialNoValueWithNoValue) {
-    auto envvar_name = llvm::StringRef("Arg2");
-    auto argument_index = GetIndexForEnvVar(envvar_name);
-
-    args.AddOrReplaceEnvironmentVariable(envvar_name, llvm::StringRef(""));
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name));
-    EXPECT_EQ(envvar_name, args.GetArgumentAtIndex(argument_index));
-}
-
-TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialNoValueWithValue) {
-    auto envvar_name = llvm::StringRef("Arg2");
-    auto argument_index = GetIndexForEnvVar(envvar_name);
-
-    auto new_value = llvm::StringRef("NewValue");
-    args.AddOrReplaceEnvironmentVariable(envvar_name, new_value);
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name));
-
-    std::stringstream stream;
-    stream << envvar_name.str() << '=' << new_value.str();
-    EXPECT_EQ(llvm::StringRef(stream.str()),
-              args.GetArgumentAtIndex(argument_index));
-}
-
-TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialValueWithNoValue) {
-    auto envvar_name = llvm::StringRef("Arg1");
-    auto argument_index = GetIndexForEnvVar(envvar_name);
-
-    args.AddOrReplaceEnvironmentVariable(envvar_name, llvm::StringRef(""));
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name));
-    EXPECT_EQ(envvar_name, args.GetArgumentAtIndex(argument_index));
-}
-
-TEST_F(EnvVarFixture, TestReplaceEnvironmentVariableInitialValueWithValue) {
-    auto envvar_name = llvm::StringRef("Arg1");
-    auto argument_index = GetIndexForEnvVar(envvar_name);
-
-    auto new_value = llvm::StringRef("NewValue");
-    args.AddOrReplaceEnvironmentVariable(envvar_name, new_value);
-    EXPECT_TRUE(args.ContainsEnvironmentVariable(envvar_name));
-
-    std::stringstream stream;
-    stream << envvar_name.str() << '=' << new_value.str();
-    EXPECT_EQ(llvm::StringRef(stream.str()),
-              args.GetArgumentAtIndex(argument_index));
-}
Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp
===================================================================
--- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp
+++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp
@@ -91,10 +91,7 @@
   ProcessLaunchInfo Info;
   Info.SetArchitecture(arch_spec);
   Info.SetArguments(args, true);
-
-  StringList Env;
-  Host::GetEnvironment(Env);
-  Info.GetEnvironmentEntries() = Args(Env);
+  Info.GetEnvironment() = Host::GetEnvironment();
 
   status = Host::LaunchProcess(Info);
   if (status.Fail())
@@ -114,14 +111,9 @@
 }
 
 Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
-  StringList env;
-  Host::GetEnvironment(env);
-  for (size_t i = 0; i < env.GetSize(); ++i) {
-    if (SendEnvironmentPacket(env[i].c_str()) != 0) {
-      return make_error<StringError>(
-          formatv("Failed to set environment variable: {0}", env[i]).str(),
-          inconvertibleErrorCode());
-    }
+  if (SendEnvironment(Host::GetEnvironment()) != 0) {
+    return make_error<StringError>("Failed to set launch environment",
+                                   inconvertibleErrorCode());
   }
   std::stringstream command;
   command << "A";
Index: lldb/trunk/source/Target/Platform.cpp
===================================================================
--- lldb/trunk/source/Target/Platform.cpp
+++ lldb/trunk/source/Target/Platform.cpp
@@ -1539,10 +1539,7 @@
   return error;
 }
 
-size_t Platform::GetEnvironment(StringList &environment) {
-  environment.Clear();
-  return false;
-}
+Environment Platform::GetEnvironment() { return Environment(); }
 
 const std::vector<ConstString> &Platform::GetTrapHandlerSymbolNames() {
   if (!m_calculated_trap_handlers) {
Index: lldb/trunk/source/Target/Process.cpp
===================================================================
--- lldb/trunk/source/Target/Process.cpp
+++ lldb/trunk/source/Target/Process.cpp
@@ -307,16 +307,7 @@
     }
   }
 
-  const uint32_t envc = m_environment.GetArgumentCount();
-  if (envc > 0) {
-    for (uint32_t i = 0; i < envc; i++) {
-      const char *env = m_environment.GetArgumentAtIndex(i);
-      if (i < 10)
-        s.Printf(" env[%u] = %s\n", i, env);
-      else
-        s.Printf("env[%u] = %s\n", i, env);
-    }
-  }
+  s.Format("{0}", m_environment);
 
   if (m_arch.IsValid()) {
     s.Printf("   arch = ");
@@ -529,7 +520,7 @@
     break;
 
   case 'v':
-    launch_info.GetEnvironmentEntries().AppendArgument(option_arg);
+    launch_info.GetEnvironment().insert(option_arg);
     break;
 
   default:
Index: lldb/trunk/source/Target/ProcessInfo.cpp
===================================================================
--- lldb/trunk/source/Target/ProcessInfo.cpp
+++ lldb/trunk/source/Target/ProcessInfo.cpp
@@ -35,7 +35,7 @@
 void ProcessInfo::Clear() {
   m_executable.Clear();
   m_arguments.Clear();
-  m_environment.Clear();
+  m_environment.clear();
   m_uid = UINT32_MAX;
   m_gid = UINT32_MAX;
   m_arch.Clear();
@@ -59,8 +59,7 @@
   s << "Arguments:\n";
   m_arguments.Dump(s);
 
-  s << "Environment:\n";
-  m_environment.Dump(s, "env");
+  s.Format("Environment:\n{0}", m_environment);
 }
 
 void ProcessInfo::SetExecutableFile(const FileSpec &exe_file,
Index: lldb/trunk/source/Target/Target.cpp
===================================================================
--- lldb/trunk/source/Target/Target.cpp
+++ lldb/trunk/source/Target/Target.cpp
@@ -3611,36 +3611,19 @@
                 nullptr, idx, g_properties[idx].default_uint_value != 0)) {
           PlatformSP platform_sp(m_target->GetPlatform());
           if (platform_sp) {
-            StringList env;
-            if (platform_sp->GetEnvironment(env)) {
-              OptionValueDictionary *env_dict =
-                  GetPropertyAtIndexAsOptionValueDictionary(nullptr,
-                                                            ePropertyEnvVars);
-              if (env_dict) {
-                const bool can_replace = false;
-                const size_t envc = env.GetSize();
-                for (size_t idx = 0; idx < envc; idx++) {
-                  const char *env_entry = env.GetStringAtIndex(idx);
-                  if (env_entry) {
-                    const char *equal_pos = ::strchr(env_entry, '=');
-                    ConstString key;
-                    // It is ok to have environment variables with no values
-                    const char *value = nullptr;
-                    if (equal_pos) {
-                      key.SetCStringWithLength(env_entry,
-                                               equal_pos - env_entry);
-                      if (equal_pos[1])
-                        value = equal_pos + 1;
-                    } else {
-                      key.SetCString(env_entry);
-                    }
-                    // Don't allow existing keys to be replaced with ones we get
-                    // from the platform environment
-                    env_dict->SetValueForKey(
-                        key, OptionValueSP(new OptionValueString(value)),
-                        can_replace);
-                  }
-                }
+            Environment env = platform_sp->GetEnvironment();
+            OptionValueDictionary *env_dict =
+                GetPropertyAtIndexAsOptionValueDictionary(nullptr,
+                                                          ePropertyEnvVars);
+            if (env_dict) {
+              const bool can_replace = false;
+              for (const auto &KV : env) {
+                // Don't allow existing keys to be replaced with ones we get
+                // from the platform environment
+                env_dict->SetValueForKey(
+                    ConstString(KV.first()),
+                    OptionValueSP(new OptionValueString(KV.second.c_str())),
+                    can_replace);
               }
             }
           }
@@ -3906,15 +3889,19 @@
   m_launch_info.GetArguments() = args;
 }
 
-size_t TargetProperties::GetEnvironmentAsArgs(Args &env) const {
+Environment TargetProperties::GetEnvironment() const {
+  // TODO: Get rid of the Args intermediate step
+  Args env;
   const uint32_t idx = ePropertyEnvVars;
-  return m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env);
+  m_collection_sp->GetPropertyAtIndexAsArgs(nullptr, idx, env);
+  return Environment(env);
 }
 
-void TargetProperties::SetEnvironmentFromArgs(const Args &env) {
+void TargetProperties::SetEnvironment(Environment env) {
+  // TODO: Get rid of the Args intermediate step
   const uint32_t idx = ePropertyEnvVars;
-  m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, env);
-  m_launch_info.GetEnvironmentEntries() = env;
+  m_collection_sp->SetPropertyAtIndexFromArgs(nullptr, idx, Args(env));
+  m_launch_info.GetEnvironment() = std::move(env);
 }
 
 bool TargetProperties::GetSkipPrologue() const {
@@ -4152,7 +4139,7 @@
   m_launch_info = launch_info;
   SetArg0(launch_info.GetArg0());
   SetRunArguments(launch_info.GetArguments());
-  SetEnvironmentFromArgs(launch_info.GetEnvironmentEntries());
+  SetEnvironment(launch_info.GetEnvironment());
   const FileAction *input_file_action =
       launch_info.GetFileActionForFD(STDIN_FILENO);
   if (input_file_action) {
@@ -4193,9 +4180,7 @@
                                                    OptionValue *) {
   TargetProperties *this_ =
       reinterpret_cast<TargetProperties *>(target_property_ptr);
-  Args args;
-  if (this_->GetEnvironmentAsArgs(args))
-    this_->m_launch_info.GetEnvironmentEntries() = args;
+  this_->m_launch_info.GetEnvironment() = this_->GetEnvironment();
 }
 
 void TargetProperties::InputPathValueChangedCallback(void *target_property_ptr,
Index: lldb/trunk/source/Utility/Environment.cpp
===================================================================
--- lldb/trunk/source/Utility/Environment.cpp
+++ lldb/trunk/source/Utility/Environment.cpp
@@ -0,0 +1,50 @@
+//===-- Environment.cpp -----------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/Environment.h"
+
+using namespace lldb_private;
+
+char *Environment::Envp::make_entry(llvm::StringRef Key,
+                                    llvm::StringRef Value) {
+  const size_t size = Key.size() + 1 /*=*/ + Value.size() + 1 /*\0*/;
+  char *Result = reinterpret_cast<char *>(
+      Allocator.Allocate(sizeof(char) * size, alignof(char)));
+  char *Next = Result;
+
+  Next = std::copy(Key.begin(), Key.end(), Next);
+  *Next++ = '=';
+  Next = std::copy(Value.begin(), Value.end(), Next);
+  *Next++ = '\0';
+
+  return Result;
+}
+
+Environment::Envp::Envp(const Environment &Env) {
+  Data = reinterpret_cast<char **>(
+      Allocator.Allocate(sizeof(char *) * (Env.size() + 1), alignof(char *)));
+  char **Next = Data;
+  for (const auto &KV : Env)
+    *Next++ = make_entry(KV.first(), KV.second);
+  *Next++ = nullptr;
+}
+
+Environment::Environment(const char *const *Env) {
+  if (!Env)
+    return;
+  while (*Env)
+    insert(*Env++);
+}
+
+void Environment::insert(const_iterator first, const_iterator last) {
+  while (first != last) {
+    try_emplace(first->first(), first->second);
+    ++first;
+  }
+}
Index: lldb/trunk/source/Utility/CMakeLists.txt
===================================================================
--- lldb/trunk/source/Utility/CMakeLists.txt
+++ lldb/trunk/source/Utility/CMakeLists.txt
@@ -48,6 +48,7 @@
   DataBufferLLVM.cpp
   DataEncoder.cpp
   DataExtractor.cpp
+  Environment.cpp
   FastDemangle.cpp
   FileSpec.cpp
   History.cpp
Index: lldb/trunk/source/Interpreter/Args.cpp
===================================================================
--- lldb/trunk/source/Interpreter/Args.cpp
+++ lldb/trunk/source/Interpreter/Args.cpp
@@ -900,50 +900,6 @@
   return result;
 }
 
-void Args::AddOrReplaceEnvironmentVariable(llvm::StringRef env_var_name,
-                                           llvm::StringRef new_value) {
-  if (env_var_name.empty())
-    return;
-
-  // Build the new entry.
-  std::string var_string(env_var_name);
-  if (!new_value.empty()) {
-    var_string += "=";
-    var_string += new_value;
-  }
-
-  size_t index = 0;
-  if (ContainsEnvironmentVariable(env_var_name, &index)) {
-    ReplaceArgumentAtIndex(index, var_string);
-    return;
-  }
-
-  // We didn't find it.  Append it instead.
-  AppendArgument(var_string);
-}
-
-bool Args::ContainsEnvironmentVariable(llvm::StringRef env_var_name,
-                                       size_t *argument_index) const {
-  // Validate args.
-  if (env_var_name.empty())
-    return false;
-
-  // Check each arg to see if it matches the env var name.
-  for (auto arg : llvm::enumerate(m_entries)) {
-    llvm::StringRef name, value;
-    std::tie(name, value) = arg.value().ref.split('=');
-    if (name != env_var_name)
-      continue;
-
-    if (argument_index)
-      *argument_index = arg.index();
-    return true;
-  }
-
-  // We didn't find a match.
-  return false;
-}
-
 size_t Args::FindArgumentIndexForOption(Option *long_options,
                                         int long_options_index) const {
   char short_buffer[3];
Index: lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h
===================================================================
--- lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h
+++ lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.h
@@ -118,7 +118,7 @@
 
   bool CanDebugProcess() override;
 
-  size_t GetEnvironment(StringList &env) override;
+  Environment GetEnvironment() override;
 
   // FIXME not sure what the _sigtramp equivalent would be on this platform
   void CalculateTrapHandlerSymbolNames() override {}
Index: lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp
===================================================================
--- lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp
+++ lldb/trunk/source/Plugins/Platform/Windows/PlatformWindows.cpp
@@ -587,14 +587,14 @@
 
 bool PlatformWindows::CanDebugProcess() { return true; }
 
-size_t PlatformWindows::GetEnvironment(StringList &env) {
+Environment PlatformWindows::GetEnvironment() {
   if (IsRemote()) {
     if (m_remote_platform_sp)
-      return m_remote_platform_sp->GetEnvironment(env);
-    return 0;
+      return m_remote_platform_sp->GetEnvironment();
+    return Environment();
   }
 
-  return Host::GetEnvironment(env);
+  return Host::GetEnvironment();
 }
 
 ConstString PlatformWindows::GetFullNameForDylib(ConstString basename) {
Index: lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm
===================================================================
--- lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm
+++ lldb/trunk/source/Plugins/Platform/MacOSX/PlatformiOSSimulatorCoreSimulatorSupport.mm
@@ -478,26 +478,17 @@
     [options setObject:args_array forKey:kSimDeviceSpawnArguments];
   }
 
-  if (launch_info.GetEnvironmentEntries().GetArgumentCount()) {
-    const Args &envs(launch_info.GetEnvironmentEntries());
-    NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init];
-    for (size_t idx = 0; idx < envs.GetArgumentCount(); idx++) {
-      llvm::StringRef arg_sr(envs.GetArgumentAtIndex(idx));
-      auto first_eq = arg_sr.find('=');
-      if (first_eq == llvm::StringRef::npos)
-        continue;
-      llvm::StringRef key = arg_sr.substr(0, first_eq);
-      llvm::StringRef value = arg_sr.substr(first_eq + 1);
+  NSMutableDictionary *env_dict = [[NSMutableDictionary alloc] init];
 
-      NSString *key_ns = [NSString stringWithUTF8String:key.str().c_str()];
-      NSString *value_ns = [NSString stringWithUTF8String:value.str().c_str()];
+  for (const auto &KV : launch_info.GetEnvironment()) {
+    NSString *key_ns = [NSString stringWithUTF8String:KV.first().str().c_str()];
+    NSString *value_ns = [NSString stringWithUTF8String:KV.second.c_str()];
 
-      [env_dict setValue:value_ns forKey:key_ns];
-    }
-
-    [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment];
+    [env_dict setValue:value_ns forKey:key_ns];
   }
 
+  [options setObject:env_dict forKey:kSimDeviceSpawnEnvironment];
+
   Status error;
   File stdin_file;
   File stdout_file;
Index: lldb/trunk/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
===================================================================
--- lldb/trunk/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ lldb/trunk/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -1282,14 +1282,8 @@
     // /bin/sh re-exec's itself as /bin/bash requiring another resume.
     // But it only does this if the COMMAND_MODE environment variable
     // is set to "legacy".
-    const char **envp =
-        launch_info.GetEnvironmentEntries().GetConstArgumentVector();
-    if (envp != NULL) {
-      for (int i = 0; envp[i] != NULL; i++) {
-        if (strcmp(envp[i], "COMMAND_MODE=legacy") == 0)
-          return 2;
-      }
-    }
+    if (launch_info.GetEnvironment().lookup("COMMAND_MODE") == "legacy")
+      return 2;
     return 1;
   } else if (strcmp(shell_name, "csh") == 0 ||
              strcmp(shell_name, "tcsh") == 0 ||
@@ -1667,25 +1661,13 @@
   if (process && strstr(GetPluginName().GetCString(), "-simulator")) {
     lldb_private::ProcessInstanceInfo proc_info;
     if (Host::GetProcessInfo(process->GetID(), proc_info)) {
-      Args &env = proc_info.GetEnvironmentEntries();
-      const size_t n = env.GetArgumentCount();
-      const llvm::StringRef k_runtime_version("SIMULATOR_RUNTIME_VERSION=");
-      const llvm::StringRef k_dyld_root_path("DYLD_ROOT_PATH=");
-      std::string dyld_root_path;
-
-      for (size_t i = 0; i < n; ++i) {
-        const char *env_cstr = env.GetArgumentAtIndex(i);
-        if (env_cstr) {
-          llvm::StringRef env_str(env_cstr);
-          if (env_str.consume_front(k_runtime_version)) {
-            if (Args::StringToVersion(env_str, major, minor, update))
-              return true;
-          } else if (env_str.consume_front(k_dyld_root_path)) {
-            dyld_root_path = env_str;
-          }
-        }
-      }
+      const Environment &env = proc_info.GetEnvironment();
+
+      if (Args::StringToVersion(env.lookup("SIMULATOR_RUNTIME_VERSION"), major,
+                                minor, update))
+        return true;
 
+      std::string dyld_root_path = env.lookup("DYLD_ROOT_PATH");
       if (!dyld_root_path.empty()) {
         dyld_root_path += "/System/Library/CoreServices/SystemVersion.plist";
         ApplePropertyList system_version_plist(dyld_root_path.c_str());
@@ -1756,14 +1738,12 @@
   // LLDB *not* to muck with the OS_ACTIVITY_DT_MODE flag when they
   // specifically want it unset.
   const char *disable_env_var = "IDE_DISABLED_OS_ACTIVITY_DT_MODE";
-  auto &env_vars = launch_info.GetEnvironmentEntries();
-  if (!env_vars.ContainsEnvironmentVariable(llvm::StringRef(disable_env_var))) {
+  auto &env_vars = launch_info.GetEnvironment();
+  if (!env_vars.count(disable_env_var)) {
     // We want to make sure that OS_ACTIVITY_DT_MODE is set so that
     // we get os_log and NSLog messages mirrored to the target process
     // stderr.
-    if (!env_vars.ContainsEnvironmentVariable(
-            llvm::StringRef("OS_ACTIVITY_DT_MODE")))
-      env_vars.AppendArgument(llvm::StringRef("OS_ACTIVITY_DT_MODE=enable"));
+    env_vars.try_emplace("OS_ACTIVITY_DT_MODE", "enable");
   }
 
   // Let our parent class do the real launching.
Index: lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
===================================================================
--- lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
+++ lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
@@ -658,13 +658,13 @@
   return false;
 }
 
-size_t PlatformPOSIX::GetEnvironment(StringList &env) {
+Environment PlatformPOSIX::GetEnvironment() {
   if (IsRemote()) {
     if (m_remote_platform_sp)
-      return m_remote_platform_sp->GetEnvironment(env);
-    return 0;
+      return m_remote_platform_sp->GetEnvironment();
+    return Environment();
   }
-  return Host::GetEnvironment(env);
+  return Host::GetEnvironment();
 }
 
 bool PlatformPOSIX::GetRemoteOSKernelDescription(std::string &s) {
Index: lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
===================================================================
--- lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
+++ lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
@@ -85,7 +85,7 @@
 
   const lldb::UnixSignalsSP &GetRemoteUnixSignals() override;
 
-  size_t GetEnvironment(lldb_private::StringList &environment) override;
+  lldb_private::Environment GetEnvironment() override;
 
   bool IsConnected() const override;
 
Index: lldb/trunk/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
===================================================================
--- lldb/trunk/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
+++ lldb/trunk/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp
@@ -423,16 +423,7 @@
   }
 
   // Send the environment and the program + arguments after we connect
-  const char **envp =
-      launch_info.GetEnvironmentEntries().GetConstArgumentVector();
-
-  if (envp) {
-    const char *env_entry;
-    for (int i = 0; (env_entry = envp[i]); ++i) {
-      if (m_gdb_client.SendEnvironmentPacket(env_entry) != 0)
-        break;
-    }
-  }
+  m_gdb_client.SendEnvironment(launch_info.GetEnvironment());
 
   ArchSpec arch_spec = launch_info.GetArchitecture();
   const char *arch_triple = arch_spec.GetTriple().str().c_str();
Index: lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ lldb/trunk/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -888,16 +888,7 @@
       }
 
       // Send the environment and the program + arguments after we connect
-      const Args &environment = launch_info.GetEnvironmentEntries();
-      if (environment.GetArgumentCount()) {
-        size_t num_environment_entries = environment.GetArgumentCount();
-        for (size_t i = 0; i < num_environment_entries; ++i) {
-          const char *env_entry = environment.GetArgumentAtIndex(i);
-          if (env_entry == NULL ||
-              m_gdb_comm.SendEnvironmentPacket(env_entry) != 0)
-            break;
-        }
-      }
+      m_gdb_comm.SendEnvironment(launch_info.GetEnvironment());
 
       {
         // Scope for the scoped timeout object
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -121,6 +121,7 @@
   ///     response was received.
   //------------------------------------------------------------------
   int SendEnvironmentPacket(char const *name_equal_value);
+  int SendEnvironment(const Environment &env);
 
   int SendLaunchArchPacket(const char *arch);
 
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp
@@ -945,8 +945,7 @@
   packet.SetFilePos(::strlen("QEnvironment:"));
   const uint32_t bytes_left = packet.GetBytesLeft();
   if (bytes_left > 0) {
-    m_process_launch_info.GetEnvironmentEntries().AppendArgument(
-        llvm::StringRef::withNullAsEmpty(packet.Peek()));
+    m_process_launch_info.GetEnvironment().insert(packet.Peek());
     return SendOKResponse();
   }
   return SendErrorResponse(12);
@@ -960,7 +959,7 @@
   if (bytes_left > 0) {
     std::string str;
     packet.GetHexByteString(str);
-    m_process_launch_info.GetEnvironmentEntries().AppendArgument(str);
+    m_process_launch_info.GetEnvironment().insert(str);
     return SendOKResponse();
   }
   return SendErrorResponse(12);
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp
@@ -1206,11 +1206,7 @@
     }
 
     // Copy the current environment to the gdbserver/debugserver instance
-    StringList env;
-    if (Host::GetEnvironment(env)) {
-      for (size_t i = 0; i < env.GetSize(); ++i)
-        launch_info.GetEnvironmentEntries().AppendArgument(env[i]);
-    }
+    launch_info.GetEnvironment() = Host::GetEnvironment();
 
     // Close STDIN, STDOUT and STDERR.
     launch_info.AppendCloseFileAction(STDIN_FILENO);
Index: lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
===================================================================
--- lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ lldb/trunk/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -822,6 +822,15 @@
   return -1;
 }
 
+int GDBRemoteCommunicationClient::SendEnvironment(const Environment &env) {
+  for (const auto &KV : env) {
+    int r = SendEnvironmentPacket(Environment::compose(KV).c_str());
+    if (r != 0)
+      return r;
+  }
+  return 0;
+}
+
 int GDBRemoteCommunicationClient::SendEnvironmentPacket(
     char const *name_equal_value) {
   if (name_equal_value && name_equal_value[0]) {
Index: lldb/trunk/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
===================================================================
--- lldb/trunk/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
+++ lldb/trunk/source/Plugins/StructuredData/DarwinLog/StructuredDataDarwinLog.cpp
@@ -1514,25 +1514,19 @@
     SetGlobalEnableOptions(debugger_sp, options_sp);
   }
 
-  auto &env_vars = launch_info.GetEnvironmentEntries();
   if (!options_sp->GetEchoToStdErr()) {
     // The user doesn't want to see os_log/NSLog messages echo to stderr.
     // That mechanism is entirely separate from the DarwinLog support.
     // By default we don't want to get it via stderr, because that would
     // be in duplicate of the explicit log support here.
 
     // Here we need to strip out any OS_ACTIVITY_DT_MODE setting to prevent
     // echoing of os_log()/NSLog() to stderr in the target program.
-    size_t argument_index;
-    if (env_vars.ContainsEnvironmentVariable(
-            llvm::StringRef("OS_ACTIVITY_DT_MODE"), &argument_index))
-      env_vars.DeleteArgumentAtIndex(argument_index);
+    launch_info.GetEnvironment().erase("OS_ACTIVITY_DT_MODE");
 
     // We will also set the env var that tells any downstream launcher
     // from adding OS_ACTIVITY_DT_MODE.
-    env_vars.AddOrReplaceEnvironmentVariable(
-        llvm::StringRef("IDE_DISABLED_OS_ACTIVITY_DT_MODE"),
-        llvm::StringRef("1"));
+    launch_info.GetEnvironment()["IDE_DISABLED_OS_ACTIVITY_DT_MODE"] = "1";
   }
 
   // Set the OS_ACTIVITY_MODE env var appropriately to enable/disable
@@ -1545,10 +1539,7 @@
   else
     env_var_value = "default";
 
-  if (env_var_value) {
-    launch_info.GetEnvironmentEntries().AddOrReplaceEnvironmentVariable(
-        llvm::StringRef("OS_ACTIVITY_MODE"), llvm::StringRef(env_var_value));
-  }
+  launch_info.GetEnvironment()["OS_ACTIVITY_MODE"] = env_var_value;
 
   return error;
 }
Index: lldb/trunk/source/Commands/CommandObjectProcess.cpp
===================================================================
--- lldb/trunk/source/Commands/CommandObjectProcess.cpp
+++ lldb/trunk/source/Commands/CommandObjectProcess.cpp
@@ -205,11 +205,7 @@
     if (target->GetDisableSTDIO())
       m_options.launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
 
-    Args environment;
-    target->GetEnvironmentAsArgs(environment);
-    if (environment.GetArgumentCount() > 0)
-      m_options.launch_info.GetEnvironmentEntries().AppendArguments(
-          environment);
+    m_options.launch_info.GetEnvironment() = target->GetEnvironment();
 
     if (!target_settings_argv0.empty()) {
       m_options.launch_info.GetArguments().AppendArgument(
Index: lldb/trunk/source/Host/posix/ProcessLauncherPosixFork.cpp
===================================================================
--- lldb/trunk/source/Host/posix/ProcessLauncherPosixFork.cpp
+++ lldb/trunk/source/Host/posix/ProcessLauncherPosixFork.cpp
@@ -38,17 +38,12 @@
 using namespace lldb;
 using namespace lldb_private;
 
-static void FixupEnvironment(Args &env) {
+static void FixupEnvironment(Environment &env) {
 #ifdef __ANDROID__
   // If there is no PATH variable specified inside the environment then set the
   // path to /system/bin. It is required because the default path used by
   // execve() is wrong on android.
-  static const char *path = "PATH=";
-  for (auto &entry : env.entries()) {
-    if (entry.ref.startswith(path))
-      return;
-  }
-  env.AppendArgument(llvm::StringRef("PATH=/system/bin"));
+  env.try_emplace("PATH", "/system/bin");
 #endif
 }
 
@@ -132,9 +127,9 @@
     ExitWithError(error_fd, "chdir");
 
   DisableASLRIfRequested(error_fd, info);
-  Args env = info.GetEnvironmentEntries();
+  Environment env = info.GetEnvironment();
   FixupEnvironment(env);
-  const char **envp = env.GetConstArgumentVector();
+  Environment::Envp envp = env.getEnvp();
 
   // Clear the signal mask to prevent the child from being affected by
   // any masking done by the parent.
@@ -159,8 +154,7 @@
   }
 
   // Execute.  We should never return...
-  execve(argv[0], const_cast<char *const *>(argv),
-         const_cast<char *const *>(envp));
+  execve(argv[0], const_cast<char *const *>(argv), envp);
 
 #if defined(__linux__)
   if (errno == ETXTBSY) {
@@ -177,8 +171,7 @@
     // this state should clear up quickly, wait a while and then give it one
     // more go.
     usleep(50000);
-    execve(argv[0], const_cast<char *const *>(argv),
-           const_cast<char *const *>(envp));
+    execve(argv[0], const_cast<char *const *>(argv), envp);
   }
 #endif
 
Index: lldb/trunk/source/Host/netbsd/Host.cpp
===================================================================
--- lldb/trunk/source/Host/netbsd/Host.cpp
+++ lldb/trunk/source/Host/netbsd/Host.cpp
@@ -48,14 +48,7 @@
 using namespace lldb;
 using namespace lldb_private;
 
-size_t Host::GetEnvironment(StringList &env) {
-  char **host_env = environ;
-  char *env_entry;
-  size_t i;
-  for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
-    env.AppendString(env_entry);
-  return i;
-}
+Environment Host::GetEnvironment() { return Environment(environ); }
 
 static bool GetNetBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
                                  ProcessInstanceInfo &process_info) {
Index: lldb/trunk/source/Host/linux/Host.cpp
===================================================================
--- lldb/trunk/source/Host/linux/Host.cpp
+++ lldb/trunk/source/Host/linux/Host.cpp
@@ -199,7 +199,7 @@
   while (!Rest.empty()) {
     llvm::StringRef Var;
     std::tie(Var, Rest) = Rest.split('\0');
-    process_info.GetEnvironmentEntries().AppendArgument(Var);
+    process_info.GetEnvironment().insert(Var);
   }
 
   llvm::StringRef Arg0;
@@ -297,14 +297,7 @@
   return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
 }
 
-size_t Host::GetEnvironment(StringList &env) {
-  char **host_env = environ;
-  char *env_entry;
-  size_t i;
-  for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
-    env.AppendString(env_entry);
-  return i;
-}
+Environment Host::GetEnvironment() { return Environment(environ); }
 
 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
   return Status("unimplemented");
Index: lldb/trunk/source/Host/windows/Host.cpp
===================================================================
--- lldb/trunk/source/Host/windows/Host.cpp
+++ lldb/trunk/source/Host/windows/Host.cpp
@@ -258,24 +258,23 @@
   return error;
 }
 
-size_t Host::GetEnvironment(StringList &env) {
+Environment Host::GetEnvironment() {
+  Environment env;
   // The environment block on Windows is a contiguous buffer of NULL terminated
-  // strings,
-  // where the end of the environment block is indicated by two consecutive
-  // NULLs.
+  // strings, where the end of the environment block is indicated by two
+  // consecutive NULLs.
   LPWCH environment_block = ::GetEnvironmentStringsW();
-  env.Clear();
   while (*environment_block != L'\0') {
     std::string current_var;
     auto current_var_size = wcslen(environment_block) + 1;
     if (!llvm::convertWideToUTF8(environment_block, current_var)) {
       environment_block += current_var_size;
       continue;
     }
     if (current_var[0] != '=')
-      env.AppendString(current_var);
+      env.insert(current_var);
 
     environment_block += current_var_size;
   }
-  return env.GetSize();
+  return env;
 }
Index: lldb/trunk/source/Host/openbsd/Host.cpp
===================================================================
--- lldb/trunk/source/Host/openbsd/Host.cpp
+++ lldb/trunk/source/Host/openbsd/Host.cpp
@@ -45,16 +45,17 @@
 using namespace lldb;
 using namespace lldb_private;
 
-size_t Host::GetEnvironment(StringList &env) {
+Environment Host::GetEnvironment() {
+  Environment env;
   char *v;
   char **var = environ;
   for (; var != NULL && *var != NULL; ++var) {
     v = strchr(*var, (int)'-');
     if (v == NULL)
       continue;
-    env.AppendString(v);
+    env.insert(v);
   }
-  return env.GetSize();
+  return env;
 }
 
 static bool
Index: lldb/trunk/source/Host/macosx/Host.mm
===================================================================
--- lldb/trunk/source/Host/macosx/Host.mm
+++ lldb/trunk/source/Host/macosx/Host.mm
@@ -431,33 +431,16 @@
     command.PutCString(" --disable-aslr");
 
   // We are launching on this host in a terminal. So compare the environment on
-  // the host
-  // to what is supplied in the launch_info. Any items that aren't in the host
-  // environment
-  // need to be sent to darwin-debug. If we send all environment entries, we
-  // might blow the
-  // max command line length, so we only send user modified entries.
-  const char **envp =
-      launch_info.GetEnvironmentEntries().GetConstArgumentVector();
-
-  StringList host_env;
-  const size_t host_env_count = Host::GetEnvironment(host_env);
-
-  if (envp && envp[0]) {
-    const char *env_entry;
-    for (size_t env_idx = 0; (env_entry = envp[env_idx]) != NULL; ++env_idx) {
-      bool add_entry = true;
-      for (size_t i = 0; i < host_env_count; ++i) {
-        const char *host_env_entry = host_env.GetStringAtIndex(i);
-        if (strcmp(env_entry, host_env_entry) == 0) {
-          add_entry = false;
-          break;
-        }
-      }
-      if (add_entry) {
-        command.Printf(" --env='%s'", env_entry);
-      }
-    }
+  // the host to what is supplied in the launch_info. Any items that aren't in
+  // the host environment need to be sent to darwin-debug. If we send all
+  // environment entries, we might blow the max command line length, so we only
+  // send user modified entries.
+  Environment host_env = Host::GetEnvironment();
+
+  for (const auto &KV : launch_info.GetEnvironment()) {
+    auto host_entry = host_env.find(KV.first());
+    if (host_entry == host_env.end() || host_entry->second != KV.second)
+      command.Format(" --env='{0}'", Environment::compose(KV));
   }
 
   command.PutCString(" -- ");
@@ -641,14 +624,7 @@
 #endif // #if !defined(__arm__) && !defined(__arm64__) && !defined(__aarch64__)
 }
 
-size_t Host::GetEnvironment(StringList &env) {
-  char **host_env = *_NSGetEnviron();
-  char *env_entry;
-  size_t i;
-  for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
-    env.AppendString(env_entry);
-  return i;
-}
+Environment Host::GetEnvironment() { return Environment(*_NSGetEnviron()); }
 
 static bool GetMacOSXProcessCPUType(ProcessInstanceInfo &process_info) {
   if (process_info.ProcessIDIsValid()) {
@@ -770,7 +746,7 @@
               proc_args.AppendArgument(llvm::StringRef(cstr));
           }
 
-          Args &proc_env = process_info.GetEnvironmentEntries();
+          Environment &proc_env = process_info.GetEnvironment();
           while ((cstr = data.GetCStr(&offset))) {
             if (cstr[0] == '\0')
               break;
@@ -785,7 +761,7 @@
                     llvm::Triple::MacOSX);
             }
 
-            proc_env.AppendArgument(llvm::StringRef(cstr));
+            proc_env.insert(cstr);
           }
           return true;
         }
@@ -939,6 +915,17 @@
   }
 }
 
+static void PackageXPCEnvironment(xpc_object_t message, llvm::StringRef prefix,
+                                  const Environment &env) {
+  xpc_dictionary_set_int64(message, (prefix + "Count").str().c_str(),
+                           env.size());
+  size_t i = 0;
+  for (const auto &KV : env) {
+    xpc_dictionary_set_string(message, (prefix + llvm::Twine(i)).str().c_str(),
+                              Environment::compose(KV).c_str());
+  }
+}
+
 /*
  A valid authorizationRef means that
     - there is the LaunchUsingXPCRightName rights in the /etc/authorization
@@ -1141,8 +1128,8 @@
 
   PackageXPCArguments(message, LauncherXPCServiceArgPrefxKey,
                       launch_info.GetArguments());
-  PackageXPCArguments(message, LauncherXPCServiceEnvPrefxKey,
-                      launch_info.GetEnvironmentEntries());
+  PackageXPCEnvironment(message, LauncherXPCServiceEnvPrefxKey,
+                        launch_info.GetEnvironment());
 
   // Posix spawn stuff.
   xpc_dictionary_set_int64(message, LauncherXPCServiceCPUTypeKey,
@@ -1356,8 +1343,7 @@
   const char *tmp_argv[2];
   char *const *argv = const_cast<char *const *>(
       launch_info.GetArguments().GetConstArgumentVector());
-  char *const *envp = const_cast<char *const *>(
-      launch_info.GetEnvironmentEntries().GetConstArgumentVector());
+  Environment::Envp envp = launch_info.GetEnvironment().getEnvp();
   if (argv == NULL) {
     // posix_spawn gets very unhappy if it doesn't have at least the program
     // name in argv[0]. One of the side affects I have noticed is the
@@ -1425,7 +1411,8 @@
                "error: {0}, ::posix_spawnp(pid => {1}, path = '{2}', "
                "file_actions = {3}, "
                "attr = {4}, argv = {5}, envp = {6} )",
-               error, result_pid, exe_path, &file_actions, &attr, argv, envp);
+               error, result_pid, exe_path, &file_actions, &attr, argv,
+               envp.get());
       if (log) {
         for (int ii = 0; argv[ii]; ++ii)
           LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
@@ -1441,7 +1428,7 @@
       LLDB_LOG(log,
                "error: {0}, ::posix_spawnp ( pid => {1}, path = '{2}', "
                "file_actions = NULL, attr = {3}, argv = {4}, envp = {5} )",
-               error, result_pid, exe_path, &attr, argv, envp);
+               error, result_pid, exe_path, &attr, argv, envp.get());
       if (log) {
         for (int ii = 0; argv[ii]; ++ii)
           LLDB_LOG(log, "argv[{0}] = '{1}'", ii, argv[ii]);
Index: lldb/trunk/source/Host/freebsd/Host.cpp
===================================================================
--- lldb/trunk/source/Host/freebsd/Host.cpp
+++ lldb/trunk/source/Host/freebsd/Host.cpp
@@ -243,14 +243,7 @@
   return false;
 }
 
-size_t Host::GetEnvironment(StringList &env) {
-  char **host_env = environ;
-  char *env_entry;
-  size_t i;
-  for (i = 0; (env_entry = host_env[i]) != NULL; ++i)
-    env.AppendString(env_entry);
-  return i;
-}
+Environment Host::GetEnvironment() { return Environment(environ); }
 
 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
   return Status("unimplemented");
Index: lldb/trunk/source/API/SBPlatform.cpp
===================================================================
--- lldb/trunk/source/API/SBPlatform.cpp
+++ lldb/trunk/source/API/SBPlatform.cpp
@@ -416,7 +416,10 @@
 
 SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
-    return platform_sp->LaunchProcess(launch_info.ref());
+    ProcessLaunchInfo info = launch_info.ref();
+    Status error = platform_sp->LaunchProcess(info);
+    launch_info.set_ref(info);
+    return error;
   });
 }
 
Index: lldb/trunk/source/API/SBTarget.cpp
===================================================================
--- lldb/trunk/source/API/SBTarget.cpp
+++ lldb/trunk/source/API/SBTarget.cpp
@@ -290,7 +290,7 @@
     if (argv)
       launch_info.GetArguments().AppendArguments(argv);
     if (envp)
-      launch_info.GetEnvironmentEntries().SetArguments(envp);
+      launch_info.GetEnvironment() = Environment(envp);
 
     if (listener.IsValid())
       launch_info.SetListener(listener.GetSP());
@@ -340,7 +340,7 @@
       }
     }
 
-    lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref();
+    lldb_private::ProcessLaunchInfo launch_info = sb_launch_info.ref();
 
     if (!launch_info.GetExecutableFile()) {
       Module *exe_module = target_sp->GetExecutableModulePointer();
@@ -353,6 +353,7 @@
       launch_info.GetArchitecture() = arch_spec;
 
     error.SetError(target_sp->Launch(launch_info, NULL));
+    sb_launch_info.set_ref(launch_info);
     sb_process.SetSP(target_sp->GetProcessSP());
   } else {
     error.SetErrorString("SBTarget is invalid");
@@ -2195,7 +2196,7 @@
   lldb::SBLaunchInfo launch_info(NULL);
   TargetSP target_sp(GetSP());
   if (target_sp)
-    launch_info.ref() = m_opaque_sp->GetProcessLaunchInfo();
+    launch_info.set_ref(m_opaque_sp->GetProcessLaunchInfo());
   return launch_info;
 }
 
Index: lldb/trunk/source/API/SBProcess.cpp
===================================================================
--- lldb/trunk/source/API/SBProcess.cpp
+++ lldb/trunk/source/API/SBProcess.cpp
@@ -140,7 +140,7 @@
       if (argv)
         launch_info.GetArguments().AppendArguments(argv);
       if (envp)
-        launch_info.GetEnvironmentEntries().SetArguments(envp);
+        launch_info.GetEnvironment() = Environment(envp);
       error.SetError(process_sp->Launch(launch_info));
     } else {
       error.SetErrorString("must be in eStateConnected to call RemoteLaunch");
Index: lldb/trunk/source/API/SBLaunchInfo.cpp
===================================================================
--- lldb/trunk/source/API/SBLaunchInfo.cpp
+++ lldb/trunk/source/API/SBLaunchInfo.cpp
@@ -16,21 +16,41 @@
 using namespace lldb;
 using namespace lldb_private;
 
+class lldb_private::SBLaunchInfoImpl : public ProcessLaunchInfo {
+public:
+  SBLaunchInfoImpl()
+      : ProcessLaunchInfo(), m_envp(GetEnvironment().getEnvp()) {}
+
+  const char *const *GetEnvp() const { return m_envp; }
+  void RegenerateEnvp() { m_envp = GetEnvironment().getEnvp(); }
+
+  SBLaunchInfoImpl &operator=(const ProcessLaunchInfo &rhs) {
+    ProcessLaunchInfo::operator=(rhs);
+    RegenerateEnvp();
+    return *this;
+  }
+
+private:
+  Environment::Envp m_envp;
+};
+
 SBLaunchInfo::SBLaunchInfo(const char **argv)
-    : m_opaque_sp(new ProcessLaunchInfo()) {
+    : m_opaque_sp(new SBLaunchInfoImpl()) {
   m_opaque_sp->GetFlags().Reset(eLaunchFlagDebug | eLaunchFlagDisableASLR);
   if (argv && argv[0])
     m_opaque_sp->GetArguments().SetArguments(argv);
 }
 
 SBLaunchInfo::~SBLaunchInfo() {}
 
-lldb_private::ProcessLaunchInfo &SBLaunchInfo::ref() { return *m_opaque_sp; }
-
 const lldb_private::ProcessLaunchInfo &SBLaunchInfo::ref() const {
   return *m_opaque_sp;
 }
 
+void SBLaunchInfo::set_ref(const ProcessLaunchInfo &info) {
+  *m_opaque_sp = info;
+}
+
 lldb::pid_t SBLaunchInfo::GetProcessID() { return m_opaque_sp->GetProcessID(); }
 
 uint32_t SBLaunchInfo::GetUserID() { return m_opaque_sp->GetUserID(); }
@@ -83,23 +103,22 @@
 }
 
 uint32_t SBLaunchInfo::GetNumEnvironmentEntries() {
-  return m_opaque_sp->GetEnvironmentEntries().GetArgumentCount();
+  return m_opaque_sp->GetEnvironment().size();
 }
 
 const char *SBLaunchInfo::GetEnvironmentEntryAtIndex(uint32_t idx) {
-  return m_opaque_sp->GetEnvironmentEntries().GetArgumentAtIndex(idx);
+  if (idx > GetNumEnvironmentEntries())
+    return nullptr;
+  return m_opaque_sp->GetEnvp()[idx];
 }
 
 void SBLaunchInfo::SetEnvironmentEntries(const char **envp, bool append) {
-  if (append) {
-    if (envp)
-      m_opaque_sp->GetEnvironmentEntries().AppendArguments(envp);
-  } else {
-    if (envp)
-      m_opaque_sp->GetEnvironmentEntries().SetArguments(envp);
-    else
-      m_opaque_sp->GetEnvironmentEntries().Clear();
-  }
+  Environment env(envp);
+  if (append)
+    m_opaque_sp->GetEnvironment().insert(env.begin(), env.end());
+  else
+    m_opaque_sp->GetEnvironment() = env;
+  m_opaque_sp->RegenerateEnvp();
 }
 
 void SBLaunchInfo::Clear() { m_opaque_sp->Clear(); }
Index: lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp
===================================================================
--- lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp
+++ lldb/trunk/tools/lldb-server/lldb-gdbserver.cpp
@@ -188,10 +188,7 @@
     exit(1);
   }
   info.SetWorkingDirectory(FileSpec(cwd, true));
-
-  StringList env;
-  Host::GetEnvironment(env);
-  info.GetEnvironmentEntries() = Args(env);
+  info.GetEnvironment() = Host::GetEnvironment();
 
   gdb_server.SetLaunchInfo(info);
 
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to