1 /* 2 * Copyright (c) 2023 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 #ifndef ECMASCRIPT_SERIALIZER_SERIALIZE_DATA_H 17 #define ECMASCRIPT_SERIALIZER_SERIALIZE_DATA_H 18 19 #include <limits> 20 21 #include "ecmascript/js_tagged_value-inl.h" 22 #include "ecmascript/mem/dyn_chunk.h" 23 #include "ecmascript/runtime.h" 24 #include "ecmascript/shared_mm/shared_mm.h" 25 #include "ecmascript/snapshot/mem/snapshot_env.h" 26 27 namespace panda::ecmascript { 28 constexpr size_t INITIAL_CAPACITY = 64; 29 constexpr int CAPACITY_INCREASE_RATE = 2; 30 constexpr uint32_t RESERVED_INDEX = 0; 31 32 typedef void* (*DetachFunc)(void *enginePointer, void *objPointer, void *hint, void *detachData); 33 typedef Local<JSValueRef> (*AttachFunc)(void *enginePointer, void *buffer, void *hint, void *attachData); 34 typedef void (*DetachFinalizer)(void *detachedObject, void *finalizerHint); 35 36 struct NativeBindingDetachInfo { 37 DetachFinalizer detachedFinalizer = nullptr; 38 void *detachedObject = nullptr; 39 void *detachedHint = nullptr; 40 NativeBindingDetachInfoNativeBindingDetachInfo41 NativeBindingDetachInfo(void *df, void *dObj, void *hint) 42 : detachedFinalizer(reinterpret_cast<DetachFinalizer>(df)), detachedObject(dObj), detachedHint(hint) 43 { 44 } 45 }; 46 47 enum class EncodeFlag : uint8_t { 48 // 0x00~0x06 represent new object to different space: 49 // 0x00: old space 50 // 0x01: non movable space 51 // 0x02: machine code space 52 // 0x03: huge space 53 // 0x04: shared old space 54 // 0x05: shared non movable space 55 // 0x06: shared huge space 56 NEW_OBJECT = 0x00, 57 REFERENCE = 0x07, 58 WEAK, 59 PRIMITIVE, 60 MULTI_RAW_DATA, 61 ROOT_OBJECT, 62 OBJECT_PROTO, 63 ARRAY_BUFFER, 64 TRANSFER_ARRAY_BUFFER, 65 SHARED_ARRAY_BUFFER, 66 SENDABLE_ARRAY_BUFFER, 67 NATIVE_BINDING_OBJECT, 68 JS_ERROR, 69 JS_REG_EXP, 70 SHARED_OBJECT, 71 LAST 72 }; 73 74 enum class SerializedObjectSpace : uint8_t { 75 OLD_SPACE = 0, 76 NON_MOVABLE_SPACE, 77 MACHINE_CODE_SPACE, 78 HUGE_SPACE, 79 SHARED_OLD_SPACE, 80 SHARED_NON_MOVABLE_SPACE, 81 SHARED_HUGE_SPACE 82 }; 83 84 enum class SerializeType : uint8_t { 85 VALUE_SERIALIZE, 86 PGO_SERIALIZE 87 }; 88 89 class SerializeData { 90 public: SerializeData(JSThread * thread)91 explicit SerializeData(JSThread *thread) : thread_(thread) {} ~SerializeData()92 ~SerializeData() 93 { 94 regionRemainSizeVector_.clear(); 95 // decrease sharedArrayBuffer reference 96 if (sharedArrayBufferSet_.size() > 0) { 97 DecreaseSharedArrayBufferReference(); 98 } 99 for (const auto &info: nativeBindingDetachInfos_) { 100 auto finalizer = reinterpret_cast<DetachFinalizer>(info->detachedFinalizer); 101 if (finalizer != nullptr) { 102 finalizer(info->detachedObject, info->detachedHint); 103 } 104 delete info; 105 } 106 nativeBindingDetachInfos_.clear(); 107 free(buffer_); 108 if (!incompleteData_ && dataIndex_ != RESERVED_INDEX) { 109 Runtime::GetInstance()->RemoveSerializationRoot(thread_, dataIndex_); 110 } 111 } 112 NO_COPY_SEMANTIC(SerializeData); 113 NO_MOVE_SEMANTIC(SerializeData); 114 EncodeNewObject(SerializedObjectSpace space)115 static uint8_t EncodeNewObject(SerializedObjectSpace space) 116 { 117 return static_cast<uint8_t>(space) | static_cast<uint8_t>(EncodeFlag::NEW_OBJECT); 118 } 119 DecodeSpace(uint8_t type)120 static SerializedObjectSpace DecodeSpace(uint8_t type) 121 { 122 ASSERT(type < static_cast<uint8_t>(EncodeFlag::REFERENCE)); 123 return static_cast<SerializedObjectSpace>(type); 124 } 125 AlignUpRegionAvailableSize(size_t size)126 static size_t AlignUpRegionAvailableSize(size_t size) 127 { 128 if (size == 0) { 129 return Region::GetRegionAvailableSize(); 130 } 131 size_t regionAvailableSize = Region::GetRegionAvailableSize(); 132 return ((size - 1) / regionAvailableSize + 1) * regionAvailableSize; // 1: align up 133 } 134 ExpandBuffer(size_t requestedSize)135 bool ExpandBuffer(size_t requestedSize) 136 { 137 size_t newCapacity = bufferCapacity_ * CAPACITY_INCREASE_RATE; 138 newCapacity = std::max(newCapacity, requestedSize); 139 if (newCapacity > sizeLimit_) { 140 return false; 141 } 142 uint8_t *newBuffer = reinterpret_cast<uint8_t *>(malloc(newCapacity)); 143 if (newBuffer == nullptr) { 144 return false; 145 } 146 if (memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_) != EOK) { 147 LOG_FULL(ERROR) << "Failed to memcpy_s Data"; 148 free(newBuffer); 149 return false; 150 } 151 free(buffer_); 152 buffer_ = newBuffer; 153 bufferCapacity_ = newCapacity; 154 return true; 155 } 156 AllocateBuffer(size_t bytes)157 bool AllocateBuffer(size_t bytes) 158 { 159 // Get internal heap size 160 if (sizeLimit_ == 0) { 161 uint64_t heapSize = thread_->GetEcmaVM()->GetJSOptions().GetSerializerBufferSizeLimit(); 162 sizeLimit_ = heapSize; 163 } 164 size_t oldSize = bufferSize_; 165 size_t newSize = oldSize + bytes; 166 if (newSize > sizeLimit_) { 167 return false; 168 } 169 if (bufferCapacity_ == 0) { 170 if (bytes < INITIAL_CAPACITY) { 171 buffer_ = reinterpret_cast<uint8_t *>(malloc(INITIAL_CAPACITY)); 172 if (buffer_ != nullptr) { 173 bufferCapacity_ = INITIAL_CAPACITY; 174 return true; 175 } else { 176 return false; 177 } 178 } else { 179 buffer_ = reinterpret_cast<uint8_t *>(malloc(bytes)); 180 if (buffer_ != nullptr) { 181 bufferCapacity_ = bytes; 182 return true; 183 } else { 184 return false; 185 } 186 } 187 } 188 if (newSize > bufferCapacity_) { 189 if (!ExpandBuffer(newSize)) { 190 return false; 191 } 192 } 193 return true; 194 } 195 RawDataEmit(const void * data,size_t length)196 ssize_t RawDataEmit(const void *data, size_t length) 197 { 198 return RawDataEmit(data, length, bufferSize_); 199 } 200 RawDataEmit(const void * data,size_t length,size_t offset)201 ssize_t RawDataEmit(const void *data, size_t length, size_t offset) 202 { 203 if (length <= 0) { 204 return -1; 205 } 206 if ((offset + length) > bufferCapacity_) { 207 if (!AllocateBuffer(length)) { 208 return -1; 209 } 210 } 211 if (memcpy_s(buffer_ + offset, bufferCapacity_ - offset, data, length) != EOK) { 212 LOG_FULL(ERROR) << "Failed to memcpy_s Data"; 213 return -1; 214 } 215 if (UNLIKELY(offset > std::numeric_limits<ssize_t>::max())) { 216 return -1; 217 } 218 ssize_t res = static_cast<ssize_t>(offset); 219 if (bufferSize_ == offset) { 220 bufferSize_ += length; 221 } 222 return res; 223 } 224 EmitChar(uint8_t c)225 void EmitChar(uint8_t c) 226 { 227 RawDataEmit(&c, U8_SIZE); 228 } 229 EmitU64(uint64_t c)230 ssize_t EmitU64(uint64_t c) 231 { 232 return RawDataEmit(reinterpret_cast<uint8_t *>(&c), U64_SIZE); 233 } 234 EmitU64(uint64_t c,size_t offset)235 ssize_t EmitU64(uint64_t c, size_t offset) 236 { 237 return RawDataEmit(reinterpret_cast<uint8_t *>(&c), U64_SIZE, offset); 238 } 239 WriteUint8(uint8_t data)240 void WriteUint8(uint8_t data) 241 { 242 RawDataEmit(&data, 1); 243 } 244 ReadUint8(size_t & position)245 uint8_t ReadUint8(size_t &position) 246 { 247 ASSERT(position < Size()); 248 return *(buffer_ + (position++)); 249 } 250 WriteEncodeFlag(EncodeFlag flag)251 void WriteEncodeFlag(EncodeFlag flag) 252 { 253 EmitChar(static_cast<uint8_t>(flag)); 254 } 255 WriteUint32(uint32_t data)256 void WriteUint32(uint32_t data) 257 { 258 RawDataEmit(reinterpret_cast<uint8_t *>(&data), U32_SIZE); 259 } 260 ReadUint32(size_t & position)261 uint32_t ReadUint32(size_t &position) 262 { 263 ASSERT(position < Size()); 264 uint32_t value = *reinterpret_cast<uint32_t *>(buffer_ + position); 265 position += sizeof(uint32_t); 266 return value; 267 } 268 WriteRawData(uint8_t * data,size_t length)269 void WriteRawData(uint8_t *data, size_t length) 270 { 271 RawDataEmit(data, length); 272 } 273 WriteJSTaggedValue(JSTaggedValue value)274 void WriteJSTaggedValue(JSTaggedValue value) 275 { 276 EmitU64(value.GetRawData()); 277 } 278 WriteJSTaggedType(JSTaggedType value)279 ssize_t WriteJSTaggedType(JSTaggedType value) 280 { 281 return EmitU64(value); 282 } 283 ReadJSTaggedType(size_t & position)284 JSTaggedType ReadJSTaggedType(size_t &position) 285 { 286 ASSERT(position < Size()); 287 JSTaggedType value = *reinterpret_cast<uint64_t *>(buffer_ + position); 288 position += sizeof(JSTaggedType); 289 return value; 290 } 291 ReadRawData(uintptr_t addr,size_t len,size_t & position)292 void ReadRawData(uintptr_t addr, size_t len, size_t &position) 293 { 294 ASSERT(position + len <= Size()); 295 if (memcpy_s(reinterpret_cast<void *>(addr), len, buffer_ + position, len) != EOK) { 296 LOG_ECMA(FATAL) << "this branch is unreachable"; 297 UNREACHABLE(); 298 } 299 position += len; 300 } 301 Data()302 uint8_t* Data() const 303 { 304 return buffer_; 305 } 306 Size()307 size_t Size() const 308 { 309 return bufferSize_; 310 } 311 SetIncompleteData(bool incomplete)312 void SetIncompleteData(bool incomplete) 313 { 314 incompleteData_ = incomplete; 315 } 316 IsIncompleteData()317 bool IsIncompleteData() const 318 { 319 return incompleteData_; 320 } 321 GetRegionRemainSizeVector()322 const std::vector<size_t>& GetRegionRemainSizeVector() const 323 { 324 return regionRemainSizeVector_; 325 } 326 GetOldSpaceSize()327 size_t GetOldSpaceSize() const 328 { 329 return oldSpaceSize_; 330 } 331 GetNonMovableSpaceSize()332 size_t GetNonMovableSpaceSize() const 333 { 334 return nonMovableSpaceSize_; 335 } 336 GetMachineCodeSpaceSize()337 size_t GetMachineCodeSpaceSize() const 338 { 339 return machineCodeSpaceSize_; 340 } 341 GetSharedOldSpaceSize()342 size_t GetSharedOldSpaceSize() const 343 { 344 return sharedOldSpaceSize_; 345 } 346 GetSharedNonMovableSpaceSize()347 size_t GetSharedNonMovableSpaceSize() const 348 { 349 return sharedNonMovableSpaceSize_; 350 } 351 CalculateSerializedObjectSize(SerializedObjectSpace space,size_t objectSize)352 void CalculateSerializedObjectSize(SerializedObjectSpace space, size_t objectSize) 353 { 354 switch (space) { 355 case SerializedObjectSpace::OLD_SPACE: 356 AlignSpaceObjectSize(oldSpaceSize_, objectSize); 357 break; 358 case SerializedObjectSpace::NON_MOVABLE_SPACE: 359 AlignSpaceObjectSize(nonMovableSpaceSize_, objectSize); 360 break; 361 case SerializedObjectSpace::MACHINE_CODE_SPACE: 362 AlignSpaceObjectSize(machineCodeSpaceSize_, objectSize); 363 break; 364 case SerializedObjectSpace::SHARED_OLD_SPACE: 365 AlignSpaceObjectSize(sharedOldSpaceSize_, objectSize); 366 break; 367 case SerializedObjectSpace::SHARED_NON_MOVABLE_SPACE: 368 AlignSpaceObjectSize(sharedNonMovableSpaceSize_, objectSize); 369 break; 370 default: 371 break; 372 } 373 } 374 AlignSpaceObjectSize(size_t & spaceSize,size_t objectSize)375 void AlignSpaceObjectSize(size_t &spaceSize, size_t objectSize) 376 { 377 size_t alignRegionSize = AlignUpRegionAvailableSize(spaceSize); 378 if (UNLIKELY(spaceSize + objectSize > alignRegionSize)) { 379 regionRemainSizeVector_.push_back(alignRegionSize - spaceSize); 380 spaceSize = alignRegionSize; 381 } 382 spaceSize += objectSize; 383 ASSERT(spaceSize <= SnapshotEnv::MAX_UINT_32); 384 } 385 DecreaseSharedArrayBufferReference()386 void DecreaseSharedArrayBufferReference() 387 { 388 auto manager = JSSharedMemoryManager::GetInstance(); 389 for (auto iter = sharedArrayBufferSet_.begin(); iter != sharedArrayBufferSet_.end(); iter++) { 390 JSSharedMemoryManager::RemoveSharedMemory(thread_->GetEnv(), reinterpret_cast<void *>(*iter), manager); 391 } 392 sharedArrayBufferSet_.clear(); 393 } 394 insertSharedArrayBuffer(uintptr_t ptr)395 void insertSharedArrayBuffer(uintptr_t ptr) 396 { 397 sharedArrayBufferSet_.insert(ptr); 398 } 399 SetDataIndex(uint32_t dataIndex)400 void SetDataIndex(uint32_t dataIndex) 401 { 402 dataIndex_ = dataIndex; 403 } 404 GetDataIndex()405 uint32_t GetDataIndex() const 406 { 407 return dataIndex_; 408 } 409 AddNativeBindingDetachInfo(panda::JSNApi::NativeBindingInfo * info,void * dObj)410 void AddNativeBindingDetachInfo(panda::JSNApi::NativeBindingInfo *info, void *dObj) 411 { 412 auto *detachInfo = new NativeBindingDetachInfo(info->detachedFinalizer, dObj, info->detachedHint); 413 nativeBindingDetachInfos_.insert(detachInfo); 414 } 415 416 private: 417 static constexpr size_t U8_SIZE = 1; 418 static constexpr size_t U16_SIZE = 2; 419 static constexpr size_t U32_SIZE = 4; 420 static constexpr size_t U64_SIZE = 8; 421 JSThread *thread_; 422 uint32_t dataIndex_ {RESERVED_INDEX}; 423 uint8_t *buffer_ {nullptr}; 424 uint64_t sizeLimit_ {0}; 425 size_t bufferSize_ {0}; 426 size_t bufferCapacity_ {0}; 427 size_t oldSpaceSize_ {0}; 428 size_t nonMovableSpaceSize_ {0}; 429 size_t machineCodeSpaceSize_ {0}; 430 size_t sharedOldSpaceSize_ {0}; 431 size_t sharedNonMovableSpaceSize_ {0}; 432 bool incompleteData_ {false}; 433 std::vector<size_t> regionRemainSizeVector_ {}; 434 std::set<uintptr_t> sharedArrayBufferSet_ {}; 435 std::set<NativeBindingDetachInfo *> nativeBindingDetachInfos_ {}; 436 }; 437 } 438 439 #endif // ECMASCRIPT_SERIALIZER_SERIALIZE_DATA_H 440