Print Aqua-compatible types (#86)

This commit is contained in:
Mike Voronov
2021-05-20 20:20:57 +03:00
committed by GitHub
parent c51b5982d6
commit c62a278897
44 changed files with 1155 additions and 583 deletions

View File

@ -0,0 +1,26 @@
[package]
name = "marine-module-interface"
description = "Fluence Marine module interface"
version = "0.1.0"
authors = ["Fluence Labs"]
license = "Apache-2.0"
edition = "2018"
[lib]
name = "marine_module_interface"
path = "src/lib.rs"
[dependencies]
marine-it-interfaces = { path = "../it-interfaces", version = "0.4.0" }
#marine-it-generator = { path = "../it-generator", verision = "0.5.2"}
anyhow = "1.0.31"
walrus = "0.18.0"
wasmer-core = { package = "wasmer-runtime-core-fl", version = "0.17.0"}
wasmer-it = { package = "wasmer-interface-types-fl", version = "0.20.0" }
nom = "5.1"
itertools = "0.10.0"
semver = "0.11.0"
serde = "=1.0.118"
thiserror = "1.0.24"

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* 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.
*/
use crate::it_interface::ITInterfaceError;
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum InterfaceError {
#[error("record type with type id {0} not found")]
NotFoundRecordTypeId(u64),
#[error("{0}")]
ITInterfaceError(#[from] ITInterfaceError),
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use super::ModuleInterface;
use super::InterfaceResult;
use super::FunctionSignature;
use super::records_transformer::RecordsTransformer;
use crate::it_interface::IModuleInterface;
use crate::it_interface::IFunctionSignature;
use crate::it_interface::IRecordTypes;
pub fn it_to_module_interface(mm_interface: IModuleInterface) -> InterfaceResult<ModuleInterface> {
let record_types = mm_interface.export_record_types;
let function_signatures = mm_interface
.function_signatures
.into_iter()
.map(|sign| serialize_function_signature(sign, &record_types))
.collect();
let record_types = RecordsTransformer::transform(&record_types)?;
let interface = ModuleInterface {
function_signatures,
record_types,
};
Ok(interface)
}
fn serialize_function_signature(
signature: IFunctionSignature,
record_types: &IRecordTypes,
) -> FunctionSignature {
use super::itype_text_view;
let arguments = signature
.arguments
.iter()
.map(|arg| (arg.name.clone(), itype_text_view(&arg.ty, record_types)))
.collect();
let output_types = signature
.outputs
.iter()
.map(|itype| itype_text_view(itype, record_types))
.collect();
FunctionSignature {
name: signature.name.to_string(),
arguments,
output_types,
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use crate::it_interface::IRecordTypes;
use wasmer_it::IType;
/// Converts the supplied IType to a Aqua0compatible text representation.
///
/// SAFETY:
/// It's assumed that arguments are well-formed and all records have a corresponded type in
/// record_types.
pub fn itype_text_view(arg_ty: &IType, record_types: &IRecordTypes) -> String {
match arg_ty {
IType::Record(record_type_id) => {
// assumed that this functions called with well-formed args
let record = record_types.get(record_type_id).unwrap();
record.name.clone()
}
IType::Array(array_ty) => format!("[]{}", itype_text_view(array_ty, record_types)),
IType::Boolean => "bool".to_string(),
IType::S8 => "i8".to_string(),
IType::S16 => "i16".to_string(),
IType::S32 => "i32".to_string(),
IType::S64 => "i64".to_string(),
IType::U8 => "u8".to_string(),
IType::U16 => "u16".to_string(),
IType::U32 => "u32".to_string(),
IType::U64 => "u64".to_string(),
IType::F32 => "f32".to_string(),
IType::F64 => "f64".to_string(),
IType::String => "string".to_string(),
IType::ByteArray => "[]u8".to_string(),
IType::I32 => "i32".to_string(),
IType::I64 => "i64".to_string(),
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
mod errors;
mod interface_transformer;
mod itype_to_text;
mod module_interface;
mod records_transformer;
pub use errors::InterfaceError;
pub use interface_transformer::it_to_module_interface;
pub use itype_to_text::*;
pub use module_interface::*;
pub type InterfaceResult<T> = std::result::Result<T, InterfaceError>;
use marine_it_interfaces::MITInterfaces;
/// Returns interface of a Marine module.
pub fn get_interface(mit: &MITInterfaces<'_>) -> InterfaceResult<ModuleInterface> {
let it_interface = crate::it_interface::get_interface(mit)?;
let interface = it_to_module_interface(it_interface)?;
Ok(interface)
}

View File

@ -0,0 +1,78 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use serde::Serialize;
use serde::Deserialize;
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct FunctionSignature {
pub name: String,
pub arguments: Vec<(String, String)>,
pub output_types: Vec<String>,
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct RecordField {
pub name: String,
pub ty: String,
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct RecordType {
pub name: String,
pub id: u64,
pub fields: Vec<RecordField>,
}
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct ModuleInterface {
pub function_signatures: Vec<FunctionSignature>,
// record types are guaranteed to be topological sorted
pub record_types: Vec<RecordType>,
}
use std::fmt;
impl fmt::Display for FunctionSignature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use itertools::Itertools;
let output = match self.output_types.len() {
0 => "()",
1 => &self.output_types[0],
_ => unimplemented!("more than 1 output type is unsupported"),
};
if self.arguments.is_empty() {
writeln!(f, "{}: -> {}", self.name, output)
} else {
let args = self.arguments.iter().map(|(_, ty)| ty).format(",");
writeln!(f, "{}: {} -> {}", self.name, args, output)
}
}
}
impl fmt::Display for RecordType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "data {}:", self.name)?;
for field in self.fields.iter() {
writeln!(f, " {}: {}", field.name, field.ty)?;
}
Ok(())
}
}

View File

@ -0,0 +1,128 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* 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.
*/
use super::RecordType;
use super::RecordField;
use super::InterfaceResult;
use super::InterfaceError;
use crate::it_interface::IRecordTypes;
use wasmer_it::IRecordType;
use wasmer_it::IType;
use std::collections::HashSet;
use std::rc::Rc;
pub(crate) struct RecordsTransformer {
used: HashSet<u64>,
sorted_order: Vec<u64>,
}
impl RecordsTransformer {
pub(crate) fn transform(record_types: &IRecordTypes) -> InterfaceResult<Vec<RecordType>> {
let records_count = record_types.len();
let mut transformer = Self {
used: HashSet::with_capacity(records_count),
sorted_order: Vec::with_capacity(records_count),
};
// TODO: check for cycles
transformer.topological_sort(record_types)?;
let record_types = transformer.into_transformed_records(record_types);
Ok(record_types)
}
fn topological_sort(&mut self, exported_records: &IRecordTypes) -> InterfaceResult<()> {
for (id, record) in exported_records {
self.dfs(*id, record, exported_records)?;
}
Ok(())
}
fn dfs(
&mut self,
record_id: u64,
record: &Rc<IRecordType>,
exported_records: &IRecordTypes,
) -> InterfaceResult<()> {
if !self.used.insert(record_id) {
return Ok(());
}
for field in (&record.fields).iter() {
self.type_dfs(&field.ty, exported_records)?;
}
self.sorted_order.push(record_id);
Ok(())
}
fn type_dfs(
&mut self,
field_ty: &IType,
exported_records: &IRecordTypes,
) -> InterfaceResult<()> {
match field_ty {
IType::Record(type_id) => {
let child_record = exported_records
.get(type_id)
.ok_or(InterfaceError::NotFoundRecordTypeId(*type_id))?;
self.dfs(*type_id, child_record, exported_records)
}
IType::Array(ty) => self.type_dfs(ty, exported_records),
_ => Ok(()),
}
}
fn into_transformed_records(self, record_types: &IRecordTypes) -> Vec<RecordType> {
self.sorted_order
.into_iter()
.map(|id| {
// unwrap is safe here because sorted_order is constructed based on record_types
let record = record_types.get(&id).unwrap();
Self::convert_record(id, record, &record_types)
})
.collect::<Vec<_>>()
}
fn convert_record(
id: u64,
record: &Rc<IRecordType>,
record_types: &IRecordTypes,
) -> RecordType {
use super::itype_text_view;
let fields = record
.fields
.iter()
.map(|field| RecordField {
name: field.name.clone(),
ty: itype_text_view(&field.ty, record_types),
})
.collect::<Vec<_>>();
RecordType {
name: record.name.clone(),
id,
fields,
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* 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.
*/
use marine_it_interfaces::MITInterfacesError;
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
pub enum ITInterfaceError {
#[error("type with idx = {0} isn't a function type")]
ITTypeNotFunction(u32),
#[error("record type with type id {0} not found")]
NotFoundRecordTypeId(u64),
#[error("mailformed module: a record contains more recursion level then allowed")]
TooManyRecursionLevels,
#[error("{0}")]
MITInterfacesError(#[from] MITInterfacesError),
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use super::IFunctionSignature;
use super::ITInterfaceError;
use super::RIResult;
use marine_it_interfaces::MITInterfaces;
use std::rc::Rc;
pub struct ITExportFuncDescriptor<'n> {
pub adapter_function_type: u32,
pub name: &'n str,
}
/// Returns all exported IT functions descriptors.
pub fn get_export_funcs_descriptors<'i>(
mit: &'i MITInterfaces<'_>,
) -> Vec<ITExportFuncDescriptor<'i>> {
// An IT function is exported if it lies in export functions and have implementation.
// An export IT function without implementation is a hack and used to call core function from
// a Wasm module. This hack is needed because there is only one call instruction in the
// interface-types crates and it's needed to distinguish somehow between calling export IT or
// core functions. This scheme is a kind of mess and it needs to be refactored one day.
mit.implementations()
.filter_map(|(adapter_function_type, core_function_type)| {
mit.exports_by_type(*core_function_type)
.map(|export_function_name| (adapter_function_type, export_function_name))
})
.map(|(&adapter_function_type, export_function_names)| {
export_function_names
.iter()
.map(move |name| ITExportFuncDescriptor {
adapter_function_type,
name,
})
})
.flatten()
.collect::<Vec<_>>()
}
/// Returns all exported IT functions.
pub fn get_export_funcs(mit: &MITInterfaces<'_>) -> RIResult<Vec<IFunctionSignature>> {
use marine_it_interfaces::ITAstType;
let funcs_descriptors = get_export_funcs_descriptors(mit);
funcs_descriptors
.into_iter()
.map(|descriptor| {
let it_type = mit.type_by_idx_r(descriptor.adapter_function_type)?;
match it_type {
ITAstType::Function {
arguments,
output_types,
} => {
let signature = IFunctionSignature {
name: Rc::new(descriptor.name.to_string()),
arguments: arguments.clone(),
outputs: output_types.clone(),
adapter_function_type: descriptor.adapter_function_type,
};
Ok(signature)
}
_ => Err(ITInterfaceError::ITTypeNotFunction(
descriptor.adapter_function_type,
)),
}
})
.collect::<RIResult<Vec<IFunctionSignature>>>()
}

View File

@ -0,0 +1,129 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use super::IRecordTypes;
use super::RIResult;
use super::ITInterfaceError;
use super::IFunctionSignature;
use marine_it_interfaces::MITInterfaces;
use wasmer_it::IType;
use std::collections::HashMap;
const TYPE_RESOLVE_RECURSION_LIMIT: u32 = 1024;
pub struct FullRecordTypes {
pub record_types: IRecordTypes,
pub export_record_types: IRecordTypes,
}
pub fn get_record_types<'f>(
mit: &MITInterfaces<'_>,
export_funcs: impl ExactSizeIterator<Item = &'f IFunctionSignature>,
) -> RIResult<FullRecordTypes> {
let all_record_types = get_all_records(mit);
let mut export_record_types = HashMap::new();
let itypes = export_funcs.flat_map(|s| {
s.arguments
.as_ref()
.iter()
.map(|a| &a.ty)
.chain(s.outputs.as_ref().iter())
});
for itype in itypes {
handle_itype(itype, &all_record_types, &mut export_record_types, 0)?;
}
let full_record_types = FullRecordTypes {
record_types: all_record_types,
export_record_types,
};
Ok(full_record_types)
}
fn handle_itype(
itype: &IType,
all_record_types: &IRecordTypes,
export_record_types: &mut IRecordTypes,
recursion_level: u32,
) -> RIResult<()> {
if recursion_level > TYPE_RESOLVE_RECURSION_LIMIT {
return Err(ITInterfaceError::TooManyRecursionLevels);
}
match itype {
IType::Record(record_type_id) => handle_record_type(
*record_type_id,
all_record_types,
export_record_types,
recursion_level + 1,
)?,
IType::Array(array_ty) => handle_itype(
array_ty,
all_record_types,
export_record_types,
recursion_level + 1,
)?,
_ => {}
}
Ok(())
}
fn handle_record_type(
record_type_id: u64,
all_record_types: &IRecordTypes,
export_record_types: &mut IRecordTypes,
recursion_level: u32,
) -> RIResult<()> {
let record_type = all_record_types
.get(&record_type_id)
.ok_or(ITInterfaceError::NotFoundRecordTypeId(record_type_id))?;
export_record_types.insert(record_type_id, record_type.clone());
for field in record_type.fields.iter() {
handle_itype(
&field.ty,
all_record_types,
export_record_types,
recursion_level + 1,
)?;
}
Ok(())
}
fn get_all_records(mit: &MITInterfaces<'_>) -> IRecordTypes {
use marine_it_interfaces::ITAstType;
mit.types()
.enumerate()
.fold(HashMap::new(), |mut record_types_by_id, (id, ty)| {
match ty {
ITAstType::Record(record_type) => {
record_types_by_id.insert(id as u64, record_type.clone());
}
ITAstType::Function { .. } => {}
};
record_types_by_id
})
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
use wasmer_it::IType;
use wasmer_it::ast::FunctionArg as IFunctionArg;
use wasmer_it::IRecordType;
use serde::Serialize;
use serde::Deserialize;
use std::collections::HashMap;
use std::rc::Rc;
pub type IRecordTypes = HashMap<u64, Rc<IRecordType>>;
/// Represent a function type inside Marine module.
#[derive(PartialEq, Eq, Debug, Clone, Hash, Serialize, Deserialize)]
pub struct IFunctionSignature {
pub name: Rc<String>,
pub arguments: Rc<Vec<IFunctionArg>>,
pub outputs: Rc<Vec<IType>>,
pub adapter_function_type: u32,
}
/// Represent an interface of a Wasm module.
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct IModuleInterface {
pub export_record_types: IRecordTypes,
pub record_types: IRecordTypes,
pub function_signatures: Vec<IFunctionSignature>,
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
mod errors;
mod export_it_functions;
mod export_it_records;
mod it_module_interface;
pub use errors::*;
pub use export_it_functions::*;
pub use export_it_records::*;
pub use it_module_interface::*;
pub type RIResult<T> = std::result::Result<T, ITInterfaceError>;
use marine_it_interfaces::MITInterfaces;
/// Returns Marine module interface that includes both export and all record types.
pub fn get_interface(mit: &MITInterfaces<'_>) -> RIResult<IModuleInterface> {
let function_signatures = get_export_funcs(mit)?;
let FullRecordTypes {
record_types,
export_record_types,
} = get_record_types(mit, function_signatures.iter())?;
let mm_interface = IModuleInterface {
record_types,
export_record_types,
function_signatures,
};
Ok(mm_interface)
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* 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.
*/
#![warn(rust_2018_idioms)]
#![deny(
dead_code,
nonstandard_style,
unused_imports,
unused_mut,
unused_variables,
unused_unsafe,
unreachable_patterns
)]
pub mod interface;
pub mod it_interface;