Coverage Report

Created: 2026-09-04 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm3/source/m3_env.c
Line
Count
Source
1
//
2
//  m3_env.c
3
//
4
//  Created by Steven Massey on 4/19/19.
5
//  Copyright © 2019 Steven Massey. All rights reserved.
6
//
7
8
#include <stdarg.h>
9
#include <limits.h>
10
#include <errno.h>
11
#include <float.h>
12
#include <ctype.h>
13
14
#include "m3_env.h"
15
#include "m3_compile.h"
16
#include "m3_exception.h"
17
#include "m3_info.h"
18
19
20
IM3Environment m3_NewEnvironment ()
21
1.89k
{
22
1.89k
    IM3Environment env = m3_AllocStruct(M3Environment);
23
24
1.89k
    if (env) {
25
1.89k
        _try
26
1.89k
        {
27
            // create FuncTypes for all simple block return ValueTypes.
28
            // v128 is skipped: it parses as a slot but has no operations.
29
18.9k
            for (u8 t = c_m3Type_none; t < c_m3Type_count; t++) {
30
17.0k
                if (t == c_m3Type_v128) {
31
1.89k
                    continue;
32
1.89k
                }
33
34
15.1k
                IM3FuncType ftype;
35
15.1k
_               (AllocFuncType(&ftype, 1));
36
37
15.1k
                ftype->numArgs  = 0;
38
15.1k
                ftype->numRets  = (t == c_m3Type_none) ? 0 : 1;
39
15.1k
                ftype->types[0] = t;
40
41
15.1k
                Environment_AddFuncType(env, &ftype);
42
43
15.1k
                env->retFuncTypes[t] = ftype;
44
15.1k
            }
45
1.89k
        }
46
47
1.89k
        _catch:
48
1.89k
        if (result) {
49
0
            m3_FreeEnvironment(env);
50
0
            env = NULL;
51
0
        }
52
1.89k
    }
53
54
1.89k
    return env;
55
1.89k
}
56
57
58
void Environment_Release (IM3Environment i_environment)
59
1.89k
{
60
1.89k
    IM3FuncType ftype = i_environment->funcTypes;
61
62
17.7k
    while (ftype) {
63
15.8k
        IM3FuncType next = ftype->next;
64
15.8k
        m3_Free(ftype);
65
15.8k
        ftype = next;
66
15.8k
    }
67
68
1.89k
    m3log(runtime, "freeing %d pages from environment", CountCodePages(i_environment->pagesReleased));
69
1.89k
    FreeCodePages(&i_environment->pagesReleased);
70
1.89k
}
71
72
73
void m3_FreeEnvironment (IM3Environment i_environment)
74
1.89k
{
75
1.89k
    if (i_environment) {
76
1.89k
        Environment_Release(i_environment);
77
1.89k
        m3_Free(i_environment);
78
1.89k
    }
79
1.89k
}
80
81
82
void m3_SetCustomSectionHandler (IM3Environment i_environment, M3SectionHandler i_handler)
83
0
{
84
0
    if (i_environment) {
85
0
        i_environment->customSectionHandler = i_handler;
86
0
    }
87
0
}
88
89
90
// returns the same io_funcType or replaces it with an equivalent that's already in the type linked list
91
M3Result Environment_AddFuncType (IM3Environment i_environment, IM3FuncType* io_funcType)
92
17.2k
{
93
17.2k
    IM3FuncType addType = *io_funcType;
94
17.2k
    IM3FuncType newType = i_environment->funcTypes;
95
96
86.0k
    while (newType) {
97
70.1k
        if (AreFuncTypesEqual(newType, addType)) {
98
1.44k
            m3_Free(addType);
99
1.44k
            break;
100
1.44k
        }
101
102
68.7k
        newType = newType->next;
103
68.7k
    }
104
105
17.2k
    if (newType == NULL) {
106
        // a type index has to fit in the heap type field of an m3type_t
107
15.8k
        if (i_environment->numFuncTypes >= d_m3MaxSaneTypesCount) {
108
0
            m3_Free(addType);
109
0
            *io_funcType = NULL;
110
0
            return "too many distinct function types";
111
0
        }
112
113
15.8k
        newType                  = addType;
114
15.8k
        newType->canonicalIndex  = i_environment->numFuncTypes++;
115
15.8k
        newType->next            = i_environment->funcTypes;
116
15.8k
        i_environment->funcTypes = newType;
117
15.8k
    }
118
119
17.2k
    *io_funcType = newType;
120
121
17.2k
    return m3Err_none;
122
17.2k
}
123
124
125
IM3CodePage RemoveCodePageOfCapacity (M3CodePage** io_list, u32 i_minimumLineCount)
126
2.39k
{
127
2.39k
    IM3CodePage prev = NULL;
128
2.39k
    IM3CodePage page = *io_list;
129
130
2.39k
    while (page) {
131
125
        if (NumFreeLines(page) >= i_minimumLineCount) {             d_m3Assert (page->info.usageCount == 0);
132
125
            IM3CodePage next = page->info.next;
133
125
            if (prev) {
134
0
                prev->info.next = next; // mid-list
135
125
            } else {
136
125
                *io_list = next;       // front of list
137
125
            }
138
139
125
            break;
140
125
        }
141
142
0
        prev = page;
143
0
        page = page->info.next;
144
0
    }
145
146
2.39k
    return page;
147
2.39k
}
148
149
150
IM3CodePage Environment_AcquireCodePage (IM3Environment i_environment, u32 i_minimumLineCount)
151
1.16k
{
152
1.16k
    return RemoveCodePageOfCapacity(&i_environment->pagesReleased, i_minimumLineCount);
153
1.16k
}
154
155
156
void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i_codePageList)
157
4.04k
{
158
4.04k
    IM3CodePage end = i_codePageList;
159
160
4.15k
    while (end) {
161
1.16k
        end->info.lineIndex = 0; // reset page
162
#if d_m3RecordBacktraces
163
        end->info.mapping->size = 0;
164
#endif // d_m3RecordBacktraces
165
166
1.16k
        IM3CodePage next = end->info.next;
167
1.16k
        if (not next) {
168
1.06k
            break;
169
1.06k
        }
170
171
106
        end = next;
172
106
    }
173
174
4.04k
    if (end) {
175
        // push list to front
176
1.06k
        end->info.next               = i_environment->pagesReleased;
177
1.06k
        i_environment->pagesReleased = i_codePageList;
178
1.06k
    }
179
4.04k
}
180
181
182
IM3Runtime m3_NewRuntime (IM3Environment i_environment, u32 i_stackSizeInBytes, void* i_userdata)
183
1.89k
{
184
1.89k
    IM3Runtime runtime = m3_AllocStruct(M3Runtime);
185
186
1.89k
    if (runtime) {
187
1.89k
        m3_ResetErrorInfo(runtime);
188
189
1.89k
        runtime->environment = i_environment;
190
1.89k
        runtime->userdata    = i_userdata;
191
192
1.89k
        runtime->originStack = m3_Malloc("Wasm Stack", i_stackSizeInBytes + 4 * sizeof(m3slot_t)); // TODO: more precise stack checks
193
194
1.89k
        if (runtime->originStack) {
195
1.89k
            runtime->stack         = runtime->originStack;
196
1.89k
            runtime->numStackSlots = i_stackSizeInBytes / sizeof(m3slot_t);          m3log (runtime, "new stack: %p, slots: %u", runtime->originStack, runtime->numStackSlots);
197
1.89k
        } else {
198
0
            m3_Free(runtime);
199
0
        }
200
1.89k
    }
201
202
1.89k
    return runtime;
203
1.89k
}
204
205
void m3_SetValidation (IM3Runtime i_runtime, bool i_enable)
206
0
{
207
0
#if d_m3EnableValidation
208
0
    if (i_runtime) {
209
0
        i_runtime->skipValidation = not i_enable;
210
0
    }
211
#else
212
    (void)i_runtime;
213
    (void)i_enable;              // nothing to skip: the validator was compiled out
214
#endif
215
0
}
216
217
void m3_SetGasLimit (IM3Runtime i_runtime, double i_gas)
218
1.89k
{
219
1.89k
#if d_m3HasGasMetering
220
1.89k
    if (i_runtime) {
221
        // A budget bigger than the counter can hold is the same as no ceiling
222
        // worth speaking of, so it saturates rather than wrapping
223
1.89k
        const double maxGas = (double)INT64_MAX / d_m3GasUnitsPerGas;
224
225
1.89k
        i64 units;
226
1.89k
        if (i_gas >= maxGas) {
227
0
            units = INT64_MAX;
228
1.89k
        } else if (i_gas > 0) {
229
1.89k
            units = (i64)(i_gas * d_m3GasUnitsPerGas);
230
1.89k
        } else {
231
0
            units = 0;
232
0
        }
233
234
1.89k
        i_runtime->gasLimit = i_runtime->gasRemaining = units;
235
1.89k
    }
236
#else
237
    (void)i_runtime;
238
    (void)i_gas;                 // nothing to meter: the instrumentation was compiled out
239
#endif
240
1.89k
}
241
242
double m3_GetGasLimit (IM3Runtime i_runtime)
243
0
{
244
0
#if d_m3HasGasMetering
245
0
    if (i_runtime) {
246
0
        return (double)i_runtime->gasLimit / d_m3GasUnitsPerGas;
247
0
    }
248
#else
249
    (void)i_runtime;
250
#endif
251
0
    return 0;
252
0
}
253
254
double m3_GetGasUsed (IM3Runtime i_runtime)
255
0
{
256
0
#if d_m3HasGasMetering
257
0
    if (i_runtime) {
258
0
        return (double)(i_runtime->gasLimit - i_runtime->gasRemaining) / d_m3GasUnitsPerGas;
259
0
    }
260
#else
261
    (void)i_runtime;
262
#endif
263
0
    return 0;
264
0
}
265
266
void* m3_GetUserData (IM3Runtime i_runtime)
267
0
{
268
0
    return i_runtime ? i_runtime->userdata : NULL;
269
0
}
270
271
272
void* ForEachModule (IM3Runtime i_runtime, ModuleVisitor i_visitor, void* i_info)
273
5.61k
{
274
5.61k
    void* r = NULL;
275
276
5.61k
    IM3Module module = i_runtime->modules;
277
278
8.86k
    while (module) {
279
4.77k
        IM3Module next = module->next;
280
4.77k
        if ((r = i_visitor(module, i_info))) {
281
1.51k
            break;
282
1.51k
        }
283
284
3.25k
        module = next;
285
3.25k
    }
286
287
5.61k
    return r;
288
5.61k
}
289
290
291
void* _FreeModule (IM3Module i_module, void* i_info)
292
1.60k
{
293
1.60k
    m3_FreeModule(i_module);
294
1.60k
    return NULL;
295
1.60k
}
296
297
298
// A module's memory and table index spaces hold borrowed pointers: linking
299
// repoints an imported slot at the memory or table the exporting module owns.
300
// m3_FreeModule tells a borrowed slot from an owned one by reading that owner,
301
// which only holds while the object is still allocated - and the free walk
302
// below visits modules in load order, with no ordering between an owner and
303
// the modules borrowing from it. So clear the borrowed slots first, in a pass
304
// that frees nothing; the walk then skips them on the NULL check it already
305
// does, instead of reading a memory some earlier module has freed.
306
void* _ReleaseBorrowedSlots (IM3Module i_module, void* i_info)
307
1.60k
{
308
2.37k
    for (u32 i = 0; i < i_module->numMemories; ++i) {
309
774
        IM3Memory memory = i_module->memories[i];
310
311
774
        if (memory and memory->owner != i_module) {
312
0
            i_module->memories[i] = NULL;
313
0
        }
314
774
    }
315
316
1.97k
    for (u32 i = 0; i < i_module->numTables; ++i) {
317
372
        IM3Table table = i_module->tables[i];
318
319
372
        if (table and table->owner != i_module) {
320
0
            i_module->tables[i] = NULL;
321
0
        }
322
372
    }
323
324
1.60k
    return NULL;
325
1.60k
}
326
327
328
void Runtime_Release (IM3Runtime i_runtime)
329
2.02k
{
330
2.02k
    ForEachModule(i_runtime, _ReleaseBorrowedSlots, NULL);
331
2.02k
    ForEachModule(i_runtime, _FreeModule, NULL);                    d_m3Assert (i_runtime->numActiveCodePages == 0);
332
333
2.02k
    Environment_ReleaseCodePages(i_runtime->environment, i_runtime->pagesOpen);
334
2.02k
    Environment_ReleaseCodePages(i_runtime->environment, i_runtime->pagesFull);
335
336
2.02k
    m3_Free(i_runtime->originStack);
337
338
2.02k
#if d_m3EnableValidation
339
2.02k
    m3_Free(i_runtime->validator);
340
2.02k
#endif
341
2.02k
}
342
343
344
void m3_FreeRuntime (IM3Runtime i_runtime)
345
1.89k
{
346
1.89k
    if (i_runtime) {
347
1.89k
        m3_PrintProfilerInfo();
348
349
1.89k
        Runtime_Release(i_runtime);
350
1.89k
        m3_Free(i_runtime);
351
1.89k
    }
352
1.89k
}
353
354
M3Result EvaluateExpression (IM3Module i_module, void* o_expressed, m3type_t i_type, bytes_t* io_bytes, cbytes_t i_end)
355
125
{
356
125
    M3Result result = m3Err_none;
357
358
    // OPTZ: use a simplified interpreter for expressions
359
360
    // create a temporary runtime context
361
#if defined(d_m3PreferStaticAlloc)
362
    static M3Runtime runtime;
363
#else
364
125
    M3Runtime runtime;
365
125
#endif
366
125
    M3_INIT(runtime);
367
368
125
    runtime.environment   = i_module->runtime->environment;
369
125
    runtime.numStackSlots = i_module->runtime->numStackSlots;
370
125
    runtime.stack         = i_module->runtime->stack;
371
372
125
    m3stack_t stack = (m3stack_t)runtime.stack;
373
374
125
    IM3Runtime savedRuntime = i_module->runtime;
375
125
    i_module->runtime       = &runtime;
376
377
125
    IM3Compilation o   = &runtime.compilation;
378
125
    o->runtime         = &runtime;
379
125
    o->module          = i_module;
380
125
    o->wasm            = *io_bytes;
381
125
    o->wasmEnd         = i_end;
382
125
    o->lastOpcodeStart = o->wasm;
383
384
    // Borrowed for the length of this expression only. Runtime_Release below hands it
385
    // to Environment_ReleaseCodePages, which resets lineIndex, so the page comes back
386
    // empty and the next expression reuses it rather than burning fresh lines.
387
125
    o->page = AcquireCodePage(&runtime);
388
389
125
    if (o->page) {
390
125
        IM3FuncType ftype = runtime.environment->retFuncTypes[BaseTypeOf(i_type)];
391
392
125
        pc_t m3code = GetPagePC(o->page);
393
125
        result      = CompileExpression(o, ftype);
394
395
125
        if (not result && o->maxStackSlots >= runtime.numStackSlots) {
396
3
            result = m3Err_trapStackOverflow;
397
3
        }
398
399
125
        if (not result) {
400
#if (d_m3EnableOpProfiling || d_m3EnableOpTracing)
401
            m3ret_t r = RunCode(m3code, stack, NULL, d_m3OpDefaultArgs, d_m3BaseCstr);
402
#else
403
91
            m3ret_t r = RunCode(m3code, stack, NULL, d_m3OpDefaultArgs);
404
91
#endif
405
406
91
            if (r == 0) {                                                                   m3log (runtime, "expression result: %s", SPrintValue (stack, i_type));
407
91
                if (SizeOfType(BaseTypeOf(i_type)) == sizeof(u32)) {
408
1
                    *(u32*)o_expressed = *((u32*)stack);
409
90
                } else {
410
90
                    *(u64*)o_expressed = *((u64*)stack);
411
90
                }
412
91
            }
413
91
        }
414
415
125
        ReleaseCodePage(&runtime, o->page);
416
125
    } else {
417
0
        result = m3Err_mallocFailedCodePage;
418
0
    }
419
420
125
    runtime.originStack = NULL;        // prevent free(stack) in ReleaseRuntime
421
125
    Runtime_Release(&runtime);
422
125
    i_module->runtime = savedRuntime;
423
424
125
    *io_bytes = o->wasm;
425
426
125
    return result;
427
125
}
428
429
430
//---------------------------------------------------------------------------------------------------------------------------------
431
//  Linking a module's imports against the exports of the modules already loaded
432
//  into the same runtime, matched on the name a module was registered under.
433
//
434
//  This is best-effort: an import nothing satisfies is left alone rather than
435
//  rejected, because a host function may still be bound to it after the module
436
//  is loaded (m3_LinkRawFunction needs the runtime, so it cannot run earlier),
437
//  and because an unsatisfiable memory or global still has to be backed by
438
//  something for the module to be loadable at all.
439
//---------------------------------------------------------------------------------------------------------------------------------
440
441
// Whether an exporting memory or table satisfies what an import asks for. The
442
// exporter's *current* size is its minimum - one that has been grown satisfies
443
// a larger import than its declaration would - and it may be no less bounded.
444
static
445
bool LimitsSatisfy (u64 i_exportedSize, bool i_exportedHasMax, u64 i_exportedMax,
446
                    u64 i_importMin, bool i_importHasMax, u64 i_importMax)
