File Coverage

Wasm3.xs
Criterion Covered Total %
statement 318 370 85.9
branch 127 234 54.2
condition n/a
subroutine n/a
pod n/a
total 445 604 73.6


line stmt bran cond sub pod time code
1             #include "easyxs/easyxs.h"
2              
3             #include "wasm3/source/wasm3.h"
4             #include "wasm3/source/m3_api_wasi.h"
5              
6             #if WW3_UVWASI
7             #include "uvwasi.h"
8             #endif
9              
10             #include
11             #include
12             #include
13              
14             #ifdef HAS_FCNTL // from Perl
15             #include
16             #endif
17              
18             #define PERL_NS "Wasm::Wasm3"
19             #define PERL_RT_CLASS (PERL_NS "::Runtime")
20             #define PERL_MODULE_CLASS (PERL_NS "::Module")
21              
22             #define _WASI_FUNCNAME "_start"
23              
24             #define MAX_UINT32 0xffffffff
25             #define MAX_MEMSIZE MAX_UINT32
26              
27             #define _warn_if_global_destruct(self_sv, the_struct) \
28             if (PL_dirty && (the_struct->pid == getpid())) { \
29             warn("%" SVf " destroyed at global destruction; memory leak likely!", self_sv); \
30             }
31              
32             typedef struct {
33             IM3Environment env;
34             pid_t pid;
35             uint32_t refcount;
36             } ww3_environ_s;
37              
38             typedef struct {
39             IM3Runtime rt;
40             pid_t pid;
41             bool any_modules_linked;
42              
43             SV* env_sv;
44             } ww3_runtime_s;
45              
46             typedef struct {
47             IM3Module module;
48             pid_t pid;
49             const uint8_t* bytes;
50             STRLEN len;
51             SV* runtime_sv;
52             } ww3_module_s;
53              
54             typedef struct {
55             SV** coderefs;
56             unsigned coderefs_count;
57              
58             #ifdef MULTIPLICITY
59             tTHX aTHX;
60             #endif
61             } ww3_runtime_userdata_s;
62              
63 8           static SV* _create_runtime (pTHX_ const char* classname, SV* stacksize_sv, SV* env_sv) {
64 8           UV stacksize = exs_SvUV(stacksize_sv);
65 8 100         if (stacksize > 0xffffffff) {
66 1           croak("Stack size (%" UVf ") exceeds max allowed (%u)", stacksize, 0xffffffffU);
67             }
68              
69             IM3Environment env;
70              
71 7           SvREFCNT_inc(env_sv);
72              
73 7           ww3_environ_s* env_sp = exs_structref_ptr(env_sv);
74 7           env = env_sp->env;
75              
76 7           SV* self_sv = exs_new_structref(ww3_runtime_s, classname);
77 7           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
78              
79             ww3_runtime_userdata_s* userdata_p;
80 7           Newxz(userdata_p, 1, ww3_runtime_userdata_s);
81              
82             #ifdef MULTIPLICITY
83             userdata_p->aTHX = aTHX;
84             #endif
85              
86 7           *rt_sp = (ww3_runtime_s) {
87 7           .rt = m3_NewRuntime(env, stacksize, userdata_p),
88 7           .pid = getpid(),
89             .env_sv = env_sv,
90             };
91              
92 7           return self_sv;
93             }
94              
95 20           static IM3Global _get_module_sv_global (pTHX_ SV* self_sv, SV* name_sv) {
96 20           const char* name = exs_SvPVutf8_nolen(name_sv);
97              
98 20           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
99              
100 20           return m3_FindGlobal(mod_sp->module, name);
101             }
102              
103 11           static void _perl_svs_to_wasm3 (pTHX_ SV** svs, unsigned count, M3ValueType* types, uint64_t* vals) {
104 22 100         for (unsigned a=0; a
105 11           SV* cur_sv = svs[a];
106              
107 11           void* wasm3_datum_ptr = vals + a;
108              
109 11           switch (types[a]) {
110             case c_m3Type_none:
111             assert(0 /* c_m3Type_none */);
112              
113             case c_m3Type_i32:
114 4 50         *( (int32_t*) wasm3_datum_ptr ) = SvIV( cur_sv );
115 4           break;
116              
117             case c_m3Type_i64:
118 2 50         *( (int64_t*) wasm3_datum_ptr ) = SvIV( cur_sv );
119 2           break;
120              
121             case c_m3Type_f32:
122 2 50         *( (float*) wasm3_datum_ptr ) = SvNV( cur_sv );
123 2           break;
124              
125             case c_m3Type_f64:
126 3 50         *( (double*) wasm3_datum_ptr ) = SvNV( cur_sv );
127 3           break;
128              
129             default:
130             assert(0 /* arg type unexpected */);
131             }
132             }
133 11           }
134              
135 10           static void _wasm3_to_perl_svs (pTHX_ unsigned count, M3ValueType* types, uint64_t* vals, SV** svs) {
136              
137 35 100         for (unsigned r=0; r
138             SV* newret;
139 25           void* val_ptr = vals + r;
140              
141 25           switch (types[r]) {
142             case c_m3Type_none:
143             assert(0 /* c_m3Type_none */);
144              
145             case c_m3Type_i32:
146 15           newret = newSViv( *( (int32_t*) val_ptr ) );
147 15           break;
148              
149             case c_m3Type_i64:
150 3           newret = newSViv( *( (int64_t*) val_ptr ) );
151 3           break;
152              
153             case c_m3Type_f32:
154 3           newret = newSVnv( *( (float*) val_ptr ) );
155 3           break;
156              
157             case c_m3Type_f64:
158 4           newret = newSVnv( *( (double*) val_ptr ) );
159 4           break;
160              
161             default:
162             assert(0 /* arg type unexpected */);
163 0           newret = NULL; /* silence warning */
164             }
165              
166 25           svs[r] = newret;
167             }
168 10           }
169              
170 6           static const void* _call_perl (IM3Runtime runtime, IM3ImportContext _ctx, uint64_t * _sp, void * _mem) {
171             #ifdef MULTIPLICITY
172             ww3_runtime_userdata_s* rt_userdata_p = m3_GetUserData(runtime);
173             pTHX = rt_userdata_p->aTHX;
174             #endif
175              
176 6           IM3Function wasm_func = _ctx->function;
177 6           SV* callback = _ctx->userdata;
178              
179 6           int args_count = m3_GetArgCount(wasm_func);
180 6           int rets_count = m3_GetRetCount(wasm_func);
181              
182 6           SV* arg_svs[1 + args_count];
183 6           arg_svs[args_count] = NULL;
184              
185 6           M3ValueType arg_types[args_count];
186 22 100         for (unsigned a=0; a
187 16           arg_types[a] = m3_GetArgType(wasm_func, a);
188             }
189              
190 6           _wasm3_to_perl_svs(aTHX_ args_count, arg_types, _sp + rets_count, arg_svs);
191              
192 6           SV* err = NULL;
193              
194             /* Perl auto-frees ret_svs. */
195 6           SV** ret_svs = exs_call_sv_list_trapped(callback, arg_svs, &err);
196              
197 6           const char* errstr = NULL;
198              
199 6 100         if (ret_svs) {
200 5           int got_count = 0;
201 5 50         if (ret_svs) {
202 5           SV** p = ret_svs;
203 12 100         while (*p++) got_count++;
204             }
205              
206 5 50         if (got_count == rets_count) {
207 5           M3ValueType types[got_count];
208 12 100         for (unsigned r=0; r
209 7           types[r] = m3_GetRetType(wasm_func, r);
210             }
211              
212 5           _perl_svs_to_wasm3( aTHX_ ret_svs, got_count, types, _sp );
213             }
214             else {
215 0           errstr = "Mismatched return values";
216             }
217              
218 5 50         if (ret_svs) {
219 12 100         while (got_count--) SvREFCNT_dec(ret_svs[got_count]);
220             }
221             }
222             else {
223             assert(err);
224 1           warn_sv(err);
225 1           errstr = "Perl callback threw exception";
226             }
227              
228 6 100         if (errstr) m3ApiTrap(errstr);
229              
230 6           m3ApiSuccess();
231             }
232              
233             #define _WW3_CROAK_IF_NO_MODULE_LOADED(rt_sp) \
234             if (!rt_sp->any_modules_linked) croak("Load a module first.")
235              
236             #define _function_sig_xsub(self_sv, name_sv, counter, getter) STMT_START { \
237             ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv); \
238             \
239             const char* name = exs_SvPVutf8_nolen(name_sv); \
240             \
241             IM3Function o_function; \
242             M3Result res = m3_FindFunction( &o_function, rt_sp->rt, name ); \
243             if (res) croak("Failed to find function %s: %s", name, res); \
244             \
245             uint32_t count = counter(o_function); \
246             \
247             EXTEND(SP, count); \
248             \
249             for (unsigned a=0; a
250             mPUSHu( getter(o_function, a) ); \
251             } \
252             \
253             XSRETURN(count); \
254             } STMT_END
255              
256             #define _croak_if_wasi_failed(self_sv, res) \
257             if (res) croak("%" SVf ": Failed to link WASI imports: %s", self_sv, res);
258              
259 1           void _link_wasi_default ( pTHX_ SV* self_sv ) {
260 1           ww3_module_s* module_sp = exs_structref_ptr(self_sv);
261              
262 1           M3Result res = m3_LinkModuleWASI(module_sp->module);
263 1 50         _croak_if_wasi_failed(self_sv, res);
264              
265 1           return;
266             }
267              
268             #define _croak_dupe_arg(name) \
269             croak(name " given multiple times!");
270              
271             #define _croak_if_not_avref(sv, name) STMT_START { \
272             if (!SvROK(sv) || (SvTYPE(SvRV(sv)) != SVt_PVAV)) { \
273             croak("“%s” must be an ARRAY reference, not %" SVf, name, argval); \
274             } \
275             } STMT_END
276              
277 3           static inline void _croak_if_bogus_fd (int fd) {
278             #ifdef HAS_FCNTL
279 3           int res = fcntl(fd, F_GETFD);
280 3 50         if (-1 == res) {
281 0 0         if (errno == EBADF) croak("Bad file descriptor given: %d\n", fd);
282 0           croak("FD %d check failed: %s", fd, strerror(errno));
283             }
284             #endif
285 3           }
286              
287 3           static inline unsigned _sv_to_fd_or_croak( pTHX_ SV* argval ) {
288 3           int fd = exs_SvUV(argval);
289 3           _croak_if_bogus_fd(fd);
290 3           int fd2 = dup(fd);
291 3 50         if (fd2 == -1) croak("dup(%d): %s", fd, strerror(errno));
292 3           return fd2;
293             }
294              
295 1           void _link_wasi (pTHX_ SV* self_sv, int argslen, SV** args) {
296             #if WW3_UVWASI
297 1           ww3_module_s* module_sp = exs_structref_ptr(self_sv);
298              
299 1 50         if (argslen % 2) {
300 0           croak("Uneven args list given!");
301             }
302              
303             uvwasi_options_t init_options;
304 1           uvwasi_options_init(&init_options);
305              
306 1           bool in_seen = false;
307 1           bool out_seen = false;
308 1           bool err_seen = false;
309 1           bool env_seen = false;
310 1           bool preopen_seen = false;
311              
312 6 100         for (int a=0; a
313 5           const char *argname = exs_SvPVbyte_nolen(args[a]);
314 5           SV* argval = args[1 + a];
315              
316 5 100         if (strEQ("in", argname)) {
317 1 50         if (in_seen) _croak_dupe_arg("in");
318 1           in_seen = true;
319              
320 1           init_options.in = _sv_to_fd_or_croak(aTHX_ argval);
321             }
322 4 100         else if (strEQ("out", argname)) {
323 1 50         if (out_seen) _croak_dupe_arg("out");
324 1           out_seen = true;
325              
326 1           init_options.out = _sv_to_fd_or_croak(aTHX_ argval);
327             }
328 3 100         else if (strEQ("err", argname)) {
329 1 50         if (err_seen) _croak_dupe_arg("err");
330 1           err_seen = true;
331              
332 1           init_options.err = _sv_to_fd_or_croak(aTHX_ argval);
333             }
334 2 100         else if (strEQ("env", argname)) {
335 1 50         if (env_seen) _croak_dupe_arg("env");
336 1           env_seen = true;
337              
338 1 50         _croak_if_not_avref(argval, "env");
    50          
339              
340 1           AV* env_av = (AV*) SvRV(argval);
341 1           SSize_t avlen = 1 + av_len(env_av);
342              
343 1 50         if (avlen % 2) {
344 0           croak("%s: odd-length array given", "env");
345             }
346              
347 1           SSize_t envlen = avlen >> 1;
348              
349 1 50         Newx(init_options.envp, 1 + envlen, const char*);
350 1           SAVEFREEPV(init_options.envp);
351              
352 1           init_options.envp[envlen] = NULL;
353              
354 3 100         for (int e=0; e
355 2           SV** name_svp = av_fetch(env_av, e, 0);
356             assert(name_svp);
357             assert(*name_svp);
358              
359 2           SV** val_svp = av_fetch(env_av, 1 + e, 0);
360             assert(val_svp);
361             assert(*val_svp);
362              
363 2           const char* name = exs_SvPVbyte_nolen(*name_svp);
364 2           const char* val = exs_SvPVbyte_nolen(*val_svp);
365              
366 2           unsigned namelen = strlen(name);
367 2           unsigned vallen = strlen(val);
368              
369 2           SSize_t env_idx = e >> 1;
370              
371 2           Newx(init_options.envp[env_idx], 2 + namelen + vallen, char);
372 2           SAVEFREEPV(init_options.envp[env_idx]);
373 2           int out = sprintf( (char*) init_options.envp[env_idx], "%s=%s", name, val );
374             PERL_UNUSED_VAR(out);
375             assert(out == (1 + namelen + vallen));
376             }
377             }
378 1 50         else if (strEQ("preopen", argname)) {
379 1 50         if (preopen_seen) _croak_dupe_arg("preopen");
380 1           preopen_seen = true;
381              
382 1 50         if (!SvROK(argval) || (SvTYPE(SvRV(argval)) != SVt_PVHV)) {
    50          
383 0           croak("“%s” must be a HASH reference, not %" SVf, "preopen", argval);
384             }
385              
386 1           HV* hv = (HV*) SvRV(argval);
387              
388 1           I32 size = hv_iterinit(hv);
389 1           init_options.preopenc = size;
390 1 50         Newx(init_options.preopens, size, uvwasi_preopen_t);
391 1           SAVEFREEPV(init_options.preopens);
392              
393 2 100         for (I32 i=0; i
394 1           HE* entry = hv_iternext(hv);
395             assert(entry);
396              
397 1           SV* keysv = hv_iterkeysv(entry);
398 1           SV* valsv = hv_iterval(hv, entry);
399              
400 2           init_options.preopens[i] = (uvwasi_preopen_t) {
401 1           .mapped_path = exs_SvPVutf8_nolen(keysv),
402 1           .real_path = exs_SvPVbyte_nolen(valsv),
403             };
404             }
405             }
406             else {
407 0           croak("Unknown: %s", argname);
408             }
409             }
410              
411 1           M3Result res = m3_LinkModuleWASIWithOptions(module_sp->module, init_options);
412 1 50         _croak_if_wasi_failed(self_sv, res);
413              
414             #else
415             croak("Unimplemented in this " PERL_NS " build");
416             #endif
417 1           }
418              
419             /* ---------------------------------------------------------------------- */
420              
421             MODULE = Wasm::Wasm3 PACKAGE = Wasm::Wasm3
422              
423             PROTOTYPES: DISABLE
424              
425             BOOT:
426              
427             /* For compat with pre-5.20 perls we avoid using newCONSTSUB() to create
428             a list-constant; instead, `use constant` in Perl: */
429 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "_M3_VERSION_MAJOR", newSVuv(M3_VERSION_MAJOR));
430 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "_M3_VERSION_MINOR", newSVuv(M3_VERSION_MINOR));
431 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "_M3_VERSION_REV", newSVuv(M3_VERSION_REV));
432              
433 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "M3_VERSION_STRING", newSVpvs(M3_VERSION));
434 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "WASI_BACKEND", newSVpvs(
435             #if WW3_UVWASI
436             "uvwasi"
437             #else
438             "simple"
439             #endif
440             ));
441 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "TYPE_I32", newSVuv(c_m3Type_i32));
442 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "TYPE_I64", newSVuv(c_m3Type_i64));
443 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "TYPE_F32", newSVuv(c_m3Type_f32));
444 4           newCONSTSUB(gv_stashpv(PERL_NS, 0), "TYPE_F64", newSVuv(c_m3Type_f64));
445              
446             SV*
447             new (const char* classname)
448             CODE:
449 6           SV* env_sv = exs_new_structref(ww3_environ_s, classname);
450 6           ww3_environ_s* env_sp = exs_structref_ptr(env_sv);
451              
452 6           *env_sp = (ww3_environ_s) {
453 6           .env = m3_NewEnvironment(),
454 6           .pid = getpid(),
455             };
456              
457 6           RETVAL = env_sv;
458              
459             OUTPUT:
460             RETVAL
461              
462             SV*
463             create_runtime (SV* self_sv, SV* stacksize_sv)
464             CODE:
465 8           RETVAL = _create_runtime(aTHX_ PERL_RT_CLASS, stacksize_sv, self_sv);
466             OUTPUT:
467             RETVAL
468              
469             SV* parse_module (SV* self_sv, SV* modbytes_sv)
470             CODE:
471 6           ww3_environ_s* env_sp = exs_structref_ptr(self_sv);
472              
473             STRLEN modlen;
474 6 50         const char* modbytes_orig = SvPVbyte(modbytes_sv, modlen);
475              
476             const uint8_t* modbytes;
477 6           Newx(modbytes, modlen, uint8_t);
478 6           Copy(modbytes_orig, modbytes, modlen, uint8_t);
479              
480             IM3Module mod;
481 6           M3Result err = m3_ParseModule(env_sp->env, &mod, modbytes, modlen);
482              
483 6 50         if (err) {
484 0           Safefree(modbytes);
485 0           croak("%s", err);
486             }
487              
488 6           RETVAL = exs_new_structref(ww3_module_s, PERL_MODULE_CLASS);
489 6           ww3_module_s* mod_sp = exs_structref_ptr(RETVAL);
490              
491 12           *mod_sp = (ww3_module_s) {
492 6           .pid = getpid(),
493             .bytes = modbytes,
494             .len = modlen,
495             .module = mod,
496             };
497              
498             OUTPUT:
499             RETVAL
500              
501             void
502             DESTROY (SV* self_sv)
503             CODE:
504 6           ww3_environ_s* env_sp = exs_structref_ptr(self_sv);
505              
506 6 50         _warn_if_global_destruct(self_sv, env_sp);
    0          
