xref: /wasmtime-44.0.1/examples/multimemory.c (revision a70809d0)
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