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