/* * Copyright (c) 2021, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include namespace Wasm { void Interpreter::interpret(Configuration& configuration) { auto& instructions = configuration.frame()->expression().instructions(); auto max_ip_value = InstructionPointer { instructions.size() }; auto& current_ip_value = configuration.ip(); while (current_ip_value < max_ip_value) { auto& instruction = instructions[current_ip_value.value()]; interpret(configuration, current_ip_value, instruction); ++current_ip_value; } } void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index) { auto label = configuration.nth_label(index.value()); VERIFY(label.has_value()); NonnullOwnPtrVector results; // Pop results in order for (size_t i = 0; i < label->arity(); ++i) results.append(move(configuration.stack().pop().get>())); size_t drop_count = index.value() + 1; if (label->continuation() < configuration.ip()) --drop_count; for (; !configuration.stack().is_empty();) { auto entry = configuration.stack().pop(); if (entry.has>()) { if (drop_count-- == 0) break; } } // Push results in reverse for (size_t i = results.size(); i > 0; --i) configuration.stack().push(move(static_cast>&>(results)[i - 1])); configuration.ip() = label->continuation() + 1; } ReadonlyBytes Interpreter::load_from_memory(Configuration& configuration, const Instruction& instruction, size_t size) { auto& address = configuration.frame()->module().memories().first(); auto memory = configuration.store().get(address); VERIFY(memory); auto& arg = instruction.arguments().get(); auto base = configuration.stack().pop().get>()->to(); VERIFY(base.has_value()); auto instance_address = base.value() + static_cast(arg.offset); if (instance_address < 0 || static_cast(instance_address + size) > memory->size()) { dbgln("LibWasm: Memory access out of bounds (expected 0 > {} and {} > {})", instance_address, instance_address + size, memory->size()); return {}; } return memory->data().bytes().slice(instance_address, size); } void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction) { dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value()); if constexpr (WASM_TRACE_DEBUG) configuration.dump_stack(); switch (instruction.opcode().value()) { case Instructions::unreachable.value(): VERIFY_NOT_REACHED(); // FIXME: This is definitely not right :) case Instructions::nop.value(): return; case Instructions::local_get.value(): configuration.stack().push(make(configuration.frame()->locals()[instruction.arguments().get().value()])); return; case Instructions::local_set.value(): { auto entry = configuration.stack().pop(); configuration.frame()->locals()[instruction.arguments().get().value()] = move(*entry.get>()); return; } case Instructions::i32_const.value(): configuration.stack().push(make(ValueType { ValueType::I32 }, static_cast(instruction.arguments().get()))); return; case Instructions::i64_const.value(): configuration.stack().push(make(ValueType { ValueType::I64 }, instruction.arguments().get())); return; case Instructions::f32_const.value(): configuration.stack().push(make(ValueType { ValueType::F32 }, static_cast(instruction.arguments().get()))); return; case Instructions::f64_const.value(): configuration.stack().push(make(ValueType { ValueType::F64 }, instruction.arguments().get())); return; case Instructions::block.value(): { size_t arity = 0; auto& args = instruction.arguments().get(); if (args.block_type.kind() != BlockType::Empty) arity = 1; configuration.stack().push(make