/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/js_serializer.h" #if defined(PANDA_TARGET_IOS) #include #elif defined(PANDA_TARGET_MACOS) #include #else #include #endif #include "ecmascript/base/array_helper.h" #include "ecmascript/base/typed_array_helper-inl.h" #include "ecmascript/base/typed_array_helper.h" #include "ecmascript/ecma_string-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_regexp.h" #include "ecmascript/js_set.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/linked_hash_table.h" #include "ecmascript/object_factory-inl.h" #include "ecmascript/shared_mm/shared_mm.h" #include "securec.h" namespace panda::ecmascript { using TypedArrayHelper = base::TypedArrayHelper; constexpr size_t INITIAL_CAPACITY = 64; constexpr int CAPACITY_INCREASE_RATE = 2; bool JSSerializer::WriteType(SerializationUID id) { uint8_t rawId = static_cast(id); return WriteRawData(&rawId, sizeof(rawId)); } void JSSerializer::InitTransferSet(CUnorderedSet transferDataSet) { transferDataSet_ = std::move(transferDataSet); } void JSSerializer::ClearTransferSet() { transferDataSet_.clear(); } // Write JSTaggedValue could be either a pointer to a HeapObject or a value bool JSSerializer::SerializeJSTaggedValue(const JSHandle &value) { [[maybe_unused]] EcmaHandleScope scope(thread_); DISALLOW_GARBAGE_COLLECTION; if (!value->IsHeapObject()) { if (!WritePrimitiveValue(value)) { return false; } } else { if (!WriteTaggedObject(value)) { return false; } } return true; } // Write JSTaggedValue that is pure value bool JSSerializer::WritePrimitiveValue(const JSHandle &value) { if (value->IsNull()) { return WriteType(SerializationUID::JS_NULL); } if (value->IsUndefined()) { return WriteType(SerializationUID::JS_UNDEFINED); } if (value->IsTrue()) { return WriteType(SerializationUID::JS_TRUE); } if (value->IsFalse()) { return WriteType(SerializationUID::JS_FALSE); } if (value->IsInt()) { return WriteInt(value->GetInt()); } if (value->IsDouble()) { return WriteDouble(value->GetDouble()); } if (value->IsHole()) { return WriteType(SerializationUID::HOLE); } return false; } bool JSSerializer::WriteInt(int32_t value) { if (!WriteType(SerializationUID::INT32)) { return false; } if (!WriteRawData(&value, sizeof(value))) { return false; } return true; } bool JSSerializer::WriteDouble(double value) { if (!WriteType(SerializationUID::DOUBLE)) { return false; } if (!WriteRawData(&value, sizeof(value))) { return false; } return true; } bool JSSerializer::WriteBoolean(bool value) { if (value) { return WriteType(SerializationUID::C_TRUE); } return WriteType(SerializationUID::C_FALSE); } bool JSSerializer::WriteRawData(const void *data, size_t length) { if (length <= 0) { return false; } if ((bufferSize_ + length) > bufferCapacity_) { if (!AllocateBuffer(length)) { return false; } } if (memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, data, length) != EOK) { LOG_FULL(ERROR) << "Failed to memcpy_s Data"; return false; } bufferSize_ += length; return true; } bool JSSerializer::WriteString(const CString &str) { if (!WriteType(SerializationUID::C_STRING)) { return false; } size_t length = str.length() + 1; // 1: '\0' if ((bufferSize_ + length) > bufferCapacity_) { if (!AllocateBuffer(length)) { return false; } } if (memcpy_s(buffer_ + bufferSize_, bufferCapacity_ - bufferSize_, str.c_str(), length) != EOK) { LOG_FULL(ERROR) << "Failed to memcpy_s Data"; return false; } bufferSize_ += length; return true; } bool JSSerializer::AllocateBuffer(size_t bytes) { // Get internal heap size if (sizeLimit_ == 0) { uint64_t heapSize = thread_->GetEcmaVM()->GetJSOptions().GetSerializerBufferSizeLimit(); sizeLimit_ = heapSize; } size_t oldSize = bufferSize_; size_t newSize = oldSize + bytes; if (newSize > sizeLimit_) { return false; } if (bufferCapacity_ == 0) { if (bytes < INITIAL_CAPACITY) { buffer_ = reinterpret_cast(malloc(INITIAL_CAPACITY)); if (buffer_ != nullptr) { bufferCapacity_ = INITIAL_CAPACITY; return true; } else { return false; } } else { buffer_ = reinterpret_cast(malloc(bytes)); if (buffer_ != nullptr) { bufferCapacity_ = bytes; return true; } else { return false; } } } if (newSize > bufferCapacity_) { if (!ExpandBuffer(newSize)) { return false; } } return true; } bool JSSerializer::ExpandBuffer(size_t requestedSize) { size_t newCapacity = bufferCapacity_ * CAPACITY_INCREASE_RATE; newCapacity = std::max(newCapacity, requestedSize); if (newCapacity > sizeLimit_) { return false; } uint8_t *newBuffer = reinterpret_cast(malloc(newCapacity)); if (newBuffer == nullptr) { return false; } if (memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_) != EOK) { LOG_FULL(ERROR) << "Failed to memcpy_s Data"; free(newBuffer); return false; } free(buffer_); buffer_ = newBuffer; bufferCapacity_ = newCapacity; return true; } // Transfer ownership of buffer, should not use this Serializer after release std::pair JSSerializer::ReleaseBuffer() { auto res = std::make_pair(buffer_, bufferSize_); buffer_ = nullptr; bufferSize_ = 0; bufferCapacity_ = 0; objectId_ = 0; return res; } bool JSSerializer::IsSerialized(uintptr_t addr) const { if (referenceMap_.find(addr) != referenceMap_.end()) { return true; } return false; } bool JSSerializer::WriteIfSerialized(uintptr_t addr) { auto iter = referenceMap_.find(addr); if (iter == referenceMap_.end()) { return false; } uint64_t id = iter->second; if (!WriteType(SerializationUID::TAGGED_OBJECT_REFERNCE)) { return false; } if (!WriteRawData(&id, sizeof(uint64_t))) { return false; } return true; } // Write HeapObject bool JSSerializer::WriteTaggedObject(const JSHandle &value) { STACK_LIMIT_CHECK(thread_, false); uintptr_t addr = reinterpret_cast(value.GetTaggedValue().GetTaggedObject()); bool serialized = IsSerialized(addr); if (serialized) { return WriteIfSerialized(addr); } referenceMap_.emplace(addr, objectId_); objectId_++; TaggedObject *taggedObject = value->GetTaggedObject(); JSType type = taggedObject->GetClass()->GetObjectType(); switch (type) { case JSType::JS_ERROR: case JSType::JS_EVAL_ERROR: case JSType::JS_RANGE_ERROR: case JSType::JS_REFERENCE_ERROR: case JSType::JS_TYPE_ERROR: case JSType::JS_AGGREGATE_ERROR: case JSType::JS_URI_ERROR: case JSType::JS_SYNTAX_ERROR: case JSType::JS_OOM_ERROR: case JSType::JS_TERMINATION_ERROR: return WriteJSError(value); case JSType::JS_DATE: return WriteJSDate(value); case JSType::JS_ARRAY: return WriteJSArray(value); case JSType::JS_MAP: return WriteJSMap(value); case JSType::JS_SET: return WriteJSSet(value); case JSType::JS_REG_EXP: return WriteJSRegExp(value); case JSType::JS_INT8_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_INT8_ARRAY); case JSType::JS_UINT8_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_UINT8_ARRAY); case JSType::JS_UINT8_CLAMPED_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_UINT8_CLAMPED_ARRAY); case JSType::JS_INT16_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_INT16_ARRAY); case JSType::JS_UINT16_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_UINT16_ARRAY); case JSType::JS_INT32_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_INT32_ARRAY); case JSType::JS_UINT32_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_UINT32_ARRAY); case JSType::JS_FLOAT32_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_FLOAT32_ARRAY); case JSType::JS_FLOAT64_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_FLOAT64_ARRAY); case JSType::JS_BIGINT64_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_BIGINT64_ARRAY); case JSType::JS_BIGUINT64_ARRAY: return WriteJSTypedArray(value, SerializationUID::JS_BIGUINT64_ARRAY); case JSType::JS_ARRAY_BUFFER: case JSType::JS_SHARED_ARRAY_BUFFER: return WriteJSArrayBuffer(value); case JSType::LINE_STRING: case JSType::CONSTANT_STRING: case JSType::TREE_STRING: case JSType::SLICED_STRING: return WriteEcmaString(value); case JSType::JS_OBJECT: return WritePlainObject(value); case JSType::JS_ASYNC_FUNCTION: // means CONCURRENT_FUNCTION return WriteJSFunction(value); case JSType::METHOD: return WriteMethod(value); case JSType::TAGGED_ARRAY: return WriteTaggedArray(value); case JSType::BIGINT: return WriteBigInt(value); default: break; } std::string typeStr = ConvertToStdString(JSHClass::DumpJSType(type)); LOG_ECMA(ERROR) << "serialized unsupported type which JSType is: " << typeStr; return false; } bool JSSerializer::WriteBigInt(const JSHandle &value) { JSHandle bigInt = JSHandle::Cast(value); if (!WriteType(SerializationUID::BIGINT)) { return false; } uint32_t len = bigInt->GetLength(); if (!WriteInt(len)) { return false; } bool sign = bigInt->GetSign(); if (!WriteBoolean(sign)) { return false; } for (uint32_t i = 0; i < len; i++) { uint32_t val = bigInt->GetDigit(i); if (!WriteInt(val)) { return false; } } return true; } bool JSSerializer::WriteTaggedArray(const JSHandle &value) { JSHandle taggedArray = JSHandle::Cast(value); if (!WriteType(SerializationUID::TAGGED_ARRAY)) { return false; } uint32_t len = taggedArray->GetLength(); if (!WriteInt(len)) { return false; } JSMutableHandle val(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < len; i++) { val.Update(taggedArray->Get(i)); if (!SerializeJSTaggedValue(val)) { return false; } } return true; } bool JSSerializer::WriteByteArray(const JSHandle &value, DataViewType viewType) { JSHandle byteArray = JSHandle::Cast(value); if (!WriteType(SerializationUID::BYTE_ARRAY)) { return false; } uint32_t arrayLength = byteArray->GetArrayLength(); if (!WriteInt(arrayLength)) { return false; } uint32_t viewTypeIndex = GetDataViewTypeIndex(viewType); if (!WriteInt(viewTypeIndex)) { return false; } JSMutableHandle val(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < arrayLength; i++) { val.Update(byteArray->Get(thread_, i, viewType)); if (!SerializeJSTaggedValue(val)) { return false; } } return true; } uint32_t JSSerializer::GetDataViewTypeIndex(const DataViewType viewType) { uint32_t index = 0; switch (viewType) { case DataViewType::INT8: index = 1; // 1 : DataViewType::INT8 break; case DataViewType::UINT8: index = 2; // 2 : DataViewType::UINT8 break; case DataViewType::UINT8_CLAMPED: index = 3; // 3 : DataViewType::UINT8_CLAMPED break; case DataViewType::INT16: index = 4; // 4 : DataViewType::INT16 break; case DataViewType::UINT16: index = 5; // 5 : DataViewType::UINT16 break; case DataViewType::INT32: index = 6; // 6 : DataViewType::INT32 break; case DataViewType::UINT32: index = 7; // 7 : DataViewType::UINT32 break; case DataViewType::FLOAT32: index = 8; // 8 : DataViewType::FLOAT32 break; case DataViewType::FLOAT64: index = 9; // 9 : DataViewType::FLOAT64 break; case DataViewType::BIGINT64: index = 10; // 10 : DataViewType::BIGINT64 break; case DataViewType::BIGUINT64: index = 11; // 11 : DataViewType::BIGUINT64 break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } return index; } bool JSSerializer::WriteMethod(const JSHandle &value) { JSHandle method = JSHandle::Cast(value); if (method->IsNativeWithCallField()) { if (!WriteType(SerializationUID::NATIVE_METHOD)) { return false; } const void *nativeFunc = method->GetNativePointer(); if (!WriteRawData(&nativeFunc, sizeof(uintptr_t))) { return false; } } else { if (!WriteType(SerializationUID::METHOD)) { return false; } const MethodLiteral *methodLiteral = method->GetMethodLiteral(); if (!WriteRawData(&methodLiteral, sizeof(uintptr_t))) { return false; } JSHandle constPool(thread_, method->GetConstantPool()); const JSPandaFile *jsPandaFile = constPool->GetJSPandaFile(); if (jsPandaFile == nullptr) { return false; } const CString &desc = jsPandaFile->GetJSPandaFileDesc(); if (!WriteString(desc)) { return false; } if (method->CanSerializeCodeEntry()) { uintptr_t codeEntry = method->GetCodeEntryOrLiteral(); if (!WriteRawData(&codeEntry, sizeof(uintptr_t))) { return false; } } } return true; } bool JSSerializer::WriteJSFunction(const JSHandle &value) { if (!WriteType(SerializationUID::CONCURRENT_FUNCTION)) { return false; } JSHandle func = JSHandle::Cast(value); // check concurrent function if (func->GetFunctionKind() != ecmascript::FunctionKind::CONCURRENT_FUNCTION) { LOG_ECMA(ERROR) << "only support serialize concurrent function"; return false; } JSHandle method(thread_, func->GetMethod()); Method *methodPtr = Method::Cast(method.GetTaggedValue()); if (methodPtr->CanSerializeCodeEntry()) { uintptr_t codeEntry = func->GetCodeEntry(); if (!WriteRawData(&codeEntry, sizeof(uintptr_t))) { return false; } } if (!SerializeJSTaggedValue(method)) { return false; } return true; } bool JSSerializer::WriteJSError(const JSHandle &value) { TaggedObject *taggedObject = value->GetTaggedObject(); JSType errorType = taggedObject->GetClass()->GetObjectType(); if (!WriteJSErrorHeader(errorType)) { return false; } auto globalConst = thread_->GlobalConstants(); JSHandle handleMsg = globalConst->GetHandledMessageString(); JSHandle msg = JSObject::GetProperty(thread_, value, handleMsg).GetValue(); // Write error message if (!SerializeJSTaggedValue(msg)) { return false; } return true; } bool JSSerializer::WriteJSErrorHeader(JSType type) { switch (type) { case JSType::JS_ERROR: return WriteType(SerializationUID::JS_ERROR); case JSType::JS_EVAL_ERROR: return WriteType(SerializationUID::EVAL_ERROR); case JSType::JS_RANGE_ERROR: return WriteType(SerializationUID::RANGE_ERROR); case JSType::JS_REFERENCE_ERROR: return WriteType(SerializationUID::REFERENCE_ERROR); case JSType::JS_TYPE_ERROR: return WriteType(SerializationUID::TYPE_ERROR); case JSType::JS_AGGREGATE_ERROR: return WriteType(SerializationUID::AGGREGATE_ERROR); case JSType::JS_URI_ERROR: return WriteType(SerializationUID::URI_ERROR); case JSType::JS_SYNTAX_ERROR: return WriteType(SerializationUID::SYNTAX_ERROR); case JSType::JS_OOM_ERROR: return WriteType(SerializationUID::OOM_ERROR); case JSType::JS_TERMINATION_ERROR: return WriteType(SerializationUID::TERMINATION_ERROR); default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } return false; } bool JSSerializer::WriteJSDate(const JSHandle &value) { JSHandle date = JSHandle::Cast(value); if (!WriteType(SerializationUID::JS_DATE)) { return false; } if (!WritePlainObject(value)) { return false; } double timeValue = date->GetTimeValue().GetDouble(); if (!WriteDouble(timeValue)) { return false; } double localOffset = date->GetLocalOffset().GetDouble(); if (!WriteDouble(localOffset)) { return false; } return true; } bool JSSerializer::WriteJSArray(const JSHandle &value) { JSHandle array = JSHandle::Cast(value); if (!WriteType(SerializationUID::JS_ARRAY)) { return false; } if (!WritePlainObject(value)) { return false; } uint32_t arrayLength = array->GetLength(); if (!WriteInt(arrayLength)) { return false; } return true; } bool JSSerializer::WriteEcmaString(const JSHandle &value) { JSHandle strHandle = JSHandle::Cast(value); auto string = EcmaStringAccessor::FlattenAllString(thread_->GetEcmaVM(), strHandle); if (!WriteType(SerializationUID::ECMASTRING)) { return false; } size_t length = string.GetLength(); if (!WriteInt(static_cast(length))) { return false; } // skip writeRawData for empty EcmaString if (length == 0) { return true; } bool isUtf8 = string.IsUtf8(); // write utf encode flag if (!WriteBoolean(isUtf8)) { return false; } if (isUtf8) { const uint8_t *data = string.GetDataUtf8(); const uint8_t strEnd = '\0'; if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) { return false; } } else { const uint16_t *data = string.GetDataUtf16(); if (!WriteRawData(data, length * sizeof(uint16_t))) { return false; } } return true; } bool JSSerializer::WriteJSMap(const JSHandle &value) { JSHandle map = JSHandle::Cast(value); if (!WriteType(SerializationUID::JS_MAP)) { return false; } if (!WritePlainObject(value)) { return false; } uint32_t size = map->GetSize(); if (!WriteInt(static_cast(size))) { return false; } JSMutableHandle key(thread_, JSTaggedValue::Undefined()); JSMutableHandle val(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < size; i++) { key.Update(map->GetKey(i)); if (!SerializeJSTaggedValue(key)) { return false; } val.Update(map->GetValue(i)); if (!SerializeJSTaggedValue(val)) { return false; } } return true; } bool JSSerializer::WriteJSSet(const JSHandle &value) { JSHandle set = JSHandle::Cast(value); if (!WriteType(SerializationUID::JS_SET)) { return false; } if (!WritePlainObject(value)) { return false; } uint32_t size = set->GetSize(); if (!WriteInt(static_cast(size))) { return false; } JSMutableHandle val(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < size; i++) { val.Update(set->GetValue(i)); if (!SerializeJSTaggedValue(val)) { return false; } } return true; } bool JSSerializer::WriteJSRegExp(const JSHandle &value) { JSHandle regExp = JSHandle::Cast(value); if (!WriteType(SerializationUID::JS_REG_EXP)) { return false; } if (!WritePlainObject(value)) { return false; } uint32_t bufferSize = regExp->GetLength(); if (!WriteInt(static_cast(bufferSize))) { return false; } // Write Accessor(ByteCodeBuffer) which is a pointer to a dynamic buffer JSHandle bufferValue(thread_, regExp->GetByteCodeBuffer()); JSHandle np = JSHandle::Cast(bufferValue); void *dynBuffer = np->GetExternalPointer(); if (!WriteRawData(dynBuffer, bufferSize)) { return false; } // Write Accessor(OriginalSource) JSHandle originalSource(thread_, regExp->GetOriginalSource()); if (!SerializeJSTaggedValue(originalSource)) { return false; } // Write Accessor(OriginalFlags) JSHandle originalFlags(thread_, regExp->GetOriginalFlags()); if (!SerializeJSTaggedValue(originalFlags)) { return false; } return true; } bool JSSerializer::WriteJSTypedArray(const JSHandle &value, SerializationUID uId) { JSHandle typedArray = JSHandle::Cast(value); if (!WriteType(uId)) { return false; } if (!WritePlainObject(value)) { return false; } [[maybe_unused]] DataViewType viewType = TypedArrayHelper::GetType(typedArray); // Write ACCESSORS(ViewedArrayBuffer) which is a pointer to an ArrayBuffer JSHandle viewedArrayBufferOrByteArray(thread_, typedArray->GetViewedArrayBufferOrByteArray()); bool isViewedArrayBuffer = false; if (viewedArrayBufferOrByteArray->IsArrayBuffer() || viewedArrayBufferOrByteArray->IsSharedArrayBuffer()) { isViewedArrayBuffer = true; if (!WriteBoolean(isViewedArrayBuffer)) { return false; } if (!SerializeJSTaggedValue(viewedArrayBufferOrByteArray)) { return false; } } else { if (!WriteBoolean(isViewedArrayBuffer)) { return false; } if (!WriteByteArray(viewedArrayBufferOrByteArray, viewType)) { return false; } } // Write ACCESSORS(TypedArrayName) JSHandle typedArrayName(thread_, typedArray->GetTypedArrayName()); if (!SerializeJSTaggedValue(typedArrayName)) { return false; } // Write ACCESSORS(ByteLength) JSTaggedValue byteLength(typedArray->GetByteLength()); if (!WriteRawData(&byteLength, sizeof(JSTaggedValue))) { return false; } // Write ACCESSORS(ByteOffset) JSTaggedValue byteOffset(typedArray->GetByteOffset()); if (!WriteRawData(&byteOffset, sizeof(JSTaggedValue))) { return false; } // Write ACCESSORS(ArrayLength) JSTaggedValue arrayLength(typedArray->GetArrayLength()); if (!WriteRawData(&arrayLength, sizeof(JSTaggedValue))) { return false; } // Write ACCESSORS(ContentType) ContentType contentType = typedArray->GetContentType(); if (!WriteRawData(&contentType, sizeof(ContentType))) { return false; } return true; } bool JSSerializer::WriteJSNativePointer(const JSHandle &nativePtr) { uintptr_t externalPtr = reinterpret_cast(nativePtr->GetExternalPointer()); if (!WriteRawData(&externalPtr, sizeof(uintptr_t))) { return false; } uintptr_t deleter = reinterpret_cast(nativePtr->GetDeleter()); if (!WriteRawData(&deleter, sizeof(uintptr_t))) { return false; } uintptr_t allocatorPtr = reinterpret_cast(nativePtr->GetData()); if (!WriteRawData(&allocatorPtr, sizeof(uintptr_t))) { return false; } int32_t bindingSize = static_cast(nativePtr->GetBindingSize()); if (!WriteInt(bindingSize)) { return false; } nativePtr->Detach(); return true; } bool JSSerializer::WriteJSArrayBuffer(const JSHandle &value) { JSHandle arrayBuffer = JSHandle::Cast(value); if (arrayBuffer->IsDetach()) { return false; } bool shared = arrayBuffer->GetShared(); bool transfer = transferDataSet_.find(static_cast(value.GetTaggedType())) != transferDataSet_.end(); if (shared) { if (transfer) { LOG_ECMA(ERROR) << "Can't transfer a shared JSArrayBuffer"; return false; } if (!WriteType(SerializationUID::JS_SHARED_ARRAY_BUFFER)) { return false; } } else if (defaultTransfer_ || transfer) { if (!WriteType(SerializationUID::JS_TRANSFER_ARRAY_BUFFER)) { return false; } } else { if (!WriteType(SerializationUID::JS_ARRAY_BUFFER)) { return false; } } bool withNativeAreaAllocator = arrayBuffer->GetWithNativeAreaAllocator(); if (!WriteBoolean(withNativeAreaAllocator)) { return false; } // Write Accessors(ArrayBufferByteLength) uint32_t arrayLength = arrayBuffer->GetArrayBufferByteLength(); if (!WriteInt(arrayLength)) { return false; } bool empty = arrayLength == 0; if (!empty) { JSHandle np(thread_, arrayBuffer->GetArrayBufferData()); if (shared) { void *buffer = np->GetExternalPointer(); JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength); uint64_t bufferAddr = reinterpret_cast(buffer); if (!WriteRawData(&bufferAddr, sizeof(uint64_t))) { return false; } } else if (defaultTransfer_ || transfer) { // Write Accessors(ArrayBufferData) which is a pointer to a Buffer if (!WriteJSNativePointer(np)) { return false; } arrayBuffer->Detach(thread_, withNativeAreaAllocator); } else { // Write Accessors(ArrayBufferData) which is a pointer to a Buffer void *buffer = np->GetExternalPointer(); if (!WriteRawData(buffer, arrayLength)) { return false; } } } // write obj properties if (!WritePlainObject(value)) { return false; } return true; } bool JSSerializer::IsNativeBindingObject(std::vector keyVector) { if (keyVector.size() < 1) { // 1: nativeBindingSymbol return false; } JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle nativeBindingSymbol = env->GetNativeBindingSymbol(); JSMutableHandle nativeBindingKey(thread_, JSTaggedValue::Undefined()); uint32_t keyLength = keyVector.size(); for (uint32_t i = 0; i < keyLength; i++) { if (keyVector[i].IsSymbol()) { nativeBindingKey.Update(keyVector[i]); if (JSTaggedValue::Equal(thread_, nativeBindingSymbol, nativeBindingKey)) { return true; } } } return false; } bool JSSerializer::WritePlainObject(const JSHandle &objValue) { JSHandle obj = JSHandle::Cast(objValue); std::vector keyVector; uint32_t propertiesLength = obj->GetNumberOfKeys(); JSObject::GetAllKeysForSerialization(obj, keyVector); if (keyVector.size() != propertiesLength) { return false; } // Write custom JS obj that only used for carrying native binding functions if (IsNativeBindingObject(keyVector)) { return WriteNativeBindingObject(objValue); } // not support native object without detach and attach if (obj->GetNativePointerFieldCount() > 0) { return false; } if (!WriteType(SerializationUID::JS_PLAIN_OBJECT)) { return false; } if (!WriteInt(static_cast(propertiesLength))) { return false; } JSMutableHandle propertyKey(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < propertiesLength; i++) { if (keyVector.empty()) { return false; } propertyKey.Update(keyVector[i]); if (!SerializeJSTaggedValue(propertyKey)) { return false; } PropertyDescriptor desc(thread_); JSObject::OrdinaryGetOwnProperty(thread_, obj, propertyKey, desc); if (!WriteDesc(desc)) { return false; } } uint32_t elementsLength = obj->GetNumberOfElements(); if (!WriteInt(static_cast(elementsLength))) { return false; } keyVector.clear(); JSObject::GetALLElementKeysIntoVector(thread_, obj, keyVector); // Write elements' description attributes and value if (keyVector.size() != elementsLength) { return false; } JSMutableHandle elementKey(thread_, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < elementsLength; i++) { elementKey.Update(keyVector[i]); if (!SerializeJSTaggedValue(elementKey)) { return false; } PropertyDescriptor desc(thread_); JSObject::OrdinaryGetOwnProperty(thread_, obj, elementKey, desc); if (!WriteDesc(desc)) { return false; } } return true; } bool JSSerializer::WriteNativeBindingObject(const JSHandle &objValue) { JSHandle obj = JSHandle::Cast(objValue); JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle nativeBindingSymbol = env->GetNativeBindingSymbol(); if (!WriteType(SerializationUID::NATIVE_BINDING_OBJECT)) { return false; } JSHandle nativeBindingValue = JSObject::GetProperty(thread_, obj, nativeBindingSymbol).GetRawValue(); if (!nativeBindingValue->IsJSNativePointer()) { return false; } auto info = reinterpret_cast( JSNativePointer::Cast(nativeBindingValue->GetTaggedObject())->GetExternalPointer()); if (info == nullptr) { return false; } void *enginePointer = info->env; void *objPointer = info->nativeValue; void *hint = info->hint; void *detachData = info->detachData; void *attachData = info->attachData; DetachFunc detachNative = reinterpret_cast(info->detachFunc); if (detachNative == nullptr) { return false; } void *buffer = detachNative(enginePointer, objPointer, hint, detachData); AttachFunc attachNative = reinterpret_cast(info->attachFunc); if (!WriteRawData(&attachNative, sizeof(uintptr_t))) { return false; } if (!WriteRawData(&buffer, sizeof(uintptr_t))) { return false; } if (!WriteRawData(&hint, sizeof(uintptr_t))) { return false; } if (!WriteRawData(&attachData, sizeof(uintptr_t))) { return false; } return true; } bool JSSerializer::WriteDesc(const PropertyDescriptor &desc) { bool isWritable = desc.IsWritable(); if (!WriteBoolean(isWritable)) { return false; } bool isEnumerable = desc.IsEnumerable(); if (!WriteBoolean(isEnumerable)) { return false; } bool isConfigurable = desc.IsConfigurable(); if (!WriteBoolean(isConfigurable)) { return false; } bool hasWritable = desc.HasWritable(); if (!WriteBoolean(hasWritable)) { return false; } bool hasEnumerable = desc.HasEnumerable(); if (!WriteBoolean(hasEnumerable)) { return false; } bool hasConfigurable = desc.HasConfigurable(); if (!WriteBoolean(hasConfigurable)) { return false; } JSHandle value = desc.GetValue(); if (!SerializeJSTaggedValue(value)) { return false; } return true; } SerializationUID JSDeserializer::ReadType() { SerializationUID uid; if (position_ >= end_) { return SerializationUID::UNKNOWN; } uid = static_cast(*position_); if (uid < SerializationUID::UID_BEGIN || uid > SerializationUID::UID_END) { return SerializationUID::UNKNOWN; } position_++; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) return uid; } bool JSDeserializer::ReadInt(int32_t *value) { size_t len = sizeof(int32_t); if (len > static_cast(end_ - position_)) { return false; } if (memcpy_s(value, len, position_, len) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } position_ += len; return true; } bool JSDeserializer::ReadObjectId(uint64_t *objectId) { size_t len = sizeof(uint64_t); if (len > static_cast(end_ - position_)) { return false; } if (memcpy_s(objectId, len, position_, len) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } position_ += len; return true; } bool JSDeserializer::ReadDouble(double *value) { size_t len = sizeof(double); if (len > static_cast(end_ - position_)) { return false; } if (memcpy_s(value, len, position_, len) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } position_ += len; return true; } JSDeserializer::~JSDeserializer() { referenceMap_.clear(); } JSHandle JSDeserializer::Deserialize() { ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Deserialize dataSize: " + std::to_string(end_ - begin_)); JSHandle res = DeserializeJSTaggedValue(); return res; } JSHandle JSDeserializer::DeserializeJSTaggedValue() { SerializationUID uid = ReadType(); if (uid == SerializationUID::UNKNOWN) { return JSHandle(); } switch (uid) { case SerializationUID::JS_NULL: return JSHandle(thread_, JSTaggedValue::Null()); case SerializationUID::JS_UNDEFINED: return JSHandle(thread_, JSTaggedValue::Undefined()); case SerializationUID::JS_TRUE: return JSHandle(thread_, JSTaggedValue::True()); case SerializationUID::JS_FALSE: return JSHandle(thread_, JSTaggedValue::False()); case SerializationUID::HOLE: return JSHandle(thread_, JSTaggedValue::Hole()); case SerializationUID::INT32: { int32_t value; if (!ReadInt(&value)) { return JSHandle(); } return JSHandle(thread_, JSTaggedValue(value)); } case SerializationUID::DOUBLE: { double value; if (!ReadDouble(&value)) { return JSHandle(); } return JSHandle(thread_, JSTaggedValue(value)); } case SerializationUID::JS_ERROR: case SerializationUID::EVAL_ERROR: case SerializationUID::RANGE_ERROR: case SerializationUID::REFERENCE_ERROR: case SerializationUID::TYPE_ERROR: case SerializationUID::AGGREGATE_ERROR: case SerializationUID::URI_ERROR: case SerializationUID::SYNTAX_ERROR: case SerializationUID::OOM_ERROR: case SerializationUID::TERMINATION_ERROR: return ReadJSError(uid); case SerializationUID::JS_DATE: return ReadJSDate(); case SerializationUID::JS_PLAIN_OBJECT: return ReadPlainObject(); case SerializationUID::NATIVE_BINDING_OBJECT: return ReadNativeBindingObject(); case SerializationUID::JS_ARRAY: return ReadJSArray(); case SerializationUID::ECMASTRING: return ReadEcmaString(); case SerializationUID::JS_MAP: return ReadJSMap(); case SerializationUID::JS_SET: return ReadJSSet(); case SerializationUID::JS_REG_EXP: return ReadJSRegExp(); case SerializationUID::JS_INT8_ARRAY: return ReadJSTypedArray(SerializationUID::JS_INT8_ARRAY); case SerializationUID::JS_UINT8_ARRAY: return ReadJSTypedArray(SerializationUID::JS_UINT8_ARRAY); case SerializationUID::JS_UINT8_CLAMPED_ARRAY: return ReadJSTypedArray(SerializationUID::JS_UINT8_CLAMPED_ARRAY); case SerializationUID::JS_INT16_ARRAY: return ReadJSTypedArray(SerializationUID::JS_INT16_ARRAY); case SerializationUID::JS_UINT16_ARRAY: return ReadJSTypedArray(SerializationUID::JS_UINT16_ARRAY); case SerializationUID::JS_INT32_ARRAY: return ReadJSTypedArray(SerializationUID::JS_INT32_ARRAY); case SerializationUID::JS_UINT32_ARRAY: return ReadJSTypedArray(SerializationUID::JS_UINT32_ARRAY); case SerializationUID::JS_FLOAT32_ARRAY: return ReadJSTypedArray(SerializationUID::JS_FLOAT32_ARRAY); case SerializationUID::JS_FLOAT64_ARRAY: return ReadJSTypedArray(SerializationUID::JS_FLOAT64_ARRAY); case SerializationUID::JS_BIGINT64_ARRAY: return ReadJSTypedArray(SerializationUID::JS_BIGINT64_ARRAY); case SerializationUID::JS_BIGUINT64_ARRAY: return ReadJSTypedArray(SerializationUID::JS_BIGUINT64_ARRAY); case SerializationUID::JS_ARRAY_BUFFER: case SerializationUID::JS_SHARED_ARRAY_BUFFER: case SerializationUID::JS_TRANSFER_ARRAY_BUFFER: return ReadJSArrayBuffer(uid); case SerializationUID::TAGGED_OBJECT_REFERNCE: return ReadReference(); case SerializationUID::CONCURRENT_FUNCTION: return ReadJSFunction(); case SerializationUID::TAGGED_ARRAY: return ReadTaggedArray(); case SerializationUID::METHOD: return ReadMethod(); case SerializationUID::NATIVE_METHOD: return ReadNativeMethod(); case SerializationUID::BIGINT: return ReadBigInt(); default: return JSHandle(); } } JSHandle JSDeserializer::ReadBigInt() { int32_t len = 0; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) { return JSHandle(); } bool sign = false; if (!ReadBoolean(&sign)) { return JSHandle(); } JSHandle bigInt = factory_->NewBigInt(len); bigInt->SetSign(sign); JSHandle bigIntVal(bigInt); referenceMap_.emplace(objectId_++, bigIntVal); for (int32_t i = 0; i < len; i++) { int32_t val = 0; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&val)) { return JSHandle(); } bigInt->SetDigit(i, val); } return bigIntVal; } JSHandle JSDeserializer::ReadTaggedArray() { int32_t len = 0; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) { return JSHandle(); } JSHandle taggedArray = factory_->NewTaggedArray(len); JSHandle arrayTag(taggedArray); referenceMap_.emplace(objectId_++, arrayTag); for (int32_t i = 0; i < len; i++) { JSHandle val = DeserializeJSTaggedValue(); taggedArray->Set(thread_, i, val.GetTaggedValue()); } return arrayTag; } JSHandle JSDeserializer::ReadByteArray() { int32_t arrayLength = 0; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) { return JSHandle(); } int32_t viewTypeIndex = 0; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&viewTypeIndex)) { return JSHandle(); } DataViewType viewType = GetDataViewTypeByIndex(viewTypeIndex); uint32_t arrayType = TypedArrayHelper::GetSizeFromType(viewType); JSHandle byteArray = factory_->NewByteArray(arrayLength, arrayType); for (int32_t i = 0; i < arrayLength; i++) { JSHandle val = DeserializeJSTaggedValue(); byteArray->Set(thread_, i, viewType, val.GetTaggedType()); } return JSHandle(byteArray); } DataViewType JSDeserializer::GetDataViewTypeByIndex(uint32_t viewTypeIndex) { DataViewType viewType; switch (viewTypeIndex) { case 1: // 1 : DataViewType::INT8 viewType = DataViewType::INT8; break; case 2: // 2 : DataViewType::UINT8 viewType = DataViewType::UINT8; break; case 3: // 3 : DataViewType::UINT8_CLAMPED viewType = DataViewType::UINT8_CLAMPED; break; case 4: // 4 : DataViewType::INT16 viewType = DataViewType::INT16; break; case 5: // 5 : DataViewType::UINT16 viewType = DataViewType::UINT16; break; case 6: // 6 : DataViewType::INT32 viewType = DataViewType::INT32; break; case 7: // 7 : DataViewType::UINT32 viewType = DataViewType::UINT32; break; case 8: // 8 : DataViewType::FLOAT32 viewType = DataViewType::FLOAT32; break; case 9: // 9 : DataViewType::FLOAT64 viewType = DataViewType::FLOAT64; break; case 10: // 10 : DataViewType::BIGINT64 viewType = DataViewType::BIGINT64; break; case 11: // 11 : DataViewType::BIGUINT64 viewType = DataViewType::BIGUINT64; break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } return viewType; } JSHandle JSDeserializer::ReadMethod() { uintptr_t methodLiteral; if (!ReadNativePointer(&methodLiteral)) { return JSHandle(); } JSHandle method = factory_->NewSMethod(reinterpret_cast(methodLiteral)); JSHandle methodTag(method); referenceMap_.emplace(objectId_++, methodTag); CString desc; if (!ReadString(&desc)) { return JSHandle(); } std::shared_ptr jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(desc); if (jsPandaFile == nullptr) { return JSHandle(); } // ai file not include method JSHandle constPool = thread_->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile.get(), method->GetMethodId()); method->SetConstantPool(thread_, constPool.GetTaggedValue()); if (method->CanSerializeCodeEntry()) { uintptr_t codeEntry; if (!ReadNativePointer(&codeEntry)) { return JSHandle(); } method->SetCodeEntryAndMarkAOTWhenBinding(codeEntry); uint8_t deoptThreshold = thread_->GetEcmaVM()->GetJSOptions().GetDeoptThreshold(); method->SetDeoptThreshold(deoptThreshold); } return methodTag; } bool JSDeserializer::ReadString(CString *value) { if (!JudgeType(SerializationUID::C_STRING)) { return false; } *value = reinterpret_cast(const_cast(position_)); size_t len = value->length() + 1; // 1: '\0' position_ += len; return true; } JSHandle JSDeserializer::ReadNativeMethod() { uintptr_t nativeFunc; if (!ReadNativePointer(&nativeFunc)) { return JSHandle(); } JSHandle method = factory_->NewMethodForNativeFunction(reinterpret_cast(nativeFunc)); JSHandle methodTag(method); referenceMap_.emplace(objectId_++, methodTag); return methodTag; } JSHandle JSDeserializer::ReadJSFunction() { JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle func = factory_->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION); JSHandle funcTag(func); referenceMap_.emplace(objectId_++, funcTag); JSHandle methodVal = DeserializeJSTaggedValue(); JSHandle method = JSHandle::Cast(methodVal); func->SetMethod(thread_, method); func->SetTaskConcurrentFuncFlag(0); // 0 : default value func->InitializeForConcurrentFunction(thread_); if (method->IsAotWithCallField()) { uintptr_t codeEntry; if (!ReadNativePointer(&codeEntry)) { return JSHandle(); } func->SetCodeEntry(codeEntry); } return funcTag; } JSHandle JSDeserializer::ReadJSError(SerializationUID uid) { base::ErrorType errorType; switch (uid) { case SerializationUID::JS_ERROR: errorType = base::ErrorType::ERROR; break; case SerializationUID::EVAL_ERROR: errorType = base::ErrorType::EVAL_ERROR; break; case SerializationUID::RANGE_ERROR: errorType = base::ErrorType::RANGE_ERROR; break; case SerializationUID::REFERENCE_ERROR: errorType = base::ErrorType::REFERENCE_ERROR; break; case SerializationUID::TYPE_ERROR: errorType = base::ErrorType::TYPE_ERROR; break; case SerializationUID::AGGREGATE_ERROR: errorType = base::ErrorType::AGGREGATE_ERROR; break; case SerializationUID::URI_ERROR: errorType = base::ErrorType::URI_ERROR; break; case SerializationUID::SYNTAX_ERROR: errorType = base::ErrorType::SYNTAX_ERROR; break; case SerializationUID::OOM_ERROR: errorType = base::ErrorType::OOM_ERROR; break; case SerializationUID::TERMINATION_ERROR: errorType = base::ErrorType::TERMINATION_ERROR; break; default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } JSHandle msg = DeserializeJSTaggedValue(); JSHandle handleMsg(msg); JSHandle errorTag = JSHandle::Cast(factory_->NewJSError(errorType, handleMsg)); referenceMap_.emplace(objectId_++, errorTag); return errorTag; } JSHandle JSDeserializer::ReadJSDate() { JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle dateFunction(env->GetDateFunction()); JSHandle date = JSHandle::Cast(factory_->NewJSObjectByConstructor(dateFunction)); JSHandle dateTag = JSHandle::Cast(date); referenceMap_.emplace(objectId_++, dateTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(dateTag)) { return JSHandle(); } double timeValue = 0.0; if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&timeValue)) { return JSHandle(); } date->SetTimeValue(thread_, JSTaggedValue(timeValue)); double localOffset = 0.0; if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&localOffset)) { return JSHandle(); } date->SetLocalOffset(thread_, JSTaggedValue(localOffset)); return dateTag; } JSHandle JSDeserializer::ReadJSArray() { STACK_LIMIT_CHECK(thread_, JSHandle(thread_, JSTaggedValue::Exception())); JSHandle jsArray = thread_->GetEcmaVM()->GetFactory()->NewJSArray(); JSHandle arrayTag = JSHandle::Cast(jsArray); referenceMap_.emplace(objectId_++, arrayTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayTag)) { return JSHandle(); } int32_t arrLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrLength)) { return JSHandle(); } jsArray->SetLength(arrLength); return arrayTag; } JSHandle JSDeserializer::ReadEcmaString() { int32_t stringLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&stringLength)) { return JSHandle(); } if (stringLength == 0) { JSHandle emptyString = JSHandle::Cast(factory_->GetEmptyString()); referenceMap_.emplace(objectId_++, emptyString); return emptyString; } bool isUtf8 = false; if (!ReadBoolean(&isUtf8)) { return JSHandle(); } JSHandle stringTag; if (isUtf8) { uint8_t *string = reinterpret_cast(GetBuffer(stringLength + 1)); if (string == nullptr) { return JSHandle(); } JSHandle ecmaString = factory_->NewFromUtf8(string, stringLength); stringTag = JSHandle(ecmaString); referenceMap_.emplace(objectId_++, stringTag); } else { uint16_t *string = reinterpret_cast(GetBuffer(stringLength * sizeof(uint16_t))); if (string == nullptr) { return JSHandle(); } JSHandle ecmaString = factory_->NewFromUtf16(string, stringLength); stringTag = JSHandle(ecmaString); referenceMap_.emplace(objectId_++, stringTag); } return stringTag; } JSHandle JSDeserializer::ReadPlainObject() { STACK_LIMIT_CHECK(thread_, JSHandle(thread_, JSTaggedValue::Exception())); JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle objFunc(env->GetObjectFunction()); JSHandle jsObject = thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc); JSHandle objTag = JSHandle::Cast(jsObject); referenceMap_.emplace(objectId_++, objTag); if (!DefinePropertiesAndElements(objTag)) { return JSHandle(); } return objTag; } JSHandle JSDeserializer::ReadNativeBindingObject() { uintptr_t funcPointer; if (!ReadNativePointer(&funcPointer)) { return JSHandle(); } AttachFunc attachFunc = reinterpret_cast(funcPointer); if (attachFunc == nullptr) { return JSHandle(); } uintptr_t bufferPointer; if (!ReadNativePointer(&bufferPointer)) { return JSHandle(); } uintptr_t hint; if (!ReadNativePointer(&hint)) { return JSHandle(); } uintptr_t attachData; if (!ReadNativePointer(&attachData)) { return JSHandle(); } Local attachVal = attachFunc(engine_, reinterpret_cast(bufferPointer), reinterpret_cast(hint), reinterpret_cast(attachData)); if (attachVal.IsEmpty()) { LOG_ECMA(ERROR) << "NativeBindingObject is empty"; attachVal = JSValueRef::Undefined(thread_->GetEcmaVM()); } objectId_++; return JSNApiHelper::ToJSHandle(attachVal); } JSHandle JSDeserializer::ReadJSMap() { STACK_LIMIT_CHECK(thread_, JSHandle(thread_, JSTaggedValue::Exception())); JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle mapFunction(env->GetBuiltinsMapFunction()); JSHandle jsMap = JSHandle::Cast(factory_->NewJSObjectByConstructor(mapFunction)); JSHandle mapTag = JSHandle::Cast(jsMap); referenceMap_.emplace(objectId_++, mapTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(mapTag)) { return JSHandle(); } int32_t size; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) { return JSHandle(); } JSHandle linkedMap = LinkedHashMap::Create(thread_); jsMap->SetLinkedMap(thread_, linkedMap); for (int32_t i = 0; i < size; i++) { JSHandle key = DeserializeJSTaggedValue(); if (key.IsEmpty()) { return JSHandle(); } JSHandle value = DeserializeJSTaggedValue(); if (value.IsEmpty()) { return JSHandle(); } JSMap::Set(thread_, jsMap, key, value); } return mapTag; } JSHandle JSDeserializer::ReadJSSet() { STACK_LIMIT_CHECK(thread_, JSHandle(thread_, JSTaggedValue::Exception())); JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle setFunction(env->GetBuiltinsSetFunction()); JSHandle jsSet = JSHandle::Cast(factory_->NewJSObjectByConstructor(setFunction)); JSHandle setTag = JSHandle::Cast(jsSet); referenceMap_.emplace(objectId_++, setTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(setTag)) { return JSHandle(); } int32_t size; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) { return JSHandle(); } JSHandle linkedSet = LinkedHashSet::Create(thread_); jsSet->SetLinkedSet(thread_, linkedSet); for (int32_t i = 0; i < size; i++) { JSHandle key = DeserializeJSTaggedValue(); if (key.IsEmpty()) { return JSHandle(); } JSSet::Add(thread_, jsSet, key); } return setTag; } JSHandle JSDeserializer::ReadJSRegExp() { JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle regexpFunction(env->GetRegExpFunction()); JSHandle obj = factory_->NewJSObjectByConstructor(regexpFunction); JSHandle regExp = JSHandle::Cast(obj); JSHandle regexpTag = JSHandle::Cast(regExp); referenceMap_.emplace(objectId_++, regexpTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(regexpTag)) { return JSHandle(); } int32_t bufferSize; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bufferSize)) { return JSHandle(); } void *buffer = GetBuffer(bufferSize); if (buffer == nullptr) { return JSHandle(); } factory_->NewJSRegExpByteCodeData(regExp, buffer, bufferSize); JSHandle originalSource = DeserializeJSTaggedValue(); regExp->SetOriginalSource(thread_, originalSource); JSHandle originalFlags = DeserializeJSTaggedValue(); regExp->SetOriginalFlags(thread_, originalFlags); return regexpTag; } JSHandle JSDeserializer::ReadJSTypedArray(SerializationUID uid) { JSHandle env = thread_->GetEcmaVM()->GetGlobalEnv(); JSHandle target = GetTypedArrayFunction(env, uid); if (target.IsEmpty()) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } JSHandle typedArray = JSHandle::Cast(factory_->NewJSObjectByConstructor(JSHandle(target))); JSHandle obj = JSHandle::Cast(typedArray); JSHandle objTag = JSHandle::Cast(obj); referenceMap_.emplace(objectId_++, objTag); if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(objTag)) { return JSHandle(); } bool isViewedArrayBuffer = false; if (!ReadBoolean(&isViewedArrayBuffer)) { return JSHandle(); } JSHandle viewedArrayBufferOrByteArray; if (isViewedArrayBuffer) { viewedArrayBufferOrByteArray = DeserializeJSTaggedValue(); } else { if (!JudgeType(SerializationUID::BYTE_ARRAY)) { return JSHandle(); } viewedArrayBufferOrByteArray = ReadByteArray(); } if (viewedArrayBufferOrByteArray.IsEmpty()) { return JSHandle(); } typedArray->SetViewedArrayBufferOrByteArray(thread_, viewedArrayBufferOrByteArray); JSHandle typedArrayName = DeserializeJSTaggedValue(); if (typedArrayName.IsEmpty()) { return JSHandle(); } typedArray->SetTypedArrayName(thread_, typedArrayName); if (!SetTypedArrayProperties(typedArray)) { return JSHandle(); } return objTag; } JSHandle JSDeserializer::GetTypedArrayFunction(JSHandle &env, SerializationUID &uid) { switch (uid) { case SerializationUID::JS_INT8_ARRAY: return env->GetInt8ArrayFunction(); case SerializationUID::JS_UINT8_ARRAY: return env->GetUint8ArrayFunction(); case SerializationUID::JS_UINT8_CLAMPED_ARRAY: return env->GetUint8ClampedArrayFunction(); case SerializationUID::JS_INT16_ARRAY: return env->GetInt16ArrayFunction(); case SerializationUID::JS_UINT16_ARRAY: return env->GetUint16ArrayFunction(); case SerializationUID::JS_INT32_ARRAY: return env->GetInt32ArrayFunction(); case SerializationUID::JS_UINT32_ARRAY: return env->GetUint32ArrayFunction(); case SerializationUID::JS_FLOAT32_ARRAY: return env->GetFloat32ArrayFunction(); case SerializationUID::JS_FLOAT64_ARRAY: return env->GetFloat64ArrayFunction(); case SerializationUID::JS_BIGINT64_ARRAY: return env->GetBigInt64ArrayFunction(); case SerializationUID::JS_BIGUINT64_ARRAY: return env->GetBigUint64ArrayFunction(); default: return JSHandle(); } } bool JSDeserializer::SetTypedArrayProperties(JSHandle &typedArray) { JSTaggedValue byteLength; if (!ReadJSTaggedValue(&byteLength) || !byteLength.IsNumber()) { return false; } typedArray->SetByteLength(byteLength.GetNumber()); JSTaggedValue byteOffset; if (!ReadJSTaggedValue(&byteOffset) || !byteOffset.IsNumber()) { return false; } typedArray->SetByteOffset(byteOffset.GetNumber()); JSTaggedValue arrayLength; if (!ReadJSTaggedValue(&arrayLength) || !arrayLength.IsNumber()) { return false; } typedArray->SetArrayLength(arrayLength.GetNumber()); ContentType *contentType = reinterpret_cast(GetBuffer(sizeof(ContentType))); if (contentType == nullptr) { return false; } typedArray->SetContentType(*contentType); return true; } JSHandle JSDeserializer::ReadJSNativePointer() { uintptr_t externalPtr; if (!ReadNativePointer(&externalPtr)) { return JSHandle(); } uintptr_t deleter; if (!ReadNativePointer(&deleter)) { return JSHandle(); } uintptr_t allocatorPtr; if (!ReadNativePointer(&allocatorPtr)) { return JSHandle(); } int32_t bindingSize; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bindingSize)) { return JSHandle(); } JSHandle np = factory_->NewJSNativePointer(ToVoidPtr(externalPtr), reinterpret_cast(deleter), ToVoidPtr(allocatorPtr), false, bindingSize); return JSHandle::Cast(np); } JSHandle JSDeserializer::ReadJSArrayBuffer(SerializationUID uid) { bool withNativeAreaAllocator; if (!ReadBoolean(&withNativeAreaAllocator)) { return JSHandle(); } // read access length int32_t arrayLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) { return JSHandle(); } // read access shared bool shared = (uid == SerializationUID::JS_SHARED_ARRAY_BUFFER); JSHandle arrayBuffer; if (arrayLength == 0) { // create an empty arrayBuffer arrayBuffer = factory_->NewJSArrayBuffer(0); arrayBuffer->SetShared(shared); } else { if (shared) { uint64_t *bufferAddr = reinterpret_cast(GetBuffer(sizeof(uint64_t))); void *bufferData = ToVoidPtr(*bufferAddr); arrayBuffer = factory_->NewJSSharedArrayBuffer(bufferData, arrayLength); } else if (uid == SerializationUID::JS_TRANSFER_ARRAY_BUFFER) { JSHandle np = ReadJSNativePointer(); if (np.IsEmpty()) { return JSHandle(); } arrayBuffer = factory_->NewJSArrayBuffer(0); arrayBuffer->Attach(thread_, arrayLength, np.GetTaggedValue(), withNativeAreaAllocator); } else { void *fromBuffer = GetBuffer(arrayLength); if (fromBuffer == nullptr) { return JSHandle(); } arrayBuffer = factory_->NewJSArrayBuffer(arrayLength); JSNativePointer* np = JSNativePointer::Cast(arrayBuffer->GetArrayBufferData().GetTaggedObject()); void *toBuffer = np->GetExternalPointer(); if (memcpy_s(toBuffer, arrayLength, fromBuffer, arrayLength) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } } } arrayBuffer->SetWithNativeAreaAllocator(withNativeAreaAllocator); JSHandle arrayBufferTag = JSHandle::Cast(arrayBuffer); referenceMap_.emplace(objectId_++, arrayBufferTag); // read jsarraybuffer properties if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) { return JSHandle(); } return arrayBufferTag; } bool JSDeserializer::ReadJSTaggedValue(JSTaggedValue *value) { size_t len = sizeof(JSTaggedValue); if (len > static_cast(end_ - position_)) { return false; } if (memcpy_s(value, len, position_, len) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } position_ += len; return true; } bool JSDeserializer::ReadNativePointer(uintptr_t *value) { size_t len = sizeof(uintptr_t); if (len > static_cast(end_ - position_)) { return false; } if (memcpy_s(value, len, position_, len) != EOK) { LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); } position_ += len; return true; } void *JSDeserializer::GetBuffer(uint32_t bufferSize) { const uint8_t *buffer = nullptr; if (bufferSize > static_cast(end_ - position_)) { return nullptr; } buffer = position_; position_ += bufferSize; uint8_t *retBuffer = const_cast(buffer); return static_cast(retBuffer); } JSHandle JSDeserializer::ReadReference() { uint64_t objId; if (!ReadObjectId(&objId)) { return JSHandle(); } auto objIter = referenceMap_.find(objId); if (objIter == referenceMap_.end()) { return JSHandle(); } return objIter->second; } bool JSDeserializer::JudgeType(SerializationUID targetUid) { if (ReadType() != targetUid) { return false; } return true; } bool JSDeserializer::DefinePropertiesAndElements(const JSHandle &obj) { int32_t propertyLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&propertyLength)) { return false; } for (int32_t i = 0; i < propertyLength; i++) { JSHandle key = DeserializeJSTaggedValue(); if (key.IsEmpty()) { return false; } PropertyDescriptor desc(thread_); if (!ReadDesc(&desc)) { return false; } if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { return false; } } int32_t elementLength; if (!JudgeType(SerializationUID::INT32) || !ReadInt(&elementLength)) { return false; } for (int32_t i = 0; i < elementLength; i++) { JSHandle key = DeserializeJSTaggedValue(); if (key.IsEmpty()) { return false; } PropertyDescriptor desc(thread_); if (!ReadDesc(&desc)) { return false; } if (!JSTaggedValue::DefineOwnProperty(thread_, obj, key, desc)) { return false; } } return true; } bool JSDeserializer::ReadDesc(PropertyDescriptor *desc) { bool isWritable = false; if (!ReadBoolean(&isWritable)) { return false; } bool isEnumerable = false; if (!ReadBoolean(&isEnumerable)) { return false; } bool isConfigurable = false; if (!ReadBoolean(&isConfigurable)) { return false; } bool hasWritable = false; if (!ReadBoolean(&hasWritable)) { return false; } bool hasEnumerable = false; if (!ReadBoolean(&hasEnumerable)) { return false; } bool hasConfigurable = false; if (!ReadBoolean(&hasConfigurable)) { return false; } JSHandle value = DeserializeJSTaggedValue(); if (value.IsEmpty()) { return false; } desc->SetValue(value); if (hasWritable) { desc->SetWritable(isWritable); } if (hasEnumerable) { desc->SetEnumerable(isEnumerable); } if (hasConfigurable) { desc->SetConfigurable(isConfigurable); } return true; } bool JSDeserializer::ReadBoolean(bool *value) { SerializationUID uid = ReadType(); if (uid == SerializationUID::C_TRUE) { *value = true; return true; } if (uid == SerializationUID::C_FALSE) { *value = false; return true; } return false; } bool Serializer::WriteValue( JSThread *thread, const JSHandle &value, const JSHandle &transfer) { ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Serializer::WriteValue"); if (data_ != nullptr) { return false; } data_.reset(new SerializationData); if (value.GetTaggedValue() == transfer.GetTaggedValue()) { valueSerializer_.SetDefaultTransfer(); } else if (!PrepareTransfer(thread, transfer)) { return false; } if (!valueSerializer_.SerializeJSTaggedValue(value)) { valueSerializer_.ReleaseBuffer(); return false; } size_t maxSerializerSize = thread->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxJSSerializerSize(); if (data_->GetSize() > maxSerializerSize) { LOG_ECMA(ERROR) << "The Serialization data size has exceed limit Size, current size is: " << data_->GetSize() << " max size is: " << maxSerializerSize; valueSerializer_.ReleaseBuffer(); return false; } std::pair pair = valueSerializer_.ReleaseBuffer(); data_->value_.reset(pair.first); data_->dataSize_ = pair.second; return true; } std::unique_ptr Serializer::Release() { return std::move(data_); } bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle &transfer) { if (transfer->IsUndefined()) { return true; } if (!transfer->IsJSArray()) { return false; } int len = base::ArrayHelper::GetArrayLength(thread, transfer); int k = 0; CUnorderedSet transferDataSet; while (k < len) { bool exists = JSTaggedValue::HasProperty(thread, transfer, k); if (exists) { JSHandle element = JSArray::FastGetPropertyByValue(thread, transfer, k); if (!element->IsArrayBuffer()) { return false; } transferDataSet.insert(static_cast(element.GetTaggedType())); } k++; } valueSerializer_.InitTransferSet(std::move(transferDataSet)); return true; } JSHandle Deserializer::ReadValue() { ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Deserializer::ReadValue"); return valueDeserializer_.Deserialize(); } } // namespace panda::ecmascript