Introduce fxcrt::AutoNuller<T>.

Similar to fxcrt::AutoRestorer<T>, but always reverts to nullptr
regardless of the initial value. Useful for when we can't store a
|T| oldvalue on the stack, or if |T| isn't copy-constructable, or if
we don't require the location to be pre-initialized to a legitimate
value.

Remove CPDF_StreamParserAutoClearer in favor of this.

Change-Id: I0258a5dc1c62b86f75d5c95a4915b3990e0c61ff
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/72832
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fpdfapi/page/cpdf_streamcontentparser.cpp b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
index 1ad817e..d0ec381 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -34,6 +34,7 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/autonuller.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxge/cfx_graphstatedata.h"
 #include "third_party/base/logging.h"
@@ -58,19 +59,6 @@
 const char kPathOperatorClosePath = 'h';
 const char kPathOperatorRectangle[] = "re";
 
-class CPDF_StreamParserAutoClearer {
- public:
-  CPDF_StreamParserAutoClearer(UnownedPtr<CPDF_StreamParser>* scoped_variable,
-                               CPDF_StreamParser* new_parser)
-      : scoped_variable_(scoped_variable) {
-    *scoped_variable_ = new_parser;
-  }
-  ~CPDF_StreamParserAutoClearer() { *scoped_variable_ = nullptr; }
-
- private:
-  UnownedPtr<CPDF_StreamParser>* scoped_variable_;
-};
-
 CFX_FloatRect GetShadingBBox(CPDF_ShadingPattern* pShading,
                              const CFX_Matrix& matrix) {
   ShadingType type = pShading->GetShadingType();
@@ -1515,7 +1503,8 @@
   uint32_t init_obj_count = m_pObjectHolder->GetPageObjectCount();
   CPDF_StreamParser syntax(pdfium::make_span(pDataStart, size_left),
                            m_pDocument->GetByteStringPool());
-  CPDF_StreamParserAutoClearer auto_clearer(&m_pSyntax, &syntax);
+  AutoNuller<UnownedPtr<CPDF_StreamParser>> auto_clearer(&m_pSyntax);
+  m_pSyntax = &syntax;
   while (1) {
     uint32_t cost = m_pObjectHolder->GetPageObjectCount() - init_obj_count;
     if (max_cost && cost >= max_cost) {
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 23d3e78..128c7a5 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -7,6 +7,7 @@
 
 source_set("fxcrt") {
   sources = [
+    "autonuller.h",
     "autorestorer.h",
     "byteorder.h",
     "bytestring.cpp",
@@ -132,6 +133,7 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "autonuller_unittest.cpp",
     "autorestorer_unittest.cpp",
     "byteorder_unittest.cpp",
     "bytestring_unittest.cpp",
diff --git a/core/fxcrt/autonuller.h b/core/fxcrt/autonuller.h
new file mode 100644
index 0000000..3a9bbdf
--- /dev/null
+++ b/core/fxcrt/autonuller.h
@@ -0,0 +1,28 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_AUTONULLER_H_
+#define CORE_FXCRT_AUTONULLER_H_
+
+namespace fxcrt {
+
+template <typename T>
+class AutoNuller {
+ public:
+  explicit AutoNuller(T* location) : m_Location(location) {}
+  ~AutoNuller() {
+    if (m_Location)
+      *m_Location = nullptr;
+  }
+  void AbandonNullification() { m_Location = nullptr; }
+
+ private:
+  T* m_Location;
+};
+
+}  // namespace fxcrt
+
+using fxcrt::AutoNuller;
+
+#endif  // CORE_FXCRT_AUTONULLER_H_
diff --git a/core/fxcrt/autonuller_unittest.cpp b/core/fxcrt/autonuller_unittest.cpp
new file mode 100644
index 0000000..482281e
--- /dev/null
+++ b/core/fxcrt/autonuller_unittest.cpp
@@ -0,0 +1,53 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/autonuller.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(fxcrt, AutoNuller) {
+  int x = 5;
+  int* ptr;
+  {
+    AutoNuller<int*> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+  }
+  EXPECT_EQ(nullptr, ptr);
+}
+
+TEST(fxcrt, AutoNullerAbandon) {
+  int x = 5;
+  int* ptr;
+  {
+    AutoNuller<int*> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+    nuller.AbandonNullification();
+  }
+  EXPECT_EQ(&x, ptr);
+}
+
+TEST(fxcrt, AutoNullerUnownedPtr) {
+  int x = 5;
+  UnownedPtr<int> ptr;
+  {
+    AutoNuller<UnownedPtr<int>> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+  }
+  EXPECT_EQ(nullptr, ptr);
+}
+
+TEST(fxcrt, AutoNullerUnownedPtrAbandon) {
+  int x = 5;
+  UnownedPtr<int> ptr;
+  {
+    AutoNuller<UnownedPtr<int>> nuller(&ptr);
+    ptr = &x;
+    EXPECT_EQ(&x, ptr);
+    nuller.AbandonNullification();
+  }
+  EXPECT_EQ(&x, ptr);
+}