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