File Coverage

lib/JavaScript/Embedded.pm
Criterion Covered Total %
statement 484 513 94.3
branch 137 166 82.5
condition 26 36 72.2
subroutine 90 93 96.7
pod 9 14 64.2
total 746 822 90.7


line stmt bran cond sub pod time code
1             package JavaScript::Embedded;
2 56     56   348604 use strict;
  56         410  
  56         2688  
3 56     56   233 use warnings;
  56         75  
  56         1107  
4 56     56   211 use Carp;
  56         79  
  56         4331  
5 56     56   21559 use Data::Dumper;
  56         240888  
  56         3235  
6 56     56   337 use Scalar::Util qw( weaken );
  56         81  
  56         7449  
7             our $VERSION = '2.7.1';
8              
9             my $GlobalRef = {};
10              
11             my $THIS;
12             my $DUKTAPE;
13             my $isNew = bless [], "NEW";
14             my $HEAP = bless [], "HEAP";
15             my $DUK = bless [], "DUK";
16             my $NOARGS = bless [], "NOARGS";
17              
18 56     56   334 use base qw/Exporter/;
  56         92  
  56         15067  
19             our @EXPORT = qw (
20             DUK_TYPE_NONE
21             DUK_TYPE_UNDEFINED
22             DUK_TYPE_NULL
23             DUK_TYPE_BOOLEAN
24             DUK_TYPE_NUMBER
25             DUK_TYPE_STRING
26             DUK_TYPE_OBJECT
27             DUK_TYPE_BUFFER
28             DUK_TYPE_POINTER
29             DUK_TYPE_LIGHTFUNC
30             DUK_ENUM_INCLUDE_NONENUMERABLE
31             DUK_ENUM_INCLUDE_HIDDEN
32             DUK_ENUM_INCLUDE_SYMBOLS
33             DUK_ENUM_EXCLUDE_STRINGS
34             DUK_ENUM_INCLUDE_INTERNAL
35             DUK_ENUM_OWN_PROPERTIES_ONLY
36             DUK_ENUM_ARRAY_INDICES_ONLY
37             DUK_ENUM_SORT_ARRAY_INDICES
38             DUK_ENUM_NO_PROXY_BEHAVIOR
39             DUK_TYPE_MASK_NONE
40             DUK_TYPE_MASK_UNDEFINED
41             DUK_TYPE_MASK_NULL
42             DUK_TYPE_MASK_BOOLEAN
43             DUK_TYPE_MASK_NUMBER
44             DUK_TYPE_MASK_STRING
45             DUK_TYPE_MASK_OBJECT
46             DUK_TYPE_MASK_BUFFER
47             DUK_TYPE_MASK_POINTER
48             DUK_TYPE_MASK_LIGHTFUNC
49             DUK_TYPE_MASK_THROW
50             DUK_COMPILE_EVAL
51             DUK_COMPILE_FUNCTION
52             DUK_COMPILE_STRICT
53             DUK_COMPILE_SAFE
54             DUK_COMPILE_NORESULT
55             DUK_COMPILE_NOSOURCE
56             DUK_COMPILE_STRLEN
57             DUK_DEFPROP_WRITABLE
58             DUK_DEFPROP_ENUMERABLE
59             DUK_DEFPROP_CONFIGURABLE
60             DUK_DEFPROP_HAVE_WRITABLE
61             DUK_DEFPROP_HAVE_ENUMERABLE
62             DUK_DEFPROP_HAVE_CONFIGURABLE
63             DUK_DEFPROP_HAVE_VALUE
64             DUK_DEFPROP_HAVE_GETTER
65             DUK_DEFPROP_HAVE_SETTER
66             DUK_DEFPROP_FORCE
67             DUK_VARARGS
68             null
69             true
70             false
71             _
72             this
73             );
74              
75             ##constants
76             use constant {
77 56         65984 DUK_TYPE_NONE => 0,
78             DUK_TYPE_UNDEFINED => 1,
79             DUK_TYPE_NULL => 2,
80             DUK_TYPE_BOOLEAN => 3,
81             DUK_TYPE_NUMBER => 4,
82             DUK_TYPE_STRING => 5,
83             DUK_TYPE_OBJECT => 6,
84             DUK_TYPE_BUFFER => 7,
85             DUK_TYPE_POINTER => 8,
86             DUK_TYPE_LIGHTFUNC => 9,
87              
88             DUK_TYPE_MASK_NONE => ( 1 << 0 ),
89             DUK_TYPE_MASK_UNDEFINED => ( 1 << 1 ),
90             DUK_TYPE_MASK_NULL => ( 1 << 2 ),
91             DUK_TYPE_MASK_BOOLEAN => ( 1 << 3 ),
92             DUK_TYPE_MASK_NUMBER => ( 1 << 4 ),
93             DUK_TYPE_MASK_STRING => ( 1 << 5 ),
94             DUK_TYPE_MASK_OBJECT => ( 1 << 6 ),
95             DUK_TYPE_MASK_BUFFER => ( 1 << 7 ),
96             DUK_TYPE_MASK_POINTER => ( 1 << 8 ),
97             DUK_TYPE_MASK_LIGHTFUNC => ( 1 << 9 ),
98             DUK_TYPE_MASK_THROW => ( 1 << 10 ),
99              
100             # Enumeration flags for duk_enum()
101             DUK_ENUM_INCLUDE_NONENUMERABLE => ( 1 << 0 ),
102             DUK_ENUM_INCLUDE_HIDDEN => ( 1 << 1 ),
103             DUK_ENUM_INCLUDE_SYMBOLS => ( 1 << 2 ),
104             DUK_ENUM_EXCLUDE_STRINGS => ( 1 << 3 ),
105             DUK_ENUM_OWN_PROPERTIES_ONLY => ( 1 << 4 ),
106             DUK_ENUM_ARRAY_INDICES_ONLY => ( 1 << 5 ),
107             DUK_ENUM_SORT_ARRAY_INDICES => ( 1 << 6 ),
108             DUK_ENUM_NO_PROXY_BEHAVIOR => ( 1 << 7 ),
109              
110             DUK_COMPILE_EVAL => ( 1 << 3 ),
111             DUK_COMPILE_FUNCTION => ( 1 << 4 ),
112             DUK_COMPILE_STRICT => ( 1 << 5 ),
113             DUK_COMPILE_SAFE => ( 1 << 6 ),
114             DUK_COMPILE_NORESULT => ( 1 << 7 ),
115             DUK_COMPILE_NOSOURCE => ( 1 << 8 ),
116             DUK_COMPILE_STRLEN => ( 1 << 9 ),
117              
118             #Flags for duk_def_prop() and its variants
119             DUK_DEFPROP_WRITABLE => ( 1 << 0 ),
120             DUK_DEFPROP_ENUMERABLE => ( 1 << 1 ),
121             DUK_DEFPROP_CONFIGURABLE => ( 1 << 2 ),
122             DUK_DEFPROP_HAVE_WRITABLE => ( 1 << 3 ),
123             DUK_DEFPROP_HAVE_ENUMERABLE => ( 1 << 4 ),
124             DUK_DEFPROP_HAVE_CONFIGURABLE => ( 1 << 5 ),
125             DUK_DEFPROP_HAVE_VALUE => ( 1 << 6 ),
126             DUK_DEFPROP_HAVE_GETTER => ( 1 << 7 ),
127             DUK_DEFPROP_HAVE_SETTER => ( 1 << 8 ),
128             DUK_DEFPROP_FORCE => ( 1 << 9 ),
129             DUK_VARARGS => -1
130 56     56   364 };
  56         117  
