Coverage Report

Created: 2026-09-01 06:51

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
// elem type + limits, shared by the table section and by table imports.
15
// The declared minimum is the table's initial size; table.grow can raise it up
16
// to the maximum, so an element segment reaching past the current size is out
17
// of bounds.
18
#if d_m3HasTypedRefs
19
20
// Reads a heap type: 'func', 'extern', or the index of a function type the
21
// module has already defined. Yields the heap-type bits of an m3type_t, which
22
// carry the canonical index rather than the module's own, so that structurally
23
// equal types compare equal across modules.
24
M3Result  ParseHeapType  (IM3Module i_module, m3type_t * o_heapBits, bytes_t * io_bytes, cbytes_t i_end)
25
{
26
    M3Result result = m3Err_none;
27
28
    i64 heap;
29
_   (ReadLebSigned (& heap, 33, io_bytes, i_end));
30
31
    if (heap == -d_waType_funcref)
32
        * o_heapBits = d_m3Type_heapAbstract;
33
    else if (heap == -d_waType_externref)
34
        * o_heapBits = d_m3Type_refExtern | d_m3Type_heapAbstract;
35
    else if (heap >= 0)
36
    {
37
        _throwif (m3Err_wasmMalformed, not i_module or (u64) heap >= i_module->numFuncTypes);
38
39
        IM3FuncType ftype = i_module->funcTypes [heap];
40
41
        // null while the type section is still being read: a type may only name
42
        // one that precedes it, recursion belongs to a later proposal
43
        _throwif (m3Err_wasmMalformed, not ftype);
44
45
        * o_heapBits = ftype->canonicalIndex;
46
    }
47
    else _throw (m3Err_invalidTypeId);
48
49
    _catch: return result;
50
}
51
52
#endif // d_m3HasTypedRefs
53
54
55
// Reads a value type, including the reference types the function references
56
// proposal spells out in full: (ref ht) and (ref null ht).
57
M3Result  ParseValueType  (IM3Module i_module, m3type_t * o_type, bytes_t * io_bytes, cbytes_t i_end)
58
18.3k
{
59
18.3k
    M3Result result = m3Err_none;
60
61
18.3k
    _throwif (m3Err_wasmUnderrun, * io_bytes >= i_end);
62
63
#if d_m3HasTypedRefs
64
    if (** io_bytes == d_waEncode_ref or ** io_bytes == d_waEncode_refNull)
65
    {
66
        u8 lead = * (* io_bytes)++;
67
68
        m3type_t heapBits = d_m3Type_heapAbstract;
69
_       (ParseHeapType (i_module, & heapBits, io_bytes, i_end));
70
71
        * o_type = d_m3Type_ref | heapBits | ((lead == d_waEncode_ref) ? d_m3Type_refNonNull : 0);
72
73
        return result;
74
    }
75
#endif
76
77
18.3k
    i8 wasmType;
78
18.3k
    u8 plainType;
79
18.3k
_   (ReadLEB_i7 (& wasmType, io_bytes, i_end));
80
18.3k
_   (NormalizeType (& plainType, wasmType));
81
82
18.3k
    * o_type = plainType;
83
84
18.3k
    _catch: return result;
85
18.3k
}
86
87
88
// Reads an elem type and limits without registering anything: an import
89
// descriptor holds on to the fields and only adds the table once it has a name.
90
static
91
M3Result  ReadType_TableType  (IM3Module i_module, M3TableInfo * o_info, bytes_t * io_bytes, cbytes_t i_end)
92
357
{
93
357
    M3Result result = m3Err_none;
94
95
357
    m3type_t elemType = c_m3Type_none;
96
357
    u8 flag = 0;
97
357
    u64 initSize = 0, maxSize = 0;
98
99
    // the width of the limits, and of every index into the table
100
357
    u32 addrBits = 32;
101
102
357
_   (ParseValueType (i_module, & elemType, io_bytes, i_end));
103
355
#if d_m3HasRefTypes
104
355
    _throwif (m3Err_wasmMalformed, not IsRefType (BaseTypeOf(elemType)));
105
#else
106
    _throwif (m3Err_wasmMalformed, elemType != c_m3Type_funcref);
107
#endif
108
109
355
_   (ReadLEB_u7 (& flag, io_bytes, i_end));
110
111
    // bit 0: has max, bit 2: table64 - the same flag memory64 gives a memory
112
354
#if d_m3HasMemory64
113
354
    _throwif (m3Err_wasmMalformed, flag & ~0x05u);
114
#else
115
    _throwif (m3Err_wasmMalformed, flag & ~0x01u);
116
#endif
117
118
354
    if (flag & 0x04u)
119
65
        addrBits = 64;
120
121
354
_   (ReadLebUnsigned (& initSize, addrBits, io_bytes, i_end));
122
354
    _throwif ("table overflow", initSize > d_m3MaxSaneTableSize);
123
124
353
    if (flag & 0x01u)
125
23
    {
126
23
_       (ReadLebUnsigned (& maxSize, addrBits, io_bytes, i_end));
127
23
        _throwif (m3Err_wasmMalformed, maxSize < initSize);
128
23
        _throwif ("table overflow", maxSize > d_m3MaxSaneTableSize);
129
21
    }
130
131
357
    _catch:
132
133
357
    o_info->elemType  = elemType;
134
357
    o_info->initSize  = (u32) initSize;
135
357
    o_info->maxSize   = (u32) maxSize;
136
357
    o_info->hasMax    = (flag & 0x01u) != 0;
137
357
    o_info->isTable64 = (flag & 0x04u) != 0;
138
139
357
    return result;
140
353
}
141
142
143
static
144
M3Result  ParseType_TableType  (IM3Module io_module, bytes_t * io_bytes, cbytes_t i_end)
145
321
{
146
321
    M3Result result = m3Err_none;
147
148
321
    M3TableInfo info;
149
150
321
_   (ReadType_TableType (io_module, & info, io_bytes, i_end));
151
317
_   (Module_AddTable (io_module, NULL, & info, false /* isImport */));
152
153
321
    _catch: return result;
154
317
}
155
156
157
158
M3Result  Parse_InitExprTyped  (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end, m3type_t * o_type);
159
160
M3Result  ParseType_Table  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
161
266
{
162
266
    M3Result result = m3Err_none;
163
164
266
    u32 numTables;
165
266
_   (ReadLEB_u32 (& numTables, & i_bytes, i_end));                       m3log (parse, "** Table [%d]", numTables);
166
167
266
    _throwif ("too many tables", (u64) numTables + io_module->numTables > d_m3MaxSaneTableCount);
168
169
583
    for (u32 i = 0; i < numTables; ++i)
170
321
    {
171
#if d_m3HasTypedRefs
172
        // function-references: 0x40 0x00 introduces a table with an explicit
173
        // initializer. A reference type never starts with 0x40, so the two
174
        // forms are told apart by the first byte; the second is reserved.
175
        bool hasInitExpr = (i_bytes < i_end) and (* i_bytes == 0x40);
176
        u32 tableIndex = io_module->numTables;
177
178
        if (hasInitExpr)
179
        {
180
            u8 reserved;
181
            ++i_bytes;
182
_           (Read_u8 (& reserved, & i_bytes, i_end));
183
            _throwif (m3Err_wasmMalformed, reserved != 0x00);
184
        }
185
186
        _       (ParseType_TableType (io_module, & i_bytes, i_end));
187
188
        if (hasInitExpr)
189
        {
190
            IM3Table table = io_module->tables [tableIndex];
191
192
            m3type_t initType;
193
194
            table->initExpr = i_bytes;
195
_           (Parse_InitExprTyped (io_module, & i_bytes, i_end, & initType));
196
            table->initExprSize = (u32) (i_bytes - table->initExpr);
197
            _throwif (m3Err_wasmMissingInitExpr, table->initExprSize <= 1);
198
199
            _throwif (m3Err_typeMismatch, not IsSubTypeOf (initType, table->type));
200
        }
201
        else
202
        {
203
            // no initializer means null, which only a nullable type can hold
204
            _throwif ("table of non-nullable type requires an initializer",
205
                      not IsNullableRef (io_module->tables [tableIndex]->type));
206
        }
207
#else
208
321
_       (ParseType_TableType (io_module, & i_bytes, i_end));
209
317
#endif
210
317
    }
211
212
262
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
213
214
266
    _catch: return result;
215
262
}
216
217
218
M3Result  ParseType_Memory  (M3MemoryInfo * o_memory, bytes_t * io_bytes, cbytes_t i_end)
219
3.61k
{
220
3.61k
    M3Result result = m3Err_none;
221
222
3.61k
    u8 flag;
223
224
    // The custom page sizes proposal admits only 1 and 65536, but nothing in
225
    // the engine cares which power of two it is, so any of them is accepted.
226
    // Declared up here so the throws below don't jump over its initialization.
227
3.61k
    u32 logPageSize = 16;
228
229
    // The width of the limits, and of everything that addresses the memory:
230
    // 32 unless bit 2 says memory64.
231
3.61k
    u32 addrBits = 32;
232
233
3.61k
_   (ReadLEB_u7 (& flag, io_bytes, i_end));
234
235
    // bit 0: has max, bit 2: memory64, bit 3: custom page size. bit 1 (shared)
236
    // belongs to a proposal we don't implement.
237
3.60k
#if d_m3HasMemory64
238
3.60k
    _throwif (m3Err_wasmMalformed, flag & ~0x0Du);
239
#else
240
    _throwif (m3Err_wasmMalformed, flag & ~0x09u);
241
#endif
242
243
3.60k
    o_memory->isMemory64 = (flag & (1u << 2)) != 0;
244
3.60k
    if (o_memory->isMemory64)
245
702
        addrBits = 64;
246
247
3.60k
_   (ReadLebUnsigned (& o_memory->initPages, addrBits, io_bytes, i_end));
248
249
3.60k
    o_memory->maxPages = 0;
250
3.60k
    o_memory->hasMax = (flag & (1u << 0)) != 0;
251
3.60k
    if (o_memory->hasMax)
252
810
    {
253
810
_       (ReadLebUnsigned (& o_memory->maxPages, addrBits, io_bytes, i_end));
254
255
        // Spec: memory limits validation - max must not be less than init
256
810
        _throwif (m3Err_wasmMalformed, o_memory->maxPages < o_memory->initPages);
257
806
    }
258
259
3.59k
    o_memory->pageSize = 0;
260
3.59k
    if (flag & (1u << 3)) {
261
497
_       (ReadLEB_u32 (& logPageSize, io_bytes, i_end));
262
263
496
        _throwif ("invalid custom page size", logPageSize > 16);
264
265
493
        o_memory->pageSize = 1u << logPageSize;
266
493
    }
267
268
    // Spec: memory limits must be valid within range 2^|addrtype|/pagesize.
269
    // That is 65536 pages at the default page size, 2^48 for a 64-bit memory,
270
    // and a whole address space of them when a page is a single byte.
271
3.59k
    {
272
3.59k
        u64 maxPagesAllowed = (logPageSize or addrBits < 64)
273
3.59k
                                ? (UINT64_C(1) << (addrBits - logPageSize))
274
3.59k
                                : UINT64_MAX;
275
276
3.59k
        _throwif (m3Err_wasmMalformed, o_memory->initPages > maxPagesAllowed);
277
3.59k
        if (o_memory->hasMax)
278
3.59k
            _throwif (m3Err_wasmMalformed, o_memory->maxPages > maxPagesAllowed);
279
3.59k
    }
280
281
3.61k
    _catch: return result;
282
3.59k
}
283
284
285
M3Result  ParseSection_Type  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
286
3.42k
{
287
3.42k
    IM3FuncType ftype = NULL;
288
289
3.42k
_try {
290
3.42k
    u32 numTypes;
291
3.42k
_   (ReadLEB_u32 (& numTypes, & i_bytes, i_end));                                   m3log (parse, "** Type [%d]", numTypes);
292
293
3.42k
    _throwif("too many types", numTypes > d_m3MaxSaneTypesCount);
294
295
3.42k
    if (numTypes)
296
3.42k
    {
297
        // table of IM3FuncType (that point to the actual M3FuncType struct in the Environment)
298
3.42k
        io_module->funcTypes = m3_AllocArray (IM3FuncType, numTypes);
299
3.42k
        _throwifnull (io_module->funcTypes);
300
3.42k
        io_module->numFuncTypes = numTypes;
301
302
7.21k
        for (u32 i = 0; i < numTypes; ++i)
303
3.82k
        {
304
3.82k
            i8 form;
305
3.82k
_           (ReadLEB_i7 (& form, & i_bytes, i_end));
306
3.81k
            _throwif (m3Err_wasmMalformed, form != -32); // for Wasm MVP
307
308
3.80k
            u32 numArgs;
309
3.80k
_           (ReadLEB_u32 (& numArgs, & i_bytes, i_end));
310
311
3.80k
            _throwif (m3Err_tooManyArgsRets, numArgs > d_m3MaxSaneFunctionArgRetCount);
312
3.80k
#if M3_HAS_VLA
313
3.80k
            m3type_t argTypes[numArgs+1]; // make ubsan happy
314
#else
315
            m3type_t argTypes [d_m3MaxSaneFunctionArgRetCount];
316
#endif
317
8.74k
            for (u32 a = 0; a < numArgs; ++a)
318
4.94k
_               (ParseValueType (io_module, & argTypes[a], & i_bytes, i_end));
319
320
3.80k
            u32 numRets;
321
3.80k
_           (ReadLEB_u32 (& numRets, & i_bytes, i_end));
322
3.80k
            _throwif (m3Err_tooManyArgsRets, (u64)(numRets) + numArgs > d_m3MaxSaneFunctionArgRetCount);
323
324
3.80k
_           (AllocFuncType (& ftype, numRets + numArgs));
325
3.80k
            ftype->numArgs = numArgs;
326
3.80k
            ftype->numRets = numRets;
327
328
12.6k
            for (u32 r = 0; r < numRets; ++r)
329
8.82k
_               (ParseValueType (io_module, & ftype->types[r], & i_bytes, i_end));
330
3.79k
            memcpy (ftype->types + numRets, argTypes, numArgs * sizeof (m3type_t));                                 m3log (parse, "    type %2d: %s", i, SPrintFuncTypeSignature (ftype));
331
332
3.79k
            Environment_AddFuncType (io_module->environment, & ftype);
333
3.79k
            io_module->funcTypes [i] = ftype;
334
3.79k
            ftype = NULL; // ownership transferred to environment
335
3.79k
        }
336
3.42k
    }
337
338
3.39k
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
339
340
3.42k
} _catch:
341
342
3.42k
    if (result)
343
86
    {
344
86
        m3_Free (ftype);
345
        // FIX: M3FuncTypes in the table are leaked
346
86
        m3_Free (io_module->funcTypes);
347
86
        io_module->numFuncTypes = 0;
348
86
    }
349
350
3.42k
    return result;
351
3.34k
}
352
353
354
M3Result  ParseSection_Function  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
355
3.28k
{
356
3.28k
    M3Result result = m3Err_none;
357
358
3.28k
    u32 numFunctions;
359
3.28k
_   (ReadLEB_u32 (& numFunctions, & i_bytes, i_end));                               m3log (parse, "** Function [%d]", numFunctions);
360
361
3.28k
    _throwif("too many functions", numFunctions > d_m3MaxSaneFunctionsCount);
362
363
3.28k
_   (Module_PreallocFunctions(io_module, io_module->numFunctions + numFunctions));
364
365
6.92k
    for (u32 i = 0; i < numFunctions; ++i)
366
3.63k
    {
367
3.63k
        u32 funcTypeIndex;
368
3.63k
_       (ReadLEB_u32 (& funcTypeIndex, & i_bytes, i_end));
369
370
3.63k
_       (Module_AddFunction (io_module, funcTypeIndex, NULL /* import info */));
371
3.63k
    }
372
373
3.28k
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
374
375
3.28k
    _catch: return result;
376
3.28k
}
377
378
379
// One externtype, read but not yet registered. The compact encoding that shares
380
// a type across a run needs the two halves apart: the type sits ahead of the
381
// item names, so it is read once and then registered for each of them.
382
typedef struct M3ImportDesc
383
{
384
    u8              kind;
385
    u32             typeIndex;                  // function
386
    m3type_t        type;                       // global type
387
    u8              isMutable;                  // global
388
    M3TableInfo     table;
389
    M3MemoryInfo    memory;
390
}
391
M3ImportDesc;
392
393
394
#if d_m3HasExceptionHandling
395
396
// A tag_type: a reserved attribute byte that must be 0 (the exception
397
// attribute) and the index of the function type giving the payload. The
398
// proposal reserves the byte so later kinds of tag can share the encoding.
399
static
400
M3Result  ReadTagType  (IM3Module i_module, u32 * o_typeIndex, bytes_t * io_bytes, cbytes_t i_end)
401
431
{
402
431
    M3Result result = m3Err_none;
403
404
431
    u8 attribute;
405
431
_   (Read_u8 (& attribute, io_bytes, i_end));
406
431
    _throwif (m3Err_wasmMalformed, attribute != 0);
407
408
427
_   (ReadLEB_u32 (o_typeIndex, io_bytes, i_end));
409
427
    _throwif (m3Err_unknownType, not i_module or * o_typeIndex >= i_module->numFuncTypes);
410
411
    // an exception tag's type describes its payload, so it yields nothing
412
425
    _throwif (m3Err_wasmMalformed, GetFuncTypeNumResults (i_module->funcTypes [* o_typeIndex]) != 0);
413
414
431
    _catch: return result;
415
425
}
416
417
#endif // d_m3HasExceptionHandling
418
419
420
// Reads and validates one externtype. Mutates nothing in the module, so an
421
// externtype is checked even when the compact run that shares it is empty.
422
static
423
M3Result  ReadImportDesc  (IM3Module i_module, M3ImportDesc * o_desc, bytes_t * io_bytes, cbytes_t i_end)
424
2.58k
{
425
2.58k
    M3Result result = m3Err_none;
426
427
2.58k
_   (Read_u8 (& o_desc->kind, io_bytes, i_end));
428
429
2.58k
    switch (o_desc->kind)
430
2.58k
    {
431
2.36k
        case d_externalKind_function:
432
2.36k
_           (ReadLEB_u32 (& o_desc->typeIndex, io_bytes, i_end));
433
2.36k
            break;
434
435
36
        case d_externalKind_table:
436
36
_           (ReadType_TableType (i_module, & o_desc->table, io_bytes, i_end));
437
34
            break;
438
439
44
        case d_externalKind_memory:
440
44
_           (ParseType_Memory (& o_desc->memory, io_bytes, i_end));
441
43
            break;
442
443
54
        case d_externalKind_global:
444
54
_           (ParseValueType (i_module, & o_desc->type, io_bytes, i_end));
445
54
_           (ReadLEB_u7 (& o_desc->isMutable, io_bytes, i_end));                    m3log (parse, "     global: %s mutable=%d", c_waTypes [BaseTypeOf(o_desc->type)], (u32) o_desc->isMutable);
446
54
            _throwif (m3Err_wasmMalformed, o_desc->isMutable > 1);
447
54
            break;
448
449
0
#if d_m3HasExceptionHandling
450
72
        case d_externalKind_tag:
451
72
_           (ReadTagType (i_module, & o_desc->typeIndex, io_bytes, i_end));
452
71
            break;
453
0
#endif
454
455
7
        default:
456
7
            _throw (m3Err_wasmMalformed);
457
2.58k
    }
458
459
2.58k
    _catch: return result;
460
2.58k
}
461
462
463
// Registers the import that i_desc describes. Takes ownership of *io_import
464
// wherever the module keeps the strings, clearing the struct so the caller's
465
// FreeImportInfo() doesn't free what was handed over.
466
static
467
M3Result  ApplyImportDesc  (IM3Module io_module, const M3ImportDesc * i_desc, M3ImportInfo * io_import)
468
5.41k
{
469
5.41k
    M3Result result = m3Err_none;
470
471
5.41k
    M3ImportInfo clearImport = { NULL, NULL };                                      m3log (parse, "    kind: %d '%s.%s' ",
472
5.41k
                                                                                            (u32) i_desc->kind, io_import->moduleUtf8, io_import->fieldUtf8);
473
5.41k
    switch (i_desc->kind)
474
5.41k
    {
475
3.43k
        case d_externalKind_function:
476
3.43k
        {
477
3.43k
_           (Module_AddFunction (io_module, i_desc->typeIndex, io_import))
478
3.43k
            * io_import = clearImport;
479
480
3.43k
            io_module->numFuncImports++;
481
3.43k
        }
482
0
        break;
483
484
175
        case d_externalKind_table:
485
175
        {
486
175
            IM3Table table;
487
175
_           (Module_AddTable (io_module, & table, & i_desc->table, true /* isImport */));
488
175
            table->import = * io_import;
489
175
            * io_import = clearImport;
490
175
        }
491
0
        break;
492
493
1.19k
        case d_externalKind_memory:
494
1.19k
        {
495
1.19k
            IM3Memory memory;
496
497
1.19k
#if d_m3HasMultiMemory
498
1.19k
            _throwif (m3Err_tooManyMemorySections, io_module->numMemories >= d_m3MaxSaneMemoriesCount);
499
#else
500
            // MVP: one memory per module, imported or declared
501
            _throwif (m3Err_tooManyMemorySections, io_module->numMemories >= 1);
502
#endif
503
504
1.19k
_           (Module_AddMemory (io_module, & memory, & i_desc->memory, true /* isImport */));
505
1.19k
            memory->import = * io_import;
506
1.19k
            * io_import = clearImport;
507
1.19k
        }
508
0
        break;
509
510
185
        case d_externalKind_global:
511
185
        {
512
185
            IM3Global global;
513
185
_           (Module_AddGlobal (io_module, & global, i_desc->type, i_desc->isMutable, true /* isImport */));
514
185
            global->import = * io_import;
515
185
            * io_import = clearImport;
516
185
        }
517
0
        break;
518
519
0
#if d_m3HasExceptionHandling
520
420
        case d_externalKind_tag:
521
420
        {
522
420
            IM3Tag tag;
523
420
_           (Module_AddTag (io_module, & tag, io_module->funcTypes [i_desc->typeIndex], true /* isImport */));
524
420
            tag->import = * io_import;
525
420
            * io_import = clearImport;
526
420
        }
527
0
        break;
528
0
#endif
529
530
0
        default:
531
0
            _throw (m3Err_wasmMalformed);
532
5.41k
    }
533
534
5.41k
    _catch: return result;
535
5.41k
}
536
537
538
#if d_m3HasCompactImports
539
540
// The compact encodings only apply when the item name is empty. Read_utf8()
541
// hands back a C string, so a name that merely begins with U+0000 would look
542
// empty; the length prefix is what has to be tested.
543
static
544
bool  IsEmptyName  (bytes_t i_bytes, cbytes_t i_end)
545
465
{
546
465
    u32 length;
547
465
    return (not ReadLEB_u32 (& length, & i_bytes, i_end)) and length == 0;
548
465
}
549
550
#endif // d_m3HasCompactImports
551
552
553
#if d_m3HasExceptionHandling
554
555
M3Result  ParseSection_Tag  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
556
5
{
557
5
    M3Result result = m3Err_none;
558
559
5
    u32 numTags;
560
5
_   (ReadLEB_u32 (& numTags, & i_bytes, i_end));                                    m3log (parse, "** Tag [%d]", numTags);
561
562
5
    _throwif ("too many tags", numTags > d_m3MaxSaneTagsCount);
563
564
359
    for (u32 i = 0; i < numTags; ++i)
565
359
    {
566
359
        u32 typeIndex;
567
359
_       (ReadTagType (io_module, & typeIndex, & i_bytes, i_end));                   m3log (parse, "    tag: [%d] type: %d", i, typeIndex);
568
569
354
_       (Module_AddTag (io_module, NULL, io_module->funcTypes [typeIndex], false));
570
354
    }
571
572
0
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
573
574
5
    _catch: return result;
575
0
}
576
577
#endif // d_m3HasExceptionHandling
578
579
580
M3Result  ParseSection_Import  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
581
54
{
582
54
    M3Result result = m3Err_none;
583
584
54
    M3ImportInfo import = { NULL, NULL };
585
586
    // the limit is on the imports the section declares, which a compact run
587
    // multiplies well past the entry count. Declared up here so the throws
588
    // below don't jump over its initialization.
589
54
    u32 numImports = 0;
590
591
    // A count of entries, not of imports: under the compact encodings a single
592
    // entry stands for a whole run of them.
593
54
    u32 numEntries;
594
54
_   (ReadLEB_u32 (& numEntries, & i_bytes, i_end));                                 m3log (parse, "** Import [%d]", numEntries);
595
596
54
    _throwif("too many imports", numEntries > d_m3MaxSaneImportsCount);
597
598
    // Most imports are functions, so we won't waste much space anyway (if any)
599
54
_   (Module_PreallocFunctions(io_module, io_module->numFunctions + numEntries));
600
601
2.16k
    for (u32 i = 0; i < numEntries; ++i)
602
2.15k
    {
603
2.15k
        M3ImportDesc desc;
604
605
2.15k
_       (Read_utf8 (& import.moduleUtf8, & i_bytes, i_end));
606
607
2.15k
#if d_m3HasCompactImports
608
2.15k
        bytes_t fieldStart = i_bytes;
609
2.15k
#endif
610
2.15k
_       (Read_utf8 (& import.fieldUtf8, & i_bytes, i_end));
611
612
2.14k
#if d_m3HasCompactImports
613
2.14k
        u8 compact = (i_bytes < i_end) ? * i_bytes : 0;
614
615
        // An empty item name where an externtype should start marks a compact
616
        // run sharing this module name. Neither marker is a valid externtype,
617
        // so nothing that used to parse changes meaning.
618
2.14k
        if ((compact == d_compactImports_perItemType or compact == d_compactImports_sharedType) and
619
465
            IsEmptyName (fieldStart, i_end))
620
464
        {
621
464
            ++i_bytes;
622
623
464
            m3_Free (import.fieldUtf8);         // it only marked the compact form
624
464
            import.fieldUtf8 = NULL;
625
626
            // the shared form puts its one externtype ahead of the item names,
627
            // so reading it here is also what validates it
628
464
            if (compact == d_compactImports_sharedType)
629
463
_               (ReadImportDesc (io_module, & desc, & i_bytes, i_end));
630
631
463
            u32 numItems;
632
463
_           (ReadLEB_u32 (& numItems, & i_bytes, i_end));                           m3log (parse, "    compact: %d import(s) of '%s'", numItems, import.moduleUtf8);
633
634
463
            _throwif("too many imports", (u64) numImports + numItems > d_m3MaxSaneImportsCount);
635
463
            numImports += numItems;
636
637
463
_           (Module_PreallocFunctions(io_module, io_module->numFunctions + numItems));
638
639
4.20k
            for (u32 j = 0; j < numItems; ++j)
640
3.75k
            {
641
3.75k
                M3ImportInfo item = { NULL, NULL };
642
643
3.75k
                item.moduleUtf8 = (cstr_t) m3_CopyMem (import.moduleUtf8, strlen (import.moduleUtf8) + 1);
644
645
3.75k
                if (item.moduleUtf8)
646
3.75k
                {
647
3.75k
                    result = Read_utf8 (& item.fieldUtf8, & i_bytes, i_end);
648
649
                    // the shared form reuses the externtype read above
650
3.75k
                    if (not result and compact == d_compactImports_perItemType)
651
471
                        result = ReadImportDesc (io_module, & desc, & i_bytes, i_end);
652
653
3.75k
                    if (not result)
654
3.74k
                        result = ApplyImportDesc (io_module, & desc, & item);
655
3.75k
                }
656
0
                else result = m3Err_mallocFailed;
657
658
3.75k
                FreeImportInfo (& item);
659
3.75k
                _throwif (result, result);
660
3.74k
            }
661
662
445
            FreeImportInfo (& import);
663
445
            continue;
664
463
        }
665
1.68k
#endif // d_m3HasCompactImports
666
667
1.68k
        _throwif("too many imports", numImports >= d_m3MaxSaneImportsCount);
668
1.68k
        ++numImports;
669
670
1.68k
_       (ReadImportDesc (io_module, & desc, & i_bytes, i_end));
671
1.67k
_       (ApplyImportDesc (io_module, & desc, & import));
672
673
1.66k
        FreeImportInfo (& import);
674
1.66k
    }
675
676
12
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
677
678
54
    _catch:
679
680
54
    FreeImportInfo (& import);
681
682
54
    return result;
683
12
}
684
685
686
M3Result  ParseSection_Export  (IM3Module io_module, bytes_t i_bytes, cbytes_t  i_end)
687
22
{
688
22
    M3Result result = m3Err_none;
689
22
    const char * utf8 = NULL;
690
22
#if d_m3EnableValidation
691
    // We store name pointers + lengths to handle embedded NUL bytes correctly
692
22
    typedef struct { const u8 * ptr; u16 len; } ExportName;
693
22
    ExportName * exportNames = NULL;
694
22
#endif
695
696
22
    u32 numExports;
697
22
_   (ReadLEB_u32 (& numExports, & i_bytes, i_end));                                 m3log (parse, "** Export [%d]", numExports);
698
699
22
    _throwif("too many exports", numExports > d_m3MaxSaneExportsCount);
700
701
22
#if d_m3EnableValidation
702
    // Spec: all export names must be different
703
22
    if (numExports > 1)
704
22
    {
705
22
        exportNames = (ExportName *) m3_Malloc ("exportNames", sizeof(ExportName) * numExports);
706
22
    }
707
22
#endif
708
709
182
    for (u32 i = 0; i < numExports; ++i)
710
181
    {
711
181
        u8 exportKind;
712
181
        u32 index;
713
714
        // Read name length and remember raw position for uniqueness check
715
181
#if d_m3EnableValidation
716
181
        const u8 * nameStart = i_bytes;
717
181
        u32 nameLen = 0;
718
181
        {
719
181
            bytes_t tmp = i_bytes;
720
181
            M3Result rl = ReadLEB_u32 (& nameLen, & tmp, i_end);
721
181
            if (rl) { m3_Free(exportNames); _throw(rl); }
722
181
            nameStart = tmp; // points to the raw name bytes
723
181
        }
724
0
#endif
725
726
181
_       (Read_utf8 (& utf8, & i_bytes, i_end));
727
169
_       (Read_u8 (& exportKind, & i_bytes, i_end));
728
169
_       (ReadLEB_u32 (& index, & i_bytes, i_end));                                  m3log (parse, "    index: %3d; kind: %d; export: '%s'; ", index, (u32) exportKind, utf8);
729
730
168
#if d_m3EnableValidation
731
168
        if (exportNames)
732
168
        {
733
1.15k
            for (u32 j = 0; j < i; ++j)
734
990
            {
735
990
                if (exportNames[j].len == nameLen &&
736
391
                    memcmp (exportNames[j].ptr, nameStart, nameLen) == 0)
737
6
                {
738
6
                    m3_Free (exportNames);
739
6
                    _throw (m3Err_wasmMalformed);  // duplicate export name
740
0
                }
741
990
            }
742
162
            exportNames[i].ptr = nameStart;
743
162
            exportNames[i].len = (u16)nameLen;
744
162
        }
745
162
#endif
746
747
162
        if (exportKind == d_externalKind_function)
748
53
        {
749
53
            _throwif(m3Err_wasmMalformed, index >= io_module->numFunctions);
750
51
            IM3Function func = &(io_module->functions [index]);
751
51
            if (func->numNames < d_m3MaxDuplicateFunctionImpl)
752
37
            {
753
37
                func->names[func->numNames++] = utf8;
754
37
                func->export_name = utf8;
755
37
                utf8 = NULL; // ownership transferred to M3Function
756
37
            }
757
51
_           (Module_DeclareFunction (io_module, index));
758
51
        }
759
109
        else if (exportKind == d_externalKind_global)
760
0
        {
761
0
            _throwif(m3Err_wasmMalformed, index >= io_module->numGlobals);
762
0
            IM3Global global = &(io_module->globals [index]);
763
0
            m3_Free (global->name);
764
0
            global->name = utf8;
765
0
            utf8 = NULL; // ownership transferred to M3Global
766
0
        }
767
109
        else if (exportKind == d_externalKind_memory)
768
0
        {
769
0
            _throwif(m3Err_wasmMalformed, index >= io_module->numMemories);
770
0
            IM3Memory memory = io_module->memories [index];
771
0
            m3_Free (memory->exportName);
772
0
            memory->exportName = utf8;
773
0
            utf8 = NULL; // ownership transferred to M3Memory
774
0
        }
775
109
        else if (exportKind == d_externalKind_table)
776
0
        {
777
0
            _throwif(m3Err_wasmMalformed, index >= io_module->numTables);
778
0
            IM3Table table = io_module->tables [index];
779
0
            m3_Free (table->exportName);
780
0
            table->exportName = utf8;
781
0
            utf8 = NULL; // ownership transferred to M3Table
782
0
        }
783
109
#if d_m3HasExceptionHandling
784
109
        else if (exportKind == d_externalKind_tag)
785
0
        {
786
0
            _throwif(m3Err_wasmMalformed, index >= io_module->numTags);
787
0
            IM3Tag tag = &(io_module->tags [index]);
788
0
            m3_Free (tag->name);
789
0
            tag->name = utf8;
790
0
            utf8 = NULL; // ownership transferred to M3Tag
791
0
        }
792
160
#endif
793
794
160
        m3_Free (utf8);
795
160
    }
796
797
1
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
798
799
22
_catch:
800
22
    m3_Free (utf8);
801
22
#if d_m3EnableValidation
802
22
    m3_Free (exportNames);
803
22
#endif
804
22
    return result;
805
0
}
806
807
808
M3Result  ParseSection_Start  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
809
0
{
810
0
    M3Result result = m3Err_none;
811
812
0
    u32 startFuncIndex;
813
0
_   (ReadLEB_u32 (& startFuncIndex, & i_bytes, i_end));                               m3log (parse, "** Start Function: %d", startFuncIndex);
814
815
0
    if (startFuncIndex < io_module->numFunctions)
816
0
    {
817
        // Spec: start function type must be [] -> []
818
0
        IM3Function func = & io_module->functions [startFuncIndex];
819
0
        if (func->funcType)
820
0
        {
821
0
            _throwif (m3Err_wasmMalformed,
822
0
                      func->funcType->numArgs != 0 || func->funcType->numRets != 0);
823
0
        }
824
825
0
        io_module->startFunction = startFuncIndex;
826
0
    }
827
0
    else result = "start function index out of bounds";
828
829
0
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
830
831
0
    _catch: return result;
832
0
}
833
834
835
// o_type, when asked for, reports what the expression leaves on the stack, so
836
// the caller can check it against the type the expression is initializing.
837
M3Result  Parse_InitExprTyped  (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end, m3type_t * o_type)
838
186
{
839
186
    M3Result result = m3Err_none;
840
841
    // this doesn't generate code pages. just walks the wasm bytecode to find the end
842
843
#if defined(d_m3PreferStaticAlloc)
844
    static M3Compilation compilation;
845
#else
846
186
    M3Compilation compilation;
847
186
#endif
848
186
    compilation = (M3Compilation){ .runtime = NULL, .module = io_module, .wasm = * io_bytes, .wasmEnd = i_end, .isInitExpr = true };
849
850
186
    result = CompileBlockStatements (& compilation);
851
852
186
    * io_bytes = compilation.wasm;
853
854
186
    if (o_type)
855
0
    {
856
0
        * o_type = compilation.stackIndex ? compilation.typeStack [compilation.stackIndex - 1]
857
0
                                          : (m3type_t) c_m3Type_none;
858
0
    }
859
860
186
    return result;
861
186
}
862
863
864
M3Result  Parse_InitExpr  (M3Module * io_module, bytes_t * io_bytes, cbytes_t i_end)
865
186
{
866
186
    return Parse_InitExprTyped (io_module, io_bytes, i_end, NULL);
867
186
}
868
869
870
M3Result  ParseSection_Element  (IM3Module io_module, bytes_t i_bytes, cbytes_t i_end)
871
20
{
872
20
    M3Result result = m3Err_none;
873
874
20
    u32 numSegments;
875
20
    bytes_t pos;
876
20
_   (ReadLEB_u32 (& numSegments, & i_bytes, i_end));                         m3log (parse, "** Element [%d]", numSegments);
877
878
20
    _throwif ("too many element segments", numSegments > d_m3MaxSaneElementSegments);
879
880
20
    io_module->elementSegments = m3_AllocArray (M3ElementSegment, numSegments);
881
20
    _throwifnull (io_module->elementSegments);
882
20
    io_module->numElementSegments = numSegments;
883
20
    io_module->elementSectionEnd = i_end;
884
885
    // Records where each segment's parts live; the elements are resolved later,
886
    // in InitTableAndElements.
887
20
    pos = i_bytes;
888
48
    for (u32 i = 0; i < numSegments; ++i)
889
36
    {
890
36
        M3ElementSegment * segment = & io_module->elementSegments [i];
891
892
36
        u32 flags;
893
36
_       (ReadLEB_u32 (& flags, & pos, i_end));
894
36
#if d_m3HasRefTypes
895
36
        _throwif (m3Err_wasmMalformed, flags > 7);
896
#else
897
        _throwif (m3Err_wasmMalformed, flags != c_m3Elem_active);
898
#endif
899
900
35
        segment->mode   = flags & 0x3;
901
35
        segment->isExpr = (flags & 0x4) != 0;
902
35
        segment->type   = c_m3Type_funcref;
903
904
35
        if (segment->mode == c_m3Elem_activeIdx)
905
35
_           (ReadLEB_u32 (& segment->tableIndex, & pos, i_end));
906
907
35
        bool isActive = (segment->mode == c_m3Elem_active or segment->mode == c_m3Elem_activeIdx);
908
909
35
        if (isActive)
910
7
        {
911
7
            _throwif (m3Err_wasmMalformed, segment->tableIndex >= io_module->numTables);
912
913
2
            segment->initExpr = pos;
914
2
_           (Parse_InitExpr (io_module, & pos, i_end));
915
1
            segment->initExprSize = (u32) (pos - segment->initExpr);
916
1
            _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1);
917
1
        }
918
919
        // Only the table-0 active form leaves the element type implicit
920
29
        if (segment->mode != c_m3Elem_active)
921
28
        {
922
28
            if (segment->isExpr)
923
16
            {
924
16
_               (ParseValueType (io_module, & segment->type, & pos, i_end));
925
16
                _throwif (m3Err_wasmMalformed, not IsRefType (BaseTypeOf(segment->type)));
926
16
            }
927
12
            else
928
12
            {
929
12
                u8 elemKind;
930
12
_               (Read_u8 (& elemKind, & pos, i_end));
931
12
                _throwif (m3Err_wasmMalformed, elemKind != 0x00);   // funcref
932
12
            }
933
28
        }
934
935
29
        if (isActive)
936
28
            _throwif (m3Err_typeMismatch, io_module->tables [segment->tableIndex]->type != segment->type);
937
938
28
_       (ReadLEB_u32 (& segment->numElements, & pos, i_end));
939
28
        _throwif ("table overflow", segment->numElements > d_m3MaxSaneTableSize);
940
941
28
        segment->elements = pos;
942
943
40
        for (u32 e = 0; e < segment->numElements; ++e)
944
12
        {
945
12
            if (segment->isExpr)
946
0
_               (Parse_InitExpr (io_module, & pos, i_end))
947
12
            else
948
12
            {
949
12
                u32 funcIndex;
950
12
_               (ReadLEB_u32 (& funcIndex, & pos, i_end));
951
12
                _throwif ("function index out of range", funcIndex >= io_module->numFunctions);
952
12
_               (Module_DeclareFunction (io_module, funcIndex));
953
12
            }
954
12
        }
955
28
    }
