mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-10-07 16:13:49 +00:00
aa45c63e6f
issue: https://gitee.com/openharmony/commonlibrary_ets_utils/issues/I7RME9 Signed-off-by: wangzhaoyong <wangzhaoyong@huawei.com> Change-Id: Ia6b98623706533c52ff08caf40de7be5e4d8f1b1
2031 lines
67 KiB
C++
2031 lines
67 KiB
C++
/*
|
|
* 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 <stdlib.h>
|
|
#elif defined(PANDA_TARGET_MACOS)
|
|
#include <sys/malloc.h>
|
|
#else
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#include "ecmascript/base/array_helper.h"
|
|
#include "ecmascript/base/typed_array_helper-inl.h"
|
|
#include "ecmascript/base/typed_array_helper.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/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<uint8_t>(id);
|
|
return WriteRawData(&rawId, sizeof(rawId));
|
|
}
|
|
|
|
void JSSerializer::InitTransferSet(CUnorderedSet<uintptr_t> 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<JSTaggedValue> &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<JSTaggedValue> &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<uint8_t *>(malloc(INITIAL_CAPACITY));
|
|
if (buffer_ != nullptr) {
|
|
bufferCapacity_ = INITIAL_CAPACITY;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
buffer_ = reinterpret_cast<uint8_t *>(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<uint8_t *>(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<uint8_t *, size_t> 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<JSTaggedValue> &value)
|
|
{
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(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:
|
|
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:
|
|
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;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool JSSerializer::WriteBigInt(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<BigInt> bigInt = JSHandle<BigInt>::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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<TaggedArray> taggedArray = JSHandle<TaggedArray>::Cast(value);
|
|
if (!WriteType(SerializationUID::TAGGED_ARRAY)) {
|
|
return false;
|
|
}
|
|
uint32_t len = taggedArray->GetLength();
|
|
if (!WriteInt(len)) {
|
|
return false;
|
|
}
|
|
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> &value, DataViewType viewType)
|
|
{
|
|
JSHandle<ByteArray> byteArray = JSHandle<ByteArray>::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<JSTaggedValue> 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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<Method> method = JSHandle<Method>::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<ConstantPool> constPool(thread_, method->GetConstantPool());
|
|
const JSPandaFile *jsPandaFile = constPool->GetJSPandaFile();
|
|
if (jsPandaFile == nullptr) {
|
|
return false;
|
|
}
|
|
const CString &desc = jsPandaFile->GetJSPandaFileDesc();
|
|
if (!WriteString(desc)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSFunction(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
if (!WriteType(SerializationUID::CONCURRENT_FUNCTION)) {
|
|
return false;
|
|
}
|
|
JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(value);
|
|
// check concurrent function
|
|
if (func->GetFunctionKind() != ecmascript::FunctionKind::CONCURRENT_FUNCTION) {
|
|
LOG_ECMA(ERROR) << "only support serialize concurrent function";
|
|
return false;
|
|
}
|
|
JSHandle<JSTaggedValue> method(thread_, func->GetMethod());
|
|
if (!SerializeJSTaggedValue(method)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSError(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
TaggedObject *taggedObject = value->GetTaggedObject();
|
|
JSType errorType = taggedObject->GetClass()->GetObjectType();
|
|
if (!WriteJSErrorHeader(errorType)) {
|
|
return false;
|
|
}
|
|
auto globalConst = thread_->GlobalConstants();
|
|
JSHandle<JSTaggedValue> handleMsg = globalConst->GetHandledMessageString();
|
|
JSHandle<JSTaggedValue> 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);
|
|
default:
|
|
LOG_ECMA(FATAL) << "this branch is unreachable";
|
|
UNREACHABLE();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSDate(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSDate> date = JSHandle<JSDate>::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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSArray> array = JSHandle<JSArray>::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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<EcmaString> strHandle = JSHandle<EcmaString>::Cast(value);
|
|
auto string = JSHandle<EcmaString>(thread_, EcmaStringAccessor::Flatten(thread_->GetEcmaVM(), strHandle));
|
|
if (!WriteType(SerializationUID::ECMASTRING)) {
|
|
return false;
|
|
}
|
|
|
|
size_t length = EcmaStringAccessor(string).GetLength();
|
|
if (!WriteInt(static_cast<int32_t>(length))) {
|
|
return false;
|
|
}
|
|
// skip writeRawData for empty EcmaString
|
|
if (length == 0) {
|
|
return true;
|
|
}
|
|
|
|
bool isUtf8 = EcmaStringAccessor(string).IsUtf8();
|
|
// write utf encode flag
|
|
if (!WriteBoolean(isUtf8)) {
|
|
return false;
|
|
}
|
|
if (isUtf8) {
|
|
const uint8_t *data = EcmaStringAccessor(string).GetDataUtf8();
|
|
const uint8_t strEnd = '\0';
|
|
if (!WriteRawData(data, length) || !WriteRawData(&strEnd, sizeof(uint8_t))) {
|
|
return false;
|
|
}
|
|
} else {
|
|
const uint16_t *data = EcmaStringAccessor(string).GetDataUtf16();
|
|
if (!WriteRawData(data, length * sizeof(uint16_t))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSMap(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSMap> map = JSHandle<JSMap>::Cast(value);
|
|
if (!WriteType(SerializationUID::JS_MAP)) {
|
|
return false;
|
|
}
|
|
if (!WritePlainObject(value)) {
|
|
return false;
|
|
}
|
|
uint32_t size = map->GetSize();
|
|
if (!WriteInt(static_cast<int32_t>(size))) {
|
|
return false;
|
|
}
|
|
JSMutableHandle<JSTaggedValue> key(thread_, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSSet> set = JSHandle<JSSet>::Cast(value);
|
|
if (!WriteType(SerializationUID::JS_SET)) {
|
|
return false;
|
|
}
|
|
if (!WritePlainObject(value)) {
|
|
return false;
|
|
}
|
|
uint32_t size = set->GetSize();
|
|
if (!WriteInt(static_cast<int32_t>(size))) {
|
|
return false;
|
|
}
|
|
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSRegExp> regExp = JSHandle<JSRegExp>::Cast(value);
|
|
if (!WriteType(SerializationUID::JS_REG_EXP)) {
|
|
return false;
|
|
}
|
|
if (!WritePlainObject(value)) {
|
|
return false;
|
|
}
|
|
uint32_t bufferSize = regExp->GetLength();
|
|
if (!WriteInt(static_cast<int32_t>(bufferSize))) {
|
|
return false;
|
|
}
|
|
// Write Accessor(ByteCodeBuffer) which is a pointer to a dynamic buffer
|
|
JSHandle<JSTaggedValue> bufferValue(thread_, regExp->GetByteCodeBuffer());
|
|
JSHandle<JSNativePointer> np = JSHandle<JSNativePointer>::Cast(bufferValue);
|
|
void *dynBuffer = np->GetExternalPointer();
|
|
if (!WriteRawData(dynBuffer, bufferSize)) {
|
|
return false;
|
|
}
|
|
// Write Accessor(OriginalSource)
|
|
JSHandle<JSTaggedValue> originalSource(thread_, regExp->GetOriginalSource());
|
|
if (!SerializeJSTaggedValue(originalSource)) {
|
|
return false;
|
|
}
|
|
// Write Accessor(OriginalFlags)
|
|
JSHandle<JSTaggedValue> originalFlags(thread_, regExp->GetOriginalFlags());
|
|
if (!SerializeJSTaggedValue(originalFlags)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSTypedArray(const JSHandle<JSTaggedValue> &value, SerializationUID uId)
|
|
{
|
|
JSHandle<JSTypedArray> typedArray = JSHandle<JSTypedArray>::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<JSTaggedValue> 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<JSTaggedValue> 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<JSNativePointer> &nativePtr)
|
|
{
|
|
uintptr_t externalPtr = reinterpret_cast<uintptr_t>(nativePtr->GetExternalPointer());
|
|
if (!WriteRawData(&externalPtr, sizeof(uintptr_t))) {
|
|
return false;
|
|
}
|
|
uintptr_t deleter = reinterpret_cast<uintptr_t>(nativePtr->GetDeleter());
|
|
if (!WriteRawData(&deleter, sizeof(uintptr_t))) {
|
|
return false;
|
|
}
|
|
uintptr_t allocatorPtr = reinterpret_cast<uintptr_t>(nativePtr->GetData());
|
|
if (!WriteRawData(&allocatorPtr, sizeof(uintptr_t))) {
|
|
return false;
|
|
}
|
|
int32_t bindingSize = static_cast<int32_t>(nativePtr->GetBindingSize());
|
|
if (!WriteInt(bindingSize)) {
|
|
return false;
|
|
}
|
|
nativePtr->Detach();
|
|
return true;
|
|
}
|
|
|
|
bool JSSerializer::WriteJSArrayBuffer(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
JSHandle<JSArrayBuffer> arrayBuffer = JSHandle<JSArrayBuffer>::Cast(value);
|
|
if (arrayBuffer->IsDetach()) {
|
|
return false;
|
|
}
|
|
bool shared = arrayBuffer->GetShared();
|
|
bool transfer = transferDataSet_.find(static_cast<uintptr_t>(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<JSNativePointer> np(thread_, arrayBuffer->GetArrayBufferData());
|
|
if (shared) {
|
|
void *buffer = np->GetExternalPointer();
|
|
JSSharedMemoryManager::GetInstance()->CreateOrLoad(&buffer, arrayLength);
|
|
uint64_t bufferAddr = reinterpret_cast<uint64_t>(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<JSTaggedValue> keyVector)
|
|
{
|
|
if (keyVector.size() < 2) { // 2: detachSymbol, attachSymbol
|
|
return false;
|
|
}
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSTaggedValue> detach = env->GetDetachSymbol();
|
|
JSHandle<JSTaggedValue> attach = env->GetAttachSymbol();
|
|
JSMutableHandle<JSTaggedValue> detachKey(thread_, JSTaggedValue::Undefined());
|
|
JSMutableHandle<JSTaggedValue> attachKey(thread_, JSTaggedValue::Undefined());
|
|
uint32_t keyLength = keyVector.size();
|
|
for (uint32_t i = 0; i < keyLength - 1; i++) {
|
|
if (keyVector[i].IsSymbol() && keyVector[i + 1].IsSymbol()) {
|
|
detachKey.Update(keyVector[i]);
|
|
attachKey.Update(keyVector[i + 1]);
|
|
if (JSTaggedValue::Equal(thread_, detach, detachKey) || JSTaggedValue::Equal(thread_, attach, attachKey)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool JSSerializer::WritePlainObject(const JSHandle<JSTaggedValue> &objValue)
|
|
{
|
|
JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(objValue);
|
|
std::vector<JSTaggedValue> keyVector;
|
|
uint32_t propertiesLength = obj->GetNumberOfKeys();
|
|
JSObject::GetAllKeys(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<int32_t>(propertiesLength))) {
|
|
return false;
|
|
}
|
|
JSMutableHandle<JSTaggedValue> 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<int32_t>(elementsLength))) {
|
|
return false;
|
|
}
|
|
keyVector.clear();
|
|
JSObject::GetALLElementKeysIntoVector(thread_, obj, keyVector);
|
|
// Write elements' description attributes and value
|
|
if (keyVector.size() != elementsLength) {
|
|
return false;
|
|
}
|
|
JSMutableHandle<JSTaggedValue> 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<JSTaggedValue> &objValue)
|
|
{
|
|
JSHandle<JSObject> obj = JSHandle<JSObject>::Cast(objValue);
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSTaggedValue> detach = env->GetDetachSymbol();
|
|
JSHandle<JSTaggedValue> attach = env->GetAttachSymbol();
|
|
if (!WriteType(SerializationUID::NATIVE_BINDING_OBJECT)) {
|
|
return false;
|
|
}
|
|
int32_t paramCount = obj->GetNativePointerFieldCount();
|
|
void *enginePointer = nullptr;
|
|
void *objPointer = nullptr;
|
|
void *hint = nullptr;
|
|
void *detachData = nullptr;
|
|
void *attachData = nullptr;
|
|
if (paramCount == 5) { // 5 : enginePointer, objPointer, hint, detachData, attachData
|
|
enginePointer = obj->GetNativePointerField(0);
|
|
objPointer = obj->GetNativePointerField(1);
|
|
hint = obj->GetNativePointerField(2); // 2 : hint
|
|
detachData = obj->GetNativePointerField(3); // 3 : detachData
|
|
attachData = obj->GetNativePointerField(4); // 4 : attachData
|
|
}
|
|
// Write custom object's values: AttachFunc*, buffer*
|
|
JSHandle<JSTaggedValue> detachVal = JSObject::GetProperty(thread_, obj, detach).GetRawValue();
|
|
JSHandle<JSTaggedValue> attackVal = JSObject::GetProperty(thread_, obj, attach).GetRawValue();
|
|
DetachFunc detachNative = reinterpret_cast<DetachFunc>(JSNativePointer::Cast(
|
|
detachVal.GetTaggedValue().GetTaggedObject())->GetExternalPointer());
|
|
if (detachNative == nullptr) {
|
|
return false;
|
|
}
|
|
void *buffer = detachNative(enginePointer, objPointer, hint, detachData);
|
|
AttachFunc attachNative = reinterpret_cast<AttachFunc>(JSNativePointer::Cast(
|
|
attackVal.GetTaggedValue().GetTaggedObject())->GetExternalPointer());
|
|
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<JSTaggedValue> value = desc.GetValue();
|
|
if (!SerializeJSTaggedValue(value)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
SerializationUID JSDeserializer::ReadType()
|
|
{
|
|
SerializationUID uid;
|
|
if (position_ >= end_) {
|
|
return SerializationUID::UNKNOWN;
|
|
}
|
|
uid = static_cast<SerializationUID>(*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<size_t>(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<size_t>(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<size_t>(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<JSTaggedValue> JSDeserializer::Deserialize()
|
|
{
|
|
size_t maxSerializerSize = thread_->GetEcmaVM()->GetEcmaParamConfiguration().GetMaxJSSerializerSize();
|
|
uint8_t dataSize = end_ - begin_;
|
|
if (dataSize > maxSerializerSize) {
|
|
LOG_ECMA(ERROR) << "The Serialization data size exceed limit Size";
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<JSTaggedValue> res = DeserializeJSTaggedValue();
|
|
return res;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::DeserializeJSTaggedValue()
|
|
{
|
|
SerializationUID uid = ReadType();
|
|
if (uid == SerializationUID::UNKNOWN) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
switch (uid) {
|
|
case SerializationUID::JS_NULL:
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Null());
|
|
case SerializationUID::JS_UNDEFINED:
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
|
|
case SerializationUID::JS_TRUE:
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::True());
|
|
case SerializationUID::JS_FALSE:
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::False());
|
|
case SerializationUID::HOLE:
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Hole());
|
|
case SerializationUID::INT32: {
|
|
int32_t value;
|
|
if (!ReadInt(&value)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue(value));
|
|
}
|
|
case SerializationUID::DOUBLE: {
|
|
double value;
|
|
if (!ReadDouble(&value)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
return JSHandle<JSTaggedValue>(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:
|
|
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<JSTaggedValue>();
|
|
}
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadBigInt()
|
|
{
|
|
int32_t len = 0;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
bool sign = false;
|
|
if (!ReadBoolean(&sign)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<BigInt> bigInt = factory_->NewBigInt(len);
|
|
bigInt->SetSign(sign);
|
|
JSHandle<JSTaggedValue> 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<JSTaggedValue>();
|
|
}
|
|
bigInt->SetDigit(i, val);
|
|
}
|
|
return bigIntVal;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadTaggedArray()
|
|
{
|
|
int32_t len = 0;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&len)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<TaggedArray> taggedArray = factory_->NewTaggedArray(len);
|
|
JSHandle<JSTaggedValue> arrayTag(taggedArray);
|
|
referenceMap_.emplace(objectId_++, arrayTag);
|
|
for (int32_t i = 0; i < len; i++) {
|
|
JSHandle<JSTaggedValue> val = DeserializeJSTaggedValue();
|
|
taggedArray->Set(thread_, i, val.GetTaggedValue());
|
|
}
|
|
return arrayTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadByteArray()
|
|
{
|
|
int32_t arrayLength = 0;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t viewTypeIndex = 0;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&viewTypeIndex)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
DataViewType viewType = GetDataViewTypeByIndex(viewTypeIndex);
|
|
uint32_t arrayType = TypedArrayHelper::GetSizeFromType(viewType);
|
|
JSHandle<ByteArray> byteArray = factory_->NewByteArray(arrayLength, arrayType);
|
|
for (int32_t i = 0; i < arrayLength; i++) {
|
|
JSHandle<JSTaggedValue> val = DeserializeJSTaggedValue();
|
|
byteArray->Set(thread_, i, viewType, val.GetTaggedType());
|
|
}
|
|
return JSHandle<JSTaggedValue>(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<JSTaggedValue> JSDeserializer::ReadMethod()
|
|
{
|
|
uintptr_t methodLiteral;
|
|
if (!ReadNativePointer(&methodLiteral)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<Method> method = factory_->NewMethod(reinterpret_cast<MethodLiteral *>(methodLiteral));
|
|
JSHandle<JSTaggedValue> methodTag(method);
|
|
referenceMap_.emplace(objectId_++, methodTag);
|
|
|
|
CString desc;
|
|
if (!ReadString(&desc)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
std::shared_ptr<JSPandaFile> jsPandaFile = JSPandaFileManager::GetInstance()->FindJSPandaFile(desc);
|
|
if (jsPandaFile == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<ConstantPool> constPool =
|
|
thread_->GetCurrentEcmaContext()->FindOrCreateConstPool(jsPandaFile.get(), method->GetMethodId());
|
|
method->SetConstantPool(thread_, constPool.GetTaggedValue());
|
|
return methodTag;
|
|
}
|
|
|
|
bool JSDeserializer::ReadString(CString *value)
|
|
{
|
|
if (!JudgeType(SerializationUID::C_STRING)) {
|
|
return false;
|
|
}
|
|
|
|
*value = reinterpret_cast<char *>(const_cast<uint8_t *>(position_));
|
|
size_t len = value->length() + 1; // 1: '\0'
|
|
position_ += len;
|
|
return true;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadNativeMethod()
|
|
{
|
|
uintptr_t nativeFunc;
|
|
if (!ReadNativePointer(&nativeFunc)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<Method> method = factory_->NewMethodForNativeFunction(reinterpret_cast<void *>(nativeFunc));
|
|
JSHandle<JSTaggedValue> methodTag(method);
|
|
referenceMap_.emplace(objectId_++, methodTag);
|
|
return methodTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSFunction()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> func = factory_->NewJSFunction(env, nullptr, FunctionKind::CONCURRENT_FUNCTION);
|
|
JSHandle<JSTaggedValue> funcTag(func);
|
|
referenceMap_.emplace(objectId_++, funcTag);
|
|
JSHandle<JSTaggedValue> methodVal = DeserializeJSTaggedValue();
|
|
JSHandle<Method> method = JSHandle<Method>::Cast(methodVal);
|
|
func->SetMethod(thread_, method);
|
|
return funcTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> 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;
|
|
default:
|
|
LOG_ECMA(FATAL) << "this branch is unreachable";
|
|
UNREACHABLE();
|
|
}
|
|
JSHandle<JSTaggedValue> msg = DeserializeJSTaggedValue();
|
|
JSHandle<EcmaString> handleMsg(msg);
|
|
JSHandle<JSTaggedValue> errorTag = JSHandle<JSTaggedValue>::Cast(factory_->NewJSError(errorType, handleMsg));
|
|
referenceMap_.emplace(objectId_++, errorTag);
|
|
return errorTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSDate()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> dateFunction(env->GetDateFunction());
|
|
JSHandle<JSDate> date = JSHandle<JSDate>::Cast(factory_->NewJSObjectByConstructor(dateFunction));
|
|
JSHandle<JSTaggedValue> dateTag = JSHandle<JSTaggedValue>::Cast(date);
|
|
referenceMap_.emplace(objectId_++, dateTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(dateTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
double timeValue = 0.0;
|
|
if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&timeValue)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
date->SetTimeValue(thread_, JSTaggedValue(timeValue));
|
|
double localOffset = 0.0;
|
|
if (!JudgeType(SerializationUID::DOUBLE) || !ReadDouble(&localOffset)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
date->SetLocalOffset(thread_, JSTaggedValue(localOffset));
|
|
return dateTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSArray()
|
|
{
|
|
JSHandle<JSArray> jsArray = thread_->GetEcmaVM()->GetFactory()->NewJSArray();
|
|
JSHandle<JSTaggedValue> arrayTag = JSHandle<JSTaggedValue>::Cast(jsArray);
|
|
referenceMap_.emplace(objectId_++, arrayTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t arrLength;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrLength)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
jsArray->SetLength(arrLength);
|
|
return arrayTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadEcmaString()
|
|
{
|
|
int32_t stringLength;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&stringLength)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
if (stringLength == 0) {
|
|
JSHandle<JSTaggedValue> emptyString = JSHandle<JSTaggedValue>::Cast(factory_->GetEmptyString());
|
|
referenceMap_.emplace(objectId_++, emptyString);
|
|
return emptyString;
|
|
}
|
|
|
|
bool isUtf8 = false;
|
|
if (!ReadBoolean(&isUtf8)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> stringTag;
|
|
if (isUtf8) {
|
|
uint8_t *string = reinterpret_cast<uint8_t*>(GetBuffer(stringLength + 1));
|
|
if (string == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
|
|
JSHandle<EcmaString> ecmaString = factory_->NewFromUtf8(string, stringLength);
|
|
stringTag = JSHandle<JSTaggedValue>(ecmaString);
|
|
referenceMap_.emplace(objectId_++, stringTag);
|
|
} else {
|
|
uint16_t *string = reinterpret_cast<uint16_t*>(GetBuffer(stringLength * sizeof(uint16_t)));
|
|
if (string == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<EcmaString> ecmaString = factory_->NewFromUtf16(string, stringLength);
|
|
stringTag = JSHandle<JSTaggedValue>(ecmaString);
|
|
referenceMap_.emplace(objectId_++, stringTag);
|
|
}
|
|
return stringTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadPlainObject()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> objFunc(env->GetObjectFunction());
|
|
JSHandle<JSObject> jsObject = thread_->GetEcmaVM()->GetFactory()->NewJSObjectByConstructor(objFunc);
|
|
JSHandle<JSTaggedValue> objTag = JSHandle<JSTaggedValue>::Cast(jsObject);
|
|
referenceMap_.emplace(objectId_++, objTag);
|
|
if (!DefinePropertiesAndElements(objTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
return objTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadNativeBindingObject()
|
|
{
|
|
uintptr_t funcPointer;
|
|
if (!ReadNativePointer(&funcPointer)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
AttachFunc attachFunc = reinterpret_cast<AttachFunc>(funcPointer);
|
|
if (attachFunc == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
uintptr_t bufferPointer;
|
|
if (!ReadNativePointer(&bufferPointer)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
uintptr_t hint;
|
|
if (!ReadNativePointer(&hint)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
uintptr_t attachData;
|
|
if (!ReadNativePointer(&attachData)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
Local<JSValueRef> attachVal = attachFunc(engine_, reinterpret_cast<void *>(bufferPointer),
|
|
reinterpret_cast<void *>(hint), reinterpret_cast<void *>(attachData));
|
|
if (attachVal.IsEmpty()) {
|
|
LOG_ECMA(ERROR) << "NativeBindingObject is empty";
|
|
attachVal = JSValueRef::Undefined(thread_->GetEcmaVM());
|
|
}
|
|
objectId_++;
|
|
return JSNApiHelper::ToJSHandle(attachVal);
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSMap()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> mapFunction(env->GetBuiltinsMapFunction());
|
|
JSHandle<JSMap> jsMap = JSHandle<JSMap>::Cast(factory_->NewJSObjectByConstructor(mapFunction));
|
|
JSHandle<JSTaggedValue> mapTag = JSHandle<JSTaggedValue>::Cast(jsMap);
|
|
referenceMap_.emplace(objectId_++, mapTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(mapTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t size;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<LinkedHashMap> linkedMap = LinkedHashMap::Create(thread_);
|
|
jsMap->SetLinkedMap(thread_, linkedMap);
|
|
for (int32_t i = 0; i < size; i++) {
|
|
JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
|
|
if (key.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<JSTaggedValue> value = DeserializeJSTaggedValue();
|
|
if (value.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSMap::Set(thread_, jsMap, key, value);
|
|
}
|
|
return mapTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSSet()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> setFunction(env->GetBuiltinsSetFunction());
|
|
JSHandle<JSSet> jsSet = JSHandle<JSSet>::Cast(factory_->NewJSObjectByConstructor(setFunction));
|
|
JSHandle<JSTaggedValue> setTag = JSHandle<JSTaggedValue>::Cast(jsSet);
|
|
referenceMap_.emplace(objectId_++, setTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(setTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t size;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&size)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<LinkedHashSet> linkedSet = LinkedHashSet::Create(thread_);
|
|
jsSet->SetLinkedSet(thread_, linkedSet);
|
|
for (int32_t i = 0; i < size; i++) {
|
|
JSHandle<JSTaggedValue> key = DeserializeJSTaggedValue();
|
|
if (key.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSSet::Add(thread_, jsSet, key);
|
|
}
|
|
return setTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSRegExp()
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSFunction> regexpFunction(env->GetRegExpFunction());
|
|
JSHandle<JSObject> obj = factory_->NewJSObjectByConstructor(regexpFunction);
|
|
JSHandle<JSRegExp> regExp = JSHandle<JSRegExp>::Cast(obj);
|
|
JSHandle<JSTaggedValue> regexpTag = JSHandle<JSTaggedValue>::Cast(regExp);
|
|
referenceMap_.emplace(objectId_++, regexpTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(regexpTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t bufferSize;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bufferSize)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
void *buffer = GetBuffer(bufferSize);
|
|
if (buffer == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
factory_->NewJSRegExpByteCodeData(regExp, buffer, bufferSize);
|
|
JSHandle<JSTaggedValue> originalSource = DeserializeJSTaggedValue();
|
|
regExp->SetOriginalSource(thread_, originalSource);
|
|
JSHandle<JSTaggedValue> originalFlags = DeserializeJSTaggedValue();
|
|
regExp->SetOriginalFlags(thread_, originalFlags);
|
|
return regexpTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSTypedArray(SerializationUID uid)
|
|
{
|
|
JSHandle<GlobalEnv> env = thread_->GetEcmaVM()->GetGlobalEnv();
|
|
JSHandle<JSTaggedValue> target;
|
|
JSHandle<JSObject> obj;
|
|
JSHandle<JSTaggedValue> objTag;
|
|
switch (uid) {
|
|
case SerializationUID::JS_INT8_ARRAY: {
|
|
target = env->GetInt8ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_UINT8_ARRAY: {
|
|
target = env->GetUint8ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_UINT8_CLAMPED_ARRAY: {
|
|
target = env->GetUint8ClampedArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_INT16_ARRAY: {
|
|
target = env->GetInt16ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_UINT16_ARRAY: {
|
|
target = env->GetUint16ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_INT32_ARRAY: {
|
|
target = env->GetInt32ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_UINT32_ARRAY: {
|
|
target = env->GetUint32ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_FLOAT32_ARRAY: {
|
|
target = env->GetFloat32ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_FLOAT64_ARRAY: {
|
|
target = env->GetFloat64ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_BIGINT64_ARRAY: {
|
|
target = env->GetBigInt64ArrayFunction();
|
|
break;
|
|
}
|
|
case SerializationUID::JS_BIGUINT64_ARRAY: {
|
|
target = env->GetBigUint64ArrayFunction();
|
|
break;
|
|
}
|
|
default:
|
|
LOG_ECMA(FATAL) << "this branch is unreachable";
|
|
UNREACHABLE();
|
|
}
|
|
JSHandle<JSTypedArray> typedArray =
|
|
JSHandle<JSTypedArray>::Cast(factory_->NewJSObjectByConstructor(JSHandle<JSFunction>(target)));
|
|
obj = JSHandle<JSObject>::Cast(typedArray);
|
|
objTag = JSHandle<JSTaggedValue>::Cast(obj);
|
|
referenceMap_.emplace(objectId_++, objTag);
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(objTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
|
|
bool isViewedArrayBuffer = false;
|
|
if (!ReadBoolean(&isViewedArrayBuffer)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<JSTaggedValue> viewedArrayBufferOrByteArray;
|
|
if (isViewedArrayBuffer) {
|
|
viewedArrayBufferOrByteArray = DeserializeJSTaggedValue();
|
|
} else {
|
|
if (!JudgeType(SerializationUID::BYTE_ARRAY)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
viewedArrayBufferOrByteArray = ReadByteArray();
|
|
}
|
|
if (viewedArrayBufferOrByteArray.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetViewedArrayBufferOrByteArray(thread_, viewedArrayBufferOrByteArray);
|
|
|
|
JSHandle<JSTaggedValue> typedArrayName = DeserializeJSTaggedValue();
|
|
if (typedArrayName.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetTypedArrayName(thread_, typedArrayName);
|
|
|
|
JSTaggedValue byteLength;
|
|
if (!ReadJSTaggedValue(&byteLength) || !byteLength.IsNumber()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetByteLength(byteLength.GetNumber());
|
|
|
|
JSTaggedValue byteOffset;
|
|
if (!ReadJSTaggedValue(&byteOffset) || !byteOffset.IsNumber()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetByteOffset(byteOffset.GetNumber());
|
|
|
|
JSTaggedValue arrayLength;
|
|
if (!ReadJSTaggedValue(&arrayLength) || !byteOffset.IsNumber()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetArrayLength(arrayLength.GetNumber());
|
|
|
|
ContentType *contentType = reinterpret_cast<ContentType*>(GetBuffer(sizeof(ContentType)));
|
|
if (contentType == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
typedArray->SetContentType(*contentType);
|
|
return objTag;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSNativePointer()
|
|
{
|
|
uintptr_t externalPtr;
|
|
if (!ReadNativePointer(&externalPtr)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
uintptr_t deleter;
|
|
if (!ReadNativePointer(&deleter)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
uintptr_t allocatorPtr;
|
|
if (!ReadNativePointer(&allocatorPtr)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
int32_t bindingSize;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&bindingSize)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
JSHandle<JSNativePointer> np = factory_->NewJSNativePointer(ToVoidPtr(externalPtr),
|
|
reinterpret_cast<DeleteEntryPoint>(deleter),
|
|
ToVoidPtr(allocatorPtr),
|
|
false,
|
|
bindingSize);
|
|
return JSHandle<JSTaggedValue>::Cast(np);
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadJSArrayBuffer(SerializationUID uid)
|
|
{
|
|
bool withNativeAreaAllocator;
|
|
if (!ReadBoolean(&withNativeAreaAllocator)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
// read access length
|
|
int32_t arrayLength;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&arrayLength)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
// read access shared
|
|
bool shared = (uid == SerializationUID::JS_SHARED_ARRAY_BUFFER);
|
|
|
|
JSHandle<JSArrayBuffer> arrayBuffer;
|
|
if (arrayLength == 0) {
|
|
// create an empty arrayBuffer
|
|
arrayBuffer = factory_->NewJSArrayBuffer(0);
|
|
arrayBuffer->SetShared(shared);
|
|
} else {
|
|
if (shared) {
|
|
uint64_t *bufferAddr = reinterpret_cast<uint64_t*>(GetBuffer(sizeof(uint64_t)));
|
|
void *bufferData = ToVoidPtr(*bufferAddr);
|
|
arrayBuffer = factory_->NewJSSharedArrayBuffer(bufferData, arrayLength);
|
|
} else if (uid == SerializationUID::JS_TRANSFER_ARRAY_BUFFER) {
|
|
JSHandle<JSTaggedValue> np = ReadJSNativePointer();
|
|
if (np.IsEmpty()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
arrayBuffer = factory_->NewJSArrayBuffer(0);
|
|
arrayBuffer->Attach(thread_, arrayLength, np.GetTaggedValue(), withNativeAreaAllocator);
|
|
} else {
|
|
void *fromBuffer = GetBuffer(arrayLength);
|
|
if (fromBuffer == nullptr) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
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<JSTaggedValue> arrayBufferTag = JSHandle<JSTaggedValue>::Cast(arrayBuffer);
|
|
referenceMap_.emplace(objectId_++, arrayBufferTag);
|
|
// read jsarraybuffer properties
|
|
if (!JudgeType(SerializationUID::JS_PLAIN_OBJECT) || !DefinePropertiesAndElements(arrayBufferTag)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
|
|
return arrayBufferTag;
|
|
}
|
|
|
|
bool JSDeserializer::ReadJSTaggedValue(JSTaggedValue *value)
|
|
{
|
|
size_t len = sizeof(JSTaggedValue);
|
|
if (len > static_cast<size_t>(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<size_t>(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<size_t>(end_ - position_)) {
|
|
return nullptr;
|
|
}
|
|
buffer = position_;
|
|
position_ += bufferSize;
|
|
uint8_t *retBuffer = const_cast<uint8_t *>(buffer);
|
|
return static_cast<void *>(retBuffer);
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> JSDeserializer::ReadReference()
|
|
{
|
|
uint64_t objId;
|
|
if (!ReadObjectId(&objId)) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
auto objIter = referenceMap_.find(objId);
|
|
if (objIter == referenceMap_.end()) {
|
|
return JSHandle<JSTaggedValue>();
|
|
}
|
|
return objIter->second;
|
|
}
|
|
|
|
bool JSDeserializer::JudgeType(SerializationUID targetUid)
|
|
{
|
|
if (ReadType() != targetUid) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool JSDeserializer::DefinePropertiesAndElements(const JSHandle<JSTaggedValue> &obj)
|
|
{
|
|
int32_t propertyLength;
|
|
if (!JudgeType(SerializationUID::INT32) || !ReadInt(&propertyLength)) {
|
|
return false;
|
|
}
|
|
for (int32_t i = 0; i < propertyLength; i++) {
|
|
JSHandle<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> 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<JSTaggedValue> &value, const JSHandle<JSTaggedValue> &transfer)
|
|
{
|
|
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)) {
|
|
return false;
|
|
}
|
|
// clear transfer obj set after serialization
|
|
valueSerializer_.ClearTransferSet();
|
|
std::pair<uint8_t*, size_t> pair = valueSerializer_.ReleaseBuffer();
|
|
data_->value_.reset(pair.first);
|
|
data_->dataSize_ = pair.second;
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<SerializationData> Serializer::Release()
|
|
{
|
|
return std::move(data_);
|
|
}
|
|
|
|
bool Serializer::PrepareTransfer(JSThread *thread, const JSHandle<JSTaggedValue> &transfer)
|
|
{
|
|
if (transfer->IsUndefined()) {
|
|
return true;
|
|
}
|
|
if (!transfer->IsJSArray()) {
|
|
return false;
|
|
}
|
|
int len = base::ArrayHelper::GetArrayLength(thread, transfer);
|
|
int k = 0;
|
|
CUnorderedSet<uintptr_t> transferDataSet;
|
|
while (k < len) {
|
|
bool exists = JSTaggedValue::HasProperty(thread, transfer, k);
|
|
if (exists) {
|
|
JSHandle<JSTaggedValue> element = JSArray::FastGetPropertyByValue(thread, transfer, k);
|
|
if (!element->IsArrayBuffer()) {
|
|
return false;
|
|
}
|
|
transferDataSet.insert(static_cast<uintptr_t>(element.GetTaggedType()));
|
|
}
|
|
k++;
|
|
}
|
|
valueSerializer_.InitTransferSet(std::move(transferDataSet));
|
|
return true;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> Deserializer::ReadValue()
|
|
{
|
|
return valueDeserializer_.Deserialize();
|
|
}
|
|
} // namespace panda::ecmascript
|