447
0
{
448
0
    if (i_exportedSize < i_importMin) {
449
0
        return false;
450
0
    }
451
452
0
    if (i_importHasMax and (not i_exportedHasMax or i_exportedMax > i_importMax)) {
453
0
        return false;
454
0
    }
455
456
0
    return true;
457
0
}
458
459
460
static
461
IM3Function Module_FindExportedFunction (IM3Module i_module, cstr_t i_name)
462
0
{
463
0
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
464
0
        IM3Function f = &i_module->functions[i];
465
466
0
        if (f->export_name and strcmp(f->export_name, i_name) == 0) {
467
            // A module that re-exports an import names the placeholder here.
468
            // Resolve to the function that actually runs, or a host-side call
469
            // would run it against the importing module's memory.
470
0
            return Function_Implementation(f);
471
0
        }
472
0
    }
473
474
0
    return NULL;
475
0
}
476
477
478
static
479
IM3Memory Module_FindExportedMemory (IM3Module i_module, cstr_t i_name)
480
0
{
481
0
    for (u32 i = 0; i < i_module->numMemories; ++i) {
482
0
        IM3Memory memory = i_module->memories[i];
483
484
0
        if (memory->exportName and strcmp(memory->exportName, i_name) == 0) {
485
0
            return memory;
486
0
        }
487
0
    }
488
489
0
    return NULL;
490
0
}
491
492
493
static
494
IM3Table Module_FindExportedTable (IM3Module i_module, cstr_t i_name)
495
0
{
496
0
    for (u32 i = 0; i < i_module->numTables; ++i) {
497
0
        IM3Table table = i_module->tables[i];
498
499
0
        if (table->exportName and strcmp(table->exportName, i_name) == 0) {
500
0
            return table;
501
0
        }
502
0
    }
503
504
0
    return NULL;
505
0
}
506
507
508
static
509
IM3Global Module_FindExportedGlobal (IM3Module i_module, cstr_t i_name)
510
0
{
511
0
    for (u32 i = 0; i < i_module->numGlobals; ++i) {
512
0
        IM3Global g = &i_module->globals[i];
513
514
0
        if (g->name and strcmp(g->name, i_name) == 0) {
515
0
            return g;
516
0
        }
517
0
    }
518
519
0
    return NULL;
520
0
}
521
522
523
// Whether the module exports anything at all under this name. Export names are
524
// unique within a module, so a name one of the lookups above missed but this
525
// one finds is exported as something else - a kind the import cannot be
526
// satisfied by, rather than a name the module never exported.
527
static
528
bool Module_HasExport (IM3Module i_module, cstr_t i_name)
529
0
{
530
0
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
531
0
        IM3Function f = &i_module->functions[i];
532
533
0
        if (f->export_name and strcmp(f->export_name, i_name) == 0) {
534
0
            return true;
535
0
        }
536
0
    }
537
538
0
    for (u32 i = 0; i < i_module->numMemories; ++i) {
539
0
        IM3Memory memory = i_module->memories[i];
540
541
0
        if (memory->exportName and strcmp(memory->exportName, i_name) == 0) {
542
0
            return true;
543
0
        }
544
0
    }
545
546
0
    for (u32 i = 0; i < i_module->numTables; ++i) {
547
0
        IM3Table table = i_module->tables[i];
548
549
0
        if (table->exportName and strcmp(table->exportName, i_name) == 0) {
550
0
            return true;
551
0
        }
552
0
    }
553
554
0
    for (u32 i = 0; i < i_module->numGlobals; ++i) {
555
0
        IM3Global g = &i_module->globals[i];
556
557
0
        if (g->name and strcmp(g->name, i_name) == 0) {
558
0
            return true;
559
0
        }
560
0
    }
561
562
0
#if d_m3HasExceptionHandling
563
0
    for (u32 i = 0; i < i_module->numTags; ++i) {
564
0
        IM3Tag tag = &i_module->tags[i];
565
566
0
        if (tag->name and strcmp(tag->name, i_name) == 0) {
567
0
            return true;
568
0
        }
569
0
    }
570
0
#endif
571
572
0
    return false;