956
957
12
    _throwif (m3Err_wasmMalformed, pos != i_end);           // section size mismatch
958
959
20
    _catch: return result;
960
12
}
961
962
963
M3Result  ParseSection_Code  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
964
3.26k
{
965
3.26k
    M3Result result;
966
967
3.26k
    u32 numFunctions;
968
3.26k
_   (ReadLEB_u32 (& numFunctions, & i_bytes, i_end));                               m3log (parse, "** Code [%d]", numFunctions);
969
970
3.26k
    if (numFunctions != io_module->numFunctions - io_module->numFuncImports)
971
1
    {
972
1
        _throw ("mismatched function count in code section");
973
0
    }
974
975
6.59k
    for (u32 f = 0; f < numFunctions; ++f)
976
3.33k
    {
977
3.33k
        const u8 * start = i_bytes;
978
979
3.33k
        u32 size;
980
3.33k
_       (ReadLEB_u32 (& size, & i_bytes, i_end));
981
982
3.33k
        if (size)
983
3.26k
        {
984
3.26k
            i_bytes += size;
985
986
3.26k
            if (i_bytes <= i_end)
987
3.26k
            {
988
                /*
989
                const u8 * ptr = i_bytes - size;
990
991
                u32 numLocalBlocks;
992
_               (ReadLEB_u32 (& numLocalBlocks, & ptr, i_end));                                      m3log (parse, "    code size: %-4d", size);
993
994
                u32 numLocals = 0;
995
996
                for (u32 l = 0; l < numLocalBlocks; ++l)
997
                {
998
                    u32 varCount;
999
                    i8 wasmType;
1000
                    u8 normalType;
1001
1002
_                   (ReadLEB_u32 (& varCount, & ptr, i_end));
1003
_                   (ReadLEB_i7 (& wasmType, & ptr, i_end));
1004
_                   (NormalizeType (& normalType, wasmType));
1005
1006
                    numLocals += varCount;                                                      m3log (parse, "      %2d locals; type: '%s'", varCount, c_waTypes [normalType]);
1007
                }
1008
                 */
1009
1010
3.26k
                IM3Function func = Module_GetFunction (io_module, f + io_module->numFuncImports);
1011
1012
3.26k
                func->module = io_module;
1013
3.26k
                func->wasm = start;
1014
3.26k
                func->wasmEnd = i_bytes;
1015
                //func->ownsWasmCode = io_module->hasWasmCodeCopy;
1016
//                func->numLocals = numLocals;
1017
3.26k
            }
1018
3.26k
            else _throw (m3Err_wasmSectionOverrun);
1019
3.26k
        }
1020
3.33k
    }
1021
1022
3.26k
    _catch:
1023
1024
3.26k
    if (not result and i_bytes != i_end)
1025
0
        result = m3Err_wasmSectionUnderrun;
1026
1027
3.26k
    return result;
1028
3.26k
}
1029
1030
1031
M3Result  ParseSection_Data  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1032
20
{
1033
20
    M3Result result = m3Err_none;
1034
1035
20
    u32 numDataSegments;
1036
20
_   (ReadLEB_u32 (& numDataSegments, & i_bytes, i_end));                            m3log (parse, "** Data [%d]", numDataSegments);
1037
1038
20
    _throwif("too many data segments", numDataSegments > d_m3MaxSaneDataSegments);
1039
1040
20
    io_module->dataSegments = m3_AllocArray (M3DataSegment, numDataSegments);
1041
20
    _throwifnull(io_module->dataSegments);
1042
20
    io_module->numDataSegments = numDataSegments;
1043
1044
84
    for (u32 i = 0; i < numDataSegments; ++i)
1045
81
    {
1046
81
        M3DataSegment * segment = & io_module->dataSegments [i];
1047
1048
        // 0: active in memory 0, 1: passive, 2: active with an explicit memory index
1049
81
        u32 flags;
1050
81
_       (ReadLEB_u32 (& flags, & i_bytes, i_end));
1051
78
        _throwif (m3Err_wasmMalformed, flags > 2);
1052
1053
66
        segment->isPassive = (flags == 1);
1054
1055
66
        if (flags == 2)
1056
5
        {
1057
5
_           (ReadLEB_u32 (& segment->memoryRegion, & i_bytes, i_end));
1058
5
        }
1059
1060
66
        if (not segment->isPassive)
1061
23
        {
1062
            // The segment names the memory it initializes; only multi-memory
1063
            // lets that be anything other than 0.
1064
#if ! d_m3HasMultiMemory
1065
            _throwif (m3Err_wasmMalformed, segment->memoryRegion != 0);
1066
#endif
1067
23
            _throwif (m3Err_wasmMalformed, segment->memoryRegion >= io_module->numMemories);
1068
1069
22
            segment->initExpr = i_bytes;
1070
22
_           (Parse_InitExpr (io_module, & i_bytes, i_end));
1071
21
            segment->initExprSize = (u32) (i_bytes - segment->initExpr);
1072
1073
21
            _throwif (m3Err_wasmMissingInitExpr, segment->initExprSize <= 1);
1074
21
        }
1075
1076
64
_       (ReadLEB_u32 (& segment->size, & i_bytes, i_end));
1077
64
        segment->data = i_bytes;                                                    m3log (parse, "    segment [%u]  memory: %u;  expr-size: %d;  size: %d",
1078
64
                                                                                       i, segment->memoryRegion, segment->initExprSize, segment->size);
1079
64
        i_bytes += segment->size;
1080
1081
64
        _throwif("data segment underflow", i_bytes > i_end);
1082
64
    }
1083
1084
3
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
1085
1086
20
    _catch:
1087
1088
20
    return result;
1089
2
}
1090
1091
1092
M3Result  ParseSection_DataCount  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1093
4
{
1094
4
    M3Result result = m3Err_none;
1095
1096
4
_   (ReadLEB_u32 (& io_module->dataCount, & i_bytes, i_end));                    m3log (parse, "** DataCount [%d]", io_module->dataCount);
1097
1098
4
    io_module->hasDataCount = true;
1099
1100
4
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
1101
1102
4
    _catch: return result;
1103
4
}
1104
1105
1106
M3Result  ParseSection_Memory  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1107
633
{
1108
633
    M3Result result = m3Err_none;
1109
1110
    // TODO: MVP; assert no memory imported
1111
1112
633
    u32 numMemories;
1113
633
_   (ReadLEB_u32 (& numMemories, & i_bytes, i_end));                             m3log (parse, "** Memory [%d]", numMemories);
1114
1115
633
#if d_m3HasMultiMemory
1116
633
    _throwif (m3Err_tooManyMemorySections,
1117
633
              (u64) numMemories + io_module->numMemories > d_m3MaxSaneMemoriesCount);
1118
#else
1119
    // MVP: at most one memory, counting any that was already imported
1120
    _throwif (m3Err_tooManyMemorySections, numMemories > 1);
1121
    _throwif (m3Err_tooManyMemorySections, (u64) numMemories + io_module->numMemories > 1);
1122
#endif
1123
1124
4.18k
    for (u32 i = 0; i < numMemories; ++i)
1125
3.57k
    {
1126
3.57k
        M3MemoryInfo info;
1127
3.57k
_       (ParseType_Memory (& info, & i_bytes, i_end));
1128
3.54k
_       (Module_AddMemory (io_module, NULL, & info, false /* isImport */));
1129
3.54k
    }
1130
1131
610
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
1132
1133
633
    _catch: return result;
1134
610
}
1135
1136
1137
M3Result  ParseSection_Global  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1138
155
{
1139
155
    M3Result result = m3Err_none;
1140
1141
155
    u32 numGlobals;
1142
155
_   (ReadLEB_u32 (& numGlobals, & i_bytes, i_end));                                 m3log (parse, "** Global [%d]", numGlobals);
1143
1144
155
    _throwif("too many globals", numGlobals > d_m3MaxSaneGlobalsCount);
1145
1146
285
    for (u32 i = 0; i < numGlobals; ++i)
1147
174
    {
1148
174
        m3type_t type = c_m3Type_none;
1149
174
        u8 isMutable;
1150
1151
174
_       (ParseValueType (io_module, & type, & i_bytes, i_end));
1152
172
_       (ReadLEB_u7 (& isMutable, & i_bytes, i_end));                                 m3log (parse, "    global: [%d] %s mutable: %d", i, c_waTypes [BaseTypeOf(type)],   (u32) isMutable);
1153
172
        _throwif (m3Err_wasmMalformed, isMutable > 1);
1154
1155
162
        IM3Global global;
1156
162
_       (Module_AddGlobal (io_module, & global, type, isMutable, false /* isImport */));
1157
1158
162
        global->initExpr = i_bytes;
1159
162
_       (Parse_InitExpr (io_module, & i_bytes, i_end));
1160
130
        global->initExprSize = (u32) (i_bytes - global->initExpr);
1161
1162
130
        _throwif (m3Err_wasmMissingInitExpr, global->initExprSize <= 1);
1163
130
    }
1164
1165
111
    _throwif (m3Err_wasmMalformed, i_bytes != i_end);      // section size mismatch
1166
1167
155
    _catch: return result;
1168
105
}
1169
1170
1171
M3Result  ParseSection_Name  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1172
3.75k
{
1173
3.75k
    M3Result result = m3Err_none;
1174
1175
3.75k
    cstr_t name;
1176
1177
8.93k
    while (i_bytes < i_end)
1178
5.21k
    {
1179
5.21k
        u8 nameType;
1180
5.21k
        u32 payloadLength;
1181
1182
5.21k
_       (ReadLEB_u7 (& nameType, & i_bytes, i_end));
1183
5.21k
_       (ReadLEB_u32 (& payloadLength, & i_bytes, i_end));
1184
1185
5.21k
        bytes_t start = i_bytes;
1186
5.21k
        if (nameType == 1)
1187
3.91k
        {
1188
3.91k
            u32 numNames;
1189
3.91k
_           (ReadLEB_u32 (& numNames, & i_bytes, i_end));
1190
1191
3.91k
            _throwif("too many names", numNames > d_m3MaxSaneFunctionsCount);
1192
1193
10.8k
            for (u32 i = 0; i < numNames; ++i)
1194
7.02k
            {
1195
7.02k
                u32 index;
1196
7.02k
_               (ReadLEB_u32 (& index, & i_bytes, i_end));
1197
7.01k
_               (Read_utf8 (& name, & i_bytes, i_end));
1198
1199
6.98k
                if (index < io_module->numFunctions)
1200
3.61k
                {
1201
3.61k
                    IM3Function func = &(io_module->functions [index]);
1202
3.61k
                    if (func->numNames == 0)
1203
3.26k
                    {
1204
3.26k
                        func->names[0] = name;        m3log (parse, "    naming function%5d:  %s", index, name);
1205
3.26k
                        func->numNames = 1;
1206
3.26k
                        name = NULL; // transfer ownership
1207
3.26k
                    }
1208
//                          else m3log (parse, "prenamed: %s", io_module->functions [index].name);
1209
3.61k
                }
1210
1211
6.98k
                m3_Free (name);
1212
6.98k
            }
1213
3.90k
        }
1214
1215
5.18k
        i_bytes = start + payloadLength;
1216
5.18k
    }
1217
1218
3.75k
    _catch: return result;
1219
3.75k
}
1220
1221
1222
M3Result  ParseSection_Custom  (M3Module * io_module, bytes_t i_bytes, cbytes_t i_end)
1223
4.72k
{
1224
4.72k
    M3Result result = m3Err_none;
1225
1226
4.72k
    cstr_t name = NULL;
1227
4.72k
_   (Read_utf8 (& name, & i_bytes, i_end));
1228
4.71k
                                                                                    m3log (parse, "** Custom: '%s'", name);
1229
4.71k
    if (strcmp (name, "name") == 0) {
1230
3.75k
_       (ParseSection_Name(io_module, i_bytes, i_end));
1231
3.71k
    } else if (io_module->environment->customSectionHandler) {
1232
0
_       (io_module->environment->customSectionHandler(io_module, name, i_bytes, i_end));
1233
0
    }
1234
1235
    // the section name is freed on the error path too: a malformed "name"
1236
    // section, or a handler that rejects the payload, still throws past here
1237
4.72k
    _catch:
1238
1239
4.72k
    m3_Free (name);
1240
1241
4.72k
    return result;
1242
4.71k
}
1243
1244
1245
M3Result  ParseModuleSection  (M3Module * o_module, u8 i_sectionType, bytes_t i_bytes, u32 i_numBytes)
1246
15.8k
{
1247
15.8k
    M3Result result = m3Err_none;
1248
1249
15.8k
    typedef M3Result (* M3Parser) (M3Module *, bytes_t, cbytes_t);
1250
1251
15.8k
    static M3Parser s_parsers [] =
1252
15.8k
    {
1253
15.8k
        ParseSection_Custom,    // 0
1254
15.8k
        ParseSection_Type,      // 1
1255
15.8k
        ParseSection_Import,    // 2
1256
15.8k
        ParseSection_Function,  // 3
1257
15.8k
        ParseType_Table,        // 4
1258
15.8k
        ParseSection_Memory,    // 5
1259
15.8k
        ParseSection_Global,    // 6
1260
15.8k
        ParseSection_Export,    // 7
1261
15.8k
        ParseSection_Start,     // 8
1262
15.8k
        ParseSection_Element,   // 9
1263
15.8k
        ParseSection_Code,      // 10
1264
15.8k
        ParseSection_Data,      // 11
1265
15.8k
        ParseSection_DataCount, // 12
1266
15.8k
#if d_m3HasExceptionHandling
1267
15.8k
        ParseSection_Tag,       // 13
1268
15.8k
#endif
1269
15.8k
    };
1270
1271
15.8k
    M3Parser parser = NULL;
1272
1273
15.8k
    if (i_sectionType < M3_COUNT_OF (s_parsers))
1274
15.8k
        parser = s_parsers [i_sectionType];
1275
1276
15.8k
    if (parser)
1277
15.8k
    {
1278
15.8k
        cbytes_t end = i_bytes + i_numBytes;
1279
15.8k
        result = parser (o_module, i_bytes, end);
1280
15.8k
    }
1281
0
    else
1282
0
    {
1283
0
        m3log (parse, " skipped section type: %d", (u32) i_sectionType);
1284
0
    }
1285
1286
15.8k
    return result;
1287
15.8k
}
1288
1289
1290
M3Result  m3_ParseModule  (IM3Environment i_environment, IM3Module * o_module, cbytes_t i_bytes, u32 i_numBytes)
1291
3.67k
{
1292
3.67k
    IM3Module module;                                                               m3log (parse, "load module: %d bytes", i_numBytes);
1293
3.67k
_try {
1294
3.67k
    module = m3_AllocStruct (M3Module);
1295
3.67k
    _throwifnull (module);
1296
3.67k
    module->name = ".unnamed";                                                      m3log (parse, "load module: %d bytes", i_numBytes);
1297
3.67k
    module->startFunction = -1;
1298
    //module->hasWasmCodeCopy = false;
1299
3.67k
    module->environment = i_environment;
1300
1301
3.67k
    const u8 * pos = i_bytes;
1302
3.67k
    const u8 * end = pos + i_numBytes;
1303
1304
3.67k
    module->wasmStart = pos;
1305
3.67k
    module->wasmEnd = end;
1306
1307
3.67k
    u32 magic, version;
1308
3.67k
_   (Read_u32 (& magic, & pos, end));
1309
3.67k
_   (Read_u32 (& version, & pos, end));
1310
1311
3.67k
    _throwif (m3Err_wasmMalformed, magic != 0x6d736100);
1312
3.67k
    _throwif (m3Err_incompatibleWasmVersion, version != 1);
1313
1314
3.67k
#if d_m3HasExceptionHandling
1315
    // the tag section sits between memory (5) and global (6)
1316
3.67k
    static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 13, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder
1317
#else
1318
    static const u8 sectionsOrder[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 10, 11, 0 }; // 0 is a placeholder
1319
#endif
1320
3.67k
    u8 expectedSection = 0;
1321
1322
19.2k
    while (pos < end)
1323
15.9k
    {
1324
15.9k
        u8 section;
1325
15.9k
_       (ReadLEB_u7 (& section, & pos, end));
1326
1327
15.9k
        if (section != 0) {
1328
            // Ensure sections appear only once and in order
1329
40.8k
            while (sectionsOrder[expectedSection++] != section) {
1330
29.7k
                _throwif(m3Err_misorderedWasmSection, expectedSection >= M3_COUNT_OF (sectionsOrder) - 1);
1331
29.7k
            }
1332
11.1k
        }
1333
1334
15.8k
        u32 sectionLength;
1335
15.8k
_       (ReadLEB_u32 (& sectionLength, & pos, end));
1336
15.8k
        _throwif(m3Err_wasmMalformed, pos + sectionLength > end);
1337
1338
15.8k
_       (ParseModuleSection (module, section, pos, sectionLength));
1339
1340
15.5k
        pos += sectionLength;
1341
15.5k
    }
1342
1343
    // Spec: if a function section exists, a code section must also exist with
1344
    // matching count (and vice versa). ParseSection_Code checks the other
1345
    // direction; this covers the case where the code section is missing entirely.
1346
3.33k
    if (module->numFunctions > module->numFuncImports)
1347
3.25k
    {
1348
3.25k
        IM3Function firstNonImport = & module->functions [module->numFuncImports];
1349
3.25k
        _throwif (m3Err_wasmMalformed, firstNonImport->wasm == NULL);
1350
3.24k
    }
1351
1352
    // Spec: the data count section must agree with the data section, which may be absent
1353
3.32k
    _throwif (m3Err_wasmMalformed, module->hasDataCount and module->dataCount != module->numDataSegments);
1354
1355
3.67k
} _catch:
1356
1357
3.67k
    if (result)
1358
355
    {
1359
355
        m3_FreeModule (module);
1360
355
        module = NULL;
1361
355
    }
1362
1363
3.67k
    * o_module = module;
1364
1365
3.67k
    return result;
1366
3.32k
}