507              
508 6           m3_FreeEnvironment(env_sp->env);
509              
510             # ----------------------------------------------------------------------
511              
512             MODULE = Wasm::Wasm3 PACKAGE = Wasm::Wasm3::Runtime
513              
514             void
515             get_function_arguments (SV* self_sv, SV* name_sv)
516             PPCODE:
517 5 50         _function_sig_xsub(self_sv, name_sv, m3_GetArgCount, m3_GetArgType);
    50          
    100          
518              
519              
520             void
521             get_function_returns (SV* self_sv, SV* name_sv)
522             PPCODE:
523 5 50         _function_sig_xsub(self_sv, name_sv, m3_GetRetCount, m3_GetRetType);
    50          
    100          
524              
525             int
526             run_wasi (SV* self_sv, ...)
527             CODE:
528 2           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
529              
530             IM3Function o_function;
531 2           M3Result res = m3_FindFunction( &o_function, rt_sp->rt, _WASI_FUNCNAME );
532 2 50         if (res) croak("Failed to find function %s: %s", _WASI_FUNCNAME, res);
533              
534 2           IM3Module o_module = m3_GetFunctionModule(o_function);
535             assert(o_module);
536              
537 2           m3_wasi_context_t* wasi = m3_GetModuleWasiContext(o_module);
538 2 50         if (!wasi) croak("No WASI context found on module “%s”!", m3_GetModuleName(o_module));
539              
540 2           wasi->argc = items - 1;
541              
542 2           const char* argv[wasi->argc];
543 2           wasi->argv = argv;
544              
545 5 100         for (uint32_t a=0; aargc; a++) {
546 3           argv[a] = exs_SvPVbyte_nolen(ST(1 + a));
547             }
548              
549 2           res = m3_CallArgv( o_function, 0, NULL );
550 2 50         if (res && res != m3Err_trapExit) croak("WASI: %s", res);
    50          
551              
552 2           RETVAL = wasi->exit_code;
553              
554             OUTPUT:
555             RETVAL
556              
557             void
558             call (SV* self_sv, SV* name_sv, ...)
559             PPCODE:
560 6           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
561              
562 6           const char* name = exs_SvPVutf8_nolen(name_sv);
563              
564             IM3Function o_function;
565 6           M3Result res = m3_FindFunction( &o_function, rt_sp->rt, name );
566 6 50         if (res) croak("Failed to find function %s: %s", name, res);
567              
568 6           uint32_t args_count = m3_GetArgCount(o_function);
569              
570 6           int given_args_count = items - 2;
571 6 50         if (given_args_count != args_count) {
572 0 0         croak("%s needs %d argument%s; %d given", name, args_count, (args_count > 1) ? "s" : "", given_args_count);
573             }
574              
575             /*
576             List & void contexts are always OK.
577             Scalar is OK as long as there aren’t multiple returns.
578             */
579 6           uint32_t returns_count = m3_GetRetCount(o_function);
580 6 100         if (returns_count > 1) {
581 2 50         if (GIMME_V == G_SCALAR) {
    50          
582 0           croak("%s returns %d arguments and so cannot be called in scalar context", name, returns_count);
583             }
584             }
585              
586 6           void* argptrs[args_count];
587 6           uint64_t args[args_count];
588 6           M3ValueType types[args_count];
589              
590 10 100         for (unsigned a=0; a
591 4           argptrs[a] = args + a;
592 4           types[a] = m3_GetArgType(o_function, a);
593             }
594              
595 6           _perl_svs_to_wasm3( aTHX_ &ST(2), args_count, types, args );
596              
597 6           res = m3_Call( o_function, args_count, (const void **) argptrs );
598 6 100         if (res) croak("%s(): %s", name, res);
599              
600 5 50         if (GIMME_V == G_VOID) {
    100          
601 1           XSRETURN_EMPTY;
602             }
603 4           else {
604 4           void* retptrs[returns_count];
605 4           uint64_t retvals[returns_count];
606 4           M3ValueType rettypes[returns_count];
607 13 100         for (unsigned r=0; r
608 9           retvals[r] = 0;
609 9           retptrs[r] = &retvals[r];
610 9           rettypes[r] = m3_GetRetType(o_function, r);
611             }
612              
613 4           res = m3_GetResults( o_function, returns_count, (const void **) retptrs );
614 4 50         if (res) croak("%s (m3_GetResults): %s", name, res);
615              
616 4           SV* ret_svs[returns_count];
617              
618 4           _wasm3_to_perl_svs( aTHX_ returns_count, rettypes, retvals, ret_svs );
619              
620 4 50         EXTEND(SP, returns_count);
621              
622 13 100         for (unsigned r=0; r
623 9           mPUSHs(ret_svs[r]);
624             }
625              
626 5           XSRETURN(returns_count);
627             }
628              
629             SV*
630             load_module (SV* self_sv, SV* module_sv)
631             CODE:
632 6 50         if (!SvROK(module_sv) || !sv_derived_from(module_sv, PERL_MODULE_CLASS)) {
    50          
633 0           croak("Need %s instance, not %" SVf, PERL_MODULE_CLASS, module_sv);
634             }
635              
636 6           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
637 6           ww3_module_s* mod_sp = exs_structref_ptr(module_sv);
638              
639 6           M3Result res = m3_LoadModule(rt_sp->rt, mod_sp->module);
640              
641 6 50         if (res) croak("%s", res);
642              
643 6           mod_sp->runtime_sv = SvREFCNT_inc(self_sv);
644 6           rt_sp->any_modules_linked = true;
645              
646 6           RETVAL = SvREFCNT_inc(self_sv);
647             OUTPUT:
648             RETVAL
649              
650             SV*
651             get_memory (SV* self_sv, SV* offset_sv=NULL, SV* wantlen_sv=NULL)
652             CODE:
653 4 50         UV offset = (offset_sv && SvOK(offset_sv)) ? exs_SvUV(offset_sv) : 0;
    50          
    0          
    0          
