Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Enumerate.h>
8
#include <LibWasm/AbstractMachine/AbstractMachine.h>
9
#include <LibWasm/AbstractMachine/BytecodeInterpreter.h>
10
#include <LibWasm/AbstractMachine/Configuration.h>
11
#include <LibWasm/AbstractMachine/Interpreter.h>
12
#include <LibWasm/AbstractMachine/Validator.h>
13
#include <LibWasm/Types.h>
14
15
namespace Wasm {
16
17
Optional<FunctionAddress> Store::allocate(ModuleInstance& instance, Module const& module, CodeSection::Code const& code, TypeIndex type_index)
18
0
{
19
0
    FunctionAddress address { m_functions.size() };
20
0
    if (type_index.value() >= instance.types().size())
21
0
        return {};
22
23
0
    auto& type = instance.types()[type_index.value()];
24
0
    m_functions.empend(WasmFunction { type, instance, module, code });
25
0
    return address;
26
0
}
27
28
Optional<FunctionAddress> Store::allocate(HostFunction&& function)
29
0
{
30
0
    FunctionAddress address { m_functions.size() };
31
0
    m_functions.empend(HostFunction { move(function) });
32
0
    return address;
33
0
}
34
35
Optional<TableAddress> Store::allocate(TableType const& type)
36
0
{
37
0
    TableAddress address { m_tables.size() };
38
0
    Vector<Reference> elements;
39
0
    elements.resize(type.limits().min());
40
0
    m_tables.empend(TableInstance { type, move(elements) });
41
0
    return address;
42
0
}
43
44
Optional<MemoryAddress> Store::allocate(MemoryType const& type)
45
0
{
46
0
    MemoryAddress address { m_memories.size() };
47
0
    auto instance = MemoryInstance::create(type);
48
0
    if (instance.is_error())
49
0
        return {};
50
51
0
    m_memories.append(instance.release_value());
52
0
    return address;
53
0
}
54
55
Optional<GlobalAddress> Store::allocate(GlobalType const& type, Value value)
56
0
{
57
0
    GlobalAddress address { m_globals.size() };
58
0
    m_globals.append(GlobalInstance { value, type.is_mutable(), type.type() });
59
0
    return address;
60
0
}
61
62
Optional<DataAddress> Store::allocate_data(Vector<u8> initializer)
63
0
{
64
0
    DataAddress address { m_datas.size() };
65
0
    m_datas.append(DataInstance { move(initializer) });
66
0
    return address;
67
0
}
68
69
Optional<ElementAddress> Store::allocate(ValueType const& type, Vector<Reference> references)
70
0
{
71
0
    ElementAddress address { m_elements.size() };
72
0
    m_elements.append(ElementInstance { type, move(references) });
73
0
    return address;
74
0
}
75
76
FunctionInstance* Store::get(FunctionAddress address)
77
0
{
78
0
    auto value = address.value();
79
0
    if (m_functions.size() <= value)
80
0
        return nullptr;
81
0
    return &m_functions[value];
82
0
}
83
84
Module const* Store::get_module_for(Wasm::FunctionAddress address)
85
0
{
86
0
    auto* function = get(address);
87
0
    if (!function || function->has<HostFunction>())
88
0
        return nullptr;
89
0
    return function->get<WasmFunction>().module_ref().ptr();
90
0
}
91
92
TableInstance* Store::get(TableAddress address)
93
0
{
94
0
    auto value = address.value();
95
0
    if (m_tables.size() <= value)
96
0
        return nullptr;
97
0
    return &m_tables[value];
98
0
}
99
100
MemoryInstance* Store::get(MemoryAddress address)
101
0
{
102
0
    auto value = address.value();
103
0
    if (m_memories.size() <= value)
104
0
        return nullptr;
105
0
    return &m_memories[value];
106
0
}
107
108
GlobalInstance* Store::get(GlobalAddress address)
109
0
{
110
0
    auto value = address.value();
111
0
    if (m_globals.size() <= value)
112
0
        return nullptr;
113
0
    return &m_globals[value];
114
0
}
115
116
ElementInstance* Store::get(ElementAddress address)
117
0
{
118
0
    auto value = address.value();
119
0
    if (m_elements.size() <= value)
120
0
        return nullptr;
121
0
    return &m_elements[value];
122
0
}
123
124
DataInstance* Store::get(DataAddress address)
125
0
{
126
0
    auto value = address.value();
127
0
    if (m_datas.size() <= value)
128
0
        return nullptr;
129
0
    return &m_datas[value];
130
0
}
131
132
ErrorOr<void, ValidationError> AbstractMachine::validate(Module& module)
133
0
{
134
0
    if (module.validation_status() != Module::ValidationStatus::Unchecked) {
135
0
        if (module.validation_status() == Module::ValidationStatus::Valid)
136
0
            return {};
137
138
0
        return ValidationError { module.validation_error() };
139
0
    }
140
141
0
    auto result = Validator {}.validate(module);
142
0
    if (result.is_error()) {
143
0
        module.set_validation_error(result.error().error_string);
144
0
        return result.release_error();
145
0
    }
146
147
0
    return {};
148
0
}
149
InstantiationResult AbstractMachine::instantiate(Module const& module, Vector<ExternValue> externs)
150
0
{
151
0
    if (auto result = validate(const_cast<Module&>(module)); result.is_error())
152
0
        return InstantiationError { ByteString::formatted("Validation failed: {}", result.error()) };
153
154
0
    auto main_module_instance_pointer = make<ModuleInstance>();
155
0
    auto& main_module_instance = *main_module_instance_pointer;
156
157
0
    main_module_instance.types() = module.type_section().types();
158
159
0
    Vector<Value> global_values;
160
0
    Vector<Vector<Reference>> elements;
161
0
    ModuleInstance auxiliary_instance;
162
163
0
    for (auto [i, import_] : enumerate(module.import_section().imports())) {
164
0
        auto extern_ = externs.at(i);
165
0
        auto invalid = import_.description().visit(
166
0
            [&](MemoryType const& mem_type) -> Optional<ByteString> {
167
0
                if (!extern_.has<MemoryAddress>())
168
0
                    return "Expected memory import"sv;
169
0
                auto other_mem_type = m_store.get(extern_.get<MemoryAddress>())->type();
170
0
                if (other_mem_type.limits().is_subset_of(mem_type.limits()))
171
0
                    return {};
172
0
                return ByteString::formatted("Memory import and extern do not match: {}-{} vs {}-{}", mem_type.limits().min(), mem_type.limits().max(), other_mem_type.limits().min(), other_mem_type.limits().max());
173
0
            },
174
0
            [&](TableType const& table_type) -> Optional<ByteString> {
175
0
                if (!extern_.has<TableAddress>())
176
0
                    return "Expected table import"sv;
177
0
                auto other_table_type = m_store.get(extern_.get<TableAddress>())->type();
178
0
                if (table_type.element_type() == other_table_type.element_type()
179
0
                    && other_table_type.limits().is_subset_of(table_type.limits()))
180
0
                    return {};
181
182
0
                return ByteString::formatted("Table import and extern do not match: {}-{} vs {}-{}", table_type.limits().min(), table_type.limits().max(), other_table_type.limits().min(), other_table_type.limits().max());
183
0
            },
184
0
            [&](GlobalType const& global_type) -> Optional<ByteString> {
185
0
                if (!extern_.has<GlobalAddress>())
186
0
                    return "Expected global import"sv;
187
0
                auto other_global_type = m_store.get(extern_.get<GlobalAddress>())->type();
188
0
                if (global_type.type() == other_global_type.type()
189
0
                    && global_type.is_mutable() == other_global_type.is_mutable())
190
0
                    return {};
191
0
                return "Global import and extern do not match"sv;
192
0
            },
193
0
            [&](FunctionType const& type) -> Optional<ByteString> {
194
0
                if (!extern_.has<FunctionAddress>())
195
0
                    return "Expected function import"sv;
196
0
                auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
197
0
                if (type.results() != other_type.results())
198
0
                    return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
199
0
                if (type.parameters() != other_type.parameters())
200
0
                    return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
201
0
                return {};
202
0
            },
203
0
            [&](TypeIndex type_index) -> Optional<ByteString> {
204
0
                if (!extern_.has<FunctionAddress>())
205
0
                    return "Expected function import"sv;
206
0
                auto other_type = m_store.get(extern_.get<FunctionAddress>())->visit([&](WasmFunction const& wasm_func) { return wasm_func.type(); }, [&](HostFunction const& host_func) { return host_func.type(); });
207
0
                auto& type = module.type_section().types()[type_index.value()];
208
0
                if (type.results() != other_type.results())
209
0
                    return ByteString::formatted("Function import and extern do not match, results: {} vs {}", type.results(), other_type.results());
210
0
                if (type.parameters() != other_type.parameters())
211
0
                    return ByteString::formatted("Function import and extern do not match, parameters: {} vs {}", type.parameters(), other_type.parameters());
212
0
                return {};
213
0
            });
214
0
        if (invalid.has_value())
215
0
            return InstantiationError { ByteString::formatted("{}::{}: {}", import_.module(), import_.name(), invalid.release_value()) };
216
0
    }
217
218
0
    for (auto& entry : externs) {
219
0
        if (auto* ptr = entry.get_pointer<GlobalAddress>())
220
0
            auxiliary_instance.globals().append(*ptr);
221
0
        else if (auto* ptr = entry.get_pointer<FunctionAddress>())
222
0
            auxiliary_instance.functions().append(*ptr);
223
0
    }
224
225
0
    Vector<FunctionAddress> module_functions;
226
0
    module_functions.ensure_capacity(module.function_section().types().size());
227
228
0
    size_t i = 0;
229
0
    for (auto& code : module.code_section().functions()) {
230
0
        auto type_index = module.function_section().types()[i];
231
0
        auto address = m_store.allocate(main_module_instance, module, code, type_index);
232
0
        VERIFY(address.has_value());
233
0
        auxiliary_instance.functions().append(*address);
234
0
        module_functions.append(*address);
235
0
        ++i;
236
0
    }
237
238
0
    BytecodeInterpreter interpreter(m_stack_info);
239
240
0
    for (auto& entry : module.global_section().entries()) {
241
0
        Configuration config { m_store };
242
0
        if (m_should_limit_instruction_count)
243
0
            config.enable_instruction_count_limit();
244
0
        config.set_frame(Frame {
245
0
            auxiliary_instance,
246
0
            Vector<Value> {},
247
0
            entry.expression(),
248
0
            1,
249
0
        });
250
0
        auto result = config.execute(interpreter).assert_wasm_result();
251
0
        if (result.is_trap())
252
0
            return InstantiationError { ByteString::formatted("Global value construction trapped: {}", result.trap().reason) };
253
0
        global_values.append(result.values().first());
254
0
    }
255
256
0
    if (auto result = allocate_all_initial_phase(module, main_module_instance, externs, global_values, module_functions); result.has_value())
257
0
        return result.release_value();
258
259
0
    for (auto& segment : module.element_section().segments()) {
260
0
        Vector<Reference> references;
261
0
        for (auto& entry : segment.init) {
262
0
            Configuration config { m_store };
263
0
            if (m_should_limit_instruction_count)
264
0
                config.enable_instruction_count_limit();
265
0
            config.set_frame(Frame {
266
0
                auxiliary_instance,
267
0
                Vector<Value> {},
268
0
                entry,
269
0
                entry.instructions().size(),
270
0
            });
271
0
            auto result = config.execute(interpreter).assert_wasm_result();
272
0
            if (result.is_trap())
273
0
                return InstantiationError { ByteString::formatted("Element construction trapped: {}", result.trap().reason) };
274
275
0
            for (auto& value : result.values()) {
276
0
                auto reference = value.to<Reference>();
277
0
                references.append(reference);
278
0
            }
279
0
        }
280
0
        elements.append(move(references));
281
0
    }
282
283
0
    if (auto result = allocate_all_final_phase(module, main_module_instance, elements); result.has_value())
284
0
        return result.release_value();
285
286
0
    size_t index = 0;
287
0
    for (auto& segment : module.element_section().segments()) {
288
0
        auto current_index = index;
289
0
        ++index;
290
0
        auto active_ptr = segment.mode.get_pointer<ElementSection::Active>();
291
0
        auto elem_instance = m_store.get(main_module_instance.elements()[current_index]);
292
0
        if (!active_ptr) {
293
0
            if (segment.mode.has<ElementSection::Declarative>())
294
0
                *elem_instance = ElementInstance(elem_instance->type(), {});
295
0
            continue;
296
0
        }
297
0
        Configuration config { m_store };
298
0
        if (m_should_limit_instruction_count)
299
0
            config.enable_instruction_count_limit();
300
0
        config.set_frame(Frame {
301
0
            auxiliary_instance,
302
0
            Vector<Value> {},
303
0
            active_ptr->expression,
304
0
            1,
305
0
        });
306
0
        auto result = config.execute(interpreter).assert_wasm_result();
307
0
        if (result.is_trap())
308
0
            return InstantiationError { ByteString::formatted("Element section initialisation trapped: {}", result.trap().reason) };
309
0
        auto d = result.values().first().to<i32>();
310
0
        auto table_instance = m_store.get(main_module_instance.tables()[active_ptr->index.value()]);
311
0
        if (current_index >= main_module_instance.elements().size())
312
0
            return InstantiationError { "Invalid element referenced by active element segment" };
313
0
        if (!table_instance || !elem_instance)
314
0
            return InstantiationError { "Invalid element referenced by active element segment" };
315
316
0
        Checked<size_t> total_size = elem_instance->references().size();
317
0
        total_size.saturating_add(d);
318
319
0
        if (total_size.value() > table_instance->elements().size())
320
0
            return InstantiationError { "Table instantiation out of bounds" };
321
322
0
        size_t i = 0;
323
0
        for (auto it = elem_instance->references().begin(); it < elem_instance->references().end(); ++i, ++it)
324
0
            table_instance->elements()[i + d] = *it;
325
        // Drop element
326
0
        *m_store.get(main_module_instance.elements()[current_index]) = ElementInstance(elem_instance->type(), {});
327
0
    }
328
329
0
    for (auto& segment : module.data_section().data()) {
330
0
        Optional<InstantiationError> result = segment.value().visit(
331
0
            [&](DataSection::Data::Active const& data) -> Optional<InstantiationError> {
332
0
                Configuration config { m_store };
333
0
                if (m_should_limit_instruction_count)
334
0
                    config.enable_instruction_count_limit();
335
0
                config.set_frame(Frame {
336
0
                    auxiliary_instance,
337
0
                    Vector<Value> {},
338
0
                    data.offset,
339
0
                    1,
340
0
                });
341
0
                auto result = config.execute(interpreter).assert_wasm_result();
342
0
                if (result.is_trap())
343
0
                    return InstantiationError { ByteString::formatted("Data section initialisation trapped: {}", result.trap().reason) };
344
0
                size_t offset = result.values().first().to<u64>();
345
0
                if (main_module_instance.memories().size() <= data.index.value()) {
346
0
                    return InstantiationError {
347
0
                        ByteString::formatted("Data segment referenced out-of-bounds memory ({}) of max {} entries",
348
0
                            data.index.value(), main_module_instance.memories().size())
349
0
                    };
350
0
                }
351
0
                auto maybe_data_address = m_store.allocate_data(data.init);
352
0
                if (!maybe_data_address.has_value()) {
353
0
                    return InstantiationError { "Failed to allocate a data instance for an active data segment"sv };
354
0
                }
355
0
                main_module_instance.datas().append(*maybe_data_address);
356
357
0
                auto address = main_module_instance.memories()[data.index.value()];
358
0
                auto instance = m_store.get(address);
359
0
                Checked<size_t> checked_offset = data.init.size();
360
0
                checked_offset += offset;
361
0
                if (checked_offset.has_overflow() || checked_offset > instance->size()) {
362
0
                    return InstantiationError {
363
0
                        ByteString::formatted("Data segment attempted to write to out-of-bounds memory ({}) in memory of size {}",
364
0
                            offset, instance->size())
365
0
                    };
366
0
                }
367
0
                if (!data.init.is_empty())
368
0
                    instance->data().overwrite(offset, data.init.data(), data.init.size());
369
0
                return {};
370
0
            },
371
0
            [&](DataSection::Data::Passive const& passive) -> Optional<InstantiationError> {
372
0
                auto maybe_data_address = m_store.allocate_data(passive.init);
373
0
                if (!maybe_data_address.has_value()) {
374
0
                    return InstantiationError { "Failed to allocate a data instance for a passive data segment"sv };
375
0
                }
376
0
                main_module_instance.datas().append(*maybe_data_address);
377
0
                return {};
378
0
            });
379
0
        if (result.has_value())
380
0
            return result.release_value();
381
0
    }
382
383
0
    if (module.start_section().function().has_value()) {
384
0
        auto& functions = main_module_instance.functions();
385
0
        auto index = module.start_section().function()->index();
386
0
        if (functions.size() <= index.value()) {
387
0
            return InstantiationError { ByteString::formatted("Start section function referenced invalid index {} of max {} entries", index.value(), functions.size()) };
388
0
        }
389
0
        auto result = invoke(functions[index.value()], {});
390
0
        if (result.is_trap())
391
0
            return InstantiationError { ByteString::formatted("Start function trapped: {}", result.trap().reason) };
392
0
    }
393
394
0
    return InstantiationResult { move(main_module_instance_pointer) };
395
0
}
396
397
Optional<InstantiationError> AbstractMachine::allocate_all_initial_phase(Module const& module, ModuleInstance& module_instance, Vector<ExternValue>& externs, Vector<Value>& global_values, Vector<FunctionAddress>& own_functions)
398
0
{
399
0
    Optional<InstantiationError> result;
400
401
0
    for (auto& entry : externs) {
402
0
        entry.visit(
403
0
            [&](FunctionAddress const& address) { module_instance.functions().append(address); },
404
0
            [&](TableAddress const& address) { module_instance.tables().append(address); },
405
0
            [&](MemoryAddress const& address) { module_instance.memories().append(address); },
406
0
            [&](GlobalAddress const& address) { module_instance.globals().append(address); });
407
0
    }
408
409
0
    module_instance.functions().extend(own_functions);
410
411
    // FIXME: What if this fails?
412
413
0
    for (auto& table : module.table_section().tables()) {
414
0
        auto table_address = m_store.allocate(table.type());
415
0
        VERIFY(table_address.has_value());
416
0
        module_instance.tables().append(*table_address);
417
0
    }
418
419
0
    for (auto& memory : module.memory_section().memories()) {
420
0
        auto memory_address = m_store.allocate(memory.type());
421
0
        VERIFY(memory_address.has_value());
422
0
        module_instance.memories().append(*memory_address);
423
0
    }
424
425
0
    size_t index = 0;
426
0
    for (auto& entry : module.global_section().entries()) {
427
0
        auto address = m_store.allocate(entry.type(), move(global_values[index]));
428
0
        VERIFY(address.has_value());
429
0
        module_instance.globals().append(*address);
430
0
        index++;
431
0
    }
432
433
0
    for (auto& entry : module.export_section().entries()) {
434
0
        Variant<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress, Empty> address {};
435
0
        entry.description().visit(
436
0
            [&](FunctionIndex const& index) {
437
0
                if (module_instance.functions().size() > index.value())
438
0
                    address = FunctionAddress { module_instance.functions()[index.value()] };
439
0
                else
440
0
                    dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.functions().size());
441
0
            },
442
0
            [&](TableIndex const& index) {
443
0
                if (module_instance.tables().size() > index.value())
444
0
                    address = TableAddress { module_instance.tables()[index.value()] };
445
0
                else
446
0
                    dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.tables().size());
447
0
            },
448
0
            [&](MemoryIndex const& index) {
449
0
                if (module_instance.memories().size() > index.value())
450
0
                    address = MemoryAddress { module_instance.memories()[index.value()] };
451
0
                else
452
0
                    dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.memories().size());
453
0
            },
454
0
            [&](GlobalIndex const& index) {
455
0
                if (module_instance.globals().size() > index.value())
456
0
                    address = GlobalAddress { module_instance.globals()[index.value()] };
457
0
                else
458
0
                    dbgln("Failed to export '{}', the exported address ({}) was out of bounds (min: 0, max: {})", entry.name(), index.value(), module_instance.globals().size());
459
0
            });
