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
check(bool success)24 void check(bool success) {
25 if (!success) {
26 printf("> Error, expected success\n");
27 exit(1);
28 }
29 }
30
check_call(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs,int32_t expected)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
check_call0(wasmtime_context_t * store,wasmtime_func_t * func,int32_t expected)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
check_call1(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg,int32_t expected)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
check_call2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2,int32_t expected)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
check_ok(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs)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
check_ok2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2)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
check_trap(wasmtime_context_t * store,wasmtime_func_t * func,const wasmtime_val_t * args,size_t nargs,size_t num_results)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
check_trap1(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg)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
check_trap2(wasmtime_context_t * store,wasmtime_func_t * func,int32_t arg1,int32_t arg2)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
main()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
exit_with_error(const char * message,wasmtime_error_t * error,wasm_trap_t * trap)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