From 753391cf29a5f0a5ef08c1997c4e648a469e1553 Mon Sep 17 00:00:00 2001 From: dingwen Date: Wed, 6 Dec 2023 15:51:03 +0800 Subject: [PATCH] Enable value serialization Description:Enable value serialization Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I8M8EI Signed-off-by: dingwen Change-Id: I80045a7c619ad1f6a3ea4dad964b208a02a5a8c3 --- ecmascript/base/error_type.h | 4 +- ecmascript/builtins/builtins.cpp | 6 + ecmascript/common.h | 2 +- ecmascript/ecma_string.h | 7 +- ecmascript/js_thread.cpp | 5 +- ecmascript/mem/heap.cpp | 5 + ecmascript/mem/heap.h | 11 + ecmascript/serializer/base_deserializer.cpp | 163 +++++++++++---- ecmascript/serializer/base_deserializer.h | 46 +++- ecmascript/serializer/base_serializer.cpp | 14 +- ecmascript/serializer/serialize_data.h | 22 +- .../serializer/tests/serializer_test.cpp | 197 +++++++++++++++++- ecmascript/serializer/value_serializer.cpp | 67 +++++- ecmascript/serializer/value_serializer.h | 7 +- 14 files changed, 482 insertions(+), 74 deletions(-) diff --git a/ecmascript/base/error_type.h b/ecmascript/base/error_type.h index ce5acacf3c..7b383e37c0 100644 --- a/ecmascript/base/error_type.h +++ b/ecmascript/base/error_type.h @@ -24,10 +24,10 @@ enum class ErrorType : uint8_t { EVAL_ERROR, RANGE_ERROR, REFERENCE_ERROR, - SYNTAX_ERROR, TYPE_ERROR, - URI_ERROR, AGGREGATE_ERROR, + URI_ERROR, + SYNTAX_ERROR, OOM_ERROR, TERMINATION_ERROR, }; diff --git a/ecmascript/builtins/builtins.cpp b/ecmascript/builtins/builtins.cpp index 5d4de39caf..aa08931ec0 100644 --- a/ecmascript/builtins/builtins.cpp +++ b/ecmascript/builtins/builtins.cpp @@ -894,6 +894,7 @@ void Builtins::LazyInitializeDate(const JSHandle &env) const auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::Date)); SetLazyAccessor(globalObject, key, accessor); env->SetDateFunction(thread_, accessor); + env->SetDatePrototype(thread_, accessor); } void Builtins::InitializeBoolean(const JSHandle &env, const JSHandle &primRefObjHClass) const @@ -1295,6 +1296,8 @@ void Builtins::LazyInitializeSet(const JSHandle &env) auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::Set)); SetLazyAccessor(globalObject, key, accessor); env->SetBuiltinsSetFunction(thread_, accessor); + env->SetSetPrototype(thread_, accessor); + env->SetSetProtoValuesFunction(thread_, accessor); } void Builtins::InitializeMap(const JSHandle &env, JSHandle objFuncPrototypeVal) const @@ -1365,6 +1368,7 @@ void Builtins::LazyInitializeMap(const JSHandle &env) const SetLazyAccessor(globalObject, key, accessor); env->SetBuiltinsMapFunction(thread_, accessor); env->SetMapPrototype(thread_, accessor); + env->SetMapProtoEntriesFunction(thread_, accessor); } void Builtins::InitializeWeakMap(const JSHandle &env, const JSHandle &objFuncClass) const @@ -2162,6 +2166,7 @@ void Builtins::LazyInitialize##Type(const JSHandle &env) const auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::Type)); \ SetLazyAccessor(globalObject, key, accessor); \ env->Set##Type##Function(thread_, accessor); \ + env->Set##Type##FunctionPrototype(thread_, accessor); \ } BUILTIN_TYPED_ARRAY_TYPES(BUILTIN_TYPED_ARRAY_DEFINE_LAZY_INITIALIZE) @@ -2461,6 +2466,7 @@ void Builtins::LazyInitializeDataView(const JSHandle &env) const auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast(BuiltinsLazyCallback::DataView)); SetLazyAccessor(globalObject, key, accessor); env->SetDataViewFunction(thread_, accessor); + env->SetDataViewPrototype(thread_, accessor); } JSHandle Builtins::NewBuiltinConstructor(const JSHandle &env, diff --git a/ecmascript/common.h b/ecmascript/common.h index b9b93a7e67..a1e5fc3bf1 100644 --- a/ecmascript/common.h +++ b/ecmascript/common.h @@ -171,7 +171,7 @@ using Address = uintptr_t; #define WIN_OR_MAC_OR_IOS_PLATFORM false #endif -#define ECMASCRIPT_ENABLE_VALUE_SERIALIZER 0 +#define ECMASCRIPT_ENABLE_VALUE_SERIALIZER 1 #define STATIC_ASSERT_EQ_ARCH(expect, valueArch32, valueArch64) \ STATIC_ASSERT_EQ_ARCH32(expect, valueArch32); \ diff --git a/ecmascript/ecma_string.h b/ecmascript/ecma_string.h index e91a88ae01..7f753a3f78 100644 --- a/ecmascript/ecma_string.h +++ b/ecmascript/ecma_string.h @@ -813,7 +813,8 @@ public: // ConstantData will also point at data of bytearray data. ACCESSORS(RelocatedData, RELOCTAED_DATA_OFFSET, ENTITY_ID_OFFSET); ACCESSORS_PRIMITIVE_FIELD(EntityId, int64_t, ENTITY_ID_OFFSET, CONSTANT_DATA_OFFSET); - ACCESSORS_NATIVE_FIELD(ConstantData, uint8_t, CONSTANT_DATA_OFFSET, SIZE); + ACCESSORS_NATIVE_FIELD(ConstantData, uint8_t, CONSTANT_DATA_OFFSET, LAST_OFFSET); + DEFINE_ALIGN_SIZE(LAST_OFFSET); CAST_CHECK(ConstantString, IsConstantString); DECL_VISIT_OBJECT(RELOCTAED_DATA_OFFSET, ENTITY_ID_OFFSET); @@ -860,7 +861,9 @@ public: static constexpr uint32_t MIN_SLICED_ECMASTRING_LENGTH = 13; static constexpr size_t PARENT_OFFSET = EcmaString::SIZE; ACCESSORS(Parent, PARENT_OFFSET, STARTINDEX_OFFSET); - ACCESSORS_PRIMITIVE_FIELD(StartIndex, uint32_t, STARTINDEX_OFFSET, SIZE); + ACCESSORS_PRIMITIVE_FIELD(StartIndex, uint32_t, STARTINDEX_OFFSET, LAST_OFFSET); + DEFINE_ALIGN_SIZE(LAST_OFFSET); + DECL_VISIT_OBJECT(PARENT_OFFSET, STARTINDEX_OFFSET); CAST_CHECK(SlicedString, IsSlicedString); diff --git a/ecmascript/js_thread.cpp b/ecmascript/js_thread.cpp index a96396ad5c..66efb680c7 100644 --- a/ecmascript/js_thread.cpp +++ b/ecmascript/js_thread.cpp @@ -527,8 +527,9 @@ bool JSThread::CheckSafepoint() gcTriggered = true; } #endif - if (IsMarkFinished() && GetEcmaVM()->GetHeap()->GetConcurrentMarker()->IsTriggeredConcurrentMark()) { - auto heap = GetEcmaVM()->GetHeap(); + auto heap = GetEcmaVM()->GetHeap(); + if (IsMarkFinished() && heap->GetConcurrentMarker()->IsTriggeredConcurrentMark() + && !heap->GetOnSerializeEvent()) { heap->GetConcurrentMarker()->HandleMarkingFinished(); gcTriggered = true; } diff --git a/ecmascript/mem/heap.cpp b/ecmascript/mem/heap.cpp index c0fc0de6fc..c02947866d 100644 --- a/ecmascript/mem/heap.cpp +++ b/ecmascript/mem/heap.cpp @@ -1251,6 +1251,11 @@ void Heap::NotifyHighSensitive(bool isStart) bool Heap::NeedStopCollection() { + // gc is not allowed during value serialize + if (onSerializeEvent_) { + return true; + } + if (!InSensitiveStatus()) { return false; } diff --git a/ecmascript/mem/heap.h b/ecmascript/mem/heap.h index 1013c308c0..39cecd5e3d 100644 --- a/ecmascript/mem/heap.h +++ b/ecmascript/mem/heap.h @@ -236,6 +236,16 @@ public: LOG_GC(INFO) << "SmartGC: enter app cold start"; } + void SetOnSerializeEvent(bool isSerialize) + { + onSerializeEvent_ = isSerialize; + } + + bool GetOnSerializeEvent() const + { + return onSerializeEvent_; + } + /* * For object allocations. */ @@ -746,6 +756,7 @@ private: IdleNotifyStatusCallback notifyIdleStatusCallback {nullptr}; std::atomic_bool onHighSensitiveEvent_ {false}; bool onStartupEvent_ {false}; + bool onSerializeEvent_ {false}; IdleTaskType idleTask_ {IdleTaskType::NO_TASK}; float idlePredictDuration_ {0.0f}; diff --git a/ecmascript/serializer/base_deserializer.cpp b/ecmascript/serializer/base_deserializer.cpp index 31715ab229..4b5c8cea7c 100644 --- a/ecmascript/serializer/base_deserializer.cpp +++ b/ecmascript/serializer/base_deserializer.cpp @@ -50,9 +50,13 @@ JSHandle BaseDeserializer::DeserializeJSTaggedValue() LOG_ECMA(ERROR) << "The serialization data is incomplete"; return JSHandle(); } + + // stop gc during deserialize + heap_->SetOnSerializeEvent(true); + uint8_t encodeFlag = data_->ReadUint8(); - uintptr_t result = 0U; - while (ReadSingleEncodeData(encodeFlag, ObjectSlot(ToUintPtr(&result)), true) == 0) { + JSTaggedType result = 0U; + while (ReadSingleEncodeData(encodeFlag, ToUintPtr(&result), true) == 0) { encodeFlag = data_->ReadUint8(); } // now new constpool here if newConstPoolInfos_ is not empty @@ -62,6 +66,12 @@ JSHandle BaseDeserializer::DeserializeJSTaggedValue() } newConstPoolInfos_.clear(); + // initialize concurrent func here after constpool is set + for (auto func : concurrentFunctions_) { + func->InitializeForConcurrentFunction(thread_); + } + concurrentFunctions_.clear(); + // new native binding object here for (auto nativeBindingInfo : nativeBindingInfos_) { DeserializeNativeBindingObject(nativeBindingInfo); @@ -69,7 +79,17 @@ JSHandle BaseDeserializer::DeserializeJSTaggedValue() } nativeBindingInfos_.clear(); - return JSHandle(thread_, JSTaggedValue(static_cast(result))); + // new js error here + for (auto jsErrorInfo : jsErrorInfos_) { + DeserializeJSError(jsErrorInfo); + delete jsErrorInfo; + } + jsErrorInfos_.clear(); + + // recovery gc after serialize + heap_->SetOnSerializeEvent(false); + + return JSHandle(thread_, JSTaggedValue(result)); } uintptr_t BaseDeserializer::DeserializeTaggedObject(SerializedObjectSpace space) @@ -78,7 +98,7 @@ uintptr_t BaseDeserializer::DeserializeTaggedObject(SerializedObjectSpace space) uintptr_t res = RelocateObjectAddr(space, objSize); objectVector_.push_back(res); size_t resIndex = objectVector_.size() - 1; - DeserializeObjectField(ObjectSlot(res), ObjectSlot(res + objSize)); + DeserializeObjectField(res, res + objSize); JSType type = reinterpret_cast(res)->GetClass()->GetObjectType(); // String need remove duplicates if string table can find if (type == JSType::LINE_STRING || type == JSType::CONSTANT_STRING) { @@ -95,7 +115,7 @@ uintptr_t BaseDeserializer::DeserializeTaggedObject(SerializedObjectSpace space) return res; } -void BaseDeserializer::DeserializeObjectField(ObjectSlot start, ObjectSlot end) +void BaseDeserializer::DeserializeObjectField(uintptr_t start, uintptr_t end) { while (start < end) { uint8_t encodeFlag = data_->ReadUint8(); @@ -114,7 +134,7 @@ void BaseDeserializer::DeserializeConstPool(NewConstPoolInfo *info) int32_t index = static_cast(indexAccessor.GetHeaderIndex()); thread_->GetCurrentEcmaContext()->AddConstpool(jsPandaFile, constpool.GetTaggedValue(), index); slot.Update(constpool.GetTaggedType()); - UpdateIfExistOldToNew(constpool.GetTaggedType(), slot); + UpdateBarrier(constpool.GetTaggedType(), slot); } void BaseDeserializer::DeserializeNativeBindingObject(NativeBindingInfo *info) @@ -134,7 +154,23 @@ void BaseDeserializer::DeserializeNativeBindingObject(NativeBindingInfo *info) JSTaggedType res = JSNApiHelper::ToJSHandle(attachVal).GetTaggedType(); slot.Update(res); if (!root) { - UpdateIfExistOldToNew(res, slot); + UpdateBarrier(res, slot); + } +} + +void BaseDeserializer::DeserializeJSError(JSErrorInfo *info) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + uint8_t type = info->errorType_; + base::ErrorType errorType = base::ErrorType(type - static_cast(JSType::JS_ERROR_FIRST)); + JSTaggedValue errorMsg = info->errorMsg_; + ObjectSlot slot = info->slot_; + bool root = info->root_; + ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory(); + JSHandle errorTag = factory->NewJSError(errorType, JSHandle(thread_, errorMsg)); + slot.Update(errorTag.GetTaggedType()); + if (!root) { + UpdateBarrier(errorTag.GetTaggedType(), slot); } } @@ -145,21 +181,28 @@ void BaseDeserializer::HandleNewObjectEncodeFlag(SerializedObjectSpace space, Ob bool isTransferBuffer = GetAndResetTransferBuffer(); void *bufferPointer = GetAndResetBufferPointer(); ConstantPool *constpool = GetAndResetConstantPool(); - if (needNewConstPool_) { - needNewConstPool_ = false; - newConstPoolInfos_.back()->slotAddr_ = slot.SlotAddress(); - } + bool needNewConstPool = GetAndResetNeedNewConstPool(); + bool isErrorMsg = GetAndResetIsErrorMsg(); // deserialize object here uintptr_t addr = DeserializeTaggedObject(space); // deserialize object epilogue + if (isErrorMsg) { + // defer new js error + jsErrorInfos_.back()->errorMsg_ = JSTaggedValue(static_cast(addr)); + return; + } + if (isTransferBuffer) { TransferArrayBufferAttach(addr); } else if (bufferPointer != nullptr) { - ResetArrayBufferNativePointer(addr, bufferPointer); + ResetNativePointerBuffer(addr, bufferPointer); } else if (constpool != nullptr) { ResetMethodConstantPool(addr, constpool); + } else if (needNewConstPool) { + // defer new constpool + newConstPoolInfos_.back()->slotAddr_ = addr + Method::CONSTANT_POOL_OFFSET; } TaggedObject *object = reinterpret_cast(addr); if (object->GetClass()->IsJSNativePointer()) { @@ -167,10 +210,13 @@ void BaseDeserializer::HandleNewObjectEncodeFlag(SerializedObjectSpace space, Ob if (nativePointer->GetDeleter() != nullptr) { thread_->GetEcmaVM()->PushToNativePointerList(nativePointer); } + } else if (object->GetClass()->IsJSFunction()) { + // defer initialize concurrent function until constpool is set + concurrentFunctions_.push_back(reinterpret_cast(object)); } UpdateMaybeWeak(slot, addr, isWeak); if (!isRoot) { - UpdateIfExistOldToNew(addr, slot); + UpdateBarrier(addr, slot); } } @@ -202,14 +248,25 @@ void BaseDeserializer::TransferArrayBufferAttach(uintptr_t objAddr) arrayBuffer->Attach(thread_, arrayLength, JSTaggedValue(np), withNativeAreaAllocator); } -void BaseDeserializer::ResetArrayBufferNativePointer(uintptr_t objAddr, void *bufferPointer) +void BaseDeserializer::ResetNativePointerBuffer(uintptr_t objAddr, void *bufferPointer) { - ASSERT(JSTaggedValue(static_cast(objAddr)).IsArrayBuffer()); - JSArrayBuffer *arrayBuffer = reinterpret_cast(objAddr); - arrayBuffer->SetWithNativeAreaAllocator(true); - JSNativePointer *np = reinterpret_cast(arrayBuffer->GetArrayBufferData().GetTaggedObject()); + JSTaggedValue obj = JSTaggedValue(static_cast(objAddr)); + ASSERT(obj.IsArrayBuffer() || obj.IsJSRegExp()); + auto nativeAreaAllocator = thread_->GetEcmaVM()->GetNativeAreaAllocator(); + JSNativePointer *np = nullptr; + if (obj.IsArrayBuffer()) { + JSArrayBuffer *arrayBuffer = reinterpret_cast(objAddr); + arrayBuffer->SetWithNativeAreaAllocator(true); + np = reinterpret_cast(arrayBuffer->GetArrayBufferData().GetTaggedObject()); + nativeAreaAllocator->IncreaseNativeSizeStats(arrayBuffer->GetArrayBufferByteLength(), NativeFlag::ARRAY_BUFFER); + } else { + JSRegExp *jsRegExp = reinterpret_cast(objAddr); + np = reinterpret_cast(jsRegExp->GetByteCodeBuffer().GetTaggedObject()); + nativeAreaAllocator->IncreaseNativeSizeStats(jsRegExp->GetLength(), NativeFlag::REGEXP_BTYECODE); + } + np->SetExternalPointer(bufferPointer); - np->SetDeleter(&NativeAreaAllocator::FreeBufferFunc); + np->SetDeleter(NativeAreaAllocator::FreeBufferFunc); np->SetData(thread_->GetEcmaVM()->GetNativeAreaAllocator()); } @@ -220,9 +277,10 @@ void BaseDeserializer::ResetMethodConstantPool(uintptr_t objAddr, ConstantPool * method->SetConstantPool(thread_, JSTaggedValue(constpool), BarrierMode::SKIP_BARRIER); } -size_t BaseDeserializer::ReadSingleEncodeData(uint8_t encodeFlag, ObjectSlot slot, bool isRoot) +size_t BaseDeserializer::ReadSingleEncodeData(uint8_t encodeFlag, uintptr_t addr, bool isRoot) { - size_t handledFieldLength = SINGLE_FILED_LENGTH; + size_t handledFieldSize = sizeof(JSTaggedType); + ObjectSlot slot(addr); switch (encodeFlag) { case NEW_OBJECT_ALL_SPACES(): { SerializedObjectSpace space = SerializeData::DecodeSpace(encodeFlag); @@ -231,15 +289,15 @@ size_t BaseDeserializer::ReadSingleEncodeData(uint8_t encodeFlag, ObjectSlot slo } case (uint8_t)EncodeFlag::REFERENCE: { uint32_t objIndex = data_->ReadUint32(); - uintptr_t addr = objectVector_[objIndex]; - UpdateMaybeWeak(slot, addr, GetAndResetWeak()); - UpdateIfExistOldToNew(addr, slot); + uintptr_t objAddr = objectVector_[objIndex]; + UpdateMaybeWeak(slot, objAddr, GetAndResetWeak()); + UpdateBarrier(objAddr, slot); break; } case (uint8_t)EncodeFlag::WEAK: { ASSERT(!isWeak_); isWeak_ = true; - handledFieldLength = 0; + handledFieldSize = 0; break; } case (uint8_t)EncodeFlag::PRIMITIVE: { @@ -248,47 +306,47 @@ size_t BaseDeserializer::ReadSingleEncodeData(uint8_t encodeFlag, ObjectSlot slo break; } case (uint8_t)EncodeFlag::MULTI_RAW_DATA: { - uint32_t length = data_->ReadUint32(); - data_->ReadRawData(slot.SlotAddress(), sizeof(JSTaggedType) * length); - handledFieldLength = length; + uint32_t size = data_->ReadUint32(); + data_->ReadRawData(addr, size); + handledFieldSize = size; break; } case (uint8_t)EncodeFlag::ROOT_OBJECT: { uint32_t index = data_->ReadUint32(); - uintptr_t addr = thread_->GetEcmaVM()->GetSnapshotEnv()->RelocateRootObjectAddr(index); + uintptr_t objAddr = thread_->GetEcmaVM()->GetSnapshotEnv()->RelocateRootObjectAddr(index); if (!isRoot) { - UpdateIfExistOldToNew(addr, slot); + UpdateBarrier(objAddr, slot); } - UpdateMaybeWeak(slot, addr, GetAndResetWeak()); + UpdateMaybeWeak(slot, objAddr, GetAndResetWeak()); break; } case (uint8_t)EncodeFlag::OBJECT_PROTO: { uint8_t type = data_->ReadUint8(); - uintptr_t addr = RelocateObjectProtoAddr(type); + uintptr_t protoAddr = RelocateObjectProtoAddr(type); if (!isRoot) { - UpdateIfExistOldToNew(addr, slot); + UpdateBarrier(protoAddr, slot); } - UpdateMaybeWeak(slot, addr, GetAndResetWeak()); + UpdateMaybeWeak(slot, protoAddr, GetAndResetWeak()); break; } case (uint8_t)EncodeFlag::TRANSFER_ARRAY_BUFFER: { isTransferArrayBuffer_ = true; - handledFieldLength = 0; + handledFieldSize = 0; break; } - case (uint8_t)EncodeFlag::ARRAY_BUFFER: { + case (uint8_t)EncodeFlag::ARRAY_BUFFER: + case (uint8_t)EncodeFlag::JS_REG_EXP: { size_t bufferLength = data_->ReadUint32(); auto nativeAreaAllocator = thread_->GetEcmaVM()->GetNativeAreaAllocator(); bufferPointer_ = nativeAreaAllocator->AllocateBuffer(bufferLength); data_->ReadRawData(ToUintPtr(bufferPointer_), bufferLength); - nativeAreaAllocator->IncreaseNativeSizeStats(bufferLength, NativeFlag::ARRAY_BUFFER); heap_->IncreaseNativeBindingSize(bufferLength); - handledFieldLength = 0; + handledFieldSize = 0; break; } case (uint8_t)EncodeFlag::METHOD: { HandleMethodEncodeFlag(); - handledFieldLength = 0; + handledFieldSize = 0; break; } case (uint8_t)EncodeFlag::NATIVE_BINDING_OBJECT: { @@ -301,24 +359,43 @@ size_t BaseDeserializer::ReadSingleEncodeData(uint8_t encodeFlag, ObjectSlot slo nativeBindingInfos_.push_back(new NativeBindingInfo(af, bufferPointer, hint, attachData, slot, isRoot)); break; } + case (uint8_t)EncodeFlag::JS_ERROR: { + uint8_t type = data_->ReadUint8(); + ASSERT(type >= static_cast(JSType::JS_ERROR_FIRST) + && type <= static_cast(JSType::JS_ERROR_LAST)); + jsErrorInfos_.push_back(new JSErrorInfo(type, JSTaggedValue::Undefined(), slot, isRoot)); + uint8_t flag = data_->ReadUint8(); + if (flag == 1) { // error msg is string + isErrorMsg_ = true; + handledFieldSize = 0; + } + break; + } default: LOG_ECMA(FATAL) << "this branch is unreachable"; UNREACHABLE(); break; } - return handledFieldLength; + return handledFieldSize; } -void BaseDeserializer::UpdateIfExistOldToNew(uintptr_t addr, ObjectSlot slot) +void BaseDeserializer::UpdateBarrier(uintptr_t addr, ObjectSlot slot) { Region *valueRegion = Region::ObjectAddressToRange(addr); + if (valueRegion == nullptr) { + return; + } + Region *rootRegion = Region::ObjectAddressToRange(slot.SlotAddress()); // root region is impossible in young space when deserialize - if (valueRegion != nullptr && valueRegion->InYoungSpace()) { + if (valueRegion->InYoungSpace()) { // Should align with '8' in 64 and 32 bit platform ASSERT(slot.SlotAddress() % static_cast(MemAlignment::MEM_ALIGN_OBJECT) == 0); - Region *rootRegion = Region::ObjectAddressToRange(slot.SlotAddress()); rootRegion->InsertOldToNewRSet(slot.SlotAddress()); } + + if (valueRegion->IsMarking()) { + Barriers::Update(slot.SlotAddress(), rootRegion, reinterpret_cast(addr), valueRegion); + } } uintptr_t BaseDeserializer::RelocateObjectAddr(SerializedObjectSpace space, size_t objSize) diff --git a/ecmascript/serializer/base_deserializer.h b/ecmascript/serializer/base_deserializer.h index 99b5a5a0ad..7cbfbbffa6 100644 --- a/ecmascript/serializer/base_deserializer.h +++ b/ecmascript/serializer/base_deserializer.h @@ -42,6 +42,17 @@ struct NativeBindingInfo { NativeBindingInfo(AttachFunc af, void *bufferPointer, void *hint, void *attachData, ObjectSlot slot, bool root) : af_(af), bufferPointer_(bufferPointer), hint_(hint), attachData_(attachData), slot_(slot), root_(root) {} }; + +struct JSErrorInfo { + uint8_t errorType_ {0}; + JSTaggedValue errorMsg_; + ObjectSlot slot_; + bool root_ {false}; + + JSErrorInfo(uint8_t errorType, JSTaggedValue errorMsg, ObjectSlot slot, bool root) + : errorType_(errorType), errorMsg_(errorMsg), slot_(slot), root_(root) {} +}; + class BaseDeserializer { public: explicit BaseDeserializer(JSThread *thread, SerializeData *data, void *hint = nullptr) @@ -63,15 +74,16 @@ private: uintptr_t DeserializeTaggedObject(SerializedObjectSpace space); void DeserializeConstPool(NewConstPoolInfo *info); void DeserializeNativeBindingObject(NativeBindingInfo *info); + void DeserializeJSError(JSErrorInfo *info); uintptr_t RelocateObjectAddr(SerializedObjectSpace space, size_t objSize); JSTaggedType RelocateObjectProtoAddr(uint8_t objectType); - void DeserializeObjectField(ObjectSlot start, ObjectSlot end); - size_t ReadSingleEncodeData(uint8_t encodeFlag, ObjectSlot slot, bool isRoot = false); + void DeserializeObjectField(uintptr_t start, uintptr_t end); + size_t ReadSingleEncodeData(uint8_t encodeFlag, uintptr_t addr, bool isRoot = false); void HandleNewObjectEncodeFlag(SerializedObjectSpace space, ObjectSlot slot, bool isRoot); void HandleMethodEncodeFlag(); void TransferArrayBufferAttach(uintptr_t objAddr); - void ResetArrayBufferNativePointer(uintptr_t objAddr, void *bufferPointer); + void ResetNativePointerBuffer(uintptr_t objAddr, void *bufferPointer); void ResetMethodConstantPool(uintptr_t objAddr, ConstantPool *constpool); void AllocateToDifferentSpaces(); @@ -79,7 +91,7 @@ private: void AllocateToOldSpace(size_t oldSpaceSize); void AllocateToNonMovableSpace(size_t nonMovableSpaceSize); void AllocateToMachineCodeSpace(size_t machineCodeSpaceSize); - void UpdateIfExistOldToNew(uintptr_t addr, ObjectSlot slot); + void UpdateBarrier(uintptr_t addr, ObjectSlot slot); bool GetAndResetWeak() { @@ -92,11 +104,29 @@ private: bool GetAndResetTransferBuffer() { + bool isTransferArrayBuffer = isTransferArrayBuffer_; if (isTransferArrayBuffer_) { isTransferArrayBuffer_ = false; - return true; } - return false; + return isTransferArrayBuffer; + } + + bool GetAndResetNeedNewConstPool() + { + bool needNewConstPool = needNewConstPool_; + if (needNewConstPool_) { + needNewConstPool_ = false; + } + return needNewConstPool; + } + + bool GetAndResetIsErrorMsg() + { + bool isErrorMsg = isErrorMsg_; + if (isErrorMsg_) { + isErrorMsg_ = false; + } + return isErrorMsg; } void *GetAndResetBufferPointer() @@ -125,7 +155,6 @@ private: } private: - static constexpr size_t SINGLE_FILED_LENGTH = 1; JSThread *thread_; Heap *heap_; std::unique_ptr data_; @@ -141,11 +170,14 @@ private: size_t regionRemainSizeIndex_ {0}; bool isWeak_ {false}; bool isTransferArrayBuffer_ {false}; + bool isErrorMsg_ {false}; void *bufferPointer_ {nullptr}; ConstantPool *constpool_ {nullptr}; bool needNewConstPool_ {false}; CVector newConstPoolInfos_; CVector nativeBindingInfos_; + CVector jsErrorInfos_; + CVector concurrentFunctions_; }; } diff --git a/ecmascript/serializer/base_serializer.cpp b/ecmascript/serializer/base_serializer.cpp index ae27914349..a0a2b2c225 100644 --- a/ecmascript/serializer/base_serializer.cpp +++ b/ecmascript/serializer/base_serializer.cpp @@ -44,7 +44,7 @@ void BaseSerializer::WriteMultiRawData(uintptr_t beginAddr, size_t fieldSize) { if (fieldSize > 0) { data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA); - data_->WriteUint32(fieldSize / sizeof(JSTaggedType)); + data_->WriteUint32(fieldSize); data_->WriteRawData(reinterpret_cast(beginAddr), fieldSize); } } @@ -158,6 +158,13 @@ void BaseSerializer::SerializeAsyncFunctionFieldIndividually(TaggedObject *root, while (slot < end) { size_t fieldOffset = slot.SlotAddress() - ToUintPtr(root); switch (fieldOffset) { + // hash filed + case sizeof(TaggedObject): { + data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE); + data_->WriteJSTaggedValue(JSTaggedValue(0)); // 0: reset hash filed + slot++; + break; + } case JSFunction::PROTO_OR_DYNCLASS_OFFSET: case JSFunction::LEXICAL_ENV_OFFSET: case JSFunction::HOME_OBJECT_OFFSET: { @@ -167,8 +174,9 @@ void BaseSerializer::SerializeAsyncFunctionFieldIndividually(TaggedObject *root, break; } case JSFunction::WORK_NODE_POINTER_OFFSET: { - data_->WriteEncodeFlag(EncodeFlag::PRIMITIVE); - data_->WriteJSTaggedType(0U); + data_->WriteEncodeFlag(EncodeFlag::MULTI_RAW_DATA); + data_->WriteUint32(sizeof(uintptr_t)); + data_->WriteRawData(reinterpret_cast(slot.SlotAddress()), sizeof(uintptr_t)); slot++; break; } diff --git a/ecmascript/serializer/serialize_data.h b/ecmascript/serializer/serialize_data.h index 671eb63716..bdbc67f461 100644 --- a/ecmascript/serializer/serialize_data.h +++ b/ecmascript/serializer/serialize_data.h @@ -29,16 +29,18 @@ enum class EncodeFlag : uint8_t { // 0x03: huge space NEW_OBJECT = 0x00, REFERENCE = 0x04, - WEAK = 0x05, - PRIMITIVE = 0x06, - MULTI_RAW_DATA = 0x07, - ROOT_OBJECT = 0x08, - OBJECT_PROTO = 0X09, - ARRAY_BUFFER = 0x0a, - TRANSFER_ARRAY_BUFFER = 0X0b, - METHOD = 0x0c, - NATIVE_BINDING_OBJECT = 0x0d, - LAST = 0x0e + WEAK, + PRIMITIVE, + MULTI_RAW_DATA, + ROOT_OBJECT, + OBJECT_PROTO, + ARRAY_BUFFER, + TRANSFER_ARRAY_BUFFER, + METHOD, + NATIVE_BINDING_OBJECT, + JS_ERROR, + JS_REG_EXP, + LAST }; enum class SerializedObjectSpace : uint8_t { diff --git a/ecmascript/serializer/tests/serializer_test.cpp b/ecmascript/serializer/tests/serializer_test.cpp index d2ec5bb244..cbba6f6db3 100644 --- a/ecmascript/serializer/tests/serializer_test.cpp +++ b/ecmascript/serializer/tests/serializer_test.cpp @@ -143,6 +143,98 @@ public: Destroy(); } + void JSPlainObjectTest3(SerializeData* data) + { + Init(); + BaseDeserializer deserializer(thread, data); + JSHandle objValue = deserializer.ReadValue(); + ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC); + ecmaVm->CollectGarbage(TriggerGCType::OLD_GC); + + JSHandle retObj = JSHandle::Cast(objValue); + EXPECT_FALSE(retObj.IsEmpty()); + EXPECT_TRUE(retObj->GetClass()->IsDictionaryMode()); + + JSHandle array = JSObject::GetOwnPropertyKeys(thread, retObj); + uint32_t length = array->GetLength(); + EXPECT_EQ(length, 1030U); + for (uint32_t i = 0; i < length; i++) { + JSHandle key(thread, array->Get(i)); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(retObj), key).GetValue(); + EXPECT_TRUE(value->IsInt()); + } + + Destroy(); + } + + void JSPlainObjectTest4(SerializeData* data) + { + Init(); + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle key(factory->NewFromASCII("str1")); + + BaseDeserializer deserializer(thread, data); + JSHandle objValue = deserializer.ReadValue(); + ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC); + ecmaVm->CollectGarbage(TriggerGCType::OLD_GC); + + JSHandle retObj = JSHandle::Cast(objValue); + EXPECT_FALSE(retObj.IsEmpty()); + + JSHandle value = + JSObject::GetProperty(thread, JSHandle(retObj), key).GetValue(); + EXPECT_TRUE(value->IsTaggedArray()); + TaggedArray *array = reinterpret_cast(value->GetTaggedObject()); + size_t length = array->GetLength(); + EXPECT_EQ(length, 102400U); // 102400: array length + for (uint32_t i = 0; i < length; i++) { + EXPECT_TRUE(array->Get(i).IsHole()); + } + + Destroy(); + } + + void JSErrorTest1(SerializeData* data) + { + Init(); + + BaseDeserializer deserializer(thread, data); + JSHandle objValue = deserializer.ReadValue(); + ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC); + ecmaVm->CollectGarbage(TriggerGCType::OLD_GC); + + EXPECT_FALSE(objValue.IsEmpty()); + EXPECT_TRUE(objValue->IsJSError()); + + Destroy(); + } + + void JSErrorTest2(SerializeData* data) + { + Init(); + + BaseDeserializer deserializer(thread, data); + JSHandle objValue = deserializer.ReadValue(); + ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC); + ecmaVm->CollectGarbage(TriggerGCType::OLD_GC); + + JSHandle retObj = JSHandle::Cast(objValue); + EXPECT_FALSE(retObj.IsEmpty()); + + JSHandle array = JSObject::GetOwnPropertyKeys(thread, retObj); + uint32_t length = array->GetLength(); + EXPECT_EQ(length, 2U); + for (uint32_t i = 0; i < length; i++) { + JSHandle key(thread, array->Get(i)); + JSHandle value = + JSObject::GetProperty(thread, JSHandle(retObj), key).GetValue(); + EXPECT_TRUE(value->IsJSError()); + } + + Destroy(); + } + void BigIntTest(SerializeData* data) { Init(); @@ -421,6 +513,8 @@ public: Init(); JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2"); JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromASCII("i"); + char buffer[] = "1234567"; // use char buffer to simulate byteCodeBuffer + uint32_t bufferSize = 7; BaseDeserializer deserializer(thread, data); JSHandle res = deserializer.ReadValue(); @@ -428,12 +522,22 @@ public: EXPECT_TRUE(res->IsJSRegExp()) << "[NotJSRegexp] Deserialize JSRegExp fail"; JSHandle resJSRegexp(res); + uint32_t resBufferSize = resJSRegexp->GetLength(); + EXPECT_TRUE(resBufferSize == bufferSize) << "Not Same Length"; JSHandle originalSource(thread, resJSRegexp->GetOriginalSource()); EXPECT_TRUE(originalSource->IsString()); JSHandle originalFlags(thread, resJSRegexp->GetOriginalFlags()); EXPECT_TRUE(originalFlags->IsString()); EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(*JSHandle(originalSource), *pattern)); EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(*JSHandle(originalFlags), *flags)); + JSHandle resBufferData(thread, resJSRegexp->GetByteCodeBuffer()); + JSHandle resNp = JSHandle::Cast(resBufferData); + void *resBuffer = resNp->GetExternalPointer(); + ASSERT_NE(resBuffer, nullptr); + + for (uint32_t i = 0; i < resBufferSize; i++) { + EXPECT_TRUE(static_cast(resBuffer)[i] == buffer[i]) << "Not Same ByteCode"; + } Destroy(); } @@ -709,7 +813,7 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject2) } key2 = JSHandle(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1)); JSObject::SetProperty(thread, JSHandle(obj), JSHandle(key2), - JSHandle(obj1)); + JSHandle(obj1)); } ValueSerializer *serializer = new ValueSerializer(thread); @@ -721,6 +825,93 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject2) delete serializer; }; +// test dictionary mode +HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject3) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj = factory->NewEmptyJSObject(); + JSHandle key1(factory->NewFromASCII("str1")); + JSHandle key2(factory->NewFromASCII("str2")); + JSHandle value1(thread, JSTaggedValue(1)); + for (int i = 0; i < 1030; i++) { + key2 = JSHandle(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1)); + JSObject::SetProperty(thread, JSHandle(obj), JSHandle(key2), value1); + } + + EXPECT_TRUE(obj->GetClass()->IsDictionaryMode()); + + ValueSerializer *serializer = new ValueSerializer(thread); + serializer->WriteValue(thread, JSHandle(obj), JSHandle(obj)); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSPlainObjectTest3, jsDeserializerTest, data.release()); + t1.join(); + delete serializer; +}; + +// test huge object serialize +HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject4) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj = factory->NewEmptyJSObject(); + JSHandle key1(factory->NewFromASCII("str1")); + // new huge tagged array + JSHandle taggedArray = + factory->NewTaggedArray(1024 * 100, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE); + + JSObject::SetProperty(thread, JSHandle(obj), JSHandle(key1), + JSHandle(taggedArray)); + + ValueSerializer *serializer = new ValueSerializer(thread); + serializer->WriteValue(thread, JSHandle(obj), JSHandle(obj)); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSPlainObjectTest4, jsDeserializerTest, data.release()); + t1.join(); + delete serializer; +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSError1) +{ + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle msg(factory->NewFromASCII("this is error")); + JSHandle errorTag = + JSHandle::Cast(factory->NewJSError(base::ErrorType::ERROR, msg)); + + ValueSerializer *serializer = new ValueSerializer(thread); + serializer->WriteValue(thread, errorTag, errorTag); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSErrorTest1, jsDeserializerTest, data.release()); + t1.join(); + delete serializer; +}; + +HWTEST_F_L0(JSSerializerTest, SerializeJSError2) +{ +#ifdef NDEBUG + ObjectFactory *factory = ecmaVm->GetFactory(); + JSHandle obj = factory->NewEmptyJSObject(); + JSHandle key1(factory->NewFromASCII("error1")); + JSHandle key2(factory->NewFromASCII("error2")); + JSHandle msg(factory->NewFromASCII("this is error")); + JSHandle errorTag = + JSHandle::Cast(factory->NewJSError(base::ErrorType::ERROR, msg)); + + + JSObject::SetProperty(thread, JSHandle(obj), JSHandle(key1), errorTag); + JSObject::SetProperty(thread, JSHandle(obj), JSHandle(key2), errorTag); + + ValueSerializer *serializer = new ValueSerializer(thread); + serializer->WriteValue(thread, JSHandle(obj), JSHandle(obj)); + std::unique_ptr data = serializer->Release(); + JSDeserializerTest jsDeserializerTest; + std::thread t1(&JSDeserializerTest::JSErrorTest2, jsDeserializerTest, data.release()); + t1.join(); + delete serializer; +#endif +}; + HWTEST_F_L0(JSSerializerTest, SerializeBigInt) { ObjectFactory *factory = ecmaVm->GetFactory(); @@ -938,7 +1129,9 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSRegExp) JSHandle::Cast(factory->NewJSObjectByConstructor(JSHandle(target), target)); JSHandle pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2"); JSHandle flags = thread->GetEcmaVM()->GetFactory()->NewFromASCII("i"); - + char buffer[] = "1234567"; // use char to simulate bytecode + uint32_t bufferSize = 7; + factory->NewJSRegExpByteCodeData(jsRegexp, static_cast(buffer), bufferSize); jsRegexp->SetOriginalSource(thread, JSHandle(pattern)); jsRegexp->SetOriginalFlags(thread, JSHandle(flags)); diff --git a/ecmascript/serializer/value_serializer.cpp b/ecmascript/serializer/value_serializer.cpp index 543848932a..486e018c7b 100644 --- a/ecmascript/serializer/value_serializer.cpp +++ b/ecmascript/serializer/value_serializer.cpp @@ -65,6 +65,7 @@ bool ValueSerializer::CheckObjectCanSerialize(TaggedObject *object) default: break; } + LOG_ECMA(ERROR) << "Unsupport serialize object type: " << JSHClass::DumpJSType(type); return false; } @@ -99,6 +100,7 @@ bool ValueSerializer::WriteValue(JSThread *thread, const JSHandle vm_->GetSnapshotEnv()->ClearEnvMap(); } if (notSupport_) { + LOG_ECMA(ERROR) << "ValueSerialize: serialize data is incomplete"; data_->SetIncompleteData(true); return false; } @@ -124,8 +126,14 @@ void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak) SerializeNativeBindingObject(object); return; } - JSType type = object->GetClass()->GetObjectType(); + if (object->GetClass()->IsJSError()) { + SerializeJSError(object); + return; + } bool arrayBufferDeferDetach = false; + JSTaggedValue trackInfo; + JSType type = object->GetClass()->GetObjectType(); + // serialize prologue switch (type) { case JSType::JS_ARRAY_BUFFER: arrayBufferDeferDetach = SerializeJSArrayBufferPrologue(object); @@ -136,10 +144,31 @@ void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak) case JSType::METHOD: SerializeMethodPrologue(reinterpret_cast(object)); break; + case JSType::JS_ARRAY: { + JSArray *array = reinterpret_cast(object); + trackInfo = array->GetTrackInfo(); + array->SetTrackInfo(thread_, JSTaggedValue::Undefined()); + break; + } + case JSType::TREE_STRING: + case JSType::SLICED_STRING: + object = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(object)); + break; + case JSType::JS_REG_EXP: + SerializeJSRegExpPrologue(reinterpret_cast(object)); + break; default: break; } + + // serialize object here SerializeTaggedObject(object); + + // serialize epilogue + if (type == JSType::JS_ARRAY) { + JSArray *array = reinterpret_cast(object); + array->SetTrackInfo(thread_, trackInfo); + } if (arrayBufferDeferDetach) { ASSERT(object->GetClass()->IsArrayBuffer()); JSArrayBuffer *arrayBuffer = reinterpret_cast(object); @@ -147,6 +176,26 @@ void ValueSerializer::SerializeObjectImpl(TaggedObject *object, bool isWeak) } } +void ValueSerializer::SerializeJSError(TaggedObject *object) +{ + [[maybe_unused]] EcmaHandleScope scope(thread_); + data_->WriteEncodeFlag(EncodeFlag::JS_ERROR); + JSType type = object->GetClass()->GetObjectType(); + ASSERT(type >= JSType::JS_ERROR_FIRST && type <= JSType::JS_ERROR_LAST); + data_->WriteUint8(static_cast(type)); + auto globalConst = thread_->GlobalConstants(); + JSHandle handleMsg = globalConst->GetHandledMessageString(); + JSHandle msg = + JSObject::GetProperty(thread_, JSHandle(thread_, object), handleMsg).GetValue(); + if (msg->IsString()) { + EcmaString *str = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(msg->GetTaggedObject())); + data_->WriteUint8(1); // 1: msg is string + SerializeTaggedObject(str); + } else { + data_->WriteUint8(0); // 0: msg is undefined + } +} + void ValueSerializer::SerializeNativeBindingObject(TaggedObject *object) { [[maybe_unused]] EcmaHandleScope scope(thread_); @@ -247,6 +296,22 @@ void ValueSerializer::SerializeMethodPrologue(Method *method) data_->WriteJSTaggedType(reinterpret_cast(jsPandaFile)); } +void ValueSerializer::SerializeJSRegExpPrologue(JSRegExp *jsRegExp) +{ + uint32_t bufferSize = jsRegExp->GetLength(); + if (bufferSize == 0) { + LOG_ECMA(ERROR) << "ValueSerialize: JSRegExp buffer size is 0"; + notSupport_ = true; + return; + } + + data_->WriteEncodeFlag(EncodeFlag::JS_REG_EXP); + data_->WriteUint32(bufferSize); + JSNativePointer *np = + reinterpret_cast(jsRegExp->GetByteCodeBuffer().GetTaggedObject()); + data_->WriteRawData(static_cast(np->GetExternalPointer()), bufferSize); +} + bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle &transfer) { if (transfer->IsUndefined()) { diff --git a/ecmascript/serializer/value_serializer.h b/ecmascript/serializer/value_serializer.h index 5c31d9a077..7305a56450 100644 --- a/ecmascript/serializer/value_serializer.h +++ b/ecmascript/serializer/value_serializer.h @@ -31,10 +31,12 @@ public: private: void SerializeObjectImpl(TaggedObject *object, bool isWeak = false) override; + void SerializeJSError(TaggedObject *object); void SerializeNativeBindingObject(TaggedObject *object); bool SerializeJSArrayBufferPrologue(TaggedObject *object); void SerializeJSSharedArrayBufferPrologue(TaggedObject *object); void SerializeMethodPrologue(Method *method); + void SerializeJSRegExpPrologue(JSRegExp *jsRegExp); void InitTransferSet(CUnorderedSet transferDataSet); void ClearTransferSet(); bool PrepareTransfer(JSThread *thread, const JSHandle &transfer); @@ -42,7 +44,10 @@ private: bool IsInternalJSType(JSType type) { - return type >= JSType::HCLASS && type <= JSType::TYPE_LAST; + if (type >= JSType::JS_RECORD_FIRST && type <= JSType::JS_RECORD_LAST) { + return false; + } + return type >= JSType::HCLASS && type <= JSType::TYPE_LAST && type != JSType::SYMBOL; } private: