Coverage Report

Created: 2026-08-13 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_parse.c
Line
Count
Source
1
//
2
//  m3_parse.c
3
//
4
//  Created by Steven Massey on 4/19/19.
5
//  Copyright © 2019 Steven Massey. All rights reserved.
6
//
7
8
#include "m3_env.h"
9
#include "m3_compile.h"
10
#include "m3_exception.h"
11
#include "m3_info.h"
12
13
14
M3Result  ParseType_Table  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
15
3
{
16
3
    M3Result result = m3Err_none;
17
18
3
    u32 numTables;
19
3
_   (ReadLEB_u32 (& numTables, & i_bytes, i_end));                       m3log (parse, "** Table [%d]", numTables);
20
21
    // MVP: at most one table, counting any that was already imported
22
1
    _throwif (m3Err_wasmMalformed, numTables > 1);
23
1
    _throwif (m3Err_wasmMalformed, numTables and io_module->hasTable);
24
25
1
    for (u32 i = 0; i < numTables; ++i)
26
1
    {
27
1
        u8 elemType;
28
1
_       (Read_u8 (& elemType, & i_bytes, i_end));
29
        // Spec: element type must be funcref (0x70)
30
0
        _throwif (m3Err_wasmMalformed, elemType != 0x70);
31
32
0
        u8 flag;
33
0
_       (ReadLEB_u7 (& flag, & i_bytes, i_end));
34
0
        u32 initSize;
35
0
_       (ReadLEB_u32 (& initSize, & i_bytes, i_end));
36
0
        if (flag & 1) {
37
0
            u32 maxSize;
38
0
_           (ReadLEB_u32 (& maxSize, & i_bytes, i_end));
39
0
            _throwif (m3Err_wasmMalformed, maxSize < initSize);
40
0
        }
41
0
        io_module->hasTable = true;
42
0
    }
43
44
0
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
45
46
3
    _catch: return result;
47
0
}
48
49
50
M3Result  ParseType_Memory  (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end)
51
71
{
52
71
    M3Result result = m3Err_none;
53
54
71
    u8 flag;
55
56
71
_   (ReadLEB_u7 (& flag, io_bytes, i_end));                   // really a u1
57
68
_   (ReadLEB_u32 (& o_memory->initPages, io_bytes, i_end));
58
59
66
    o_memory->maxPages = 0;
60
66
    if (flag & (1u << 0))
61
31
    {
62
31
_       (ReadLEB_u32 (& o_memory->maxPages, io_bytes, i_end));
63
64
        // Spec: memory limits validation - max must not be less than init
65
28
        _throwif (m3Err_wasmMalformed, o_memory->maxPages < o_memory->initPages);
66
22
    }
67
68
57
    o_memory->pageSize = 0;
69
57
    if (flag & (1u << 3)) {
70
14
        u32 logPageSize;
71
14
_       (ReadLEB_u32 (& logPageSize, io_bytes, i_end));
72
9
        o_memory->pageSize = 1u << logPageSize;
73
9
    }
74
75
    // Spec: memory limits must be valid within range 2^16 (65536 pages)
76
    // Only enforce for standard page size (no custom page size flag)
77
52
    if (!(flag & (1u << 3)))
78
43
    {
79
43
        _throwif (m3Err_wasmMalformed, o_memory->initPages > 65536);
80
41
        if (flag & (1u << 0))
81
35
            _throwif (m3Err_wasmMalformed, o_memory->maxPages > 65536);
82
35
    }
83
84
71
    _catch: return result;
85
52
}
86
87
88
M3Result  ParseSection_Type  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
89
974
{
90
974
    IM3FuncType ftype = NULL;
91
92
974
_try {
93
974
    u32 numTypes;
94
974
_   (ReadLEB_u32 (& numTypes, & i_bytes, i_end));                                   m3log (parse, "** Type [%d]", numTypes);
95
96
973
    _throwif("too many types", numTypes > d_m3MaxSaneTypesCount);
97
98
968
    if (numTypes)
99
963
    {
100
        // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment)
101
963
        io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes);
102
963
        _throwifnull (io_module->funcTypes);
103
963
        io_module->numFuncTypes = numTypes;
104
105
2.18k
        for (u32 i = 0; i < numTypes; ++i)
106
1.39k
        {
107
1.39k
            i8 form;
108
1.39k
_           (ReadLEB_i7 (& form, & i_bytes, i_end));
109
1.35k
            _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP
110
111
1.31k
            u32 numArgs;
112
1.31k
_           (ReadLEB_u32 (& numArgs, & i_bytes, i_end));
113
114
1.30k
            _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount);
115
#if defined(M3_COMPILER_MSVC)
116
            u8 argTypes [d_m3MaxSaneFunctionArgRetCount];
117
#else
118
1.29k
            u8 argTypes[numArgs+1]; // make ubsan happy
119
1.29k
#endif
120
5.54k
            for (u32 a = 0; a < numArgs; ++a)
121
4.28k
            {
122
4.28k
                i8 wasmType;
123
4.28k
                u8 argType;
124
4.28k
_               (ReadLEB_i7 (& wasmType, & i_bytes, i_end));
125
4.27k
_               (NormalizeType (& argType, wasmType));
126
127
4.25k
                argTypes[a] = argType;
128
4.25k
            }
129
130
1.26k
            u32 numRets;
131
1.26k
_           (ReadLEB_u32 (& numRets, & i_bytes, i_end));
132
1.25k
            _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount);
133
134
1.24k
_           (AllocFuncType (& ftype, numRets + numArgs));
135
1.24k
            ftype->numArgs = numArgs;
136
1.24k
            ftype->numRets = numRets;
137
138
3.84k
            for (u32 r = 0; r < numRets; ++r)
139
2.62k
            {
140
2.62k
                i8 wasmType;
141
2.62k
                u8 retType;
142
2.62k
_               (ReadLEB_i7 (& wasmType, & i_bytes, i_end));
143
2.61k
_               (NormalizeType (& retType, wasmType));
144
145
2.60k
                ftype->types[r] = retType;
146
2.60k
            }
147
1.22k
            memcpy (ftype->types + numRets, argTypes, numArgs);                                 m3log (parse, "    type %2d: %s", i, SPrintFuncTypeSignature (ftype));
148
149
1.22k
            Environment_AddFuncType (io_module->environment, & ftype);
150
1.22k
            io_module->funcTypes [i] = ftype;
151
1.22k
            ftype = NULL; // ownership transferred to environment
152
1.22k
        }
