Remove BindingEnv memory leaks.

Apart from the top-level one from the State instance,
all BindingEnv instances created by sub-parsers (i.e. when
processing included Ninja build plans) were leaked.

This patch ensures that all BindingEnv objects are owned
by the State instance, and destroyed with it.

Fuchsia-Topic: persistent-mode
Original-Change-Id: I00752fe23bdc7f537e01fee17a43fc0f3674b6ac
Change-Id: I9102fcdda54e9037def31b0c8ad0415952f8432e
Reviewed-on: https://fuchsia-review.googlesource.com/c/third_party/github.com/ninja-build/ninja/+/977218
Reviewed-by: David Fang <fangism@google.com>
Commit-Queue: David Turner <digit@google.com>
diff --git a/src/build.cc b/src/build.cc
index 4a280ae..b1012a7 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -525,7 +525,7 @@
       scan_(state, build_log, deps_log, disk_interface,
             &config_.depfile_parser_options) {
   lock_file_path_ = ".ninja_lock";
-  string build_dir = state_->bindings_.LookupVariable("builddir");
+  string build_dir = state_->bindings().LookupVariable("builddir");
   if (!build_dir.empty())
     lock_file_path_ = build_dir + "/" + lock_file_path_;
 }
diff --git a/src/clean.cc b/src/clean.cc
index 2374ad0..e48eb8e 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -249,7 +249,7 @@
   assert(rule);
 
   Reset();
-  const Rule* r = state_->bindings_.LookupRule(rule);
+  const Rule* r = state_->bindings().LookupRule(rule);
   if (r) {
     CleanRule(r);
   } else {
@@ -267,7 +267,7 @@
   LoadDyndeps();
   for (int i = 0; i < rule_count; ++i) {
     const char* rule_name = rules[i];
-    const Rule* rule = state_->bindings_.LookupRule(rule_name);
+    const Rule* rule = state_->bindings().LookupRule(rule_name);
     if (rule) {
       if (IsVerbose())
         printf("Rule %s\n", rule_name);
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index c52925c..67a9dee 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -31,7 +31,7 @@
                                ManifestParserOptions options)
     : Parser(state, file_reader),
       options_(options), quiet_(false) {
-  env_ = &state->bindings_;
+  env_ = &state->bindings();
 }
 
 bool ManifestParser::Parse(const string& filename, const string& input,
@@ -307,7 +307,7 @@
 
   // Bindings on edges are rare, so allocate per-edge envs only when needed.
   bool has_indent_token = lexer_.PeekToken(Lexer::INDENT);
-  BindingEnv* env = has_indent_token ? new BindingEnv(env_) : env_;
+  BindingEnv* env = has_indent_token ? state_->CreateNewEnv(env_) : env_;
   while (has_indent_token) {
     string key;
     EvalString val;
@@ -421,7 +421,7 @@
 
   ManifestParser subparser(state_, file_reader_, options_);
   if (new_scope) {
-    subparser.env_ = new BindingEnv(env_);
+    subparser.env_ = state_->CreateNewEnv(env_);
   } else {
     subparser.env_ = env_;
   }
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index e41d8ae..4de646d 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -50,8 +50,8 @@
 "\n"
 "build result: cat in_1.cc in-2.O\n"));
 
-  ASSERT_EQ(3u, state.bindings_.GetRules().size());
-  const Rule* rule = state.bindings_.GetRules().begin()->second.get();
+  ASSERT_EQ(3u, state.bindings().GetRules().size());
+  const Rule* rule = state.bindings().GetRules().begin()->second.get();
   EXPECT_EQ("cat", rule->name());
   EXPECT_EQ("[cat ][$in][ > ][$out]",
             rule->GetBinding("command")->Serialize());
@@ -83,8 +83,8 @@
 "build result: cat in_1.cc in-2.O\n"
 "  #comment\n"));
 
-  ASSERT_EQ(2u, state.bindings_.GetRules().size());
-  const Rule* rule = state.bindings_.GetRules().begin()->second.get();
+  ASSERT_EQ(2u, state.bindings().GetRules().size());
+  const Rule* rule = state.bindings().GetRules().begin()->second.get();
   EXPECT_EQ("cat", rule->name());
   Edge* edge = state.GetNode("result", 0)->in_edge();
   EXPECT_TRUE(edge->GetBindingBool("restat"));
