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