def serialize_any(self, obj: typing.Any, obj_type): if obj_type in self.primitive_type_serializer: self.primitive_type_serializer[obj_type](obj) elif hasattr(obj_type, "__origin__"): # Generic type types = getattr(obj_type, "__args__") if getattr(obj_type, "__origin__") == collections.abc.Sequence: # Sequence assert len(types) == 1 item_type = types[0] self.serialize_len(len(obj)) for item in obj: self.serialize_any(item, item_type) elif getattr(obj_type, "__origin__") == tuple: # Tuple for i in range(len(obj)): self.serialize_any(obj[i], types[i]) elif getattr(obj_type, "__origin__") == typing.Union: # Option assert len(types) == 2 and types[1] == type(None) if obj is None: self.output.write(b"\x00") else: self.output.write(b"\x01") self.serialize_any(obj, types[0]) elif getattr(obj_type, "__origin__") == dict: # Map assert len(types) == 2 self.serialize_len(len(obj)) offsets = [] for key, value in obj.items(): offsets.append(self.get_buffer_offset()) self.serialize_any(key, types[0]) self.serialize_any(value, types[1]) self.sort_map_entries(offsets) else: raise st.SerializationError("Unexpected type", obj_type) else: if not dataclasses.is_dataclass(obj_type): # Enum if not hasattr(obj_type, "VARIANTS"): raise st.SerializationError("Unexpected type", obj_type) if not hasattr(obj, "INDEX"): raise st.SerializationError( "Wrong Value for the type", obj, obj_type ) self.serialize_variant_index(obj.__class__.INDEX) # Proceed to variant obj_type = obj_type.VARIANTS[obj.__class__.INDEX] if not dataclasses.is_dataclass(obj_type): raise st.SerializationError("Unexpected type", obj_type) # pyre-ignore if not isinstance(obj, obj_type): raise st.SerializationError("Wrong Value for the type", obj, obj_type) # Content of struct or variant fields = dataclasses.fields(obj_type) types = get_type_hints(obj_type) self.increase_container_depth() for field in fields: field_value = obj.__dict__[field.name] field_type = types[field.name] self.serialize_any(field_value, field_type) self.decrease_container_depth()
def increase_container_depth(self): if self.container_depth_budget is not None: if self.container_depth_budget == 0: raise st.SerializationError("Exceeded maximum container depth") self.container_depth_budget -= 1
def serialize_variant_index(self, value: int): if value > MAX_U32: raise st.SerializationError( "Variant index exceeds the maximum supported value.") self.serialize_u32_as_uleb128(value)
def serialize_len(self, value: int): if value > MAX_LENGTH: raise st.SerializationError("Length exceeds the maximum supported value.") self.output.write(int(value).to_bytes(8, "little", signed=False))
def serialize_len(self, value: int): if value > MAX_LENGTH: raise st.SerializationError( "Length exceeds the maximum supported value.") self.serialize_u32_as_uleb128(value)