153
963
    }
154
155
793
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
156
157
974
} _catch:
158
159
974
    if (result)
160
244
    {
161
244
        m3_Free (ftype);
162
        // FIX: M3FuncTypes in the table are leaked
163
244
        m3_Free (io_module->funcTypes);
164
244
        io_module->numFuncTypes = 0;
165
244
    }
166
167
974
    return result;
168
730
}
169
170
171
M3Result  ParseSection_Function  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
172
777
{
173
777
    M3Result result = m3Err_none;
174
175
777
    u32 numFunctions;
176
777
_   (ReadLEB_u32 (& numFunctions, & i_bytes, i_end));                               m3log (parse, "** Function [%d]", numFunctions);
177
178
775
    _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount);
179
180
767
_   (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions));
181
182
2.20k
    for (u32 i = 0; i < numFunctions; ++i)
183
1.52k
    {
184
1.52k
        u32 funcTypeIndex;
185
1.52k
_       (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end));
186
187
1.48k
_       (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */));
188
1.44k
    }
189
190
689
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
191
192
777
    _catch: return result;
193
688
}
194
195
196
M3Result  ParseSection_Import  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
197
76
{
198
76
    M3Result result = m3Err_none;
199
200
76
    M3ImportInfo import = { NULL, NULL }, clearImport = { NULL, NULL };
201
202
76
    u32 numImports;
203
76
_   (ReadLEB_u32 (& numImports, & i_bytes, i_end));                                 m3log (parse, "** Import [%d]", numImports);
204
205
73
    _throwif("too many imports", numImports > d_m3MaxSaneImportsCount);
206
207
    // Most imports are functions, so we won't waste much space anyway (if any)
208
62
_   (Module_PreallocFunctions(io_module, numImports));
209
210
208
    for (u32 i = 0; i < numImports; ++i)
211
196
    {
212
196
        u8 importKind;
213
214
196
_       (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end));
215
169
_       (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end));
216
168
_       (Read_u8 (& importKind, & i_bytes, i_end));                                 m3log (parse, "    kind: %d '%s.%s' ",
217
167
                                                                                                (u32) importKind, import.moduleUtf8, import.fieldUtf8);