654              
655 4           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
656              
657 4 50         _WW3_CROAK_IF_NO_MODULE_LOADED(rt_sp);
658              
659             uint32_t memsize;
660 4           uint8_t* mem = m3_GetMemory(rt_sp->rt, &memsize, 0);
661              
662 4 50         if (offset > memsize) {
663 0           croak("offset (%" UVf ") exceeds memory size (%" PRIu32 ")", offset, memsize);
664             }
665              
666 4 50         UV wantlen = (wantlen_sv && SvOK(wantlen_sv)) ? exs_SvUV(wantlen_sv) : (memsize - offset);
    50          
    0          
    0          
667              
668 4 50         if (wantlen > (memsize - offset)) {
669 0           wantlen = (memsize - offset);
670             }
671              
672 4           RETVAL = newSVpvn((char*) (mem + offset), wantlen);
673              
674             OUTPUT:
675             RETVAL
676              
677             SV*
678             set_memory (SV* self_sv, SV* offset_sv, SV* new_content)
679             CODE:
680 0           UV offset = exs_SvUV(offset_sv);
681             STRLEN newlen;
682 0 0         const uint8_t* newbytes = (uint8_t*) SvPVbyte(new_content, newlen);
683              
684 0           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
685              
686 0 0         _WW3_CROAK_IF_NO_MODULE_LOADED(rt_sp);
687              
688             uint32_t memsize;
689 0           uint8_t* mem = m3_GetMemory(rt_sp->rt, &memsize, 0);
690              
691 0 0         if ((newlen + offset) > memsize) {
692 0           croak("Memory overflow: offset %" UVf " + length %zu = %" UVf ", exceeds %" PRIu32, offset, newlen, offset + newlen, memsize);
693             }
694              
695 0           Copy(newbytes, mem + offset, newlen, uint8_t);
696              
697 0           RETVAL = SvREFCNT_inc(self_sv);
698              
699             OUTPUT:
700             RETVAL
701              
702             UV
703             get_memory_size (SV* self_sv)
704             CODE:
705 0           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
706              
707 0 0         _WW3_CROAK_IF_NO_MODULE_LOADED(rt_sp);
708              
709 0           uint32_t memsize = 0;
710 0           m3_GetMemory(rt_sp->rt, &memsize, 0);
711              
712 0           RETVAL = memsize;
713              
714             OUTPUT:
715             RETVAL
716              
717             void
718             DESTROY (SV* self_sv)
719             CODE:
720 7           ww3_runtime_s* rt_sp = exs_structref_ptr(self_sv);
721              
722 7 50         _warn_if_global_destruct(self_sv, rt_sp);
    0          
