Coverage Report

Created: 2026-10-17 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_module.c
Line
Count
Source
1
//
2
//  m3_module.c
3
//
4
//  Created by Steven Massey on 5/7/19.
5
//  Copyright © 2019 Steven Massey. All rights reserved.
6
//
7
8
#include "m3_env.h"
9
#include "m3_exception.h"
10
11
12
void Module_FreeFunctions (IM3Module i_module)
13
33.5k
{
14
65.3k
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
15
31.7k
        IM3Function func = &i_module->functions[i];
16
31.7k
        Function_Release(func);
17
31.7k
    }
18
33.5k
}
19
20
21
void m3_FreeModule (IM3Module i_module)
22
33.5k
{
23
33.5k
    if (i_module) {
24
33.5k
        m3log(module, "freeing module: %s (funcs: %d; segments: %d)",
25
33.5k
              i_module->name, i_module->numFunctions, i_module->numDataSegments);
26
27
33.5k
        Module_FreeFunctions(i_module);
28
29
33.5k
        m3_Free(i_module->functions);
30
        //m3_Free (i_module->imports);
31
33.5k
        m3_Free(i_module->funcTypes);
32
33.5k
        m3_Free(i_module->dataSegments);
33
34
35.7k
        for (u32 i = 0; i < i_module->numTables; ++i) {
35
2.18k
            IM3Table table = i_module->tables[i];
36
37
            // a slot may point at a table another module owns; that module
38
            // frees it
39
2.18k
            if (not table or table->owner != i_module) {
40
0
                continue;
41
0
            }
42
43
2.18k
            if (table->elements and i_module->runtime) {
44
831
                i_module->runtime->tableElementsUsed -= table->size;
45
831
            }
46
2.18k
            m3_Free(table->elements);
47
2.18k
            FreeImportInfo(&table->import);
48
2.18k
            m3_Free(table);
49
2.18k
        }
50
33.5k
        m3_Free(i_module->tables);
51
52
221M
        for (u32 i = 0; i < i_module->numElementSegments; ++i) {
53
221M
            m3_Free(i_module->elementSegments[i].resolved);
54
221M
        }
55
33.5k
        m3_Free(i_module->elementSegments);
56
33.5k
        m3_Free(i_module->declaredFuncs);
57
58
35.0k
        for (u32 i = 0; i < i_module->numGlobals; ++i) {
59
1.49k
            FreeImportInfo(&(i_module->globals[i].import));
60
1.49k
        }
61
33.5k
        m3_Free(i_module->globals);
62
63
33.5k
#if d_m3HasExceptionHandling || d_m3HasStackSwitching
64
34.4k
        for (u32 i = 0; i < i_module->numTags; ++i) {
65
914
            FreeImportInfo(&(i_module->tags[i].import));
66
914
        }
67
33.5k
        m3_Free(i_module->tags);
68
33.5k
#endif
69
70
42.2k
        for (u32 i = 0; i < i_module->numMemories; ++i) {
71
8.63k
            IM3Memory memory = i_module->memories[i];
72
73
            // a slot may point at a memory another module owns; that module
74
            // frees it
75
8.63k
            if (not memory or memory->owner != i_module) {
76
0
                continue;
77
0
            }
78
79
8.63k
            FreeMemoryBlock(memory);
80
8.63k
            FreeImportInfo(&memory->import);
81
8.63k
            m3_Free(memory);
82
8.63k
        }
83
33.5k
        m3_Free(i_module->memories);
84
33.5k
        FreeMemoryBlock(&i_module->emptyMemory);
85
86
34.1k
        for (u32 i = 0; i < i_module->numExports; ++i) {
87
569
            m3_Free(i_module->exports[i].name);
88
569
        }
89
33.5k
        m3_Free(i_module->exports);
90
91
33.5k
#if d_m3HasSnapshots
92
33.5k
        M3EmbeddedSnapshot* snap = i_module->snapshots;
93
34.3k
        while (snap) {
94
818
            M3EmbeddedSnapshot* next = snap->next;
95
818
            m3_Free(snap->name);
96
818
            m3_Free(snap);
97
818
            snap = next;
98
818
        }
99
33.5k
#endif
100
101
33.5k
        m3_Free(i_module);
102
33.5k
    }
103
33.5k
}
104
105
106
// Supplies the value of an imported global. Unlike m3_SetGlobal this ignores
107
// mutability: the import's mutability governs what the wasm code may do with it,
108
// not whether the host may provide it in the first place.
109
M3Result m3_LinkGlobal (IM3Module            io_module,
110
                        const char* const    i_moduleName,
111
                        const char* const    i_globalName,
112
                        const IM3TaggedValue i_value)