218
167
        switch (importKind)
219
167
        {
220
116
            case d_externalKind_function:
221
116
            {
222
116
                u32 typeIndex;
223
116
_               (ReadLEB_u32 (& typeIndex, & i_bytes, i_end))
224
225
115
_               (Module_AddFunction (io_module, typeIndex, & import))
226
115
                import = clearImport;
227
228
115
                io_module->numFuncImports++;
229
115
            }
230
0
            break;
231
232
7
            case d_externalKind_table:
233
7
            {
234
                // Parse and validate table type (elem type + limits)
235
7
                u8 elemType;
236
7
_               (Read_u8 (& elemType, & i_bytes, i_end));
237
5
                _throwif (m3Err_wasmMalformed, elemType != 0x70); // must be funcref
238
0
                u8 flag;
239
0
_               (ReadLEB_u7 (& flag, & i_bytes, i_end));
240
0
                u32 initSize;
241
0
_               (ReadLEB_u32 (& initSize, & i_bytes, i_end));
242
0
                if (flag & 1) {
243
0
                    u32 maxSize;
244
0
_                   (ReadLEB_u32 (& maxSize, & i_bytes, i_end));
245
0
                }
246
0
                io_module->hasTable = true;
247
0
            }
248
0
            break;
249
250
33
            case d_externalKind_memory:
251
33
            {
252
33
_               (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end));
253
30
                io_module->memoryImported = true;
254
30
                io_module->memoryImport = import;
255
30
                import = clearImport;
256
30
            }
257
0
            break;
258
259
10
            case d_externalKind_global:
260
10
            {
261
10
                i8 waType;
262
10
                u8 type, isMutable;
263
264
10
_               (ReadLEB_i7 (& waType, & i_bytes, i_end));
265
9
_               (NormalizeType (& type, waType));
266
7
_               (ReadLEB_u7 (& isMutable, & i_bytes, i_end));                     m3log (parse, "     global: %s mutable=%d", c_waTypes [type], (u32) isMutable);
267
6
                _throwif (m3Err_wasmMalformed, isMutable > 1);
268
269
1
                IM3Global global;
270
1
_               (Module_AddGlobal (io_module, & global, type, isMutable, true /* isImport */));
271
1
                global->import = import;
272
1
                import = clearImport;
273
1
            }
274
0
            break;
275
276
1
            default:
277
1
                _throw (m3Err_wasmMalformed);
278
167
        }
279
280
146
        FreeImportInfo (& import);
281
146
    }
282
283
12
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
284
285
76
    _catch:
286
287
76
    FreeImportInfo (& import);
288
289
76
    return result;
