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#include "ecmascript/serializer/base_serializer-inl.h"
17
18namespace panda::ecmascript {
19
20SerializedObjectSpace BaseSerializer::GetSerializedObjectSpace(TaggedObject *object) const
21{
22    auto region = Region::ObjectAddressToRange(object);
23    auto flag = region->GetRegionSpaceFlag();
24    switch (flag) {
25        case RegionSpaceFlag::IN_EDEN_SPACE:
26        case RegionSpaceFlag::IN_OLD_SPACE:
27        case RegionSpaceFlag::IN_YOUNG_SPACE:
28        case RegionSpaceFlag::IN_APPSPAWN_SPACE:
29            return SerializedObjectSpace::OLD_SPACE;
30        case RegionSpaceFlag::IN_NON_MOVABLE_SPACE:
31        case RegionSpaceFlag::IN_READ_ONLY_SPACE:
32            return SerializedObjectSpace::NON_MOVABLE_SPACE;
33        case RegionSpaceFlag::IN_MACHINE_CODE_SPACE:
34            return SerializedObjectSpace::MACHINE_CODE_SPACE;
35        case RegionSpaceFlag::IN_HUGE_OBJECT_SPACE:
36            return SerializedObjectSpace::HUGE_SPACE;
37        case RegionSpaceFlag::IN_SHARED_APPSPAWN_SPACE:
38        case RegionSpaceFlag::IN_SHARED_OLD_SPACE:
39            return SerializedObjectSpace::SHARED_OLD_SPACE;
40        case RegionSpaceFlag::IN_SHARED_NON_MOVABLE:
41            return SerializedObjectSpace::SHARED_NON_MOVABLE_SPACE;
42        case RegionSpaceFlag::IN_SHARED_HUGE_OBJECT_SPACE:
43            return SerializedObjectSpace::SHARED_HUGE_SPACE;
44        default:
45            LOG_ECMA(FATAL) << "this branch is unreachable";
46            UNREACHABLE();
47    }
48}
49
50void BaseSerializer::WriteMultiRawData(uintptr_t beginAddr, size_t fieldSize)
51{
52    if (fieldSize > 0) {
53        data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
54        data_->WriteUint32(fieldSize);
55        data_->WriteRawData(reinterpret_cast<uint8_t *>(beginAddr), fieldSize);
56    }
57}
58
59// Write JSTaggedValue could be either a pointer to a HeapObject or a value
60void BaseSerializer::SerializeJSTaggedValue(JSTaggedValue value)
61{
62    DISALLOW_GARBAGE_COLLECTION;
63    if (!value.IsHeapObject()) {
64        data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
65        data_->WriteJSTaggedValue(value);
66    } else {
67        TaggedObject *object = nullptr;
68        bool isWeak = value.IsWeak();
69        object = isWeak ? value.GetWeakReferent() : value.GetTaggedObject();
70        SerializeObjectImpl(object, isWeak);
71    }
72}
73
74bool BaseSerializer::SerializeReference(TaggedObject *object)
75{
76    if (referenceMap_.find(object) != referenceMap_.end()) {
77        uint32_t objectIndex = referenceMap_.find(object)->second;
78        data_->WriteEncodeFlag(EncodeFlag::REFERENCE);
79        data_->WriteUint32(objectIndex);
80        return true;
81    }
82    return false;
83}
84
85bool BaseSerializer::SerializeRootObject(TaggedObject *object)
86{
87    size_t index = vm_->GetSnapshotEnv()->FindEnvObjectIndex(ToUintPtr(object));
88    if (index != SnapshotEnv::MAX_UINT_32) {
89        data_->WriteEncodeFlag(EncodeFlag::ROOT_OBJECT);
90        data_->WriteUint32(index);
91        return true;
92    }
93
94    return false;
95}
96
97void BaseSerializer::SerializeSharedObject(TaggedObject *object)
98{
99    data_->WriteEncodeFlag(EncodeFlag::SHARED_OBJECT);
100    data_->WriteUint32(sharedObjChunk_->Size());
101    referenceMap_.emplace(object, objectIndex_++);
102    sharedObjChunk_->Emplace(static_cast<JSTaggedType>(ToUintPtr(object)));
103}
104
105bool BaseSerializer::SerializeSpecialObjIndividually(JSType objectType, TaggedObject *root,
106                                                     ObjectSlot start, ObjectSlot end)
107{
108    switch (objectType) {
109        case JSType::HCLASS:
110            SerializeHClassFieldIndividually(root, start, end);
111            return true;
112        case JSType::LEXICAL_ENV:
113            SerializeLexicalEnvFieldIndividually(root, start, end);
114            return true;
115        case JSType::SENDABLE_ENV:
116            SerializeSendableEnvFieldIndividually(root, start, end);
117            return true;
118        case JSType::JS_SHARED_FUNCTION:
119        case JSType::JS_SHARED_ASYNC_FUNCTION:
120            SerializeSFunctionFieldIndividually(root, start, end);
121            return true;
122        case JSType::JS_ASYNC_FUNCTION:
123            SerializeAsyncFunctionFieldIndividually(root, start, end);
124            return true;
125        default:
126            return false;
127    }
128}
129
130void BaseSerializer::SerializeHClassFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
131{
132    ASSERT(root->GetClass()->IsHClass());
133    ObjectSlot slot = start;
134    while (slot < end) {
135        size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
136        switch (fieldOffset) {
137            case JSHClass::PROTOTYPE_OFFSET: {
138                JSHClass *kclass = reinterpret_cast<JSHClass *>(root);
139                JSTaggedValue proto = kclass->GetPrototype();
140                JSType type = kclass->GetObjectType();
141                if ((serializeSharedEvent_ > 0) &&
142                    (type == JSType::JS_SHARED_OBJECT || type == JSType::JS_SHARED_FUNCTION)) {
143                    SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
144                } else {
145                    SerializeObjectProto(kclass, proto);
146                }
147                slot++;
148                break;
149            }
150            case JSHClass::TRANSTIONS_OFFSET:
151            case JSHClass::PARENT_OFFSET: {
152                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
153                data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
154                slot++;
155                break;
156            }
157            case JSHClass::PROTO_CHANGE_MARKER_OFFSET:
158            case JSHClass::PROTO_CHANGE_DETAILS_OFFSET:
159            case JSHClass::ENUM_CACHE_OFFSET: {
160                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
161                data_->WriteJSTaggedValue(JSTaggedValue::Null());
162                slot++;
163                break;
164            }
165            default: {
166                SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
167                slot++;
168                break;
169            }
170        }
171    }
172}
173
174void BaseSerializer::SerializeSFunctionFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
175{
176    ASSERT(root->GetClass()->GetObjectType() == JSType::JS_SHARED_FUNCTION ||
177        root->GetClass()->GetObjectType() == JSType::JS_SHARED_ASYNC_FUNCTION);
178    ObjectSlot slot = start;
179    while (slot < end) {
180        size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
181        switch (fieldOffset) {
182            case JSFunction::MACHINECODE_OFFSET:
183            case JSFunction::BASELINECODE_OFFSET:
184            case JSFunction::RAW_PROFILE_TYPE_INFO_OFFSET: {
185                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
186                data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
187                slot++;
188                break;
189            }
190            case JSFunction::ECMA_MODULE_OFFSET: {
191                SerializeSFunctionModule(JSFunction::Cast(root));
192                slot++;
193                break;
194            }
195            case JSFunction::WORK_NODE_POINTER_OFFSET: {
196                data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
197                data_->WriteUint32(sizeof(uintptr_t));
198                data_->WriteRawData(reinterpret_cast<uint8_t *>(slot.SlotAddress()), sizeof(uintptr_t));
199                break;
200            }
201            default: {
202                SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
203                slot++;
204                break;
205            }
206        }
207    }
208}
209
210void BaseSerializer::SerializeSFunctionModule(JSFunction *func)
211{
212    JSTaggedValue moduleValue = func->GetModule();
213    if (moduleValue.IsHeapObject()) {
214        if (!Region::ObjectAddressToRange(moduleValue.GetTaggedObject())->InSharedHeap()) {
215            LOG_ECMA(ERROR) << "Shared function reference to local module";
216        }
217        if (!SerializeReference(moduleValue.GetTaggedObject())) {
218            // Module of shared function should write pointer directly when serialize
219            SerializeSharedObject(moduleValue.GetTaggedObject());
220        }
221    } else {
222        data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
223        data_->WriteJSTaggedValue(moduleValue);
224    }
225}
226
227void BaseSerializer::SerializeLexicalEnvFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
228{
229    ASSERT(root->GetClass()->GetObjectType() == JSType::LEXICAL_ENV);
230    ObjectSlot slot = start;
231    while (slot < end) {
232        size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
233        switch (fieldOffset) {
234            case PARENT_ENV_SLOT:
235            case SCOPE_INFO_SLOT: {
236                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
237                data_->WriteJSTaggedValue(JSTaggedValue::Hole());
238                slot++;
239                break;
240            }
241            default: {
242                SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
243                slot++;
244                break;
245            }
246        }
247    }
248}
249
250void BaseSerializer::SerializeSendableEnvFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
251{
252    ASSERT(root->GetClass()->GetObjectType() == JSType::SENDABLE_ENV);
253    ObjectSlot slot = start;
254    while (slot < end) {
255        size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
256        switch (fieldOffset) {
257            case PARENT_ENV_SLOT:
258            case SCOPE_INFO_SLOT: {
259                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
260                data_->WriteJSTaggedValue(JSTaggedValue::Hole());
261                slot++;
262                break;
263            }
264            default: {
265                SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
266                slot++;
267                break;
268            }
269        }
270    }
271}
272
273void BaseSerializer::SerializeAsyncFunctionFieldIndividually(TaggedObject *root, ObjectSlot start, ObjectSlot end)
274{
275    ASSERT(root->GetClass()->GetObjectType() == JSType::JS_ASYNC_FUNCTION);
276    ObjectSlot slot = start;
277    while (slot < end) {
278        size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root);
279        switch (fieldOffset) {
280            // hash filed
281            case sizeof(TaggedObject): {
282                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
283                data_->WriteJSTaggedValue(JSTaggedValue(0)); // 0: reset hash filed
284                slot++;
285                break;
286            }
287            case JSFunction::PROTO_OR_DYNCLASS_OFFSET:
288            case JSFunction::LEXICAL_ENV_OFFSET:
289            case JSFunction::MACHINECODE_OFFSET:
290            case JSFunction::BASELINECODE_OFFSET:
291            case JSFunction::RAW_PROFILE_TYPE_INFO_OFFSET:
292            case JSFunction::HOME_OBJECT_OFFSET:
293            case JSFunction::ECMA_MODULE_OFFSET: {
294                data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
295                data_->WriteJSTaggedValue(JSTaggedValue::Undefined());
296                slot++;
297                break;
298            }
299            case JSFunction::WORK_NODE_POINTER_OFFSET: {
300                data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA);
301                data_->WriteUint32(sizeof(uintptr_t));
302                data_->WriteRawData(reinterpret_cast<uint8_t *>(slot.SlotAddress()), sizeof(uintptr_t));
303                slot++;
304                break;
305            }
306            default: {
307                SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
308                slot++;
309                break;
310            }
311        }
312    }
313}
314
315void BaseSerializer::SerializeObjectProto(JSHClass *kclass, JSTaggedValue proto)
316{
317    if (!proto.IsHeapObject()) {
318        data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
319        data_->WriteJSTaggedValue(proto);
320    } else if (!SerializeReference(proto.GetTaggedObject()) && !SerializeRootObject(proto.GetTaggedObject())) {
321        data_->WriteEncodeFlag(EncodeFlag::OBJECT_PROTO);
322        data_->WriteUint8(static_cast<uint8_t>(kclass->GetObjectType()));
323    }
324}
325
326void BaseSerializer::SerializeTaggedObjField(SerializeType serializeType, TaggedObject *root,
327                                             ObjectSlot start, ObjectSlot end)
328{
329    JSType objectType = root->GetClass()->GetObjectType();
330    if (serializeType != SerializeType::VALUE_SERIALIZE
331        || !SerializeSpecialObjIndividually(objectType, root, start, end)) {
332        for (ObjectSlot slot = start; slot < end; slot++) {
333            SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
334        }
335    }
336}
337
338void BaseSerializer::SerializeInObjField(TaggedObject *object, ObjectSlot start, ObjectSlot end)
339{
340    auto hclass = object->GetClass();
341    auto layout = LayoutInfo::Cast(hclass->GetLayout().GetTaggedObject());
342    size_t index = 0;
343    for (ObjectSlot slot = start; slot < end; slot++) {
344        auto attr = layout->GetAttr(index++);
345        if (attr.GetRepresentation() == Representation::DOUBLE || attr.GetRepresentation() == Representation::INT) {
346            auto fieldAddr = slot.SlotAddress();
347            data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE);
348            data_->WriteRawData(reinterpret_cast<uint8_t *>(fieldAddr), sizeof(JSTaggedType));
349        } else {
350            SerializeJSTaggedValue(JSTaggedValue(slot.GetTaggedType()));
351        }
352    }
353}
354}  // namespace panda::ecmascript
355
356