723              
724 7           ww3_runtime_userdata_s* userdata = m3_GetUserData(rt_sp->rt);
725 7 50         if (userdata) {
726 13 100         for (unsigned c=0; c < userdata->coderefs_count; c++) {
727 6           SvREFCNT_dec(userdata->coderefs[c]);
728             }
729              
730 7           Safefree(userdata->coderefs);
731 7           Safefree(userdata);
732             }
733              
734 7           m3_FreeRuntime(rt_sp->rt);
735              
736 7 50         if (rt_sp->env_sv) SvREFCNT_dec(rt_sp->env_sv);
737              
738             # ----------------------------------------------------------------------
739              
740             MODULE = Wasm::Wasm3 PACKAGE = Wasm::Wasm3::Module
741              
742             void
743             run_start (SV* self_sv)
744             ALIAS:
745             compile = 1
746             CODE:
747 0           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
748 0           IM3Module mod = mod_sp->module;
749              
750 0 0         M3Result res = ix ? m3_CompileModule(mod) : m3_RunStart(mod);
751 0 0         if (res) croak("%s", res);
752              
753             const char*
754             get_name (SV* self_sv)
755             CODE:
756 0           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
757              
758 0           RETVAL = m3_GetModuleName(mod_sp->module);
759              
760             OUTPUT:
761             RETVAL
762              
763             SV*
764             set_name (SV* self_sv, SV* name_sv)
765             CODE:
766 0           const char* name = exs_SvPVutf8_nolen(name_sv);
767              
768 0           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
769              
770 0           m3_SetModuleName(mod_sp->module, name);
771              
772 0           RETVAL = SvREFCNT_inc(self_sv);
773              
774             OUTPUT:
775             RETVAL
776              
777             SV*
778             get_global_type (SV* self_sv, SV* name_sv)
779             CODE:
780 0           IM3Global i_global = _get_module_sv_global(aTHX_ self_sv, name_sv);
781              
782 0 0         if (i_global) {
783 0           RETVAL = newSVuv( m3_GetGlobalType(i_global) );
784             }
785             else {
786 0           RETVAL = &PL_sv_undef;
787             }
788              
789             OUTPUT:
790             RETVAL
791              
792             SV*
793             get_global (SV* self_sv, SV* name_sv)
794             CODE:
795 12           IM3Global i_global = _get_module_sv_global(aTHX_ self_sv, name_sv);
796              
797 12 50         if (i_global) {
798             M3TaggedValue tagged;
799 12           M3Result res = m3_GetGlobal(i_global, &tagged);
800 12 50         if (res) croak("%s", res);
801              
802 12           switch (tagged.type) {
803             case c_m3Type_none:
804 0           croak("Global “%" SVf "” is untyped!", name_sv);
805              
806             case c_m3Type_i32:
807 3           RETVAL = newSViv( tagged.value.i32 );
808 12           break;
809              
810             case c_m3Type_i64:
811 3           RETVAL = newSViv( tagged.value.i64 );
812 3           break;
813              
814             case c_m3Type_f32:
815 3           RETVAL = newSVnv( tagged.value.f32 );
816 3           break;
817              
818             case c_m3Type_f64:
819 3           RETVAL = newSVnv( tagged.value.f64 );
820 3           break;
821              
822             default:
823 0           croak("Global “%" SVf "” is of unexpected type (%d)!", name_sv, tagged.type);
824             }
825             }
826             else {
827 0           RETVAL = &PL_sv_undef;
828             }
829              
830             OUTPUT:
831             RETVAL
832              
833             SV*
834             link_function (SV* self_sv, SV* modname_sv, SV* funcname_sv, SV* signature_sv, SV* coderef)
835             CODE:
836 6           const char* modname = exs_SvPVutf8_nolen(modname_sv);
837 6           const char* funcname = exs_SvPVutf8_nolen(funcname_sv);
838 6           const char* signature = exs_SvPVutf8_nolen(signature_sv);
839              
840 6 50         if (!SvROK(coderef) || (SVt_PVCV != SvTYPE(SvRV(coderef)))) {
    50          
841 0           croak("Last argument must be a coderef, not “%" SVf "”", coderef);
842             }
843              
844 6           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
845              
846 6           IM3Runtime rt = m3_GetModuleRuntime(mod_sp->module);
847 6 50         if (!rt) croak("No runtime set up!");
848              
849 6           ww3_runtime_userdata_s* rt_userdata_p = m3_GetUserData(rt);
850 6 100         if (rt_userdata_p->coderefs_count) {
851 2 50         Renew(rt_userdata_p->coderefs, 1 + rt_userdata_p->coderefs_count, SV*);
852             }
853             else {
854 4           Newx(rt_userdata_p->coderefs, 1, SV*);
855             }
856              
857 6           rt_userdata_p->coderefs[rt_userdata_p->coderefs_count] = SvREFCNT_inc(coderef);
858 6           rt_userdata_p->coderefs_count++;
859              
860 6           M3Result res = m3_LinkRawFunctionEx(
861             mod_sp->module,
862             modname,
863             funcname,
864             signature,
865             _call_perl,
866             coderef
867             );
868 6 50         if (res) croak("%s", res);
869              
870 6           RETVAL = SvREFCNT_inc(self_sv);
871              
872             OUTPUT:
873             RETVAL
874              
875             SV*
876             set_global (SV* self_sv, SV* name_sv, SV* value_sv)
877             CODE:
878 8 50         SvGETMAGIC(value_sv);
    0          
