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