From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- mfbt/tests/TestNonDereferenceable.cpp | 171 ++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 mfbt/tests/TestNonDereferenceable.cpp (limited to 'mfbt/tests/TestNonDereferenceable.cpp') diff --git a/mfbt/tests/TestNonDereferenceable.cpp b/mfbt/tests/TestNonDereferenceable.cpp new file mode 100644 index 0000000000..2f8f7c1dd1 --- /dev/null +++ b/mfbt/tests/TestNonDereferenceable.cpp @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include "mozilla/Assertions.h" +#include "mozilla/NonDereferenceable.h" + +using mozilla::NonDereferenceable; + +#define CHECK MOZ_RELEASE_ASSERT + +void TestNonDereferenceableSimple() { + // Default construction. + NonDereferenceable nd0; + CHECK(!nd0); + CHECK(!nd0.value()); + + int i = 1; + int i2 = 2; + + // Construction with pointer. + NonDereferenceable nd1(&i); + CHECK(!!nd1); + CHECK(nd1.value() == reinterpret_cast(&i)); + + // Assignment with pointer. + nd1 = &i2; + CHECK(nd1.value() == reinterpret_cast(&i2)); + + // Copy-construction. + NonDereferenceable nd2(nd1); + CHECK(nd2.value() == reinterpret_cast(&i2)); + + // Copy-assignment. + nd2 = nd0; + CHECK(!nd2.value()); + + // Move-construction. + NonDereferenceable nd3{NonDereferenceable(&i)}; + CHECK(nd3.value() == reinterpret_cast(&i)); + + // Move-assignment. + nd3 = std::move(nd1); + CHECK(nd3.value() == reinterpret_cast(&i2)); + // Note: Not testing nd1's value because we don't want to assume what state + // it is left in after move. But at least it should be reusable: + nd1 = &i; + CHECK(nd1.value() == reinterpret_cast(&i)); +} + +void TestNonDereferenceableHierarchy() { + struct Base1 { + // Member variable, to make sure Base1 is not empty. + int x1; + }; + struct Base2 { + int x2; + }; + struct Derived : Base1, Base2 {}; + + Derived d; + + // Construct NonDereferenceable from raw pointer. + NonDereferenceable ndd = NonDereferenceable(&d); + CHECK(ndd); + CHECK(ndd.value() == reinterpret_cast(&d)); + + // Cast Derived to Base1. + NonDereferenceable ndb1 = ndd; + CHECK(ndb1); + CHECK(ndb1.value() == reinterpret_cast(static_cast(&d))); + + // Cast Base1 back to Derived. + NonDereferenceable nddb1 = ndb1; + CHECK(nddb1.value() == reinterpret_cast(&d)); + + // Cast Derived to Base2. + NonDereferenceable ndb2 = ndd; + CHECK(ndb2); + CHECK(ndb2.value() == reinterpret_cast(static_cast(&d))); + // Sanity check that Base2 should be offset from the start of Derived. + CHECK(ndb2.value() != ndd.value()); + + // Cast Base2 back to Derived. + NonDereferenceable nddb2 = ndb2; + CHECK(nddb2.value() == reinterpret_cast(&d)); + + // Note that it's not possible to jump between bases, as they're not obviously + // related, i.e.: `NonDereferenceable ndb22 = ndb1;` doesn't compile. + // However it's possible to explicitly navigate through the derived object: + NonDereferenceable ndb22 = NonDereferenceable(ndb1); + CHECK(ndb22.value() == reinterpret_cast(static_cast(&d))); + + // Handling nullptr; should stay nullptr even for offset bases. + ndd = nullptr; + CHECK(!ndd); + CHECK(!ndd.value()); + ndb1 = ndd; + CHECK(!ndb1); + CHECK(!ndb1.value()); + ndb2 = ndd; + CHECK(!ndb2); + CHECK(!ndb2.value()); + nddb2 = ndb2; + CHECK(!nddb2); + CHECK(!nddb2.value()); +} + +template +struct CRTPBase { + // Convert `this` from `CRTPBase*` to `T*` while construction is still in + // progress; normally UBSan -fsanitize=vptr would catch this, but using + // NonDereferenceable should keep UBSan happy. + CRTPBase() : mDerived(this) {} + NonDereferenceable mDerived; +}; + +void TestNonDereferenceableCRTP() { + struct Derived : CRTPBase, CRTPBase {}; + using Base1 = Derived::CRTPBase; + using Base2 = Derived::CRTPBase; + + Derived d; + // Verify that base constructors have correctly captured the address of the + // (at the time still incomplete) derived object. + CHECK(d.Base1::mDerived.value() == reinterpret_cast(&d)); + CHECK(d.Base2::mDerived.value() == reinterpret_cast(&d)); + + // Construct NonDereferenceable from raw pointer. + NonDereferenceable ndd = NonDereferenceable(&d); + CHECK(ndd); + CHECK(ndd.value() == reinterpret_cast(&d)); + + // Cast Derived to Base1. + NonDereferenceable ndb1 = ndd; + CHECK(ndb1); + CHECK(ndb1.value() == reinterpret_cast(static_cast(&d))); + + // Cast Base1 back to Derived. + NonDereferenceable nddb1 = ndb1; + CHECK(nddb1.value() == reinterpret_cast(&d)); + + // Cast Derived to Base2. + NonDereferenceable ndb2 = ndd; + CHECK(ndb2); + CHECK(ndb2.value() == reinterpret_cast(static_cast(&d))); + // Sanity check that Base2 should be offset from the start of Derived. + CHECK(ndb2.value() != ndd.value()); + + // Cast Base2 back to Derived. + NonDereferenceable nddb2 = ndb2; + CHECK(nddb2.value() == reinterpret_cast(&d)); + + // Note that it's not possible to jump between bases, as they're not obviously + // related, i.e.: `NonDereferenceable ndb22 = ndb1;` doesn't compile. + // However it's possible to explicitly navigate through the derived object: + NonDereferenceable ndb22 = NonDereferenceable(ndb1); + CHECK(ndb22.value() == reinterpret_cast(static_cast(&d))); +} + +int main() { + TestNonDereferenceableSimple(); + TestNonDereferenceableHierarchy(); + TestNonDereferenceableCRTP(); + + return 0; +} -- cgit v1.2.3