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
#![allow(clippy::single_match)]

mod builders;
mod code;
mod expression;
mod function;
mod imports;
mod module;
mod statement;
mod types;

use builders::component::*;

use claw_ast as ast;
use claw_resolver::{ResolvedComponent, ResolverError};
use miette::Diagnostic;
use thiserror::Error;
use types::EncodeType;

#[derive(Error, Debug, Diagnostic)]
pub enum GenerationError {
    #[error(transparent)]
    #[diagnostic(transparent)]
    Resolver(#[from] ResolverError),
}

pub const MAX_FLAT_PARAMS: u8 = 16;
pub const MAX_FLAT_RESULTS: u8 = 1;

pub fn generate(resolved_comp: &ResolvedComponent) -> Result<Vec<u8>, GenerationError> {
    let component = generate_component(resolved_comp)?;
    Ok(component.finalize().finish())
}

fn generate_component(
    resolved_comp: &ResolvedComponent,
) -> Result<ComponentBuilder, GenerationError> {
    let mut builder = ComponentBuilder::default();

    let alloc_module = builder.module_bytes(gen_allocator());

    let args: Vec<(&str, ModuleInstantiateArgs)> = vec![];
    let alloc_instance = builder.instantiate(alloc_module, args);

    let memory = builder.alias_memory(alloc_instance, "memory");
    let realloc = builder.alias_core_func(alloc_instance, "realloc");

    let import_encoder = imports::ImportEncoder::new(&mut builder, resolved_comp, memory, realloc);
    let imports = import_encoder.encode()?;

    let function_encoder = function::FunctionEncoder::new(resolved_comp);
    let functions = function_encoder.encode()?;

    let code_module = builder.module(module::generate(resolved_comp, &imports, &functions)?);

    let args = vec![
        ("alloc", ModuleInstantiateArgs::Instance(alloc_instance)),
        (
            "claw",
            ModuleInstantiateArgs::Instance(imports.imports_instance),
        ),
    ];
    let code_instance = builder.instantiate(code_module, args);

    generate_exports(&mut builder, resolved_comp, code_instance, memory, realloc)?;

    Ok(builder)
}

fn generate_exports(
    component: &mut ComponentBuilder,
    resolved_comp: &ResolvedComponent,
    code_instance: ComponentModuleInstanceIndex,
    memory: ComponentCoreMemoryIndex,
    realloc: ComponentCoreFunctionIndex,
) -> Result<(), GenerationError> {
    let comp = &resolved_comp.component;

    for function in resolved_comp.component.functions.values() {
        if function.exported {
            let name = comp.get_name(function.ident);
            // Alias module instance export into component
            let core_func_idx = component.alias_core_func(code_instance, name);
            // Alias the post return
            let post_return_idx =
                component.alias_core_func(code_instance, format!("{}_post_return", name).as_str());

            // Encode component func type
            let params = function.params.iter().map(|(param_name, param_type)| {
                let param_name = resolved_comp.component.get_name(*param_name);
                let param_type = comp.get_type(*param_type);
                let param_type = match param_type {
                    ast::ValType::Result(_) => todo!(),
                    ast::ValType::Primitive(ptype) => ptype.to_comp_valtype(resolved_comp),
                };
                (param_name, param_type)
            });
            let results = function.results.map(|result_type| {
                let result_type = comp.get_type(result_type);
                match result_type {
                    ast::ValType::Result(_) => todo!(),
                    ast::ValType::Primitive(ptype) => ptype.to_comp_valtype(resolved_comp),
                }
            });
            let type_idx = component.func_type(params, results);

            // Lift aliased function to component function
            let func_idx =
                component.lift_func(core_func_idx, type_idx, memory, realloc, post_return_idx);
            // Export component function
            component.export_func(name, func_idx, type_idx);
        }
    }
    Ok(())
}

// ValType

pub fn gen_allocator() -> Vec<u8> {
    let wat = include_str!("../allocator.wat");
    wat::parse_str(wat).unwrap()
}