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