1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://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,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17
18 #include "assembly-parser.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/panda_vm.h"
21 #include "runtime/include/class_linker.h"
22 #include "runtime/include/thread_scopes.h"
23 #include "runtime/mem/vm_handle.h"
24 #include "runtime/handle_scope-inl.h"
25 #include "runtime/include/coretypes/array.h"
26 #include "runtime/include/coretypes/string.h"
27 #include "runtime/mem/object_helpers-inl.h"
28
29 namespace panda::mem {
30
separator()31 inline std::string separator()
32 {
33 #ifdef _WIN32
34 return "\\";
35 #else
36 return "/";
37 #endif
38 }
39
40 class StaticObjectHelpersTest : public testing::Test {
41 public:
StaticObjectHelpersTest()42 StaticObjectHelpersTest()
43 {
44 RuntimeOptions options;
45 options.SetBootClassSpaces({"core"});
46 options.SetRuntimeType("core");
47 options.SetGcType("epsilon");
48 options.SetGcTriggerType("debug-never");
49 auto exec_path = panda::os::file::File::GetExecutablePath();
50 std::string panda_std_lib =
51 exec_path.Value() + separator() + ".." + separator() + "pandastdlib" + separator() + "pandastdlib.bin";
52 options.SetBootPandaFiles({panda_std_lib});
53
54 Runtime::Create(options);
55 }
56
~StaticObjectHelpersTest()57 ~StaticObjectHelpersTest()
58 {
59 Runtime::Destroy();
60 }
61
GetThread()62 MTManagedThread *GetThread()
63 {
64 return MTManagedThread::GetCurrent();
65 }
66
LoadTestClass(const char * source)67 Class *LoadTestClass(const char *source)
68 {
69 pandasm::Parser p;
70 auto res = p.Parse(source);
71 auto pf = pandasm::AsmEmitter::Emit(res.Value());
72 EXPECT_NE(pf, nullptr);
73 if (pf == nullptr) {
74 return nullptr;
75 }
76
77 ClassLinker *class_linker = Runtime::GetCurrent()->GetClassLinker();
78 class_linker->AddPandaFile(std::move(pf));
79 auto *extension = class_linker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
80 {
81 ScopedManagedCodeThread s(GetThread());
82 PandaString descriptor;
83 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("Test"), &descriptor));
84 EXPECT_NE(nullptr, klass);
85 if (klass != nullptr) {
86 EXPECT_TRUE(class_linker->InitializeClass(GetThread(), klass));
87 }
88 return klass;
89 }
90 }
91
NewTestInstance(const char * source)92 ObjectHeader *NewTestInstance(const char *source)
93 {
94 Class *klass = LoadTestClass(source);
95 if (klass == nullptr) {
96 return nullptr;
97 }
98 return AllocObject(klass);
99 }
100
AllocString()101 coretypes::String *AllocString()
102 {
103 Runtime *runtime = Runtime::GetCurrent();
104 LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
105 ScopedManagedCodeThread s(GetThread());
106 return coretypes::String::CreateEmptyString(ctx, runtime->GetPandaVM());
107 }
108
AllocObject(Class * klass)109 ObjectHeader *AllocObject(Class *klass)
110 {
111 Runtime *runtime = Runtime::GetCurrent();
112 ScopedManagedCodeThread s(GetThread());
113 return runtime->GetPandaVM()->GetHeapManager()->AllocateObject(klass, klass->GetObjectSize());
114 }
115
AllocStringArray(size_t length)116 coretypes::Array *AllocStringArray(size_t length)
117 {
118 Runtime *runtime = Runtime::GetCurrent();
119 LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
120 SpaceType space_type = SpaceType::SPACE_TYPE_OBJECT;
121 auto *klass = runtime->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::ARRAY_STRING);
122 ScopedManagedCodeThread s(GetThread());
123 return coretypes::Array::Create(klass, length, space_type);
124 }
125 };
126
TEST_F(StaticObjectHelpersTest,TestPrimitiveField)127 TEST_F(StaticObjectHelpersTest, TestPrimitiveField)
128 {
129 Class *klass = LoadTestClass(R"(
130 .record Test {
131 i32 sfield <static>
132 i32 ifield
133 }
134 )");
135 ASSERT_NE(nullptr, klass);
136 bool found = false;
137 auto handler = [&found]([[maybe_unused]] ObjectHeader *obj, [[maybe_unused]] ObjectHeader *ref,
138 [[maybe_unused]] uint32_t offset, [[maybe_unused]] bool is_volatile) {
139 found = true;
140 return true;
141 };
142 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(klass->GetManagedObject(), handler);
143 ASSERT_FALSE(found);
144 }
145
TEST_F(StaticObjectHelpersTest,TestStaticRefField)146 TEST_F(StaticObjectHelpersTest, TestStaticRefField)
147 {
148 Class *klass = LoadTestClass(R"(
149 .record panda.String <external>
150 .record Test {
151 panda.String field <static>
152 panda.String nullField <static>
153 }
154 )");
155 ASSERT_NE(klass, nullptr);
156 Field *field = klass->GetStaticFieldByName(reinterpret_cast<const uint8_t *>("field"));
157 ASSERT_NE(nullptr, field);
158 ObjectHeader *expected = AllocString();
159 ASSERT_NE(nullptr, expected);
160 ObjectAccessor::SetFieldObject<false>(klass, *field, expected);
161
162 size_t count = 0;
163 auto handler = [klass, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
164 ++count;
165 EXPECT_EQ(obj, klass->GetManagedObject());
166 EXPECT_EQ(expected, ref);
167 EXPECT_EQ(ref, ObjectAccessor::GetObject<false>(obj, offset));
168 EXPECT_FALSE(is_volatile);
169 return true;
170 };
171 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(klass->GetManagedObject(), handler);
172 ASSERT_EQ(1, count);
173 }
174
TEST_F(StaticObjectHelpersTest,TestStaticVolatileRefField)175 TEST_F(StaticObjectHelpersTest, TestStaticVolatileRefField)
176 {
177 Class *klass = LoadTestClass(R"(
178 .record panda.String <external>
179 .record Test {
180 panda.String field <static, volatile>
181 panda.String nullField <static, volatile>
182 }
183 )");
184 ASSERT_NE(klass, nullptr);
185 Field *field = klass->GetStaticFieldByName(reinterpret_cast<const uint8_t *>("field"));
186 ASSERT_NE(nullptr, field);
187 ObjectHeader *expected = AllocString();
188 ASSERT_NE(nullptr, expected);
189 ObjectAccessor::SetFieldObject<false>(klass, *field, expected);
190
191 size_t count = 0;
192 auto handler = [klass, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
193 ++count;
194 EXPECT_EQ(obj, klass->GetManagedObject());
195 EXPECT_EQ(expected, ref);
196 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
197 EXPECT_TRUE(is_volatile);
198 return true;
199 };
200 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(klass->GetManagedObject(), handler);
201 ASSERT_EQ(1, count);
202 }
203
TEST_F(StaticObjectHelpersTest,TestInstanceRefField)204 TEST_F(StaticObjectHelpersTest, TestInstanceRefField)
205 {
206 ObjectHeader *object = NewTestInstance(R"(
207 .record panda.String <external>
208 .record Test {
209 panda.String field
210 panda.String nullField
211 }
212 )");
213
214 ASSERT_NE(nullptr, object);
215 Class *klass = object->ClassAddr<Class>();
216 Field *field = klass->GetInstanceFieldByName(reinterpret_cast<const uint8_t *>("field"));
217 ASSERT_NE(nullptr, field);
218 ObjectHeader *expected = AllocString();
219 ASSERT_NE(nullptr, expected);
220 ObjectAccessor::SetFieldObject<false>(object, *field, expected);
221
222 size_t count = 0;
223 auto handler = [object, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
224 ++count;
225 EXPECT_EQ(object, obj);
226 EXPECT_EQ(expected, ref);
227 EXPECT_EQ(ref, ObjectAccessor::GetObject<false>(obj, offset));
228 EXPECT_FALSE(is_volatile);
229 return true;
230 };
231 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(object, handler);
232 ASSERT_EQ(1, count);
233 }
234
TEST_F(StaticObjectHelpersTest,TestVolatileInstanceRefField)235 TEST_F(StaticObjectHelpersTest, TestVolatileInstanceRefField)
236 {
237 ObjectHeader *object = NewTestInstance(R"(
238 .record panda.String <external>
239 .record Test {
240 panda.String field <volatile>
241 panda.String nullField <volatile>
242 }
243 )");
244
245 ASSERT_NE(nullptr, object);
246 Class *klass = object->ClassAddr<Class>();
247 Field *field = klass->GetInstanceFieldByName(reinterpret_cast<const uint8_t *>("field"));
248 ASSERT_NE(nullptr, field);
249 ObjectHeader *expected = AllocString();
250 ASSERT_NE(nullptr, expected);
251 ObjectAccessor::SetFieldObject<false>(object, *field, expected);
252
253 size_t count = 0;
254 auto handler = [object, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
255 ++count;
256 EXPECT_EQ(object, obj);
257 EXPECT_EQ(expected, ref);
258 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
259 EXPECT_TRUE(is_volatile);
260 return true;
261 };
262 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(object, handler);
263 ASSERT_EQ(1, count);
264 }
265
TEST_F(StaticObjectHelpersTest,TestArray)266 TEST_F(StaticObjectHelpersTest, TestArray)
267 {
268 coretypes::Array *array = AllocStringArray(2);
269 ASSERT_NE(nullptr, array);
270 ObjectHeader *expected = AllocString();
271 ASSERT_NE(nullptr, expected);
272 array->Set(0, expected);
273
274 size_t count = 0;
275 auto handler = [array, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool is_volatile) {
276 ++count;
277 EXPECT_EQ(array, obj);
278 EXPECT_EQ(expected, ref);
279 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
280 EXPECT_FALSE(is_volatile);
281 return true;
282 };
283 GCStaticObjectHelpers::TraverseAllObjectsWithInfo(array, handler);
284 ASSERT_EQ(1, count);
285 }
286
287 } // namespace panda::mem
288