879              
880 8 50         if (SvROK(value_sv)) {
881 0           croak("References cannot be WASM global values!");
882             }
883              
884 8           IM3Global i_global = _get_module_sv_global(aTHX_ self_sv, name_sv);
885              
886 8 50         if (!i_global) croak("Global “%" SVf "” not found!", name_sv);
887              
888 16           M3TaggedValue tagged_val = {
889 8           .type = m3_GetGlobalType(i_global),
890             };
891              
892 8           switch ( tagged_val.type ) {
893             case c_m3Type_none:
894 0           croak("Global “%" SVf "” is untyped!", name_sv);
895              
896             case c_m3Type_i32:
897 2 50         tagged_val.value.i32 = SvIV(value_sv);
898 2           break;
899              
900             case c_m3Type_i64:
901 2 50         tagged_val.value.i64 = SvIV(value_sv);
902 2           break;
903              
904             case c_m3Type_f32:
905 2 50         tagged_val.value.f32 = SvNV(value_sv);
906 2           break;
907              
908             case c_m3Type_f64:
909 2 50         tagged_val.value.f64 = SvNV(value_sv);
910 2           break;
911              
912             default:
913 0           croak("Global “%" SVf "” is of unexpected type (%d)!", name_sv, tagged_val.type);
914             }
915              
916 8           M3Result res = m3_SetGlobal(i_global, &tagged_val);
917 8 50         if (res) croak("%s", res);
918              
919 8           RETVAL = SvREFCNT_inc(self_sv);
920             OUTPUT:
921             RETVAL
922              
923             void
924             _link_wasi_default (SV* self_sv)
925             CODE:
926 1           _link_wasi_default(aTHX_ self_sv);
927              
928             void
929             _link_wasi (SV* self_sv, ...)
930             CODE:
931 1           _link_wasi(aTHX_ self_sv, items - 1, &ST(1));
932              
933             void
934             _destroy_xs (SV* self_sv)
935             CODE:
936 6           ww3_module_s* mod_sp = exs_structref_ptr(self_sv);
937              
938 6 50         _warn_if_global_destruct(self_sv, mod_sp);
    0          
939              
940 6           SV* runtime_sv = mod_sp->runtime_sv;
941              
942 6 50         if (runtime_sv) {
943 6           SvREFCNT_dec(runtime_sv);
944             }
945             else {
946 0           Safefree(mod_sp->bytes);
947 0           m3_FreeModule(mod_sp->module);
948             }