| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| use crate::metadata::{checksum_metadata, codes}; |
| use crate::*; |
| use anyhow::{bail, ensure, Context, Result}; |
| |
| pub fn read_metadata(data: &[u8]) -> Result<Metadata> { |
| MetadataReader::new(data).read_metadata() |
| } |
| |
| // Read a metadata type, this is pub so that we can test it in the metadata fixture |
| pub fn read_metadata_type(data: &[u8]) -> Result<Type> { |
| MetadataReader::new(data).read_type() |
| } |
| |
| /// Helper struct for read_metadata() |
| struct MetadataReader<'a> { |
| // This points to the initial data we were passed in |
| initial_data: &'a [u8], |
| // This points to the remaining data to be read |
| buf: &'a [u8], |
| } |
| |
| impl<'a> MetadataReader<'a> { |
| fn new(data: &'a [u8]) -> Self { |
| Self { |
| initial_data: data, |
| buf: data, |
| } |
| } |
| |
| // Read a top-level metadata item |
| // |
| // This consumes self because MetadataReader is only intended to read a single item. |
| fn read_metadata(mut self) -> Result<Metadata> { |
| let value = self.read_u8()?; |
| Ok(match value { |
| codes::NAMESPACE => NamespaceMetadata { |
| crate_name: self.read_string()?, |
| name: self.read_string()?, |
| } |
| .into(), |
| codes::UDL_FILE => UdlFile { |
| module_path: self.read_string()?, |
| namespace: self.read_string()?, |
| file_stub: self.read_string()?, |
| } |
| .into(), |
| codes::FUNC => self.read_func()?.into(), |
| codes::CONSTRUCTOR => self.read_constructor()?.into(), |
| codes::METHOD => self.read_method()?.into(), |
| codes::RECORD => self.read_record()?.into(), |
| codes::ENUM => self.read_enum()?.into(), |
| codes::INTERFACE => self.read_object(ObjectImpl::Struct)?.into(), |
| codes::TRAIT_INTERFACE => self.read_object(ObjectImpl::Trait)?.into(), |
| codes::CALLBACK_TRAIT_INTERFACE => self.read_object(ObjectImpl::CallbackTrait)?.into(), |
| codes::CALLBACK_INTERFACE => self.read_callback_interface()?.into(), |
| codes::TRAIT_METHOD => self.read_trait_method()?.into(), |
| codes::UNIFFI_TRAIT => self.read_uniffi_trait()?.into(), |
| _ => bail!("Unexpected metadata code: {value:?}"), |
| }) |
| } |
| |
| fn read_u8(&mut self) -> Result<u8> { |
| if !self.buf.is_empty() { |
| let value = self.buf[0]; |
| self.buf = &self.buf[1..]; |
| Ok(value) |
| } else { |
| bail!("Buffer is empty") |
| } |
| } |
| |
| fn peek_u8(&mut self) -> Result<u8> { |
| if !self.buf.is_empty() { |
| Ok(self.buf[0]) |
| } else { |
| bail!("Buffer is empty") |
| } |
| } |
| |
| fn read_u16(&mut self) -> Result<u16> { |
| if self.buf.len() >= 2 { |
| // read the value as little-endian |
| let value = u16::from_le_bytes([self.buf[0], self.buf[1]]); |
| self.buf = &self.buf[2..]; |
| Ok(value) |
| } else { |
| bail!("Not enough data left in buffer to read a u16 value"); |
| } |
| } |
| |
| fn read_u32(&mut self) -> Result<u32> { |
| if self.buf.len() >= 4 { |
| // read the value as little-endian |
| let value = self.buf[0] as u32 |
| + ((self.buf[1] as u32) << 8) |
| + ((self.buf[2] as u32) << 16) |
| + ((self.buf[3] as u32) << 24); |
| self.buf = &self.buf[4..]; |
| Ok(value) |
| } else { |
| bail!("Not enough data left in buffer to read a u32 value"); |
| } |
| } |
| |
| fn read_bool(&mut self) -> Result<bool> { |
| Ok(self.read_u8()? == 1) |
| } |
| |
| fn read_string(&mut self) -> Result<String> { |
| let size = self.read_u8()? as usize; |
| let slice; |
| (slice, self.buf) = self.buf.split_at(size); |
| String::from_utf8(slice.into()).context("Invalid string data") |
| } |
| |
| fn read_long_string(&mut self) -> Result<String> { |
| let size = self.read_u16()? as usize; |
| let slice; |
| (slice, self.buf) = self.buf.split_at(size); |
| String::from_utf8(slice.into()).context("Invalid string data") |
| } |
| |
| fn read_optional_long_string(&mut self) -> Result<Option<String>> { |
| Ok(Some(self.read_long_string()?).filter(|str| !str.is_empty())) |
| } |
| |
| fn read_type(&mut self) -> Result<Type> { |
| let value = self.read_u8()?; |
| Ok(match value { |
| codes::TYPE_U8 => Type::UInt8, |
| codes::TYPE_I8 => Type::Int8, |
| codes::TYPE_U16 => Type::UInt16, |
| codes::TYPE_I16 => Type::Int16, |
| codes::TYPE_U32 => Type::UInt32, |
| codes::TYPE_I32 => Type::Int32, |
| codes::TYPE_U64 => Type::UInt64, |
| codes::TYPE_I64 => Type::Int64, |
| codes::TYPE_F32 => Type::Float32, |
| codes::TYPE_F64 => Type::Float64, |
| codes::TYPE_BOOL => Type::Boolean, |
| codes::TYPE_STRING => Type::String, |
| codes::TYPE_DURATION => Type::Duration, |
| codes::TYPE_SYSTEM_TIME => Type::Timestamp, |
| codes::TYPE_RECORD => Type::Record { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| }, |
| codes::TYPE_ENUM => Type::Enum { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| }, |
| codes::TYPE_INTERFACE => Type::Object { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| imp: ObjectImpl::Struct, |
| }, |
| codes::TYPE_TRAIT_INTERFACE => Type::Object { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| imp: ObjectImpl::Trait, |
| }, |
| codes::TYPE_CALLBACK_TRAIT_INTERFACE => Type::Object { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| imp: ObjectImpl::CallbackTrait, |
| }, |
| codes::TYPE_CALLBACK_INTERFACE => Type::CallbackInterface { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| }, |
| codes::TYPE_CUSTOM => Type::Custom { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| builtin: Box::new(self.read_type()?), |
| }, |
| codes::TYPE_OPTION => Type::Optional { |
| inner_type: Box::new(self.read_type()?), |
| }, |
| codes::TYPE_VEC => { |
| let inner_type = self.read_type()?; |
| if inner_type == Type::UInt8 { |
| Type::Bytes |
| } else { |
| Type::Sequence { |
| inner_type: Box::new(inner_type), |
| } |
| } |
| } |
| codes::TYPE_HASH_MAP => Type::Map { |
| key_type: Box::new(self.read_type()?), |
| value_type: Box::new(self.read_type()?), |
| }, |
| codes::TYPE_UNIT => bail!("Unexpected TYPE_UNIT"), |
| codes::TYPE_RESULT => bail!("Unexpected TYPE_RESULT"), |
| _ => bail!("Unexpected metadata type code: {value:?}"), |
| }) |
| } |
| |
| fn read_optional_type(&mut self) -> Result<Option<Type>> { |
| Ok(match self.peek_u8()? { |
| codes::TYPE_UNIT => { |
| _ = self.read_u8(); |
| None |
| } |
| _ => Some(self.read_type()?), |
| }) |
| } |
| |
| fn read_return_type(&mut self) -> Result<(Option<Type>, Option<Type>)> { |
| Ok(match self.peek_u8()? { |
| codes::TYPE_UNIT => { |
| _ = self.read_u8(); |
| (None, None) |
| } |
| codes::TYPE_RESULT => { |
| _ = self.read_u8(); |
| (self.read_optional_type()?, self.read_optional_type()?) |
| } |
| _ => (Some(self.read_type()?), None), |
| }) |
| } |
| |
| fn read_func(&mut self) -> Result<FnMetadata> { |
| let module_path = self.read_string()?; |
| let name = self.read_string()?; |
| let is_async = self.read_bool()?; |
| let inputs = self.read_inputs()?; |
| let (return_type, throws) = self.read_return_type()?; |
| let docstring = self.read_optional_long_string()?; |
| Ok(FnMetadata { |
| module_path, |
| name, |
| is_async, |
| inputs, |
| return_type, |
| throws, |
| docstring, |
| checksum: self.calc_checksum(), |
| }) |
| } |
| |
| fn read_constructor(&mut self) -> Result<ConstructorMetadata> { |
| let module_path = self.read_string()?; |
| let self_name = self.read_string()?; |
| let name = self.read_string()?; |
| let is_async = self.read_bool()?; |
| let inputs = self.read_inputs()?; |
| let (return_type, throws) = self.read_return_type()?; |
| let docstring = self.read_optional_long_string()?; |
| |
| return_type |
| .filter(|t| { |
| matches!( |
| t, |
| Type::Object { name, imp: ObjectImpl::Struct, .. } if name == &self_name |
| ) |
| }) |
| .context("Constructor return type must be Arc<Self>")?; |
| |
| Ok(ConstructorMetadata { |
| module_path, |
| self_name, |
| is_async, |
| name, |
| inputs, |
| throws, |
| checksum: self.calc_checksum(), |
| docstring, |
| }) |
| } |
| |
| fn read_method(&mut self) -> Result<MethodMetadata> { |
| let module_path = self.read_string()?; |
| let self_name = self.read_string()?; |
| let name = self.read_string()?; |
| let is_async = self.read_bool()?; |
| let inputs = self.read_inputs()?; |
| let (return_type, throws) = self.read_return_type()?; |
| let docstring = self.read_optional_long_string()?; |
| Ok(MethodMetadata { |
| module_path, |
| self_name, |
| name, |
| is_async, |
| inputs, |
| return_type, |
| throws, |
| takes_self_by_arc: false, // not emitted by macros |
| checksum: self.calc_checksum(), |
| docstring, |
| }) |
| } |
| |
| fn read_record(&mut self) -> Result<RecordMetadata> { |
| Ok(RecordMetadata { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| fields: self.read_fields()?, |
| docstring: self.read_optional_long_string()?, |
| }) |
| } |
| |
| fn read_enum(&mut self) -> Result<EnumMetadata> { |
| let module_path = self.read_string()?; |
| let name = self.read_string()?; |
| let forced_flatness = match self.read_u8()? { |
| 0 => None, |
| 1 => Some(false), |
| 2 => Some(true), |
| _ => unreachable!("invalid flatness"), |
| }; |
| let discr_type = if self.read_bool()? { |
| Some(self.read_type()?) |
| } else { |
| None |
| }; |
| let variants = if forced_flatness == Some(true) { |
| self.read_flat_variants()? |
| } else { |
| self.read_variants()? |
| }; |
| |
| Ok(EnumMetadata { |
| module_path, |
| name, |
| forced_flatness, |
| discr_type, |
| variants, |
| non_exhaustive: self.read_bool()?, |
| docstring: self.read_optional_long_string()?, |
| }) |
| } |
| |
| fn read_object(&mut self, imp: ObjectImpl) -> Result<ObjectMetadata> { |
| Ok(ObjectMetadata { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| imp, |
| docstring: self.read_optional_long_string()?, |
| }) |
| } |
| |
| fn read_uniffi_trait(&mut self) -> Result<UniffiTraitMetadata> { |
| let code = self.read_u8()?; |
| let mut read_metadata_method = || -> Result<MethodMetadata> { |
| let code = self.read_u8()?; |
| ensure!(code == codes::METHOD, "expected METHOD but read {code}"); |
| self.read_method() |
| }; |
| |
| Ok(match UniffiTraitDiscriminants::from(code)? { |
| UniffiTraitDiscriminants::Debug => UniffiTraitMetadata::Debug { |
| fmt: read_metadata_method()?, |
| }, |
| UniffiTraitDiscriminants::Display => UniffiTraitMetadata::Display { |
| fmt: read_metadata_method()?, |
| }, |
| UniffiTraitDiscriminants::Eq => UniffiTraitMetadata::Eq { |
| eq: read_metadata_method()?, |
| ne: read_metadata_method()?, |
| }, |
| UniffiTraitDiscriminants::Hash => UniffiTraitMetadata::Hash { |
| hash: read_metadata_method()?, |
| }, |
| }) |
| } |
| |
| fn read_callback_interface(&mut self) -> Result<CallbackInterfaceMetadata> { |
| Ok(CallbackInterfaceMetadata { |
| module_path: self.read_string()?, |
| name: self.read_string()?, |
| docstring: self.read_optional_long_string()?, |
| }) |
| } |
| |
| fn read_trait_method(&mut self) -> Result<TraitMethodMetadata> { |
| let module_path = self.read_string()?; |
| let trait_name = self.read_string()?; |
| let index = self.read_u32()?; |
| let name = self.read_string()?; |
| let is_async = self.read_bool()?; |
| let inputs = self.read_inputs()?; |
| let (return_type, throws) = self.read_return_type()?; |
| let docstring = self.read_optional_long_string()?; |
| Ok(TraitMethodMetadata { |
| module_path, |
| trait_name, |
| index, |
| name, |
| is_async, |
| inputs, |
| return_type, |
| throws, |
| takes_self_by_arc: false, // not emitted by macros |
| checksum: self.calc_checksum(), |
| docstring, |
| }) |
| } |
| |
| fn read_fields(&mut self) -> Result<Vec<FieldMetadata>> { |
| let len = self.read_u8()?; |
| (0..len) |
| .map(|_| { |
| let name = self.read_string()?; |
| let ty = self.read_type()?; |
| let default = self.read_optional_default(&name, &ty)?; |
| Ok(FieldMetadata { |
| name, |
| ty, |
| default, |
| docstring: self.read_optional_long_string()?, |
| }) |
| }) |
| .collect() |
| } |
| |
| fn read_variants(&mut self) -> Result<Vec<VariantMetadata>> { |
| let len = self.read_u8()?; |
| (0..len) |
| .map(|_| { |
| Ok(VariantMetadata { |
| name: self.read_string()?, |
| discr: self.read_optional_default("<variant-value>", &Type::UInt64)?, |
| fields: self.read_fields()?, |
| docstring: self.read_optional_long_string()?, |
| }) |
| }) |
| .collect() |
| } |
| |
| fn read_flat_variants(&mut self) -> Result<Vec<VariantMetadata>> { |
| let len = self.read_u8()?; |
| (0..len) |
| .map(|_| { |
| Ok(VariantMetadata { |
| name: self.read_string()?, |
| discr: None, |
| fields: vec![], |
| docstring: self.read_optional_long_string()?, |
| }) |
| }) |
| .collect() |
| } |
| |
| fn read_inputs(&mut self) -> Result<Vec<FnParamMetadata>> { |
| let len = self.read_u8()?; |
| (0..len) |
| .map(|_| { |
| let name = self.read_string()?; |
| let ty = self.read_type()?; |
| let default = self.read_optional_default(&name, &ty)?; |
| Ok(FnParamMetadata { |
| name, |
| ty, |
| default, |
| // not emitted by macros |
| by_ref: false, |
| optional: false, |
| }) |
| }) |
| .collect() |
| } |
| |
| fn calc_checksum(&self) -> Option<u16> { |
| let bytes_read = self.initial_data.len() - self.buf.len(); |
| let metadata_buf = &self.initial_data[..bytes_read]; |
| Some(checksum_metadata(metadata_buf)) |
| } |
| |
| fn read_optional_default(&mut self, name: &str, ty: &Type) -> Result<Option<LiteralMetadata>> { |
| if self.read_bool()? { |
| Ok(Some(self.read_default(name, ty)?)) |
| } else { |
| Ok(None) |
| } |
| } |
| |
| fn read_default(&mut self, name: &str, ty: &Type) -> Result<LiteralMetadata> { |
| let literal_kind = self.read_u8()?; |
| |
| Ok(match literal_kind { |
| codes::LIT_STR => { |
| ensure!( |
| matches!(ty, Type::String), |
| "field {name} of type {ty:?} can't have a default value of type string" |
| ); |
| LiteralMetadata::String(self.read_string()?) |
| } |
| codes::LIT_INT => { |
| let base10_digits = self.read_string()?; |
| // procmacros emit the type for discriminant values based purely on whether the constant |
| // is positive or negative. |
| let ty = if !base10_digits.is_empty() |
| && base10_digits.as_bytes()[0] == b'-' |
| && ty == &Type::UInt64 |
| { |
| &Type::Int64 |
| } else { |
| ty |
| }; |
| macro_rules! parse_int { |
| ($ty:ident, $variant:ident) => { |
| LiteralMetadata::$variant( |
| base10_digits |
| .parse::<$ty>() |
| .with_context(|| { |
| format!("parsing default for field {name}: {base10_digits}") |
| })? |
| .into(), |
| Radix::Decimal, |
| ty.to_owned(), |
| ) |
| }; |
| } |
| |
| match ty { |
| Type::UInt8 => parse_int!(u8, UInt), |
| Type::Int8 => parse_int!(i8, Int), |
| Type::UInt16 => parse_int!(u16, UInt), |
| Type::Int16 => parse_int!(i16, Int), |
| Type::UInt32 => parse_int!(u32, UInt), |
| Type::Int32 => parse_int!(i32, Int), |
| Type::UInt64 => parse_int!(u64, UInt), |
| Type::Int64 => parse_int!(i64, Int), |
| _ => { |
| bail!("field {name} of type {ty:?} can't have a default value of type integer"); |
| } |
| } |
| } |
| codes::LIT_FLOAT => match ty { |
| Type::Float32 | Type::Float64 => { |
| LiteralMetadata::Float(self.read_string()?, ty.to_owned()) |
| } |
| _ => { |
| bail!("field {name} of type {ty:?} can't have a default value of type float"); |
| } |
| }, |
| codes::LIT_BOOL => LiteralMetadata::Boolean(self.read_bool()?), |
| codes::LIT_NONE => match ty { |
| Type::Optional { .. } => LiteralMetadata::None, |
| _ => bail!("field {name} of type {ty:?} can't have a default value of None"), |
| }, |
| codes::LIT_SOME => match ty { |
| Type::Optional { inner_type, .. } => LiteralMetadata::Some { |
| inner: Box::new(self.read_default(name, inner_type)?), |
| }, |
| _ => bail!("field {name} of type {ty:?} can't have a default value of None"), |
| }, |
| codes::LIT_EMPTY_SEQ => LiteralMetadata::EmptySequence, |
| _ => bail!("Unexpected literal kind code: {literal_kind:?}"), |
| }) |
| } |
| } |