113
0
{
114
0
    M3Result result = m3Err_globalLookupFailed;
115
116
0
    for (u32 i = 0; i < io_module->numGlobals; ++i) {
117
0
        IM3Global g = &io_module->globals[i];
118
119
0
        if (not (g->import.moduleUtf8 and g->import.fieldUtf8)) {
120
0
            continue;
121
0
        }
122
123
0
        if (strcmp(g->import.moduleUtf8, i_moduleName) != 0 or
124
0
            strcmp(g->import.fieldUtf8, i_globalName) != 0) {
125
0
            continue;
126
0
        }
127
128
0
        if (g->type != i_value->type) {
129
0
            return m3Err_globalTypeMismatch;
130
0
        }
131
132
0
        switch (i_value->type) {
133
0
        case c_m3Type_i32: g->i32Value = i_value->value.i32; break;
134
0
        case c_m3Type_i64: g->i64Value = i_value->value.i64; break;
135
0
#if d_m3HasFloat
136
0
        case c_m3Type_f32: g->f32Value = i_value->value.f32; break;
137
0
        case c_m3Type_f64: g->f64Value = i_value->value.f64; break;
138
0
#endif
139
0
        default: return m3Err_invalidTypeId;
140
0
        }
141
142
0
        result = m3Err_none;
143
0
    }
144
145
0
    return result;
146
0
}
147
148
149
// The function count is final by the time anything can declare a reference:
150
// imports and the function section both precede the global, export and element
151
// sections, and those in turn precede the code section.
152
M3Result Module_DeclareFunction (IM3Module io_module, u32 i_index)
153
480
{
154
480
    M3Result result = m3Err_none;
155
156
480
    if (i_index >= io_module->numFunctions) {
157
0
        return "function index out of range";
158
0
    }
159
160
480
    if (not io_module->declaredFuncs) {
161
95
        io_module->declaredFuncs = m3_AllocArray(u8, (io_module->numFunctions + 7) / 8);
162
95
        _throwifnull(io_module->declaredFuncs);
163
95
    }
164
165
480
    io_module->declaredFuncs[i_index / 8] |= (u8)(1u << (i_index % 8));
166
167
480
    _catch: return result;
168
480
}
169
170
171
bool Module_IsFunctionDeclared (IM3Module i_module, u32 i_index)
172
231
{
173
231
    if (not i_module->declaredFuncs or i_index >= i_module->numFunctions) {
174
1
        return false;
175
1
    }
176
177
230
    return (i_module->declaredFuncs[i_index / 8] & (1u << (i_index % 8))) != 0;
178
231
}
179
180
181
// Appends an entry to the module's memory index space. The M3Memory is
182
// allocated separately from the index so that another module importing it can
183
// hold the same pointer.
184
M3Result Module_AddMemory (IM3Module io_module, IM3Memory* o_memory, const M3MemoryInfo* i_info, bool i_isImported)
185
8.63k
{
186
8.63k
    IM3Memory memory = NULL;
187
8.63k
_try {
188
8.63k
    u32 index = io_module->numMemories;
189
190
8.63k
    memory = m3_AllocStruct(M3Memory);
191
8.63k
    _throwifnull(memory);
192
193
8.63k
    io_module->memories = m3_ReallocArray(IM3Memory, io_module->memories, index + 1, index);
194
8.63k
    _throwifnull(io_module->memories);
195
196
8.63k
    memory->owner      = io_module;
197
8.63k
    memory->imported   = i_isImported;
198
8.63k
    memory->initPages  = i_info->initPages;
199
8.63k
    memory->maxPages   = i_info->maxPages;
200
8.63k
    memory->pageSize   = i_info->pageSize;
201
8.63k
    memory->hasMax     = i_info->hasMax;
202
8.63k
    memory->isMemory64 = i_info->isMemory64;
203
8.63k
    memory->isShared   = i_info->isShared;
204
205
8.63k
    io_module->memories[index] = memory;
206
8.63k
    io_module->numMemories     = index + 1;
207
208
8.63k
    if (o_memory) {
209
730
        *o_memory = memory;
210
730
    }
211
212
8.63k
    return result;
213
214
8.63k
} _catch:
215
0
    m3_Free(memory);
216
0
    return result;
217
8.63k
}
218
219
220
// Appends an entry to the module's table index space. Allocated separately from
221
// the index, like a memory, so another module importing it can hold the same
222
// pointer.
223
M3Result Module_AddTable (IM3Module io_module, IM3Table* o_table, const M3TableInfo* i_info, bool i_isImported)
224
2.18k
{
225
2.18k
    IM3Table table = NULL;
226
2.18k
_try {
227
2.18k
    u32 index = io_module->numTables;
228
229
2.18k
    table = m3_AllocStruct(M3Table);
230
2.18k
    _throwifnull(table);
231
232
2.18k
    io_module->tables = m3_ReallocArray(IM3Table, io_module->tables, index + 1, index);
233
2.18k
    _throwifnull(io_module->tables);
234
235
2.18k
    table->owner     = io_module;
236
2.18k
    table->imported  = i_isImported;
237
2.18k
    table->type      = i_info->elemType;
238
2.18k
    table->size      = i_info->initSize;
239
2.18k
    table->initSize  = i_info->initSize;
240
2.18k
    table->maxSize   = i_info->maxSize;
241
2.18k
    table->hasMax    = i_info->hasMax;
242
2.18k
    table->isTable64 = i_info->isTable64;
243
244
2.18k
    io_module->tables[index] = table;
245
2.18k
    io_module->numTables     = index + 1;
246
247
2.18k
    if (o_table) {
248
981
        *o_table = table;
249
981
    }
250
251
2.18k
    return result;
252
253
2.18k
} _catch:
254
0
    m3_Free(table);
255
0
    return result;
256
2.18k
}
257
258
259
#if d_m3HasExceptionHandling || d_m3HasStackSwitching
260
261
M3Result Module_AddTag (IM3Module io_module, IM3Tag* o_tag, IM3FuncType i_type, bool i_isImported)
262
914
{
263
914
_try {
264
914
    u32 index = io_module->numTags++;
265
266
914
    io_module->tags = m3_ReallocArray(M3Tag, io_module->tags, io_module->numTags, index);
267
914
    _throwifnull(io_module->tags);
268
914
    M3Tag* tag = &io_module->tags[index];
269
270
914
    tag->type     = i_type;
271
914
    tag->imported = i_isImported;
272
914
    tag->resolved = NULL;
273
914
    memset(&tag->import, 0, sizeof(tag->import));
274
275
914
    if (o_tag) {
276
415
        *o_tag = tag;
277
415
    }
278
279
914
} _catch:
280
914
    return result;
281
914
}
282
283
#endif // d_m3HasExceptionHandling || d_m3HasStackSwitching
284
285
286
#if d_m3HasStackSwitching
287
288
// The continuation type a spelled-out reference names. Reference types carry
289
// the canonical index, and the module's own type space is where a name for it
290
// can be found again - which switch needs, because the type of the peer it
291
// suspends into is only written down as the last parameter of the type it
292
// switches to.
293
IM3FuncType Module_ContTypeOfRef (IM3Module i_module, m3type_t i_refType)
294
0
{
295
0
    if (not IsSpelledRefType(i_refType)) {
296
0
        return NULL;
297
0
    }
298
299
0
    u32 heap = HeapTypeOf(i_refType);
300
301
0
    if (heap == d_m3Type_heapAbstract) {
302
0
        return NULL;
303
0
    }
304
305
0
    for (u32 i = 0; i < i_module->numFuncTypes; ++i) {
306
0
        IM3FuncType ftype = i_module->funcTypes[i];
307
308
0
        if (ftype and ftype->isContinuation and ftype->canonicalIndex == heap) {
309
0
            return ftype;
310
0
        }
311
0
    }
312
313
0
    return NULL;
314
0
}
315
316
#endif // d_m3HasStackSwitching
317
318
319
M3Result Module_AddGlobal (IM3Module io_module, IM3Global* o_global, m3type_t i_type, bool i_mutable, bool i_isImported)
320
1.49k
{
321
1.49k
_try {
322
1.49k
    u32 index = io_module->numGlobals++;
323
324
1.49k
    io_module->globals = m3_ReallocArray(M3Global, io_module->globals, io_module->numGlobals, index);
325
1.49k
    _throwifnull(io_module->globals);
326
1.49k
    M3Global* global = &io_module->globals[index];
327
328
1.49k
    global->type      = i_type;
329
1.49k
    global->imported  = i_isImported;
330
1.49k
    global->isMutable = i_mutable;
331
332
1.49k
    if (o_global) {
333
1.49k
        *o_global = global;
334
1.49k
    }
335
336
1.49k
} _catch:
337
1.49k
    return result;
338
1.49k
}
339
340
M3Result Module_PreallocFunctions (IM3Module io_module, u32 i_totalFunctions)
341
60.5k
{
342
60.5k
_try {
343
60.5k
    if (i_totalFunctions > io_module->allFunctions) {
344
28.7k
        io_module->functions    = m3_ReallocArray(M3Function, io_module->functions, i_totalFunctions, io_module->allFunctions);
345
28.7k
        io_module->allFunctions = i_totalFunctions;
346
28.7k
        _throwifnull(io_module->functions);
347
28.7k
    }
348
60.5k
} _catch:
349
60.5k
    return result;
350
60.5k
}
351
352
M3Result Module_AddFunction (IM3Module io_module, u32 i_typeIndex, IM3ImportInfo i_importInfo)
353
31.7k
{
354
31.7k
_try {
355
356
31.7k
    u32 index = io_module->numFunctions++;
357
31.7k
_   (Module_PreallocFunctions(io_module, io_module->numFunctions));
358
359
31.7k
    _throwif("type sig index out of bounds", i_typeIndex >= io_module->numFuncTypes);
360
361
31.6k
    IM3FuncType ft = io_module->funcTypes[i_typeIndex];
362
363
31.6k
    IM3Function func = Module_GetFunction(io_module, index);
364
31.6k
    func->funcType   = ft;
365
31.6k
    func->module     = io_module;       // an import has no body, but still belongs to this module
366
367
#ifdef DEBUG
368
    func->index = index;
369
#endif
370
371
31.6k
    if (i_importInfo and func->numNames == 0) {
372
3.41k
        func->import   = *i_importInfo;
373
3.41k
        func->names[0] = i_importInfo->fieldUtf8;
374
3.41k
        func->numNames = 1;
375
3.41k
    }
376
377
31.6k
    m3log(module, "   added function: %3d; sig: %d", index, i_typeIndex);
378
379
31.7k
} _catch:
380
31.7k
    return result;
381
31.6k
}
382
383
#ifdef DEBUG
384
void Module_GenerateNames (IM3Module i_module)
385
{
386
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
387
        IM3Function func = &i_module->functions[i];
388
389
        if (func->numNames == 0) {
390
            char* buff = m3_AllocArray(char, 16);
391
            snprintf(buff, 16, "$func%d", i);
392
            func->names[0] = buff;
393
            func->numNames = 1;
394
        }
395
    }
396
}
397
#endif
398
399
IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex)
400
77.1k
{
401
77.1k
    IM3Function func = NULL;
402
403
77.1k
    if (i_functionIndex < i_module->numFunctions) {
404
77.1k
        func = &i_module->functions[i_functionIndex];
405
        //func->module = i_module;
406
77.1k
    }
407
408
77.1k
    return func;
409
77.1k
}
410
411
412
const char* m3_GetModuleName (IM3Module i_module)
413
0
{
414
0
    if (!i_module || !i_module->name) {
415
0
        return ".unnamed";
416
0
    }
417
418
0
    return i_module->name;
419
0
}
420
421
void m3_SetModuleName (IM3Module i_module, const char* name)
422
0
{
423
0
    if (i_module) {
424
0
        i_module->name = name;
425
0
    }
426
0
}
427
428
IM3Runtime m3_GetModuleRuntime (IM3Module i_module)
429
0
{
430
0
    return i_module ? i_module->runtime : NULL;
431
0
}