573
0
}
574
575
576
// Points each of the module's imports at whatever already-loaded module exports
577
// it. Runs before anything is allocated or initialized: a memory import has to
578
// be resolved before InitMemory would give it pages of its own, and a global
579
// import before InitGlobals runs an initializer that reads it.
580
static
581
M3Result LinkImports (IM3Runtime io_runtime, IM3Module io_module)
582
1.60k
{
583
1.60k
    M3Result result = m3Err_none;
584
585
4.39k
    for (u32 i = 0; i < io_module->numFunctions; ++i) {
586
2.78k
        IM3Function f = &io_module->functions[i];
587
588
2.78k
        if (f->wasm or not (f->import.moduleUtf8 and f->import.fieldUtf8)) {
589
1.52k
            continue;
590
1.52k
        }
591
592
1.26k
        IM3Module from = m3_FindModule(io_runtime, f->import.moduleUtf8);
593
1.26k
        if (not from) {
594
1.26k
            continue;
595
1.26k
        }
596
597
0
        IM3Function exported = Module_FindExportedFunction(from, f->import.fieldUtf8);
598
0
        if (not exported) {
599
            // the import names a module that is loaded, so its exports settle the
600
            // question: a name it exports as something else is a type mismatch, and
601
            // one it does not export at all is an unknown import
602
0
            _throwif(m3Err_incompatibleImportType, Module_HasExport(from, f->import.fieldUtf8));
603
0
            _throw(m3Err_unknownImport);
604
0
        }
605
606
        // func types are canonical within an environment, so this is the
607
        // structural equivalence the spec asks for
608
0
        _throwif(m3Err_incompatibleImportType, exported->funcType != f->funcType);
609
610
0
        f->resolved = Function_Implementation(exported);
611
0
    }
612
613
2.37k
    for (u32 i = 0; i < io_module->numMemories; ++i) {
614
774
        IM3Memory memory = io_module->memories[i];
615
616
774
        if (not memory->imported or memory->owner != io_module) {
617
756
            continue;
618
756
        }
619
620
18
        IM3Module from = m3_FindModule(io_runtime, memory->import.moduleUtf8);
621
18
        if (not from) {
622
18
            continue;
623
18
        }
624
625
0
        IM3Memory exported = Module_FindExportedMemory(from, memory->import.fieldUtf8);
626
0
        if (not exported) {
627
0
            _throwif(m3Err_incompatibleImportType, Module_HasExport(from, memory->import.fieldUtf8));
628
0
            _throw(m3Err_unknownImport);
629
0
        }
630
631
        // the address type is part of the memory type, so an i64 memory does
632
        // not satisfy an i32 import, or the other way round - and so is the page
633
        // size, which custom page sizes made a declared property of a memory
634
0
        _throwif(m3Err_incompatibleImportType, exported->isMemory64 != memory->isMemory64);
635
0
        _throwif(m3Err_incompatibleImportType, Memory_PageSize(exported) != Memory_PageSize(memory));
636
637
0
        _throwif(m3Err_incompatibleImportType,
638
0
                 not LimitsSatisfy(exported->numPages, exported->hasMax, exported->maxPages,
639
0
                                   memory->initPages, memory->hasMax, memory->maxPages));
640
641
        // hand the slot over to the exporter's memory, and drop the placeholder
642
0
        m3_Free(memory->mallocated);
643
0
        m3_Free(memory->exportName);
644
0
        FreeImportInfo(&memory->import);
645
0
        m3_Free(memory);
646
647
0
        io_module->memories[i] = exported;
648
0
    }
649
650
1.97k
    for (u32 i = 0; i < io_module->numTables; ++i) {
651
372
        IM3Table table = io_module->tables[i];
652
653
372
        if (not table->imported or table->owner != io_module) {
654
298
            continue;
655
298
        }
656
657
74
        IM3Module from = m3_FindModule(io_runtime, table->import.moduleUtf8);
658
74
        if (not from) {
659
74
            continue;
660
74
        }
661
662
0
        IM3Table exported = Module_FindExportedTable(from, table->import.fieldUtf8);
663
0
        if (not exported) {
664
0
            _throwif(m3Err_incompatibleImportType, Module_HasExport(from, table->import.fieldUtf8));
665
0
            _throw(m3Err_unknownImport);
666
0
        }
667
668
0
        _throwif(m3Err_incompatibleImportType, exported->type != table->type);
669
670
        // the index type is part of the table type, the same way it is for a memory
671
0
        _throwif(m3Err_incompatibleImportType, exported->isTable64 != table->isTable64);
672
673
0
        _throwif(m3Err_incompatibleImportType,
674
0
                 not LimitsSatisfy(exported->size, exported->hasMax, exported->maxSize,
675
0
                                   table->initSize, table->hasMax, table->maxSize));
676
677
        // hand the slot over to the exporter's table, and drop the placeholder
678
0
        m3_Free(table->elements);
679
0
        m3_Free(table->exportName);
680
0
        FreeImportInfo(&table->import);
681
0
        m3_Free(table);
682
683
0
        io_module->tables[i] = exported;
684
0
    }
685
686
1.73k
    for (u32 i = 0; i < io_module->numGlobals; ++i) {
687
133
        IM3Global g = &io_module->globals[i];
688
689
133
        if (not g->imported or not (g->import.moduleUtf8 and g->import.fieldUtf8)) {
690
126
            continue;
691
126
        }
692
693
7
        IM3Module from = m3_FindModule(io_runtime, g->import.moduleUtf8);
694
7
        if (not from) {
695
7
            continue;
696
7
        }
697
698
0
        IM3Global exported = Module_FindExportedGlobal(from, g->import.fieldUtf8);
699
0
        if (not exported) {
700
0
            _throwif(m3Err_incompatibleImportType, Module_HasExport(from, g->import.fieldUtf8));
701
0
            _throw(m3Err_unknownImport);
702
0
        }
703
704
0
        _throwif(m3Err_incompatibleImportType, exported->type != g->type);
705
0
        _throwif(m3Err_incompatibleImportType, exported->isMutable != g->isMutable);
706
707
0
        g->resolved = exported->resolved ? exported->resolved : exported;
708
0
    }
709
710
1.60k
    _catch: return result;
711
1.60k
}
712
713
714
// Backs each of the module's memories with pages. LinkImports has already
715
// pointed any import it could satisfy at the exporting module's memory, and
716
// those are skipped here. An import nothing satisfied is still backed locally
717
// from its own declared limits, so that the module remains loadable.
718
M3Result InitMemory (IM3Runtime io_runtime, IM3Module i_module)
719
1.60k
{
720
1.60k
    M3Result result = m3Err_none;
721
722
    // Fixed from here on: the index space stops changing after parse, and
723
    // linking has already repointed any slot it was going to.
724
1.60k
    i_module->memory0 = i_module->numMemories ? i_module->memories[0]
725
1.60k
                                              : &i_module->emptyMemory;
726
727
1.60k
    if (i_module->numMemories == 0) {
728
        // nothing addressable, but _mem still has to point somewhere
729
869
        i_module->emptyMemory.owner    = i_module;
730
869
        i_module->emptyMemory.pageSize = d_m3DefaultMemPageSize;
731
732
869
_       (ResizeMemory(io_runtime, &i_module->emptyMemory, 0));
733
869
    }
734
735
2.37k
    for (u32 i = 0; i < i_module->numMemories; ++i) {
736
774
        IM3Memory memory = i_module->memories[i];
737
738
        // a slot that already points at another module's memory is that
739
        // module's to allocate
740
774
        if (memory->owner != i_module or memory->mallocated) {
741
0
            continue;
742
0
        }
743
744
774
        u32 pageSize = Memory_PageSize(memory);
745
746
774
        memory->pageSize = pageSize;
747
748
        // Without a declared maximum a memory may grow to the spec limit of
749
        // 2^|addrtype|/pagesize pages, which is the usual 65536 at the default
750
        // page size, 2^48 for a 64-bit memory, and a whole address space of
751
        // them when a page is a single byte. A declared maximum of zero is a
752
        // real limit, not the absence of one.
753
        //
754
        // 2^64/pagesize overflows a u64 only when a page is a single byte, and
755
        // a power-of-two page size divides the address space exactly, so the
756
        // 64-bit division below is 2^64/pagesize written so that it fits.
757
774
        if (not memory->hasMax) {
758
483
            memory->maxPages = memory->isMemory64
759
483
                                 ? (pageSize > 1 ? (UINT64_MAX / pageSize) + 1 : UINT64_MAX)
760
483
                                 : (0x100000000ull / pageSize);
761
483
        }
762
763
774
_       (ResizeMemory(io_runtime, memory, memory->initPages));
764
774
    }
765
766
1.60k
    _catch: return result;
767
1.60k
}
768
769
770
M3Result ResizeMemory (IM3Runtime io_runtime, IM3Memory memory, u64 i_numPages)
771
1.65k
{
772
1.65k
    M3Result result = m3Err_none;
773
774
1.65k
    u64 numPagesToAlloc = i_numPages;
775
776
1.65k
    if (numPagesToAlloc <= memory->maxPages) {
777
        // A 64-bit memory may ask for up to 2^48 pages, which overflows a u64
778
        // of bytes. Nothing that large can be backed, so refuse it up front
779
        // rather than multiplying into a wrapped size.
780
1.64k
        _throwif("linear memory limitation exceeded",
781
1.64k
                 numPagesToAlloc > d_m3AddressLimit / memory->pageSize);
782
783
1.64k
        u64 numPageBytes = numPagesToAlloc * memory->pageSize;
784
785
1.64k
#if d_m3MaxLinearMemoryPages > 0
786
        // the limit is a memory size, counted in default-sized pages; comparing
787
        // it against a raw page count would make it 65536 times stricter for a
788
        // module whose pages are one byte
789
1.64k
        _throwif("linear memory limitation exceeded",
790
1.64k
                 numPageBytes > (u64)d_m3MaxLinearMemoryPages * d_m3DefaultMemPageSize);
791
1.64k
#endif
792
793
        // Limit the amount of memory that gets actually allocated
794
1.64k
        if (io_runtime->memoryLimit) {
795
1.64k
            numPageBytes = M3_MIN(numPageBytes, (u64)io_runtime->memoryLimit);
796
1.64k
        }
797
798
1.64k
        _throwif("linear memory limitation exceeded", numPageBytes > (u64)SIZE_MAX - sizeof(M3MemoryHeader));
799
800
1.64k
        size_t numBytes = (size_t)numPageBytes + sizeof(M3MemoryHeader);
801
802
1.64k
        size_t numPreviousBytes = (size_t)memory->numPages * memory->pageSize;
803
1.64k
        if (numPreviousBytes) {
804
6
            numPreviousBytes += sizeof(M3MemoryHeader);
805
6
        }
806
807
1.64k
        void* newMem = m3_Realloc("Wasm Linear Memory", memory->mallocated, numBytes, numPreviousBytes);
808
1.64k
        _throwifnull(newMem);
809
810
1.64k
        memory->mallocated = (M3MemoryHeader*)newMem;
811
812
#if d_m3LogRuntime
813
        M3MemoryHeader* oldMallocated = memory->mallocated;
814
#endif
815
816
1.64k
        memory->numPages = numPagesToAlloc;
817
818
1.64k
        memory->mallocated->length  = (size_t)numPageBytes;
819
1.64k
        memory->mallocated->runtime = io_runtime;
820
1.64k
        memory->mallocated->memory  = memory;
821
822
1.64k
        memory->mallocated->maxStack = (m3slot_t*)io_runtime->stack + io_runtime->numStackSlots;
823
824
1.64k
        m3log(runtime, "resized old: %p; mem: %p; length: %zu; pages: %llu", oldMallocated, memory->mallocated, memory->mallocated->length, (unsigned long long)memory->numPages);
825
1.64k
    } else {
826
1
        result = m3Err_wasmMemoryOverflow;
827
1
    }
828
829
1.65k
    _catch: return result;
830
1.65k
}
831
832
833
M3Result InitGlobals (IM3Module io_module)
834
1.60k
{
835
1.60k
    M3Result result = m3Err_none;
836
837
1.60k
    if (io_module->numGlobals) {
838
        // placing the globals in their structs isn't good for cache locality, but i don't really know what the global
839
        // access patterns typically look like yet.
840
841
        //          io_module->globalMemory = m3Alloc (m3reg_t, io_module->numGlobals);
842
843
        //          if (io_module->globalMemory)
844
132
        {
845
230
            for (u32 i = 0; i < io_module->numGlobals; ++i) {
846
132
                M3Global* g = &io_module->globals[i];                           m3log (runtime, "initializing global: %d", i);
847
848
132
                if (g->initExpr) {
849
125
                    bytes_t start = g->initExpr;
850
851
125
                    result = EvaluateExpression(io_module, &g->i64Value, g->type, &start, g->initExpr + g->initExprSize);
852
853
125
                    if (not result) {
854
                        // io_module->globalMemory [i] = initValue;
855
91
                    } else {
856
34
                        break;
857
34
                    }
858
125
                } else {                                                        m3log (runtime, "importing global");
859
7
                }
860
132
            }
861
132
        }
862
        //          else result = ErrorModule (m3Err_mallocFailed, io_module, "could allocate globals for module: '%s", io_module->name);
863
132
    }
864
865
1.60k
    return result;
866
1.60k
}
867
868
869
M3Result InitDataSegments (IM3Module io_module)
870
1.56k
{
871
1.56k
    M3Result result = m3Err_none;
872
873
1.56k
    for (u32 i = 0; i < io_module->numDataSegments; ++i) {
874
0
        M3DataSegment* segment = &io_module->dataSegments[i];
875
876
        // A passive segment stays available for memory.init until data.drop.
877
        // An active one is copied here and then counts as dropped.
878
0
        if (segment->isPassive) {
879
0
            continue;
880
0
        }
881
882
0
        _throwif("data segment memory index out of range",
883
0
                 segment->memoryRegion >= io_module->numMemories);
884
885
0
        IM3Memory io_memory = io_module->memories[segment->memoryRegion];
886
887
0
        _throwif("unallocated linear memory", !(io_memory->mallocated));
888
889
        // The offset expression has the memory's address type, and is
890
        // unsigned: an i32 offset of -1 is 4294967295, way out of bounds
891
        // rather than negative.
892
0
        u64     segmentOffset = 0;
893
0
        bytes_t start         = segment->initExpr;
894
895
0
        if (io_memory->isMemory64) {
896
0
_           (EvaluateExpression(io_module, &segmentOffset, c_m3Type_i64, &start, segment->initExpr + segment->initExprSize));
897
0
        } else {
898
0
            u32 offset32;
899
0
_           (EvaluateExpression(io_module, &offset32, c_m3Type_i32, &start, segment->initExpr + segment->initExprSize));
900
0
            segmentOffset = offset32;
901
0
        }
902
903
0
        m3log(runtime, "loading data segment: %d; size: %d; offset: %llu", i, segment->size, (unsigned long long)segmentOffset);
904
905
0
        if (segmentOffset <= io_memory->mallocated->length &&
906
0
            (u64)segment->size <= io_memory->mallocated->length - segmentOffset) {
907
0
            u8* dest = m3MemData(io_memory->mallocated) + segmentOffset;
908
0
            memcpy(dest, segment->data, segment->size);
909
0
        } else {
910
0
            _throw("data segment out of bounds");
911
0
        }
912
913
0
        segment->dropped = true;
914
0
    }
915
916
1.56k
    _catch: return result;
917
1.56k
}
918
919
920
// Turns a segment's elements into references. Element expressions are constant
921
// expressions restricted to ref.null/ref.func, so they're read directly rather
922
// than run through the compiler.
923
static
924
M3Result ResolveElements (IM3Module io_module, M3ElementSegment* i_segment, void** o_elements)
925
0
{
926
0
    M3Result result = m3Err_none;
927
928
0
    bytes_t  pos = i_segment->elements;
929
0
    cbytes_t end = io_module->elementSectionEnd;
930
931
0
    for (u32 e = 0; e < i_segment->numElements; ++e) {
932
0
        u32   funcIndex;
933
0
        void* ref = NULL;
934
935
0
        if (i_segment->isExpr) {
936
0
            m3opcode_t opcode;
937
0
_           (Read_opcode(&opcode, &pos, end));
938
939
0
            if (opcode == c_waOp_refFunc) {
940
0
_               (ReadLEB_u32(&funcIndex, &pos, end));
941
0
                _throwif("function index out of range", funcIndex >= io_module->numFunctions);
942
0
                ref = Function_Implementation(&io_module->functions[funcIndex]);
943
0
            } else if (opcode == c_waOp_refNull) {
944
0
                i8 waType;
945
0
                u8 nullType;
946
0
_               (ReadLEB_i7(&waType, &pos, end));
947
0
_               (NormalizeType(&nullType, waType));
948
0
                _throwif(m3Err_typeMismatch, nullType != i_segment->type);
949
0
            } else if (opcode == c_waOp_getGlobal) {
950
                // wasm 2.0 lets an element expression read an imported
951
                // immutable global, which is how one module seeds another's
952
                // table with a reference it exported.
953
0
                u32 globalIndex;
954
0
_               (ReadLEB_u32(&globalIndex, &pos, end));
955
0
                _throwif(m3Err_globaIndexOutOfBounds, globalIndex >= io_module->numGlobals);
956
957
0
                IM3Global global = &io_module->globals[globalIndex];
958
959
0
                _throwif(m3Err_globaIndexOutOfBounds, not global->imported);
960
0
                _throwif(m3Err_wasmMalformed, global->isMutable);
961
0
                _throwif(m3Err_typeMismatch, BaseTypeOf(global->type) != BaseTypeOf(i_segment->type));
962
963
                // read the cell the import was linked to, not the placeholder
964
0
                if (global->resolved) {
965
0
                    global = global->resolved;
966
0
                }
967
968
0
                ref = global->refValue;
969
0
            } else {
970
0
                _throw("constant expression required");
971
0
            }
972
973
0
_           (Read_opcode(&opcode, &pos, end));
974
0
            _throwif(m3Err_wasmMalformed, opcode != c_waOp_end);
975
0
        } else {
976
0
_           (ReadLEB_u32(&funcIndex, &pos, end));
977
0
            _throwif("function index out of range", funcIndex >= io_module->numFunctions);
978
0
            ref = Function_Implementation(&io_module->functions[funcIndex]);
979
0
        }
980
981
0
        o_elements[e] = ref;
982
0
    }
983
984
0
    _catch: return result;
985
0
}
986
987
988
M3Result InitTableAndElements (IM3Module io_module)
989
1.56k
{
990
1.56k
    M3Result result = m3Err_none;
991
992
1.56k
    cbytes_t end = io_module->elementSectionEnd;
993
1.56k
    M3Table* table;
994
995
1.94k
    for (u32 i = 0; i < io_module->numTables; ++i) {
996
372
        table = io_module->tables[i];
997
998
        // a slot pointing at another module's table is that module's to fill
999
372
        if (table->owner != io_module) {
1000
0
            continue;
1001
0
        }
1002
1003
372
        if (table->size) {
1004
277
            table->elements = m3_AllocArray(void*, table->size);
1005
277
            _throwifnull(table->elements);
1006
1007
277
            if (table->initExpr) {
1008
0
                void*   value = NULL;
1009
0
                bytes_t start = table->initExpr;
1010
0
_               (EvaluateExpression(io_module, &value, BaseTypeOf(table->type),
1011
0
                                     &start, table->initExpr + table->initExprSize));
1012
1013
0
                for (u32 e = 0; e < table->size; ++e) {
1014
0
                    table->elements[e] = value;
1015
0
                }
1016
0
            }
1017
277
        }
1018
372
    }
1019
1020
1.56k
    for (u32 i = 0; i < io_module->numElementSegments; ++i) {
1021
0
        M3ElementSegment* segment = &io_module->elementSegments[i];
1022
1023
        // Declarative segments only make their functions referenceable, and
1024
        // passive ones wait for table.init, so neither is written out here.
1025
0
        if (segment->mode == c_m3Elem_declarative) {
1026
0
            segment->dropped = true;
1027
0
            continue;
1028
0
        }
1029
1030
0
        if (segment->mode == c_m3Elem_passive) {
1031
0
            if (segment->numElements) {
1032
0
                segment->resolved = m3_AllocArray(void*, segment->numElements);
1033
0
                _throwifnull(segment->resolved);
1034
0
_               (ResolveElements(io_module, segment, segment->resolved));
1035
0
            }
1036
0
            continue;
1037
0
        }
1038
1039
0
        table = io_module->tables[segment->tableIndex];
1040
1041
        // The offset expression has the table's index type, and is unsigned:
1042
        // an i32 offset of -1 is 4294967295, out of bounds rather than negative.
1043
0
        u64     offset = 0;
1044
0
        bytes_t expr   = segment->initExpr;
1045
1046
0
        if (table->isTable64) {
1047
0
_           (EvaluateExpression(io_module, &offset, c_m3Type_i64, &expr, end));
1048
0
        } else {
1049
0
            u32 offset32;
1050
0
_           (EvaluateExpression(io_module, &offset32, c_m3Type_i32, &expr, end));
1051
0
            offset = offset32;
1052
0
        }
1053
1054
0
        _throwif("out of bounds table access",
1055
0
                 offset > table->size or segment->numElements > table->size - offset);
1056
1057
0
_       (ResolveElements(io_module, segment, table->elements + offset));
1058
1059
0
        segment->dropped = true;
1060
0
    }
1061
1062
1.56k
    _catch: return result;
1063
1.56k
}
1064
1065
M3Result m3_CompileModule (IM3Module io_module)
1066
0
{
1067
0
    M3Result result = m3Err_none;
1068
1069
0
    for (u32 i = 0; i < io_module->numFunctions; ++i) {
1070
0
        IM3Function f = &io_module->functions[i];
1071
0
        if (f->wasm and not f->compiled) {
1072
0
_           (CompileFunction(f));
1073
0
        }
1074
0
    }
1075
1076
0
    _catch: return result;
1077
0
}
1078
1079
#if d_m3HasExceptionHandling
1080
1081
M3Exception* NewException (IM3Runtime io_runtime, IM3Tag i_tag, u32 i_numArgs)
1082
2
{
1083
2
    M3Exception* exception = (M3Exception*)m3_Malloc("M3Exception", sizeof(M3Exception) + i_numArgs * sizeof(u64));
1084
1085
2
    if (exception) {
1086
2
        exception->tag     = i_tag;
1087
2
        exception->numArgs = i_numArgs;
1088
2
        exception->reified = false;
1089
2
        exception->prev    = NULL;
1090
2
        exception->next    = io_runtime->exceptions;
1091
1092
2
        if (exception->next) {
1093
0
            exception->next->prev = exception;
1094
0
        }
1095
1096
2
        io_runtime->exceptions = exception;
1097
2
    }
1098
1099
2
    return exception;
1100
2
}
1101
1102
1103
// Releases one exception ahead of the rest. The caller has to know nothing can
1104
// still name it: no exnref was ever taken of it, and its payload has already
1105
// been copied out.
1106
void FreeException (IM3Runtime io_runtime, M3Exception* i_exception)
1107
0
{
1108
0
    if (i_exception->prev) {
1109
0
        i_exception->prev->next = i_exception->next;
1110
0
    } else {
1111
0
        io_runtime->exceptions = i_exception->next;
1112
0
    }
1113
1114
0
    if (i_exception->next) {
1115
0
        i_exception->next->prev = i_exception->prev;
1116
0
    }
1117
1118
0
    if (io_runtime->pendingException == i_exception) {
1119
0
        io_runtime->pendingException = NULL;
1120
0
    }
1121
1122
0
    m3_Free_Impl(i_exception);
1123
0
}
1124
1125
1126
// Releases every exception the runtime still holds. Only safe once the Wasm
1127
// stack is empty, which is why the outermost RunCodeChecked() is the one that
1128
// calls it.
1129
void FreeExceptions (IM3Runtime io_runtime)
1130
448
{
1131
448
    M3Exception* exception = io_runtime->exceptions;
1132
1133
448
    io_runtime->exceptions       = NULL;
1134
448
    io_runtime->pendingException = NULL;
1135
1136
450
    while (exception) {
1137
2
        M3Exception* next = exception->next;
1138
2
        m3_Free_Impl(exception);
1139
2
        exception = next;
1140
2
    }
1141
448
}
1142
1143
#endif // d_m3HasExceptionHandling
1144
1145
1146
// Run compiled code on the runtime's stack, bounding native recursion for the
1147
// duration of the call. The outermost invocation establishes the stack limit;
1148
// nested ones (an imported function calling back into Wasm) inherit it.
1149
static inline
1150
M3Result RunCodeChecked (IM3Runtime i_runtime, IM3Function i_function)
1151
448
{
1152
448
    pc_t i_pc = i_function->compiled;
1153
1154
    // execution runs against the memory of the module the entry point belongs
1155
    // to, not against some runtime-wide one
1156
448
    M3MemoryHeader* _mem = Module_MemoryHeader(i_function->module);
1157
1158
448
    d_m3StackLimitEnter(i_runtime);
1159
448
#if d_m3HasExceptionHandling
1160
    // handler stacks don't nest across a call boundary: a host function calling
1161
    // back into Wasm cannot be caught by a try_table its own caller entered
1162
448
    u32 savedTryDepth   = i_runtime->tryDepth;
1163
448
    i_runtime->tryDepth = 0;
1164
448
    i_runtime->exceptionNesting++;
1165
448
#endif
1166
#if (d_m3EnableOpProfiling || d_m3EnableOpTracing)
1167
    M3Result result = (M3Result)RunCode(i_pc, (m3stack_t)i_runtime->stack, _mem, d_m3OpDefaultArgs, d_m3BaseCstr);
1168
#else
1169
448
    M3Result result = (M3Result)RunCode(i_pc, (m3stack_t)i_runtime->stack, _mem, d_m3OpDefaultArgs);
1170
448
#endif
1171
448
#if d_m3HasExceptionHandling
1172
448
    i_runtime->tryDepth = savedTryDepth;
1173
1174
    // an exception that reached the bottom of the call stack found no handler
1175
448
    if (M3_UNLIKELY(result == m3Err_pendingException)) {
1176
2
        result = m3Err_trapUncaughtException;
1177
2
    }
1178
1179
448
    if (--i_runtime->exceptionNesting == 0) {
1180
448
        FreeExceptions(i_runtime);
1181
448
    }
1182
448
#endif
1183
448
    d_m3StackLimitLeave(i_runtime);
1184
1185
448
    return result;
1186
448
}
1187
1188
M3Result m3_RunStart (IM3Module io_module)
1189
0
{
1190
0
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
1191
    // Execution disabled for fuzzing builds
1192
0
    return m3Err_none;
1193
0
#endif
1194
1195
0
    M3Result result           = m3Err_none;
1196
0
    i32      startFunctionTmp = -1;
1197
1198
0
    if (io_module and io_module->startFunction >= 0) {
1199
0
        IM3Function function = &io_module->functions[io_module->startFunction];
1200
1201
0
        if (not function->compiled) {
1202
0
_           (CompileFunction(function));
1203
0
        }
1204
1205
0
        IM3FuncType ftype = function->funcType;
1206
0
        if (ftype->numArgs != 0 || ftype->numRets != 0) {
1207
0
            _throw(m3Err_argumentCountMismatch);
1208
0
        }
1209
1210
0
        IM3Module  module  = function->module;
1211
0
        IM3Runtime runtime = module->runtime;
1212
1213
0
        startFunctionTmp = io_module->startFunction;
1214
1215
0
        io_module->startFunction = -1;
1216
1217
0
        result = RunCodeChecked(runtime, function);
1218
1219
0
        if (result) {
1220
0
            io_module->startFunction = startFunctionTmp;
1221
0
            EXCEPTION_PRINT(result);
1222
0
            goto _catch;
1223
0
        }
1224
0
    }
1225
1226
0
    _catch: return result;
1227
0
}
1228
1229
// TODO: deal with main + side-modules loading efforcement
1230
M3Result m3_LoadModule (IM3Runtime io_runtime, IM3Module io_module)
1231
1.60k
{
1232
1.60k
    M3Result result = m3Err_none;
1233
1234
1.60k
    if (M3_UNLIKELY(io_module->runtime)) {
1235
0
        return m3Err_moduleAlreadyLinked;
1236
0
    }
1237
1238
1.60k
    io_module->runtime = io_runtime;
1239
1240
    // linking first: a memory import has to be resolved before InitMemory would
1241
    // give it pages of its own, and a global import before an initializer reads it
1242
1.60k
_   (LinkImports(io_runtime, io_module));
1243
1244
1.60k
_   (InitMemory(io_runtime, io_module));
1245
1.60k
_   (InitGlobals(io_module));
1246
    // Spec order: element segments are applied before data segments. It matters
1247
    // when one of them traps - whatever ran before the trap stays done.
1248
1.56k
_   (InitTableAndElements(io_module));
1249
1.56k
_   (InitDataSegments(io_module));
1250
1251
    // Start func might use imported functions, which are not liked here yet,
1252
    // so it will be called before a function call is attempted (in m3_FindFunction)
1253
1254
#ifdef DEBUG
1255
    Module_GenerateNames(io_module);
1256
#endif
1257
1258
1.56k
    io_module->next     = io_runtime->modules;
1259
1.56k
    io_runtime->modules = io_module;
1260
1.56k
    return result; // ok
1261
1262
34
_catch:
1263
    // The runtime owns the module either way. Instantiation may already have
1264
    // written this module's functions into a table another module owns, and the
1265
    // spec keeps whatever it managed to do before the trap, so those entries
1266
    // stay callable - retaining the module is what stops them dangling.
1267
    //
1268
    // It goes on the tail of the list rather than the head: it is not a module
1269
    // anyone should find by name, only one whose functions may still be
1270
    // reachable through someone else's table.
1271
34
    io_module->next = NULL;
1272
1273
34
    IM3Module* tail = &io_runtime->modules;
1274
34
    while (*tail) {
1275
0
        tail = &(*tail)->next;
1276
0
    }
1277
34
    *tail = io_module;
1278
1279
34
    return result;
1280
1.56k
}
1281
1282
IM3Global m3_FindGlobal (IM3Module         io_module,
1283
                         const char* const i_globalName)
