File Coverage

duk.xs
Criterion Covered Total %
statement 126 136 92.6
branch 40 62 64.5
condition n/a
subroutine n/a
pod n/a
total 166 198 83.8


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT /* we want efficiency */
2             #include "EXTERN.h"
3             #include "perl.h"
4             #include "XSUB.h"
5             #include "ppport.h"
6              
7             /*
8             * Duktape is an embeddable Javascript engine, with a focus on portability and
9             * compact footprint.
10             *
11             * http://duktape.org/index.html
12             */
13             #include "pl_duk.h"
14             #include "pl_stats.h"
15             #include "pl_module.h"
16             #include "pl_eventloop.h"
17             #include "pl_console.h"
18             #include "pl_native.h"
19             #include "pl_inlined.h"
20             #include "pl_sandbox.h"
21             #include "pl_util.h"
22              
23             #define MAX_MEMORY_MINIMUM (128 * 1024) /* 128 KB */
24             #define MAX_TIMEOUT_MINIMUM (500000) /* 500_000 us = 500 ms = 0.5 s */
25              
26             #define TIMEOUT_RESET(duk) \
27             do { \
28             if (duk->max_timeout_us > 0) { \
29             duk->eval_start_us = now_us(); \
30             } \
31             } while (0) \
32              
33 0           static void duk_fatal_error_handler(void* udata, const char* msg)
34             {
35             dTHX;
36              
37             /* Duk* duk = (Duk*) udata; */
38             UNUSED_ARG(udata);
39              
40 0 0         PerlIO_printf(PerlIO_stderr(), "duktape fatal error, aborting: %s\n", msg ? msg : "*NONE*");
41 0           abort();
42             }
43              
44 373           static void set_up(Duk* duk)
45             {
46 373 50         if (duk->inited) {
47 0           return;
48             }
49 373           duk->inited = 1;
50              
51 373           duk->ctx = duk_create_heap(pl_sandbox_alloc, pl_sandbox_realloc, pl_sandbox_free, duk, duk_fatal_error_handler);
52 373 50         if (!duk->ctx) {
53 0           croak("Could not create duk heap\n");
54             }
55              
56 373 100         TIMEOUT_RESET(duk);
57              
58             /* register a bunch of native functions */
59 373           pl_register_native_functions(duk);
60              
61             /* initialize module handling functions */
62 373           pl_register_module_functions(duk);
63              
64             /* register event loop dispatcher */
65 373           pl_register_eventloop(duk);
66              
67             /* inline a bunch of JS functions */
68 373           pl_register_inlined_functions(duk);
69              
70             /* initialize console object */
71 373           pl_console_init(duk);
72             }
73              
74 373           static void tear_down(Duk* duk)
75             {
76 373 50         if (!duk->inited) {
77 0           return;
78             }
79 373           duk->inited = 0;
80              
81 373           duk_destroy_heap(duk->ctx);
82             }
83              
84 259           static void destroy_duktape_object(pTHX_ Duk* duk)
85             {
86 259 50         if (duk) {
87             /* Free Perl objects. */
88 259           SvREFCNT_dec((SV *) duk->stats);
89 259           SvREFCNT_dec((SV *) duk->msgs);
90 259 100         if( duk->version) {
91 1           SvREFCNT_dec((SV *) duk->version);
92             }
93              
94 259           hv_clear(duk->funcref);
95 259           SvREFCNT_dec((SV *) duk->funcref);
96 259           free(duk);
97             }
98 259           }
99              
100 259           static Duk* create_duktape_object(pTHX_ HV* opt)
101             {
102 259           Duk* duk = (Duk*) malloc(sizeof(Duk));
103 259           memset(duk, 0, sizeof(Duk));
104              
105 259           duk->pagesize_bytes = getpagesize();
106              
107 259           duk->stats = newHV();
108 259           duk->msgs = newHV();
109 259           duk->funcref = newHV();
110              
111 259 100         if (opt) {
112 109           hv_iterinit(opt);
113 207           while (1) {
114 316           SV* value = 0;
115 316           I32 klen = 0;
116 316           char* kstr = 0;
117 316           HE* entry = hv_iternext(opt);
118 316 100         if (!entry) {
119 109           break; /* no more hash keys */
120             }
121 207           kstr = hv_iterkey(entry, &klen);
122 207 50         if (!kstr || klen < 0) {
    50          
123 207           continue; /* invalid key */
124             }
125 207           value = hv_iterval(opt, entry);
126 207 50         if (!value) {
127 0           continue; /* invalid value */
128             }
129 207 100         if (memcmp(kstr, DUK_OPT_NAME_GATHER_STATS, klen) == 0) {
130 102           duk->flags |= SvTRUE(value) ? DUK_OPT_FLAG_GATHER_STATS : 0;
131 102           continue;
132             }
133 105 100         if (memcmp(kstr, DUK_OPT_NAME_SAVE_MESSAGES, klen) == 0) {
134 102 100         duk->flags |= SvTRUE(value) ? DUK_OPT_FLAG_SAVE_MESSAGES : 0;
135 102           continue;
136             }
137 3 100         if (memcmp(kstr, DUK_OPT_NAME_MAX_MEMORY_BYTES, klen) == 0) {
138 1           size_t param = SvIV(value);
139 1           duk->max_allocated_bytes = param > MAX_MEMORY_MINIMUM ? param : MAX_MEMORY_MINIMUM;
140 1           continue;
141             }
142 2 100         if (memcmp(kstr, DUK_OPT_NAME_MAX_TIMEOUT_US, klen) == 0) {
143 1           double param = SvIV(value);
144 1 50         duk->max_timeout_us = param > MAX_TIMEOUT_MINIMUM ? param : MAX_TIMEOUT_MINIMUM;
145 1           continue;
146             }
147 1 50         if (memcmp(kstr, DUK_OPT_NAME_CATCH_PERL_EXCEPTIONS, klen) == 0) {
148 1 50         duk->flags |= SvTRUE(value) ? DUK_OPT_FLAG_CATCH_PERL_EXCEPTIONS : 0;
149 1           continue;
150             }
151 0           croak("Unknown option %*.*s\n", (int) klen, (int) klen, kstr);
152             }
153             }
154              
155 259           set_up(duk);
156 259           return duk;
157             }
158              
159 259           static int session_dtor(pTHX_ SV* sv, MAGIC* mg)
160             {
161 259           Duk* duk = (Duk*) mg->mg_ptr;
162             UNUSED_ARG(sv);
163 259           tear_down(duk);
164 259           destroy_duktape_object(aTHX_ duk);
165 259           return 0;
166             }
167              
168             static MGVTBL session_magic_vtbl = { .svt_free = session_dtor };
169              
170             MODULE = JavaScript::Duktape::XS PACKAGE = JavaScript::Duktape::XS
171             PROTOTYPES: DISABLE
172              
173             #################################################################
174              
175             Duk*
176             new(char* CLASS, HV* opt = NULL)
177             CODE:
178             UNUSED_ARG(opt);
179 259           RETVAL = create_duktape_object(aTHX_ opt);
180             OUTPUT: RETVAL
181              
182             void
183             reset(Duk* duk)
184             PPCODE:
185 114           tear_down(duk);
186 114           set_up(duk);
187              
188             HV*
189             get_stats(Duk* duk)
190             CODE:
191 9           RETVAL = duk->stats;
192             OUTPUT: RETVAL
193              
194             void
195             reset_stats(Duk* duk)
196             PPCODE:
197 0           hv_clear(duk->stats);
198              
199             HV*
200             get_msgs(Duk* duk)
201             CODE:
202 101           RETVAL = duk->msgs;
203             OUTPUT: RETVAL
204              
205             void
206             reset_msgs(Duk* duk)
207             PPCODE:
208 0           hv_clear(duk->msgs);
209              
210             SV*
211             get(Duk* duk, const char* name)
212             PREINIT:
213 1001093           duk_context* ctx = 0;
214             Stats stats;
215             CODE:
216 1001093 50         TIMEOUT_RESET(duk);
217 1001093           ctx = duk->ctx;
218 1001093           pl_stats_start(aTHX_ duk, &stats);
219 1001093           RETVAL = pl_get_global_or_property(aTHX_ ctx, name);
220 1001093           pl_stats_stop(aTHX_ duk, &stats, "get");
221             OUTPUT: RETVAL
222              
223             SV*
224             exists(Duk* duk, const char* name)
225             PREINIT:
226 60           duk_context* ctx = 0;
227             Stats stats;
228             CODE:
229 60 50         TIMEOUT_RESET(duk);
230 60           ctx = duk->ctx;
231 60           pl_stats_start(aTHX_ duk, &stats);
232 60           RETVAL = pl_exists_global_or_property(aTHX_ ctx, name);
233 60           pl_stats_stop(aTHX_ duk, &stats, "exists");
234             OUTPUT: RETVAL
235              
236             SV*
237             typeof(Duk* duk, const char* name)
238             PREINIT:
239 20           duk_context* ctx = 0;
240             Stats stats;
241             CODE:
242 20 50         TIMEOUT_RESET(duk);
243 20           ctx = duk->ctx;
244 20           pl_stats_start(aTHX_ duk, &stats);
245 20           RETVAL = pl_typeof_global_or_property(aTHX_ ctx, name);
246 20           pl_stats_stop(aTHX_ duk, &stats, "typeof");
247             OUTPUT: RETVAL
248              
249             SV*
250             instanceof(Duk* duk, const char* object, const char* class)
251             PREINIT:
252 12           duk_context* ctx = 0;
253             Stats stats;
254             CODE:
255 12 50         TIMEOUT_RESET(duk);
256 12           ctx = duk->ctx;
257 12           pl_stats_start(aTHX_ duk, &stats);
258 12           RETVAL = pl_instanceof_global_or_property(aTHX_ ctx, object, class);
259 12           pl_stats_stop(aTHX_ duk, &stats, "instanceof");
260             OUTPUT: RETVAL
261            
262             int
263             set(Duk* duk, const char* name, SV* value)
264             PREINIT:
265             Stats stats;
266             CODE:
267 2117 50         TIMEOUT_RESET(duk);
268 2117           pl_stats_start(aTHX_ duk, &stats);
269 2117           RETVAL = pl_set_global_or_property(aTHX_ duk, name, value);
270 2117           pl_stats_stop(aTHX_ duk, &stats, "set");
271             OUTPUT: RETVAL
272              
273             int
274             remove(Duk* duk, const char* name)
275             PREINIT:
276             Stats stats;
277             CODE:
278 8 50         TIMEOUT_RESET(duk);
279 8           pl_stats_start(aTHX_ duk, &stats);
280 8           RETVAL = pl_del_global_or_property(aTHX_ duk, name);
281 8           pl_stats_stop(aTHX_ duk, &stats, "remove");
282             OUTPUT: RETVAL
283              
284             SV*
285             eval(Duk* duk, const char* js, const char* file = 0)
286             CODE:
287 2000196 100         TIMEOUT_RESET(duk);
288 2000196           RETVAL = pl_eval(aTHX_ duk, js, file);
289             OUTPUT: RETVAL
290              
291             SV*
292             dispatch_function_in_event_loop(Duk* duk, const char* func)
293             PREINIT:
294             Stats stats;
295             CODE:
296 3 50         TIMEOUT_RESET(duk);
297 3           pl_stats_start(aTHX_ duk, &stats);
298 3           RETVAL = newSViv(pl_run_function_in_event_loop(duk, func));
299 3           pl_stats_stop(aTHX_ duk, &stats, "dispatch");
300             OUTPUT: RETVAL
301              
302             HV*
303             get_version_info(Duk* duk)
304             CODE:
305 1 50         if (!duk->version) {
306 1           duk->version = pl_get_version_info(aTHX);
307             }
308 1           RETVAL = duk->version;
309             OUTPUT: RETVAL
310              
311             SV*
312             run_gc(Duk* duk)
313             PREINIT:
314             Stats stats;
315             CODE:
316 1 50         TIMEOUT_RESET(duk);
317 1           pl_stats_start(aTHX_ duk, &stats);
318 1           RETVAL = newSVnv(pl_run_gc(duk));
319 1           pl_stats_stop(aTHX_ duk, &stats, "run_gc");
320             OUTPUT: RETVAL
321              
322             SV*
323             global_objects(Duk* duk)
324             PREINIT:
325 18           duk_context* ctx = 0;
326             Stats stats;
327             CODE:
328 18 50         TIMEOUT_RESET(duk);
329 18           ctx = duk->ctx;
330 18           pl_stats_start(aTHX_ duk, &stats);
331 18           RETVAL = pl_global_objects(aTHX_ ctx);
332 18           pl_stats_stop(aTHX_ duk, &stats, "global_objects");
333             OUTPUT: RETVAL