• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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