1284
0
{
1285
    // Search exports
1286
0
    for (u32 i = 0; i < io_module->numGlobals; ++i) {
1287
0
        IM3Global g = &io_module->globals[i];
1288
0
        if (g->name and strcmp(g->name, i_globalName) == 0) {
1289
            // a re-exported global is the one that was imported, so reads and
1290
            // writes have to reach the cell that actually holds the value
1291
0
            return g->resolved ? g->resolved : g;
1292
0
        }
1293
0
    }
1294
1295
    // Search imports
1296
0
    for (u32 i = 0; i < io_module->numGlobals; ++i) {
1297
0
        IM3Global g = &io_module->globals[i];
1298
1299
0
        if (g->import.moduleUtf8 and g->import.fieldUtf8) {
1300
0
            if (strcmp(g->import.fieldUtf8, i_globalName) == 0) {
1301
0
                return g->resolved ? g->resolved : g;
1302
0
            }
1303
0
        }
1304
0
    }
1305
0
    return NULL;
1306
0
}
1307
1308
M3Result m3_GetGlobal (IM3Global      i_global,
1309
                       IM3TaggedValue o_value)
1310
0
{
1311
0
    if (not i_global) {
1312
0
        return m3Err_globalLookupFailed;
1313
0
    }
1314
1315
0
    switch (i_global->type) {
1316
0
    case c_m3Type_i32: o_value->value.i32 = i_global->i32Value; break;
1317
0
    case c_m3Type_i64: o_value->value.i64 = i_global->i64Value; break;
1318
0
#if d_m3HasFloat
1319
0
    case c_m3Type_f32: o_value->value.f32 = i_global->f32Value; break;
1320
0
    case c_m3Type_f64: o_value->value.f64 = i_global->f64Value; break;
1321
0
#endif
1322
0
    default: return m3Err_invalidTypeId;
1323
0
    }
1324
1325
0
    o_value->type = (M3ValueType)(i_global->type);
1326
0
    return m3Err_none;
1327
0
}
1328
1329
M3Result m3_SetGlobal (IM3Global            i_global,
1330
                       const IM3TaggedValue i_value)