290
10
}
291
292
293
M3Result  ParseSection_Export  (IM3Module io_module, bytes_t i_bytes, cbytes_t  i_end)
294
131
{
295
131
    M3Result result = m3Err_none;
296
131
    const char * utf8 = NULL;
297
131
#if d_m3EnableValidation
298
    // We store name pointers + lengths to handle embedded NUL bytes correctly
299
131
    typedef struct { const u8 * ptr; u16 len; } ExportName;
300
131
    ExportName * exportNames = NULL;
301
131
#endif
302
303
131
    u32 numExports;
304
131
_   (ReadLEB_u32 (& numExports, & i_bytes, i_end));                                 m3log (parse, "** Export [%d]", numExports);
305
306
129
    _throwif("too many exports", numExports > d_m3MaxSaneExportsCount);
307
308
118
#if d_m3EnableValidation
309
    // Spec: all export names must be different
310
118
    if (numExports > 1)
311
101
    {
312
101
        exportNames = (ExportName *) m3_Malloc ("exportNames", sizeof(ExportName) * numExports);
313
101
    }
314
118
#endif
315
316
140
    for (u32 i = 0; i < numExports; ++i)
317
136
    {
318
136
        u8 exportKind;
319
136
        u32 index;
320
321
        // Read name length and remember raw position for uniqueness check
322
136
#if d_m3EnableValidation
323
136
        const u8 * nameStart = i_bytes;
324
136
        u32 nameLen = 0;
325
136
        {
326
136
            bytes_t tmp = i_bytes;
327
136
            M3Result rl = ReadLEB_u32 (& nameLen, & tmp, i_end);
328
136
            if (rl) { m3_Free(exportNames); _throw(rl); }
329
116
            nameStart = tmp; // points to the raw name bytes
330
116
        }
331
0
#endif
332
333
116
_       (Read_utf8 (& utf8, & i_bytes, i_end));
334
99
_       (Read_u8 (& exportKind, & i_bytes, i_end));
335
94
_       (ReadLEB_u32 (& index, & i_bytes, i_end));                                  m3log (parse, "    index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8);
336
337
93
#if d_m3EnableValidation
338
93
        if (exportNames)
339
81
        {
340
87
            for (u32 j = 0; j < i; ++j)
341
12
            {
342
12
                if (exportNames[j].len == nameLen &&
343
7
                    memcmp (exportNames[j].ptr, nameStart, nameLen) == 0)
344
6
                {
345
6
                    m3_Free (exportNames);
346
6
                    _throw (m3Err_wasmMalformed);  // duplicate export name
347
0
                }
348
12
            }
349
75
            exportNames[i].ptr = nameStart;
350
75
            exportNames[i].len = (u16)nameLen;
351
75
        }
352
87
#endif
353
354
87
        if (exportKind == d_externalKind_function)
355
34
        {
356
34
            _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions);
357
5
            IM3Function func = &(io_module->functions [index]);
358
5
            if (func->numNames < d_m3MaxDuplicateFunctionImpl)
359
5
            {
360
5
                func->names[func->numNames++] = utf8;
361
5
                func->export_name = utf8;
362
5
                utf8 = NULL; // ownership transferred to M3Function
363
5
            }
364
5
        }
365
53
        else if (exportKind == d_externalKind_global)
366
29
        {
367
29
            _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals);
368
0
            IM3Global global = &(io_module->globals [index]);
369
0
            m3_Free (global->name);
370
0
            global->name = utf8;
371
0
            utf8 = NULL; // ownership transferred to M3Global
372
0
        }
373
24
        else if (exportKind == d_externalKind_memory)
374
4
        {
375
4
            _throwif(m3Err_wasmMalformed, index != 0);
376
1
            _throwif(m3Err_wasmMalformed, not (io_module->memoryImported or io_module->memoryDeclared));
377
0
            m3_Free (io_module->memoryExportName);
378
0
            io_module->memoryExportName = utf8;
379
0
            utf8 = NULL; // ownership transferred to M3Module
380
0
        }
381
20
        else if (exportKind == d_externalKind_table)
382
3
        {
383
3
            _throwif(m3Err_wasmMalformed, index != 0);
384
1
            _throwif(m3Err_wasmMalformed, not io_module->hasTable);
385
0
            m3_Free (io_module->table0ExportName);
386
0
            io_module->table0ExportName = utf8;
387
0
            utf8 = NULL; // ownership transferred to M3Module
388
0
        }
389
390
22
        m3_Free (utf8);
391
22
    }
392
393
4
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
394
395
131
_catch:
396
131
    m3_Free (utf8);
397
131
#if d_m3EnableValidation
398
131
    m3_Free (exportNames);
399
131
#endif
400
131
    return result;
401
4
}
402
403
404
M3Result  ParseSection_Start  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
405
20
{
406
20
    M3Result result = m3Err_none;
407
408
20
    u32 startFuncIndex;
409
20
_   (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end));                               m3log (parse, "** Start Function: %d", startFuncIndex);
410
411
18
    if (startFuncIndex < io_module->numFunctions)
