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