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
check(bool success)20 void check(bool success) {
21 if (!success) {
22 printf("> Error, expected success\n");
23 exit(1);
24 }
25 }
26
check_call(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs,int32_t expected)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
check_call0(wasmtime_context_t * store,wasmtime_func_t * func,int32_t expected)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
check_call1(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg,int32_t expected)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
check_call2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2,int32_t expected)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
check_ok(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs)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
check_ok2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2)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
check_trap(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs,size_t num_results)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
check_trap1(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg)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
check_trap2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2)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
main()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
exit_with_error(const char * message,wasmtime_error_t * error,wasm_trap_t * trap)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