1 /**
2 * Copyright (c) 2021-2024 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 ark::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.SetLoadRuntimes({"core"});
46 options.SetGcType("epsilon");
47 options.SetGcTriggerType("debug-never");
48 auto execPath = ark::os::file::File::GetExecutablePath();
49 std::string pandaStdLib =
50 execPath.Value() + Separator() + ".." + Separator() + "pandastdlib" + Separator() + "pandastdlib.bin";
51 options.SetBootPandaFiles({pandaStdLib});
52
53 Runtime::Create(options);
54 }
55
~StaticObjectHelpersTest()56 ~StaticObjectHelpersTest() override
57 {
58 Runtime::Destroy();
59 }
60
61 NO_COPY_SEMANTIC(StaticObjectHelpersTest);
62 NO_MOVE_SEMANTIC(StaticObjectHelpersTest);
63
GetThread()64 MTManagedThread *GetThread()
65 {
66 return MTManagedThread::GetCurrent();
67 }
68
LoadTestClass(const char * source)69 Class *LoadTestClass(const char *source)
70 {
71 pandasm::Parser p;
72 auto res = p.Parse(source);
73 auto pf = pandasm::AsmEmitter::Emit(res.Value());
74 EXPECT_NE(pf, nullptr);
75 if (pf == nullptr) {
76 return nullptr;
77 }
78
79 ClassLinker *classLinker = Runtime::GetCurrent()->GetClassLinker();
80 classLinker->AddPandaFile(std::move(pf));
81 auto *extension = classLinker->GetExtension(panda_file::SourceLang::PANDA_ASSEMBLY);
82 {
83 ScopedManagedCodeThread s(GetThread());
84 PandaString descriptor;
85 Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("Test"), &descriptor));
86 EXPECT_NE(nullptr, klass);
87 if (klass != nullptr) {
88 EXPECT_TRUE(classLinker->InitializeClass(GetThread(), klass));
89 }
90 return klass;
91 }
92 }
93
NewTestInstance(const char * source)94 ObjectHeader *NewTestInstance(const char *source)
95 {
96 Class *klass = LoadTestClass(source);
97 if (klass == nullptr) {
98 return nullptr;
99 }
100 return AllocObject(klass);
101 }
102
AllocString()103 coretypes::String *AllocString()
104 {
105 Runtime *runtime = Runtime::GetCurrent();
106 LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
107 ScopedManagedCodeThread s(GetThread());
108 return coretypes::String::CreateEmptyString(ctx, runtime->GetPandaVM());
109 }
110
AllocObject(Class * klass)111 ObjectHeader *AllocObject(Class *klass)
112 {
113 Runtime *runtime = Runtime::GetCurrent();
114 ScopedManagedCodeThread s(GetThread());
115 return runtime->GetPandaVM()->GetHeapManager()->AllocateObject(klass, klass->GetObjectSize());
116 }
117
AllocStringArray(size_t length)118 coretypes::Array *AllocStringArray(size_t length)
119 {
120 Runtime *runtime = Runtime::GetCurrent();
121 LanguageContext ctx = runtime->GetLanguageContext(panda_file::SourceLang::PANDA_ASSEMBLY);
122 SpaceType spaceType = SpaceType::SPACE_TYPE_OBJECT;
123 auto *klass = runtime->GetClassLinker()->GetExtension(ctx)->GetClassRoot(ClassRoot::ARRAY_STRING);
124 ScopedManagedCodeThread s(GetThread());
125 return coretypes::Array::Create(klass, length, spaceType);
126 }
127 };
128
TEST_F(StaticObjectHelpersTest,TestPrimitiveField)129 TEST_F(StaticObjectHelpersTest, TestPrimitiveField)
130 {
131 Class *klass = LoadTestClass(R"(
132 .record Test {
133 i32 sfield <static>
134 i32 ifield
135 }
136 )");
137 ASSERT_NE(nullptr, klass);
138 bool found = false;
139 auto handler = [&found]([[maybe_unused]] ObjectHeader *obj, [[maybe_unused]] ObjectHeader *ref,
140 [[maybe_unused]] uint32_t offset, [[maybe_unused]] bool isVolatile) {
141 found = true;
142 return true;
143 };
144 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(klass->GetManagedObject(), handler);
145 ASSERT_FALSE(found);
146 }
147
TEST_F(StaticObjectHelpersTest,TestStaticRefField)148 TEST_F(StaticObjectHelpersTest, TestStaticRefField)
149 {
150 Class *klass = LoadTestClass(R"(
151 .record panda.String <external>
152 .record Test {
153 panda.String field <static>
154 panda.String nullField <static>
155 }
156 )");
157 ASSERT_NE(klass, nullptr);
158 Field *field = klass->GetStaticFieldByName(reinterpret_cast<const uint8_t *>("field"));
159 ASSERT_NE(nullptr, field);
160 ObjectHeader *expected = AllocString();
161 ASSERT_NE(nullptr, expected);
162 ObjectAccessor::SetFieldObject<false>(klass, *field, expected);
163
164 size_t count = 0;
165 auto handler = [klass, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool isVolatile) {
166 ++count;
167 EXPECT_EQ(obj, klass->GetManagedObject());
168 EXPECT_EQ(expected, ref);
169 EXPECT_EQ(ref, ObjectAccessor::GetObject<false>(obj, offset));
170 EXPECT_FALSE(isVolatile);
171 return true;
172 };
173 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(klass->GetManagedObject(), handler);
174 ASSERT_EQ(1, count);
175 }
176
TEST_F(StaticObjectHelpersTest,TestStaticVolatileRefField)177 TEST_F(StaticObjectHelpersTest, TestStaticVolatileRefField)
178 {
179 Class *klass = LoadTestClass(R"(
180 .record panda.String <external>
181 .record Test {
182 panda.String field <static, volatile>
183 panda.String nullField <static, volatile>
184 }
185 )");
186 ASSERT_NE(klass, nullptr);
187 Field *field = klass->GetStaticFieldByName(reinterpret_cast<const uint8_t *>("field"));
188 ASSERT_NE(nullptr, field);
189 ObjectHeader *expected = AllocString();
190 ASSERT_NE(nullptr, expected);
191 ObjectAccessor::SetFieldObject<false>(klass, *field, expected);
192
193 size_t count = 0;
194 auto handler = [klass, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool isVolatile) {
195 ++count;
196 EXPECT_EQ(obj, klass->GetManagedObject());
197 EXPECT_EQ(expected, ref);
198 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
199 EXPECT_TRUE(isVolatile);
200 return true;
201 };
202 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(klass->GetManagedObject(), handler);
203 ASSERT_EQ(1, count);
204 }
205
TEST_F(StaticObjectHelpersTest,TestInstanceRefField)206 TEST_F(StaticObjectHelpersTest, TestInstanceRefField)
207 {
208 ObjectHeader *object = NewTestInstance(R"(
209 .record panda.String <external>
210 .record Test {
211 panda.String field
212 panda.String nullField
213 }
214 )");
215
216 ASSERT_NE(nullptr, object);
217 auto *klass = object->ClassAddr<Class>();
218 Field *field = klass->GetInstanceFieldByName(reinterpret_cast<const uint8_t *>("field"));
219 ASSERT_NE(nullptr, field);
220 ObjectHeader *expected = AllocString();
221 ASSERT_NE(nullptr, expected);
222 ObjectAccessor::SetFieldObject<false>(object, *field, expected);
223
224 size_t count = 0;
225 auto handler = [object, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool isVolatile) {
226 ++count;
227 EXPECT_EQ(object, obj);
228 EXPECT_EQ(expected, ref);
229 EXPECT_EQ(ref, ObjectAccessor::GetObject<false>(obj, offset));
230 EXPECT_FALSE(isVolatile);
231 return true;
232 };
233 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(object, handler);
234 ASSERT_EQ(1, count);
235 }
236
TEST_F(StaticObjectHelpersTest,TestVolatileInstanceRefField)237 TEST_F(StaticObjectHelpersTest, TestVolatileInstanceRefField)
238 {
239 ObjectHeader *object = NewTestInstance(R"(
240 .record panda.String <external>
241 .record Test {
242 panda.String field <volatile>
243 panda.String nullField <volatile>
244 }
245 )");
246
247 ASSERT_NE(nullptr, object);
248 auto *klass = object->ClassAddr<Class>();
249 Field *field = klass->GetInstanceFieldByName(reinterpret_cast<const uint8_t *>("field"));
250 ASSERT_NE(nullptr, field);
251 ObjectHeader *expected = AllocString();
252 ASSERT_NE(nullptr, expected);
253 ObjectAccessor::SetFieldObject<false>(object, *field, expected);
254
255 size_t count = 0;
256 auto handler = [object, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool isVolatile) {
257 ++count;
258 EXPECT_EQ(object, obj);
259 EXPECT_EQ(expected, ref);
260 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
261 EXPECT_TRUE(isVolatile);
262 return true;
263 };
264 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(object, handler);
265 ASSERT_EQ(1, count);
266 }
267
TEST_F(StaticObjectHelpersTest,TestArray)268 TEST_F(StaticObjectHelpersTest, TestArray)
269 {
270 coretypes::Array *array = AllocStringArray(2);
271 ASSERT_NE(nullptr, array);
272 ObjectHeader *expected = AllocString();
273 ASSERT_NE(nullptr, expected);
274 array->Set(0, expected);
275
276 size_t count = 0;
277 auto handler = [array, &count, expected](ObjectHeader *obj, ObjectHeader *ref, uint32_t offset, bool isVolatile) {
278 ++count;
279 EXPECT_EQ(array, obj);
280 EXPECT_EQ(expected, ref);
281 EXPECT_EQ(ref, ObjectAccessor::GetObject<true>(obj, offset));
282 EXPECT_FALSE(isVolatile);
283 return true;
284 };
285 GCStaticObjectHelpers::TraverseAllObjectsWithInfo<false>(array, handler);
286 ASSERT_EQ(1, count);
287 }
288
289 } // namespace ark::mem
290