• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2025 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #include "pw_toolchain/globals.h"
16 
17 #include <cstdint>
18 
19 #include "pw_assert/check.h"
20 #include "pw_polyfill/standard.h"
21 #include "pw_unit_test/framework.h"
22 
23 namespace {
24 
25 #if PW_CXX_STANDARD_IS_SUPPORTED(20)
26 
27 // Constant initialization example for the docs.
28 // DOCSTAG[pw_toolchain-globals-init]
29 // This function initializes an array to non-zero values.
InitializedArray()30 constexpr std::array<uint8_t, 4096> InitializedArray() {
31   std::array<uint8_t, 4096> data{};
32   for (size_t i = 0; i < data.size(); ++i) {
33     data[i] = static_cast<uint8_t>(i);
34   }
35   return data;
36 }
37 
38 // This array constant initialized, which increases the binary size by 4KB.
39 constinit std::array<uint8_t, 4096> constant_initialized = InitializedArray();
40 
41 // This array is statically initialized and takes no space in the binary, but
42 // the InitializedArray() function is included in the binary.
43 pw::RuntimeInitGlobal<std::array<uint8_t, 4096>> runtime_initialized(
44     InitializedArray());
45 
46 // This array is zero-initialized and takes no space in the binary. It must be
47 // manually initialized.
48 std::array<uint8_t, 4096> zero_initialized;
49 // DOCSTAG[pw_toolchain-globals-init]
50 
TEST(RuntimeInitGlobal,BigArrayExample)51 TEST(RuntimeInitGlobal, BigArrayExample) {
52   EXPECT_EQ(constant_initialized[255], 255u);
53   EXPECT_EQ((*runtime_initialized)[255], 255u);
54   EXPECT_EQ(zero_initialized[255], 0u);
55 }
56 
57 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(20)
58 
59 class HasADestructor {
60  public:
HasADestructor(bool & destructor_called_flag)61   HasADestructor(bool& destructor_called_flag)
62       : destructor_called_(destructor_called_flag) {
63     destructor_called_ = false;
64   }
65 
~HasADestructor()66   ~HasADestructor() { destructor_called_ = true; }
67 
68  private:
69   bool& destructor_called_;
70 };
71 
72 class CrashInDestructor {
73  public:
MyAddress() const74   const CrashInDestructor* MyAddress() const { return this; }
75 
76   int some_value = 0;
77 
78  private:
~CrashInDestructor()79   ~CrashInDestructor() { PW_CRASH("This destructor should never execute!"); }
80 };
81 
82 class TrivialDestructor {
83  public:
TrivialDestructor(int initial_value)84   TrivialDestructor(int initial_value) : value(initial_value) {}
85 
86   int value;
87 };
88 
89 class ConstexprConstructible {
90  public:
ConstexprConstructible()91   constexpr ConstexprConstructible() : crash(true) {}
92 
~ConstexprConstructible()93   ~ConstexprConstructible() { PW_CHECK(!crash); }
94 
95   bool crash;
96 };
97 
TEST(RuntimeInitGlobal,ShouldNotCallDestructor)98 TEST(RuntimeInitGlobal, ShouldNotCallDestructor) {
99   bool destructor_called = false;
100 
101   {
102     HasADestructor should_be_destroyed(destructor_called);
103   }
104 
105   EXPECT_TRUE(destructor_called);
106 
107   {
108     pw::RuntimeInitGlobal<HasADestructor> should_not_be_destroyed(
109         destructor_called);
110   }
111 
112   EXPECT_FALSE(destructor_called);
113 }
114 
TEST(RuntimeInitGlobal,MemberAccess)115 TEST(RuntimeInitGlobal, MemberAccess) {
116   pw::RuntimeInitGlobal<CrashInDestructor> no_destructor;
117 
118   no_destructor->some_value = 123;
119   EXPECT_EQ(123, (*no_destructor).some_value);
120   EXPECT_EQ(no_destructor.operator->(), no_destructor->MyAddress());
121 }
122 
TEST(RuntimeInitGlobal,TrivialDestructor)123 TEST(RuntimeInitGlobal, TrivialDestructor) {
124   pw::RuntimeInitGlobal<TrivialDestructor> no_destructor(555);
125 
126   EXPECT_EQ(no_destructor->value, 555);
127   no_destructor->value = 123;
128   EXPECT_EQ(no_destructor->value, 123);
129 }
130 
TEST(RuntimeInitGlobal,TrivialType)131 TEST(RuntimeInitGlobal, TrivialType) {
132   pw::RuntimeInitGlobal<int> no_destructor;
133 
134   EXPECT_EQ(*no_destructor, 0);
135   *no_destructor = 123;
136   EXPECT_EQ(*no_destructor, 123);
137 }
138 
TEST(RuntimeInitGlobal,FunctionStatic)139 TEST(RuntimeInitGlobal, FunctionStatic) {
140   static pw::RuntimeInitGlobal<CrashInDestructor> function_static_no_destructor;
141 }
142 
143 pw::RuntimeInitGlobal<CrashInDestructor> global_no_destructor;
144 
145 static_assert(!std::is_trivially_destructible<CrashInDestructor>::value,
146               "Type should not be trivially destructible");
147 static_assert(std::is_trivially_destructible<
148                   pw::RuntimeInitGlobal<CrashInDestructor>>::value,
149               "Wrapper should be trivially destructible");
150 
151 }  // namespace
152