• 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 #include "ecmascript/serializer/value_serializer.h"
17 
18 #include "ecmascript/base/array_helper.h"
19 #include "ecmascript/js_serializer.h"
20 #include "ecmascript/shared_mm/shared_mm.h"
21 
22 namespace panda::ecmascript {
23 
CheckObjectCanSerialize(TaggedObject * object,bool & findSharedObject)24 bool ValueSerializer::CheckObjectCanSerialize(TaggedObject *object, bool &findSharedObject)
25 {
26     JSType type = object->GetClass()->GetObjectType();
27     if (IsInternalJSType(type)) {
28         return true;
29     }
30     switch (type) {
31         case JSType::JS_ERROR:
32         case JSType::JS_EVAL_ERROR:
33         case JSType::JS_RANGE_ERROR:
34         case JSType::JS_REFERENCE_ERROR:
35         case JSType::JS_TYPE_ERROR:
36         case JSType::JS_AGGREGATE_ERROR:
37         case JSType::JS_URI_ERROR:
38         case JSType::JS_SYNTAX_ERROR:
39         case JSType::JS_OOM_ERROR:
40         case JSType::JS_TERMINATION_ERROR:
41         case JSType::JS_DATE:
42         case JSType::JS_ARRAY:
43         case JSType::JS_MAP:
44         case JSType::JS_SET:
45         case JSType::JS_REG_EXP:
46         case JSType::JS_INT8_ARRAY:
47         case JSType::JS_UINT8_ARRAY:
48         case JSType::JS_UINT8_CLAMPED_ARRAY:
49         case JSType::JS_INT16_ARRAY:
50         case JSType::JS_UINT16_ARRAY:
51         case JSType::JS_INT32_ARRAY:
52         case JSType::JS_UINT32_ARRAY:
53         case JSType::JS_FLOAT32_ARRAY:
54         case JSType::JS_FLOAT64_ARRAY:
55         case JSType::JS_BIGINT64_ARRAY:
56         case JSType::JS_BIGUINT64_ARRAY:
57         case JSType::JS_ARRAY_BUFFER:
58         case JSType::JS_SHARED_ARRAY_BUFFER:
59         case JSType::LINE_STRING:
60         case JSType::CONSTANT_STRING:
61         case JSType::TREE_STRING:
62         case JSType::SLICED_STRING:
63         case JSType::JS_OBJECT:
64         case JSType::JS_ASYNC_FUNCTION:  // means CONCURRENT_FUNCTION
65             return true;
66         case JSType::JS_SHARED_OBJECT: {
67             if (serializeSharedEvent_ > 0) {
68                 return true;
69             }
70             if (defaultCloneShared_ || cloneSharedSet_.find(ToUintPtr(object)) != cloneSharedSet_.end()) {
71                 findSharedObject = true;
72                 serializeSharedEvent_++;
73                 return true;
74             }
75             break;
76         }
77         case JSType::JS_SHARED_FUNCTION: {
78             if (serializeSharedEvent_ > 0) {
79                 return true;
80             }
81             break;
82         }
83         default:
84             break;
85     }
86     LOG_ECMA(ERROR) << "Unsupport serialize object type: " << JSHClass::DumpJSType(type);
87     return false;
88 }
89 
InitTransferSet(CUnorderedSet<uintptr_t> transferDataSet)90 void ValueSerializer::InitTransferSet(CUnorderedSet<uintptr_t> transferDataSet)
91 {
92     transferDataSet_ = std::move(transferDataSet);
93 }
94 
WriteValue(JSThread * thread,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & transfer,const JSHandle<JSTaggedValue> & cloneList)95 bool ValueSerializer::WriteValue(JSThread *thread,
96                                  const JSHandle<JSTaggedValue> &value,
97                                  const JSHandle<JSTaggedValue> &transfer,
98                                  const JSHandle<JSTaggedValue> &cloneList)
99 {
100     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "ValueSerializer::WriteValue");
101     ASSERT(!value->IsWeak());
102     if (!defaultTransfer_ && !PrepareTransfer(thread, transfer)) {
103         LOG_ECMA(ERROR) << "ValueSerialize: PrepareTransfer fail";
104         data_->SetIncompleteData(true);
105         return false;
106     }
107     if (!defaultCloneShared_ && !PrepareClone(thread, cloneList)) {
108         LOG_ECMA(ERROR) << "ValueSerialize: PrepareClone fail";
109         data_->SetIncompleteData(true);
110         return false;
111     }
112     if (value->IsHeapObject()) {
113         // Add fast path for string
114         if (value->IsString()) {
115             vm_->GetSnapshotEnv()->InitializeStringClass();
116         } else {
117             vm_->GetSnapshotEnv()->Initialize();
118         }
119     }
120     SerializeJSTaggedValue(value.GetTaggedValue());
121     if (value->IsHeapObject()) {
122         vm_->GetSnapshotEnv()->ClearEnvMap();
123     }
124     if (notSupport_) {
125         LOG_ECMA(ERROR) << "ValueSerialize: serialize data is incomplete";
126         data_->SetIncompleteData(true);
127         return false;
128     }
129     size_t maxSerializerSize = vm_->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
130     if (data_->Size() > maxSerializerSize) {
131         LOG_ECMA(ERROR) << "The serialization data size has exceed limit Size, current size is: " << data_->Size()
132                         << " max size is: " << maxSerializerSize;
133         return false;
134     }
135     return true;
136 }
137 
SerializeObjectImpl(TaggedObject * object,bool isWeak)138 void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak)
139 {
140     if (notSupport_) {
141         return;
142     }
143     bool cloneSharedObject = false;
144     if (!CheckObjectCanSerialize(object, cloneSharedObject)) {
145         notSupport_ = true;
146         return;
147     }
148     if (isWeak) {
149         data_->WriteEncodeFlag(EncodeFlag::WEAK);
150     }
151     if (SerializeReference(object) || SerializeRootObject(object)) {
152         return;
153     }
154     if (object->GetClass()->IsNativeBindingObject()) {
155         SerializeNativeBindingObject(object);
156         return;
157     }
158     if (object->GetClass()->IsJSError()) {
159         SerializeJSError(object);
160         return;
161     }
162     bool arrayBufferDeferDetach = false;
163     JSTaggedValue trackInfo;
164     JSTaggedType hashfield = JSTaggedValue::VALUE_ZERO;
165     JSType type = object->GetClass()->GetObjectType();
166     // serialize prologue
167     switch (type) {
168         case JSType::JS_ARRAY_BUFFER:
169             arrayBufferDeferDetach = SerializeJSArrayBufferPrologue(object);
170             break;
171         case JSType::JS_SHARED_ARRAY_BUFFER:
172             SerializeJSSharedArrayBufferPrologue(object);
173             break;
174         case JSType::METHOD:
175             SerializeMethodPrologue(reinterpret_cast<Method *>(object));
176             break;
177         case JSType::JS_ARRAY: {
178             JSArray *array = reinterpret_cast<JSArray *>(object);
179             trackInfo = array->GetTrackInfo();
180             array->SetTrackInfo(thread_, JSTaggedValue::Undefined());
181             break;
182         }
183         case JSType::TREE_STRING:
184         case JSType::SLICED_STRING:
185             object = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(object));
186             break;
187         case JSType::JS_REG_EXP:
188             SerializeJSRegExpPrologue(reinterpret_cast<JSRegExp *>(object));
189             break;
190         case JSType::JS_SHARED_FUNCTION: {
191             if (serializeSharedEvent_ > 0) {
192                 data_->WriteEncodeFlag(EncodeFlag::JS_FUNCTION_IN_SHARED);
193             }
194             break;
195         }
196         case JSType::JS_OBJECT:
197             hashfield = Barriers::GetValue<JSTaggedType>(object, JSObject::HASH_OFFSET);
198             Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, JSTaggedValue::VALUE_ZERO);
199             break;
200         default:
201             break;
202     }
203 
204     // serialize object here
205     SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(object);
206 
207     // serialize epilogue
208     if (type == JSType::JS_ARRAY) {
209         JSArray *array = reinterpret_cast<JSArray *>(object);
210         array->SetTrackInfo(thread_, trackInfo);
211     } else if (type == JSType::JS_OBJECT) {
212         if (JSTaggedValue(hashfield).IsHeapObject()) {
213             Barriers::SetObject<true>(thread_, object, JSObject::HASH_OFFSET, hashfield);
214         } else {
215             Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, hashfield);
216         }
217     }
218     if (cloneSharedObject) {
219         serializeSharedEvent_--;
220     }
221     if (arrayBufferDeferDetach) {
222         ASSERT(object->GetClass()->IsArrayBuffer());
223         JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
224         arrayBuffer->Detach(thread_, arrayBuffer->GetWithNativeAreaAllocator());
225     }
226 }
227 
SerializeJSError(TaggedObject * object)228 void ValueSerializer::SerializeJSError(TaggedObject *object)
229 {
230     [[maybe_unused]] EcmaHandleScope scope(thread_);
231     data_->WriteEncodeFlag(EncodeFlag::JS_ERROR);
232     JSType type = object->GetClass()->GetObjectType();
233     ASSERT(type >= JSType::JS_ERROR_FIRST && type <= JSType::JS_ERROR_LAST);
234     data_->WriteUint8(static_cast<uint8_t>(type));
235     auto globalConst = thread_->GlobalConstants();
236     JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
237     JSHandle<JSTaggedValue> msg =
238         JSObject::GetProperty(thread_, JSHandle<JSTaggedValue>(thread_, object), handleMsg).GetValue();
239     if (msg->IsString()) {
240         EcmaString *str = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(msg->GetTaggedObject()));
241         data_->WriteUint8(1); // 1: msg is string
242         SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(str);
243     } else {
244         data_->WriteUint8(0); // 0: msg is undefined
245     }
246 }
247 
SerializeNativeBindingObject(TaggedObject * object)248 void ValueSerializer::SerializeNativeBindingObject(TaggedObject *object)
249 {
250     [[maybe_unused]] EcmaHandleScope scope(thread_);
251     JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
252     JSHandle<JSTaggedValue> nativeBindingSymbol = env->GetNativeBindingSymbol();
253     JSHandle<JSTaggedValue> nativeBindingValue =
254         JSObject::GetProperty(thread_, JSHandle<JSObject>(thread_, object), nativeBindingSymbol).GetRawValue();
255     if (!nativeBindingValue->IsJSNativePointer()) {
256         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject nativeBindingValue is not JSNativePointer";
257         notSupport_ = true;
258         return;
259     }
260     auto info = reinterpret_cast<panda::JSNApi::NativeBindingInfo *>(
261         JSNativePointer::Cast(nativeBindingValue->GetTaggedObject())->GetExternalPointer());
262     if (info == nullptr) {
263         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject NativeBindingInfo is nullptr";
264         notSupport_ = true;
265         return;
266     }
267     void *enginePointer = info->env;
268     void *objPointer = info->nativeValue;
269     void *hint = info->hint;
270     void *detachData = info->detachData;
271     void *attachData = info->attachData;
272     DetachFunc detachNative = reinterpret_cast<DetachFunc>(info->detachFunc);
273     if (detachNative == nullptr) {
274         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject detachNative == nullptr";
275         notSupport_ = true;
276         return;
277     }
278     void *buffer = detachNative(enginePointer, objPointer, hint, detachData);
279     AttachFunc attachNative = reinterpret_cast<AttachFunc>(info->attachFunc);
280     data_->WriteEncodeFlag(EncodeFlag::NATIVE_BINDING_OBJECT);
281     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachNative));
282     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(buffer));
283     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(hint));
284     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachData));
285 }
286 
SerializeJSArrayBufferPrologue(TaggedObject * object)287 bool ValueSerializer::SerializeJSArrayBufferPrologue(TaggedObject *object)
288 {
289     ASSERT(object->GetClass()->IsArrayBuffer());
290     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
291     if (arrayBuffer->IsDetach()) {
292         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached array buffer";
293         notSupport_ = true;
294         return false;
295     }
296     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
297     bool clone = cloneArrayBufferSet_.find(ToUintPtr(object)) != cloneArrayBufferSet_.end();
298     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
299     if (arrayLength > 0) {
300         if (transfer) {
301             if (clone) {
302                 notSupport_ = true;
303                 LOG_ECMA(ERROR) << "ValueSerialize: can't put arraybuffer in both transfer list and clone list";
304                 return false;
305             }
306             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
307             return true;
308         } else if (clone || !defaultTransfer_) {
309             data_->WriteEncodeFlag(EncodeFlag::ARRAY_BUFFER);
310             data_->WriteUint32(arrayLength);
311             JSNativePointer *np =
312                 reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
313             data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
314             return false;
315         } else {
316             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
317             return true;
318         }
319     }
320     return false;
321 }
322 
SerializeJSSharedArrayBufferPrologue(TaggedObject * object)323 void ValueSerializer::SerializeJSSharedArrayBufferPrologue(TaggedObject *object)
324 {
325     ASSERT(object->GetClass()->IsSharedArrayBuffer());
326     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
327     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
328     if (arrayBuffer->IsDetach() || transfer) {
329         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached or transfer shared array buffer";
330         notSupport_ = true;
331         return;
332     }
333     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
334     if (arrayLength > 0) {
335         JSNativePointer *np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
336         void *buffer = np->GetExternalPointer();
337         if (JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength)) {
338             LOG_ECMA(ERROR) << "ValueSerialize: can't find buffer form shared memory pool";
339             notSupport_ = true;
340             return;
341         }
342     }
343 }
344 
SerializeMethodPrologue(Method * method)345 void ValueSerializer::SerializeMethodPrologue(Method *method)
346 {
347     JSTaggedValue constPoolVal = method->GetConstantPool();
348     if (!constPoolVal.IsHeapObject()) {
349         return;
350     }
351     ConstantPool *constPool = reinterpret_cast<ConstantPool *>(constPoolVal.GetTaggedObject());
352     const JSPandaFile *jsPandaFile = constPool->GetJSPandaFile();
353     data_->WriteEncodeFlag(EncodeFlag::METHOD);
354     data_->WriteJSTaggedType(method->GetLiteralInfo());
355     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(jsPandaFile));
356 }
357 
SerializeJSRegExpPrologue(JSRegExp * jsRegExp)358 void ValueSerializer::SerializeJSRegExpPrologue(JSRegExp *jsRegExp)
359 {
360     uint32_t bufferSize = jsRegExp->GetLength();
361     if (bufferSize == 0) {
362         LOG_ECMA(ERROR) << "ValueSerialize: JSRegExp buffer size is 0";
363         notSupport_ = true;
364         return;
365     }
366 
367     data_->WriteEncodeFlag(EncodeFlag::JS_REG_EXP);
368     data_->WriteUint32(bufferSize);
369     JSNativePointer *np =
370         reinterpret_cast<JSNativePointer *>(jsRegExp->GetByteCodeBuffer().GetTaggedObject());
371     data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), bufferSize);
372 }
373 
PrepareTransfer(JSThread * thread,const JSHandle<JSTaggedValue> & transfer)374 bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
375 {
376     if (transfer->IsUndefined()) {
377         return true;
378     }
379     if (!transfer->IsJSArray()) {
380         return false;
381     }
382     int len = base::ArrayHelper::GetArrayLength(thread, transfer);
383     int k = 0;
384     while (k < len) {
385         bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
386         if (exists) {
387             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
388             if (!element->IsArrayBuffer()) {
389                 return false;
390             }
391             transferDataSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
392         }
393         k++;
394     }
395     return true;
396 }
397 
PrepareClone(JSThread * thread,const JSHandle<JSTaggedValue> & cloneList)398 bool ValueSerializer::PrepareClone(JSThread *thread, const JSHandle<JSTaggedValue> &cloneList)
399 {
400     if (cloneList->IsUndefined()) {
401         return true;
402     }
403     if (!cloneList->IsJSArray()) {
404         return false;
405     }
406     int len = base::ArrayHelper::GetArrayLength(thread, cloneList);
407     int index = 0;
408     while (index < len) {
409         bool exists = JSTaggedValue::HasProperty(thread, cloneList, index);
410         if (exists) {
411             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, cloneList, index);
412             if (element->IsArrayBuffer()) {
413                 cloneArrayBufferSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
414             } else if (element->IsJSSharedObject()) {
415                 cloneSharedSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
416             } else {
417                 return false;
418             }
419         }
420         index++;
421     }
422     return true;
423 }
424 }  // namespace panda::ecmascript
425 
426