@@ -103,7 +103,7 @@
 "variable=1\n"));
 
   // the variable must be in the top level environment
-  EXPECT_EQ("1", state.bindings_.LookupVariable("variable"));
+  EXPECT_EQ("1", state.bindings().LookupVariable("variable"));
 }
 
 TEST_F(ParserTest, ResponseFiles) {
@@ -116,8 +116,8 @@
 "build out: cat_rsp in\n"
 "  rspfile=out.rsp\n"));
 
-  ASSERT_EQ(2u, state.bindings_.GetRules().size());
-  const Rule* rule = state.bindings_.GetRules().begin()->second.get();
+  ASSERT_EQ(2u, state.bindings().GetRules().size());
+  const Rule* rule = state.bindings().GetRules().begin()->second.get();
   EXPECT_EQ("cat_rsp", rule->name());
   EXPECT_EQ("[cat ][$rspfile][ > ][$out]",
             rule->GetBinding("command")->Serialize());
@@ -133,8 +133,8 @@
 "build out: cat_rsp in in2\n"
 "  rspfile=out.rsp\n"));
 
-  ASSERT_EQ(2u, state.bindings_.GetRules().size());
-  const Rule* rule = state.bindings_.GetRules().begin()->second.get();
+  ASSERT_EQ(2u, state.bindings().GetRules().size());
+  const Rule* rule = state.bindings().GetRules().begin()->second.get();
   EXPECT_EQ("cat_rsp", rule->name());
   EXPECT_EQ("[cat ][$in_newline][ > ][$out]",
             rule->GetBinding("command")->Serialize());
@@ -161,7 +161,7 @@
   Edge* edge = state.edges_[0];
   EXPECT_EQ("ld one-letter-test -pthread -under -o a b c",
             edge->EvaluateCommand());
-  EXPECT_EQ("1/2", state.bindings_.LookupVariable("nested2"));
+  EXPECT_EQ("1/2", state.bindings().LookupVariable("nested2"));
 
   edge = state.edges_[1];
   EXPECT_EQ("ld one-letter-test 1/2/3 -under -o supernested x",
@@ -194,8 +194,8 @@
 "build a: link c $\n"
 " d e f\n"));
 
-  ASSERT_EQ(2u, state.bindings_.GetRules().size());
-  const Rule* rule = state.bindings_.GetRules().begin()->second.get();
+  ASSERT_EQ(2u, state.bindings().GetRules().size());
+  const Rule* rule = state.bindings().GetRules().begin()->second.get();
   EXPECT_EQ("link", rule->name());
   EXPECT_EQ("[foo bar baz]", rule->GetBinding("command")->Serialize());
 }
@@ -205,15 +205,15 @@
 "foo = bar\\baz\n"
 "foo2 = bar\\ baz\n"
 ));
-  EXPECT_EQ("bar\\baz", state.bindings_.LookupVariable("foo"));
-  EXPECT_EQ("bar\\ baz", state.bindings_.LookupVariable("foo2"));
+  EXPECT_EQ("bar\\baz", state.bindings().LookupVariable("foo"));
+  EXPECT_EQ("bar\\ baz", state.bindings().LookupVariable("foo2"));
 }
 
 TEST_F(ParserTest, Comment) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(
 "# this is a comment\n"
 "foo = not # a comment\n"));
-  EXPECT_EQ("not # a comment", state.bindings_.LookupVariable("foo"));
+  EXPECT_EQ("not # a comment", state.bindings().LookupVariable("foo"));
 }
 
 TEST_F(ParserTest, Dollars) {
@@ -224,7 +224,7 @@
 "x = $$dollar\n"
 "build $x: foo y\n"
 ));
-  EXPECT_EQ("$dollar", state.bindings_.LookupVariable("x"));
+  EXPECT_EQ("$dollar", state.bindings().LookupVariable("x"));
 #ifdef _WIN32
   EXPECT_EQ("$dollarbar$baz$blah", state.edges_[0]->EvaluateCommand());
 #else