1331
0
{
1332
0
    if (not i_global) {
1333
0
        return m3Err_globalLookupFailed;
1334
0
    }
1335
0
    if (not i_global->isMutable) {
1336
0
        return m3Err_globalNotMutable;
1337
0
    }
1338
0
    if (i_global->type != i_value->type) {
1339
0
        return m3Err_globalTypeMismatch;
1340
0
    }
1341
1342
0
    switch (i_value->type) {
1343
0
    case c_m3Type_i32: i_global->i32Value = i_value->value.i32; break;
1344
0
    case c_m3Type_i64: i_global->i64Value = i_value->value.i64; break;
1345
0
#if d_m3HasFloat
1346
0
    case c_m3Type_f32: i_global->f32Value = i_value->value.f32; break;
1347
0
    case c_m3Type_f64: i_global->f64Value = i_value->value.f64; break;
1348
0
#endif
1349
0
    default: return m3Err_invalidTypeId;
1350
0
    }
1351
1352
0
    return m3Err_none;
1353
0
}
1354
1355
M3ValueType m3_GetGlobalType (IM3Global i_global)
1356
0
{
1357
0
    return (i_global) ? (M3ValueType)(i_global->type) : c_m3Type_none;
1358
0
}
1359
1360
1361
void* v_FindFunction (IM3Module i_module, void* i_info)
1362
1.56k
{
1363
1.56k
    const char* const i_name = (const char*)i_info;
1364
1365
    // Prefer exported functions
1366
4.35k
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
1367
2.78k
        IM3Function f = &i_module->functions[i];
1368
2.78k
        if (f->export_name and strcmp(f->export_name, i_name) == 0) {
1369
            // A module that re-exports an import names the placeholder here.
1370
            // Resolve to the function that actually runs, or a host-side call
1371
            // would run it against the importing module's memory.
1372
0
            return Function_Implementation(f);
1373
0
        }
1374
2.78k
    }
