/* * 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. */ #ifndef ECMASCRIPT_JS_SERIALIZER_H #define ECMASCRIPT_JS_SERIALIZER_H #include "ecmascript/ecma_vm.h" #include "ecmascript/js_date.h" #include "ecmascript/js_map.h" #include "ecmascript/js_native_pointer.h" #include "ecmascript/js_object.h" #include "ecmascript/js_thread.h" #include "ecmascript/js_typed_array.h" #include "ecmascript/jspandafile/js_pandafile.h" #include "ecmascript/jspandafile/program_object.h" #include "ecmascript/mem/dyn_chunk.h" #include "ecmascript/napi/jsnapi_helper.h" #include "ecmascript/napi/include/jsnapi.h" using panda::JSValueRef; namespace panda::ecmascript { class JSPandaFile; class ConstantPool; typedef void* (*DetachFunc)(void *enginePointer, void *objPointer, void *hint, void *detachData); typedef Local (*AttachFunc)(void *enginePointer, void *buffer, void *hint, void *attachData); enum class SerializationUID : uint8_t { UID_BEGIN = 0x01, // JS special values JS_NULL, JS_UNDEFINED, JS_TRUE, JS_FALSE, HOLE, // Number types INT32, DOUBLE, BIGINT, ECMASTRING, C_STRING, // Boolean types C_TRUE, C_FALSE, // Tagged object reference mark TAGGED_OBJECT_REFERNCE, // Support tagged object id reference begin JS_DATE, JS_REG_EXP, JS_PLAIN_OBJECT, NATIVE_BINDING_OBJECT, JS_SET, JS_MAP, JS_ARRAY, JS_ARRAY_BUFFER, JS_SHARED_ARRAY_BUFFER, JS_TRANSFER_ARRAY_BUFFER, // TypedArray begin JS_UINT8_ARRAY, JS_UINT8_CLAMPED_ARRAY, JS_UINT16_ARRAY, JS_UINT32_ARRAY, JS_INT8_ARRAY, JS_INT16_ARRAY, JS_INT32_ARRAY, JS_FLOAT32_ARRAY, JS_FLOAT64_ARRAY, JS_BIGINT64_ARRAY, JS_BIGUINT64_ARRAY, // TypedArray end // Support tagged object id reference end // Error UIDs JS_ERROR, EVAL_ERROR, RANGE_ERROR, REFERENCE_ERROR, TYPE_ERROR, AGGREGATE_ERROR, URI_ERROR, SYNTAX_ERROR, OOM_ERROR, ERROR_MESSAGE_BEGIN, ERROR_MESSAGE_END, // Function begin CONCURRENT_FUNCTION, METHOD, NATIVE_METHOD, TAGGED_ARRAY, // Function end BYTE_ARRAY, UID_END, UNKNOWN }; class JSSerializer { public: explicit JSSerializer(JSThread *thread) : thread_(thread) {} ~JSSerializer() = default; bool SerializeJSTaggedValue(const JSHandle &value); void InitTransferSet(CUnorderedSet transferDataSet); void ClearTransferSet(); // Return pointer to the buffer and its length, should not use this Serializer anymore after Release std::pair ReleaseBuffer(); private: bool WriteJSFunction(const JSHandle &value); bool WriteMethod(const JSHandle &value); bool WriteConstantPool(const JSHandle &value); bool WriteTaggedArray(const JSHandle &value); bool WriteByteArray(const JSHandle &value, DataViewType viewType); bool WriteTaggedObject(const JSHandle &value); bool WritePrimitiveValue(const JSHandle &value); bool WriteInt(int32_t value); bool WriteDouble(double value); bool WriteRawData(const void *data, size_t length); bool WriteType(SerializationUID uId); bool AllocateBuffer(size_t bytes); bool ExpandBuffer(size_t requestedSize); bool WriteBoolean(bool value); bool WriteJSError(const JSHandle &value); bool WriteJSErrorHeader(JSType type); bool WriteJSDate(const JSHandle &value); bool WriteJSArray(const JSHandle &value); bool WriteJSMap(const JSHandle &value); bool WriteJSSet(const JSHandle &value); bool WriteJSRegExp(const JSHandle &value); bool WriteEcmaString(const JSHandle &value); bool WriteJSTypedArray(const JSHandle &value, SerializationUID uId); bool WritePlainObject(const JSHandle &value); bool WriteNativeBindingObject(const JSHandle &value); bool WriteJSNativePointer(const JSHandle &value); bool WriteJSArrayBuffer(const JSHandle &value); bool WriteBigInt(const JSHandle &value); bool WriteDesc(const PropertyDescriptor &desc); bool IsNativeBindingObject(std::vector keyVector); bool IsTargetSymbol(JSTaggedValue symbolVal); bool IsSerialized(uintptr_t addr) const; bool WriteIfSerialized(uintptr_t addr); uint32_t GetDataViewTypeIndex(const DataViewType dataViewType); bool WriteString(const CString &str); NO_MOVE_SEMANTIC(JSSerializer); NO_COPY_SEMANTIC(JSSerializer); JSThread *thread_; uint8_t *buffer_ = nullptr; uint64_t sizeLimit_ = 0; size_t bufferSize_ = 0; size_t bufferCapacity_ = 0; // The Reference map is used for check whether a tagged object has been serialized // Reference map works only if no gc happens during serialization CUnorderedMap referenceMap_; CUnorderedSet transferDataSet_; uint64_t objectId_ = 0; }; class JSDeserializer { public: // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) JSDeserializer(JSThread *thread, uint8_t *data, size_t size, void *hint = nullptr) : thread_(thread), factory_(thread->GetEcmaVM()->GetFactory()), begin_(data), position_(data), end_(data + size), engine_(hint) { } ~JSDeserializer(); JSHandle Deserialize(); private: bool ReadInt(int32_t *value); bool ReadObjectId(uint64_t *objectId); bool ReadDouble(double *value); SerializationUID ReadType(); JSHandle ReadJSFunctionBase(); JSHandle ReadJSFunction(); JSHandle ReadTaggedArray(); JSHandle ReadByteArray(); JSHandle ReadMethod(); JSHandle ReadNativeMethod(); JSHandle ReadJSError(SerializationUID uid); JSHandle ReadJSDate(); JSHandle ReadJSArray(); JSHandle ReadPlainObject(); JSHandle ReadEcmaString(); JSHandle ReadJSMap(); JSHandle ReadJSSet(); JSHandle ReadJSRegExp(); JSHandle ReadJSTypedArray(SerializationUID uid); JSHandle ReadJSNativePointer(); JSHandle ReadJSArrayBuffer(SerializationUID uid); JSHandle ReadReference(); JSHandle ReadNativeBindingObject(); JSHandle ReadBigInt(); JSHandle DeserializeJSTaggedValue(); bool JudgeType(SerializationUID targetUid); void *GetBuffer(uint32_t bufferSize); bool ReadJSTaggedValue(JSTaggedValue *originalFlags); bool ReadNativePointer(uintptr_t *pointer); bool DefinePropertiesAndElements(const JSHandle &obj); bool ReadDesc(PropertyDescriptor *desc); bool ReadBoolean(bool *value); bool ReadString(CString *value); DataViewType GetDataViewTypeByIndex(uint32_t viewTypeIndex); NO_MOVE_SEMANTIC(JSDeserializer); NO_COPY_SEMANTIC(JSDeserializer); JSThread *thread_ = nullptr; ObjectFactory *factory_ = nullptr; uint8_t *begin_ = nullptr; const uint8_t *position_ = nullptr; const uint8_t *end_ = nullptr; uint64_t objectId_ = 0; CUnorderedMap> referenceMap_; void *engine_ = nullptr; }; class SerializationData { public: SerializationData() : dataSize_(0), value_(nullptr) {} ~SerializationData() = default; uint8_t* GetData() const { return value_.get(); } size_t GetSize() const { return dataSize_; } private: struct Deleter { void operator()(uint8_t* ptr) const { free(ptr); } }; size_t dataSize_; std::unique_ptr value_; private: friend class Serializer; NO_COPY_SEMANTIC(SerializationData); }; class Serializer { public: explicit Serializer(JSThread *thread) : valueSerializer_(thread) {} ~Serializer() = default; bool WriteValue(JSThread *thread, const JSHandle &value, const JSHandle &transfer); std::unique_ptr Release(); private: bool PrepareTransfer(JSThread *thread, const JSHandle &transfer); private: ecmascript::JSSerializer valueSerializer_; std::unique_ptr data_; NO_COPY_SEMANTIC(Serializer); }; class Deserializer { public: Deserializer(JSThread *thread, SerializationData *data, void *hint) : valueDeserializer_(thread, data->GetData(), data->GetSize(), hint), data_(data) {} ~Deserializer() { data_.reset(nullptr); } JSHandle ReadValue(); private: ecmascript::JSDeserializer valueDeserializer_; std::unique_ptr data_; NO_COPY_SEMANTIC(Deserializer); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_SERIALIZER_H