@@ -905,7 +905,7 @@
 
   ASSERT_EQ(1u, fs_.files_read_.size());
   EXPECT_EQ("include.ninja", fs_.files_read_[0]);
-  EXPECT_EQ("inner", state.bindings_.LookupVariable("var"));
+  EXPECT_EQ("inner", state.bindings().LookupVariable("var"));
 }
 
 TEST_F(ParserTest, BrokenInclude) {
diff --git a/src/ninja.cc b/src/ninja.cc
index c08b20e..4739510 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -658,7 +658,7 @@
 
   // Print rules
 
-  for (const auto& pair : state_.bindings_.GetRules()) {
+  for (const auto& pair : state_.bindings().GetRules()) {
     printf("%s", pair.first.c_str());
     if (print_description) {
     const Rule* rule = pair.second.get();
@@ -1321,7 +1321,7 @@
 }
 
 bool NinjaMain::EnsureBuildDirExists() {
-  build_dir_ = state_.bindings_.LookupVariable("builddir");
+  build_dir_ = state_.bindings().LookupVariable("builddir");
   if (!build_dir_.empty() && !config_.dry_run) {
     if (!disk_interface_.MakeDirs(build_dir_ + "/.") && errno != EEXIST) {
       Error("creating build directory %s: %s",
diff --git a/src/state.cc b/src/state.cc
index cc78d03..84d08a4 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -68,8 +68,14 @@
 }
 
 State::State() {
+  // Create top-level scope.
+  bindings_.emplace_back(new BindingEnv());
+
+  // Add the phony rule to it.
   phony_rule_ = new Rule("phony");
-  bindings_.AddRule(phony_rule_);
+  bindings_[0]->AddRule(phony_rule_);
+
+  // Create default and console pools.
   default_pool_ = new Pool("", 0);
   console_pool_ = new Pool("console", 1);
   AddPool(default_pool_);
@@ -102,7 +108,7 @@
   Edge* edge = new Edge();
   edge->rule_ = rule;
   edge->pool_ = default_pool_;
-  edge->env_ = &bindings_;
+  edge->env_ = bindings_[0].get();
   edge->id_ = edges_.size();
   edges_.push_back(edge);
   return edge;
@@ -204,6 +210,11 @@
   return defaults_.empty() ? RootNodes(err) : defaults_;
 }
 
+BindingEnv* State::CreateNewEnv(BindingEnv* parent) {
+  bindings_.emplace_back(new BindingEnv(parent));
+  return bindings_.back().get();
+}
+
 void State::Reset() {
   METRIC_RECORD("state reset");
   for (auto& pair : pools_)
diff --git a/src/state.h b/src/state.h
index 958463b..7000637 100644
--- a/src/state.h
+++ b/src/state.h
@@ -154,6 +154,12 @@
   std::vector<Node*> RootNodes(std::string* error) const;
   std::vector<Node*> DefaultNodes(std::string* error) const;
 
+  /// Create new BindingEnv instance.
+  BindingEnv* CreateNewEnv(BindingEnv* parent);
+
+  /// Return top-level BindingEnv instance.
+  BindingEnv& bindings() const { return *bindings_[0]; }
+
   /// Mapping of path -> Node.
   /// NOTE: This owns all Node instances, but a DepsLog instance contains
   /// pointers to them as well.
@@ -172,7 +178,12 @@
   std::vector<Edge*> edges_;
 
   Rule* phony_rule_ = nullptr;
-  BindingEnv bindings_;
+
+  /// Note: BindingEnv need pointer stability (as they all include a parent
+  /// pointer)
+  std::vector<std::unique_ptr<BindingEnv>> bindings_;
+
+  /// Points to elements of paths_.
   std::vector<Node*> defaults_;
 };
 
diff --git a/src/state_test.cc b/src/state_test.cc
index e0e3060..7b6dd8f 100644
--- a/src/state_test.cc
+++ b/src/state_test.cc
@@ -31,7 +31,7 @@
 
   Rule* rule = new Rule("cat");
   rule->AddBinding("command", command);
-  state.bindings_.AddRule(rule);
+  state.bindings().AddRule(rule);
 
   Edge* edge = state.AddEdge(rule);
   state.AddIn(edge, "in1", 0);