• 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/checkpoint/thread_state_transition.h"
19 #include "ecmascript/base/array_helper.h"
20 
21 namespace panda::ecmascript {
22 
CheckObjectCanSerialize(TaggedObject * object,bool & findSharedObject)23 bool ValueSerializer::CheckObjectCanSerialize(TaggedObject *object, bool &findSharedObject)
24 {
25     JSType type = object->GetClass()->GetObjectType();
26     if (IsInternalJSType(type)) {
27         return true;
28     }
29     switch (type) {
30         case JSType::JS_PRIMITIVE_REF:
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_API_BITVECTOR:
67         case JSType::JS_SHARED_SET:
68         case JSType::JS_SHARED_MAP:
69         case JSType::JS_SENDABLE_ARRAY_BUFFER:
70         case JSType::JS_SHARED_ARRAY:
71         case JSType::JS_SHARED_INT8_ARRAY:
72         case JSType::JS_SHARED_UINT8_ARRAY:
73         case JSType::JS_SHARED_UINT8_CLAMPED_ARRAY:
74         case JSType::JS_SHARED_INT16_ARRAY:
75         case JSType::JS_SHARED_UINT16_ARRAY:
76         case JSType::JS_SHARED_INT32_ARRAY:
77         case JSType::JS_SHARED_UINT32_ARRAY:
78         case JSType::JS_SHARED_FLOAT32_ARRAY:
79         case JSType::JS_SHARED_FLOAT64_ARRAY:
80         case JSType::JS_SHARED_BIGINT64_ARRAY:
81         case JSType::JS_SHARED_BIGUINT64_ARRAY:
82         case JSType::JS_SHARED_OBJECT:
83         case JSType::JS_SHARED_FUNCTION:
84         case JSType::JS_SHARED_ASYNC_FUNCTION: {
85             if (serializeSharedEvent_ > 0) {
86                 return true;
87             }
88             if (defaultCloneShared_ || cloneSharedSet_.find(ToUintPtr(object)) != cloneSharedSet_.end()) {
89                 findSharedObject = true;
90                 serializeSharedEvent_++;
91             }
92             return true;
93         }
94         default:
95             break;
96     }
97     LOG_ECMA(ERROR) << "Unsupport serialize object type: " << JSHClass::DumpJSType(type);
98     return false;
99 }
100 
WriteValue(JSThread * thread,const JSHandle<JSTaggedValue> & value,const JSHandle<JSTaggedValue> & transfer,const JSHandle<JSTaggedValue> & cloneList)101 bool ValueSerializer::WriteValue(JSThread *thread,
102                                  const JSHandle<JSTaggedValue> &value,
103                                  const JSHandle<JSTaggedValue> &transfer,
104                                  const JSHandle<JSTaggedValue> &cloneList)
105 {
106     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "ValueSerializer::WriteValue");
107     ASSERT(!value->IsWeak());
108     if (!defaultTransfer_ && !PrepareTransfer(thread, transfer)) {
109         LOG_ECMA(ERROR) << "ValueSerialize: PrepareTransfer fail";
110         data_->SetIncompleteData(true);
111         return false;
112     }
113     if (!defaultCloneShared_ && !PrepareClone(thread, cloneList)) {
114         LOG_ECMA(ERROR) << "ValueSerialize: PrepareClone fail";
115         data_->SetIncompleteData(true);
116         return false;
117     }
118     SerializeJSTaggedValue(value.GetTaggedValue());
119     // ThreadNativeScope may trigger moving gc, so PushSerializationRoot must do before native state.
120     // Push share root object to runtime map
121     uint32_t index = data_->GetDataIndex();
122     bool chunkEmpty = sharedObjChunk_->Empty();
123     if (!chunkEmpty) {
124         index = Runtime::GetInstance()->PushSerializationRoot(thread_, std::move(sharedObjChunk_));
125     }
126     {
127         ThreadNativeScope nativeScope(thread);
128         for (auto &entry : detachCallbackInfo_) {
129             auto info = entry.second;
130             DetachFunc detachNative = reinterpret_cast<DetachFunc>(info->detachFunc);
131             if (detachNative == nullptr || entry.first < 0) {
132                 LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject detachNative == nullptr";
133                 notSupport_ = true;
134                 break;
135             }
136             void *buffer = detachNative(info->env, info->nativeValue, info->hint, info->detachData);
137             if (info->detachedFinalizer != nullptr) {
138                 data_->AddNativeBindingDetachInfo(info, buffer);
139             }
140             data_->EmitU64(reinterpret_cast<uint64_t>(buffer), static_cast<size_t>(entry.first));
141         }
142     }
143     if (notSupport_) {
144         LOG_ECMA(ERROR) << "ValueSerialize: serialize data is incomplete";
145         data_->SetIncompleteData(true);
146         if (!chunkEmpty) {
147             // If notSupport, serializeRoot should be removed.
148             Runtime::GetInstance()->RemoveSerializationRoot(thread_, index);
149         }
150         return false;
151     }
152     if (!chunkEmpty) {
153         [[maybe_unused]] EcmaHandleScope scope(thread_);
154         JSMutableHandle<JSTaggedValue> strHandle(thread_, JSTaggedValue::Undefined());
155         SerializationChunk *chunk = Runtime::GetInstance()->GetSerializeRootMapValue(thread_, index);
156         size_t size = chunk->Size();
157         for (size_t i = 0; i < size; ++i) {
158             JSTaggedValue val(chunk->Get(i));
159             if (UNLIKELY(val.IsTreeString())) {
160                 strHandle.Update(val);
161                 JSHandle<JSTaggedValue> flattenStr(JSTaggedValue::PublishSharedValue(thread, strHandle));
162                 chunk->Set(i, flattenStr.GetTaggedType());
163             }
164         }
165         data_->SetDataIndex(index);
166     }
167     size_t maxSerializerSize = vm_->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
168     if (data_->Size() > maxSerializerSize) {
169         LOG_ECMA(ERROR) << "The serialization data size has exceed limit Size, current size is: " << data_->Size()
170                         << " max size is: " << maxSerializerSize;
171         return false;
172     }
173     return true;
174 }
175 
SerializeObjectImpl(TaggedObject * object,bool isWeak)176 void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak)
177 {
178     if (notSupport_) {
179         return;
180     }
181     bool cloneSharedObject = false;
182     if (!CheckObjectCanSerialize(object, cloneSharedObject)) {
183         notSupport_ = true;
184         return;
185     }
186     if (isWeak) {
187         data_->WriteEncodeFlag(EncodeFlag::WEAK);
188     }
189     if (SerializeReference(object) || SerializeRootObject(object)) {
190         return;
191     }
192     Region *region = Region::ObjectAddressToRange(object);
193     JSHClass *objClass = object->GetClass();
194     if (objClass->IsString() || objClass->IsMethod() || objClass->IsJSSharedFunction() ||
195         objClass->IsJSSharedAsyncFunction() || region->InSharedReadOnlySpace() ||
196         (serializeSharedEvent_ == 0 && region->InSharedHeap())) {
197         SerializeSharedObject(object);
198         return;
199     }
200     if (objClass->IsNativeBindingObject()) {
201         SerializeNativeBindingObject(object);
202         return;
203     }
204     if (objClass->IsJSError()) {
205         SerializeJSError(object);
206         return;
207     }
208     bool arrayBufferDeferDetach = false;
209     JSTaggedValue trackInfo;
210     JSTaggedType hashfield = JSTaggedValue::VALUE_ZERO;
211     JSType type = objClass->GetObjectType();
212     // serialize prologue
213     switch (type) {
214         case JSType::JS_ARRAY_BUFFER: {
215             supportJSNativePointer_ = true;
216             arrayBufferDeferDetach = SerializeJSArrayBufferPrologue(object);
217             break;
218         }
219         case JSType::JS_SHARED_ARRAY_BUFFER: {
220             supportJSNativePointer_ = true;
221             SerializeJSSharedArrayBufferPrologue(object);
222             break;
223         }
224         case JSType::JS_SENDABLE_ARRAY_BUFFER: {
225             supportJSNativePointer_ = true;
226             SerializeJSSendableArrayBufferPrologue(object);
227             break;
228         }
229         case JSType::JS_ARRAY: {
230             JSArray *array = reinterpret_cast<JSArray *>(object);
231             trackInfo = array->GetTrackInfo();
232             array->SetTrackInfo(thread_, JSTaggedValue::Undefined());
233             break;
234         }
235         case JSType::JS_REG_EXP: {
236             supportJSNativePointer_ = true;
237             SerializeJSRegExpPrologue(reinterpret_cast<JSRegExp *>(object));
238             break;
239         }
240         case JSType::JS_OBJECT: {
241             hashfield = Barriers::GetValue<JSTaggedType>(object, JSObject::HASH_OFFSET);
242             Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, JSTaggedValue::VALUE_ZERO);
243             break;
244         }
245         default:
246             break;
247     }
248 
249     // serialize object here
250     SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(object);
251 
252     // serialize epilogue
253     switch (type) {
254         case JSType::JS_ARRAY_BUFFER:
255         case JSType::JS_SHARED_ARRAY_BUFFER:
256         case JSType::JS_SENDABLE_ARRAY_BUFFER:
257         case JSType::JS_REG_EXP:
258             // JSNativePointer supports serialization only during serialize JSArrayBuffer,
259             // JSSharedArrayBuffer and JSRegExp
260             supportJSNativePointer_ = false;
261             break;
262         case JSType::JS_ARRAY: {
263             JSArray *array = reinterpret_cast<JSArray *>(object);
264             array->SetTrackInfo(thread_, trackInfo);
265             break;
266         }
267         case JSType::JS_OBJECT: {
268             if (JSTaggedValue(hashfield).IsHeapObject()) {
269                 Barriers::SetObject<true>(thread_, object, JSObject::HASH_OFFSET, hashfield);
270             } else {
271                 Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, hashfield);
272             }
273             break;
274         }
275         default:
276             break;
277     }
278     if (cloneSharedObject) {
279         serializeSharedEvent_--;
280     }
281     if (arrayBufferDeferDetach) {
282         ASSERT(objClass->IsArrayBuffer());
283         JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
284         arrayBuffer->Detach(thread_, arrayBuffer->GetWithNativeAreaAllocator(), true);
285     }
286 }
287 
SerializeJSError(TaggedObject * object)288 void ValueSerializer::SerializeJSError(TaggedObject *object)
289 {
290     [[maybe_unused]] EcmaHandleScope scope(thread_);
291     data_->WriteEncodeFlag(EncodeFlag::JS_ERROR);
292     JSType type = object->GetClass()->GetObjectType();
293     ASSERT(type >= JSType::JS_ERROR_FIRST && type <= JSType::JS_ERROR_LAST);
294     data_->WriteUint8(static_cast<uint8_t>(type));
295     auto globalConst = thread_->GlobalConstants();
296     JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
297     JSHandle<JSTaggedValue> msg =
298         JSObject::GetProperty(thread_, JSHandle<JSTaggedValue>(thread_, object), handleMsg).GetValue();
299     if (msg->IsString()) {
300         data_->WriteUint8(1); // 1: msg is string
301         // string must be shared
302         SerializeSharedObject(msg->GetTaggedObject());
303     } else {
304         data_->WriteUint8(0); // 0: msg is undefined
305     }
306 }
307 
SerializeNativeBindingObject(TaggedObject * object)308 void ValueSerializer::SerializeNativeBindingObject(TaggedObject *object)
309 {
310     [[maybe_unused]] EcmaHandleScope scope(thread_);
311     JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
312     JSHandle<JSTaggedValue> nativeBindingSymbol = env->GetNativeBindingSymbol();
313     JSHandle<JSTaggedValue> nativeBindingValue =
314         JSObject::GetProperty(thread_, JSHandle<JSObject>(thread_, object), nativeBindingSymbol).GetRawValue();
315     if (!nativeBindingValue->IsJSNativePointer()) {
316         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject nativeBindingValue is not JSNativePointer";
317         notSupport_ = true;
318         return;
319     }
320     auto info = reinterpret_cast<panda::JSNApi::NativeBindingInfo *>(
321         JSNativePointer::Cast(nativeBindingValue->GetTaggedObject())->GetExternalPointer());
322     if (info == nullptr) {
323         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject NativeBindingInfo is nullptr";
324         notSupport_ = true;
325         return;
326     }
327     void *hint = info->hint;
328     void *attachData = info->attachData;
329     AttachFunc attachNative = reinterpret_cast<AttachFunc>(info->attachFunc);
330     data_->WriteEncodeFlag(EncodeFlag::NATIVE_BINDING_OBJECT);
331     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachNative));
332     ssize_t offset = data_->EmitU64(0); // 0 is a placeholder which will be filled later
333     detachCallbackInfo_.push_back({offset, info});
334     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(hint));
335     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachData));
336 }
337 
SerializeJSArrayBufferPrologue(TaggedObject * object)338 bool ValueSerializer::SerializeJSArrayBufferPrologue(TaggedObject *object)
339 {
340     ASSERT(object->GetClass()->IsArrayBuffer());
341     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
342     if (arrayBuffer->IsDetach()) {
343         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached array buffer";
344         notSupport_ = true;
345         return false;
346     }
347     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
348     bool clone = cloneArrayBufferSet_.find(ToUintPtr(object)) != cloneArrayBufferSet_.end();
349     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
350     if (arrayLength > 0) {
351         if (transfer) {
352             if (clone) {
353                 notSupport_ = true;
354                 LOG_ECMA(ERROR) << "ValueSerialize: can't put arraybuffer in both transfer list and clone list";
355                 return false;
356             }
357             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
358             return true;
359         } else if (clone || !defaultTransfer_) {
360             bool nativeAreaAllocated = arrayBuffer->GetWithNativeAreaAllocator();
361             if (!nativeAreaAllocated) {
362                 LOG_ECMA(ERROR) << "ValueSerialize: don't support clone arraybuffer has external allocated buffer, \
363                     considering transfer it";
364                 notSupport_ = true;
365                 return false;
366             }
367             data_->WriteEncodeFlag(EncodeFlag::ARRAY_BUFFER);
368             data_->WriteUint32(arrayLength);
369             JSNativePointer *np =
370                 reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
371             data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
372             return false;
373         } else {
374             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
375             return true;
376         }
377     }
378     return false;
379 }
380 
SerializeJSSharedArrayBufferPrologue(TaggedObject * object)381 void ValueSerializer::SerializeJSSharedArrayBufferPrologue(TaggedObject *object)
382 {
383     ASSERT(object->GetClass()->IsSharedArrayBuffer());
384     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
385     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
386     if (arrayBuffer->IsDetach() || transfer) {
387         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached or transfer shared array buffer";
388         notSupport_ = true;
389         return;
390     }
391     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
392     if (arrayLength > 0) {
393         JSNativePointer *np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
394         void *buffer = np->GetExternalPointer();
395         if (JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength)) {
396             LOG_ECMA(ERROR) << "ValueSerialize: can't find buffer form shared memory pool";
397             notSupport_ = true;
398             return;
399         }
400         data_->WriteEncodeFlag(EncodeFlag::SHARED_ARRAY_BUFFER);
401         data_->insertSharedArrayBuffer(reinterpret_cast<uintptr_t>(buffer));
402     }
403 }
404 
SerializeJSSendableArrayBufferPrologue(TaggedObject * object)405 void ValueSerializer::SerializeJSSendableArrayBufferPrologue(TaggedObject *object)
406 {
407     ASSERT(object->GetClass()->IsSendableArrayBuffer());
408     JSSendableArrayBuffer *arrayBuffer = reinterpret_cast<JSSendableArrayBuffer *>(object);
409     if (arrayBuffer->IsDetach()) {
410         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached sendable array buffer";
411         notSupport_ = true;
412         return;
413     }
414     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
415     if (arrayLength > 0) {
416         bool nativeAreaAllocated = arrayBuffer->GetWithNativeAreaAllocator();
417         if (!nativeAreaAllocated) {
418             LOG_ECMA(ERROR) << "ValueSerialize: don't support clone sendablearraybuffer has external allocated buffer";
419             notSupport_ = true;
420             return;
421         }
422         data_->WriteEncodeFlag(EncodeFlag::SENDABLE_ARRAY_BUFFER);
423         data_->WriteUint32(arrayLength);
424         JSNativePointer *np =
425             reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
426         data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
427     }
428 }
429 
SerializeJSRegExpPrologue(JSRegExp * jsRegExp)430 void ValueSerializer::SerializeJSRegExpPrologue(JSRegExp *jsRegExp)
431 {
432     uint32_t bufferSize = jsRegExp->GetLength();
433     if (bufferSize == 0) {
434         LOG_ECMA(ERROR) << "ValueSerialize: JSRegExp buffer size is 0";
435         notSupport_ = true;
436         return;
437     }
438 
439     data_->WriteEncodeFlag(EncodeFlag::JS_REG_EXP);
440     data_->WriteUint32(bufferSize);
441     JSNativePointer *np =
442         reinterpret_cast<JSNativePointer *>(jsRegExp->GetByteCodeBuffer().GetTaggedObject());
443     data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), bufferSize);
444 }
445 
PrepareTransfer(JSThread * thread,const JSHandle<JSTaggedValue> & transfer)446 bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
447 {
448     if (transfer->IsUndefined()) {
449         return true;
450     }
451     if (!transfer->IsJSArray()) {
452         return false;
453     }
454     int len = base::ArrayHelper::GetArrayLength(thread, transfer);
455     int k = 0;
456     while (k < len) {
457         bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
458         if (exists) {
459             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
460             if (!element->IsArrayBuffer()) {
461                 return false;
462             }
463             transferDataSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
464         }
465         k++;
466     }
467     return true;
468 }
469 
PrepareClone(JSThread * thread,const JSHandle<JSTaggedValue> & cloneList)470 bool ValueSerializer::PrepareClone(JSThread *thread, const JSHandle<JSTaggedValue> &cloneList)
471 {
472     if (cloneList->IsUndefined()) {
473         return true;
474     }
475     if (!cloneList->IsJSArray()) {
476         return false;
477     }
478     int len = base::ArrayHelper::GetArrayLength(thread, cloneList);
479     int index = 0;
480     while (index < len) {
481         bool exists = JSTaggedValue::HasProperty(thread, cloneList, index);
482         if (exists) {
483             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, cloneList, index);
484             if (element->IsArrayBuffer()) {
485                 cloneArrayBufferSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
486             } else if (element->IsJSShared()) {
487                 cloneSharedSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
488             } else {
489                 return false;
490             }
491         }
492         index++;
493     }
494     return true;
495 }
496 }  // namespace panda::ecmascript
497 
498