412
1
    {
413
        // Spec: start function type must be [] -> []
414
1
        IM3Function func = & io_module->functions [startFuncIndex];
415
1
        if (func->funcType)
416
1
        {
417
1
            _throwif (m3Err_wasmMalformed,
418
1
                      func->funcType->numArgs != 0 || func->funcType->numRets != 0);
419
1
        }
420
421
1
        io_module->startFunction = startFuncIndex;
422
1
    }
423
17
    else result = "start function index out of bounds";
424
425
18
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
426
427
20
    _catch: return result;
428
16
}
429
430
431
M3Result  Parse_InitExpr  (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end)
432
4
{
433
4
    M3Result result = m3Err_none;
434
435
    // this doesn't generate code pages. just walks the wasm bytecode to find the end
436
437
#if defined(d_m3PreferStaticAlloc)
438
    static M3Compilation compilation;
439
#else
440
4
    M3Compilation compilation;
441
4
#endif
442
4
    compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end, .isInitExpr = true };
443
444
4
    result = CompileBlockStatements (& compilation);
445
446
4
    * io_bytes = compilation.wasm;
447
448
4
    return result;
449
4
}
450
451
452
M3Result  ParseSection_Element  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
453
59
{
454
59
    M3Result result = m3Err_none;
455
456
59
    u32 numSegments;
457
59
    bytes_t pos;
458
59
_   (ReadLEB_u32 (& numSegments, & i_bytes, i_end));                         m3log (parse, "** Element [%d]", numSegments);
459
460
57
    _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments);
461
462
    // Element segments need a table to populate
463
51
    _throwif (m3Err_wasmMalformed, numSegments and not io_module->hasTable);
464
465
2
    io_module->elementSection = i_bytes;
466
2
    io_module->elementSectionEnd = i_end;
467
2
    io_module->numElementSegments = numSegments;
468
469
    // Walk the section to validate structure and detect section size mismatch.
470
    // The actual element initialization happens later in InitElements.
471
2
    pos = i_bytes;
472
2
    for (u32 i = 0; i < numSegments; ++i)
473
0
    {
474
0
        u32 tableIndex;
475
0
_       (ReadLEB_u32 (& tableIndex, & pos, i_end));
476
477
        // Walk the init expression (offset) to find its end
478
0
_       (Parse_InitExpr (io_module, & pos, i_end));
479
480
0
        u32 numElements;
481
0
_       (ReadLEB_u32 (& numElements, & pos, i_end));
482
483
0
        for (u32 e = 0; e < numElements; ++e)
484
0
        {
485
0
            u32 funcIndex;
486
0
_           (ReadLEB_u32 (& funcIndex, & pos, i_end));
487
0
        }
488
0
    }
489
490
2
    _throwif (m3Err_wasmMalformed, pos != i_end);           // section size mismatch
491
492
59
    _catch: return result;
493
1
}
494
495
496
M3Result  ParseSection_Code  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
497
682
{
498
682
    M3Result result;
499
500
682
    u32 numFunctions;
501
682
_   (ReadLEB_u32 (& numFunctions, & i_bytes, i_end));                               m3log (parse, "** Code [%d]", numFunctions);
502
503
679
    if (numFunctions != io_module->numFunctions - io_module->numFuncImports)
504
26
    {
505
26
        _throw ("mismatched function count in code section");
506
0
    }
507
508
1.36k
    for (u32 f = 0; f < numFunctions; ++f)
509
751
    {
510
751
        const u8 * start = i_bytes;
511
512
751
        u32 size;
513
751
_       (ReadLEB_u32 (& size, & i_bytes, i_end));
514
515
746
        if (size)
516
658
        {
517
658
            const u8 * ptr = i_bytes;
518
658
            i_bytes += size;
519
520
658
            if (i_bytes <= i_end)
521
627
            {
522
                /*
523
                u32 numLocalBlocks;
524
_               (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end));                                      m3log (parse, "    code size: %-4d", size);
525
526
                u32 numLocals = 0;
527
528
                for (u32 l = 0; l < numLocalBlocks; ++l)
529
                {
530
                    u32 varCount;
531
                    i8 wasmType;
532
                    u8 normalType;
533
534
_                   (ReadLEB_u32 (& varCount, & ptr, i_end));
535
_                   (ReadLEB_i7 (& wasmType, & ptr, i_end));
536
_                   (NormalizeType (& normalType, wasmType));
537
538
                    numLocals += varCount;                                                      m3log (parse, "      %2d locals; type: '%s'", varCount, c_waTypes [normalType]);
539
                }
540
                 */
541
542
627
                IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports);
543
544
627
                func->module = io_module;
545
627
                func->wasm = start;
546
627
                func->wasmEnd = i_bytes;
547
                //func->ownsWasmCode = io_module->hasWasmCodeCopy;
548
//                func->numLocals = numLocals;
549
627
            }
550
627
            else _throw (m3Err_wasmSectionOverrun);
551
627
        }
552
746
    }