131              
132             sub new {
133 74     74 0 1318644 my $class = shift;
134 74         206 my %options = @_;
135              
136 74   100     433 my $max_memory = $options{max_memory} || 0;
137 74   100     323 my $timeout = $options{timeout} || 0;
138              
139 74 100       212 if ($timeout){
140 2 100       124 croak "timeout option must be a number" if !JavaScript::Embedded::Vm::duk_sv_is_number( $timeout );
141             }
142              
143 73 100       190 if ( $max_memory ){
144 6 100       153 croak "max_memory option must be a number" if !JavaScript::Embedded::Vm::duk_sv_is_number( $max_memory );
145 5 100       181 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
146             }
147              
148 71         170 my $self = bless {}, $class;
149              
150 71         45624 my $duk = $self->{duk} = JavaScript::Embedded::Vm->perl_duk_new( $max_memory, $timeout );
151              
152 71         381 $self->{pid} = $$;
153 71         164 $self->{max_memory} = $max_memory;
154              
155             # Initialize global stash 'PerlGlobalStash'
156             # this will be used to store some perl refs
157 71         645 $duk->push_global_stash();
158 71         310 $duk->push_object();
159 71         356 $duk->put_prop_string( -2, "PerlGlobalStash" );
160 71         326 $duk->pop();
161              
162 71         427 $THIS = bless { duk => $duk, heapptr => 0 }, "JavaScript::Embedded::Object";
163              
164             ##finalizer method
165             $self->{finalizer} = sub {
166 274     274   1982 my $ref = $duk->get_string(0);
167 274         3440 delete $GlobalRef->{$ref};
168 274         10075 return 1;
169 71         425 };
170              
171 71         377 weaken $GlobalRef;
172              
173 71         453 $duk->perl_push_function( $self->{finalizer}, 1 );
174 71         309 $duk->put_global_string('perlFinalizer');
175              
176 71         258 return $self;
177             }
178              
179 2     2 1 10 sub null { $JavaScript::Embedded::NULL::null; }
180 9     9 1 14416 sub true { $JavaScript::Embedded::Bool::true; }
181 7     7 1 30 sub false { $JavaScript::Embedded::Bool::false }
182 3     3   35 sub JavaScript::Embedded::_ { $NOARGS }
183 4     4 1 30 sub this { $THIS }
184              
185             sub set {
186 46     46 1 12365 my $self = shift;
187 46         74 my $name = shift;
188 46         58 my $val = shift;
189 46         105 my $duk = $self->vm;
190              
191 46 100       178 if ( $name =~ /\./ ) {
192              
193 2         7 my @props = split /\./, $name;
194 2         4 my $last = pop @props;
195 2         5 my $others = join '.', @props;
196              
197 2 50       57 if ( $duk->peval_string($others) != 0 ) {
198 0         0 croak $others . " is not a javascript object ";
199             }
200              
201 2         9 my $type = $duk->get_type(-1);
202 2 50       5 if ( $type != DUK_TYPE_OBJECT ) {
203 0         0 croak $others . " isn't an object";
204             }
205              
206 2         6 $duk->push_string($last);
207 2         5 $duk->push_perl($val);
208 2         9 $duk->put_prop(-3);
209 2         4 $duk->pop();
210 2         6 return 1;
211             }
212              
213 44         140 $duk->push_perl($val);
214 44         176 $duk->put_global_string($name);
215 44         93 return 1;
216             }
217              
218             sub get {
219 1     1 1 5 my $self = shift;
220 1         2 my $name = shift;
221 1         2 my $duk = $self->vm;
222 1         4 $duk->push_string($name);
223 1 50       29 if ( $duk->peval() != 0 ) {
224 0         0 croak $duk->last_error_string();
225             }
226 1         4 my $ret = $duk->to_perl(-1);
227 1         3 $duk->pop();
228 1         3 return $ret;
229             }
230              
231             sub get_object {
232 6     6 1 150 my $self = shift;
233 6         11 my $name = shift;
234 6         11 my $duk = $self->vm;
235 6         27 $duk->push_string($name);
236 6 50       175 if ( $duk->peval() != 0 ) {
237 0         0 croak $duk->last_error_string();
238             }
239 6         23 my $ret = $duk->to_perl_object(-1);
240 6         20 $duk->pop();
241 6         13 return $ret;
242             }
243              
244             ##FIXME : should pop here?
245             sub eval {
246 31     31 1 1744 my $self = shift;
247 31         56 my $string = shift;
248 31         79 my $duk = $self->duk;
249              
250 31 100       3757353 if ( $duk->peval_string($string) != 0 ) {
251 9         36 croak $duk->last_error_string();
252             }
253              
254 22         107 return $duk->to_perl(-1);
255             }
256              
257 53     53 0 100 sub vm { shift->{duk}; }
258 83     83 0 376 sub duk { shift->{duk}; }
259              
260             sub set_timeout {
261 2     2 0 7 my $self = shift;
262 2         5 $self->duk->set_timeout( shift );
263             }
264              
265             sub resize_memory {
266 2     2 1 1937244 my $self = shift;
267 2         7 $self->duk->resize_memory( shift );
268             }
269              
270             sub destroy {
271 71     71 0 184 local $@;
272 71         114 my $self = shift;
273 71         151 my $duk = delete $self->{duk};
274 71 50       946 return if !$duk;
275 71         338 $duk->free_perl_duk();
276 71         23724 $duk->destroy_heap();
277             }
278              
279             sub DESTROY {
280 71     71   3130530 my $self = shift;
281 71 50 33     594 if ( $self->{pid} && $self->{pid} == $$ ) {
282 71         457 $self->destroy();
283             }
284             }
285              
286             package JavaScript::Embedded::Vm;
287 56     56   457 use strict;
  56         116  
  56         1393  
