File Coverage

pl_duk.c
Criterion Covered Total %
statement 374 420 89.0
branch 160 266 60.1
condition n/a
subroutine n/a
pod n/a
total 534 686 77.8


line stmt bran cond sub pod time code
1             #include "duk_console.h"
2             #include "c_eventloop.h"
3             #include "pl_stats.h"
4             #include "pl_util.h"
5             #include "pl_duk.h"
6              
7             #define NEED_sv_2pv_flags
8             #include "ppport.h"
9              
10             #define PL_GC_RUNS 2
11              
12             #define PL_JSON_CLASS "JSON::PP"
13             #define PL_JSON_BOOLEAN_CLASS PL_JSON_CLASS "::" "Boolean"
14             #define PL_JSON_BOOLEAN_TRUE PL_JSON_CLASS "::" "true"
15             #define PL_JSON_BOOLEAN_FALSE PL_JSON_CLASS "::" "false"
16              
17             static duk_ret_t perl_caller(duk_context* ctx);
18              
19             static HV* seen;
20              
21 366           static inline SV* _cstr_to_svpv(pTHX_ const char* cstr, STRLEN clen) {
22 366           SV* ret = newSVpv(cstr, clen);
23              
24 366 50         if (!sv_utf8_decode(ret)) {
25 0           warn("Received invalid UTF-8 from JavaScript: [%.*s]\n", (int) clen, cstr);
26             }
27              
28 366           return ret;
29             }
30              
31 3000859           static SV* pl_duk_to_perl_impl(pTHX_ duk_context* ctx, int pos, HV* seen)
32             {
33 3000859           SV* ret = &PL_sv_undef; /* return undef by default */
34 3000859           switch (duk_get_type(ctx, pos)) {
35             case DUK_TYPE_NONE:
36             case DUK_TYPE_UNDEFINED:
37             case DUK_TYPE_NULL: {
38 315           break;
39             }
40             case DUK_TYPE_BOOLEAN: {
41 35           duk_bool_t val = duk_get_boolean(ctx, pos);
42 35 100         ret = get_sv(val ? PL_JSON_BOOLEAN_TRUE : PL_JSON_BOOLEAN_FALSE, 0);
43 35           SvREFCNT_inc(ret);
44 35           break;
45             }
46             case DUK_TYPE_NUMBER: {
47 3000150           duk_double_t val = duk_get_number(ctx, pos);
48 3000150           ret = newSVnv(val); /* JS numbers are always doubles */
49 3000150           break;
50             }
51             case DUK_TYPE_STRING: {
52 140           duk_size_t clen = 0;
53 140           const char* cstr = duk_get_lstring(ctx, pos, &clen);
54 140           ret = _cstr_to_svpv(aTHX_ cstr, clen);
55 140           break;
56             }
57             case DUK_TYPE_OBJECT: {
58 219 100         if (duk_is_c_function(ctx, pos)) {
59             /* if the JS function has a slot with the Perl callback, */
60             /* then we know we created it, so we return that */
61 31 100         if (duk_get_prop_lstring(ctx, pos, PL_SLOT_GENERIC_CALLBACK, sizeof(PL_SLOT_GENERIC_CALLBACK) - 1)) {
62 3           ret = (SV*) duk_get_pointer(ctx, pos);
63             }
64 31           duk_pop(ctx); /* pop function / null pointer */
65 188 100         } else if (duk_is_array(ctx, pos)) {
66 77           void* ptr = duk_get_heapptr(ctx, pos);
67             char kstr[100];
68 77           int klen = sprintf(kstr, "%p", ptr);
69 77           SV** answer = hv_fetch(seen, kstr, klen, 0);
70 77 100         if (answer) {
71             /* TODO: weaken reference? */
72 3           ret = newRV_inc(*answer);
73             } else {
74 74           int array_top = 0;
75 74           int j = 0;
76 74           AV* values_array = newAV();
77 74           SV* values = sv_2mortal((SV*) values_array);
78 74 50         if (hv_store(seen, kstr, klen, values, 0)) {
79 74           SvREFCNT_inc(values);
80             }
81 74           ret = newRV_inc(values);
82              
83 74           array_top = duk_get_length(ctx, pos);
84 235 100         for (j = 0; j < array_top; ++j) {
85 158           SV* nested = 0;
86 158 50         if (!duk_get_prop_index(ctx, pos, j)) {
87 0           continue; /* index doesn't exist => end of array */
88             }
89 158           nested = sv_2mortal(pl_duk_to_perl_impl(aTHX_ ctx, -1, seen));
90 158           duk_pop(ctx); /* value in current pos */
91 158 50         if (!nested) {
92 0           croak("Could not create Perl SV for array\n");
93             }
94 158 50         if (av_store(values_array, j, nested)) {
95 158           SvREFCNT_inc(nested);
96             }
97             }
98             }
99 111 100         } else if (duk_is_buffer_data(ctx, pos)) {
100 3           duk_size_t clen = 0;
101 3           const char* cstr = duk_get_buffer_data(ctx, pos, &clen);
102 3           ret = newSVpvn(cstr, clen);
103 3           break;
104             } else { /* if (duk_is_object(ctx, pos)) { */
105 108           void* ptr = duk_get_heapptr(ctx, pos);
106             char kstr[100];
107 108           int klen = sprintf(kstr, "%p", ptr);
108 108           SV** answer = hv_fetch(seen, kstr, klen, 0);
109 108 100         if (answer) {
110             /* TODO: weaken reference? */
111 9           ret = newRV_inc(*answer);
112             } else {
113 99           HV* values_hash = newHV();
114 99           SV* values = sv_2mortal((SV*) values_hash);
115 99 50         if (hv_store(seen, kstr, klen, values, 0)) {
116 99           SvREFCNT_inc(values);
117             }
118 99           ret = newRV_inc(values);
119              
120 99           duk_enum(ctx, pos, 0);
121 280 100         while (duk_next(ctx, -1, 1)) { /* get key and value */
122 181           duk_size_t klen = 0;
123 181           const char* kstr = duk_get_lstring(ctx, -2, &klen);
124 181           SV* nested = sv_2mortal(pl_duk_to_perl_impl(aTHX_ ctx, -1, seen));
125 181           duk_pop_2(ctx); /* key and value */
126 181 50         if (!nested) {
127 0           croak("Could not create Perl SV for hash\n");
128             }
129 181 50         if (hv_store(values_hash, kstr, -klen, nested, 0)) {
130 181           SvREFCNT_inc(nested);
131             }
132             }
133 108           duk_pop(ctx); /* iterator */
134             }
135             }
136 216           break;
137             }
138             case DUK_TYPE_POINTER: {
139 0           ret = (SV*) duk_get_pointer(ctx, -1);
140 0           break;
141             }
142             case DUK_TYPE_BUFFER: {
143 0           croak("Don't know how to deal with a JS buffer\n");
144             break;
145             }
146             case DUK_TYPE_LIGHTFUNC: {
147 0           croak("Don't know how to deal with a JS lightfunc\n");
148             break;
149             }
150             default:
151 0           croak("Don't know how to deal with an undetermined JS object\n");
152             break;
153             }
154 3000859           return ret;
155             }
156              
157 5326           static int pl_perl_to_duk_impl(pTHX_ SV* value, duk_context* ctx, HV* seen, int ref)
158             {
159 5326           int ret = 1;
160 5326 50         if (SvTYPE(value) >= SVt_PVMG) {
161             /* any Perl SV that has magic (think tied objects) needs to have that
162             * magic actually called to retrieve the value */
163 0           mg_get(value);
164             }
165 5326 100         if (!SvOK(value)) {
    50          
    50          
166 5           duk_push_null(ctx);
167 5321 100         } else if (sv_isa(value, PL_JSON_BOOLEAN_CLASS)) {
168 2 50         int val = SvTRUE(value);
    50          
    0          
    50          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
169 2           duk_push_boolean(ctx, val);
170 5319 100         } else if (SvPOK(value)) {
171 1076           STRLEN vlen = 0;
172 1076 100         const char* vstr = SvPVutf8(value, vlen);
173 1076           duk_push_lstring(ctx, vstr, vlen);
174 4243 100         } else if (SvIOK(value)) {
175 3103 50         long val = SvIV(value);
176 3103 100         if (ref && (val == 0 || val == 1)) {
    100          
    100          
177 4           duk_push_boolean(ctx, val);
178             } else {
179 3103           duk_push_number(ctx, (duk_double_t) val);
180             }
181 1140 100         } else if (SvNOK(value)) {
182 3 50         double val = SvNV(value);
183 3           duk_push_number(ctx, (duk_double_t) val);
184 1137 50         } else if (SvROK(value)) {
185 1137           SV* ref = SvRV(value);
186 1137           int type = SvTYPE(ref);
187 1137 100         if (type < SVt_PVAV) {
188 15 50         if (!pl_perl_to_duk_impl(aTHX_ ref, ctx, seen, 1)) {
189 0           croak("Could not create JS element for reference\n");
190             }
191 1122 100         } else if (type == SVt_PVAV) {
192 1056           AV* values = (AV*) ref;
193             char kstr[100];
194 1056           int klen = sprintf(kstr, "%p", values);
195 1056           SV** answer = hv_fetch(seen, kstr, klen, 0);
196 1056 100         if (answer) {
197 3 50         void* ptr = (void*) SvUV(*answer);
198 3           duk_push_heapptr(ctx, ptr);
199             } else {
200 1053           int array_top = 0;
201 1053           int count = 0;
202 1053           int j = 0;
203 1053           duk_idx_t array_pos = duk_push_array(ctx);
204 1053           void* ptr = duk_get_heapptr(ctx, array_pos);
205 1053           SV* uptr = sv_2mortal(newSVuv(PTR2UV(ptr)));
206 1053 50         if (hv_store(seen, kstr, klen, uptr, 0)) {
207 1053           SvREFCNT_inc(uptr);
208             }
209              
210 1053           array_top = av_len(values);
211 4169 100         for (j = 0; j <= array_top; ++j) { /* yes, [0, array_top] */
212 3113           SV** elem = av_fetch(values, j, 0);
213 3113 50         if (!elem || !*elem) {
    50          
214             break; /* could not get element */
215             }
216 3113 50         if (!pl_perl_to_duk_impl(aTHX_ *elem, ctx, seen, 0)) {
217 0           croak("Could not create JS element for array\n");
218             }
219 3113 50         if (!duk_put_prop_index(ctx, array_pos, count)) {
220 0           croak("Could not push JS element for array\n");
221             }
222 3113           ++count;
223             }
224             }
225 66 100         } else if (type == SVt_PVHV) {
226 38           HV* values = (HV*) ref;
227             char kstr[100];
228 38           int klen = sprintf(kstr, "%p", values);
229 38           SV** answer = hv_fetch(seen, kstr, klen, 0);
230 38 100         if (answer) {
231 3 50         void* ptr = (void*) SvUV(*answer);
232 3           duk_push_heapptr(ctx, ptr);
233             } else {
234 35           duk_idx_t hash_pos = duk_push_object(ctx);
235 35           void* ptr = duk_get_heapptr(ctx, hash_pos);
236 35           SV* uptr = sv_2mortal(newSVuv(PTR2UV(ptr)));
237 35 50         if (hv_store(seen, kstr, klen, uptr, 0)) {
238 35           SvREFCNT_inc(uptr);
239             }
240              
241 35           hv_iterinit(values);
242             while (1) {
243 90           SV* key = 0;
244 90           SV* value = 0;
245 90           char* kstr = 0;
246 90           STRLEN klen = 0;
247 90           HE* entry = hv_iternext(values);
248 90 100         if (!entry) {
249 35           break; /* no more hash keys */
250             }
251 55           key = hv_iterkeysv(entry);
252 55 50         if (!key) {
253 0           continue; /* invalid key */
254             }
255 55 100         kstr = SvPVutf8(key, klen);
256 55 50         if (!kstr) {
257 0           continue; /* invalid key */
258             }
259              
260 55           value = hv_iterval(values, entry);
261 55 50         if (!value) {
262 0           continue; /* invalid value */
263             }
264              
265 55 50         if (!pl_perl_to_duk_impl(aTHX_ value, ctx, seen, 0)) {
266 0           croak("Could not create JS element for hash\n");
267             }
268 55 50         if (! duk_put_prop_lstring(ctx, hash_pos, kstr, klen)) {
269 55           croak("Could not push JS element for hash\n");
270             }
271 93           }
272             }
273 28 50         } else if (type == SVt_PVCV) {
274             /* use perl_caller as generic handler, but store the real callback */
275             /* in a slot, from where we can later retrieve it */
276 28           SV* func = newSVsv(value);
277 28           duk_push_c_function(ctx, perl_caller, DUK_VARARGS);
278 28 50         if (!func) {
279 0           croak("Could not create copy of Perl callback\n");
280             }
281 28           duk_push_pointer(ctx, func);
282 28 50         if (! duk_put_prop_lstring(ctx, -2, PL_SLOT_GENERIC_CALLBACK, sizeof(PL_SLOT_GENERIC_CALLBACK) - 1)) {
283 28           croak("Could not associate C dispatcher and Perl callback\n");
284             }
285             } else {
286 1137           croak("Don't know how to deal with an undetermined Perl reference (type: %d)\n", type);
287             ret = 0;
288             }
289             } else {
290 0           croak("Don't know how to deal with an undetermined Perl object\n");
291             ret = 0;
292             }
293 5326           return ret;
294             }
295              
296 3000520           SV* pl_duk_to_perl(pTHX_ duk_context* ctx, int pos)
297             {
298 3000520           SV* ret = 0;
299 3000520 100         if (!seen) {
300 26           seen = newHV();
301             }
302 3000520           ret = pl_duk_to_perl_impl(aTHX_ ctx, pos, seen);
303 3000520           hv_clear(seen);
304 3000520           return ret;
305             }
306              
307 2143           int pl_perl_to_duk(pTHX_ SV* value, duk_context* ctx)
308             {
309 2143           int ret = 0;
310 2143 50         if (!seen) {
311 0           seen = newHV();
312             }
313 2143           ret = pl_perl_to_duk_impl(aTHX_ value, ctx, seen, 0);
314 2143           hv_clear(seen);
315 2143           return ret;
316             }
317              
318 18           static const char* get_typeof(duk_context* ctx, int pos)
319             {
320 18           const char* label = "undefined";
321 18           switch (duk_get_type(ctx, pos)) {
322             case DUK_TYPE_NONE:
323             case DUK_TYPE_UNDEFINED:
324 0           break;
325             case DUK_TYPE_NULL:
326 1           label = "null";
327 1           break;
328             case DUK_TYPE_BOOLEAN:
329 4           label = "boolean";
330 4           break;
331             case DUK_TYPE_NUMBER:
332 3           label = "number";
333 3           break;
334             case DUK_TYPE_STRING:
335 5           label = "string";
336 5           break;
337             case DUK_TYPE_OBJECT:
338 5 100         if (duk_is_array(ctx, pos)) {
339 2           label = "array";
340             }
341 3 50         else if (duk_is_symbol(ctx, pos)) {
342 0           label = "symbol";
343             }
344 3 50         else if (duk_is_pointer(ctx, pos)) {
345 0           label = "pointer";
346             }
347 3 100         else if (duk_is_function(ctx, pos)) {
348 1           label = "function";
349             }
350 2 50         else if (duk_is_c_function(ctx, pos)) {
351 0           label = "c_function";
352             }
353 2 50         else if (duk_is_thread(ctx, pos)) {
354 0           label = "thread";
355             }
356             else {
357 2           label = "object";
358             }
359 5           break;
360             case DUK_TYPE_POINTER:
361 0           label = "pointer";
362 0           break;
363             case DUK_TYPE_BUFFER:
364 0           label = "buffer";
365 0           break;
366             case DUK_TYPE_LIGHTFUNC:
367 0           label = "lightfunc";
368 0           break;
369             default:
370 0           croak("Don't know how to deal with an undetermined JS object\n");
371             break;
372             }
373 18           return label;
374             }
375              
376 35           int pl_call_perl_sv(duk_context* ctx, SV* func)
377             {
378 35           duk_idx_t j = 0;
379 35           duk_idx_t nargs = 0;
380 35           SV* ret = 0;
381             SV *err_tmp;
382              
383             /* prepare Perl environment for calling the CV */
384             dTHX;
385 35           dSP;
386 35           ENTER;
387 35           SAVETMPS;
388 35 50         PUSHMARK(SP);
389              
390             /* pass in the stack each of the params we received */
391 35           nargs = duk_get_top(ctx);
392 106 100         for (j = 0; j < nargs; j++) {
393 71           SV* val = pl_duk_to_perl(aTHX_ ctx, j);
394 71 50         mXPUSHs(val);
395             }
396              
397             /* you would think we need to pop off the args from duktape's stack, but
398             * they get popped off somewhere else, probably by duktape itself */
399              
400             /* call actual Perl CV, passing all params */
401 35           PUTBACK;
402 35           call_sv(func, G_SCALAR | G_EVAL);
403 35           SPAGAIN;
404              
405 35 50         err_tmp = ERRSV;
406 35 50         if (SvTRUE(err_tmp)) {
    50          
    50          
    0          
    0          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
407 3 50         croak("Perl sub died with error: %s", SvPV_nolen(err_tmp));
408             }
409              
410             /* get returned value from Perl and push its JS equivalent back in */
411             /* duktape's stack */
412 32           ret = POPs;
413 32           pl_perl_to_duk(aTHX_ ret, ctx);
414              
415             /* cleanup and return 1, indicating we are returning a value */
416 32           PUTBACK;
417 32 50         FREETMPS;
418 32           LEAVE;
419 32           return 1;
420             }
421              
422 1003316           static int find_last_dot(const char* name, int* len)
423             {
424 1003316           int last_dot = -1;
425 1003316           int l = 0;
426 7040045 100         for (; name[l] != '\0'; ++l) {
427 6036729 100         if (name[l] == '.') {
428 1046           last_dot = l;
429             }
430             }
431 1003316           *len = l;
432 1003316           return last_dot;
433             }
434              
435 1001197           static int find_global_or_property(duk_context* ctx, const char* name)
436             {
437 1001197           int ret = 0;
438 1001197           int len = 0;
439 1001197           int last_dot = find_last_dot(name, &len);
440 1001197 100         if (last_dot < 0) {
441 1001164 100         if (duk_get_global_string(ctx, name)) {
442             /* that leaves global value in stack, for caller to deal with */
443 1000134           ret = 1;
444             } else {
445 1001164           duk_pop(ctx); /* pop value (which was undef) */
446             }
447             } else {
448 33 50         if (duk_peval_lstring(ctx, name, last_dot) == 0) {
449             /* that leaves object containing value in stack */
450 33 100         if (duk_get_prop_lstring(ctx, -1, name + last_dot + 1, len - last_dot - 1)) {
451             /* that leaves value in stack */
452 28           ret = 1;
453              
454             /* have [object, value], need just [value] */
455 28           duk_swap(ctx, -2, -1); /* now have [value, object] */
456 28           duk_pop(ctx); /* pop object, leave canoli... er, value */
457             } else {
458 33           duk_pop_2(ctx); /* pop object and value (which was undef) */
459             }
460             } else {
461 0           duk_pop(ctx); /* pop error */
462             }
463             }
464 1001197           return ret;
465             }
466              
467 60           SV* pl_exists_global_or_property(pTHX_ duk_context* ctx, const char* name)
468             {
469 60           SV* ret = &PL_sv_no; /* return false by default */
470 60 100         if (find_global_or_property(ctx, name)) {
471 30           ret = &PL_sv_yes;
472 30           duk_pop(ctx); /* pop value */
473             }
474 60           return ret;
475             }
476              
477 20           SV* pl_typeof_global_or_property(pTHX_ duk_context* ctx, const char* name)
478             {
479 20           const char* cstr = "undefined";
480 20           STRLEN clen = 0;
481 20           SV* ret = 0;
482 20 100         if (find_global_or_property(ctx, name)) {
483 18           cstr = get_typeof(ctx, -1);
484 18           duk_pop(ctx); /* pop value */
485             }
486              
487 20           ret = _cstr_to_svpv(aTHX_ cstr, clen);
488              
489 20           return ret;
490             }
491              
492 12           SV* pl_instanceof_global_or_property(pTHX_ duk_context* ctx, const char* object, const char* class)
493             {
494 12           SV* ret = &PL_sv_no; /* return false by default */
495 12 50         if (find_global_or_property(ctx, object)) {
496 12 100         if (find_global_or_property(ctx, class)) {
497 9 100         if (duk_instanceof(ctx, -2, -1)) {
498 6           ret = &PL_sv_yes;
499             }
500 9           duk_pop(ctx); /* pop class */
501             }
502 12           duk_pop(ctx); /* pop value */
503             }
504 12           return ret;
505             }
506              
507 1001093           SV* pl_get_global_or_property(pTHX_ duk_context* ctx, const char* name)
508             {
509 1001093           SV* ret = &PL_sv_undef; /* return undef by default */
510 1001093 100         if (find_global_or_property(ctx, name)) {
511             /* Convert found value to Perl and pop it off the stack */
512 1000093           ret = pl_duk_to_perl(aTHX_ ctx, -1);
513 1000093           duk_pop(ctx);
514             }
515 1001093           return ret;
516             }
517              
518 2111           int pl_set_global_or_property(pTHX_ duk_context* ctx, const char* name, SV* value)
519             {
520 2111           int len = 0;
521 2111           int last_dot = 0;
522              
523             /* fprintf(stderr, "STACK: %ld\n", (long) duk_get_top(ctx)); */
524              
525 2111 50         if (pl_perl_to_duk(aTHX_ value, ctx)) {
526             /* that put value in stack */
527             } else {
528 0           return 0;
529             }
530 2111           last_dot = find_last_dot(name, &len);
531 2111 100         if (last_dot < 0) {
532 1107 50         if (duk_put_global_lstring(ctx, name, len)) {
533             /* that consumed value that was in stack */
534             } else {
535 0           duk_pop(ctx); /* pop value */
536 0           croak("Could not save duk value for %s\n", name);
537             }
538             } else {
539 1004           duk_push_lstring(ctx, name + last_dot + 1, len - last_dot - 1);
540             /* that put key in stack */
541 1004 50         if (duk_peval_lstring(ctx, name, last_dot) == 0) {
542             /* that put object in stack */
543             } else {
544 0           duk_pop_2(ctx); /* object (error) and value */
545 0           croak("Could not eval JS object %*.*s: %s\n",
546             last_dot, last_dot, name, duk_safe_to_string(ctx, -1));
547             }
548             /* Have [value, key, object], need [object, key, value], hence swap */
549 1004           duk_swap(ctx, -3, -1);
550              
551 1004           duk_put_prop(ctx, -3); /* consumes key and value */
552 1004           duk_pop(ctx); /* pop object */
553             }
554 2111           return 1;
555             }
556              
557 8           int pl_del_global_or_property(pTHX_ duk_context* ctx, const char* name)
558             {
559 8           int len = 0;
560 8           int last_dot = find_last_dot(name, &len);
561 8 100         if (last_dot < 0) {
562 4           duk_push_global_object(ctx);
563 4           duk_del_prop_lstring(ctx, -1, name, len);
564             } else {
565 4 50         if (duk_peval_lstring(ctx, name, last_dot) == 0) {
566             /* that put object in stack */
567             } else {
568 0           duk_pop(ctx); /* object (error) */
569 0           croak("Could not eval JS object %*.*s: %s\n",
570             last_dot, last_dot, name, duk_safe_to_string(ctx, -1));
571             }
572 4           duk_del_prop_lstring(ctx, -1, name + last_dot + 1, len - last_dot - 1);
573             }
574 8           duk_pop(ctx); /* pop global or property object */
575 8           return 1;
576             }
577              
578 2000359           SV* pl_eval(pTHX_ Duk* duk, const char* js, const char* file)
579             {
580 2000359           SV* ret = &PL_sv_undef; /* return undef by default */
581 2000359           duk_context* ctx = duk->ctx;
582 2000359           duk_int_t rc = 0;
583              
584             do {
585             Stats stats;
586 2000359           duk_uint_t flags = 0;
587              
588             /* flags |= DUK_COMPILE_STRICT; */
589              
590 2000359           pl_stats_start(aTHX_ duk, &stats);
591 2000359 100         if (!file) {
592             /* Compile the requested code without a reference to the file where it lives */
593 2000088           rc = duk_pcompile_string(ctx, flags, js);
594             }
595             else {
596             /* Compile the requested code referencing the file where it lives */
597 271           duk_push_string(ctx, file);
598 271           rc = duk_pcompile_string_filename(ctx, flags, js);
599             }
600 2000359           pl_stats_stop(aTHX_ duk, &stats, "compile");
601 2000359 50         if (rc != DUK_EXEC_SUCCESS) {
602             /* Only for an error this early we print something out and bail out */
603 0           duk_console_log(DUK_CONSOLE_FLUSH | DUK_CONSOLE_TO_STDERR,
604             "JS could not compile code: %s\n",
605             duk_safe_to_string(ctx, -1));
606 0           break;
607             }
608              
609             /* Run the requested code and check for possible errors*/
610 2000359           pl_stats_start(aTHX_ duk, &stats);
611 2000359           rc = duk_pcall(ctx, 0);
612 2000356           pl_stats_stop(aTHX_ duk, &stats, "run");
613 2000356           check_duktape_call_for_errors(rc, ctx);
614              
615             /* Convert returned value to Perl and pop it off the stack */
616 2000356           ret = pl_duk_to_perl(aTHX_ ctx, -1);
617 2000356           duk_pop(ctx);
618              
619             /* Launch eventloop and check for errors again. */
620             /* This call only returns after the eventloop terminates. */
621 2000356           rc = duk_safe_call(ctx, eventloop_run, duk, 0 /*nargs*/, 1 /*nrets*/);
622 2000356           check_duktape_call_for_errors(rc, ctx);
623              
624 2000356           duk_pop(ctx); /* pop return value from duk_safe_call */
625             } while (0);
626              
627 2000356           return ret;
628             }
629              
630 1           int pl_run_gc(Duk* duk)
631             {
632 1           int j = 0;
633              
634             /*
635             * From docs in http://duktape.org/api.html#duk_gc
636             *
637             * You may want to call this function twice to ensure even objects with
638             * finalizers are collected. Currently it takes two mark-and-sweep rounds
639             * to collect such objects. First round marks the object as finalizable
640             * and runs the finalizer. Second round ensures the object is still
641             * unreachable after finalization and then frees the object.
642             */
643 1           duk_context* ctx = duk->ctx;
644 3 100         for (j = 0; j < PL_GC_RUNS; ++j) {
645             /* DUK_GC_COMPACT: Force object property table compaction */
646 2           duk_gc(ctx, DUK_GC_COMPACT);
647             }
648 1           return PL_GC_RUNS;
649             }
650              
651 18           SV* pl_global_objects(pTHX_ duk_context* ctx)
652             {
653 18           int count = 0;
654 18           AV* values = newAV();
655              
656 18           duk_push_global_object(ctx);
657 18           duk_enum(ctx, -1, 0);
658 223 100         while (duk_next(ctx, -1, 0)) { /* get keys only */
659 205           duk_size_t klen = 0;
660 205           const char* kstr = duk_get_lstring(ctx, -1, &klen);
661 205           SV* name = sv_2mortal(_cstr_to_svpv(aTHX_ kstr, klen));
662 205 50         if (av_store(values, count, name)) {
663 205           SvREFCNT_inc(name);
664 205           ++count;
665             }
666 205           duk_pop(ctx); /* key */
667             }
668 18           duk_pop_2(ctx); /* iterator and global object */
669 18           return newRV_inc((SV*) values);
670             }
671              
672 11           static duk_ret_t perl_caller(duk_context* ctx)
673             {
674 11           SV* func = 0;
675              
676             /* get actual Perl CV stored as a function property */
677 11           duk_push_current_function(ctx);
678 11 50         if (!duk_get_prop_lstring(ctx, -1, PL_SLOT_GENERIC_CALLBACK, sizeof(PL_SLOT_GENERIC_CALLBACK) - 1)) {
679 0           croak("Calling Perl handler for a non-Perl function\n");
680             }
681              
682 11           func = (SV*) duk_get_pointer(ctx, -1);
683 11           duk_pop_2(ctx); /* pop pointer and function */
684 11 50         if (func == 0) {
685 0           croak("Could not get value for property %s\n", PL_SLOT_GENERIC_CALLBACK);
686             }
687              
688 11           return pl_call_perl_sv(ctx, func);
689             }
690              
691 3           static void add_hash_key_int(pTHX_ HV* hash, const char* key, int val)
692             {
693 3           STRLEN klen = strlen(key);
694 3           SV* pval = sv_2mortal(newSVnv(val));
695 3 50         if (hv_store(hash, key, klen, pval, 0)) {
696 3           SvREFCNT_inc(pval);
697             }
698             else {
699 0           croak("Could not create numeric entry %s=%d in hash\n", key, val);
700             }
701 3           }
702              
703 1           static void add_hash_key_str(pTHX_ HV* hash, const char* key, const char* val)
704             {
705 1           STRLEN klen = strlen(key);
706 1           STRLEN vlen = strlen(val);
707 1           SV* pval = sv_2mortal(_cstr_to_svpv(aTHX_ val, vlen));
708 1 50         if (hv_store(hash, key, klen, pval, 0)) {
709 1           SvREFCNT_inc(pval);
710             }
711             else {
712 0           croak("Could not create string entry %s=[%s] in hash\n", key, val);
713             }
714 1           }
715              
716 1           HV* pl_get_version_info(pTHX)
717             {
718 1           int patch = 0;
719 1           int minor = 0;
720 1           int major = 0;
721             char buf[100];
722 1           HV* version = newHV();
723 1           long duk_version = DUK_VERSION;
724              
725 1           patch = duk_version % 100;
726 1           duk_version /= 100;
727 1           minor = duk_version % 100;
728 1           duk_version /= 100;
729 1           major = duk_version;
730              
731 1           add_hash_key_int(aTHX_ version, "major" , major);
732 1           add_hash_key_int(aTHX_ version, "minor" , minor);
733 1           add_hash_key_int(aTHX_ version, "patch" , patch);
734              
735 1           sprintf(buf, "%d.%d.%d", major, minor, patch);
736 1           add_hash_key_str(aTHX_ version, "version", buf);
737              
738 1           return version;
739             }