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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
use crate::core::*;
use crate::gensym;
use crate::token::{Id, Index, Span};
use std::mem;

pub fn run(fields: &mut Vec<ModuleField>) {
    for mut item in mem::take(fields) {
        match &mut item {
            ModuleField::Func(f) => {
                for name in f.exports.names.drain(..) {
                    fields.push(export(f.span, name, ExportKind::Func, &mut f.id));
                }
                match f.kind {
                    FuncKind::Import(import) => {
                        item = ModuleField::Import(Import {
                            span: f.span,
                            module: import.module,
                            field: import.field,
                            item: ItemSig {
                                span: f.span,
                                id: f.id,
                                name: f.name,
                                kind: ItemKind::Func(f.ty.clone()),
                            },
                        });
                    }
                    FuncKind::Inline { .. } => {}
                }
            }

            ModuleField::Memory(m) => {
                for name in m.exports.names.drain(..) {
                    fields.push(export(m.span, name, ExportKind::Memory, &mut m.id));
                }
                match m.kind {
                    MemoryKind::Import { import, ty } => {
                        item = ModuleField::Import(Import {
                            span: m.span,
                            module: import.module,
                            field: import.field,
                            item: ItemSig {
                                span: m.span,
                                id: m.id,
                                name: None,
                                kind: ItemKind::Memory(ty),
                            },
                        });
                    }
                    // If data is defined inline insert an explicit `data` module
                    // field here instead, switching this to a `Normal` memory.
                    MemoryKind::Inline { is_32, ref data } => {
                        let len = data.iter().map(|l| l.len()).sum::<usize>() as u32;
                        let pages = (len + page_size() - 1) / page_size();
                        let kind = MemoryKind::Normal(if is_32 {
                            MemoryType::B32 {
                                limits: Limits {
                                    min: pages,
                                    max: Some(pages),
                                },
                                shared: false,
                            }
                        } else {
                            MemoryType::B64 {
                                limits: Limits64 {
                                    min: u64::from(pages),
                                    max: Some(u64::from(pages)),
                                },
                                shared: false,
                            }
                        });
                        let data = match mem::replace(&mut m.kind, kind) {
                            MemoryKind::Inline { data, .. } => data,
                            _ => unreachable!(),
                        };
                        let id = gensym::fill(m.span, &mut m.id);
                        fields.push(ModuleField::Data(Data {
                            span: m.span,
                            id: None,
                            name: None,
                            kind: DataKind::Active {
                                memory: Index::Id(id),
                                offset: Expression {
                                    instrs: Box::new([if is_32 {
                                        Instruction::I32Const(0)
                                    } else {
                                        Instruction::I64Const(0)
                                    }]),
                                },
                            },
                            data,
                        }));
                    }

                    MemoryKind::Normal(_) => {}
                }
            }

            ModuleField::Table(t) => {
                for name in t.exports.names.drain(..) {
                    fields.push(export(t.span, name, ExportKind::Table, &mut t.id));
                }
                match &mut t.kind {
                    TableKind::Import { import, ty } => {
                        item = ModuleField::Import(Import {
                            span: t.span,
                            module: import.module,
                            field: import.field,
                            item: ItemSig {
                                span: t.span,
                                id: t.id,
                                name: None,
                                kind: ItemKind::Table(*ty),
                            },
                        });
                    }
                    // If data is defined inline insert an explicit `data` module
                    // field here instead, switching this to a `Normal` memory.
                    TableKind::Inline { payload, elem } => {
                        let len = match payload {
                            ElemPayload::Indices(v) => v.len(),
                            ElemPayload::Exprs { exprs, .. } => exprs.len(),
                        };
                        let kind = TableKind::Normal(TableType {
                            limits: Limits {
                                min: len as u32,
                                max: Some(len as u32),
                            },
                            elem: *elem,
                        });
                        let payload = match mem::replace(&mut t.kind, kind) {
                            TableKind::Inline { payload, .. } => payload,
                            _ => unreachable!(),
                        };
                        let id = gensym::fill(t.span, &mut t.id);
                        fields.push(ModuleField::Elem(Elem {
                            span: t.span,
                            id: None,
                            name: None,
                            kind: ElemKind::Active {
                                table: Index::Id(id),
                                offset: Expression {
                                    instrs: Box::new([Instruction::I32Const(0)]),
                                },
                            },
                            payload,
                        }));
                    }

                    TableKind::Normal(_) => {}
                }
            }

            ModuleField::Global(g) => {
                for name in g.exports.names.drain(..) {
                    fields.push(export(g.span, name, ExportKind::Global, &mut g.id));
                }
                match g.kind {
                    GlobalKind::Import(import) => {
                        item = ModuleField::Import(Import {
                            span: g.span,
                            module: import.module,
                            field: import.field,
                            item: ItemSig {
                                span: g.span,
                                id: g.id,
                                name: None,
                                kind: ItemKind::Global(g.ty),
                            },
                        });
                    }
                    GlobalKind::Inline { .. } => {}
                }
            }

            ModuleField::Tag(e) => {
                for name in e.exports.names.drain(..) {
                    fields.push(export(e.span, name, ExportKind::Tag, &mut e.id));
                }
                match e.kind {
                    TagKind::Import(import) => {
                        item = ModuleField::Import(Import {
                            span: e.span,
                            module: import.module,
                            field: import.field,
                            item: ItemSig {
                                span: e.span,
                                id: e.id,
                                name: None,
                                kind: ItemKind::Tag(e.ty.clone()),
                            },
                        });
                    }
                    TagKind::Inline { .. } => {}
                }
            }

            ModuleField::Import(_)
            | ModuleField::Type(_)
            | ModuleField::Rec(_)
            | ModuleField::Export(_)
            | ModuleField::Start(_)
            | ModuleField::Elem(_)
            | ModuleField::Data(_)
            | ModuleField::Custom(_) => {}
        }

        fields.push(item);
    }

    fn page_size() -> u32 {
        1 << 16
    }
}

fn export<'a>(
    span: Span,
    name: &'a str,
    kind: ExportKind,
    id: &mut Option<Id<'a>>,
) -> ModuleField<'a> {
    let id = gensym::fill(span, id);
    ModuleField::Export(Export {
        span,
        name,
        kind,
        item: Index::Id(id),
    })
}