!5456 Enable value serialization

Merge pull request !5456 from dingwen/master
This commit is contained in:
openharmony_ci 2023-12-10 11:02:44 +00:00 committed by Gitee
commit f352b9e4ad
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
14 changed files with 482 additions and 74 deletions

View File

@ -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,
};

View File

@ -870,6 +870,7 @@ void Builtins::LazyInitializeDate(const JSHandle<GlobalEnv> &env) const
auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast<void *>(BuiltinsLazyCallback::Date));
SetLazyAccessor(globalObject, key, accessor);
env->SetDateFunction(thread_, accessor);
env->SetDatePrototype(thread_, accessor);
}
void Builtins::InitializeBoolean(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &primRefObjHClass) const
@ -1271,6 +1272,8 @@ void Builtins::LazyInitializeSet(const JSHandle<GlobalEnv> &env)
auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast<void *>(BuiltinsLazyCallback::Set));
SetLazyAccessor(globalObject, key, accessor);
env->SetBuiltinsSetFunction(thread_, accessor);
env->SetSetPrototype(thread_, accessor);
env->SetSetProtoValuesFunction(thread_, accessor);
}
void Builtins::InitializeMap(const JSHandle<GlobalEnv> &env, JSHandle<JSTaggedValue> objFuncPrototypeVal) const
@ -1341,6 +1344,7 @@ void Builtins::LazyInitializeMap(const JSHandle<GlobalEnv> &env) const
SetLazyAccessor(globalObject, key, accessor);
env->SetBuiltinsMapFunction(thread_, accessor);
env->SetMapPrototype(thread_, accessor);
env->SetMapProtoEntriesFunction(thread_, accessor);
}
void Builtins::InitializeWeakMap(const JSHandle<GlobalEnv> &env, const JSHandle<JSHClass> &objFuncClass) const
@ -2149,6 +2153,7 @@ void Builtins::LazyInitialize##Type(const JSHandle<GlobalEnv> &env) const
auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast<void *>(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)
@ -2460,6 +2465,7 @@ void Builtins::LazyInitializeDataView(const JSHandle<GlobalEnv> &env) const
auto accessor = factory_->NewInternalAccessor(nullptr, reinterpret_cast<void *>(BuiltinsLazyCallback::DataView));
SetLazyAccessor(globalObject, key, accessor);
env->SetDataViewFunction(thread_, accessor);
env->SetDataViewPrototype(thread_, accessor);
}
JSHandle<JSFunction> Builtins::NewBuiltinConstructor(const JSHandle<GlobalEnv> &env,

View File

@ -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); \

View File

@ -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);

View File

@ -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;
}

View File

@ -1258,6 +1258,11 @@ void Heap::NotifyHighSensitive(bool isStart)
bool Heap::NeedStopCollection()
{
// gc is not allowed during value serialize
if (onSerializeEvent_) {
return true;
}
if (!InSensitiveStatus()) {
return false;
}

View File

@ -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};

View File

