File Coverage

lib/JavaScript/Embedded.pm
Criterion Covered Total %
statement 488 517 94.3
branch 139 168 82.7
condition 26 36 72.2
subroutine 92 95 96.8
pod 9 14 64.2
total 754 830 90.8


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