1375
1376
    // Search internal functions
1377
2.83k
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
1378
2.78k
        IM3Function f = &i_module->functions[i];
1379
1380
2.78k
        bool isImported = f->import.moduleUtf8 or f->import.fieldUtf8;
1381
1382
2.78k
        if (isImported) {
1383
1.26k
            continue;
1384
1.26k
        }
1385
1386
1.53k
        for (int j = 0; j < f->numNames; j++) {
1387
1.52k
            if (f->names[j] and strcmp(f->names[j], i_name) == 0) {
1388
1.51k
                return f;
1389
1.51k
            }
1390
1.52k
        }
1391
1.52k
    }
1392
1393
51
    return NULL;
1394
1.56k
}
1395
1396
1397
// Shared tail of the two lookups: a function is only usable once it has code.
1398
static
1399
M3Result PrepareFoundFunction (IM3Function* o_function, IM3Function i_function)
1400
1.51k
{
1401
1.51k
    M3Result result = m3Err_none;
1402
1403
1.51k
    if (not i_function->compiled) {
1404
1.51k
_       (CompileFunction(i_function))
1405
1.51k
    }
1406
1407
1.51k
    _catch:
1408
1.51k
    *o_function = result ? NULL : i_function;
1409
1410
1.51k
    return result;
1411
1.51k
}
1412
1413
1414
// Searches every module in the runtime, most recently loaded first. That is a
1415
// guess once more than one module is loaded and two of them export the same
1416
// name - m3_FindFunctionIn says which module is meant.
1417
M3Result m3_FindFunction (IM3Function* o_function, IM3Runtime i_runtime, const char* const i_functionName)
1418
1.56k
{
1419
1.56k
                                                                d_m3Assert (o_function and i_runtime and i_functionName);
1420
1.56k
    IM3Function function = NULL;
1421
1422
1.56k
    if (not i_runtime->modules) {
1423
0
        *o_function = NULL;
1424
0
        return "no modules loaded";
1425
0
    }
1426
1427
1.56k
    function = (IM3Function)ForEachModule(i_runtime, v_FindFunction, (void*)i_functionName);
1428
1429
1.56k
    if (not function) {
1430
51
        *o_function = NULL;
1431
51
        return ErrorModule(m3Err_functionLookupFailed, i_runtime->modules, "'%s'", i_functionName);
1432
51
    }
1433
1434
1.51k
    return PrepareFoundFunction(o_function, function);
1435
1.56k
}
1436
1437
1438
IM3Module m3_FindModule (IM3Runtime i_runtime, const char* const i_moduleName)
1439
1.36k
{
1440
1.36k
    if (not i_runtime or not i_moduleName) {
1441
0
        return NULL;
1442
0
    }
1443
1444
    // the list is newest-first, so a name registered twice names the newer one
1445
1.36k
    for (IM3Module m = i_runtime->modules; m; m = m->next) {
1446
0
        if (m->name and strcmp(m->name, i_moduleName) == 0) {
1447
0
            return m;
1448
0
        }
1449
0
    }
1450
1451
1.36k
    return NULL;
1452
1.36k
}
1453
1454
1455
// Searches one module's exports, which is what naming a module means.
1456
M3Result m3_FindFunctionIn (IM3Function* o_function, IM3Module i_module, const char* const i_functionName)
1457
0
{
1458
0
                                                                d_m3Assert (o_function and i_functionName);
1459
0
    if (not i_module) {
1460
0
        *o_function = NULL;
1461
0
        return m3Err_functionLookupFailed;      // ErrorModule would deref it
1462
0
    }
1463
1464
0
    IM3Function function = (IM3Function)v_FindFunction(i_module, (void*)i_functionName);
1465
1466
0
    if (not function) {
1467
0
        *o_function = NULL;
1468
0
        return ErrorModule(m3Err_functionLookupFailed, i_module, "'%s'", i_functionName);
1469
0
    }
1470
1471
0
    return PrepareFoundFunction(o_function, function);
1472
0
}
1473
1474
1475
M3Result m3_GetTableFunction (IM3Function* o_function, IM3Module i_module, uint32_t i_index)
1476
0
{
1477
0
_try {
1478
0
    M3Table*    table;
1479
0
    IM3Function function;
1480
1481
0
    _throwif("no table", i_module->numTables == 0);
1482
1483
0
    table = i_module->tables[0];
1484
0
    _throwif("function index out of range", i_index >= table->size);
1485
1486
0
    function = (IM3Function)table->elements[i_index];
1487
1488
0
    if (function) {
1489
0
        if (not function->compiled) {
1490
0
_           (CompileFunction(function))
1491
0
        }
1492
0
    }
1493
1494
0
    *o_function = function;
1495
0
} _catch:
1496
0
    return result;
1497
0
}
1498
1499
1500
static
1501
M3Result checkStartFunction (IM3Module i_module)
1502
467
{
1503
467
    M3Result result = m3Err_none;                               d_m3Assert(i_module);
1504
1505
    // Check if start function needs to be called
1506
467
    if (i_module->startFunction >= 0) {
1507
0
        result = m3_RunStart(i_module);
1508
0
    }
1509
1510
467
    return result;
1511
467
}
1512
1513
uint32_t m3_GetArgCount (IM3Function i_function)
1514
0
{
1515
0
    if (i_function) {
1516
0
        IM3FuncType ft = i_function->funcType;
1517
0
        if (ft) {
1518
0
            return ft->numArgs;
1519
0
        }
1520
0
    }
1521
0
    return 0;
1522
0
}
1523
1524
uint32_t m3_GetRetCount (IM3Function i_function)
1525
0
{
1526
0
    if (i_function) {
1527
0
        IM3FuncType ft = i_function->funcType;
1528
0
        if (ft) {
1529
0
            return ft->numRets;
1530
0
        }
1531
0
    }
1532
0
    return 0;
1533
0
}
1534
1535
1536
M3ValueType m3_GetArgType (IM3Function i_function, uint32_t index)
1537
0
{
1538
0
    if (i_function) {
1539
0
        IM3FuncType ft = i_function->funcType;
1540
0
        if (ft and index < ft->numArgs) {
1541
0
            return (M3ValueType)BaseTypeOf(d_FuncArgType(ft, index));
1542
0
        }
1543
0
    }
1544
0
    return c_m3Type_none;
1545
0
}
1546
1547
M3ValueType m3_GetRetType (IM3Function i_function, uint32_t index)
1548
0
{
1549
0
    if (i_function) {
1550
0
        IM3FuncType ft = i_function->funcType;
1551
0
        if (ft and index < ft->numRets) {
1552
0
            return (M3ValueType)BaseTypeOf(d_FuncRetType(ft, index));
1553
0
        }
1554
0
    }
1555
0
    return c_m3Type_none;
1556
0
}
1557
1558
1559
u8* GetStackPointerForArgs (IM3Function i_function)
1560
467
{
1561
467
    u64*        stack = (u64*)i_function->module->runtime->stack;
1562
467
    IM3FuncType ftype = i_function->funcType;
1563
1564
467
    stack += ftype->numRets;
1565
1566
467
    return (u8*)stack;
1567
467
}
1568
1569
1570
M3Result m3_CallV (IM3Function i_function, ...)
1571
467
{
1572
467
    va_list ap;
1573
467
    va_start(ap, i_function);
1574
467
    M3Result r = m3_CallVL(i_function, ap);
1575
467
    va_end(ap);
1576
467
    return r;
1577
467
}
1578
1579
static
1580
void ReportNativeStackUsage ()
1581
448
{
1582
#if d_m3LogNativeStack
1583
    int stackUsed = m3StackGetMax();
1584
    fprintf(stderr, "Native stack used: %d\n", stackUsed);
1585
#endif
1586
448
}
1587
1588
1589
M3Result m3_CallVL (IM3Function i_function, va_list i_args)
1590
467
{
1591
467
    IM3Runtime  runtime = i_function->module->runtime;
1592
467
    IM3FuncType ftype   = i_function->funcType;
1593
467
    M3Result    result  = m3Err_none;
1594
467
    u8*         s       = NULL;
1595
1596
467
    if (!i_function->compiled) {
1597
0
        return m3Err_missingCompiledCode;
1598
0
    }
1599
1600
#if d_m3RecordBacktraces
1601
    ClearBacktrace(runtime);
1602
#endif
1603
1604
467
    m3StackCheckInit();
1605
1606
467
_   (checkStartFunction(i_function->module))
1607
1608
467
    s = GetStackPointerForArgs(i_function);
1609
1610
487
    for (u32 i = 0; i < ftype->numArgs; ++i) {
1611
39
        switch (d_FuncArgType(ftype, i)) {
1612
15
        case c_m3Type_i32:
1613
15
            *(i32*)(s) = va_arg(i_args, i32);
1614
15
            s += 8;
1615
15
            break;
1616
2
        case c_m3Type_i64:
1617
2
            *(i64*)(s) = va_arg(i_args, i64);
1618
2
            s += 8;
1619
2
            break;
1620
0
        case c_m3Type_funcref:
1621
0
        case c_m3Type_externref:
1622
0
        case c_m3Type_exnref:
1623
0
            *(uintptr_t*)(s) = va_arg(i_args, uintptr_t);
1624
0
            s += 8;
1625
0
            break;
1626
0
#if d_m3HasFloat
1627
0
        case c_m3Type_f32:
1628
0
            *(f32*)(s) = (f32)va_arg(i_args, f64);
1629
0
            s += 8;
1630
0
            break; // f32 is passed as f64
1631
3
        case c_m3Type_f64:
1632
3
            *(f64*)(s) = va_arg(i_args, f64);
1633
3
            s += 8;
1634
3
            break;
1635
0
#endif
1636
19
        default: return "unknown argument type";
1637
39
        }
1638
39
    }
1639
1640
448
    result = RunCodeChecked(runtime, i_function);
1641
448
    ReportNativeStackUsage();
1642
1643
448
    runtime->lastCalled = result ? NULL : i_function;
1644
1645
448
    _catch: return result;
1646
448
}
1647
1648
M3Result m3_Call (IM3Function i_function, uint32_t i_argc, const void* i_argptrs[])
1649
0
{
1650
0
    IM3Runtime  runtime = i_function->module->runtime;
1651
0
    IM3FuncType ftype   = i_function->funcType;
1652
0
    M3Result    result  = m3Err_none;
1653
0
    u8*         s       = NULL;
1654
1655
0
    if (i_argc != ftype->numArgs) {
1656
0
        return m3Err_argumentCountMismatch;
1657
0
    }
1658
0
    if (!i_function->compiled) {
1659
0
        return m3Err_missingCompiledCode;
1660
0
    }
1661
1662
#if d_m3RecordBacktraces
1663
    ClearBacktrace(runtime);
1664
#endif
1665
1666
0
    m3StackCheckInit();
1667
1668
0
_   (checkStartFunction(i_function->module))
1669
1670
0
    s = GetStackPointerForArgs(i_function);
1671
1672
0
    for (u32 i = 0; i < ftype->numArgs; ++i) {
1673
0
        switch (d_FuncArgType(ftype, i)) {
1674
0
        case c_m3Type_i32:
1675
0
            *(i32*)(s) = *(i32*)i_argptrs[i];
1676
0
            s += 8;
1677
0
            break;
1678
0
        case c_m3Type_i64:
1679
0
            *(i64*)(s) = *(i64*)i_argptrs[i];
1680
0
            s += 8;
1681
0
            break;
1682
0
        case c_m3Type_funcref:
1683
0
        case c_m3Type_externref:
1684
0
        case c_m3Type_exnref:
1685
0
            *(uintptr_t*)(s) = *(uintptr_t*)i_argptrs[i];
1686
0
            s += 8;
1687
0
            break;
1688
0
#if d_m3HasFloat
1689
0
        case c_m3Type_f32:
1690
0
            *(f32*)(s) = *(f32*)i_argptrs[i];
1691
0
            s += 8;
1692
0
            break;
1693
0
        case c_m3Type_f64:
1694
0
            *(f64*)(s) = *(f64*)i_argptrs[i];
1695
0
            s += 8;
1696
0
            break;
1697
0
#endif
1698
0
        default: return "unknown argument type";
1699
0
        }
1700
0
    }
1701
1702
0
    result = RunCodeChecked(runtime, i_function);
1703
1704
0
    ReportNativeStackUsage();
1705
1706
0
    runtime->lastCalled = result ? NULL : i_function;
1707
1708
0
    _catch: return result;
1709
0
}
1710
1711
// Argument parsing for m3_CallArgv. Strict on purpose: the whole string has to
1712
// be consumed, so "12abc" is rejected rather than read as 12, and an empty or
1713
// unparsable argument is an error rather than the zero that strtoul with a
1714
// NULL end pointer used to hand back. See wasm3/wasm3#367.
1715
static
1716
M3Result ParseArgInteger (ccstr_t i_arg, u32 i_numBits, u64* o_value)
1717
0
{
1718
0
    if (not i_arg or not *i_arg) {
1719
0
        return "empty argument";
1720
0
    }
1721
1722
    // strtoull would skip leading space, but trailing space is rejected below;
1723
    // accepting one and not the other would just be confusing
1724
0
    if (isspace((unsigned char)*i_arg)) {
1725
0
        return "argument is not a number";
1726
0
    }
1727
1728
0
    char* end = NULL;
1729
0
    u64   value;
1730
1731
0
    errno = 0;
1732
1733
    // an argument may be spelled signed or unsigned: -1 and 4294967295 name the
1734
    // same i32
1735
0
    if (*i_arg == '-') {
1736
0
        i64 signedValue = strtoll(i_arg, &end, 10);
1737
1738
0
        if (i_numBits == 32 and (signedValue < INT32_MIN or signedValue > INT32_MAX)) {
1739
0
            return "argument out of range";
1740
0
        }
1741
1742
0
        value = (u64)signedValue;
1743
0
    } else {
1744
0
        value = strtoull(i_arg, &end, 10);
1745
1746
0
        if (i_numBits == 32 and value > UINT32_MAX) {
1747
0
            return "argument out of range";
1748
0
        }
1749
0
    }
1750
1751
0
    if (errno == ERANGE) {
1752
0
        return "argument out of range";
1753
0
    }
1754
1755
0
    if (end == i_arg or *end) {
1756
0
        return "argument is not a number";
1757
0
    }
1758
1759
0
    *o_value = value;
1760
1761
0
    return m3Err_none;
1762
0
}
1763
1764
1765
#if d_m3HasFloat
1766
static
1767
M3Result ParseArgFloat (ccstr_t i_arg, f64* o_value)
1768
0
{
1769
0
    if (not i_arg or not *i_arg) {
1770
0
        return "empty argument";
1771
0
    }
1772
1773
0
    if (isspace((unsigned char)*i_arg)) {
1774
0
        return "argument is not a number";
1775
0
    }
1776
1777
0
    char* end = NULL;
1778
1779
0
    errno = 0;
1780
1781
0
    f64 value = strtod(i_arg, &end);
1782
1783
0
    if (end == i_arg or *end) {
1784
0
        return "argument is not a number";
1785
0
    }
1786
1787
    // strtod reports underflow through ERANGE as well, and a denormal result is
1788
    // perfectly usable, so only an overflow to infinity is out of range
1789
0
    if (errno == ERANGE and (value > DBL_MAX or value < -DBL_MAX)) {
1790
0
        return "argument out of range";
1791
0
    }
1792
1793
0
    *o_value = value;
1794
1795
0
    return m3Err_none;
1796
0
}
1797
#endif
1798
1799
1800
// A reference argument is either the null reference or a host handle written as
1801
// an integer.
1802
static
1803
M3Result ParseArgReference (ccstr_t i_arg, u64* o_value)
1804
0
{
1805
0
    if (i_arg and strcmp(i_arg, "null") == 0) {
1806
0
        *o_value = 0;
1807
0
        return m3Err_none;
1808
0
    }
1809
1810
0
    return ParseArgInteger(i_arg, 64, o_value);
1811
0
}
1812
1813
1814
M3Result m3_CallArgv (IM3Function i_function, uint32_t i_argc, const char* i_argv[])
1815
0
{
1816
0
    IM3FuncType ftype   = i_function->funcType;
1817
0
    IM3Runtime  runtime = i_function->module->runtime;
1818
0
    M3Result    result  = m3Err_none;
1819
0
    u8*         s       = NULL;
1820
1821
0
    if (i_argc != ftype->numArgs) {
1822
0
        return m3Err_argumentCountMismatch;
1823
0
    }
1824
0
    if (!i_function->compiled) {
1825
0
        return m3Err_missingCompiledCode;
1826
0
    }
1827
1828
#if d_m3RecordBacktraces
1829
    ClearBacktrace(runtime);
1830
#endif
1831
1832
0
    m3StackCheckInit();
1833
1834
0
_   (checkStartFunction(i_function->module))
1835
1836
0
    s = GetStackPointerForArgs(i_function);
1837
1838
0
    for (u32 i = 0; i < ftype->numArgs; ++i) {
1839
0
        u64 value = 0;
1840
0
#if d_m3HasFloat
1841
0
        f64 fvalue = 0;
1842
0
#endif
1843
0
        switch (d_FuncArgType(ftype, i)) {
1844
0
        case c_m3Type_i32:
1845
0
_           (ParseArgInteger(i_argv[i], 32, &value)) * (i32*)(s) = (i32)value;
1846
0
            s += 8;
1847
0
            break;
1848
0
        case c_m3Type_i64:
1849
0
_           (ParseArgInteger(i_argv[i], 64, &value)) * (i64*)(s) = (i64)value;
1850
0
            s += 8;
1851
0
            break;
1852
0
        case c_m3Type_funcref:
1853
0
        case c_m3Type_externref:
1854
0
        case c_m3Type_exnref:
1855
0
_           (ParseArgReference(i_argv[i], &value)) * (uintptr_t*)(s) = (uintptr_t)value;
1856
0
            s += 8;
1857
0
            break;
1858
0
#if d_m3HasFloat
1859
            // strtof would be less portable
1860
0
        case c_m3Type_f32:
1861
0
_           (ParseArgFloat(i_argv[i], &fvalue)) * (f32*)(s) = (f32)fvalue;
1862
0
            s += 8;
1863
0
            break;
1864
0
        case c_m3Type_f64:
1865
0
_           (ParseArgFloat(i_argv[i], &fvalue)) * (f64*)(s) = fvalue;
1866
0
            s += 8;
1867
0
            break;
1868
0
#endif
1869
0
        default: _throw("unknown argument type");
1870
0
        }
1871
0
    }
1872
1873
0
    result = RunCodeChecked(runtime, i_function);
1874
1875
0
    ReportNativeStackUsage();
1876
1877
0
    runtime->lastCalled = result ? NULL : i_function;
1878
1879
0
    _catch: return result;
1880
0
}
1881
1882
1883
//u8 * AlignStackPointerTo64Bits (const u8 * i_stack)
1884
//{
1885
//    uintptr_t ptr = (uintptr_t) i_stack;
1886
//    return (u8 *) ((ptr + 7) & ~7);
1887
//}
1888
1889
1890
M3Result m3_GetResults (IM3Function i_function, uint32_t i_retc, const void* o_retptrs[])
1891
0
{
1892
0
    IM3FuncType ftype   = i_function->funcType;
1893
0
    IM3Runtime  runtime = i_function->module->runtime;
1894
1895
0
    if (i_retc != ftype->numRets) {
1896
0
        return m3Err_argumentCountMismatch;
1897
0
    }
1898
0
    if (i_function != runtime->lastCalled) {
1899
0
        return "function not called";
1900
0
    }
1901
1902
0
    u8* s = (u8*)runtime->stack;
1903
1904
0
    for (u32 i = 0; i < ftype->numRets; ++i) {
1905
0
        switch (d_FuncRetType(ftype, i)) {
1906
0
        case c_m3Type_i32:
1907
0
            *(i32*)o_retptrs[i] = *(i32*)(s);
1908
0
            s += 8;
1909
0
            break;
1910
0
        case c_m3Type_i64:
1911
0
            *(i64*)o_retptrs[i] = *(i64*)(s);
1912
0
            s += 8;
1913
0
            break;
1914
0
        case c_m3Type_funcref:
1915
0
        case c_m3Type_externref:
1916
0
        case c_m3Type_exnref:
1917
0
            *(uintptr_t*)o_retptrs[i] = *(uintptr_t*)(s);
1918
0
            s += 8;
1919
0
            break;
1920
0
#if d_m3HasFloat
1921
0
        case c_m3Type_f32:
1922
0
            *(f32*)o_retptrs[i] = *(f32*)(s);
1923
0
            s += 8;
1924
0
            break;
1925
0
        case c_m3Type_f64:
1926
0
            *(f64*)o_retptrs[i] = *(f64*)(s);
1927
0
            s += 8;
1928
0
            break;
1929
0
#endif
1930
0
        default: return "unknown return type";
1931
0
        }
1932
0
    }
1933
0
    return m3Err_none;
1934
0
}
1935
1936
M3Result m3_GetResultsV (IM3Function i_function, ...)
1937
0
{
1938
0
    va_list ap;
1939
0
    va_start(ap, i_function);
1940
0
    M3Result r = m3_GetResultsVL(i_function, ap);
1941
0
    va_end(ap);
1942
0
    return r;
1943
0
}
1944
1945
M3Result m3_GetResultsVL (IM3Function i_function, va_list o_rets)
1946
0
{
1947
0
    IM3Runtime  runtime = i_function->module->runtime;
1948
0
    IM3FuncType ftype   = i_function->funcType;
1949
1950
0
    if (i_function != runtime->lastCalled) {
1951
0
        return "function not called";
1952
0
    }
1953
1954
0
    u8* s = (u8*)runtime->stack;
1955
0
    for (u32 i = 0; i < ftype->numRets; ++i) {
1956
0
        switch (d_FuncRetType(ftype, i)) {
1957
0
        case c_m3Type_i32:
1958
0
            *va_arg(o_rets, i32*) = *(i32*)(s);
1959
0
            s += 8;
1960
0
            break;
1961
0
        case c_m3Type_i64:
1962
0
            *va_arg(o_rets, i64*) = *(i64*)(s);
1963
0
            s += 8;
1964
0
            break;
1965
0
        case c_m3Type_funcref:
1966
0
        case c_m3Type_externref:
1967
0
        case c_m3Type_exnref:
1968
0
            *va_arg(o_rets, uintptr_t*) = *(uintptr_t*)(s);
1969
0
            s += 8;
1970
0
            break;
1971
0
#if d_m3HasFloat
1972
0
        case c_m3Type_f32:
1973
0
            *va_arg(o_rets, f32*) = *(f32*)(s);
1974
0
            s += 8;
1975
0
            break;
1976
0
        case c_m3Type_f64:
1977
0
            *va_arg(o_rets, f64*) = *(f64*)(s);
1978
0
            s += 8;
1979
0
            break;
1980
0
#endif
1981
0
        default: return "unknown argument type";
1982
0
        }
1983
0
    }
1984
0
    return m3Err_none;
1985
0
}
1986
1987
void ReleaseCodePageNoTrack (IM3Runtime i_runtime, IM3CodePage i_codePage)
1988
1.23k
{
1989
1.23k
    if (i_codePage) {
1990
1.23k
        IM3CodePage* list;
1991
1992
1.23k
        bool pageFull = (NumFreeLines(i_codePage) < d_m3CodePageFreeLinesThreshold);
1993
1.23k
        if (pageFull) {
1994
8
            list = &i_runtime->pagesFull;
1995
1.22k
        } else {
1996
1.22k
            list = &i_runtime->pagesOpen;
1997
1.22k
        }
1998
1999
1.23k
        PushCodePage(list, i_codePage);                         m3log (emit, "release page: %d to queue: '%s'", i_codePage->info.sequence, pageFull ? "full" : "open")
2000
1.23k
    }
2001
1.23k
}
2002
2003
2004
IM3CodePage AcquireCodePageWithCapacity (IM3Runtime i_runtime, u32 i_minLineCount)
2005
1.23k
{
2006
1.23k
    IM3CodePage page = RemoveCodePageOfCapacity(&i_runtime->pagesOpen, i_minLineCount);
2007
2008
1.23k
    if (not page) {
2009
1.16k
        page = Environment_AcquireCodePage(i_runtime->environment, i_minLineCount);
2010
2011
1.16k
        if (not page) {
2012
1.10k
            page = NewCodePage(i_runtime, i_minLineCount);
2013
1.10k
        }
2014
2015
1.16k
        if (page) {
2016
1.16k
            i_runtime->numCodePages++;
2017
1.16k
        }
2018
1.16k
    }
2019
2020
1.23k
    if (page) {                                                  m3log (emit, "acquire page: %d", page->info.sequence);
2021
1.23k
        i_runtime->numActiveCodePages++;
2022
1.23k
    }
2023
2024
1.23k
    return page;
2025
1.23k
}
2026
2027
2028
IM3CodePage AcquireCodePage (IM3Runtime i_runtime)
2029
1.22k
{
2030
1.22k
    return AcquireCodePageWithCapacity(i_runtime, d_m3CodePageFreeLinesThreshold);
2031
1.22k
}
2032
2033
2034
void ReleaseCodePage (IM3Runtime i_runtime, IM3CodePage i_codePage)
2035
1.23k
{
2036
1.23k
    if (i_codePage) {
2037
1.23k
        ReleaseCodePageNoTrack(i_runtime, i_codePage);
2038
1.23k
        i_runtime->numActiveCodePages--;
2039
2040
#if defined(DEBUG)
2041
        u32 numOpen = CountCodePages(i_runtime->pagesOpen);
2042
        u32 numFull = CountCodePages(i_runtime->pagesFull);
2043
2044
        m3log(runtime, "runtime: %p; open-pages: %d; full-pages: %d; active: %d; total: %d", i_runtime, numOpen, numFull, i_runtime->numActiveCodePages, i_runtime->numCodePages);
2045
2046
        d_m3Assert(numOpen + numFull + i_runtime->numActiveCodePages == i_runtime->numCodePages);
2047
2048
#  if d_m3LogCodePages
2049
        dump_code_page(i_codePage, /* startPC: */ NULL);
2050
#  endif
2051
#endif
2052
1.23k
    }
2053
1.23k
}
2054
2055
2056
#if d_m3VerboseErrorMessages
2057
M3Result m3Error (M3Result i_result, IM3Runtime i_runtime, IM3Module i_module, IM3Function i_function,
2058
                  const char* const i_file, u32 i_lineNum, const char* const i_errorMessage, ...)