@ -50,9 +50,13 @@ JSHandle<JSTaggedValue> BaseDeserializer::DeserializeJSTaggedValue()
LOG_ECMA(ERROR) << "The serialization data is incomplete";
return JSHandle<JSTaggedValue>();
}
// 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<JSTaggedValue> 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<JSTaggedValue> BaseDeserializer::DeserializeJSTaggedValue()
}
nativeBindingInfos_.clear();
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue(static_cast<JSTaggedType>(result)));
// new js error here
for (auto jsErrorInfo : jsErrorInfos_) {
DeserializeJSError(jsErrorInfo);
delete jsErrorInfo;
}
jsErrorInfos_.clear();
// recovery gc after serialize
heap_->SetOnSerializeEvent(false);
return JSHandle<JSTaggedValue>(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<TaggedObject *>(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<int32_t>(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<uint8_t>(JSType::JS_ERROR_FIRST));
JSTaggedValue errorMsg = info->errorMsg_;
ObjectSlot slot = info->slot_;
bool root = info->root_;
ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
JSHandle<JSObject> errorTag = factory->NewJSError(errorType, JSHandle<EcmaString>(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<JSTaggedType>(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<TaggedObject *>(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<JSFunction *>(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<JSTaggedType>(objAddr)).IsArrayBuffer());
JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(objAddr);
arrayBuffer->SetWithNativeAreaAllocator(true);
JSNativePointer *np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
JSTaggedValue obj = JSTaggedValue(static_cast<JSTaggedType>(objAddr));
ASSERT(obj.IsArrayBuffer() || obj.IsJSRegExp());
auto nativeAreaAllocator = thread_->GetEcmaVM()->GetNativeAreaAllocator();
JSNativePointer *np = nullptr;
if (obj.IsArrayBuffer()) {
JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(objAddr);
arrayBuffer->SetWithNativeAreaAllocator(true);
np = reinterpret_cast<JSNativePointer *>(arrayBuffer->GetArrayBufferData().GetTaggedObject());
nativeAreaAllocator->IncreaseNativeSizeStats(arrayBuffer->GetArrayBufferByteLength(), NativeFlag::ARRAY_BUFFER);
} else {
JSRegExp *jsRegExp = reinterpret_cast<JSRegExp *>(objAddr);
np = reinterpret_cast<JSNativePointer *>(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<uint8_t>(JSType::JS_ERROR_FIRST)
&& type <= static_cast<uint8_t>(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<uint8_t>(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<TaggedObject *>(addr), valueRegion);
}
}
uintptr_t BaseDeserializer::RelocateObjectAddr(SerializedObjectSpace space, size_t objSize)

View File

@ -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<SerializeData> 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<NewConstPoolInfo *> newConstPoolInfos_;
CVector<NativeBindingInfo *> nativeBindingInfos_;
CVector<JSErrorInfo *> jsErrorInfos_;
CVector<JSFunction *> concurrentFunctions_;
};
}

View File

@ -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<uint8_t *>(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<uint8_t *>(slot.SlotAddress()), sizeof(uintptr_t));
slot++;
break;
}

View File

@ -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 {

View File

@ -143,6 +143,98 @@ public:
Destroy();
}
void JSPlainObjectTest3(SerializeData* data)
{
Init();
BaseDeserializer deserializer(thread, data);
JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
EXPECT_FALSE(retObj.IsEmpty());
EXPECT_TRUE(retObj->GetClass()->IsDictionaryMode());
JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
uint32_t length = array->GetLength();
EXPECT_EQ(length, 1030U);
for (uint32_t i = 0; i < length; i++) {
JSHandle<JSTaggedValue> key(thread, array->Get(i));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
EXPECT_TRUE(value->IsInt());
}
Destroy();
}
void JSPlainObjectTest4(SerializeData* data)
{
Init();
ObjectFactory *factory = ecmaVm->GetFactory();
JSHandle<JSTaggedValue> key(factory->NewFromASCII("str1"));
BaseDeserializer deserializer(thread, data);
JSHandle<JSTaggedValue> objValue = deserializer.ReadValue();
ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
EXPECT_FALSE(retObj.IsEmpty());
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
EXPECT_TRUE(value->IsTaggedArray());
TaggedArray *array = reinterpret_cast<TaggedArray *>(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<JSTaggedValue> 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<JSTaggedValue> objValue = deserializer.ReadValue();
ecmaVm->CollectGarbage(TriggerGCType::YOUNG_GC);
ecmaVm->CollectGarbage(TriggerGCType::OLD_GC);
JSHandle<JSObject> retObj = JSHandle<JSObject>::Cast(objValue);
EXPECT_FALSE(retObj.IsEmpty());
JSHandle<TaggedArray> array = JSObject::GetOwnPropertyKeys(thread, retObj);
uint32_t length = array->GetLength();
EXPECT_EQ(length, 2U);
for (uint32_t i = 0; i < length; i++) {
JSHandle<JSTaggedValue> key(thread, array->Get(i));
JSHandle<JSTaggedValue> value =
JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(retObj), key).GetValue();
EXPECT_TRUE(value->IsJSError());
}
Destroy();
}
void BigIntTest(SerializeData* data)
{
Init();
@ -421,6 +513,8 @@ public:
Init();
JSHandle<EcmaString> pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2");
JSHandle<EcmaString> flags = thread->GetEcmaVM()->GetFactory()->NewFromASCII("i");
char buffer[] = "1234567"; // use char buffer to simulate byteCodeBuffer
uint32_t bufferSize = 7;
BaseDeserializer deserializer(thread, data);
JSHandle<JSTaggedValue> res = deserializer.ReadValue();
@ -428,12 +522,22 @@ public:
EXPECT_TRUE(res->IsJSRegExp()) << "[NotJSRegexp] Deserialize JSRegExp fail";
JSHandle<JSRegExp> resJSRegexp(res);
uint32_t resBufferSize = resJSRegexp->GetLength();
EXPECT_TRUE(resBufferSize == bufferSize) << "Not Same Length";
JSHandle<JSTaggedValue> originalSource(thread, resJSRegexp->GetOriginalSource());
EXPECT_TRUE(originalSource->IsString());
JSHandle<JSTaggedValue> originalFlags(thread, resJSRegexp->GetOriginalFlags());
EXPECT_TRUE(originalFlags->IsString());
EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(*JSHandle<EcmaString>(originalSource), *pattern));
EXPECT_TRUE(EcmaStringAccessor::StringsAreEqual(*JSHandle<EcmaString>(originalFlags), *flags));
JSHandle<JSTaggedValue> resBufferData(thread, resJSRegexp->GetByteCodeBuffer());
JSHandle<JSNativePointer> resNp = JSHandle<JSNativePointer>::Cast(resBufferData);
void *resBuffer = resNp->GetExternalPointer();
ASSERT_NE(resBuffer, nullptr);
for (uint32_t i = 0; i < resBufferSize; i++) {
EXPECT_TRUE(static_cast<char *>(resBuffer)[i] == buffer[i]) << "Not Same ByteCode";
}
Destroy();
}
@ -709,7 +813,7 @@ HWTEST_F_L0(JSSerializerTest, SerializeJSPlainObject2)
}
key2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1));
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2),
JSHandle<JSTaggedValue>(obj1));
JSHandle<JSTaggedValue>(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<JSObject> obj = factory->NewEmptyJSObject();
JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
JSHandle<EcmaString> key2(factory->NewFromASCII("str2"));
JSHandle<JSTaggedValue> value1(thread, JSTaggedValue(1));
for (int i = 0; i < 1030; i++) {
key2 = JSHandle<EcmaString>(thread, EcmaStringAccessor::Concat(ecmaVm, key2, key1));
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2), value1);
}
EXPECT_TRUE(obj->GetClass()->IsDictionaryMode());
ValueSerializer *serializer = new ValueSerializer(thread);
serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(obj));
std::unique_ptr<SerializeData> 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<JSObject> obj = factory->NewEmptyJSObject();
JSHandle<EcmaString> key1(factory->NewFromASCII("str1"));
// new huge tagged array
JSHandle<TaggedArray> taggedArray =
factory->NewTaggedArray(1024 * 100, JSTaggedValue::Hole(), MemSpaceType::OLD_SPACE);
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1),
JSHandle<JSTaggedValue>(taggedArray));
ValueSerializer *serializer = new ValueSerializer(thread);
serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(obj));
std::unique_ptr<SerializeData> 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<EcmaString> msg(factory->NewFromASCII("this is error"));
JSHandle<JSTaggedValue> errorTag =
JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::ERROR, msg));
ValueSerializer *serializer = new ValueSerializer(thread);
serializer->WriteValue(thread, errorTag, errorTag);
std::unique_ptr<SerializeData> 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<JSObject> obj = factory->NewEmptyJSObject();
JSHandle<EcmaString> key1(factory->NewFromASCII("error1"));
JSHandle<EcmaString> key2(factory->NewFromASCII("error2"));
JSHandle<EcmaString> msg(factory->NewFromASCII("this is error"));
JSHandle<JSTaggedValue> errorTag =
JSHandle<JSTaggedValue>::Cast(factory->NewJSError(base::ErrorType::ERROR, msg));
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key1), errorTag);
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(key2), errorTag);
ValueSerializer *serializer = new ValueSerializer(thread);
serializer->WriteValue(thread, JSHandle<JSTaggedValue>(obj), JSHandle<JSTaggedValue>(obj));
std::unique_ptr<SerializeData> 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<JSRegExp>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(target), target));
JSHandle<EcmaString> pattern = thread->GetEcmaVM()->GetFactory()->NewFromASCII("key2");
JSHandle<EcmaString> flags = thread->GetEcmaVM()->GetFactory()->NewFromASCII("i");
char buffer[] = "1234567"; // use char to simulate bytecode
uint32_t bufferSize = 7;
factory->NewJSRegExpByteCodeData(jsRegexp, static_cast<void *>(buffer), bufferSize);
jsRegexp->SetOriginalSource(thread, JSHandle<JSTaggedValue>(pattern));
jsRegexp->SetOriginalFlags(thread, JSHandle<JSTaggedValue>(flags));

