Coverage Report

Created: 2026-09-01 06:51

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