1/* 2 * Copyright (c) 2021 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 "ecmascript/tests/test_helper.h" 17 18#include "ecmascript/ecma_vm.h" 19#include "ecmascript/global_env.h" 20#include "ecmascript/js_handle.h" 21#include "ecmascript/js_hclass.h" 22#include "ecmascript/js_thread.h" 23#include "ecmascript/jspandafile/program_object.h" 24#include "ecmascript/object_factory.h" 25#include "ecmascript/snapshot/mem/snapshot.h" 26#include "ecmascript/snapshot/mem/snapshot_processor.h" 27#include "ecmascript/snapshot/tests/snapshot_mock.h" 28 29using namespace panda::ecmascript; 30 31namespace panda::test { 32class SnapshotTest : public testing::Test { 33public: 34 static void SetUpTestCase() 35 { 36 GTEST_LOG_(INFO) << "SetUpTestCase"; 37 } 38 39 static void TearDownTestCase() 40 { 41 GTEST_LOG_(INFO) << "TearDownCase"; 42 } 43 44 void SetUp() override 45 { 46 JSRuntimeOptions options; 47 ecmaVm = JSNApi::CreateEcmaVM(options); 48 ASSERT_TRUE(ecmaVm != nullptr) << "Cannot create EcmaVM"; 49 thread = ecmaVm->GetJSThread(); 50 thread->ManagedCodeBegin(); 51 scope = new EcmaHandleScope(thread); 52 } 53 54 void TearDown() override 55 { 56 thread->ManagedCodeEnd(); 57 delete scope; 58 scope = nullptr; 59 ecmaVm->SetEnableForceGC(false); 60 thread->ClearException(); 61 JSNApi::DestroyJSVM(ecmaVm); 62 } 63 64 void CompatibilityHelper(base::FileHeaderBase::VersionType serializeVersion, 65 base::FileHeaderBase::VersionType deserializeVersion, bool expected) 66 { 67 static constexpr uint32_t ARRAY_SIZE = 300; 68 static constexpr uint32_t KILO_BITS = 1024; 69 auto factory = ecmaVm->GetFactory(); 70 auto env = ecmaVm->GetGlobalEnv(); 71 72 JSHandle<TaggedArray> array1 = factory->NewTaggedArray(ARRAY_SIZE * KILO_BITS / sizeof(uint8_t)); 73 74 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction()); 75 array1->Set(thread, 0, funcFunc.GetTaggedValue()); 76 77 CString fileName = "snapshot"; 78 SnapshotMock snapshotSerialize(ecmaVm); 79 snapshotSerialize.SetLastVersion(serializeVersion); 80 // serialize in earlier version tag 81 snapshotSerialize.Serialize(*array1, nullptr, fileName); 82 83 std::thread t1([&]() { 84 JSRuntimeOptions options; 85 EcmaVM *ecmaVm2 = JSNApi::CreateEcmaVM(options); 86 // deserialize with last version tag 87 SnapshotMock snapshotDeserialize(ecmaVm2); 88 snapshotDeserialize.SetLastVersion(deserializeVersion); 89 EXPECT_EQ(snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName), expected); 90 91 ASSERT_EQ(const_cast<Heap *>(ecmaVm2->GetHeap())->GetHugeObjectSpace()->GetFirstRegion() != nullptr, 92 expected); 93 JSNApi::DestroyJSVM(ecmaVm2); 94 }); 95 { 96 ThreadSuspensionScope suspensionScope(thread); 97 t1.join(); 98 } 99 std::remove(fileName.c_str()); 100 } 101 102 EcmaVM *ecmaVm {nullptr}; 103 ecmascript::EcmaHandleScope *scope {nullptr}; 104 JSThread *thread {nullptr}; 105}; 106 107HWTEST_F_L0(SnapshotTest, SerializeConstPool) 108{ 109 auto factory = ecmaVm->GetFactory(); 110 auto env = ecmaVm->GetGlobalEnv(); 111 112 JSHandle<ConstantPool> constpool = factory->NewConstantPool(10); 113 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction()); 114 JSHandle<JSFunction> dateFunc(env->GetDateFunction()); 115 JSHandle<JSFunction> numberFunc(env->GetNumberFunction()); 116 JSHandle<EcmaString> str1 = factory->NewFromASCII("str11"); 117 JSHandle<EcmaString> str2 = factory->NewFromASCII("str22"); 118 JSHandle<EcmaString> str3 = factory->NewFromASCII("str333333333333"); 119 JSHandle<EcmaString> str4 = factory->ConcatFromString(str1, str3); 120 JSHandle<EcmaString> str5 = factory->NewFromASCII("str44"); 121 constpool->SetObjectToCache(thread, 0, funcFunc.GetTaggedValue()); 122 constpool->SetObjectToCache(thread, 1, dateFunc.GetTaggedValue()); 123 constpool->SetObjectToCache(thread, 2, str1.GetTaggedValue()); 124 constpool->SetObjectToCache(thread, 3, numberFunc.GetTaggedValue()); 125 constpool->SetObjectToCache(thread, 4, str2.GetTaggedValue()); 126 constpool->SetObjectToCache(thread, 5, str1.GetTaggedValue()); 127 constpool->SetObjectToCache(thread, 6, str3.GetTaggedValue()); 128 constpool->SetObjectToCache(thread, 7, str4.GetTaggedValue()); 129 constpool->SetObjectToCache(thread, 8, str4.GetTaggedValue()); 130 constpool->SetObjectToCache(thread, 9, str5.GetTaggedValue()); 131 132 CString fileName = "snapshot"; 133 Snapshot snapshotSerialize(ecmaVm); 134 // serialize 135 snapshotSerialize.Serialize(*constpool, nullptr, fileName); 136 // deserialize 137 Snapshot snapshotDeserialize(ecmaVm); 138 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName); 139 140 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion(); 141 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin()); 142 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool), 143 constpool1->GetClass()->SizeFromJSHClass(constpool1)); 144 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsJSFunction()); 145 EXPECT_TRUE(constpool1->GetObjectFromCache(1).IsJSFunction()); 146 EXPECT_TRUE(constpool1->GetObjectFromCache(3).IsJSFunction()); 147 EcmaString *str11 = reinterpret_cast<EcmaString *>(constpool1->Get(2).GetTaggedObject()); 148 EcmaString *str22 = reinterpret_cast<EcmaString *>(constpool1->Get(4).GetTaggedObject()); 149 EcmaString *str33 = reinterpret_cast<EcmaString *>(constpool1->Get(5).GetTaggedObject()); 150 EcmaString *str44 = reinterpret_cast<EcmaString *>(constpool1->Get(6).GetTaggedObject()); 151 EcmaString *str55 = reinterpret_cast<EcmaString *>(constpool1->Get(7).GetTaggedObject()); 152 EcmaString *str66 = reinterpret_cast<EcmaString *>(constpool1->Get(8).GetTaggedObject()); 153 EcmaString *str77 = reinterpret_cast<EcmaString *>(constpool1->Get(9).GetTaggedObject()); 154 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str11).ToCString().c_str(), "str11"), 0); 155 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str22).ToCString().c_str(), "str22"), 0); 156 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str33).ToCString().c_str(), "str11"), 0); 157 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str44).ToCString().c_str(), "str333333333333"), 0); 158 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str55).ToCString().c_str(), "str11str333333333333"), 0); 159 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str66).ToCString().c_str(), "str11str333333333333"), 0); 160 EXPECT_EQ(std::strcmp(EcmaStringAccessor(str77).ToCString().c_str(), "str44"), 0); 161 std::remove(fileName.c_str()); 162} 163 164HWTEST_F_L0(SnapshotTest, SerializeDifferentSpace) 165{ 166 auto factory = ecmaVm->GetFactory(); 167 JSHandle<ConstantPool> constpool = factory->NewConstantPool(400); 168 for (int i = 0; i < 100; i++) { 169 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); 170 constpool->SetObjectToCache(thread, i, array.GetTaggedValue()); 171 } 172 for (int i = 0; i < 100; i++) { 173 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE); 174 constpool->SetObjectToCache(thread, i + 100, array.GetTaggedValue()); 175 } 176 for (int i = 0; i < 100; i++) { 177 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(10); 178 constpool->SetObjectToCache(thread, i + 300, constpool1.GetTaggedValue()); 179 } 180 181 CString fileName = "snapshot"; 182 Snapshot snapshotSerialize(ecmaVm); 183 // serialize 184 snapshotSerialize.Serialize(*constpool, nullptr, fileName); 185 // deserialize 186 Snapshot snapshotDeserialize(ecmaVm); 187 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName); 188 189 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion(); 190 auto constpool1 = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin()); 191 EXPECT_EQ(constpool->GetClass()->SizeFromJSHClass(*constpool), 192 constpool1->GetClass()->SizeFromJSHClass(constpool1)); 193 EXPECT_TRUE(constpool1->GetObjectFromCache(0).IsTaggedArray()); 194 EXPECT_TRUE(constpool1->GetObjectFromCache(100).IsTaggedArray()); 195 EXPECT_TRUE(constpool1->GetObjectFromCache(300).IsTaggedArray()); 196 197 auto obj1 = constpool1->GetObjectFromCache(0).GetTaggedObject(); 198 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace()); 199 auto obj2 = constpool1->GetObjectFromCache(100).GetTaggedObject(); 200 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace()); 201 std::remove(fileName.c_str()); 202} 203 204HWTEST_F_L0(SnapshotTest, SerializeMultiFile) 205{ 206 auto factory = ecmaVm->GetFactory(); 207 JSHandle<ConstantPool> constpool1 = factory->NewConstantPool(400); 208 JSHandle<ConstantPool> constpool2 = factory->NewConstantPool(400); 209 for (int i = 0; i < 100; i++) { 210 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::SEMI_SPACE); 211 constpool1->SetObjectToCache(thread, i, array.GetTaggedValue()); 212 constpool2->SetObjectToCache(thread, i, array.GetTaggedValue()); 213 } 214 for (int i = 0; i < 100; i++) { 215 JSHandle<TaggedArray> array = factory->NewTaggedArray(10, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE); 216 constpool1->SetObjectToCache(thread, i + 100, array.GetTaggedValue()); 217 constpool2->SetObjectToCache(thread, i + 100, array.GetTaggedValue()); 218 } 219 for (int i = 0; i < 100; i++) { 220 JSHandle<ConstantPool> constpool3 = factory->NewConstantPool(10); 221 constpool1->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue()); 222 constpool2->SetObjectToCache(thread, i + 300, constpool3.GetTaggedValue()); 223 } 224 225 CString fileName1 = "snapshot1"; 226 CString fileName2 = "snapshot2"; 227 Snapshot snapshotSerialize(ecmaVm); 228 // serialize 229 snapshotSerialize.Serialize(*constpool1, nullptr, fileName1); 230 snapshotSerialize.Serialize(*constpool2, nullptr, fileName2); 231 // deserialize 232 Snapshot snapshotDeserialize(ecmaVm); 233 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName1); 234 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName2); 235 236 auto beginRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetOldSpace()->GetCurrentRegion(); 237 auto constpool = reinterpret_cast<ConstantPool *>(beginRegion->GetBegin()); 238 EXPECT_TRUE(constpool->GetObjectFromCache(0).IsTaggedArray()); 239 EXPECT_TRUE(constpool->GetObjectFromCache(100).IsTaggedArray()); 240 auto obj1 = constpool->GetObjectFromCache(0).GetTaggedObject(); 241 EXPECT_TRUE(Region::ObjectAddressToRange(obj1)->InOldSpace()); 242 auto obj2 = constpool->GetObjectFromCache(100).GetTaggedObject(); 243 EXPECT_TRUE(Region::ObjectAddressToRange(obj2)->InOldSpace()); 244 std::remove(fileName1.c_str()); 245 std::remove(fileName2.c_str()); 246} 247 248HWTEST_F_L0(SnapshotTest, SerializeBuiltins) 249{ 250 // remove builtins.snapshot file first if exist 251 CString fileName = "builtins.snapshot"; 252 std::remove(fileName.c_str()); 253 // generate builtins.snapshot file 254 std::thread t1([]() { 255 JSRuntimeOptions options1; 256 options1.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_SERIALIZE); 257 EcmaVM *ecmaVm1 = JSNApi::CreateEcmaVM(options1); 258 JSNApi::DestroyJSVM(ecmaVm1); 259 }); 260 { 261 ThreadSuspensionScope suspensionScope(thread); 262 t1.join(); 263 } 264 265 // create EcmaVM use builtins deserialzie 266 std::thread t2([&]() { 267 JSRuntimeOptions options2; 268 options2.SetArkProperties(ArkProperties::ENABLE_SNAPSHOT_DESERIALIZE); 269 EcmaVM *ecmaVm2 = JSNApi::CreateEcmaVM(options2); 270 EXPECT_TRUE(ecmaVm2->GetGlobalEnv()->GetClass()->GetObjectType() == JSType::GLOBAL_ENV); 271 auto globalConst = const_cast<GlobalEnvConstants *>(ecmaVm2->GetJSThread()->GlobalConstants()); 272 globalConst->VisitRangeSlot([]([[maybe_unused]] Root type, ObjectSlot start, ObjectSlot end) { 273 size_t sharedBeginHclassIndex = static_cast<size_t>(ConstantIndex::SHARED_HCLASS_BEGIN); 274 size_t sharedHclassEndIndex = static_cast<size_t>(ConstantIndex::SHARED_HCLASS_END); 275 size_t hclassBeginIndex = static_cast<size_t>(ConstantIndex::NON_SHARED_HCLASS_BEGIN); 276 size_t hclassEndIndex = static_cast<size_t>(ConstantIndex::NON_SHARED_HCLASS_END); 277 size_t index = sharedBeginHclassIndex; 278 while (start < end) { 279 JSTaggedValue object(start.GetTaggedType()); 280 start++; 281 if ((index >= sharedBeginHclassIndex && index <= sharedHclassEndIndex) || 282 (index >= hclassBeginIndex && index <= hclassEndIndex)) { 283 EXPECT_TRUE(object.IsJSHClass()); 284 } 285 index++; 286 } 287 }); 288 JSNApi::DestroyJSVM(ecmaVm2); 289 }); 290 { 291 ThreadSuspensionScope suspensionScope(thread); 292 t2.join(); 293 } 294 std::remove(fileName.c_str()); 295} 296 297HWTEST_F_L0(SnapshotTest, SerializeHugeObject) 298{ 299 auto factory = ecmaVm->GetFactory(); 300 auto env = ecmaVm->GetGlobalEnv(); 301 302 JSHandle<TaggedArray> array1 = factory->NewTaggedArray(300 * 1024 / 8); 303 JSHandle<TaggedArray> array2 = factory->NewTaggedArray(300 * 1024 / 8); 304 JSHandle<TaggedArray> array3 = factory->NewTaggedArray(100); 305 306 JSHandle<JSFunction> funcFunc(env->GetFunctionFunction()); 307 JSHandle<JSFunction> dateFunc(env->GetDateFunction()); 308 JSHandle<JSFunction> numberFunc(env->GetNumberFunction()); 309 array1->Set(thread, 0, array2.GetTaggedValue()); 310 array1->Set(thread, 1, funcFunc.GetTaggedValue()); 311 array1->Set(thread, 2, dateFunc.GetTaggedValue()); 312 array1->Set(thread, 3, numberFunc.GetTaggedValue()); 313 array2->Set(thread, 0, array3.GetTaggedValue()); 314 array2->Set(thread, 1, funcFunc.GetTaggedValue()); 315 array2->Set(thread, 2, dateFunc.GetTaggedValue()); 316 array2->Set(thread, 3, numberFunc.GetTaggedValue()); 317 318 CString fileName = "snapshot"; 319 Snapshot snapshotSerialize(ecmaVm); 320 // serialize 321 snapshotSerialize.Serialize(*array1, nullptr, fileName); 322 // deserialize 323 Snapshot snapshotDeserialize(ecmaVm); 324 snapshotDeserialize.Deserialize(SnapshotType::VM_ROOT, fileName); 325 326 auto lastRegion = const_cast<Heap *>(ecmaVm->GetHeap())->GetHugeObjectSpace()->GetCurrentRegion(); 327 auto array4 = reinterpret_cast<TaggedArray *>(lastRegion->GetBegin()); 328 EXPECT_TRUE(array4->Get(0).IsTaggedArray()); 329 EXPECT_TRUE(array4->Get(1).IsJSFunction()); 330 EXPECT_TRUE(array4->Get(2).IsJSFunction()); 331 EXPECT_TRUE(array4->Get(3).IsJSFunction()); 332 std::remove(fileName.c_str()); 333} 334 335HWTEST_F_L0(SnapshotTest, BackwardCompatibility) 336{ 337 base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1}; 338 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; 339 CompatibilityHelper(oldVersion, newVersion, false); 340} 341 342HWTEST_F_L0(SnapshotTest, ForwardCompatibility) 343{ 344 base::FileHeaderBase::VersionType oldVersion = {0, 0, 0, 1}; 345 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; 346 CompatibilityHelper(newVersion, oldVersion, false); 347} 348 349HWTEST_F_L0(SnapshotTest, StrictCompatibility) 350{ 351 base::FileHeaderBase::VersionType newVersion = {4, 0, 0, 1}; 352 CompatibilityHelper(newVersion, newVersion, true); 353} 354 355HWTEST_F_L0(SnapshotTest, VersionTest) 356{ 357 base::FileHeaderBase::VersionType version = {4, 3, 2, 1}; 358 uint32_t versionNumber = 0x04030201U; 359 EXPECT_EQ(version, base::FileHeaderBase::ToVersion(versionNumber)); 360 EXPECT_EQ(versionNumber, base::FileHeaderBase::ToVersionNumber(version)); 361} 362 363} // namespace panda::test 364