460
461
0
        if (address.has<Empty>()) {
462
0
            result = InstantiationError { "An export could not be resolved" };
463
0
            continue;
464
0
        }
465
466
0
        module_instance.exports().append(ExportInstance {
467
0
            entry.name(),
468
0
            move(address).downcast<FunctionAddress, TableAddress, MemoryAddress, GlobalAddress>(),
469
0
        });
470
0
    }
471
472
0
    return result;
473
0
}
474
475
Optional<InstantiationError> AbstractMachine::allocate_all_final_phase(Module const& module, ModuleInstance& module_instance, Vector<Vector<Reference>>& elements)
476
0
{
477
0
    size_t index = 0;
478
0
    for (auto& segment : module.element_section().segments()) {
479
0
        auto address = m_store.allocate(segment.type, move(elements[index]));
480
0
        VERIFY(address.has_value());
481
0
        module_instance.elements().append(*address);
482
0
        index++;
483
0
    }
484
485
0
    return {};
486
0
}
487
488
Result AbstractMachine::invoke(FunctionAddress address, Vector<Value> arguments)
489
0
{
490
0
    BytecodeInterpreter interpreter(m_stack_info);
491
0
    return invoke(interpreter, address, move(arguments));
492
0
}
493
494
Result AbstractMachine::invoke(Interpreter& interpreter, FunctionAddress address, Vector<Value> arguments)
495
0
{
496
0
    Configuration configuration { m_store };
497
0
    if (m_should_limit_instruction_count)
498
0
        configuration.enable_instruction_count_limit();
499
0
    return configuration.call(interpreter, address, move(arguments));
500
0
}
501
502
void Linker::link(ModuleInstance const& instance)
503
0
{
504
0
    populate();
505
0
    if (m_unresolved_imports.is_empty())
506
0
        return;
507
508
0
    HashTable<Name> resolved_imports;
509
0
    for (auto& import_ : m_unresolved_imports) {
510
0
        auto it = instance.exports().find_if([&](auto& export_) { return export_.name() == import_.name; });
511
0
        if (!it.is_end()) {
512
0
            resolved_imports.set(import_);
513
0
            m_resolved_imports.set(import_, it->value());
514
0
        }
515
0
    }
516
517
0
    for (auto& entry : resolved_imports)
518
0
        m_unresolved_imports.remove(entry);
519
0
}
520
521
void Linker::link(HashMap<Linker::Name, ExternValue> const& exports)
522
0
{
523
0
    populate();
524
0
    if (m_unresolved_imports.is_empty())
525
0
        return;
526
527
0
    if (exports.is_empty())
528
0
        return;
529
530
0
    HashTable<Name> resolved_imports;
531
0
    for (auto& import_ : m_unresolved_imports) {
532
0
        auto export_ = exports.get(import_);
533
0
        if (export_.has_value()) {
534
0
            resolved_imports.set(import_);
535
0
            m_resolved_imports.set(import_, export_.value());
536
0
        }
537
0
    }
538
539
0
    for (auto& entry : resolved_imports)
540
0
        m_unresolved_imports.remove(entry);
541
0
}
542
543
AK::ErrorOr<Vector<ExternValue>, LinkError> Linker::finish()
544
0
{
545
0
    populate();
546
0
    if (!m_unresolved_imports.is_empty()) {
547
0
        if (!m_error.has_value())
548
0
            m_error = LinkError {};
549
0
        for (auto& entry : m_unresolved_imports)
550
0
            m_error->missing_imports.append(entry.name);
551
0
        return *m_error;
552
0
    }
553
554
0
    if (m_error.has_value())
555
0
        return *m_error;
556
557
    // Result must be in the same order as the module imports
558
0
    Vector<ExternValue> exports;
559
0
    exports.ensure_capacity(m_ordered_imports.size());
560
0
    for (auto& import_ : m_ordered_imports)
561
0
        exports.unchecked_append(*m_resolved_imports.get(import_));
562
0
    return exports;
563
0
}
564
565
void Linker::populate()
566
0
{
567
0
    if (!m_ordered_imports.is_empty())
568
0
        return;
569
570
0
    for (auto& import_ : m_module.import_section().imports()) {
571
0
        m_ordered_imports.append({ import_.module(), import_.name(), import_.description() });
572
0
        m_unresolved_imports.set(m_ordered_imports.last());
573
0
    }
574
0
}
575
}