• 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             data_->EmitU64(reinterpret_cast<uint64_t>(buffer), static_cast<size_t>(entry.first));
138         }
139     }
140     if (notSupport_) {
141         LOG_ECMA(ERROR) << "ValueSerialize: serialize data is incomplete";
142         data_->SetIncompleteData(true);
143         if (!chunkEmpty) {
144             // If notSupport, serializeRoot should be removed.
145             Runtime::GetInstance()->RemoveSerializationRoot(thread_, index);
146         }
147         return false;
148     }
149     if (!chunkEmpty) {
150         data_->SetDataIndex(index);
151     }
152     size_t maxSerializerSize = vm_->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
153     if (data_->Size() > maxSerializerSize) {
154         LOG_ECMA(ERROR) << "The serialization data size has exceed limit Size, current size is: " << data_->Size()
155                         << " max size is: " << maxSerializerSize;
156         return false;
157     }
158     return true;
159 }
160 
SerializeObjectImpl(TaggedObject * object,bool isWeak)161 void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak)
162 {
163     if (notSupport_) {
164         return;
165     }
166     bool cloneSharedObject = false;
167     if (!CheckObjectCanSerialize(object, cloneSharedObject)) {
168         notSupport_ = true;
169         return;
170     }
171     if (isWeak) {
172         data_->WriteEncodeFlag(EncodeFlag::WEAK);
173     }
174     if (SerializeReference(object) || SerializeRootObject(object)) {
175         return;
176     }
177     Region *region = Region::ObjectAddressToRange(object);
178     if (object->GetClass()->IsString() || object->GetClass()->IsMethod() || region->InSharedReadOnlySpace() ||
179         (serializeSharedEvent_ == 0 && region->InSharedHeap())) {
180         SerializeSharedObject(object);
181         return;
182     }
183     if (object->GetClass()->IsNativeBindingObject()) {
184         SerializeNativeBindingObject(object);
185         return;
186     }
187     if (object->GetClass()->IsJSError()) {
188         SerializeJSError(object);
189         return;
190     }
191     bool arrayBufferDeferDetach = false;
192     JSTaggedValue trackInfo;
193     JSTaggedType hashfield = JSTaggedValue::VALUE_ZERO;
194     JSType type = object->GetClass()->GetObjectType();
195     // serialize prologue
196     switch (type) {
197         case JSType::JS_ARRAY_BUFFER: {
198             supportJSNativePointer_ = true;
199             arrayBufferDeferDetach = SerializeJSArrayBufferPrologue(object);
200             break;
201         }
202         case JSType::JS_SHARED_ARRAY_BUFFER: {
203             supportJSNativePointer_ = true;
204             SerializeJSSharedArrayBufferPrologue(object);
205             break;
206         }
207         case JSType::JS_SENDABLE_ARRAY_BUFFER: {
208             supportJSNativePointer_ = true;
209             SerializeJSSendableArrayBufferPrologue(object);
210             break;
211         }
212         case JSType::JS_ARRAY: {
213             JSArray *array = reinterpret_cast<JSArray *>(object);
214             trackInfo = array->GetTrackInfo();
215             array->SetTrackInfo(thread_, JSTaggedValue::Undefined());
216             break;
217         }
218         case JSType::JS_REG_EXP: {
219             supportJSNativePointer_ = true;
220             SerializeJSRegExpPrologue(reinterpret_cast<JSRegExp *>(object));
221             break;
222         }
223         case JSType::JS_OBJECT: {
224             hashfield = Barriers::GetValue<JSTaggedType>(object, JSObject::HASH_OFFSET);
225             Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, JSTaggedValue::VALUE_ZERO);
226             break;
227         }
228         default:
229             break;
230     }
231 
232     // serialize object here
233     SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(object);
234 
235     // serialize epilogue
236     switch (type) {
237         case JSType::JS_ARRAY_BUFFER:
238         case JSType::JS_SHARED_ARRAY_BUFFER:
239         case JSType::JS_SENDABLE_ARRAY_BUFFER:
240         case JSType::JS_REG_EXP:
241             // JSNativePointer supports serialization only during serialize JSArrayBuffer,
242             // JSSharedArrayBuffer and JSRegExp
243             supportJSNativePointer_ = false;
244             break;
245         case JSType::JS_ARRAY: {
246             JSArray *array = reinterpret_cast<JSArray *>(object);
247             array->SetTrackInfo(thread_, trackInfo);
248             break;
249         }
250         case JSType::JS_OBJECT: {
251             if (JSTaggedValue(hashfield).IsHeapObject()) {
252                 Barriers::SetObject<true>(thread_, object, JSObject::HASH_OFFSET, hashfield);
253             } else {
254                 Barriers::SetPrimitive<JSTaggedType>(object, JSObject::HASH_OFFSET, hashfield);
255             }
256             break;
257         }
258         default:
259             break;
260     }
261     if (cloneSharedObject) {
262         serializeSharedEvent_--;
263     }
264     if (arrayBufferDeferDetach) {
265         ASSERT(object->GetClass()->IsArrayBuffer());
266         JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
267         arrayBuffer->Detach(thread_, arrayBuffer->GetWithNativeAreaAllocator(), true);
268     }
269 }
270 
SerializeJSError(TaggedObject * object)271 void ValueSerializer::SerializeJSError(TaggedObject *object)
272 {
273     [[maybe_unused]] EcmaHandleScope scope(thread_);
274     data_->WriteEncodeFlag(EncodeFlag::JS_ERROR);
275     JSType type = object->GetClass()->GetObjectType();
276     ASSERT(type >= JSType::JS_ERROR_FIRST && type <= JSType::JS_ERROR_LAST);
277     data_->WriteUint8(static_cast<uint8_t>(type));
278     auto globalConst = thread_->GlobalConstants();
279     JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
280     JSHandle<JSTaggedValue> msg =
281         JSObject::GetProperty(thread_, JSHandle<JSTaggedValue>(thread_, object), handleMsg).GetValue();
282     if (msg->IsString()) {
283         data_->WriteUint8(1); // 1: msg is string
284         // string must be shared
285         SerializeSharedObject(msg->GetTaggedObject());
286     } else {
287         data_->WriteUint8(0); // 0: msg is undefined
288     }
289 }
290 
SerializeNativeBindingObject(TaggedObject * object)291 void ValueSerializer::SerializeNativeBindingObject(TaggedObject *object)
292 {
293     [[maybe_unused]] EcmaHandleScope scope(thread_);
294     JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
295     JSHandle<JSTaggedValue> nativeBindingSymbol = env->GetNativeBindingSymbol();
296     JSHandle<JSTaggedValue> nativeBindingValue =
297         JSObject::GetProperty(thread_, JSHandle<JSObject>(thread_, object), nativeBindingSymbol).GetRawValue();
298     if (!nativeBindingValue->IsJSNativePointer()) {
299         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject nativeBindingValue is not JSNativePointer";
300         notSupport_ = true;
301         return;
302     }
303     auto info = reinterpret_cast<panda::JSNApi::NativeBindingInfo *>(
304         JSNativePointer::Cast(nativeBindingValue->GetTaggedObject())->GetExternalPointer());
305     if (info == nullptr) {
306         LOG_ECMA(ERROR) << "ValueSerialize: SerializeNativeBindingObject NativeBindingInfo is nullptr";
307         notSupport_ = true;
308         return;
309     }
310     void *hint = info->hint;
311     void *attachData = info->attachData;
312     AttachFunc attachNative = reinterpret_cast<AttachFunc>(info->attachFunc);
313     data_->WriteEncodeFlag(EncodeFlag::NATIVE_BINDING_OBJECT);
314     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachNative));
315     ssize_t offset = data_->EmitU64(0); // 0 is a placeholder which will be filled later
316     detachCallbackInfo_.push_back({offset, info});
317     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(hint));
318     data_->WriteJSTaggedType(reinterpret_cast<JSTaggedType>(attachData));
319 }
320 
SerializeJSArrayBufferPrologue(TaggedObject * object)321 bool ValueSerializer::SerializeJSArrayBufferPrologue(TaggedObject *object)
322 {
323     ASSERT(object->GetClass()->IsArrayBuffer());
324     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
325     if (arrayBuffer->IsDetach()) {
326         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached array buffer";
327         notSupport_ = true;
328         return false;
329     }
330     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
331     bool clone = cloneArrayBufferSet_.find(ToUintPtr(object)) != cloneArrayBufferSet_.end();
332     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
333     if (arrayLength > 0) {
334         if (transfer) {
335             if (clone) {
336                 notSupport_ = true;
337                 LOG_ECMA(ERROR) << "ValueSerialize: can't put arraybuffer in both transfer list and clone list";
338                 return false;
339             }
340             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
341             return true;
342         } else if (clone || !defaultTransfer_) {
343             bool nativeAreaAllocated = arrayBuffer->GetWithNativeAreaAllocator();
344             if (!nativeAreaAllocated) {
345                 LOG_ECMA(ERROR) << "ValueSerialize: don't support clone arraybuffer has external allocated buffer, \
346                     considering transfer it";
347                 notSupport_ = true;
348                 return false;
349             }
350             data_->WriteEncodeFlag(EncodeFlag::ARRAY_BUFFER);
351             data_->WriteUint32(arrayLength);
352             JSNativePointer *np =
353                 reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
354             data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
355             return false;
356         } else {
357             data_->WriteEncodeFlag(EncodeFlag::TRANSFER_ARRAY_BUFFER);
358             return true;
359         }
360     }
361     return false;
362 }
363 
SerializeJSSharedArrayBufferPrologue(TaggedObject * object)364 void ValueSerializer::SerializeJSSharedArrayBufferPrologue(TaggedObject *object)
365 {
366     ASSERT(object->GetClass()->IsSharedArrayBuffer());
367     JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(object);
368     bool transfer = transferDataSet_.find(ToUintPtr(object)) != transferDataSet_.end();
369     if (arrayBuffer->IsDetach() || transfer) {
370         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached or transfer shared array buffer";
371         notSupport_ = true;
372         return;
373     }
374     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
375     if (arrayLength > 0) {
376         JSNativePointer *np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
377         void *buffer = np->GetExternalPointer();
378         if (JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength)) {
379             LOG_ECMA(ERROR) << "ValueSerialize: can't find buffer form shared memory pool";
380             notSupport_ = true;
381             return;
382         }
383         data_->WriteEncodeFlag(EncodeFlag::SHARED_ARRAY_BUFFER);
384         data_->insertSharedArrayBuffer(reinterpret_cast<uintptr_t>(buffer));
385     }
386 }
387 
SerializeJSSendableArrayBufferPrologue(TaggedObject * object)388 void ValueSerializer::SerializeJSSendableArrayBufferPrologue(TaggedObject *object)
389 {
390     ASSERT(object->GetClass()->IsSendableArrayBuffer());
391     JSSendableArrayBuffer *arrayBuffer = reinterpret_cast<JSSendableArrayBuffer *>(object);
392     if (arrayBuffer->IsDetach()) {
393         LOG_ECMA(ERROR) << "ValueSerialize: don't support serialize detached sendable array buffer";
394         notSupport_ = true;
395         return;
396     }
397     size_t arrayLength = arrayBuffer->GetArrayBufferByteLength();
398     if (arrayLength > 0) {
399         bool nativeAreaAllocated = arrayBuffer->GetWithNativeAreaAllocator();
400         if (!nativeAreaAllocated) {
401             LOG_ECMA(ERROR) << "ValueSerialize: don't support clone sendablearraybuffer has external allocated buffer";
402             notSupport_ = true;
403             return;
404         }
405         data_->WriteEncodeFlag(EncodeFlag::SENDABLE_ARRAY_BUFFER);
406         data_->WriteUint32(arrayLength);
407         JSNativePointer *np =
408             reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
409         data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), arrayLength);
410     }
411 }
412 
SerializeJSRegExpPrologue(JSRegExp * jsRegExp)413 void ValueSerializer::SerializeJSRegExpPrologue(JSRegExp *jsRegExp)
414 {
415     uint32_t bufferSize = jsRegExp->GetLength();
416     if (bufferSize == 0) {
417         LOG_ECMA(ERROR) << "ValueSerialize: JSRegExp buffer size is 0";
418         notSupport_ = true;
419         return;
420     }
421 
422     data_->WriteEncodeFlag(EncodeFlag::JS_REG_EXP);
423     data_->WriteUint32(bufferSize);
424     JSNativePointer *np =
425         reinterpret_cast<JSNativePointer *>(jsRegExp->GetByteCodeBuffer().GetTaggedObject());
426     data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), bufferSize);
427 }
428 
PrepareTransfer(JSThread * thread,const JSHandle<JSTaggedValue> & transfer)429 bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
430 {
431     if (transfer->IsUndefined()) {
432         return true;
433     }
434     if (!transfer->IsJSArray()) {
435         return false;
436     }
437     int len = base::ArrayHelper::GetArrayLength(thread, transfer);
438     int k = 0;
439     while (k < len) {
440         bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
441         if (exists) {
442             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
443             if (!element->IsArrayBuffer()) {
444                 return false;
445             }
446             transferDataSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
447         }
448         k++;
449     }
450     return true;
451 }
452 
PrepareClone(JSThread * thread,const JSHandle<JSTaggedValue> & cloneList)453 bool ValueSerializer::PrepareClone(JSThread *thread, const JSHandle<JSTaggedValue> &cloneList)
454 {
455     if (cloneList->IsUndefined()) {
456         return true;
457     }
458     if (!cloneList->IsJSArray()) {
459         return false;
460     }
461     int len = base::ArrayHelper::GetArrayLength(thread, cloneList);
462     int index = 0;
463     while (index < len) {
464         bool exists = JSTaggedValue::HasProperty(thread, cloneList, index);
465         if (exists) {
466             JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, cloneList, index);
467             if (element->IsArrayBuffer()) {
468                 cloneArrayBufferSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
469             } else if (element->IsJSShared()) {
470                 cloneSharedSet_.insert(static_cast<uintptr_t>(element.GetTaggedType()));
471             } else {
472                 return false;
473             }
474         }
475         index++;
476     }
477     return true;
478 }
479 }  // namespace panda::ecmascript
480 
481