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