288 56     56   248 use warnings;
  56         81  
  56         2316  
289 56     56   253 no warnings 'redefine';
  56         103  
  56         1930  
290 56     56   277 use Data::Dumper;
  56         114  
  56         2768  
291 56     56   317 use Config qw( %Config );
  56         92  
  56         2011  
292 56     56   19385 use JavaScript::Embedded::C::libPath;
  56         97  
  56         1430  
293 56     56   268 use Carp;
  56         76  
  56         6163  
294              
295             my $Duklib;
296              
297             my $BOOL_PACKAGES = {
298             'JavaScript::Embedded::Bool' => 1,
299             'boolean' => 1,
300             'JSON::PP::Boolean' => 1,
301             'JSON::Tiny::_Bool' => 1,
302             'Data::MessagePack::Boolean' => 1
303             };
304              
305             BEGIN {
306 56     56   196 my $FunctionsMap = _get_path("FunctionsMap.pl");
307 56         29985 require $FunctionsMap;
308              
309 336     336   2020 sub _get_path { &JavaScript::Embedded::C::libPath::getPath }
310              
311 56 50       449 $Duklib =
312             $^O eq 'MSWin32'
313             ? _get_path('duktape.dll')
314             : _get_path('duktape.so');
315             }
316              
317 56         254 use Inline C => config =>
318             typemaps => _get_path('typemap'),
319 56     56   29931 INC => '-I' . _get_path('../C') . ' -I' . _get_path('../C/lib');
  56         1505963  
