skip to content
==> src/builder.c <==
1/* Byte encoders and the module assembler. */
2#include "wasm/builder.h"
3
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8static void *grow(void *p, size_t n, size_t size) {
9    void *q = realloc(p, (n ? n : 1) * size);
10    if (!q) {
11        fputs("wasm-builder: out of memory\n", stderr);
12        abort();
13    }
14    return q;
15}
16
17void wasm_buf_free(wasm_buf *b) {
18    free(b->data);
19    memset(b, 0, sizeof *b);
20}
21
22void wb_bytes(wasm_buf *b, const void *p, size_t n) {
23    if (b->len + n > b->cap) {
24        b->cap = b->cap ? b->cap * 2 : 64;
25        while (b->cap < b->len + n) b->cap *= 2;
26        b->data = grow(b->data, b->cap, 1);
27    }
28    if (n) memcpy(b->data + b->len, p, n);
29    b->len += n;
30}
31
32void wb_byte(wasm_buf *b, uint8_t v) { wb_bytes(b, &v, 1); }
33
34void wb_u64(wasm_buf *b, uint64_t v) {
35    do {
36        uint8_t x = v & 0x7f;
37        v >>= 7;
38        wb_byte(b, (uint8_t)(x | (v ? 0x80 : 0)));
39    } while (v);
40}
41
42void wb_u32(wasm_buf *b, uint32_t v) { wb_u64(b, v); }
43
44void wb_s64(wasm_buf *b, int64_t v) {
45    for (;;) {
46        uint8_t x = (uint8_t)(v & 0x7f);
47        /* An arithmetic shift, written out so it doesn't depend on the implementation. */
48        v = v < 0 ? ~(~v >> 7) : v >> 7;
49        if ((v == 0 && !(x & 0x40)) || (v == -1 && (x & 0x40))) {
50            wb_byte(b, x);
51            return;
52        }
53        wb_byte(b, (uint8_t)(x | 0x80));
54    }
55}
56
57void wb_s32(wasm_buf *b, int32_t v) { wb_s64(b, v); }
58
59void wb_f32(wasm_buf *b, float v) {
60    uint32_t u;
61    memcpy(&u, &v, 4);
62    uint8_t le[4] = {(uint8_t)u, (uint8_t)(u >> 8), (uint8_t)(u >> 16), (uint8_t)(u >> 24)};
63    wb_bytes(b, le, 4);
64}
65
66void wb_f64(wasm_buf *b, double v) {
67    uint64_t u;
68    memcpy(&u, &v, 8);
69    for (int i = 0; i < 8; i++) wb_byte(b, (uint8_t)(u >> (8 * i)));
70}
71
72void wb_name(wasm_buf *b, const char *s) {
73    size_t n = strlen(s);
74    wb_u32(b, (uint32_t)n);
75    wb_bytes(b, s, n);
76}
77
78void wb_section(wasm_buf *b, uint8_t id, const wasm_buf *payload) {
79    wb_byte(b, id);
80    wb_u32(b, (uint32_t)payload->len);
81    wb_bytes(b, payload->data, payload->len);
82}
83
84void wb_i32_const(wasm_buf *b, int32_t v) {
85    wb_byte(b, 0x41);
86    wb_s32(b, v);
87}
88
89void wb_i64_const(wasm_buf *b, int64_t v) {
90    wb_byte(b, 0x42);
91    wb_s64(b, v);
92}
93
94void wb_op(wasm_buf *b, uint8_t op) { wb_byte(b, op); }
95
96void wb_op_u32(wasm_buf *b, uint8_t op, uint32_t a) {
97    wb_byte(b, op);
98    wb_u32(b, a);
99}
100
101void wb_fc(wasm_buf *b, uint32_t sub) {
102    wb_byte(b, 0xfc);
103    wb_u32(b, sub);
104}
105
106void wb_memarg(wasm_buf *b, uint8_t op, uint32_t align, uint32_t offset) {
107    wb_byte(b, op);
108    wb_u32(b, align);
109    wb_u32(b, offset);
110}
111
112static wasm_valtype letter(char c) {
113    switch (c) {
114    case 'i': return WASM_I32;
115    case 'l': return WASM_I64;
116    case 'f': return WASM_F32;
117    case 'd': return WASM_F64;
118    case 'F': return WASM_FUNCREF;
119    case 'E': return WASM_EXTERNREF;
120    case 'V': return WASM_V128;
121    default: return 0;
122    }
123}
124
125void wasm_builder_free(wasm_builder *w) {
126    wasm_buf_free(&w->types);
127    for (uint32_t i = 0; i < w->nimports; i++) {
128        free(w->imports[i].module);
129        free(w->imports[i].name);
130    }
131    free(w->imports);
132    for (uint32_t i = 0; i < w->nfuncs; i++) {
133        free(w->funcs[i].locals);
134        wasm_buf_free(&w->funcs[i].body);
135    }
136    free(w->funcs);
137    for (uint32_t i = 0; i < w->nglobals; i++) wasm_buf_free(&w->globals[i].init);
138    free(w->globals);
139    for (uint32_t i = 0; i < w->nexports; i++) free(w->exports[i].name);
140    free(w->exports);
141    for (uint32_t i = 0; i < w->nelems; i++) free(w->elems[i].funcs);
142    free(w->elems);
143    for (uint32_t i = 0; i < w->ndatas; i++) wasm_buf_free(&w->datas[i].bytes);
144    free(w->datas);
145    memset(w, 0, sizeof *w);
146}
147
148static char *dup(const char *s) {
149    char *d = grow(NULL, strlen(s) + 1, 1);
150    memcpy(d, s, strlen(s) + 1);
151    return d;
152}
153
154uint32_t wasm_builder_type(wasm_builder *w, const char *sig) {
155    const char *gt = strchr(sig, '>');
156    size_t np = gt ? (size_t)(gt - sig) : strlen(sig);
157    wb_byte(&w->types, WASM_FUNC_FORM);
158    wb_u32(&w->types, (uint32_t)np);
159    for (size_t i = 0; i < np; i++) wb_byte(&w->types, letter(sig[i]));
160    const char *res = gt ? gt + 1 : "";
161    wb_u32(&w->types, (uint32_t)strlen(res));
162    for (const char *c = res; *c; c++) wb_byte(&w->types, letter(*c));
163    return w->ntypes++;
164}
165
166static wasm_builder_import *import(wasm_builder *w, const char *module, const char *name, uint8_t kind) {
167    w->imports = grow(w->imports, w->nimports + 1, sizeof *w->imports);
168    wasm_builder_import *im = &w->imports[w->nimports++];
169    memset(im, 0, sizeof *im);
170    im->module = dup(module);
171    im->name = dup(name);
172    im->kind = kind;
173    return im;
174}
175
176uint32_t wasm_builder_import_func(wasm_builder *w, const char *module, const char *name, uint32_t type) {
177    import(w, module, name, WASM_EXTERN_FUNC)->type = type;
178    return w->nfunc_imports++;
179}
180
181void wasm_builder_import_memory(wasm_builder *w, const char *module, const char *name, uint32_t min) {
182    import(w, module, name, WASM_EXTERN_MEMORY)->min = min;
183}
184
185wasm_buf *wasm_builder_func_add(wasm_builder *w, uint32_t type, const char *locals, uint32_t *index) {
186    w->funcs = grow(w->funcs, w->nfuncs + 1, sizeof *w->funcs);
187    wasm_builder_func *f = &w->funcs[w->nfuncs];
188    memset(f, 0, sizeof *f);
189    f->type = type;
190    f->nlocals = (uint32_t)strlen(locals);
191    f->locals = grow(NULL, f->nlocals, 1);
192    for (uint32_t i = 0; i < f->nlocals; i++) f->locals[i] = letter(locals[i]);
193    if (index) *index = w->nfunc_imports + w->nfuncs;
194    w->nfuncs++;
195    return &f->body;
196}
197
198void wasm_builder_memory(wasm_builder *w, uint32_t min, uint32_t max, bool has_max) {
199    w->has_memory = true;
200    w->mem_min = min;
201    w->mem_max = max;
202    w->mem_has_max = has_max;
203}
204
205void wasm_builder_table(wasm_builder *w, uint32_t min, uint32_t max, bool has_max) {
206    w->has_table = true;
207    w->table_min = min;
208    w->table_max = max;
209    w->table_has_max = has_max;
210}
211
212wasm_buf *wasm_builder_add_global(wasm_builder *w, wasm_valtype type, bool mut) {
213    w->globals = grow(w->globals, w->nglobals + 1, sizeof *w->globals);
214    wasm_builder_global *g = &w->globals[w->nglobals++];
215    memset(g, 0, sizeof *g);
216    g->type = type;
217    g->mut = mut;
218    return &g->init;
219}
220
221void wasm_builder_add_export(wasm_builder *w, const char *name, uint8_t kind, uint32_t index) {
222    w->exports = grow(w->exports, w->nexports + 1, sizeof *w->exports);
223    w->exports[w->nexports++] = (wasm_builder_export){dup(name), kind, index};
224}
225
226void wasm_builder_data(wasm_builder *w, uint32_t offset, const void *bytes, size_t n) {
227    w->datas = grow(w->datas, w->ndatas + 1, sizeof *w->datas);
228    wasm_builder_segment *d = &w->datas[w->ndatas++];
229    memset(d, 0, sizeof *d);
230    d->offset = offset;
231    wb_bytes(&d->bytes, bytes, n);
232}
233
234void wasm_builder_data_passive(wasm_builder *w, const void *bytes, size_t n) {
235    wasm_builder_data(w, 0, bytes, n);
236    w->datas[w->ndatas - 1].passive = true;
237    w->datacount = true;
238}
239
240void wasm_builder_elem(wasm_builder *w, uint32_t offset, const uint32_t *funcs, uint32_t n) {
241    w->elems = grow(w->elems, w->nelems + 1, sizeof *w->elems);
242    wasm_builder_segment *e = &w->elems[w->nelems++];
243    memset(e, 0, sizeof *e);
244    e->offset = offset;
245    e->funcs = grow(NULL, n, sizeof *funcs);
246    if (n) memcpy(e->funcs, funcs, n * sizeof *funcs);
247    e->nfuncs = n;
248}
249
250static void limits(wasm_buf *b, uint32_t min, uint32_t max, bool has_max) {
251    wb_byte(b, has_max ? 1 : 0);
252    wb_u32(b, min);
253    if (has_max) wb_u32(b, max);
254}
255
256void wasm_builder_finish(const wasm_builder *w, wasm_buf *out) {
257    memset(out, 0, sizeof *out);
258    wb_bytes(out, "\0asm\x01\0\0\0", 8);
259    wasm_buf s = {0};
260    if (w->ntypes) {
261        wb_u32(&s, w->ntypes);
262        wb_bytes(&s, w->types.data, w->types.len);
263        wb_section(out, WASM_SEC_TYPE, &s);
264    }
265    if (w->nimports) {
266        s.len = 0;
267        wb_u32(&s, w->nimports);
268        for (uint32_t i = 0; i < w->nimports; i++) {
269            const wasm_builder_import *im = &w->imports[i];
270            wb_name(&s, im->module);
271            wb_name(&s, im->name);
272            wb_byte(&s, im->kind);
273            if (im->kind == WASM_EXTERN_FUNC) wb_u32(&s, im->type);
274            else if (im->kind == WASM_EXTERN_MEMORY) limits(&s, im->min, im->max, im->has_max);
275        }
276        wb_section(out, WASM_SEC_IMPORT, &s);
277    }
278    if (w->nfuncs) {
279        s.len = 0;
280        wb_u32(&s, w->nfuncs);
281        for (uint32_t i = 0; i < w->nfuncs; i++) wb_u32(&s, w->funcs[i].type);
282        wb_section(out, WASM_SEC_FUNCTION, &s);
283    }
284    if (w->has_table) {
285        s.len = 0;
286        wb_u32(&s, 1);
287        wb_byte(&s, WASM_FUNCREF);
288        limits(&s, w->table_min, w->table_max, w->table_has_max);
289        wb_section(out, WASM_SEC_TABLE, &s);
290    }
291    if (w->has_memory) {
292        s.len = 0;
293        wb_u32(&s, 1);
294        limits(&s, w->mem_min, w->mem_max, w->mem_has_max);
295        wb_section(out, WASM_SEC_MEMORY, &s);
296    }
297    if (w->nglobals) {
298        s.len = 0;
299        wb_u32(&s, w->nglobals);
300        for (uint32_t i = 0; i < w->nglobals; i++) {
301            wb_byte(&s, w->globals[i].type);
302            wb_byte(&s, w->globals[i].mut);
303            wb_bytes(&s, w->globals[i].init.data, w->globals[i].init.len);
304            wb_byte(&s, 0x0b);
305        }
306        wb_section(out, WASM_SEC_GLOBAL, &s);
307    }
308    if (w->nexports) {
309        s.len = 0;
310        wb_u32(&s, w->nexports);
311        for (uint32_t i = 0; i < w->nexports; i++) {
312            wb_name(&s, w->exports[i].name);
313            wb_byte(&s, w->exports[i].kind);
314            wb_u32(&s, w->exports[i].index);
315        }
316        wb_section(out, WASM_SEC_EXPORT, &s);
317    }
318    if (w->has_start) {
319        s.len = 0;
320        wb_u32(&s, w->start);
321        wb_section(out, WASM_SEC_START, &s);
322    }
323    if (w->nelems) {
324        s.len = 0;
325        wb_u32(&s, w->nelems);
326        for (uint32_t i = 0; i < w->nelems; i++) {
327            wb_u32(&s, 0);
328            wb_i32_const(&s, (int32_t)w->elems[i].offset);
329            wb_byte(&s, 0x0b);
330            wb_u32(&s, w->elems[i].nfuncs);
331            for (uint32_t k = 0; k < w->elems[i].nfuncs; k++) wb_u32(&s, w->elems[i].funcs[k]);
332        }
333        wb_section(out, WASM_SEC_ELEMENT, &s);
334    }
335    if (w->datacount) {
336        s.len = 0;
337        wb_u32(&s, w->ndatas);
338        wb_section(out, WASM_SEC_DATACOUNT, &s);
339    }
340    if (w->nfuncs) {
341        s.len = 0;
342        wb_u32(&s, w->nfuncs);
343        for (uint32_t i = 0; i < w->nfuncs; i++) {
344            const wasm_builder_func *f = &w->funcs[i];
345            wasm_buf e = {0};
346            /* Locals, run-length encoded. */
347            uint32_t groups = 0;
348            for (uint32_t k = 0; k < f->nlocals; k++)
349                if (k == 0 || f->locals[k] != f->locals[k - 1]) groups++;
350            wb_u32(&e, groups);
351            for (uint32_t k = 0; k < f->nlocals;) {
352                uint32_t n = 1;
353                while (k + n < f->nlocals && f->locals[k + n] == f->locals[k]) n++;
354                wb_u32(&e, n);
355                wb_byte(&e, f->locals[k]);
356                k += n;
357            }
358            wb_bytes(&e, f->body.data, f->body.len);
359            wb_byte(&e, 0x0b);
360            wb_u32(&s, (uint32_t)e.len);
361            wb_bytes(&s, e.data, e.len);
362            wasm_buf_free(&e);
363        }
364        wb_section(out, WASM_SEC_CODE, &s);
365    }
366    if (w->ndatas) {
367        s.len = 0;
368        wb_u32(&s, w->ndatas);
369        for (uint32_t i = 0; i < w->ndatas; i++) {
370            const wasm_builder_segment *d = &w->datas[i];
371            if (d->passive) {
372                wb_u32(&s, 1);
373            } else {
374                wb_u32(&s, 0);
375                wb_i32_const(&s, (int32_t)d->offset);
376                wb_byte(&s, 0x0b);
377            }
378            wb_u32(&s, (uint32_t)d->bytes.len);
379            wb_bytes(&s, d->bytes.data, d->bytes.len);
380        }
381        wb_section(out, WASM_SEC_DATA, &s);
382    }
383    wasm_buf_free(&s);
384}