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