320             # myextlib => $Duklib,
321             # LIBS => '-L'. _get_path('../C/lib') . ' -lduktape';
322              
323 56     56   8343 use Inline C => _get_path('duk_perl.c');
  56         112  
  56         129  
324              
325 56         297 use Inline C => q{
326             void poke_buffer(IV to, IV from, IV sz) {
327             memcpy( to, from, sz );
328             }
329 56     56   23499110 };
  56         133  
330              
331             my $ptr_format = do {
332             my $ptr_size = $Config{ptrsize};
333             $ptr_size == 4 ? "L"
334             : $ptr_size == 8 ? "Q"
335             : die("Unrecognized pointer size");
336             };
337              
338 5     5   9705 sub peek { unpack 'P' . $_[1], pack $ptr_format, $_[0] }
339 6     6   2388 sub pv_address { unpack( $ptr_format, pack( "p", $_[0] ) ) }
340              
341             sub push_perl {
342 178025     178025   152131 my $self = shift;
343 178025         154976 my $val = shift;
344 178025   100     359716 my $stash = shift || {};
345              
346 178025 100       226563 if ( my $ref = ref $val ) {
347 101 100       604 if ( $ref eq 'JavaScript::Embedded::NULL' ) {
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
348 1         3 $self->push_null();
349             }
350              
351             elsif ( $BOOL_PACKAGES->{$ref} ) {
352 6 100       9 if ($val) {
353 3         19 $self->push_true();
354             }
355             else {
356 3         18 $self->push_false();
357             }
358             }
359              
360             elsif ( $ref eq 'ARRAY' ) {
361 12         41 my $arr_idx = $self->push_array();
362 12         49 $stash->{$val} = $self->get_heapptr(-1);
363 12         18 my $len = scalar @{$val};
  12         20  
364 12         126 for ( my $idx = 0 ; $idx < $len ; $idx++ ) {
365 32 100       76 if ( $stash->{ $val->[$idx] } ) {
366 2         9 $self->push_heapptr( $stash->{ $val->[$idx] } );
367             }
368             else {
369 30         59 $self->push_perl( $val->[$idx], $stash );
370             }
371 32         121 $self->put_prop_index( $arr_idx, $idx );
372             }
373             }
374              
375             elsif ( $ref eq 'HASH' ) {
376 16         52 $self->push_object();
377 16         49 $stash->{$val} = $self->get_heapptr(-1);
378 16         27 while ( my ( $k, $v ) = each %{$val} ) {
  45         175  
379 29         66 $self->push_string($k);
380 29 100 100     181 if ( $v && $stash->{$v} ) {
381 1         2 $self->push_heapptr( $stash->{$v} );
382             }
383             else {
384 28         103 $self->push_perl( $v, $stash );
385             }
386 29         93 $self->put_prop(-3);
387             }
388             }
389              
390             elsif ( $ref eq 'CODE' ) {
391 46         142 $self->push_function($val);
392             }
393              
394             elsif ( $ref eq 'JavaScript::Embedded::Object' ) {
395 12         31 $self->push_heapptr( $val->{heapptr} );
396             }
397              
398             elsif ( $ref eq 'JavaScript::Embedded::Function' ) {
399 0         0 $self->push_heapptr( $val->($HEAP) );
400             }
401              
402             elsif ( $ref eq 'JavaScript::Embedded::Pointer' ) {
403 0         0 $self->push_pointer($$val);
404             }
405              
406             elsif ( $ref eq 'JavaScript::Embedded::Buffer' ) {
407 6 100       22 my $len = defined $$val ? length($$val) : 0;
408 6         8118 my $ptr = $self->push_fixed_buffer($len);
409 6         19 poke_buffer( $ptr, pv_address($$val), $len );
410             }
411              
412             elsif ( $ref eq 'SCALAR' ) {
413 2 100       5 $$val ? $self->push_true() : $self->push_false()
414             }
415              
416             else {
417 0         0 $self->push_undefined();
418             }
419             }
420             else {
421 177924 100       298831 if ( !defined $val ) {
    100          
422 3         12 $self->push_undefined();
423             }
424             elsif ( duk_sv_is_number($val) ) {
425 177491         286960 $self->push_number($val);
426             }
427             else {
428 430         10390 $self->push_string($val);
429             }
430             }
431             }
432              
433             sub to_perl_object {
434 239     239   497 my $self = shift;
435 239         234 my $index = shift;
436 239         405 my $heapptr = $self->get_heapptr($index);
437 239 50       313 if ( !$heapptr ) { croak "value at stack $index is not an object" }
  0         0  
438 239         591 return JavaScript::Embedded::Util::jsObject(
439             {
440             duk => $self,
441             heapptr => $heapptr
442             }
443             );
444             }
445              
446             sub to_perl {
447 12337     12337   10467 my $self = shift;
448 12337         9613 my $index = shift;
449 12337   100     21746 my $stash = shift || {};
450              
451 12337         10375 my $ret;
452              
453 12337         14217 my $type = $self->get_type($index);
454              
455 12337 100       16996 if ( $type == JavaScript::Embedded::DUK_TYPE_UNDEFINED ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
456 173         196 $ret = undef;
457             }
458              
459             elsif ( $type == JavaScript::Embedded::DUK_TYPE_STRING ) {
460 8507         12528 $ret = $self->get_utf8_string($index);
461             }
462              
463             elsif ( $type == JavaScript::Embedded::DUK_TYPE_NUMBER ) {
464 2319         2918 $ret = $self->get_number($index);
465             }
466              
467             elsif ( $type == JavaScript::Embedded::DUK_TYPE_BUFFER ) {
468 5         21 my $ptr = $self->get_buffer_data( $index, my $sz );
469 5         12 $ret = peek( $ptr, $sz );
470             }
471              
472             elsif ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT ) {
473              
474 1288 100       2137 if ( $self->is_function($index) ) {
475 444         520 my $ptr = $self->get_heapptr($index);
476             return sub {
477 9     9   59 $self->push_heapptr($ptr);
478 9         14 $self->push_this();
479 9         12 my $len = 0 + @_;
480 9         18 for ( my $i = 0 ; $i < $len ; $i++ ) {
481 9         16 $self->push_perl( $_[$i] );
482             }
483 9 100       70 if ( $self->pcall_method($len) == 1 ) {
484 5         9 croak $self->last_error_string();
485             }
486 4         12 my $ret = $self->to_perl(-1);
487 4         10 $self->pop();
488 4         6 return $ret;
489 444         1936 };
490             }
491              
492 844         1166 my $isArray = $self->is_array($index);
493              
494 844         1081 my $heapptr = $self->require_heapptr($index);
495 844 50       1074 if ( $stash->{$heapptr} ) {
496 0         0 $ret = $stash->{$heapptr};
497             }
498             else {
499 844 100       1039 $ret = $isArray ? [] : {};
500 844         2402 $stash->{$heapptr} = $ret;
501             }
502              
503 844         4828 $self->enum( $index, JavaScript::Embedded::DUK_ENUM_OWN_PROPERTIES_ONLY );
504              
505 844         2461 while ( $self->next( -1, 1 ) ) {
506 5750         6582 my ( $key, $val );
507              
508 5750         6327 $key = $self->to_perl(-2);
509              
510 5750 100       8089 if ( $self->get_type(-1) == JavaScript::Embedded::DUK_TYPE_OBJECT ) {
511 1309         1626 my $heapptr = $self->get_heapptr(-1);
512 1309 100       1764 if ( $stash->{$heapptr} ) {
513 63         66 $val = $stash->{$heapptr};
514             }
515             else {
516 1246         1280 $val = $self->to_perl( -1, $stash );
517             }
518             }
519             else {
520 4441         4495 $val = $self->to_perl(-1);
521             }
522              
523 5750         8297 $self->pop_n(2);
524              
525 5750 100       5579 if ($isArray) {
526 133         423 $ret->[$key] = $val;
527             }
528             else {
529 5617         19464 $ret->{$key} = $val;
530             }
531             }
532              
533 844         1622 $self->pop();
534             }
535              
536             elsif ( $type == JavaScript::Embedded::DUK_TYPE_BOOLEAN ) {
537 39         74 my $bool = $self->get_boolean($index);
538 39 100       52 if ( $bool == 1 ) {
539 31         46 $ret = JavaScript::Embedded::Bool::true();
540             }
541             else {
542 8         22 $ret = JavaScript::Embedded::Bool::false();
543             }
544             }
545              
546             elsif ( $type == JavaScript::Embedded::DUK_TYPE_NULL ) {
547 3         6 $ret = JavaScript::Embedded::NULL::null();
548             }
549              
550             elsif ( $type == JavaScript::Embedded::DUK_TYPE_POINTER ) {
551 3         7 my $p = $self->get_pointer($index);
552 3         10 $ret = bless \$p, 'JavaScript::Embedded::Pointer';
553             }
554              
555 11893         17072 return $ret;
556             }
557              
558             ##############################################
559             # push functions
560             ##############################################
561             sub push_function {
562 271     271   3484 my $self = shift;
563 271         299 my $sub = shift;
564 271   100     765 my $nargs = shift || -1;
565              
566             $self->push_c_function(
567             sub {
568 177589     177589   144682 my @args;
569 177589         196012 my $top = $self->get_top();
570 177589         258274 for ( my $i = 0 ; $i < $top ; $i++ ) {
571 413         644 push @args, $self->to_perl($i);
572             }
573              
574 177589         239588 $self->push_this();
575 177589         203089 my $heap = $self->get_heapptr(-1);
576 177589         259763 $self->pop();
577              
578 177589 100       205452 if ( !$heap ) {
579 177481         243027 $self->push_global_object();
580 177481         189549 $heap = $self->get_heapptr(-1);
581 177481         187969 $self->pop();
582             }
583              
584 177589         179153 $THIS->{heapptr} = $heap;
585 177589         149166 $THIS->{duk} = $self;
586              
587 177589         223352 my $ret = $sub->(@args);
588 177568         569431 $self->push_perl($ret);
589 177568         183990 return 1;
590             },
591 271         1170 $nargs
592             );
593             }
594              
595             #####################################################################
596             # safe call
597             #####################################################################
598             sub push_c_function {
599 274     274   345 my $self = shift;
600 274         268 my $sub = shift;
601 274   100     462 my $nargs = shift || -1;
602              
603             $GlobalRef->{"$sub"} = sub {
604 177602     177602   222591 my @args = @_;
605 177602         219986 my $top = $self->get_top();
606 177602         144201 my $ret = 1;
607              
608             my $err = $self->safe_call(
609             sub {
610 177602         168961 $ret = $sub->(@args);
611 177581         172082 return 1;
612             },
613 177602         428804 $top,
614             1
615             );
616              
617 177602 100       291424 if ($err) {
618 21         47 croak $self->last_error_string();
619             }
620 177581         1090999 return $ret;
621 274         1072 };
622              
623 274         1292 $self->perl_push_function( $GlobalRef->{"$sub"}, $nargs );
624 274         9828 $self->eval_string("(function(){perlFinalizer('$sub')})");
625 274         1514 $self->set_finalizer(-2);
626             }
627              
628             #####################################################################
629             # safe call
630             #####################################################################
631             sub safe_call {
632 177649     177649   189063 my $self = shift;
633 177649         141849 my $sub = shift;
634 177649         139682 my $ret;
635             my $safe = sub {
636 177649     177649   157971 local $@;
637 177649         167454 eval { $ret = $sub->($self) };
  177649         177704  
638 177649 100       270183 if ( my $error = $@ ) {
639 37 100       218 if ( $error =~ /^Duk::Error/i ) {
640 22         79 croak $self->last_error_string();
641             }
642             else {
643 15         570 $self->eval_string('(function (e){ throw new Error(e) })');
644 15         55 $self->push_string($error);
645 15         169 $self->call(1);
646             }
647             }
648              
649 177612 50       311989 return defined $ret ? $ret : 1;
650 177649         283314 };
651              
652 177649         183932 eval { $ret = $self->perl_duk_safe_call( $safe, @_ ) };
  177649         257613  
653 177649 100       397316 return defined $ret ? $ret : 1;
654             }
655              
656             sub set_timeout {
657 4     4   1705 my $self = shift;
658 4         7 my $timeout = shift;
659              
660 4 100       117 croak "timeout must be a number" if !duk_sv_is_number($timeout);
661 3         27 $self->perl_duk_set_timeout($timeout);
662             }
663              
664             sub resize_memory {
665 2     2   4 my $self = shift;
666 2   50     7 my $max_memory = shift || 0;
667              
668 2 50       10 croak "max_memory should be a number" if !duk_sv_is_number( $max_memory );
669 2 100       131 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
670              
671 1         5 $self->perl_duk_resize_memory($max_memory);
672             }
673              
674             ##############################################
675             # custom functions
676             ##############################################
677             *get_utf8_string = \&perl_duk_get_utf8_string;
678             *push_perl_function = \&push_c_function;
679             *push_light_function = \&perl_push_function;
680              
681             ##############################################
682             # overridden functions
683             ##############################################
684             *require_context = \&perl_duk_require_context;
685              
686             ##############################################
687             # helper functions
688             ##############################################
689             *reset_top = \&perl_duk_reset_top;
690              
691             sub last_error_string {
692 57     57   80 my $self = shift;
693 57         153 $self->dup(-1);
694 57         417 my $error_str = $self->safe_to_string(-1);
695 57         150 $self->pop();
696 57         7581 return $error_str;
697             }
698              
699             sub dump {
700 3     3   19 my $self = shift;
701 3   100     19 my $name = shift || "Duktape";
702 3   50     14 my $fh = shift || \*STDOUT;
703 3         10 my $n = $self->get_top();
704 3         173 printf $fh "%s (top=%ld):", $name, $n;
705 3         17 for ( my $i = 0 ; $i < $n ; $i++ ) {
706 4         66 printf $fh " ";
707 4         20 $self->dup($i);
708 4         93 printf $fh "%s", $self->safe_to_string(-1);
709 4         22 $self->pop();
710             }
711 3         255 printf $fh "\n";
712             }
713              
714       0     sub DESTROY { }
715              
716             package JavaScript::Embedded::Bool;
717             {
718 56     56   1282122 use warnings;
  56         129  
  56         2924  
719 56     56   308 use strict;
  56         91  
  56         4675  
720             our ( $true, $false );
721             use overload
722 18     18   1676 '""' => sub { ${ $_[0] } },
  18         88  
723 43 100   43   1800 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  43         107  
724 56     56   56212 fallback => 1;
  56         45083  
  56         575  
725              
726             BEGIN {
727 56     56   7791 my $use_boolean = eval { require boolean; 1; };
  56         7433  
  0         0  
728 56         210 my $t = 1;
729 56         88 my $f = 0;
730 56 50       206 $true = $use_boolean ? boolean::true() : bless \$t, 'JavaScript::Embedded::Bool';
731 56 50       4512 $false = $use_boolean ? boolean::false() : bless \$f, 'JavaScript::Embedded::Bool';
732             }
733              
734 31     31   43 sub true { $true }
735 8     8   13 sub false { $false }
736              
737 2 100   2   191 sub TO_JSON { ${$_[0]} ? \1 : \0 }
  2         27  
738             }
739              
740             package JavaScript::Embedded::NULL;
741             {
742 56     56   325 use warnings;
  56         99  
  56         1582  
743 56     56   280 use strict;
  56         121  
  56         4255  
744             our ($null);
745             use overload
746 2     2   198 '""' => sub { ${ $_[0] } },
  2         11  
747 5 50   5   8 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  5         12  
748 56     56   345 fallback => 1;
  56         102  
  56         407  
749              
750             BEGIN {
751 56     56   4560 my $n = '';
752 56         2579 $null = bless \$n, 'JavaScript::Embedded::NULL';
753             }
754              
755 3     3   5 sub null { $null }
756             }
757              
758             package JavaScript::Embedded::Object;
759             {
760 56     56   369 use warnings;
  56         145  
  56         1549  
761 56     56   282 use strict;
  56         100  
  56         1193  
762 56     56   318 use Carp;
  56         181  
  56         3442  
763 56     56   308 use Data::Dumper;
  56         125  
  56         2988  
764             my $CONSTRUCTORS = {};
765 56     56   315 use Scalar::Util 'weaken';
  56         103  
  56         4142  
766             use overload '""' => sub {
767 3     3   487 my $self = shift;
768 3         10 $self->inspect();
769             },
770 56     56   315 fallback => 1;
  56         86  
  56         301  
771              
772             sub inspect {
773 3     3   6 my $self = shift;
774 3         78 my $heapptr = $self->{heapptr};
775 3         5 my $duk = $self->{duk};
776 3         10 $duk->push_heapptr($heapptr);
777 3         6 my $ret = $duk->to_perl(-1);
778 3         7 $duk->pop();
779 3         7 return $ret;
780             }
781              
782             our $AUTOLOAD;
783              
784             sub AUTOLOAD {
785 522     522   10098 my $self = shift;
786 522         742 my $heapptr = $self->{heapptr};
787 522         511 my $duk = $self->{duk};
788 522         2803 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
789 522 50       1003 return if $method eq 'DESTROY';
790 522         833 return JavaScript::Embedded::Util::autoload( $self, $method, $duk, $heapptr, @_ );
791             }
792              
793             DESTROY {
794 251     251   52888 my $self = shift;
795 251         342 my $duk = $self->{duk};
796              
797 251         312 my $refcount = delete $self->{refcount};
798 251 100       481 return if ( !$refcount );
799 234         628 $duk->push_global_stash();
800 234         576 $duk->get_prop_string( -1, "PerlGlobalStash" );
801 234         457 $duk->push_number($refcount);
802 234         2086 $duk->del_prop(-2);
803 234         807 $duk->pop_2();
804             }
805             }
806              
807             package JavaScript::Embedded::Function;
808             {
809 56     56   17552 use strict;
  56         94  
  56         1364  
810 56     56   299 use warnings;
  56         103  
  56         1420  
811 56     56   253 use Data::Dumper;
  56         85  
  56         10388  
812              
813             sub new {
814 136     136   7061 my $self = shift;
815 136         235 $self->( $isNew, @_ );
816             }
817              
818             our $AUTOLOAD;
819              
820             sub AUTOLOAD {
821 0     0   0 my $self = shift;
822 0         0 my $heapptr = $self->($HEAP);
823 0         0 my $duk = $self->($DUK);
824              
825 0         0 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
826 0 0       0 return if $method eq 'DESTROY';
827 0         0 return JavaScript::Embedded::Util::autoload( $self, $method, $duk, $heapptr, @_ );
828             }
829              
830       0     sub DESTROY { }
831             };
832              
833             package JavaScript::Embedded::Util;
834             {
835 56     56   339 use strict;
  56         114  
  56         1228  
836 56     56   254 use warnings;
  56         100  
  56         1728  
837 56     56   255 use Data::Dumper;
  56         96  
  56         2012  
838 56     56   292 use Carp;
  56         93  
  56         41617  
839              
840             sub autoload {
841 522     522   566 my $self = shift;
842 522         548 my $method = shift;
843 522         448 my $duk = shift;
844 522         452 my $heapptr = shift;
845              
846 522         1076 $duk->push_heapptr($heapptr);
847 522 50       709 if ( $method eq 'new' ) {
848 0         0 my $len = @_ + 0;
849 0         0 foreach my $val (@_) {
850 0         0 $duk->push_perl($val);
851             }
852 0 0       0 if ( $duk->pnew($len) != 0 ) {
853 0         0 croak $duk->last_error_string();
854             }
855 0         0 my $val = $duk->to_perl_object(-1);
856 0         0 $duk->pop();
857 0         0 return $val;
858             }
859              
860 522         516 my $val = undef;
861 522         1553 $duk->get_prop_string( -1, $method );
862              
863 522         844 my $type = $duk->get_type(-1);
864 522 100 66     1139 if ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT
865             || $type == JavaScript::Embedded::DUK_TYPE_BUFFER )
866             {
867              
868 379 50       678 if ( $duk->is_function(-1) ) {
869 379         530 my $function_heap = $duk->get_heapptr(-1);
870              
871 379 100       481 if (@_) {
872             #called with special no arg _
873 377 100       540 shift if ( ref $_[0] eq 'NOARGS' );
874 377         511 $val = jsFunction( $method, $duk, $function_heap, $heapptr, 'call', @_ );
875             }
876             else {
877 2         4 $val = jsFunction( $method, $duk, $function_heap, $heapptr );
878             }
879             }
880             else {
881 0         0 $val = $duk->to_perl_object(-1);
882             }
883             }
884             else {
885 143         246 $val = $duk->to_perl(-1);
886             }
887 522         867 $duk->pop_2();
888 522         1255 return $val;
889             }
890              
891             sub jsFunction {
892 384     384   409 my $methodname = shift;
893 384         345 my $duk = shift;
894 384         321 my $heapptr = shift;
895 384   33     497 my $constructor = shift || $heapptr;
896 384         370 my $doCall = shift;
897             my $sub = sub {
898              
899             # check first value, if it a ref of NEW
900             # then this is a constructor call, other wise
901             # it's just a normal call
902 515     515   536 my $isNew;
903 515         686 my $ref = ref $_[0];
904 515 100       1026 if ( $ref eq "NEW" ) {
    50          
    50          
905 136         128 shift;
906 136         129 $isNew = 1;
907             }
908             elsif ( $ref eq "HEAP" ) {
909 0         0 return $heapptr;
910             }
911             elsif ( $ref eq "DUK" ) {
912 0         0 return $duk;
913             }
914              
915 515         584 my $len = @_ + 0;
916 515         849 $duk->push_heapptr($heapptr);
917 515 100       932 $duk->push_heapptr($constructor) if !$isNew;
918 515         677 foreach my $val (@_) {
919 543 100       677 if ( ref $val eq 'CODE' ) {
920 211         323 $duk->push_function($val);
921             }
922             else {
923 332         489 $duk->push_perl($val);
924             }
925             }
926              
927 515 100       710 if ($isNew) {
928 136 50       1024 if ( $duk->pnew($len) != 0 ) {
929 0         0 croak $duk->last_error_string();
930             }
931             }
932             else {
933 379 50       1750 if ( $duk->pcall_method($len) != 0 ) {
934 0         0 croak $duk->last_error_string();
935             }
936             }
937              
938 515         560 my $ret;
939             ##getting function call values
940 515         755 my $type = $duk->get_type(-1);
941 515 100 66     1132 if ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT
942             || $type == JavaScript::Embedded::DUK_TYPE_BUFFER )
943             {
944 205         298 $ret = $duk->to_perl_object(-1);
945             }
946             else {
947 310         431 $ret = $duk->to_perl(-1);
948             }
949 515         1027 $duk->pop();
950 515         2567 return $ret;
951 384         1338 };
952              
953 384 100       786 return $sub->(@_) if $doCall;
954 7         36 return bless $sub, "JavaScript::Embedded::Function";
955             }
956              
957             my $REFCOUNT = 0;
958              
959             sub jsObject {
960 239     239   246 my $options = shift;
961              
962 239         257 my $duk = $options->{duk};
963 239         236 my $heapptr = $options->{heapptr};
964 239   33     518 my $constructor = $options->{constructor} || $heapptr;
965              
966             #We may push same heapptr on the global stack more
967             #than once, this results in segmentation fault when
968             #we destroy the object and delete heapptr from the
969             #global stash then trying to use it again
970             #TODO : this is really a poor man solution
971             #for this problem, we use a refcounter to create
972             #a unique id for each heapptr, a better solution
973             #would be making sure same heapptr pushed once and not to
974             #be free unless all gone
975 239         647 my $refcount = ( ++$REFCOUNT ) + ( rand(3) );
976              
977 239         530 $duk->push_global_stash();
978 239         510 $duk->get_prop_string( -1, "PerlGlobalStash" );
979 239         420 $duk->push_number($refcount);
980 239         376 $duk->push_heapptr($heapptr);
981 239         1669 $duk->put_prop(-3); #PerlGlobalStash[heapptr] = object
982 239         408 $duk->pop_2();
983              
984 239         314 my $type = $duk->get_type(-1);
985              
986 239 100       474 if ( $duk->is_function(-1) ) {
987 5         17 return JavaScript::Embedded::Util::jsFunction( 'anon', $duk, $heapptr, $constructor );
988             }
989              
990 234         738 return bless {
991             refcount => $refcount,
992             duk => $duk,
993             heapptr => $heapptr
994             }, "JavaScript::Embedded::Object";
995             }
996             }
997              
998             1;
999              
1000             __END__