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