File Coverage

pl_duk.c
Criterion Covered Total %
statement 407 465 87.5
branch 147 194 75.7
condition n/a
subroutine n/a
pod n/a
total 554 659 84.0


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