553
554
682
    _catch:
555
556
682
    if (not result and i_bytes != i_end)
557
3
        result = m3Err_wasmSectionUnderrun;
558
559
682
    return result;
560
653
}
561
562
563
M3Result  ParseSection_Data  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
564
78
{
565
78
    M3Result result = m3Err_none;
566
567
78
    u32 numDataSegments;
568
78
_   (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end));                            m3log (parse, "** Data [%d]", numDataSegments);
569
570
75
    _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments);
571
572
63
    io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments);
573
63
    _throwifnull(io_module->dataSegments);
574
63
    io_module->numDataSegments = numDataSegments;
575
576
63
    for (u32 i = 0; i < numDataSegments; ++i)
577
61
    {
578
61
        M3DataSegment * segment = & io_module->dataSegments [i];
579
580
61
_       (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end));
581
582
        // Spec: MVP only supports memory index 0, and it has to exist
583
35
        _throwif (m3Err_wasmMalformed, segment->memoryRegion != 0);
584
1
        _throwif (m3Err_wasmMalformed, not (io_module->memoryImported or io_module->memoryDeclared));
585
586
0
        segment->initExpr = i_bytes;
587
0
_       (Parse_InitExpr (io_module, & i_bytes, i_end));
588
0
        segment->initExprSize = (u32) (i_bytes - segment->initExpr);
589
590
0
        _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1);
591
592
0
_       (ReadLEB_u32 (& segment->size, & i_bytes, i_end));
593
0
        segment->data = i_bytes;                                                    m3log (parse, "    segment [%u]  memory: %u;  expr-size: %d;  size: %d",
594
0
                                                                                       i, segment->memoryRegion, segment->initExprSize, segment->size);
595
0
        i_bytes += segment->size;
596
597
0
        _throwif("data segment underflow", i_bytes > i_end);
598
0
    }
599
600
2
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
601
602
78
    _catch:
603
604
78
    return result;
605
2
}
606
607
608
M3Result  ParseSection_Memory  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
609
60
{
610
60
    M3Result result = m3Err_none;
611
612
    // TODO: MVP; assert no memory imported
613
614
60
    u32 numMemories;
615
60
_   (ReadLEB_u32 (& numMemories, & i_bytes, i_end));                             m3log (parse, "** Memory [%d]", numMemories);
616
617
58
    _throwif (m3Err_tooManyMemorySections, numMemories > 1);
618
619
41
    if (numMemories)
620
38
    {
621
38
_       (ParseType_Memory (& io_module->memoryInfo, & i_bytes, i_end));
622
14
        io_module->memoryDeclared = true;
623
14
    }
624
625
17
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
626
627
60
    _catch: return result;
628
16
}
629
630
631
M3Result  ParseSection_Global  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
632
73
{
633
73
    M3Result result = m3Err_none;
634
635
73
    u32 numGlobals;
636
73
_   (ReadLEB_u32 (& numGlobals, & i_bytes, i_end));                                 m3log (parse, "** Global [%d]", numGlobals);
637
638
72
    _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount);
639
640
62
    for (u32 i = 0; i < numGlobals; ++i)
