/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 | | } |