1 /* 2 Example of instantiating of the WebAssembly module and invoking its exported 3 function. 4 5 You can compile and run this example on Linux with: 6 7 cargo build --release -p wasmtime-c-api 8 cc examples/memory.c \ 9 -I crates/c-api/include \ 10 target/release/libwasmtime.a \ 11 -lpthread -ldl -lm \ 12 -o memory 13 ./memory 14 15 Note that on Windows and macOS the command will be similar, but you'll need 16 to tweak the `-lpthread` and such annotations. 17 18 You can also build using cmake: 19 20 mkdir build && cd build && cmake .. && cmake --build . --target wasmtime-memory 21 22 Also note that this example was taken from 23 https://github.com/WebAssembly/wasm-c-api/blob/master/example/memory.c 24 originally 25 */ 26 27 #include <inttypes.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <wasm.h> 32 #include <wasmtime.h> 33 34 static void exit_with_error(const char *message, wasmtime_error_t *error, 35 wasm_trap_t *trap); 36 37 void check(bool success) { 38 if (!success) { 39 printf("> Error, expected success\n"); 40 exit(1); 41 } 42 } 43 44 void check_call(wasmtime_context_t *store, wasmtime_func_t *func, 45 const wasmtime_val_t *args, size_t nargs, int32_t expected) { 46 wasmtime_val_t results[1]; 47 wasm_trap_t *trap = NULL; 48 wasmtime_error_t *error = 49 wasmtime_func_call(store, func, args, nargs, results, 1, &trap); 50 if (error != NULL || trap != NULL) 51 exit_with_error("failed to call function", error, trap); 52 if (results[0].of.i32 != expected) { 53 printf("> Error on result\n"); 54 exit(1); 55 } 56 } 57 58 void check_call0(wasmtime_context_t *store, wasmtime_func_t *func, 59 int32_t expected) { 60 check_call(store, func, NULL, 0, expected); 61 } 62 63 void check_call1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg, 64 int32_t expected) { 65 wasmtime_val_t args[1]; 66 args[0].kind = WASMTIME_I32; 67 args[0].of.i32 = arg; 68 check_call(store, func, args, 1, expected); 69 } 70 71 void check_call2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 72 int32_t arg2, int32_t expected) { 73 wasmtime_val_t args[2]; 74 args[0].kind = WASMTIME_I32; 75 args[0].of.i32 = arg1; 76 args[1].kind = WASMTIME_I32; 77 args[1].of.i32 = arg2; 78 check_call(store, func, args, 2, expected); 79 } 80 81 void check_ok(wasmtime_context_t *store, wasmtime_func_t *func, 82 const wasmtime_val_t *args, size_t nargs) { 83 wasm_trap_t *trap = NULL; 84 wasmtime_error_t *error = 85 wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap); 86 if (error != NULL || trap != NULL) 87 exit_with_error("failed to call function", error, trap); 88 } 89 90 void check_ok2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 91 int32_t arg2) { 92 wasmtime_val_t args[2]; 93 args[0].kind = WASMTIME_I32; 94 args[0].of.i32 = arg1; 95 args[1].kind = WASMTIME_I32; 96 args[1].of.i32 = arg2; 97 check_ok(store, func, args, 2); 98 } 99 100 void check_trap(wasmtime_context_t *store, wasmtime_func_t *func, 101 const wasmtime_val_t *args, size_t nargs, size_t num_results) { 102 assert(num_results <= 1); 103 wasmtime_val_t results[1]; 104 wasm_trap_t *trap = NULL; 105 wasmtime_error_t *error = 106 wasmtime_func_call(store, func, args, nargs, results, num_results, &trap); 107 if (error != NULL) 108 exit_with_error("failed to call function", error, NULL); 109 if (trap == NULL) { 110 printf("> Error on result, expected trap\n"); 111 exit(1); 112 } 113 wasm_trap_delete(trap); 114 } 115 116 void check_trap1(wasmtime_context_t *store, wasmtime_func_t *func, 117 int32_t arg) { 118 wasmtime_val_t args[1]; 119 args[0].kind = WASMTIME_I32; 120 args[0].of.i32 = arg; 121 check_trap(store, func, args, 1, 1); 122 } 123 124 void check_trap2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, 125 int32_t arg2) { 126 wasmtime_val_t args[2]; 127 args[0].kind = WASMTIME_I32; 128 args[0].of.i32 = arg1; 129 args[1].kind = WASMTIME_I32; 130 args[1].of.i32 = arg2; 131 check_trap(store, func, args, 2, 0); 132 } 133 134 int main(int argc, const char *argv[]) { 135 // Initialize. 136 printf("Initializing...\n"); 137 wasm_engine_t *engine = wasm_engine_new(); 138 wasmtime_store_t *store = wasmtime_store_new(engine, NULL, NULL); 139 wasmtime_context_t *context = wasmtime_store_context(store); 140 141 // Load our input file to parse it next 142 FILE *file = fopen("examples/memory.wat", "r"); 143 if (!file) { 144 printf("> Error loading file!\n"); 145 return 1; 146 } 147 fseek(file, 0L, SEEK_END); 148 size_t file_size = ftell(file); 149 fseek(file, 0L, SEEK_SET); 150 wasm_byte_vec_t wat; 151 wasm_byte_vec_new_uninitialized(&wat, file_size); 152 if (fread(wat.data, file_size, 1, file) != 1) { 153 printf("> Error loading module!\n"); 154 return 1; 155 } 156 fclose(file); 157 158 // Parse the wat into the binary wasm format 159 wasm_byte_vec_t binary; 160 wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); 161 if (error != NULL) 162 exit_with_error("failed to parse wat", error, NULL); 163 wasm_byte_vec_delete(&wat); 164 165 // Compile. 166 printf("Compiling module...\n"); 167 wasmtime_module_t *module = NULL; 168 error = 169 wasmtime_module_new(engine, (uint8_t *)binary.data, binary.size, &module); 170 if (error) 171 exit_with_error("failed to compile module", error, NULL); 172 wasm_byte_vec_delete(&binary); 173 174 // Instantiate. 175 printf("Instantiating module...\n"); 176 wasmtime_instance_t instance; 177 wasm_trap_t *trap = NULL; 178 error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); 179 if (error != NULL || trap != NULL) 180 exit_with_error("failed to instantiate", error, trap); 181 wasmtime_module_delete(module); 182 183 // Extract export. 184 printf("Extracting exports...\n"); 185 wasmtime_memory_t memory; 186 wasmtime_func_t size_func, load_func, store_func; 187 wasmtime_extern_t item; 188 bool ok; 189 ok = wasmtime_instance_export_get(context, &instance, "memory", 190 strlen("memory"), &item); 191 assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); 192 memory = item.of.memory; 193 ok = wasmtime_instance_export_get(context, &instance, "size", strlen("size"), 194 &item); 195 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 196 size_func = item.of.func; 197 ok = wasmtime_instance_export_get(context, &instance, "load", strlen("load"), 198 &item); 199 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 200 load_func = item.of.func; 201 ok = wasmtime_instance_export_get(context, &instance, "store", 202 strlen("store"), &item); 203 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 204 store_func = item.of.func; 205 206 // Check initial memory. 207 printf("Checking memory...\n"); 208 check(wasmtime_memory_size(context, &memory) == 2); 209 check(wasmtime_memory_data_size(context, &memory) == 0x20000); 210 check(wasmtime_memory_data(context, &memory)[0] == 0); 211 check(wasmtime_memory_data(context, &memory)[0x1000] == 1); 212 check(wasmtime_memory_data(context, &memory)[0x1003] == 4); 213 214 check_call0(context, &size_func, 2); 215 check_call1(context, &load_func, 0, 0); 216 check_call1(context, &load_func, 0x1000, 1); 217 check_call1(context, &load_func, 0x1003, 4); 218 check_call1(context, &load_func, 0x1ffff, 0); 219 check_trap1(context, &load_func, 0x20000); 220 221 // Mutate memory. 222 printf("Mutating memory...\n"); 223 wasmtime_memory_data(context, &memory)[0x1003] = 5; 224 check_ok2(context, &store_func, 0x1002, 6); 225 check_trap2(context, &store_func, 0x20000, 0); 226 227 check(wasmtime_memory_data(context, &memory)[0x1002] == 6); 228 check(wasmtime_memory_data(context, &memory)[0x1003] == 5); 229 check_call1(context, &load_func, 0x1002, 6); 230 check_call1(context, &load_func, 0x1003, 5); 231 232 // Grow memory. 233 printf("Growing memory...\n"); 234 uint64_t old_size; 235 error = wasmtime_memory_grow(context, &memory, 1, &old_size); 236 if (error != NULL) 237 exit_with_error("failed to grow memory", error, trap); 238 check(wasmtime_memory_size(context, &memory) == 3); 239 check(wasmtime_memory_data_size(context, &memory) == 0x30000); 240 241 check_call1(context, &load_func, 0x20000, 0); 242 check_ok2(context, &store_func, 0x20000, 0); 243 check_trap1(context, &load_func, 0x30000); 244 check_trap2(context, &store_func, 0x30000, 0); 245 246 error = wasmtime_memory_grow(context, &memory, 1, &old_size); 247 assert(error != NULL); 248 wasmtime_error_delete(error); 249 error = wasmtime_memory_grow(context, &memory, 0, &old_size); 250 if (error != NULL) 251 exit_with_error("failed to grow memory", error, trap); 252 253 // Create stand-alone memory. 254 printf("Creating stand-alone memory...\n"); 255 wasm_limits_t limits = {5, 5}; 256 wasm_memorytype_t *memorytype = wasm_memorytype_new(&limits); 257 wasmtime_memory_t memory2; 258 error = wasmtime_memory_new(context, memorytype, &memory2); 259 if (error != NULL) 260 exit_with_error("failed to create memory", error, trap); 261 wasm_memorytype_delete(memorytype); 262 check(wasmtime_memory_size(context, &memory2) == 5); 263 264 // Shut down. 265 printf("Shutting down...\n"); 266 wasmtime_store_delete(store); 267 wasm_engine_delete(engine); 268 269 // All done. 270 printf("Done.\n"); 271 return 0; 272 } 273 274 static void exit_with_error(const char *message, wasmtime_error_t *error, 275 wasm_trap_t *trap) { 276 fprintf(stderr, "error: %s\n", message); 277 wasm_byte_vec_t error_message; 278 if (error != NULL) { 279 wasmtime_error_message(error, &error_message); 280 wasmtime_error_delete(error); 281 } else { 282 wasm_trap_message(trap, &error_message); 283 wasm_trap_delete(trap); 284 } 285 fprintf(stderr, "%.*s\n", (int)error_message.size, error_message.data); 286 wasm_byte_vec_delete(&error_message); 287 exit(1); 288 } 289