summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLei Zhang <thestig@chromium.org>2015-09-22 19:15:49 -0700
committerLei Zhang <thestig@chromium.org>2015-09-22 19:15:49 -0700
commitcef2a9c51bee4b987fc813013d45dad6535a9a46 (patch)
tree6bb1cb91c327f9664ab510bca617205b638f1c6d
parent3b4382a847b5a7439a3107512dbe54c317108579 (diff)
downloadpdfium-cef2a9c51bee4b987fc813013d45dad6535a9a46.tar.xz
Change nonstd::unique_ptr to take a custom deleter.
Code is mostly stolen from Chromium's scoped_ptr. - Add unit tests. - Use this to fix a leak. BUG=chromium:531408 R=jyasskin@chromium.org, tsepez@chromium.org Review URL: https://codereview.chromium.org/1351383004 .
-rw-r--r--BUILD.gn1
-rw-r--r--core/include/fxcrt/fx_basic.h8
-rw-r--r--core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp7
-rw-r--r--third_party/base/nonstd_unique_ptr.h321
-rw-r--r--third_party/base/nonstd_unique_ptr_unittest.cpp303
-rw-r--r--third_party/base/template_util.h46
6 files changed, 587 insertions, 99 deletions
diff --git a/BUILD.gn b/BUILD.gn
index bba48180a9..ec28f17975 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -740,6 +740,7 @@ test("pdfium_unittests") {
"core/src/fxcrt/fx_system_unittest.cpp",
"testing/fx_string_testhelpers.cpp",
"testing/fx_string_testhelpers.h",
+ "third_party/base/nonstd_unique_ptr_unittest.cpp",
]
deps = [
"//testing/gtest",
diff --git a/core/include/fxcrt/fx_basic.h b/core/include/fxcrt/fx_basic.h
index bc3d81200f..3e556f5439 100644
--- a/core/include/fxcrt/fx_basic.h
+++ b/core/include/fxcrt/fx_basic.h
@@ -947,6 +947,13 @@ class CFX_AutoRestorer {
T m_OldValue;
};
+// Used with nonstd::unique_ptr to Release() objects that can't be deleted.
+template <class T>
+struct ReleaseDeleter {
+ inline void operator()(T* ptr) const { ptr->Release(); }
+};
+
+// TODO(thestig) Remove in favor of nonstd::unique_ptr.
template <class T>
class CFX_SmartPointer {
public:
@@ -959,6 +966,7 @@ class CFX_SmartPointer {
protected:
T* m_pObj;
};
+
#define FX_DATALIST_LENGTH 1024
template <size_t unit>
class CFX_SortListArray {
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
index ff14a984bd..7482f0b8e4 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
@@ -2376,8 +2376,9 @@ CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
continue;
}
key = PDF_NameDecode(key);
- CPDF_Object* pObj = GetObject(pObjList, objnum, gennum);
- if (pObj == NULL) {
+ nonstd::unique_ptr<CPDF_Object, ReleaseDeleter<CPDF_Object>> obj(
+ GetObject(pObjList, objnum, gennum));
+ if (!obj) {
if (pDict) {
pDict->Release();
}
@@ -2394,7 +2395,7 @@ CPDF_Object* CPDF_SyntaxParser::GetObjectByStrict(
}
if (key.GetLength() > 1) {
pDict->AddValue(CFX_ByteStringC(key.c_str() + 1, key.GetLength() - 1),
- pObj);
+ obj.release());
}
}
if (pContext) {
diff --git a/third_party/base/nonstd_unique_ptr.h b/third_party/base/nonstd_unique_ptr.h
index d666e1eeb2..f519b345b1 100644
--- a/third_party/base/nonstd_unique_ptr.h
+++ b/third_party/base/nonstd_unique_ptr.h
@@ -73,6 +73,10 @@
#include <stddef.h>
#include <stdlib.h>
+#include <ostream>
+
+#include "template_util.h"
+
namespace nonstd {
// Replacement for move, but doesn't allow things that are already
@@ -82,47 +86,114 @@ T&& move(T& t) {
return static_cast<T&&>(t);
}
+// Function object which deletes its parameter, which must be a pointer.
+// If C is an array type, invokes 'delete[]' on the parameter; otherwise,
+// invokes 'delete'. The default deleter for unique_ptr<T>.
+template <class T>
+struct DefaultDeleter {
+ DefaultDeleter() {}
+ template <typename U>
+ DefaultDeleter(const DefaultDeleter<U>& other) {
+ // IMPLEMENTATION NOTE: C++11 20.7.1.1.2p2 only provides this constructor
+ // if U* is implicitly convertible to T* and U is not an array type.
+ //
+ // Correct implementation should use SFINAE to disable this
+ // constructor. However, since there are no other 1-argument constructors,
+ // using a static_assert() based on is_convertible<> and requiring
+ // complete types is simpler and will cause compile failures for equivalent
+ // misuses.
+ //
+ // Note, the is_convertible<U*, T*> check also ensures that U is not an
+ // array. T is guaranteed to be a non-array, so any U* where U is an array
+ // cannot convert to T*.
+ enum { T_must_be_complete = sizeof(T) };
+ enum { U_must_be_complete = sizeof(U) };
+ static_assert((pdfium::base::is_convertible<U*, T*>::value),
+ "U_ptr_must_implicitly_convert_to_T_ptr");
+ }
+ inline void operator()(T* ptr) const {
+ enum { type_must_be_complete = sizeof(T) };
+ delete ptr;
+ }
+};
+
+// Specialization of DefaultDeleter for array types.
+template <class T>
+struct DefaultDeleter<T[]> {
+ inline void operator()(T* ptr) const {
+ enum { type_must_be_complete = sizeof(T) };
+ delete[] ptr;
+ }
+
+ private:
+ // Disable this operator for any U != T because it is undefined to execute
+ // an array delete when the static type of the array mismatches the dynamic
+ // type.
+ //
+ // References:
+ // C++98 [expr.delete]p3
+ // http://cplusplus.github.com/LWG/lwg-defects.html#938
+ template <typename U>
+ void operator()(U* array) const;
+};
+
+template <class T, int n>
+struct DefaultDeleter<T[n]> {
+ // Never allow someone to declare something like unique_ptr<int[10]>.
+ static_assert(sizeof(T) == -1, "do_not_use_array_with_size_as_type");
+};
+
+namespace internal {
+
// Common implementation for both pointers to elements and pointers to
// arrays. These are differentiated below based on the need to invoke
// delete vs. delete[] as appropriate.
-template <class C>
+template <class C, class D>
class unique_ptr_base {
public:
-
// The element type
typedef C element_type;
- explicit unique_ptr_base(C* p) : ptr_(p) { }
+ explicit unique_ptr_base(C* p) : data_(p) {}
+
+ // Initializer for deleters that have data parameters.
+ unique_ptr_base(C* p, const D& d) : data_(p, d) {}
// Move constructor.
- unique_ptr_base(unique_ptr_base<C>&& that) {
- ptr_ = that.ptr_;
- that.ptr_ = nullptr;
- }
+ unique_ptr_base(unique_ptr_base<C, D>&& that)
+ : data_(that.release(), that.get_deleter()) {}
- // Accessors to get the owned object.
- // operator* and operator-> will assert() if there is no current object.
- C& operator*() const {
- assert(ptr_ != NULL);
- return *ptr_;
+ ~unique_ptr_base() {
+ enum { type_must_be_complete = sizeof(C) };
+ if (data_.ptr != nullptr) {
+ // Not using get_deleter() saves one function call in non-optimized
+ // builds.
+ static_cast<D&>(data_)(data_.ptr);
+ }
}
- C* operator->() const {
- assert(ptr_ != NULL);
- return ptr_;
+
+ void reset(C* p = nullptr) {
+ C* old = data_.ptr;
+ data_.ptr = p;
+ if (old != nullptr)
+ static_cast<D&>(data_)(old);
}
- C* get() const { return ptr_; }
+
+ C* get() const { return data_.ptr; }
+ D& get_deleter() { return data_; }
+ const D& get_deleter() const { return data_; }
// Comparison operators.
// These return whether two unique_ptr refer to the same object, not just to
// two different but equal objects.
- bool operator==(C* p) const { return ptr_ == p; }
- bool operator!=(C* p) const { return ptr_ != p; }
+ bool operator==(C* p) const { return data_.ptr == p; }
+ bool operator!=(C* p) const { return data_.ptr != p; }
- // Swap two scoped pointers.
+ // Swap two unique pointers.
void swap(unique_ptr_base& p2) {
- C* tmp = ptr_;
- ptr_ = p2.ptr_;
- p2.ptr_ = tmp;
+ Data tmp = data_;
+ data_ = p2.data_;
+ p2.data_ = tmp;
}
// Release a pointer.
@@ -131,125 +202,180 @@ class unique_ptr_base {
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
C* release() {
- C* retVal = ptr_;
- ptr_ = NULL;
- return retVal;
+ C* ptr = data_.ptr;
+ data_.ptr = nullptr;
+ return ptr;
}
// Allow promotion to bool for conditional statements.
- explicit operator bool() const { return ptr_ != NULL; }
+ explicit operator bool() const { return data_.ptr != nullptr; }
protected:
- C* ptr_;
+ // Use the empty base class optimization to allow us to have a D
+ // member, while avoiding any space overhead for it when D is an
+ // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good
+ // discussion of this technique.
+ struct Data : public D {
+ explicit Data(C* ptr_in) : ptr(ptr_in) {}
+ Data(C* ptr_in, const D& other) : D(other), ptr(ptr_in) {}
+ C* ptr;
+ };
+
+ Data data_;
};
+} // namespace internal
+
// Implementation for ordinary pointers using delete.
-template <class C>
-class unique_ptr : public unique_ptr_base<C> {
+template <class C, class D = DefaultDeleter<C>>
+class unique_ptr : public internal::unique_ptr_base<C, D> {
public:
- using unique_ptr_base<C>::ptr_;
+ // Constructor. Defaults to initializing with nullptr.
+ unique_ptr() : internal::unique_ptr_base<C, D>(nullptr) {}
- // Constructor. Defaults to initializing with NULL. There is no way
- // to create an uninitialized unique_ptr. The input parameter must be
- // allocated with new (not new[] - see below).
- explicit unique_ptr(C* p = NULL) : unique_ptr_base<C>(p) { }
+ // Constructor. Takes ownership of p.
+ explicit unique_ptr(C* p) : internal::unique_ptr_base<C, D>(p) {}
- // Move constructor.
- unique_ptr(unique_ptr<C>&& that) : unique_ptr_base<C>(nonstd::move(that)) {}
+ // Constructor. Allows initialization of a stateful deleter.
+ unique_ptr(C* p, const D& d) : internal::unique_ptr_base<C, D>(p, d) {}
- // Destructor. If there is a C object, delete it.
- // We don't need to test ptr_ == NULL because C++ does that for us.
- ~unique_ptr() {
- enum { type_must_be_complete = sizeof(C) };
- delete ptr_;
- }
+ // Constructor. Allows construction from a nullptr.
+ unique_ptr(decltype(nullptr)) : internal::unique_ptr_base<C, D>(nullptr) {}
- // Reset. Deletes the current owned object, if any.
- // Then takes ownership of a new object, if given.
- // this->reset(this->get()) works.
- void reset(C* p = NULL) {
- if (p != ptr_) {
- enum { type_must_be_complete = sizeof(C) };
- C* old_ptr = ptr_;
- ptr_ = p;
- delete old_ptr;
- }
+ // Move constructor.
+ unique_ptr(unique_ptr&& that)
+ : internal::unique_ptr_base<C, D>(nonstd::move(that)) {}
+
+ // operator=. Allows assignment from a nullptr. Deletes the currently owned
+ // object, if any.
+ unique_ptr& operator=(decltype(nullptr)) {
+ this->reset();
+ return *this;
}
// Move assignment.
unique_ptr<C>& operator=(unique_ptr<C>&& that) {
- if (that.ptr_ != ptr_)
- reset(that.release());
+ this->reset(that.release());
return *this;
}
-private:
- // Forbid comparison of unique_ptr types. If C2 != C, it totally doesn't
- // make sense, and if C2 == C, it still doesn't make sense because you should
- // never have the same object owned by two different unique_ptrs.
- template <class C2> bool operator==(unique_ptr<C2> const& p2) const;
- template <class C2> bool operator!=(unique_ptr<C2> const& p2) const;
+ // Accessors to get the owned object.
+ // operator* and operator-> will assert() if there is no current object.
+ C& operator*() const {
+ assert(this->data_.ptr != nullptr);
+ return *this->data_.ptr;
+ }
+ C* operator->() const {
+ assert(this->data_.ptr != nullptr);
+ return this->data_.ptr;
+ }
+
+ // Comparison operators.
+ // These return whether two unique_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(const C* p) const { return this->get() == p; }
+ bool operator!=(const C* p) const { return this->get() != p; }
+ private:
// Disallow evil constructors. It doesn't make sense to make a copy of
// something that's allegedly unique.
unique_ptr(const unique_ptr&) = delete;
void operator=(const unique_ptr&) = delete;
+
+ // Forbid comparison of unique_ptr types. If U != C, it totally
+ // doesn't make sense, and if U == C, it still doesn't make sense
+ // because you should never have the same object owned by two different
+ // unique_ptrs.
+ template <class U>
+ bool operator==(unique_ptr<U> const& p2) const;
+ template <class U>
+ bool operator!=(unique_ptr<U> const& p2) const;
};
// Specialization for arrays using delete[].
-template <class C>
-class unique_ptr<C[]> : public unique_ptr_base<C> {
+template <class C, class D>
+class unique_ptr<C[], D> : public internal::unique_ptr_base<C, D> {
public:
- using unique_ptr_base<C>::ptr_;
-
- // Constructor. Defaults to initializing with NULL. There is no way
- // to create an uninitialized unique_ptr. The input parameter must be
- // allocated with new[] (not new - see above).
- explicit unique_ptr(C* p = NULL) : unique_ptr_base<C>(p) { }
+ // Constructor. Defaults to initializing with nullptr.
+ unique_ptr() : internal::unique_ptr_base<C, D>(nullptr) {}
+
+ // Constructor. Stores the given array. Note that the argument's type
+ // must exactly match T*. In particular:
+ // - it cannot be a pointer to a type derived from T, because it is
+ // inherently unsafe in the general case to access an array through a
+ // pointer whose dynamic type does not match its static type (eg., if
+ // T and the derived types had different sizes access would be
+ // incorrectly calculated). Deletion is also always undefined
+ // (C++98 [expr.delete]p3). If you're doing this, fix your code.
+ // - it cannot be const-qualified differently from T per unique_ptr spec
+ // (http://cplusplus.github.com/LWG/lwg-active.html#2118). Users wanting
+ // to work around this may use const_cast<const T*>().
+ explicit unique_ptr(C* p) : internal::unique_ptr_base<C, D>(p) {}
+
+ // Constructor. Allows construction from a nullptr.
+ unique_ptr(decltype(nullptr)) : internal::unique_ptr_base<C, D>(nullptr) {}
// Move constructor.
- unique_ptr(unique_ptr<C>&& that) : unique_ptr_base<C>(nonstd::move(that)) {}
+ unique_ptr(unique_ptr&& that)
+ : internal::unique_ptr_base<C, D>(nonstd::move(that)) {}
- // Destructor. If there is a C object, delete it.
- // We don't need to test ptr_ == NULL because C++ does that for us.
- ~unique_ptr() {
- enum { type_must_be_complete = sizeof(C) };
- delete[] ptr_;
- }
-
- // Reset. Deletes the current owned object, if any.
- // Then takes ownership of a new object, if given.
- // this->reset(this->get()) works.
- void reset(C* p = NULL) {
- if (p != ptr_) {
- enum { type_must_be_complete = sizeof(C) };
- C* old_ptr = ptr_;
- ptr_ = p;
- delete[] old_ptr;
- }
+ // operator=. Allows assignment from a nullptr. Deletes the currently owned
+ // array, if any.
+ unique_ptr& operator=(decltype(nullptr)) {
+ this->reset();
+ return *this;
}
// Move assignment.
unique_ptr<C>& operator=(unique_ptr<C>&& that) {
- if (that.ptr_ != ptr_)
- reset(that.release());
+ this->reset(that.release());
return *this;
}
+ // Reset. Deletes the currently owned array, if any.
+ // Then takes ownership of a new object, if given.
+ void reset(C* array = nullptr) {
+ static_cast<internal::unique_ptr_base<C, D>*>(this)->reset(array);
+ }
+
// Support indexing since it is holding array.
- C& operator[] (size_t i) { return ptr_[i]; }
+ C& operator[](size_t i) { return this->data_.ptr[i]; }
-private:
- // Forbid comparison of unique_ptr types. If C2 != C, it totally doesn't
- // make sense, and if C2 == C, it still doesn't make sense because you should
- // never have the same object owned by two different unique_ptrs.
- template <class C2> bool operator==(unique_ptr<C2> const& p2) const;
- template <class C2> bool operator!=(unique_ptr<C2> const& p2) const;
+ // Comparison operators.
+ // These return whether two unique_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* array) const { return this->get() == array; }
+ bool operator!=(C* array) const { return this->get() != array; }
+
+ private:
+ // Disable initialization from any type other than element_type*, by
+ // providing a constructor that matches such an initialization, but is
+ // private and has no definition. This is disabled because it is not safe to
+ // call delete[] on an array whose static type does not match its dynamic
+ // type.
+ template <typename U>
+ explicit unique_ptr(U* array);
+ explicit unique_ptr(int disallow_construction_from_null);
+
+ // Disable reset() from any type other than element_type*, for the same
+ // reasons as the constructor above.
+ template <typename U>
+ void reset(U* array);
+ void reset(int disallow_reset_from_null);
// Disallow evil constructors. It doesn't make sense to make a copy of
// something that's allegedly unique.
unique_ptr(const unique_ptr&) = delete;
void operator=(const unique_ptr&) = delete;
+
+ // Forbid comparison of unique_ptr types. If U != C, it totally
+ // doesn't make sense, and if U == C, it still doesn't make sense
+ // because you should never have the same object owned by two different
+ // unique_ptrs.
+ template <class U>
+ bool operator==(unique_ptr<U> const& p2) const;
+ template <class U>
+ bool operator!=(unique_ptr<U> const& p2) const;
};
// Free functions
@@ -268,6 +394,11 @@ bool operator!=(C* p1, const unique_ptr<C>& p2) {
return p1 != p2.get();
}
+template <typename T>
+std::ostream& operator<<(std::ostream& out, const unique_ptr<T>& p) {
+ return out << p.get();
+}
+
} // namespace nonstd
#endif // NONSTD_UNIQUE_PTR_H_
diff --git a/third_party/base/nonstd_unique_ptr_unittest.cpp b/third_party/base/nonstd_unique_ptr_unittest.cpp
index 49cc901fe2..2b120581f4 100644
--- a/third_party/base/nonstd_unique_ptr_unittest.cpp
+++ b/third_party/base/nonstd_unique_ptr_unittest.cpp
@@ -85,3 +85,306 @@ TEST(UniquePtrTest, MoveTest) {
}
EXPECT_EQ(0, constructed);
}
+
+TEST(UniquePtrTest, UniquePtr) {
+ int constructed = 0;
+
+ // Ensure size of unique_ptr<> doesn't increase unexpectedly.
+ static_assert(sizeof(int*) >= sizeof(unique_ptr<int>),
+ "unique_ptr_larger_than_raw_ptr");
+
+ {
+ unique_ptr<CtorDtorLogger> scoper(new CtorDtorLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ EXPECT_EQ(10, scoper->SomeMeth(10));
+ EXPECT_EQ(10, scoper.get()->SomeMeth(10));
+ EXPECT_EQ(10, (*scoper).SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test reset() and release()
+ {
+ unique_ptr<CtorDtorLogger> scoper(new CtorDtorLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoper.reset(new CtorDtorLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ scoper.reset();
+ EXPECT_EQ(0, constructed);
+ EXPECT_FALSE(scoper.get());
+
+ scoper.reset(new CtorDtorLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+
+ CtorDtorLogger* take = scoper.release();
+ EXPECT_EQ(1, constructed);
+ EXPECT_FALSE(scoper.get());
+ delete take;
+ EXPECT_EQ(0, constructed);
+
+ scoper.reset(new CtorDtorLogger(&constructed));
+ EXPECT_EQ(1, constructed);
+ EXPECT_TRUE(scoper.get());
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test swap(), == and !=
+ {
+ unique_ptr<CtorDtorLogger> scoper1;
+ unique_ptr<CtorDtorLogger> scoper2;
+ EXPECT_TRUE(scoper1 == scoper2.get());
+ EXPECT_FALSE(scoper1 != scoper2.get());
+
+ CtorDtorLogger* logger = new CtorDtorLogger(&constructed);
+ scoper1.reset(logger);
+ EXPECT_EQ(logger, scoper1.get());
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+
+ scoper2.swap(scoper1);
+ EXPECT_EQ(logger, scoper2.get());
+ EXPECT_FALSE(scoper1.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(UniquePtrTest, UniquePtrWithArray) {
+ static const int kNumLoggers = 12;
+
+ int constructed = 0;
+
+ {
+ unique_ptr<CtorDtorLogger[]> scoper(new CtorDtorLogger[kNumLoggers]);
+ EXPECT_TRUE(scoper);
+ EXPECT_EQ(&scoper[0], scoper.get());
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+
+ EXPECT_EQ(10, scoper.get()->SomeMeth(10));
+ EXPECT_EQ(10, scoper[2].SomeMeth(10));
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test reset() and release()
+ {
+ unique_ptr<CtorDtorLogger[]> scoper;
+ EXPECT_FALSE(scoper.get());
+ EXPECT_FALSE(scoper.release());
+ EXPECT_FALSE(scoper.get());
+ scoper.reset();
+ EXPECT_FALSE(scoper.get());
+
+ scoper.reset(new CtorDtorLogger[kNumLoggers]);
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+ scoper.reset();
+ EXPECT_EQ(0, constructed);
+
+ scoper.reset(new CtorDtorLogger[kNumLoggers]);
+ for (int i = 0; i < kNumLoggers; ++i) {
+ scoper[i].SetPtr(&constructed);
+ }
+ EXPECT_EQ(12, constructed);
+ CtorDtorLogger* ptr = scoper.release();
+ EXPECT_EQ(12, constructed);
+ delete[] ptr;
+ EXPECT_EQ(0, constructed);
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test swap(), ==, !=, and type-safe Boolean.
+ {
+ unique_ptr<CtorDtorLogger[]> scoper1;
+ unique_ptr<CtorDtorLogger[]> scoper2;
+ EXPECT_TRUE(scoper1 == scoper2.get());
+ EXPECT_FALSE(scoper1 != scoper2.get());
+
+ CtorDtorLogger* loggers = new CtorDtorLogger[kNumLoggers];
+ for (int i = 0; i < kNumLoggers; ++i) {
+ loggers[i].SetPtr(&constructed);
+ }
+ scoper1.reset(loggers);
+ EXPECT_TRUE(scoper1);
+ EXPECT_EQ(loggers, scoper1.get());
+ EXPECT_FALSE(scoper2);
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+
+ scoper2.swap(scoper1);
+ EXPECT_EQ(loggers, scoper2.get());
+ EXPECT_FALSE(scoper1.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+ }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(UniquePtrTest, ReturnTypeBehavior) {
+ int constructed = 0;
+
+ // Test that we can return a unique_ptr.
+ {
+ CtorDtorLogger* logger = new CtorDtorLogger(&constructed);
+ unique_ptr<CtorDtorLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Test uncaught return type not leak.
+ {
+ CtorDtorLogger* logger = new CtorDtorLogger(&constructed);
+ unique_ptr<CtorDtorLogger> scoper(logger);
+ EXPECT_EQ(1, constructed);
+ }
+ EXPECT_EQ(0, constructed);
+
+ // Call TestReturnOfType() so the compiler doesn't warn for an unused
+ // function.
+ { TestReturnOfType(&constructed); }
+ EXPECT_EQ(0, constructed);
+}
+
+TEST(UniquePtrTest, CustomDeleter) {
+ double dummy_value; // Custom deleter never touches this value.
+ int deletes = 0;
+ int alternate_deletes = 0;
+
+ // Normal delete support.
+ {
+ deletes = 0;
+ unique_ptr<double, CountingDeleter> scoper(&dummy_value,
+ CountingDeleter(&deletes));
+ EXPECT_EQ(0, deletes);
+ EXPECT_TRUE(scoper.get());
+ }
+ EXPECT_EQ(1, deletes);
+
+ // Test reset() and release().
+ deletes = 0;
+ {
+ unique_ptr<double, CountingDeleter> scoper(nullptr,
+ CountingDeleter(&deletes));
+ EXPECT_FALSE(scoper.get());
+ EXPECT_FALSE(scoper.release());
+ EXPECT_FALSE(scoper.get());
+ scoper.reset();
+ EXPECT_FALSE(scoper.get());
+ EXPECT_EQ(0, deletes);
+
+ scoper.reset(&dummy_value);
+ scoper.reset();
+ EXPECT_EQ(1, deletes);
+
+ scoper.reset(&dummy_value);
+ EXPECT_EQ(&dummy_value, scoper.release());
+ }
+ EXPECT_EQ(1, deletes);
+
+ // Test get_deleter().
+ deletes = 0;
+ alternate_deletes = 0;
+ {
+ unique_ptr<double, CountingDeleter> scoper(&dummy_value,
+ CountingDeleter(&deletes));
+ // Call deleter manually.
+ EXPECT_EQ(0, deletes);
+ scoper.get_deleter()(&dummy_value);
+ EXPECT_EQ(1, deletes);
+
+ // Deleter is still there after reset.
+ scoper.reset();
+ EXPECT_EQ(2, deletes);
+ scoper.get_deleter()(&dummy_value);
+ EXPECT_EQ(3, deletes);
+
+ // Deleter can be assigned into (matches C++11 unique_ptr<> spec).
+ scoper.get_deleter() = CountingDeleter(&alternate_deletes);
+ scoper.reset(&dummy_value);
+ EXPECT_EQ(0, alternate_deletes);
+ }
+ EXPECT_EQ(3, deletes);
+ EXPECT_EQ(1, alternate_deletes);
+
+ // Test swap(), ==, !=, and type-safe Boolean.
+ {
+ unique_ptr<double, CountingDeleter> scoper1(nullptr,
+ CountingDeleter(&deletes));
+ unique_ptr<double, CountingDeleter> scoper2(nullptr,
+ CountingDeleter(&deletes));
+ EXPECT_TRUE(scoper1 == scoper2.get());
+ EXPECT_FALSE(scoper1 != scoper2.get());
+
+ scoper1.reset(&dummy_value);
+ EXPECT_TRUE(scoper1);
+ EXPECT_EQ(&dummy_value, scoper1.get());
+ EXPECT_FALSE(scoper2);
+ EXPECT_FALSE(scoper2.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+
+ scoper2.swap(scoper1);
+ EXPECT_EQ(&dummy_value, scoper2.get());
+ EXPECT_FALSE(scoper1.get());
+ EXPECT_FALSE(scoper1 == scoper2.get());
+ EXPECT_TRUE(scoper1 != scoper2.get());
+ }
+}
+
+unique_ptr<int> NullIntReturn() {
+ return nullptr;
+}
+
+TEST(UniquePtrTest, Nullptr) {
+ unique_ptr<int> scoper1(nullptr);
+ unique_ptr<int> scoper2(new int);
+ scoper2 = nullptr;
+ unique_ptr<int> scoper3(NullIntReturn());
+ unique_ptr<int> scoper4 = NullIntReturn();
+ EXPECT_EQ(nullptr, scoper1.get());
+ EXPECT_EQ(nullptr, scoper2.get());
+ EXPECT_EQ(nullptr, scoper3.get());
+ EXPECT_EQ(nullptr, scoper4.get());
+}
+
+unique_ptr<int[]> NullIntArrayReturn() {
+ return nullptr;
+}
+
+TEST(UniquePtrTest, NullptrArray) {
+ unique_ptr<int[]> scoper1(nullptr);
+ unique_ptr<int[]> scoper2(new int[3]);
+ scoper2 = nullptr;
+ unique_ptr<int[]> scoper3(NullIntArrayReturn());
+ unique_ptr<int[]> scoper4 = NullIntArrayReturn();
+ EXPECT_EQ(nullptr, scoper1.get());
+ EXPECT_EQ(nullptr, scoper2.get());
+ EXPECT_EQ(nullptr, scoper3.get());
+ EXPECT_EQ(nullptr, scoper4.get());
+}
+
+// Logging a unique_ptr<T> to an ostream shouldn't convert it to a boolean
+// value first.
+TEST(ScopedPtrTest, LoggingDoesntConvertToBoolean) {
+ unique_ptr<int> x(new int);
+ std::stringstream s1;
+ s1 << x;
+
+ std::stringstream s2;
+ s2 << x.get();
+
+ EXPECT_EQ(s2.str(), s1.str());
+}
diff --git a/third_party/base/template_util.h b/third_party/base/template_util.h
index ab660940f0..719f3f1c3a 100644
--- a/third_party/base/template_util.h
+++ b/third_party/base/template_util.h
@@ -21,7 +21,8 @@ typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
template <class T, class U> struct is_same : public false_type {};
-template <class T> struct is_same<T,T> : true_type {};
+template <class T>
+struct is_same<T, T> : true_type {};
template<bool B, class T = void>
struct enable_if {};
@@ -29,6 +30,49 @@ struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
+namespace internal {
+
+// Types YesType and NoType are guaranteed such that sizeof(YesType) <
+// sizeof(NoType).
+typedef char YesType;
+
+struct NoType {
+ YesType dummy[2];
+};
+
+// This class is an implementation detail for is_convertible, and you
+// don't need to know how it works to use is_convertible. For those
+// who care: we declare two different functions, one whose argument is
+// of type To and one with a variadic argument list. We give them
+// return types of different size, so we can use sizeof to trick the
+// compiler into telling us which function it would have chosen if we
+// had called it with an argument of type From. See Alexandrescu's
+// _Modern C++ Design_ for more details on this sort of trick.
+
+struct ConvertHelper {
+ template <typename To>
+ static YesType Test(To);
+
+ template <typename To>
+ static NoType Test(...);
+
+ template <typename From>
+ static From& Create();
+};
+
+} // namespace internal
+
+// Inherits from true_type if From is convertible to To, false_type otherwise.
+//
+// Note that if the type is convertible, this will be a true_type REGARDLESS
+// of whether or not the conversion would emit a warning.
+template <typename From, typename To>
+struct is_convertible
+ : integral_constant<bool,
+ sizeof(internal::ConvertHelper::Test<To>(
+ internal::ConvertHelper::Create<From>())) ==
+ sizeof(internal::YesType)> {};
+
} // namespace base
} // namespace pdfium