File Coverage

QuickJS.xs
Criterion Covered Total %
statement 451 482 93.5
branch 151 202 74.7
condition n/a
subroutine n/a
pod n/a
total 602 684 88.0


line stmt bran cond sub pod time code
1             #include
2              
3             #include "easyxs/easyxs.h"
4              
5             #include "quickjs/quickjs.h"
6             #include "quickjs/quickjs-libc.h"
7              
8             #define PERL_NS_ROOT "JavaScript::QuickJS"
9              
10             #define PERL_BOOLEAN_CLASS "Types::Serialiser::Boolean"
11              
12             #define PQJS_JSOBJECT_CLASS PERL_NS_ROOT "::JSObject"
13             #define PQJS_FUNCTION_CLASS PERL_NS_ROOT "::Function"
14             #define PQJS_REGEXP_CLASS PERL_NS_ROOT "::RegExp"
15             #define PQJS_DATE_CLASS PERL_NS_ROOT "::Date"
16             #define PQJS_PROMISE_CLASS PERL_NS_ROOT "::Promise"
17              
18             typedef struct {
19             JSContext *ctx;
20             pid_t pid;
21             bool added_std;
22             bool added_os;
23             bool added_helpers;
24             char* module_base_path;
25             } perl_qjs_s;
26              
27             typedef struct {
28             JSContext *ctx;
29             JSValue jsobj;
30             pid_t pid;
31             } perl_qjs_jsobj_s;
32              
33             typedef struct {
34             #ifdef MULTIPLICITY
35             tTHX aTHX;
36             #endif
37             SV** svs;
38             U32 svs_count;
39             U32 refcount;
40             bool ran_js_std_init_handlers;
41             JSValue regexp_jsvalue;
42             JSValue date_jsvalue;
43             JSValue promise_jsvalue;
44             } ctx_opaque_s;
45              
46             const char* __jstype_name_back[] = {
47             [JS_TAG_BIG_DECIMAL - JS_TAG_FIRST] = "big decimal",
48             [JS_TAG_BIG_INT - JS_TAG_FIRST] = "big integer",
49             [JS_TAG_BIG_FLOAT - JS_TAG_FIRST] = "big float",
50             [JS_TAG_SYMBOL - JS_TAG_FIRST] = "symbol",
51             [JS_TAG_MODULE - JS_TAG_FIRST] = "module",
52             [JS_TAG_OBJECT - JS_TAG_FIRST] = "object",
53             [JS_TAG_FLOAT64 - JS_TAG_FIRST] = "float64",
54              
55             /* Small hack to ensure we can always read: */
56             [99] = NULL,
57             };
58              
59             const char* const DATE_GETTER_FROM_IX[] = {
60             "toString",
61             "toUTCString",
62             "toGMTString",
63             "toISOString",
64             "toDateString",
65             "toTimeString",
66             "toLocaleString",
67             "toLocaleDateString",
68             "toLocaleTimeString",
69             "getTimezoneOffset",
70             "getTime",
71             "getFullYear",
72             "getUTCFullYear",
73             "getMonth",
74             "getUTCMonth",
75             "getDate",
76             "getUTCDate",
77             "getHours",
78             "getUTCHours",
79             "getMinutes",
80             "getUTCMinutes",
81             "getSeconds",
82             "getUTCSeconds",
83             "getMilliseconds",
84             "getUTCMilliseconds",
85             "getDay",
86             "getUTCDay",
87             "toJSON",
88             };
89              
90             const char* const DATE_SETTER_FROM_IX[] = {
91             "setTime",
92             "setMilliseconds",
93             "setUTCMilliseconds",
94             "setSeconds",
95             "setUTCSeconds",
96             "setMinutes",
97             "setUTCMinutes",
98             "setHours",
99             "setUTCHours",
100             "setDate",
101             "setUTCDate",
102             "setMonth",
103             "setUTCMonth",
104             "setFullYear",
105             "setUTCFullYear",
106             };
107              
108             #if defined _WIN32 || defined __CYGWIN__
109             # define PATH_SEPARATOR '\\'
110             #else
111             # define PATH_SEPARATOR '/'
112             #endif
113              
114             #define _jstype_name(typenum) __jstype_name_back[ typenum - JS_TAG_FIRST ]
115              
116             static SV* _JSValue_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp);
117              
118 20           static inline SV* _JSValue_special_object_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp, const char* class) {
119             assert(!*err_svp);
120              
121 20           SV* sv = exs_new_structref(perl_qjs_jsobj_s, class);
122 20           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(sv);
123              
124 20           *pqjs = (perl_qjs_jsobj_s) {
125             .ctx = ctx,
126 20           .jsobj = JS_DupValue(ctx, jsval),
127 20           .pid = getpid(),
128             };
129              
130 20           ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
131 20           ctxdata->refcount++;
132              
133 20           return sv;
134             }
135              
136 13           static inline SV* _JSValue_object_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
137             assert(!*err_svp);
138              
139             JSPropertyEnum *tab_atom;
140             uint32_t tab_atom_count;
141              
142 13           int propnameserr = JS_GetOwnPropertyNames(ctx, &tab_atom, &tab_atom_count, jsval, JS_GPN_STRING_MASK);
143              
144             PERL_UNUSED_VAR(propnameserr);
145             assert(!propnameserr);
146              
147 13           HV* hv = newHV();
148              
149 1277 100         for(int i = 0; i < tab_atom_count; i++) {
150 1265           JSValue key = JS_AtomToString(ctx, tab_atom[i].atom);
151             STRLEN strlen;
152 1265           const char* keystr = JS_ToCStringLen(ctx, &strlen, key);
153              
154 1265           JSValue value = JS_GetProperty(ctx, jsval, tab_atom[i].atom);
155              
156 1265           SV* val_sv = _JSValue_to_SV(aTHX_ ctx, value, err_svp);
157              
158 1265 100         if (val_sv) {
159 1264           hv_store(hv, keystr, -strlen, val_sv, 0);
160             }
161              
162 1265           JS_FreeCString(ctx, keystr);
163 1265           JS_FreeValue(ctx, key);
164 1265           JS_FreeValue(ctx, value);
165 1265           JS_FreeAtom(ctx, tab_atom[i].atom);
166              
167 1265 100         if (!val_sv) break;
168             }
169              
170 13           js_free(ctx, tab_atom);
171              
172 13 100         if (*err_svp) {
173 1           SvREFCNT_dec( (SV*) hv );
174 1           return NULL;
175             }
176              
177 12           return newRV_noinc((SV*) hv);
178             }
179              
180 25           static inline SV* _JSValue_array_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
181 25           JSValue jslen = JS_GetPropertyStr(ctx, jsval, "length");
182             uint32_t len;
183 25           JS_ToUint32(ctx, &len, jslen);
184 25           JS_FreeValue(ctx, jslen);
185              
186 25           AV* av = newAV();
187              
188 25 100         if (len) {
189 20           av_fill( av, len - 1 );
190 148 100         for (uint32_t i=0; i
191 129           JSValue jsitem = JS_GetPropertyUint32(ctx, jsval, i);
192              
193 129           SV* val_sv = _JSValue_to_SV(aTHX_ ctx, jsitem, err_svp);
194              
195 129 100         if (val_sv) av_store( av, i, val_sv );
196              
197 129           JS_FreeValue(ctx, jsitem);
198              
199 129 100         if (!val_sv) break;
200             }
201             }
202              
203 25 100         if (*err_svp) {
204 1           SvREFCNT_dec((SV*) av);
205 1           return NULL;
206             }
207              
208 24           return newRV_noinc((SV*) av);
209             }
210              
211             /* NO JS exceptions allowed here! */
212 1648           static SV* _JSValue_to_SV (pTHX_ JSContext* ctx, JSValue jsval, SV** err_svp) {
213             assert(!*err_svp);
214              
215             SV* RETVAL;
216              
217 1648           int tag = JS_VALUE_GET_NORM_TAG(jsval);
218              
219             assert(tag != JS_TAG_EXCEPTION);
220              
221 1648           switch (tag) {
222 1177           case JS_TAG_STRING:
223             STMT_START {
224             STRLEN strlen;
225 1177           const char* str = JS_ToCStringLen(ctx, &strlen, jsval);
226 1177           RETVAL = newSVpvn_flags(str, strlen, SVf_UTF8);
227 1177           JS_FreeCString(ctx, str);
228             } STMT_END;
229 1177           break;
230              
231 104           case JS_TAG_INT:
232 104           RETVAL = newSViv(JS_VALUE_GET_INT(jsval));
233 104           break;
234              
235 60           case JS_TAG_FLOAT64:
236 60           RETVAL = newSVnv(JS_VALUE_GET_FLOAT64(jsval));
237 60           break;
238              
239 12           case JS_TAG_BOOL:
240 12 100         RETVAL = boolSV(JS_VALUE_GET_BOOL(jsval));
241 12           break;
242              
243 223           case JS_TAG_NULL:
244             case JS_TAG_UNDEFINED:
245 223           RETVAL = &PL_sv_undef;
246 223           break;
247              
248 68           case JS_TAG_OBJECT:
249 68 100         if (JS_IsFunction(ctx, jsval)) {
250 10           load_module(
251             PERL_LOADMOD_NOIMPORT,
252             newSVpvs(PQJS_FUNCTION_CLASS),
253             NULL
254             );
255              
256 10           SV* func_sv = exs_new_structref(perl_qjs_jsobj_s, PQJS_FUNCTION_CLASS);
257 10           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(func_sv);
258              
259 10           *pqjs = (perl_qjs_jsobj_s) {
260             .ctx = ctx,
261 10           .jsobj = JS_DupValue(ctx, jsval),
262 10           .pid = getpid(),
263             };
264              
265 10           ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
266 10           ctxdata->refcount++;
267              
268 10           RETVAL = func_sv;
269             }
270 58 100         else if (JS_IsArray(ctx, jsval)) {
271 25           RETVAL = _JSValue_array_to_SV(aTHX_ ctx, jsval, err_svp);
272             }
273             else {
274              
275 33           ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
276              
277 33 100         if (JS_IsInstanceOf(ctx, jsval, ctxdata->regexp_jsvalue)) {
278 2           RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_REGEXP_CLASS);
279             }
280 31 100         else if (JS_IsInstanceOf(ctx, jsval, ctxdata->date_jsvalue)) {
281 2           RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_DATE_CLASS);
282             }
283 29 100         else if (JS_IsInstanceOf(ctx, jsval, ctxdata->promise_jsvalue)) {
284 16           RETVAL = _JSValue_special_object_to_SV(aTHX_ ctx, jsval, err_svp, PQJS_PROMISE_CLASS);
285             }
286             else {
287 13           RETVAL = _JSValue_object_to_SV(aTHX_ ctx, jsval, err_svp);
288             }
289             }
290              
291 68           break;
292              
293 4           default:
294             STMT_START {
295 4           const char* typename = _jstype_name(tag);
296              
297 4 50         if (typename) {
298 4           *err_svp = newSVpvf("Cannot convert JS %s (QuickJS tag %d) to Perl!", typename, tag);
299             }
300             else {
301 0           *err_svp = newSVpvf("Cannot convert (unexpected) JS tag value %d to Perl!", tag);
302             }
303              
304 4           return NULL;
305             } STMT_END;
306             }
307              
308 1644           return RETVAL;
309             }
310              
311 28           static inline void _ctx_add_sv(pTHX_ JSContext* ctx, SV* sv) {
312 28           ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
313              
314 28           ctxdata->svs_count++;
315              
316 28 100         if (ctxdata->svs_count == 1) {
317 15           Newx(ctxdata->svs, ctxdata->svs_count, SV*);
318             }
319             else {
320 13           Renew(ctxdata->svs, ctxdata->svs_count, SV*);
321             }
322              
323 28           ctxdata->svs[ctxdata->svs_count - 1] = SvREFCNT_inc(sv);
324 28           }
325              
326             static JSValue _sv_to_jsvalue(pTHX_ JSContext* ctx, SV* value, SV** error);
327              
328             #define _MAX_ERRSV_TO_JS_TRIES 10
329              
330             /* Kind of like _sv_to_jsvalue(), but we need to handle the case where
331             the error SV’s own conversion to a JSValue fails. In that case, we
332             warn on the 1st error, then propagate the 2nd. Repeat until there are
333             no errors.
334             */
335 9           static JSValue _sv_error_to_jsvalue(pTHX_ JSContext* ctx, SV* error) {
336 9           SV* error2 = NULL;
337              
338 9           uint32_t tries = 0;
339              
340             JSValue to_js;
341              
342             while (1) {
343 10           to_js = _sv_to_jsvalue(aTHX_ ctx, error, &error2);
344              
345 10 100         if (!error2) break;
346              
347 1           warn_sv(error);
348              
349 1           tries++;
350 1 50         if (tries > _MAX_ERRSV_TO_JS_TRIES) {
351 0           warn_sv(error2);
352 0           return JS_NewString(ctx, "Failed to convert Perl error to JavaScript after " STRINGIFY(_MAX_ERRSV_TO_JS_TRIES) " tries!");
353             }
354              
355 1           error = error2;
356 1           error2 = NULL;
357             }
358              
359 9           return to_js;
360             }
361              
362 24           static JSValue __do_perl_callback(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int jsmagic, JSValue *func_data) {
363              
364             #ifdef MULTIPLICITY
365             ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
366             pTHX = ctxdata->aTHX;
367             #endif
368              
369             PERL_UNUSED_VAR(jsmagic);
370 24           SV* cb_sv = ((SV**) func_data)[0];
371              
372 24           SV* args[argc + 1];
373 24           args[argc] = NULL;
374              
375 24           SV* error_sv = NULL;
376              
377 42 100         for (int a=0; a
378 20           args[a] = _JSValue_to_SV(aTHX_ ctx, argv[a], &error_sv);
379              
380 20 100         if (error_sv) {
381 3 100         while (--a >= 0) {
382 1           SvREFCNT_dec(args[a]);
383             }
384              
385 2           break;
386             }
387             }
388              
389 24 100         if (!error_sv) {
390 22           SV* from_perl = exs_call_sv_scalar_trapped(cb_sv, args, &error_sv);
391              
392 22 100         if (from_perl) {
393 20           JSValue to_js = _sv_to_jsvalue(aTHX_ ctx, from_perl, &error_sv);
394              
395 20           sv_2mortal(from_perl);
396              
397 20 100         if (!error_sv) return to_js;
398             }
399             }
400              
401 7           JSValue jserr = _sv_error_to_jsvalue(aTHX_ ctx, error_sv);
402              
403 7           return JS_Throw(ctx, jserr);
404             }
405              
406 57           static JSValue _sviv_to_js(pTHX_ JSContext* ctx, SV* value) {
407             if (sizeof(IV) == sizeof(int64_t)) {
408 114           return JS_NewInt64(ctx, (int64_t) SvIV(value));
409             }
410              
411             return JS_NewInt32(ctx, (int32_t) SvIV(value));
412             }
413              
414 1373           static JSValue _sv_to_jsvalue(pTHX_ JSContext* ctx, SV* value, SV** error_svp) {
415 1373 100         SvGETMAGIC(value);
    50          
416              
417 1373 100         switch ( exs_sv_type(value) ) {
    100          
    50          
    100          
    50          
    100          
    50          
418 229           case EXS_SVTYPE_UNDEF:
419 1365           return JS_NULL;
420              
421 0           case EXS_SVTYPE_BOOLEAN:
422 0           return JS_NewBool(ctx, SvTRUE(value));
423              
424 1055           case EXS_SVTYPE_STRING: STMT_START {
425             STRLEN len;
426 1055           const char* str = SvPVutf8(value, len);
427              
428 1055           return JS_NewStringLen(ctx, str, len);
429             } STMT_END;
430              
431 0           case EXS_SVTYPE_UV: STMT_START {
432 0           UV val_uv = SvUV(value);
433              
434             if (sizeof(UV) == sizeof(uint64_t)) {
435 0 0         if (val_uv > IV_MAX) {
436 0 0         return JS_NewFloat64(ctx, val_uv);
437             }
438             else {
439 0 0         return JS_NewInt64(ctx, (int64_t) val_uv);
440             }
441             }
442             else {
443             return JS_NewUint32(ctx, (uint32_t) val_uv);
444             }
445             } STMT_END;
446              
447 27           case EXS_SVTYPE_IV: STMT_START {
448 27           return _sviv_to_js(aTHX_ ctx, value);
449             } STMT_END;
450              
451 4           case EXS_SVTYPE_NV: STMT_START {
452 8           return JS_NewFloat64(ctx, (double) SvNV(value));
453             } STMT_END;
454              
455 58           case EXS_SVTYPE_REFERENCE:
456 58 100         if (sv_isobject(value)) {
457 10 100         if (sv_derived_from(value, PERL_BOOLEAN_CLASS)) {
458 4           return JS_NewBool(ctx, SvTRUE(SvRV(value)));
459             }
460 8 100         else if (sv_derived_from(value, PQJS_JSOBJECT_CLASS)) {
461 3           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(value);
462              
463 3 100         if (LIKELY(pqjs->ctx == ctx)) {
464 2           return JS_DupValue(ctx, pqjs->jsobj);
465             }
466              
467 1           *error_svp = newSVpvf("%s for QuickJS %p given to QuickJS %p!", sv_reftype(SvRV(value), 1), pqjs->ctx, ctx);
468 1           return JS_NULL;
469             }
470              
471 5           break;
472             }
473              
474 48           switch (SvTYPE(SvRV(value))) {
475 28           case SVt_PVCV:
476 28           _ctx_add_sv(aTHX_ ctx, value);
477              
478             /* A hack to store our callback via the func_data pointer: */
479 28           JSValue dummy = JS_MKPTR(JS_TAG_INT, value);
480              
481 45           return JS_NewCFunctionData(
482             ctx,
483             __do_perl_callback,
484             0, 0,
485             1, &dummy
486             );
487              
488 7           case SVt_PVAV: STMT_START {
489 7           AV* av = (AV*) SvRV(value);
490 7           JSValue jsarray = JS_NewArray(ctx);
491 14 50         JS_SetPropertyStr(ctx, jsarray, "length", JS_NewUint32(ctx, 1 + av_len(av)));
492              
493 11 100         for (int32_t i=0; i <= av_len(av); i++) {
494 5           SV** svp = av_fetch(av, i, 0);
495             assert(svp);
496             assert(*svp);
497              
498 5           JSValue jsval = _sv_to_jsvalue(aTHX_ ctx, *svp, error_svp);
499              
500 5 100         if (*error_svp) {
501 1           JS_FreeValue(ctx, jsarray);
502 1           return _sv_error_to_jsvalue(aTHX_ ctx, *error_svp);
503             }
504              
505 4           JS_SetPropertyUint32(ctx, jsarray, i, jsval);
506             }
507              
508 6           return jsarray;
509             } STMT_END;
510              
511 10           case SVt_PVHV: STMT_START {
512 10           HV* hv = (HV*) SvRV(value);
513 10           JSValue jsobj = JS_NewObject(ctx);
514              
515 10           hv_iterinit(hv);
516              
517             HE* hvent;
518 1268 100         while ( (hvent = hv_iternext(hv)) ) {
519 1259           SV* key_sv = hv_iterkeysv(hvent);
520 1259           SV* val_sv = hv_iterval(hv, hvent);
521              
522             STRLEN keylen;
523 1259           const char* key = SvPVutf8(key_sv, keylen);
524              
525 1259           JSValue jsval = _sv_to_jsvalue(aTHX_ ctx, val_sv, error_svp);
526 1259 100         if (*error_svp) {
527 1           JS_FreeValue(ctx, jsobj);
528 1           return _sv_error_to_jsvalue(aTHX_ ctx, *error_svp);
529             }
530              
531 1258           JSAtom prop = JS_NewAtomLen(ctx, key, keylen);
532              
533             /* NB: ctx takes over jsval. */
534 1258           JS_DefinePropertyValue(ctx, jsobj, prop, jsval, JS_PROP_WRITABLE);
535              
536 1258           JS_FreeAtom(ctx, prop);
537             }
538              
539 9           return jsobj;
540             } STMT_END;
541              
542 3           default:
543 3           break;
544             }
545              
546             default:
547 3           break;
548             }
549              
550 8           *error_svp = newSVpvf("Cannot convert %" SVf " to JavaScript!", value);
551              
552 8           return JS_NULL;
553             }
554              
555 42           static JSContext* _create_new_jsctx( pTHX_ JSRuntime *rt ) {
556 42           JSContext *ctx = JS_NewContext(rt);
557              
558             ctx_opaque_s* ctxdata;
559 42           Newxz(ctxdata, 1, ctx_opaque_s);
560 42           JS_SetContextOpaque(ctx, ctxdata);
561              
562 42           JSValue global = JS_GetGlobalObject(ctx);
563              
564 42           *ctxdata = (ctx_opaque_s) {
565             .refcount = 1,
566 42           .regexp_jsvalue = JS_GetPropertyStr(ctx, global, "RegExp"),
567 42           .date_jsvalue = JS_GetPropertyStr(ctx, global, "Date"),
568 42           .promise_jsvalue = JS_GetPropertyStr(ctx, global, "Promise"),
569             #ifdef MULTIPLICITY
570             .aTHX = aTHX,
571             #endif
572             };
573              
574 42           JS_FreeValue(ctx, global);
575              
576 42           return ctx;
577             }
578              
579 14           static SV* _get_exception_from_jsvalue(pTHX_ JSContext* ctx, JSValue jsret) {
580             SV* err;
581              
582 14           JSValue jserr = JS_GetException(ctx);
583             //err = _JSValue_to_SV(aTHX_ ctx, jserr);
584              
585             /* Ideal here is to capture all aspects of the error object,
586             including its `name` and members. But for now just give
587             a string.
588              
589             JSValue jslen = JS_GetPropertyStr(ctx, jserr, "name");
590             STRLEN namelen;
591             const char* namestr = JS_ToCStringLen(ctx, &namelen, jslen);
592             */
593              
594             STRLEN strlen;
595 14           const char* str = JS_ToCStringLen(ctx, &strlen, jserr);
596              
597 14           err = newSVpvn_flags(str, strlen, SVf_UTF8);
598              
599 14           JS_FreeCString(ctx, str);
600 14           JS_FreeValue(ctx, jserr);
601              
602 14           return err;
603             }
604              
605 236           static inline SV* _return_jsvalue_or_croak(pTHX_ JSContext* ctx, JSValue jsret) {
606             SV* err;
607             SV* RETVAL;
608              
609 236 100         if (JS_IsException(jsret)) {
610 14           err = _get_exception_from_jsvalue(aTHX_ ctx, jsret);
611 14           RETVAL = NULL; // silence uninitialized warning
612             }
613             else {
614 222           err = NULL;
615 222           RETVAL = _JSValue_to_SV(aTHX_ ctx, jsret, &err);
616             }
617              
618 236           JS_FreeValue(ctx, jsret);
619              
620 236 100         if (err) croak_sv(err);
621              
622 220           return RETVAL;
623             }
624              
625 54           static void _free_jsctx(pTHX_ JSContext* ctx) {
626 54           ctx_opaque_s* ctxdata = JS_GetContextOpaque(ctx);
627              
628 54 100         if (--ctxdata->refcount == 0) {
629 31           JS_FreeValue(ctx, ctxdata->regexp_jsvalue);
630 31           JS_FreeValue(ctx, ctxdata->date_jsvalue);
631 31           JS_FreeValue(ctx, ctxdata->promise_jsvalue);
632              
633 31           JSRuntime *rt = JS_GetRuntime(ctx);
634              
635 43 100         for (U32 i=0; isvs_count; i++) {
636 12           SvREFCNT_dec(ctxdata->svs[i]);
637             }
638              
639 31 50         if (ctxdata->ran_js_std_init_handlers) {
640 0           js_std_free_handlers(rt);
641             }
642              
643 31           Safefree(ctxdata);
644              
645 31           JS_FreeContext(ctx);
646              
647 31           JS_FreeRuntime(rt);
648             }
649 54           }
650              
651 8           static JSModuleDef *pqjs_module_loader(JSContext *ctx,
652             const char *module_name, void *opaque) {
653 8           char** module_base_path_p = (char**) opaque;
654              
655 8           char* module_base_path = *module_base_path_p;
656              
657             JSModuleDef *moduledef;
658              
659 8 100         if (module_base_path) {
660 1           size_t base_path_len = strlen(module_base_path);
661 1           size_t module_name_len = strlen(module_name);
662              
663 1           char real_path[1 + base_path_len + module_name_len];
664              
665 1           memcpy(real_path, module_base_path, base_path_len);
666 1           memcpy(real_path + base_path_len, module_name, module_name_len);
667 1           real_path[base_path_len + module_name_len] = 0;
668              
669 1           moduledef = js_module_loader(ctx, real_path, NULL);
670             }
671             else {
672 7           moduledef = js_module_loader(ctx, module_name, NULL);
673             }
674              
675 8           return moduledef;
676             }
677              
678             /* These must correlate to the ALIAS values below. */
679             static const char* _REGEXP_ACCESSORS[] = {
680             "flags",
681             "dotAll",
682             "global",
683             "hasIndices",
684             "ignoreCase",
685             "multiline",
686             "source",
687             "sticky",
688             "unicode",
689             "lastIndex",
690             };
691              
692             /* These must correlate to the ALIAS values below. */
693             static const char* _FUNCTION_ACCESSORS[] = {
694             "length",
695             "name",
696             };
697              
698             #define FUNC_CALL_INITIAL_ARGS 2
699              
700             // returns the SV to croak.
701 15           static SV* _svs_to_jsvars(pTHX_ JSContext* jsctx, int32_t params_count, SV** svs, JSValue* jsvars) {
702 15           SV* error = NULL;
703              
704 33 100         for (int32_t i=0; i
705 20           SV* cur_sv = svs[i];
706              
707 20           JSValue jsval = _sv_to_jsvalue(aTHX_ jsctx, cur_sv, &error);
708              
709 20 100         if (error) {
710 2 50         while (--i >= 0) {
711 0           JS_FreeValue(jsctx, jsvars[i]);
712             }
713              
714 2           break;
715             }
716              
717 18           jsvars[i] = jsval;
718             }
719              
720 15           return error;
721             }
722              
723 5           static void _import_module_to_global(pTHX_ JSContext* jsctx, const char* modname) {
724 5           const char* jstemplate = "import * as theModule from '%s';\n"
725             "globalThis.%s = theModule;\n";
726              
727 5           char js[255] = { 0 };
728 5           snprintf(js, 255, jstemplate, modname, modname);
729              
730 5           JSValue val = JS_Eval(jsctx, js, strlen(js), "", JS_EVAL_TYPE_MODULE);
731 5 50         if (JS_IsException(val)) {
732 0           SV* errsv = _get_exception_from_jsvalue(aTHX_ jsctx, val);
733 0 0         if (errsv) {
734 0           croak_sv(errsv);
735             }
736              
737 0           croak("Got empty exception??");
738             }
739              
740 5           JS_FreeValue(jsctx, val);
741 5           }
742              
743             /* ---------------------------------------------------------------------- */
744              
745             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS
746              
747             PROTOTYPES: DISABLE
748              
749             SV*
750             _new (SV* classname_sv)
751             CODE:
752 42           JSRuntime *rt = JS_NewRuntime();
753 42           JS_SetHostPromiseRejectionTracker(rt, js_std_promise_rejection_tracker, NULL);
754 42           JS_SetModuleLoaderFunc(rt, NULL, js_module_loader, NULL);
755              
756 42           JSContext *ctx = _create_new_jsctx(aTHX_ rt);
757              
758 42           RETVAL = exs_new_structref(perl_qjs_s, SvPVbyte_nolen(classname_sv));
759 42           perl_qjs_s* pqjs = exs_structref_ptr(RETVAL);
760              
761 42           *pqjs = (perl_qjs_s) {
762             .ctx = ctx,
763 42           .pid = getpid(),
764             };
765              
766 42           JS_SetModuleLoaderFunc(
767             rt,
768             NULL,
769             pqjs_module_loader,
770 42           &pqjs->module_base_path
771             );
772              
773 42           JS_SetRuntimeInfo(rt, PERL_NS_ROOT);
774              
775             OUTPUT:
776             RETVAL
777              
778             void
779             DESTROY (SV* self_sv)
780             CODE:
781 42           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
782              
783 42 50         if (PL_dirty && pqjs->pid == getpid()) {
    0          
784 0           warn("DESTROYing %" SVf " at global destruction; memory leak likely!\n", self_sv);
785             }
786              
787 42 100         if (pqjs->module_base_path) Safefree(pqjs->module_base_path);
788              
789 42           _free_jsctx(aTHX_ pqjs->ctx);
790              
791             SV*
792             _configure (SV* self_sv, SV* max_stack_size_sv, SV* memory_limit_sv, SV* gc_threshold_sv)
793             CODE:
794 8           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
795              
796 8           JSRuntime *rt = JS_GetRuntime(pqjs->ctx);
797              
798 8 100         if (SvOK(max_stack_size_sv)) {
799 4           JS_SetMaxStackSize(rt, exs_SvUV(max_stack_size_sv));
800             }
801              
802 6 100         if (SvOK(memory_limit_sv)) {
803 4           JS_SetMemoryLimit(rt, exs_SvUV(memory_limit_sv));
804             }
805              
806 4 50         if (SvOK(gc_threshold_sv)) {
807 4           JS_SetGCThreshold(rt, exs_SvUV(gc_threshold_sv));
808             }
809              
810 2           RETVAL = SvREFCNT_inc(self_sv);
811              
812             OUTPUT:
813             RETVAL
814              
815             SV*
816             std (SV* self_sv)
817             ALIAS:
818             os = 1
819             helpers = 2
820             CODE:
821 6           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
822              
823 6           switch (ix) {
824 4           case 0:
825 4 50         if (!pqjs->added_std) {
826 4           js_init_module_std(pqjs->ctx, "std");
827 4           _import_module_to_global(aTHX_ pqjs->ctx, "std");
828 4           pqjs->added_std = true;
829             }
830              
831 4           break;
832 1           case 1:
833 1 50         if (!pqjs->added_os) {
834 1           js_init_module_os(pqjs->ctx, "os");
835 1           pqjs->added_os = true;
836              
837 1           ctx_opaque_s* ctxdata = JS_GetContextOpaque(pqjs->ctx);
838              
839 1 50         if (!ctxdata->ran_js_std_init_handlers) {
840 1           JSRuntime *rt = JS_GetRuntime(pqjs->ctx);
841 1           js_std_init_handlers(rt);
842              
843 1           ctxdata->ran_js_std_init_handlers = true;
844             }
845              
846 1           _import_module_to_global(aTHX_ pqjs->ctx, "os");
847             }
848              
849 1           break;
850 1           case 2:
851 1 50         if (!pqjs->added_helpers) {
852 1           js_std_add_helpers(pqjs->ctx, 0, NULL);
853 1           pqjs->added_helpers = true;
854             }
855              
856 1           break;
857              
858 0           default:
859 0           croak("%s: Bad XS alias: %d\n", __func__, (int) ix);
860             }
861              
862 6           RETVAL = SvREFCNT_inc(self_sv);
863              
864             OUTPUT:
865             RETVAL
866              
867             SV*
868             unset_module_base (SV* self_sv)
869             CODE:
870 1           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
871              
872 1 50         if (pqjs->module_base_path) {
873 0           Safefree(pqjs->module_base_path);
874 0           pqjs->module_base_path = NULL;
875             }
876              
877 1           RETVAL = SvREFCNT_inc(self_sv);
878             OUTPUT:
879             RETVAL
880              
881             SV*
882             set_module_base (SV* self_sv, SV* path_sv)
883             CODE:
884 1 50         if (!SvOK(path_sv)) croak("Give a path! (Did you want unset_module_base?)");
885              
886 1           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
887              
888 1           const char* path = exs_SvPVbyte_nolen(path_sv);
889              
890 1           size_t path_len = strlen(path);
891              
892 1 50         if (pqjs->module_base_path) {
893 0           Renew(pqjs->module_base_path, 2 + path_len, char);
894             }
895             else {
896 1           Newx(pqjs->module_base_path, 2 + path_len, char);
897             }
898              
899 1           Copy(path, pqjs->module_base_path, 2 + path_len, char);
900              
901             /** If the given path is “/foo/bar”, we store “/foo/bar/”.
902             This means if “/foo/bar/” is given we store “/foo/bar//”,
903             which is ugly but should work on all supported platforms.
904             */
905 1           pqjs->module_base_path[path_len] = PATH_SEPARATOR;
906 1           pqjs->module_base_path[1 + path_len] = 0;
907              
908 1           RETVAL = SvREFCNT_inc(self_sv);
909             OUTPUT:
910             RETVAL
911              
912             SV*
913             set_globals (SV* self_sv, ...)
914             CODE:
915 42 50         if (items < 2) croak("Need at least 1 key/value pair.");
916              
917 42 50         if (!(items % 2)) croak("Need an even list of key/value pairs.");
918              
919 42           I32 valscount = (items - 1) >> 1;
920              
921 42           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
922              
923             SV* jsname_sv, *value_sv;
924              
925 42           SV* error = NULL;
926              
927 42           JSAtom jsnames[valscount];
928 42           JSValue jsvals[valscount];
929              
930 87 100         for (int i=0; i < valscount; i++) {
931 47           jsname_sv = ST( 1 + (i << 1) );
932 47           value_sv = ST( 2 + (i << 1) );
933              
934             STRLEN jsnamelen;
935 47           const char* jsname_str = SvPVutf8(jsname_sv, jsnamelen);
936              
937 47           JSValue jsval = _sv_to_jsvalue(aTHX_ pqjs->ctx, value_sv, &error);
938              
939 47 100         if (error) {
940 2 50         while (i-- > 0) {
941 0           JS_FreeAtom(pqjs->ctx, jsnames[i]);
942 0           JS_FreeValue(pqjs->ctx, jsvals[i]);
943             }
944              
945 2           croak_sv(error);
946             }
947              
948 45           jsnames[i] = JS_NewAtomLen(pqjs->ctx, jsname_str, jsnamelen);
949 45           jsvals[i] = jsval;
950             }
951              
952 40           JSValue jsglobal = JS_GetGlobalObject(pqjs->ctx);
953              
954 85 100         for (int i=0; i < valscount; i++) {
955             /* NB: ctx takes over jsval. */
956 45           JS_DefinePropertyValue(pqjs->ctx, jsglobal, jsnames[i], jsvals[i], JS_PROP_WRITABLE);
957 45           JS_FreeAtom(pqjs->ctx, jsnames[i]);
958             }
959              
960 40           JS_FreeValue(pqjs->ctx, jsglobal);
961              
962 40           RETVAL = SvREFCNT_inc(self_sv);
963              
964             OUTPUT:
965             RETVAL
966              
967             SV*
968             eval (SV* self_sv, SV* js_code_sv)
969             ALIAS:
970             eval_module = 1
971             CODE:
972 129           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
973 129           JSContext *ctx = pqjs->ctx;
974              
975             STRLEN js_code_len;
976 129           const char* js_code = SvPVutf8(js_code_sv, js_code_len);
977              
978 129           int eval_flags = ix ? JS_EVAL_TYPE_MODULE : JS_EVAL_TYPE_GLOBAL;
979 129           eval_flags |= JS_EVAL_FLAG_STRICT;
980              
981 129           JSValue jsret = JS_Eval(ctx, js_code, js_code_len, "", eval_flags);
982              
983             // In eval_module()’s case we want to croak if there was
984             // an exception. If not we’ll just discard the return value
985 129           RETVAL = _return_jsvalue_or_croak(aTHX_ ctx, jsret);
986              
987             OUTPUT:
988             RETVAL
989              
990             SV*
991             await (SV* self_sv)
992             CODE:
993 7           perl_qjs_s* pqjs = exs_structref_ptr(self_sv);
994 7           JSContext *ctx = pqjs->ctx;
995              
996 7           js_std_loop(ctx);
997              
998 7           RETVAL = SvREFCNT_inc(self_sv);
999              
1000             OUTPUT:
1001             RETVAL
1002              
1003             # ----------------------------------------------------------------------
1004              
1005             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Date
1006              
1007             SV*
1008             setTime (SV* self_sv, SV* num_sv)
1009             ALIAS:
1010             setMilliseconds = 1
1011             setUTCMilliseconds = 2
1012             setSeconds = 3
1013             setUTCSeconds = 4
1014             setMinutes = 5
1015             setUTCMinutes = 6
1016             setHours = 7
1017             setUTCHours = 8
1018             setDate = 9
1019             setUTCDate = 10
1020             setMonth = 11
1021             setUTCMonth = 12
1022             setFullYear = 13
1023             setUTCFullYear = 14
1024              
1025             CODE:
1026 30           const char* setter_name = DATE_SETTER_FROM_IX[ix];
1027              
1028 30           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1029 30           JSContext *ctx = pqjs->ctx;
1030              
1031 30           JSAtom prop = JS_NewAtom(ctx, setter_name);
1032              
1033             JSValue arg;
1034             NV nvval;
1035              
1036 30 50         switch (exs_sv_type(num_sv)) {
    50          
    50          
    100          
    50          
    50          
    0          
    100          
1037              
1038             // In 32-bit perls setTime() values often overflow IV_MAX.
1039 15           case EXS_SVTYPE_NV:
1040             case EXS_SVTYPE_STRING:
1041 15           nvval = SvNV(num_sv);
1042 15 50         if (nvval > IV_MAX || nvval < IV_MIN) {
    50          
1043 0           arg = JS_NewFloat64(ctx, (double) nvval);
1044 0           break;
1045             }
1046              
1047             // fallthrough
1048              
1049             default:
1050 30           arg = _sviv_to_js(aTHX_ ctx, num_sv);
1051             }
1052              
1053 30           JSValue jsret = JS_Invoke(
1054             ctx,
1055             pqjs->jsobj,
1056             prop,
1057             1,
1058             &arg
1059             );
1060              
1061 30           JS_FreeAtom(ctx, prop);
1062 30           JS_FreeValue(ctx, arg);
1063              
1064 30           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1065              
1066             OUTPUT:
1067             RETVAL
1068              
1069             SV*
1070             toString (SV* self_sv, ...)
1071             ALIAS:
1072             toUTCString = 1
1073             toGMTString = 2
1074             toISOString = 3
1075             toDateString = 4
1076             toTimeString = 5
1077             toLocaleString = 6
1078             toLocaleDateString = 7
1079             toLocaleTimeString = 8
1080             getTimezoneOffset = 9
1081             getTime = 10
1082             getFullYear = 11
1083             getUTCFullYear = 12
1084             getMonth = 13
1085             getUTCMonth = 14
1086             getDate = 15
1087             getUTCDate = 16
1088             getHours = 17
1089             getUTCHours = 18
1090             getMinutes = 19
1091             getUTCMinutes = 20
1092             getSeconds = 21
1093             getUTCSeconds = 22
1094             getMilliseconds = 23
1095             getUTCMilliseconds = 24
1096             getDay = 25
1097             getUTCDay = 26
1098             toJSON = 27
1099             CODE:
1100 59           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1101 59           JSContext *ctx = pqjs->ctx;
1102              
1103 59           JSAtom prop = JS_NewAtom(ctx, DATE_GETTER_FROM_IX[ix]);
1104              
1105             JSValue jsret;
1106              
1107 59 100         if (items > 1) {
1108 1           int params_count = items - 1;
1109              
1110 1           JSValue jsargs[params_count];
1111              
1112 1           SV* error = _svs_to_jsvars(aTHX_ ctx, params_count, &ST(1), jsargs);
1113 1 50         if (error) {
1114 1           JS_FreeAtom(pqjs->ctx, prop);
1115 1           croak_sv(error);
1116             }
1117              
1118 0           jsret = JS_Invoke(
1119             ctx,
1120             pqjs->jsobj,
1121             prop,
1122             params_count,
1123             jsargs
1124             );
1125              
1126 0 0         for (uint32_t i=0; i
1127 0           JS_FreeValue(ctx, jsargs[i]);
1128             }
1129             }
1130             else {
1131 58           jsret = JS_Invoke(
1132             ctx,
1133             pqjs->jsobj,
1134             prop,
1135             0,
1136             NULL
1137             );
1138             }
1139              
1140 58           JS_FreeAtom(ctx, prop);
1141              
1142 58           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1143              
1144             OUTPUT:
1145             RETVAL
1146              
1147             # ----------------------------------------------------------------------
1148              
1149             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::RegExp
1150              
1151             SV*
1152             exec (SV* self_sv, SV* specimen_sv)
1153             ALIAS:
1154             test = 1
1155             CODE:
1156 4           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1157 4           JSContext *ctx = pqjs->ctx;
1158              
1159             STRLEN specimen_len;
1160 4           const char* specimen = SvPVutf8(specimen_sv, specimen_len);
1161              
1162             /* TODO: optimize? */
1163 4 100         JSAtom prop = JS_NewAtom(ctx, ix ? "test" : "exec");
1164              
1165 4           JSValue specimen_js = JS_NewStringLen(ctx, specimen, specimen_len);
1166              
1167 4           JSValue jsret = JS_Invoke(
1168             ctx,
1169             pqjs->jsobj,
1170             prop,
1171             1,
1172             &specimen_js
1173             );
1174              
1175 4           JS_FreeValue(ctx, specimen_js);
1176 4           JS_FreeAtom(ctx, prop);
1177              
1178 4           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1179              
1180             OUTPUT:
1181             RETVAL
1182              
1183             SV*
1184             flags( SV* self_sv)
1185             ALIAS:
1186             dotAll = 1
1187             global = 2
1188             hasIndices = 3
1189             ignoreCase = 4
1190             multiline = 5
1191             source = 6
1192             sticky = 7
1193             unicode = 8
1194             lastIndex = 9
1195             CODE:
1196 8           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1197              
1198 8           JSValue myret = JS_GetPropertyStr(pqjs->ctx, pqjs->jsobj, _REGEXP_ACCESSORS[ix]);
1199              
1200 8           SV* err = NULL;
1201              
1202 8           RETVAL = _JSValue_to_SV(aTHX_ pqjs->ctx, myret, &err);
1203              
1204 8           JS_FreeValue(pqjs->ctx, myret);
1205              
1206 8 50         if (err) croak_sv(err);
1207              
1208             OUTPUT:
1209             RETVAL
1210              
1211             # ----------------------------------------------------------------------
1212              
1213             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::JSObject
1214              
1215             void
1216             DESTROY( SV* self_sv )
1217             CODE:
1218 12           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1219              
1220 12 50         if (PL_dirty && pqjs->pid == getpid()) {
    0          
1221 0           warn("DESTROYing %" SVf " at global destruction; memory leak likely!\n", self_sv);
1222             }
1223              
1224 12           JS_FreeValue(pqjs->ctx, pqjs->jsobj);
1225              
1226 12           _free_jsctx(aTHX_ pqjs->ctx);
1227              
1228             # ----------------------------------------------------------------------
1229              
1230             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Function
1231              
1232             SV*
1233             _give_self( SV* self_sv, ... )
1234             CODE:
1235 1           RETVAL = SvREFCNT_inc(self_sv);
1236             OUTPUT:
1237             RETVAL
1238              
1239             SV*
1240             length( SV* self_sv)
1241             ALIAS:
1242             name = 1
1243             CODE:
1244 4           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1245              
1246 4           JSValue myret = JS_GetPropertyStr(pqjs->ctx, pqjs->jsobj, _FUNCTION_ACCESSORS[ix]);
1247              
1248 4           SV* err = NULL;
1249              
1250 4           RETVAL = _JSValue_to_SV(aTHX_ pqjs->ctx, myret, &err);
1251              
1252 4           JS_FreeValue(pqjs->ctx, myret);
1253              
1254 4 50         if (err) croak_sv(err);
1255              
1256             OUTPUT:
1257             RETVAL
1258              
1259             SV*
1260             call( SV* self_sv, SV* this_sv=&PL_sv_undef, ... )
1261             CODE:
1262 10           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1263              
1264 10           U32 params_count = items - FUNC_CALL_INITIAL_ARGS;
1265              
1266 10           SV* error = NULL;
1267              
1268 10           JSValue thisjs = _sv_to_jsvalue(aTHX_ pqjs->ctx, this_sv, &error);
1269 10 100         if (error) croak_sv(error);
1270              
1271 9           JSValue jsvars[params_count];
1272              
1273 9           error = _svs_to_jsvars( aTHX_ pqjs->ctx, params_count, &ST(FUNC_CALL_INITIAL_ARGS), jsvars );
1274 9 100         if (error) {
1275 1           JS_FreeValue(pqjs->ctx, thisjs);
1276 1           croak_sv(error);
1277             }
1278              
1279 8           JSValue jsret = JS_Call(pqjs->ctx, pqjs->jsobj, thisjs, params_count, jsvars);
1280              
1281 8           JS_FreeValue(pqjs->ctx, thisjs);
1282              
1283 16 100         for (uint32_t i=0; i
1284 8           JS_FreeValue(pqjs->ctx, jsvars[i]);
1285             }
1286              
1287 8           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1288              
1289             OUTPUT:
1290             RETVAL
1291              
1292             # ----------------------------------------------------------------------
1293              
1294             MODULE = JavaScript::QuickJS PACKAGE = JavaScript::QuickJS::Promise
1295              
1296             SV*
1297             then( SV* self_sv, SV* on_success=&PL_sv_undef, SV* on_failure=&PL_sv_undef )
1298             CODE:
1299             PERL_UNUSED_ARG(on_success);
1300             PERL_UNUSED_ARG(on_failure);
1301              
1302 5           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1303              
1304 5           SV* error = NULL;
1305              
1306             JSValue jsvars[2];
1307 5           int callbacks_count = sizeof(jsvars) / sizeof(jsvars[0]);
1308              
1309 5           error = _svs_to_jsvars( aTHX_
1310             pqjs->ctx,
1311             callbacks_count,
1312 5           &ST(1),
1313             jsvars
1314             );
1315 5 50         if (error) {
1316 0           croak_sv(error);
1317             }
1318              
1319 5           JSAtom method_name = JS_NewAtom(pqjs->ctx, "then");
1320              
1321 5           JSValue jsret = JS_Invoke(
1322             pqjs->ctx,
1323             pqjs->jsobj,
1324             method_name,
1325             callbacks_count,
1326             jsvars
1327             );
1328              
1329 5           JS_FreeAtom(pqjs->ctx, method_name);
1330              
1331 15 100         for (uint32_t i=0; i
1332 10           JS_FreeValue(pqjs->ctx, jsvars[i]);
1333             }
1334              
1335 5           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1336              
1337             OUTPUT:
1338             RETVAL
1339              
1340             SV*
1341             catch( SV* self_sv, SV* callback=&PL_sv_undef)
1342             ALIAS:
1343             finally = 1
1344             CODE:
1345 2           perl_qjs_jsobj_s* pqjs = exs_structref_ptr(self_sv);
1346              
1347 2           SV* error = NULL;
1348              
1349 2           JSValue callbackjs = _sv_to_jsvalue(aTHX_ pqjs->ctx, callback, &error);
1350 2 50         if (error) croak_sv(error);
1351              
1352 2 100         JSAtom method_name = JS_NewAtom(pqjs->ctx, ix ? "finally" : "catch");
1353              
1354 2           JSValue jsret = JS_Invoke(
1355             pqjs->ctx,
1356             pqjs->jsobj,
1357             method_name,
1358             1,
1359             &callbackjs
1360             );
1361              
1362 2           JS_FreeAtom(pqjs->ctx, method_name);
1363 2           JS_FreeValue(pqjs->ctx, callbackjs);
1364              
1365 2           RETVAL = _return_jsvalue_or_croak(aTHX_ pqjs->ctx, jsret);
1366              
1367             OUTPUT:
1368             RETVAL