arkcompiler_ets_runtime/ecmascript/js_serializer.cpp
wangzhaoyong aa45c63e6f support default transfer
issue: https://gitee.com/openharmony/commonlibrary_ets_utils/issues/I7RME9

Signed-off-by: wangzhaoyong <wangzhaoyong@huawei.com>
Change-Id: Ia6b98623706533c52ff08caf40de7be5e4d8f1b1
2023-08-17 21:12:48 +08:00

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