641
59
    {
642
59
        i8 waType;
643
59
        u8 type, isMutable;
644
645
59
_       (ReadLEB_i7 (& waType, & i_bytes, i_end));
646
38
_       (NormalizeType (& type, waType));
647
32
_       (ReadLEB_u7 (& isMutable, & i_bytes, i_end));                                 m3log (parse, "    global: [%d] %s mutable: %d", i, c_waTypes [type],   (u32) isMutable);
648
29
        _throwif (m3Err_wasmMalformed, isMutable > 1);
649
650
4
        IM3Global global;
651
4
_       (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */));
652
653
4
        global->initExpr = i_bytes;
654
4
_       (Parse_InitExpr (io_module, & i_bytes, i_end));
655
1
        global->initExprSize = (u32) (i_bytes - global->initExpr);
656
657
1
        _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1);
658
0
    }
659
660
3
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
661
662
73
    _catch: return result;
663
2
}
664
665
666
M3Result  ParseSection_Name  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
667
1.18k
{
668
1.18k
    M3Result result = m3Err_none;
669
670
1.18k
    cstr_t name;
671
672
3.27k
    while (i_bytes < i_end)
673
2.16k
    {
674
2.16k
        u8 nameType;
675
2.16k
        u32 payloadLength;
676
677
2.16k
_       (ReadLEB_u7 (& nameType, & i_bytes, i_end));
678
2.16k
_       (ReadLEB_u32 (& payloadLength, & i_bytes, i_end));
679
680
2.15k
        bytes_t start = i_bytes;
681
2.15k
        if (nameType == 1)
682
1.28k
        {
683
1.28k
            u32 numNames;
684
1.28k
_           (ReadLEB_u32 (& numNames, & i_bytes, i_end));
685
686
1.28k
            _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount);
687
688
3.26k
            for (u32 i = 0; i < numNames; ++i)
689
2.04k
            {
690
2.04k
                u32 index;
691
2.04k
_               (ReadLEB_u32 (& index, & i_bytes, i_end));
692
2.02k
_               (Read_utf8 (& name, & i_bytes, i_end));
693
694
1.98k
                if (index < io_module->numFunctions)
695
1.03k
                {
696
1.03k
                    IM3Function func = &(io_module->functions [index]);
697
1.03k
                    if (func->numNames == 0)
698
615
                    {
699
615
                        func->names[0] = name;        m3log (parse, "    naming function%5d:  %s", index, name);
700
615
                        func->numNames = 1;
701
615
                        name = NULL; // transfer ownership
702
615
                    }
703
//                          else m3log (parse, "prenamed: %s", io_module->functions [index].name);
704
1.03k
                }
705
706
1.98k
                m3_Free (name);
707
1.98k
            }
708
1.28k
        }
709
710
2.08k
        i_bytes = start + payloadLength;
711
2.08k
    }
712
713
1.18k
    _catch: return result;
714
1.18k
}
715
716
717
M3Result  ParseSection_Custom  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
718
2.22k
{
719
2.22k
    M3Result result;
720
721
2.22k
    cstr_t name;
722
2.22k
_   (Read_utf8 (& name, & i_bytes, i_end));
723
2.14k
                                                                                    m3log (parse, "** Custom: '%s'", name);
724
2.14k
    if (strcmp (name, "name") == 0) {
725
1.18k
_       (ParseSection_Name(io_module, i_bytes, i_end));
726
1.11k
    } else if (io_module->environment->customSectionHandler) {
727
0
_       (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end));
728
0
    }
729
730
2.07k
    m3_Free (name);
731
732
2.22k
    _catch: return result;
733
2.07k
}
734
735
736
M3Result  ParseModuleSection  (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes)
737
5.16k
{
738
5.16k
    M3Result result = m3Err_none;
739
740
5.16k
    typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t);
741
742
5.16k
    static M3Parser s_parsers [] =
