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