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