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