1 /* 2 An example of how to interact with multiple memories. 3 4 You can compile and run this example on Linux with: 5 6 cargo build --release -p wasmtime-c-api 7 cc examples/multimemory.c \ 8 -I crates/c-api/include \ 9 -I crates/c-api/wasm-c-api/include \ 10 target/release/libwasmtime.a \ 11 -lpthread -ldl -lm \ 12 -o multimemory 13 ./multimemory 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 19 #include <inttypes.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <wasm.h> 24 #include <wasmtime.h> 25 26 static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap); 27 28 void check(bool success) { 29 if (!success) { 30 printf("> Error, expected success\n"); 31 exit(1); 32 } 33 } 34 35 void check_call(wasmtime_context_t *store, 36 wasmtime_func_t *func, 37 const wasmtime_val_t* args, 38 size_t nargs, 39 int32_t expected) { 40 wasmtime_val_t results[1]; 41 wasm_trap_t *trap = NULL; 42 wasmtime_error_t *error = wasmtime_func_call( 43 store, func, args, nargs, results, 1, &trap 44 ); 45 if (error != NULL || trap != NULL) 46 exit_with_error("failed to call function", error, trap); 47 if (results[0].of.i32 != expected) { 48 printf("> Error on result\n"); 49 exit(1); 50 } 51 } 52 53 void check_call0(wasmtime_context_t *store, wasmtime_func_t *func, int32_t expected) { 54 check_call(store, func, NULL, 0, expected); 55 } 56 57 void check_call1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg, int32_t expected) { 58 wasmtime_val_t args[1]; 59 args[0].kind = WASMTIME_I32; 60 args[0].of.i32 = arg; 61 check_call(store, func, args, 1, expected); 62 } 63 64 void check_call2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2, int32_t expected) { 65 wasmtime_val_t args[2]; 66 args[0].kind = WASMTIME_I32; 67 args[0].of.i32 = arg1; 68 args[1].kind = WASMTIME_I32; 69 args[1].of.i32 = arg2; 70 check_call(store, func, args, 2, expected); 71 } 72 73 void check_ok(wasmtime_context_t *store, wasmtime_func_t *func, const wasmtime_val_t* args, size_t nargs) { 74 wasm_trap_t *trap = NULL; 75 wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, NULL, 0, &trap); 76 if (error != NULL || trap != NULL) 77 exit_with_error("failed to call function", error, trap); 78 } 79 80 void check_ok2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) { 81 wasmtime_val_t args[2]; 82 args[0].kind = WASMTIME_I32; 83 args[0].of.i32 = arg1; 84 args[1].kind = WASMTIME_I32; 85 args[1].of.i32 = arg2; 86 check_ok(store, func, args, 2); 87 } 88 89 void check_trap(wasmtime_context_t *store, 90 wasmtime_func_t *func, 91 const wasmtime_val_t *args, 92 size_t nargs, 93 size_t num_results) { 94 assert(num_results <= 1); 95 wasmtime_val_t results[1]; 96 wasm_trap_t *trap = NULL; 97 wasmtime_error_t *error = wasmtime_func_call(store, func, args, nargs, results, num_results, &trap); 98 if (error != NULL) 99 exit_with_error("failed to call function", error, NULL); 100 if (trap == NULL) { 101 printf("> Error on result, expected trap\n"); 102 exit(1); 103 } 104 wasm_trap_delete(trap); 105 } 106 107 void check_trap1(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg) { 108 wasmtime_val_t args[1]; 109 args[0].kind = WASMTIME_I32; 110 args[0].of.i32 = arg; 111 check_trap(store, func, args, 1, 1); 112 } 113 114 void check_trap2(wasmtime_context_t *store, wasmtime_func_t *func, int32_t arg1, int32_t arg2) { 115 wasmtime_val_t args[2]; 116 args[0].kind = WASMTIME_I32; 117 args[0].of.i32 = arg1; 118 args[1].kind = WASMTIME_I32; 119 args[1].of.i32 = arg2; 120 check_trap(store, func, args, 2, 0); 121 } 122 123 int main(int argc, const char* argv[]) { 124 // Initialize. 125 printf("Initializing...\n"); 126 127 wasm_config_t *config = wasm_config_new(); 128 assert(config != NULL); 129 wasmtime_config_wasm_multi_memory_set(config, true); 130 131 wasm_engine_t *engine = wasm_engine_new_with_config(config); 132 assert(engine != NULL); 133 134 wasmtime_store_t* store = wasmtime_store_new(engine, NULL, NULL); 135 wasmtime_context_t *context = wasmtime_store_context(store); 136 137 // Load our input file to parse it next 138 FILE* file = fopen("examples/multimemory.wat", "r"); 139 if (!file) { 140 printf("> Error loading file!\n"); 141 return 1; 142 } 143 fseek(file, 0L, SEEK_END); 144 size_t file_size = ftell(file); 145 fseek(file, 0L, SEEK_SET); 146 wasm_byte_vec_t wat; 147 wasm_byte_vec_new_uninitialized(&wat, file_size); 148 if (fread(wat.data, file_size, 1, file) != 1) { 149 printf("> Error loading module!\n"); 150 return 1; 151 } 152 fclose(file); 153 154 // Parse the wat into the binary wasm format 155 wasm_byte_vec_t binary; 156 wasmtime_error_t *error = wasmtime_wat2wasm(wat.data, wat.size, &binary); 157 if (error != NULL) 158 exit_with_error("failed to parse wat", error, NULL); 159 wasm_byte_vec_delete(&wat); 160 161 // Compile. 162 printf("Compiling module...\n"); 163 wasmtime_module_t* module = NULL; 164 error = wasmtime_module_new(engine, (uint8_t*) binary.data, binary.size, &module); 165 if (error) 166 exit_with_error("failed to compile module", error, NULL); 167 wasm_byte_vec_delete(&binary); 168 169 // Instantiate. 170 printf("Instantiating module...\n"); 171 wasmtime_instance_t instance; 172 wasm_trap_t *trap = NULL; 173 error = wasmtime_instance_new(context, module, NULL, 0, &instance, &trap); 174 if (error != NULL || trap != NULL) 175 exit_with_error("failed to instantiate", error, trap); 176 wasmtime_module_delete(module); 177 178 // Extract export. 179 printf("Extracting exports...\n"); 180 wasmtime_memory_t memory0, memory1; 181 wasmtime_func_t size0, load0, store0, size1, load1, store1; 182 wasmtime_extern_t item; 183 bool ok; 184 ok = wasmtime_instance_export_get(context, &instance, "memory0", strlen("memory0"), &item); 185 assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); 186 memory0 = item.of.memory; 187 ok = wasmtime_instance_export_get(context, &instance, "size0", strlen("size0"), &item); 188 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 189 size0 = item.of.func; 190 ok = wasmtime_instance_export_get(context, &instance, "load0", strlen("load0"), &item); 191 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 192 load0 = item.of.func; 193 ok = wasmtime_instance_export_get(context, &instance, "store0", strlen("store0"), &item); 194 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 195 store0 = item.of.func; 196 ok = wasmtime_instance_export_get(context, &instance, "memory1", strlen("memory1"), &item); 197 assert(ok && item.kind == WASMTIME_EXTERN_MEMORY); 198 memory1 = item.of.memory; 199 ok = wasmtime_instance_export_get(context, &instance, "size1", strlen("size1"), &item); 200 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 201 size1 = item.of.func; 202 ok = wasmtime_instance_export_get(context, &instance, "load1", strlen("load1"), &item); 203 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 204 load1 = item.of.func; 205 ok = wasmtime_instance_export_get(context, &instance, "store1", strlen("store1"), &item); 206 assert(ok && item.kind == WASMTIME_EXTERN_FUNC); 207 store1 = item.of.func; 208 209 // Check initial memory. 210 printf("Checking memory...\n"); 211 check(wasmtime_memory_size(context, &memory0) == 2); 212 check(wasmtime_memory_data_size(context, &memory0) == 0x20000); 213 check(wasmtime_memory_data(context, &memory0)[0] == 0); 214 check(wasmtime_memory_data(context, &memory0)[0x1000] == 1); 215 check(wasmtime_memory_data(context, &memory0)[0x1001] == 2); 216 check(wasmtime_memory_data(context, &memory0)[0x1002] == 3); 217 check(wasmtime_memory_data(context, &memory0)[0x1003] == 4); 218 219 check_call0(context, &size0, 2); 220 check_call1(context, &load0, 0, 0); 221 check_call1(context, &load0, 0x1000, 1); 222 check_call1(context, &load0, 0x1001, 2); 223 check_call1(context, &load0, 0x1002, 3); 224 check_call1(context, &load0, 0x1003, 4); 225 check_call1(context, &load0, 0x1ffff, 0); 226 check_trap1(context, &load0, 0x20000); 227 228 check(wasmtime_memory_size(context, &memory1) == 2); 229 check(wasmtime_memory_data_size(context, &memory1) == 0x20000); 230 check(wasmtime_memory_data(context, &memory1)[0] == 0); 231 check(wasmtime_memory_data(context, &memory1)[0x1000] == 4); 232 check(wasmtime_memory_data(context, &memory1)[0x1001] == 3); 233 check(wasmtime_memory_data(context, &memory1)[0x1002] == 2); 234 check(wasmtime_memory_data(context, &memory1)[0x1003] == 1); 235 236 check_call0(context, &size1, 2); 237 check_call1(context, &load1, 0, 0); 238 check_call1(context, &load1, 0x1000, 4); 239 check_call1(context, &load1, 0x1001, 3); 240 check_call1(context, &load1, 0x1002, 2); 241 check_call1(context, &load1, 0x1003, 1); 242 check_call1(context, &load1, 0x1ffff, 0); 243 check_trap1(context, &load1, 0x20000); 244 245 // Mutate memory. 246 printf("Mutating memory...\n"); 247 wasmtime_memory_data(context, &memory0)[0x1003] = 5; 248 check_ok2(context, &store0, 0x1002, 6); 249 check_trap2(context, &store0, 0x20000, 0); 250 251 check(wasmtime_memory_data(context, &memory0)[0x1002] == 6); 252 check(wasmtime_memory_data(context, &memory0)[0x1003] == 5); 253 check_call1(context, &load0, 0x1002, 6); 254 check_call1(context, &load0, 0x1003, 5); 255 256 wasmtime_memory_data(context, &memory1)[0x1003] = 7; 257 check_ok2(context, &store1, 0x1002, 8); 258 check_trap2(context, &store1, 0x20000, 0); 259 260 check(wasmtime_memory_data(context, &memory1)[0x1002] == 8); 261 check(wasmtime_memory_data(context, &memory1)[0x1003] == 7); 262 check_call1(context, &load1, 0x1002, 8); 263 check_call1(context, &load1, 0x1003, 7); 264 265 // Grow memory. 266 printf("Growing memory...\n"); 267 uint32_t old_size; 268 error = wasmtime_memory_grow(context, &memory0, 1, &old_size); 269 if (error != NULL) 270 exit_with_error("failed to grow memory", error, trap); 271 check(wasmtime_memory_size(context, &memory0) == 3); 272 check(wasmtime_memory_data_size(context, &memory0) == 0x30000); 273 274 check_call1(context, &load0, 0x20000, 0); 275 check_ok2(context, &store0, 0x20000, 0); 276 check_trap1(context, &load0, 0x30000); 277 check_trap2(context, &store0, 0x30000, 0); 278 279 error = wasmtime_memory_grow(context, &memory0, 1, &old_size); 280 assert(error != NULL); 281 wasmtime_error_delete(error); 282 error = wasmtime_memory_grow(context, &memory0, 0, &old_size); 283 if (error != NULL) 284 exit_with_error("failed to grow memory", error, trap); 285 286 error = wasmtime_memory_grow(context, &memory1, 2, &old_size); 287 if (error != NULL) 288 exit_with_error("failed to grow memory", error, trap); 289 check(wasmtime_memory_size(context, &memory1) == 4); 290 check(wasmtime_memory_data_size(context, &memory1) == 0x40000); 291 292 check_call1(context, &load1, 0x30000, 0); 293 check_ok2(context, &store1, 0x30000, 0); 294 check_trap1(context, &load1, 0x40000); 295 check_trap2(context, &store1, 0x40000, 0); 296 297 error = wasmtime_memory_grow(context, &memory1, 1, &old_size); 298 assert(error != NULL); 299 wasmtime_error_delete(error); 300 error = wasmtime_memory_grow(context, &memory1, 0, &old_size); 301 if (error != NULL) 302 exit_with_error("failed to grow memory", error, trap); 303 304 // Shut down. 305 printf("Shutting down...\n"); 306 wasmtime_store_delete(store); 307 wasm_engine_delete(engine); 308 309 // All done. 310 printf("Done.\n"); 311 return 0; 312 } 313 314 static void exit_with_error(const char *message, wasmtime_error_t *error, wasm_trap_t *trap) { 315 fprintf(stderr, "error: %s\n", message); 316 wasm_byte_vec_t error_message; 317 if (error != NULL) { 318 wasmtime_error_message(error, &error_message); 319 wasmtime_error_delete(error); 320 } else { 321 wasm_trap_message(trap, &error_message); 322 wasm_trap_delete(trap); 323 } 324 fprintf(stderr, "%.*s\n", (int) error_message.size, error_message.data); 325 wasm_byte_vec_delete(&error_message); 326 exit(1); 327 } 328