1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * 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::default_export_api_config::*;
use crate::errors::WITGeneratorError;
use crate::instructions_generator::WITGenerator;
use crate::instructions_generator::WITResolver;
use crate::Result;

pub use fluence_sdk_wit::SDKAst;
use wasmer_wit::ast::Interfaces;
use wasmer_wit::IRecordType;
use wasmer_wit::IType;

/// Parse generated by rust-sdk AST types, generate instructions and embed them to Wasm file.
pub fn embed_wit<P>(path: P) -> Result<()>
where
    P: Into<std::path::PathBuf>,
{
    let path = path.into();

    let wasm_module = walrus::ModuleConfig::new()
        .parse_file(path.clone())
        .map_err(|e| WITGeneratorError::IOError(format!("{:?} can't be parsed: {:?}", path, e)))?;

    let module_ast = wasm_ast_extractor(&wasm_module)?;
    let interfaces = generate_interfaces(&module_ast)?;

    let wasm_module = fce_wit_parser::delete_wit_section(wasm_module);
    let mut wasm_module = fce_wit_parser::embed_wit(wasm_module, &interfaces);

    wasm_module.emit_wasm_file(path).map_err(|e| {
        WITGeneratorError::IOError(format!("resulted Wasm file can't be emitted: {:?}", e))
    })
}

pub(crate) struct ModuleAST {
    pub(crate) records: Vec<fluence_sdk_wit::RecordType>,
    pub(crate) functions: Vec<fluence_sdk_wit::FnType>,
    pub(crate) extern_mods: Vec<fluence_sdk_wit::ExternModType>,
}

/// Extract all custom AST types previously embedded by rust-sdk from compiled binary.
fn wasm_ast_extractor(wasm_module: &walrus::Module) -> Result<ModuleAST> {
    use fluence_sdk_wit::*;

    let mut records: Vec<RecordType> = Vec::new();
    let mut functions: Vec<FnType> = Vec::new();
    let mut extern_mods: Vec<ExternModType> = Vec::new();

    // consider only sections name of that starts with GENERATED_SECTION_PREFIX
    for custom_module in wasm_module.customs.iter().filter(|(_, section)| {
        section
            .name()
            .starts_with(fluence_sdk_wit::GENERATED_SECTION_PREFIX)
    }) {
        let default_ids = walrus::IdsToIndices::default();
        let raw_data = custom_module.1.data(&default_ids);
        let decoded_json: SDKAst = serde_json::from_slice(&raw_data)?;
        match decoded_json {
            SDKAst::Record(record) => records.push(record),
            SDKAst::Function(function) => functions.push(function),
            SDKAst::ExternMod(extern_mod) => extern_mods.push(extern_mod),
        }
    }

    Ok(ModuleAST {
        records,
        functions,
        extern_mods,
    })
}

fn generate_interfaces(module_ast: &ModuleAST) -> Result<Interfaces<'_>> {
    let mut wit_resolver = WITResolver::default();
    generate_default_export_api(&mut wit_resolver.interfaces);

    for record in &module_ast.records {
        record.generate_wit(&mut wit_resolver)?;
    }
    validate_records(&wit_resolver)?;

    for function in &module_ast.functions {
        function.generate_wit(&mut wit_resolver)?;
    }
    for extern_mod in &module_ast.extern_mods {
        extern_mod.generate_wit(&mut wit_resolver)?;
    }

    Ok(wit_resolver.interfaces)
}

fn generate_default_export_api(interfaces: &mut Interfaces<'_>) {
    // TODO: the order is matter
    ALLOCATE_FUNC.update_interfaces(interfaces);
    RELEASE_OBJECTS.update_interfaces(interfaces);
    GET_RESULT_SIZE_FUNC.update_interfaces(interfaces);
    GET_RESULT_PTR_FUNC.update_interfaces(interfaces);
    SET_RESULT_SIZE_FUNC.update_interfaces(interfaces);
    SET_RESULT_PTR_FUNC.update_interfaces(interfaces);
}

fn validate_records(wit_resolver: &WITResolver<'_>) -> Result<()> {
    fn validate_record_type(
        record_type: &IRecordType,
        recursion_level: u32,
        wit_resolver: &WITResolver<'_>,
    ) -> Result<()> {
        if recursion_level >= crate::TYPE_RESOLVE_RECURSION_LIMIT {
            return Err(WITGeneratorError::CorruptedRecord(String::from(
                "too many inner structures level",
            )));
        }

        for field in record_type.fields.iter() {
            match &field.ty {
                IType::Record(record_type_id) => {
                    let inner_record_type = wit_resolver.get_record_type(*record_type_id)?;
                    validate_record_type(&inner_record_type, recursion_level + 1, wit_resolver)?;
                }
                _ => continue,
            }
        }

        Ok(())
    }

    if wit_resolver.unresolved_types_count() != 0 {
        return Err(WITGeneratorError::CorruptedRecord(format!(
            "{} types unresolved",
            wit_resolver.unresolved_types_count()
        )));
    }

    for ty in wit_resolver.interfaces.types.iter() {
        let record_type = match ty {
            wasmer_wit::ast::Type::Record(ty) => ty,
            _ => continue,
        };

        validate_record_type(record_type, 0, wit_resolver)?;
    }

    Ok(())
}