2059
414
{
2060
414
    if (i_runtime) {
2061
414
        i_runtime->error         = (M3ErrorInfo){ .result = i_result, .runtime = i_runtime, .module = i_module, .function = i_function, .file = i_file, .line = i_lineNum };
2062
414
        i_runtime->error.message = i_runtime->error_message;
2063
2064
414
        va_list args;
2065
414
        va_start(args, i_errorMessage);
2066
414
        vsnprintf(i_runtime->error_message, sizeof(i_runtime->error_message), i_errorMessage, args);
2067
414
        va_end(args);
2068
414
    }
2069
2070
414
    return i_result;
2071
414
}
2072
#endif
2073
2074
2075
void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info)
2076
0
{
2077
0
    if (i_runtime) {
2078
0
        *o_info = i_runtime->error;
2079
0
        m3_ResetErrorInfo(i_runtime);
2080
0
    }
2081
0
}
2082
2083
2084
void m3_ResetErrorInfo (IM3Runtime i_runtime)
2085
1.89k
{
2086
1.89k
    if (i_runtime) {
2087
1.89k
        M3_INIT(i_runtime->error);
2088
1.89k
        i_runtime->error.message = "";
2089
1.89k
    }
2090
1.89k
}
2091
2092
uint8_t* m3_GetMemory (IM3Module i_module, size_t* o_memorySizeInBytes, uint32_t i_memoryIndex)
2093
0
{
2094
0
    uint8_t* memory = NULL;
2095
0
    size_t   size   = 0;
2096
2097
0
    if (i_module and i_memoryIndex < i_module->numMemories) {
2098
0
        IM3Memory mem = i_module->memories[i_memoryIndex];
2099
2100
0
        if (mem->mallocated) {
2101
0
            size = mem->mallocated->length;
2102
2103
0
            if (size) {
2104
0
                memory = m3MemData(mem->mallocated);
2105
0
            }
2106
0
        }
2107
0
    }
2108
2109
0
    if (o_memorySizeInBytes) {
2110
0
        *o_memorySizeInBytes = size;
2111
0
    }
2112
2113
0
    return memory;
2114
0
}
2115
2116
2117
size_t m3_GetMemorySize (IM3Module i_module, uint32_t i_memoryIndex)
2118
0
{
2119
0
    if (not i_module or i_memoryIndex >= i_module->numMemories) {
2120
0
        return 0;
2121
0
    }
2122
2123
0
    IM3Memory mem = i_module->memories[i_memoryIndex];
2124
2125
0
    return mem->mallocated ? mem->mallocated->length : 0;
2126
0
}
2127
2128
2129
size_t m3_GetMemorySizeAt (const void* i_memory)
2130
0
{
2131
0
    if (not i_memory) {
2132
0
        return 0;
2133
0
    }
2134
2135
    // the header sits immediately before the data it describes
2136
0
    const M3MemoryHeader* header = ((const M3MemoryHeader*)i_memory) - 1;
2137
2138
0
    return header->length;
2139
0
}
2140
2141
2142
M3Result m3_FindExportedMemory (IM3Module i_module, cstr_t i_name, u32* o_memoryIndex)
2143
0
{
2144
0
    if (not i_module or not i_name or not o_memoryIndex) {
2145
0
        return m3Err_unknownMemory;
2146
0
    }
2147
2148
0
    for (u32 i = 0; i < i_module->numMemories; ++i) {
2149
0
        IM3Memory memory = i_module->memories[i];
2150
2151
0
        if (memory->exportName and strcmp(memory->exportName, i_name) == 0) {
2152
0
            *o_memoryIndex = i;
2153
0
            return m3Err_none;
2154
0
        }
2155
0
    }
2156
2157
0
    return m3Err_unknownMemory;
2158
0
}
2159
2160
2161
bool Module_HasLinkedHostImport (IM3Module i_module, cstr_t i_importModule)
2162
0
{
2163
0
    for (u32 i = 0; i < i_module->numFunctions; ++i) {
2164
0
        IM3Function f = &i_module->functions[i];
2165
2166
        // hostMemory is set when a host function is bound to the import, so it
2167
        // is also the mark of one - see CompileRawFunction
2168
0
        if (not f->hostMemory or f->wasm or f->resolved) {
2169
0
            continue;
2170
0
        }
2171
2172
0
        if (f->import.moduleUtf8 and strcmp(f->import.moduleUtf8, i_importModule) == 0) {
2173
0
            return true;
2174
0
        }
2175
0
    }
2176
2177
0
    return false;
2178
0
}
2179
2180
2181
M3Result m3_BindImportMemory (IM3Module io_module, cstr_t i_importModule, u32 i_memoryIndex)
2182
0
{
2183
0
    if (not io_module or not i_importModule) {
2184
0
        return m3Err_moduleNotLinked;
2185
0
    }
2186
2187
0
    if (i_memoryIndex >= io_module->numMemories) {
2188
0
        return m3Err_unknownMemory;
2189
0
    }
2190
2191
    // the memory itself, not the index: linking may have pointed this slot at
2192
    // another module's memory, and growing one reallocates behind it
2193
0
    IM3Memory memory = io_module->memories[i_memoryIndex];
2194
2195
0
    const bool wildcardModule = (strcmp(i_importModule, "*") == 0);
2196
2197
0
    for (u32 i = 0; i < io_module->numFunctions; ++i) {
2198
0
        IM3Function f = &io_module->functions[i];
2199
2200
        // only an import that a host function was bound to has a memory to
2201
        // pin: one with a body of its own reads memory through the interpreter,
2202
        // and one resolved to another module's export runs that module's code
2203
0
        if (not f->hostMemory or f->wasm or f->resolved) {
2204
0
            continue;
2205
0
        }
2206
2207
0
        if (wildcardModule or (f->import.moduleUtf8 and strcmp(f->import.moduleUtf8, i_importModule) == 0)) {
2208
0
            f->hostMemory = memory;
2209
0
        }
2210
0
    }
2211
2212
0
    return m3Err_none;
2213
0
}
2214
2215
2216
M3BacktraceInfo* m3_GetBacktrace (IM3Runtime i_runtime)
2217
0
{
2218
#if d_m3RecordBacktraces
2219
    return &i_runtime->backtrace;
2220
#else
2221
    return NULL;
2222
0
#endif
2223
0
}