// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: 2020 The Gleam contributors //! Seriaisation and deserialisation of Gleam compiler metadata into binary files //! using serde. #[cfg(test)] mod tests; use std::{collections::HashMap, sync::Arc}; use crate::{ ast::{ BitArrayOption, BitArraySegment, CallArg, Constant, RecordBeingUpdated, RecordUpdateArg, TypedConstant, }, type_::{ self, AccessorsMap, ModuleInterface, RecordAccessor, Type, TypeAliasConstructor, TypeConstructor, TypeValueConstructor, TypeValueConstructorField, TypeVar, TypeVariantConstructors, ValueConstructor, ValueConstructorVariant, }, uid::UniqueIdGenerator, }; pub fn encode(module: &ModuleInterface) -> Result, bincode::error::EncodeError> { bincode::serde::encode_to_vec(module, bincode::config::legacy()) } pub fn decode( bytes: &[u8], ids: UniqueIdGenerator, ) -> Result { bincode::serde::decode_from_slice(bytes, bincode::config::legacy()) .map(|(module, _)| remap_type_variable_ids(module, ids)) } fn remap_type_variable_ids(module: ModuleInterface, ids: UniqueIdGenerator) -> ModuleInterface { RemapIds::new(ids).module(module) } struct RemapIds { ids: UniqueIdGenerator, remapped_variable_ids: HashMap, } impl RemapIds { fn new(ids: UniqueIdGenerator) -> Self { Self { ids, remapped_variable_ids: HashMap::new(), } } fn module(&mut self, module: ModuleInterface) -> ModuleInterface { let ModuleInterface { name, origin, package, types, types_value_constructors, values, accessors, line_numbers, src_path, is_internal, warnings, minimum_required_version, type_aliases, documentation, contains_echo, references, inline_functions, } = module; let types = types .into_iter() .map(|(name, type_)| (name, self.type_constructor(type_))) .collect(); let types_value_constructors = types_value_constructors .into_iter() .map(|(name, type_)| (name, self.type_variant_constructors(type_))) .collect(); let values = values .into_iter() .map(|(name, value)| (name, self.value_constructor(value))) .collect(); let accessors = accessors .into_iter() .map(|(name, accessors)| (name, self.accessors_map(accessors))) .collect(); let type_aliases = type_aliases .into_iter() .map(|(name, type_)| (name, self.type_alias(type_))) .collect(); ModuleInterface { name, origin, package, types, types_value_constructors, values, accessors, line_numbers, src_path, is_internal, warnings, minimum_required_version, type_aliases, documentation, contains_echo, references, inline_functions, } } fn type_constructor(&mut self, type_: TypeConstructor) -> TypeConstructor { let TypeConstructor { publicity, origin, module, parameters, type_, deprecation, documentation, } = type_; let parameters = parameters .into_iter() .map(|parameter| self.type_(parameter)) .collect(); let type_ = self.type_(type_); TypeConstructor { publicity, origin, module, parameters, type_, deprecation, documentation, } } fn type_variant_constructors( &mut self, type_: TypeVariantConstructors, ) -> TypeVariantConstructors { let TypeVariantConstructors { type_parameters_ids, opaque, variants, } = type_; let type_parameters_ids = type_parameters_ids .into_iter() .map(|id| self.id(id)) .collect(); let variants = variants .into_iter() .map(|variant| self.type_value_constructor(variant)) .collect(); TypeVariantConstructors { type_parameters_ids, opaque, variants, } } fn type_value_constructor(&mut self, variant: TypeValueConstructor) -> TypeValueConstructor { let TypeValueConstructor { name, parameters, documentation, } = variant; let parameters = parameters .into_iter() .map( |TypeValueConstructorField { type_, label, documentation, }| TypeValueConstructorField { type_: self.type_(type_), label, documentation, }, ) .collect(); TypeValueConstructor { name, parameters, documentation, } } fn value_constructor(&mut self, value: ValueConstructor) -> ValueConstructor { let ValueConstructor { publicity, deprecation, variant, type_, } = value; let variant = self.value_constructor_variant(variant); let type_ = self.type_(type_); ValueConstructor { publicity, deprecation, variant, type_, } } fn value_constructor_variant( &mut self, variant: ValueConstructorVariant, ) -> ValueConstructorVariant { match variant { ValueConstructorVariant::LocalVariable { location, origin } => { ValueConstructorVariant::LocalVariable { location, origin } } ValueConstructorVariant::ModuleConstant { documentation, location, module, name, literal, implementations, } => ValueConstructorVariant::ModuleConstant { documentation, location, module, name, literal: self.constant(literal), implementations, }, ValueConstructorVariant::ModuleFn { name, field_map, module, arity, location, documentation, implementations, external_erlang, external_javascript, purity, } => ValueConstructorVariant::ModuleFn { name, field_map, module, arity, location, documentation, implementations, external_erlang, external_javascript, purity, }, ValueConstructorVariant::Record { name, arity, field_map, location, module, variants_count, variant_index, documentation, } => ValueConstructorVariant::Record { name, arity, field_map, location, module, variants_count, variant_index, documentation, }, } } fn constant(&mut self, constant: TypedConstant) -> TypedConstant { match constant { Constant::Int { location, value, int_value, } => Constant::Int { location, value, int_value, }, Constant::Float { location, value, float_value, } => Constant::Float { location, value, float_value, }, Constant::String { location, value } => Constant::String { location, value }, Constant::Tuple { location, elements, type_, } => Constant::Tuple { location, elements: elements .into_iter() .map(|element| self.constant(element)) .collect(), type_: self.type_(type_), }, Constant::List { location, elements, type_, tail, } => Constant::List { location, elements: elements .into_iter() .map(|element| self.constant(element)) .collect(), type_: self.type_(type_), tail: tail.map(|tail| Box::new(self.constant(*tail))), }, Constant::Record { location, module, name, arguments, type_, field_map, record_constructor, } => Constant::Record { location, module, name, arguments: arguments.map(|arguments| { arguments .into_iter() .map( |CallArg { label, location, value, implicit, }| CallArg { label, location, value: self.constant(value), implicit, }, ) .collect() }), type_: self.type_(type_), field_map, record_constructor: record_constructor .map(|constructor| Box::new(self.value_constructor(*constructor))), }, Constant::RecordUpdate { location, constructor_location, module, name, record: RecordBeingUpdated { base: record, location: record_location, }, arguments, type_, field_map, } => Constant::RecordUpdate { location, constructor_location, module, name, record: RecordBeingUpdated { base: Box::new(self.constant(*record)), location: record_location, }, arguments: arguments .into_iter() .map( |RecordUpdateArg { label, location, value, }| RecordUpdateArg { label, location, value: self.constant(value), }, ) .collect(), type_: self.type_(type_), field_map, }, Constant::BitArray { location, segments } => Constant::BitArray { location, segments: segments .into_iter() .map(|segment| self.bit_array_segment(segment)) .collect(), }, Constant::Var { location, module, name, constructor, type_, } => Constant::Var { location, module, name, constructor: constructor .map(|constructor| Box::new(self.value_constructor(*constructor))), type_: self.type_(type_), }, Constant::StringConcatenation { location, left, right, } => Constant::StringConcatenation { location, left: Box::new(self.constant(*left)), right: Box::new(self.constant(*right)), }, Constant::Invalid { location, type_, extra_information, } => Constant::Invalid { location, type_: self.type_(type_), extra_information, }, Constant::Todo { location, type_, message, } => Constant::Todo { location, type_: self.type_(type_), message: message.map(|message| Box::new(self.constant(*message))), }, } } fn bit_array_segment( &mut self, segment: BitArraySegment>, ) -> BitArraySegment> { let BitArraySegment { location, value, options, type_, } = segment; let value = Box::new(self.constant(*value)); let options = options .into_iter() .map(|option| self.bit_array_option(option)) .collect(); let type_ = self.type_(type_); BitArraySegment { location, value, options, type_, } } fn bit_array_option( &mut self, option: BitArrayOption, ) -> BitArrayOption { match option { BitArrayOption::Bytes { .. } | BitArrayOption::Int { .. } | BitArrayOption::Float { .. } | BitArrayOption::Bits { .. } | BitArrayOption::Utf8 { .. } | BitArrayOption::Utf16 { .. } | BitArrayOption::Utf32 { .. } | BitArrayOption::Utf8Codepoint { .. } | BitArrayOption::Utf16Codepoint { .. } | BitArrayOption::Utf32Codepoint { .. } | BitArrayOption::Signed { .. } | BitArrayOption::Unsigned { .. } | BitArrayOption::Big { .. } | BitArrayOption::Little { .. } | BitArrayOption::Native { .. } | BitArrayOption::Unit { .. } => option, BitArrayOption::Size { location, value, short_form, } => BitArrayOption::Size { location, value: Box::new(self.constant(*value)), short_form, }, } } fn accessors_map(&mut self, accessors: AccessorsMap) -> AccessorsMap { let AccessorsMap { publicity, type_, shared_accessors, variant_specific_accessors, variant_positional_accessors, } = accessors; let type_ = self.type_(type_); let shared_accessors = shared_accessors .into_iter() .map(|(label, accessor)| (label, self.record_accessor(accessor))) .collect(); let variant_specific_accessors = variant_specific_accessors .into_iter() .map(|variant_accessors| { variant_accessors .into_iter() .map(|(label, accessor)| (label, self.record_accessor(accessor))) .collect() }) .collect(); let variant_positional_accessors = variant_positional_accessors .into_iter() .map(|variant_accessors| { variant_accessors .into_iter() .map(|type_| self.type_(type_)) .collect() }) .collect(); AccessorsMap { publicity, type_, shared_accessors, variant_specific_accessors, variant_positional_accessors, } } fn record_accessor(&mut self, accessor: RecordAccessor) -> RecordAccessor { let RecordAccessor { index, label, type_, documentation, } = accessor; RecordAccessor { index, label, type_: self.type_(type_), documentation, } } fn type_alias(&mut self, type_: TypeAliasConstructor) -> TypeAliasConstructor { let TypeAliasConstructor { publicity, module, type_, arity, deprecation, documentation, origin, parameters, } = type_; let type_ = self.type_(type_); let parameters = parameters .into_iter() .map(|parameter| self.type_(parameter)) .collect(); TypeAliasConstructor { publicity, module, type_, arity, deprecation, documentation, origin, parameters, } } fn type_(&mut self, type_: Arc) -> Arc { let type_ = match Arc::unwrap_or_clone(type_) { Type::Named { publicity, package, module, name, arguments, inferred_variant, } => Type::Named { publicity, package, module, name, arguments: arguments .into_iter() .map(|argument| self.type_(argument)) .collect(), inferred_variant, }, Type::Fn { arguments, return_ } => Type::Fn { arguments: arguments .into_iter() .map(|argument| self.type_(argument)) .collect(), return_: self.type_(return_), }, Type::Var { type_ } => return self.type_var(&type_.borrow()), Type::Tuple { elements } => Type::Tuple { elements: elements .into_iter() .map(|element| self.type_(element)) .collect(), }, }; Arc::new(type_) } fn type_var(&mut self, variable: &TypeVar) -> Arc { match variable { TypeVar::Link { type_ } => self.type_(type_.clone()), TypeVar::Unbound { id } => type_::prelude::unbound_var(self.id(*id)), TypeVar::Generic { id } => type_::prelude::generic_var(self.id(*id)), } } fn id(&mut self, id: u64) -> u64 { match self.remapped_variable_ids.get(&id) { Some(new_id) => *new_id, None => { let new_id = self.ids.next(); _ = self.remapped_variable_ids.insert(id, new_id); new_id } } } }