View File

@ -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<JSTaggedValue>
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<Method *>(object));
break;
case JSType::JS_ARRAY: {
JSArray *array = reinterpret_cast<JSArray *>(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<JSRegExp *>(object));
break;
default:
break;
}
// serialize object here
SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(object);
// serialize epilogue
if (type == JSType::JS_ARRAY) {
JSArray *array = reinterpret_cast<JSArray *>(object);
array->SetTrackInfo(thread_, trackInfo);
}
if (arrayBufferDeferDetach) {
ASSERT(object->GetClass()->IsArrayBuffer());
JSArrayBuffer *arrayBuffer = reinterpret_cast<JSArrayBuffer *>(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<uint8_t>(type));
auto globalConst = thread_->GlobalConstants();
JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
JSHandle<JSTaggedValue> msg =
JSObject::GetProperty(thread_, JSHandle<JSTaggedValue>(thread_, object), handleMsg).GetValue();
if (msg->IsString()) {
EcmaString *str = EcmaStringAccessor::FlattenNoGC(vm_, EcmaString::Cast(msg->GetTaggedObject()));
data_->WriteUint8(1); // 1: msg is string
SerializeTaggedObject<SerializeType::VALUE_SERIALIZE>(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<JSTaggedType>(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<JSNativePointer *>(jsRegExp->GetByteCodeBuffer().GetTaggedObject());
data_->WriteRawData(static_cast<uint8_t *>(np->GetExternalPointer()), bufferSize);
}
bool ValueSerializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
{
if (transfer->IsUndefined()) {

View File

@ -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<uintptr_t> transferDataSet);
void ClearTransferSet();
bool PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &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: