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