|  | #include <iostream> | 
|  | #include <fstream> | 
|  | #include <cstdlib> | 
|  | #include <string> | 
|  | #include <cinttypes> | 
|  |  | 
|  | #include "wasm.hh" | 
|  |  | 
|  |  | 
|  | // A function to be called from Wasm code. | 
|  | auto neg_callback( | 
|  | const wasm::Val args[], wasm::Val results[] | 
|  | ) -> wasm::own<wasm::Trap*> { | 
|  | std::cout << "Calling back..." << std::endl; | 
|  | results[0] = wasm::Val(-args[0].i32()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  |  | 
|  | auto get_export_table(wasm::vec<wasm::Extern*>& exports, size_t i) -> wasm::Table* { | 
|  | if (exports.size() <= i || !exports[i]->table()) { | 
|  | std::cout << "> Error accessing table export " << i << "!" << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | return exports[i]->table(); | 
|  | } | 
|  |  | 
|  | auto get_export_func(const wasm::vec<wasm::Extern*>& exports, size_t i) -> const wasm::Func* { | 
|  | if (exports.size() <= i || !exports[i]->func()) { | 
|  | std::cout << "> Error accessing function export " << i << "!" << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | return exports[i]->func(); | 
|  | } | 
|  |  | 
|  | template<class T, class U> | 
|  | void check(T actual, U expected) { | 
|  | if (actual != expected) { | 
|  | std::cout << "> Error on result, expected " << expected << ", got " << actual << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | void check(bool success) { | 
|  | if (! success) { | 
|  | std::cout << "> Error, expected success" << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | auto call( | 
|  | const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2 | 
|  | ) -> wasm::Val { | 
|  | wasm::Val args[2] = {std::move(arg1), std::move(arg2)}; | 
|  | wasm::Val results[1]; | 
|  | if (func->call(args, results)) { | 
|  | std::cout << "> Error on result, expected return" << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | return results[0].copy(); | 
|  | } | 
|  |  | 
|  | void check_trap(const wasm::Func* func, wasm::Val&& arg1, wasm::Val&& arg2) { | 
|  | wasm::Val args[2] = {std::move(arg1), std::move(arg2)}; | 
|  | wasm::Val results[1]; | 
|  | if (! func->call(args, results)) { | 
|  | std::cout << "> Error on result, expected trap" << std::endl; | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  |  | 
|  | void run() { | 
|  | // Initialize. | 
|  | std::cout << "Initializing..." << std::endl; | 
|  | auto engine = wasm::Engine::make(); | 
|  | auto store_ = wasm::Store::make(engine.get()); | 
|  | auto store = store_.get(); | 
|  |  | 
|  | // Load binary. | 
|  | std::cout << "Loading binary..." << std::endl; | 
|  | std::ifstream file("table.wasm"); | 
|  | file.seekg(0, std::ios_base::end); | 
|  | auto file_size = file.tellg(); | 
|  | file.seekg(0); | 
|  | auto binary = wasm::vec<byte_t>::make_uninitialized(file_size); | 
|  | file.read(binary.get(), file_size); | 
|  | file.close(); | 
|  | if (file.fail()) { | 
|  | std::cout << "> Error loading module!" << std::endl; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Compile. | 
|  | std::cout << "Compiling module..." << std::endl; | 
|  | auto module = wasm::Module::make(store, binary); | 
|  | if (!module) { | 
|  | std::cout << "> Error compiling module!" << std::endl; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Instantiate. | 
|  | std::cout << "Instantiating module..." << std::endl; | 
|  | auto instance = wasm::Instance::make(store, module.get(), nullptr); | 
|  | if (!instance) { | 
|  | std::cout << "> Error instantiating module!" << std::endl; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Extract export. | 
|  | std::cout << "Extracting exports..." << std::endl; | 
|  | auto exports = instance->exports(); | 
|  | size_t i = 0; | 
|  | auto table = get_export_table(exports, i++); | 
|  | auto call_indirect = get_export_func(exports, i++); | 
|  | auto f = get_export_func(exports, i++); | 
|  | auto g = get_export_func(exports, i++); | 
|  |  | 
|  | // Create external function. | 
|  | std::cout << "Creating callback..." << std::endl; | 
|  | auto neg_type = wasm::FuncType::make( | 
|  | wasm::vec<wasm::ValType*>::make(wasm::ValType::make(wasm::I32)), | 
|  | wasm::vec<wasm::ValType*>::make(wasm::ValType::make(wasm::I32)) | 
|  | ); | 
|  | auto h = wasm::Func::make(store, neg_type.get(), neg_callback); | 
|  |  | 
|  | // Check initial table. | 
|  | std::cout << "Checking table..." << std::endl; | 
|  | check(table->size(), 2u); | 
|  | check(table->get(0) == nullptr); | 
|  | check(table->get(1) != nullptr); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(0)); | 
|  | check(call(call_indirect, wasm::Val::i32(7), wasm::Val::i32(1)).i32(), 7); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(2)); | 
|  |  | 
|  | // Mutate table. | 
|  | std::cout << "Mutating table..." << std::endl; | 
|  | check(table->set(0, g)); | 
|  | check(table->set(1, nullptr)); | 
|  | check(! table->set(2, f)); | 
|  | check(table->get(0) != nullptr); | 
|  | check(table->get(1) == nullptr); | 
|  | check(call(call_indirect, wasm::Val::i32(7), wasm::Val::i32(0)).i32(), 666); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(1)); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(2)); | 
|  |  | 
|  | // Grow table. | 
|  | std::cout << "Growing table..." << std::endl; | 
|  | check(table->grow(3)); | 
|  | check(table->size(), 5u); | 
|  | check(table->set(2, f)); | 
|  | check(table->set(3, h.get())); | 
|  | check(! table->set(5, nullptr)); | 
|  | check(table->get(2) != nullptr); | 
|  | check(table->get(3) != nullptr); | 
|  | check(table->get(4) == nullptr); | 
|  | check(call(call_indirect, wasm::Val::i32(5), wasm::Val::i32(2)).i32(), 5); | 
|  | check(call(call_indirect, wasm::Val::i32(6), wasm::Val::i32(3)).i32(), -6); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(4)); | 
|  | check_trap(call_indirect, wasm::Val::i32(0), wasm::Val::i32(5)); | 
|  |  | 
|  | check(table->grow(2, f)); | 
|  | check(table->size(), 7u); | 
|  | check(table->get(5) != nullptr); | 
|  | check(table->get(6) != nullptr); | 
|  |  | 
|  | check(! table->grow(5)); | 
|  | check(table->grow(3)); | 
|  | check(table->grow(0)); | 
|  |  | 
|  | // Create stand-alone table. | 
|  | // TODO(wasm+): Once Wasm allows multiple tables, turn this into import. | 
|  | std::cout << "Creating stand-alone table..." << std::endl; | 
|  | auto tabletype = wasm::TableType::make( | 
|  | wasm::ValType::make(wasm::FUNCREF), wasm::Limits(5, 5)); | 
|  | auto table2 = wasm::Table::make(store, tabletype.get()); | 
|  | check(table2->size() == 5); | 
|  | check(! table2->grow(1)); | 
|  | check(table2->grow(0)); | 
|  |  | 
|  | // Shut down. | 
|  | std::cout << "Shutting down..." << std::endl; | 
|  | } | 
|  |  | 
|  |  | 
|  | int main(int argc, const char* argv[]) { | 
|  | run(); | 
|  | std::cout << "Done." << std::endl; | 
|  | return 0; | 
|  | } |