743
5.16k
    {
744
5.16k
        ParseSection_Custom,    // 0
745
5.16k
        ParseSection_Type,      // 1
746
5.16k
        ParseSection_Import,    // 2
747
5.16k
        ParseSection_Function,  // 3
748
5.16k
        ParseType_Table,        // 4
749
5.16k
        ParseSection_Memory,    // 5
750
5.16k
        ParseSection_Global,    // 6
751
5.16k
        ParseSection_Export,    // 7
752
5.16k
        ParseSection_Start,     // 8
753
5.16k
        ParseSection_Element,   // 9
754
5.16k
        ParseSection_Code,      // 10
755
5.16k
        ParseSection_Data,      // 11
756
5.16k
        NULL,                   // 12: TODO DataCount
757
5.16k
    };
758
759
5.16k
    M3Parser parser = NULL;
760
761
5.16k
    if (i_sectionType <= 12)
762
5.16k
        parser = s_parsers [i_sectionType];
763
764
5.16k
    if (parser)
765
5.15k
    {
766
5.15k
        cbytes_t end = i_bytes + i_numBytes;
767
5.15k
        result = parser (o_module, i_bytes, end);
768
5.15k
    }
769
1
    else
770
1
    {
771
1
        m3log (parse, " skipped section type: %d", (u32) i_sectionType);
772
1
    }
773
774
5.16k
    return result;
775
5.16k
}
776
777
778
M3Result  m3_ParseModule  (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes)
779
1.91k
{
780
1.91k
    IM3Module module;                                                               m3log (parse, "load module: %d bytes", i_numBytes);
781
1.91k
_try {
782
1.91k
    module = m3_AllocStruct (M3Module);
783
1.91k
    _throwifnull (module);
784
1.91k
    module->name = ".unnamed";                                                      m3log (parse, "load module: %d bytes", i_numBytes);
785
1.91k
    module->startFunction = -1;
786
    //module->hasWasmCodeCopy = false;
787
1.91k
    module->environment = i_environment;
788
789
1.91k
    const u8 * pos = i_bytes;
790
1.91k
    const u8 * end = pos + i_numBytes;
791
792
1.91k
    module->wasmStart = pos;
793
1.91k
    module->wasmEnd = end;
794
795
1.91k
    u32 magic, version;
796
1.91k
_   (Read_u32 (& magic, & pos, end));
797
1.91k
_   (Read_u32 (& version, & pos, end));
798
799
1.91k
    _throwif (m3Err_wasmMalformed, magic != 0x6d736100);
800
1.86k
    _throwif (m3Err_incompatibleWasmVersion, version != 1);
801
802
1.81k
    static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder
803
1.81k
    u8 expectedSection = 0;
804
805
5.95k
    while (pos < end)
806
5.26k
    {
807
5.26k
        u8 section;
808
5.26k
_       (ReadLEB_u7 (& section, & pos, end));
809
810
5.25k
        if (section != 0) {
811
            // Ensure sections appear only once and in order
812
11.8k
            while (sectionsOrder[expectedSection++] != section) {
813
8.87k
                _throwif(m3Err_misorderedWasmSection, expectedSection >= 12);
814
8.85k
            }
815
2.99k
        }
816
817
5.23k
        u32 sectionLength;
818
5.23k
_       (ReadLEB_u32 (& sectionLength, & pos, end));
819
5.18k
        _throwif(m3Err_wasmMalformed, pos + sectionLength > end);
820
821
5.16k
_       (ParseModuleSection (module, section, pos, sectionLength));
822
823
4.13k
        pos += sectionLength;
824
4.13k
    }
825
826
    // Spec: if a function section exists, a code section must also exist with
827
    // matching count (and vice versa). ParseSection_Code checks the other
828
    // direction; this covers the case where the code section is missing entirely.
829
690
    if (module->numFunctions > module->numFuncImports)
830
613
    {
831
613
        IM3Function firstNonImport = & module->functions [module->numFuncImports];
832
613
        _throwif (m3Err_wasmMalformed, firstNonImport->wasm == NULL);
833
585
    }
834
835
1.91k
} _catch:
836
837
1.91k
    if (result)
838
1.25k
    {
839
1.25k
        m3_FreeModule (module);
840
1.25k
        module = NULL;
841
1.25k
    }
842
843
1.91k
    * o_module = module;
844
845
1.91k
    return result;
846
690
}