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   363246 use strict;
  56         444  
  56         1322  
3 56     56   283 use warnings;
  56         82  
  56         2497  
4 56     56   228 use Carp;
  56         83  
  56         4662  
5 56     56   22982 use Data::Dumper;
  56         256525  
  56         3639  
6 56     56   434 use Scalar::Util qw( weaken );
  56         84  
  56         7911  
7             our $VERSION = '2.7.0';
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   359 use base qw/Exporter/;
  56         102  
  56         16363  
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         70615 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   417 };
  56         142  
131              
132             sub new {
133 74     74 0 1381734 my $class = shift;
134 74         229 my %options = @_;
135              
136 74   100     472 my $max_memory = $options{max_memory} || 0;
137 74   100     369 my $timeout = $options{timeout} || 0;
138              
139 74 100       247 if ($timeout){
140 2 100       113 croak "timeout option must be a number" if !JavaScript::Embedded::Vm::duk_sv_is_number( $timeout );
141             }
142              
143 73 100       216 if ( $max_memory ){
144 6 100       169 croak "max_memory option must be a number" if !JavaScript::Embedded::Vm::duk_sv_is_number( $max_memory );
145 5 100       218 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
146             }
147              
148 71         219 my $self = bless {}, $class;
149              
150 71         49098 my $duk = $self->{duk} = JavaScript::Embedded::Vm->perl_duk_new( $max_memory, $timeout );
151              
152 71         434 $self->{pid} = $$;
153 71         170 $self->{max_memory} = $max_memory;
154              
155             # Initialize global stash 'PerlGlobalStash'
156             # this will be used to store some perl refs
157 71         666 $duk->push_global_stash();
158 71         329 $duk->push_object();
159 71         393 $duk->put_prop_string( -2, "PerlGlobalStash" );
160 71         340 $duk->pop();
161              
162 71         495 $THIS = bless { duk => $duk, heapptr => 0 }, "JavaScript::Embedded::Object";
163              
164             ##finalizer method
165             $self->{finalizer} = sub {
166 274     274   1800 my $ref = $duk->get_string(0);
167 274         4206 delete $GlobalRef->{$ref};
168 274         11255 return 1;
169 71         466 };
170              
171 71         429 weaken $GlobalRef;
172              
173 71         433 $duk->perl_push_function( $self->{finalizer}, 1 );
174 71         338 $duk->put_global_string('perlFinalizer');
175              
176 71         278 return $self;
177             }
178              
179 2     2 1 14 sub null { $JavaScript::Embedded::NULL::null; }
180 9     9 1 14271 sub true { $JavaScript::Embedded::Bool::true; }
181 7     7 1 30 sub false { $JavaScript::Embedded::Bool::false }
182 3     3   30 sub JavaScript::Embedded::_ { $NOARGS }
183 4     4 1 36 sub this { $THIS }
184              
185             sub set {
186 46     46 1 12768 my $self = shift;
187 46         87 my $name = shift;
188 46         58 my $val = shift;
189 46         120 my $duk = $self->vm;
190              
191 46 100       178 if ( $name =~ /\./ ) {
192              
193 2         8 my @props = split /\./, $name;
194 2         3 my $last = pop @props;
195 2         4 my $others = join '.', @props;
196              
197 2 50       55 if ( $duk->peval_string($others) != 0 ) {
198 0         0 croak $others . " is not a javascript object ";
199             }
200              
201 2         7 my $type = $duk->get_type(-1);
202 2 50       4 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         4 $duk->push_perl($val);
208 2         14 $duk->put_prop(-3);
209 2         4 $duk->pop();
210 2         7 return 1;
211             }
212              
213 44         156 $duk->push_perl($val);
214 44         171 $duk->put_global_string($name);
215 44         101 return 1;
216             }
217              
218             sub get {
219 1     1 1 4 my $self = shift;
220 1         2 my $name = shift;
221 1         2 my $duk = $self->vm;
222 1         6 $duk->push_string($name);
223 1 50       37 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         2 return $ret;
229             }
230              
231             sub get_object {
232 6     6 1 143 my $self = shift;
233 6         8 my $name = shift;
234 6         9 my $duk = $self->vm;
235 6         16 $duk->push_string($name);
236 6 50       144 if ( $duk->peval() != 0 ) {
237 0         0 croak $duk->last_error_string();
238             }
239 6         16 my $ret = $duk->to_perl_object(-1);
240 6         16 $duk->pop();
241 6         13 return $ret;
242             }
243              
244             ##FIXME : should pop here?
245             sub eval {
246 31     31 1 1928 my $self = shift;
247 31         56 my $string = shift;
248 31         97 my $duk = $self->duk;
249              
250 31 100       3746284 if ( $duk->peval_string($string) != 0 ) {
251 9         61 croak $duk->last_error_string();
252             }
253              
254 22         119 return $duk->to_perl(-1);
255             }
256              
257 53     53 0 100 sub vm { shift->{duk}; }
258 83     83 0 407 sub duk { shift->{duk}; }
259              
260             sub set_timeout {
261 2     2 0 11 my $self = shift;
262 2         5 $self->duk->set_timeout( shift );
263             }
264              
265             sub resize_memory {
266 2     2 1 2294743 my $self = shift;
267 2         7 $self->duk->resize_memory( shift );
268             }
269              
270             sub destroy {
271 71     71 0 133 local $@;
272 71         127 my $self = shift;
273 71         152 my $duk = delete $self->{duk};
274 71 50       1058 return if !$duk;
275 71         403 $duk->free_perl_duk();
276 71         25233 $duk->destroy_heap();
277             }
278              
279             sub DESTROY {
280 71     71   3334409 my $self = shift;
281 71 50 33     702 if ( $self->{pid} && $self->{pid} == $$ ) {
282 71         720 $self->destroy();
283             }
284             }
285              
286             package JavaScript::Embedded::Vm;
287 56     56   433 use strict;
  56         100  
  56         1536  
288 56     56   260 use warnings;
  56         88  
  56         2192  
289 56     56   291 no warnings 'redefine';
  56         110  
  56         2131  
290 56     56   296 use Data::Dumper;
  56         119  
  56         3142  
291 56     56   365 use Config qw( %Config );
  56         90  
  56         2164  
292 56     56   21932 use JavaScript::Embedded::C::libPath;
  56         100  
  56         1487  
293 56     56   272 use Carp;
  56         80  
  56         6619  
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   1813 my $FunctionsMap = _get_path("FunctionsMap.pl");
307 56         30415 require $FunctionsMap;
308              
309 336     336   2140 sub _get_path { &JavaScript::Embedded::C::libPath::getPath }
310              
311 56 50       525 $Duklib =
312             $^O eq 'MSWin32'
313             ? _get_path('duktape.dll')
314             : _get_path('duktape.so');
315             }
316              
317 56         251 use Inline C => config =>
318             typemaps => _get_path('typemap'),
319 56     56   33095 INC => '-I' . _get_path('../C') . ' -I' . _get_path('../C/lib');
  56         1589180  
320             # myextlib => $Duklib,
321             # LIBS => '-L'. _get_path('../C/lib') . ' -lduktape';
322              
323 56     56   8820 use Inline C => _get_path('duk_perl.c');
  56         103  
  56         134  
324              
325 56         284 use Inline C => q{
326             void poke_buffer(IV to, IV from, IV sz) {
327             memcpy( to, from, sz );
328             }
329 56     56   23299058 };
  56         123  
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   10677 sub peek { unpack 'P' . $_[1], pack $ptr_format, $_[0] }
339 6     6   2239 sub pv_address { unpack( $ptr_format, pack( "p", $_[0] ) ) }
340              
341             sub push_perl {
342 162478     162478   148163 my $self = shift;
343 162478         134333 my $val = shift;
344 162478   100     347182 my $stash = shift || {};
345              
346 162478 100       205127 if ( my $ref = ref $val ) {
347 104 100       658 if ( $ref eq 'JavaScript::Embedded::NULL' ) {
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
348 1         4 $self->push_null();
349             }
350              
351             elsif ( $BOOL_PACKAGES->{$ref} ) {
352 6 100       12 if ($val) {
353 3         19 $self->push_true();
354             }
355             else {
356 3         21 $self->push_false();
357             }
358             }
359              
360             elsif ( $ref eq 'ARRAY' ) {
361 12         33 my $arr_idx = $self->push_array();
362 12         49 $stash->{$val} = $self->get_heapptr(-1);
363 12         14 my $len = scalar @{$val};
  12         19  
364 12         31 for ( my $idx = 0 ; $idx < $len ; $idx++ ) {
365 32 100       69 if ( $stash->{ $val->[$idx] } ) {
366 2         7 $self->push_heapptr( $stash->{ $val->[$idx] } );
367             }
368             else {
369 30         65 $self->push_perl( $val->[$idx], $stash );
370             }
371 32         104 $self->put_prop_index( $arr_idx, $idx );
372             }
373             }
374              
375             elsif ( $ref eq 'HASH' ) {
376 16         87 $self->push_object();
377 16         53 $stash->{$val} = $self->get_heapptr(-1);
378 16         25 while ( my ( $k, $v ) = each %{$val} ) {
  45         147  
379 29         72 $self->push_string($k);
380 29 100 100     195 if ( $v && $stash->{$v} ) {
381 1         6 $self->push_heapptr( $stash->{$v} );
382             }
383             else {
384 28         102 $self->push_perl( $v, $stash );
385             }
386 29         90 $self->put_prop(-3);
387             }
388             }
389              
390             elsif ( $ref eq 'CODE' ) {
391 46         167 $self->push_function($val);
392             }
393              
394             elsif ( $ref eq 'JavaScript::Embedded::Object' ) {
395 15         36 $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       19 my $len = defined $$val ? length($$val) : 0;
408 6         8651 my $ptr = $self->push_fixed_buffer($len);
409 6         20 poke_buffer( $ptr, pv_address($$val), $len );
410             }
411              
412             elsif ( $ref eq 'SCALAR' ) {
413 2 100       8 $$val ? $self->push_true() : $self->push_false()
414             }
415              
416             else {
417 0         0 $self->push_undefined();
418             }
419             }
420             else {
421 162374 100       274778 if ( !defined $val ) {
    100          
422 3         10 $self->push_undefined();
423             }
424             elsif ( duk_sv_is_number($val) ) {
425 161941         276020 $self->push_number($val);
426             }
427             else {
428 430         11448 $self->push_string($val);
429             }
430             }
431             }
432              
433             sub to_perl_object {
434 245     245   533 my $self = shift;
435 245         218 my $index = shift;
436 245         355 my $heapptr = $self->get_heapptr($index);
437 245 50       345 if ( !$heapptr ) { croak "value at stack $index is not an object" }
  0         0  
438 245         588 return JavaScript::Embedded::Util::jsObject(
439             {
440             duk => $self,
441             heapptr => $heapptr
442             }
443             );
444             }
445              
446             sub to_perl {
447 12350     12350   10376 my $self = shift;
448 12350         9479 my $index = shift;
449 12350   100     21963 my $stash = shift || {};
450              
451 12350         10388 my $ret;
452              
453 12350         15239 my $type = $self->get_type($index);
454              
455 12350 100       16790 if ( $type == JavaScript::Embedded::DUK_TYPE_UNDEFINED ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
456 173         193 $ret = undef;
457             }
458              
459             elsif ( $type == JavaScript::Embedded::DUK_TYPE_STRING ) {
460 8513         13587 $ret = $self->get_utf8_string($index);
461             }
462              
463             elsif ( $type == JavaScript::Embedded::DUK_TYPE_NUMBER ) {
464 2326         2965 $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         13 $ret = peek( $ptr, $sz );
470             }
471              
472             elsif ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT ) {
473              
474 1288 100       2106 if ( $self->is_function($index) ) {
475 444         544 my $ptr = $self->get_heapptr($index);
476             return sub {
477 9     9   80 $self->push_heapptr($ptr);
478 9         31 $self->push_this();
479 9         18 my $len = 0 + @_;
480 9         29 for ( my $i = 0 ; $i < $len ; $i++ ) {
481 9         22 $self->push_perl( $_[$i] );
482             }
483 9 100       98 if ( $self->pcall_method($len) == 1 ) {
484 5         20 croak $self->last_error_string();
485             }
486 4         13 my $ret = $self->to_perl(-1);
487 4         10 $self->pop();
488 4         10 return $ret;
489 444         2064 };
490             }
491              
492 844         1200 my $isArray = $self->is_array($index);
493              
494 844         1159 my $heapptr = $self->require_heapptr($index);
495 844 50       990 if ( $stash->{$heapptr} ) {
496 0         0 $ret = $stash->{$heapptr};
497             }
498             else {
499 844 100       1063 $ret = $isArray ? [] : {};
500 844         1373 $stash->{$heapptr} = $ret;
501             }
502              
503 844         4911 $self->enum( $index, JavaScript::Embedded::DUK_ENUM_OWN_PROPERTIES_ONLY );
504              
505 844         2619 while ( $self->next( -1, 1 ) ) {
506 5750         6634 my ( $key, $val );
507              
508 5750         6841 $key = $self->to_perl(-2);
509              
510 5750 100       8380 if ( $self->get_type(-1) == JavaScript::Embedded::DUK_TYPE_OBJECT ) {
511 1309         1673 my $heapptr = $self->get_heapptr(-1);
512 1309 100       1820 if ( $stash->{$heapptr} ) {
513 63         70 $val = $stash->{$heapptr};
514             }
515             else {
516 1246         1451 $val = $self->to_perl( -1, $stash );
517             }
518             }
519             else {
520 4441         4725 $val = $self->to_perl(-1);
521             }
522              
523 5750         8306 $self->pop_n(2);
524              
525 5750 100       5529 if ($isArray) {
526 133         453 $ret->[$key] = $val;
527             }
528             else {
529 5617         21390 $ret->{$key} = $val;
530             }
531             }
532              
533 844         1611 $self->pop();
534             }
535              
536             elsif ( $type == JavaScript::Embedded::DUK_TYPE_BOOLEAN ) {
537 39         80 my $bool = $self->get_boolean($index);
538 39 100       56 if ( $bool == 1 ) {
539 31         53 $ret = JavaScript::Embedded::Bool::true();
540             }
541             else {
542 8         38 $ret = JavaScript::Embedded::Bool::false();
543             }
544             }
545              
546             elsif ( $type == JavaScript::Embedded::DUK_TYPE_NULL ) {
547 3         64 $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         12 $ret = bless \$p, 'JavaScript::Embedded::Pointer';
553             }
554              
555 11906         18005 return $ret;
556             }
557              
558             ##############################################
559             # push functions
560             ##############################################
561             sub push_function {
562 271     271   2516 my $self = shift;
563 271         299 my $sub = shift;
564 271   100     780 my $nargs = shift || -1;
565              
566             $self->push_c_function(
567             sub {
568 162039     162039   123628 my @args;
569 162039         173157 my $top = $self->get_top();
570 162039         231557 for ( my $i = 0 ; $i < $top ; $i++ ) {
571 423         681 push @args, $self->to_perl($i);
572             }
573              
574 162039         220804 $self->push_this();
575 162039         191200 my $heap = $self->get_heapptr(-1);
576 162039         236199 $self->pop();
577              
578 162039 100       193676 if ( !$heap ) {
579 161931         214192 $self->push_global_object();
580 161931         169105 $heap = $self->get_heapptr(-1);
581 161931         175014 $self->pop();
582             }
583              
584 162039         173796 $THIS->{heapptr} = $heap;
585 162039         135283 $THIS->{duk} = $self;
586              
587 162039         222975 my $ret = $sub->(@args);
588 162018         546441 $self->push_perl($ret);
589 162018         174389 return 1;
590             },
591 271         1221 $nargs
592             );
593             }
594              
595             #####################################################################
596             # safe call
597             #####################################################################
598             sub push_c_function {
599 274     274   361 my $self = shift;
600 274         282 my $sub = shift;
601 274   100     449 my $nargs = shift || -1;
602              
603             $GlobalRef->{"$sub"} = sub {
604 162052     162052   198643 my @args = @_;
605 162052         193610 my $top = $self->get_top();
606 162052         128929 my $ret = 1;
607              
608             my $err = $self->safe_call(
609             sub {
610 162052         155976 $ret = $sub->(@args);
611 162031         157408 return 1;
612             },
613 162052         403914 $top,
614             1
615             );
616              
617 162052 100       264183 if ($err) {
618 21         58 croak $self->last_error_string();
619             }
620 162031         1078126 return $ret;
621 274         1210 };
622              
623 274         1319 $self->perl_push_function( $GlobalRef->{"$sub"}, $nargs );
624 274         9878 $self->eval_string("(function(){perlFinalizer('$sub')})");
625 274         1627 $self->set_finalizer(-2);
626             }
627              
628             #####################################################################
629             # safe call
630             #####################################################################
631             sub safe_call {
632 162099     162099   155142 my $self = shift;
633 162099         123905 my $sub = shift;
634 162099         122080 my $ret;
635             my $safe = sub {
636 162099     162099   142933 local $@;
637 162099         146118 eval { $ret = $sub->($self) };
  162099         163968  
638 162099 100       237446 if ( my $error = $@ ) {
639 37 100       247 if ( $error =~ /^Duk::Error/i ) {
640 22         87 croak $self->last_error_string();
641             }
642             else {
643 15         650 $self->eval_string('(function (e){ throw new Error(e) })');
644 15         76 $self->push_string($error);
645 15         212 $self->call(1);
646             }
647             }
648              
649 162062 50       263421 return defined $ret ? $ret : 1;
650 162099         242549 };
651              
652 162099         157018 eval { $ret = $self->perl_duk_safe_call( $safe, @_ ) };
  162099         238848  
653 162099 100       366442 return defined $ret ? $ret : 1;
654             }
655              
656             sub set_timeout {
657 4     4   1215 my $self = shift;
658 4         6 my $timeout = shift;
659              
660 4 100       116 croak "timeout must be a number" if !duk_sv_is_number($timeout);
661 3         34 $self->perl_duk_set_timeout($timeout);
662             }
663              
664             sub resize_memory {
665 2     2   5 my $self = shift;
666 2   50     9 my $max_memory = shift || 0;
667              
668 2 50       9 croak "max_memory should be a number" if !duk_sv_is_number( $max_memory );
669 2 100       141 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   86 my $self = shift;
693 57         175 $self->dup(-1);
694 57         484 my $error_str = $self->safe_to_string(-1);
695 57         166 $self->pop();
696 57         8728 return $error_str;
697             }
698              
699             sub dump {
700 3     3   19 my $self = shift;
701 3   100     24 my $name = shift || "Duktape";
702 3   50     16 my $fh = shift || \*STDOUT;
703 3         12 my $n = $self->get_top();
704 3         159 printf $fh "%s (top=%ld):", $name, $n;
705 3         19 for ( my $i = 0 ; $i < $n ; $i++ ) {
706 4         38 printf $fh " ";
707 4         21 $self->dup($i);
708 4         65 printf $fh "%s", $self->safe_to_string(-1);
709 4         26 $self->pop();
710             }
711 3         29 printf $fh "\n";
712             }
713              
714       0     sub DESTROY { }
715              
716             package JavaScript::Embedded::Bool;
717             {
718 56     56   1362498 use warnings;
  56         116  
  56         3141  
719 56     56   343 use strict;
  56         104  
  56         4853  
720             our ( $true, $false );
721             use overload
722 18     18   1616 '""' => sub { ${ $_[0] } },
  18         92  
723 43 100   43   2029 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  43         110  
724 56     56   60443 fallback => 1;
  56         46864  
  56         688  
725              
726             BEGIN {
727 56     56   8530 my $use_boolean = eval { require boolean; 1; };
  56         8520  
  0         0  
728 56         210 my $t = 1;
729 56         96 my $f = 0;
730 56 50       217 $true = $use_boolean ? boolean::true() : bless \$t, 'JavaScript::Embedded::Bool';
731 56 50       4824 $false = $use_boolean ? boolean::false() : bless \$f, 'JavaScript::Embedded::Bool';
732             }
733              
734 31     31   39 sub true { $true }
735 8     8   15 sub false { $false }
736              
737 2 100   2   134 sub TO_JSON { ${$_[0]} ? \1 : \0 }
  2         29  
738             }
739              
740             package JavaScript::Embedded::NULL;
741             {
742 56     56   369 use warnings;
  56         100  
  56         1673  
743 56     56   324 use strict;
  56         131  
  56         4500  
744             our ($null);
745             use overload
746 2     2   201 '""' => sub { ${ $_[0] } },
  2         11  
747 5 50   5   6 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  5         13  
748 56     56   346 fallback => 1;
  56         137  
  56         372  
749              
750             BEGIN {
751 56     56   4869 my $n = '';
752 56         2744 $null = bless \$n, 'JavaScript::Embedded::NULL';
753             }
754              
755 3     3   8 sub null { $null }
756             }
757              
758             package JavaScript::Embedded::Object;
759             {
760 56     56   367 use warnings;
  56         132  
  56         1903  
761 56     56   301 use strict;
  56         108  
  56         1246  
762 56     56   315 use Carp;
  56         138  
  56         3831  
763 56     56   352 use Data::Dumper;
  56         100  
  56         3290  
764             my $CONSTRUCTORS = {};
765 56     56   323 use Scalar::Util 'weaken';
  56         96  
  56         4283  
766             use overload '""' => sub {
767 3     3   324 my $self = shift;
768 3         7 $self->inspect();
769             },
770 56     56   312 fallback => 1;
  56         96  
  56         330  
771              
772             sub inspect {
773 3     3   3 my $self = shift;
774 3         56 my $heapptr = $self->{heapptr};
775 3         4 my $duk = $self->{duk};
776 3         7 $duk->push_heapptr($heapptr);
777 3         32 my $ret = $duk->to_perl(-1);
778 3         6 $duk->pop();
779 3         9 return $ret;
780             }
781              
782             our $AUTOLOAD;
783              
784             sub AUTOLOAD {
785 525     525   10931 my $self = shift;
786 525         762 my $heapptr = $self->{heapptr};
787 525         549 my $duk = $self->{duk};
788 525         2949 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
789 525 50       1030 return if $method eq 'DESTROY';
790 525         886 return JavaScript::Embedded::Util::autoload( $self, $method, $duk, $heapptr, @_ );
791             }
792              
793             DESTROY {
794 257     257   64608 my $self = shift;
795 257         376 my $duk = $self->{duk};
796              
797 257         326 my $refcount = delete $self->{refcount};
798 257 100       474 return if ( !$refcount );
799 240         700 $duk->push_global_stash();
800 240         628 $duk->get_prop_string( -1, "PerlGlobalStash" );
801 240         434 $duk->push_number($refcount);
802 240         2264 $duk->del_prop(-2);
803 240         798 $duk->pop_2();
804             }
805             }
806              
807             package JavaScript::Embedded::Function;
808             {
809 56     56   19770 use strict;
  56         110  
  56         1419  
810 56     56   284 use warnings;
  56         108  
  56         1516  
811 56     56   254 use Data::Dumper;
  56         96  
  56         10974  
812              
813             sub new {
814 136     136   7403 my $self = shift;
815 136         241 $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   320 use strict;
  56         123  
  56         1328  
836 56     56   247 use warnings;
  56         101  
  56         1937  
837 56     56   279 use Data::Dumper;
  56         85  
  56         2075  
838 56     56   275 use Carp;
  56         116  
  56         44598  
839              
840             sub autoload {
841 525     525   547 my $self = shift;
842 525         498 my $method = shift;
843 525         467 my $duk = shift;
844 525         462 my $heapptr = shift;
845              
846 525         1071 $duk->push_heapptr($heapptr);
847 525 50       731 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 525         525 my $val = undef;
861 525         1600 $duk->get_prop_string( -1, $method );
862              
863 525         872 my $type = $duk->get_type(-1);
864 525 100 66     1149 if ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT
865             || $type == JavaScript::Embedded::DUK_TYPE_BUFFER )
866             {
867              
868 382 50       693 if ( $duk->is_function(-1) ) {
869 382         528 my $function_heap = $duk->get_heapptr(-1);
870              
871 382 100       481 if (@_) {
872             #called with special no arg _
873 380 100       567 shift if ( ref $_[0] eq 'NOARGS' );
874 380         562 $val = jsFunction( $method, $duk, $function_heap, $heapptr, 'call', @_ );
875             }
876             else {
877 2         13 $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         244 $val = $duk->to_perl(-1);
886             }
887 525         890 $duk->pop_2();
888 525         1317 return $val;
889             }
890              
891             sub jsFunction {
892 387     387   438 my $methodname = shift;
893 387         330 my $duk = shift;
894 387         334 my $heapptr = shift;
895 387   33     559 my $constructor = shift || $heapptr;
896 387         344 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 518     518   474 my $isNew;
903 518         649 my $ref = ref $_[0];
904 518 100       1061 if ( $ref eq "NEW" ) {
    50          
    50          
905 136         125 shift;
906 136         134 $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 518         633 my $len = @_ + 0;
916 518         918 $duk->push_heapptr($heapptr);
917 518 100       922 $duk->push_heapptr($constructor) if !$isNew;
918 518         666 foreach my $val (@_) {
919 546 100       722 if ( ref $val eq 'CODE' ) {
920 211         318 $duk->push_function($val);
921             }
922             else {
923 335         474 $duk->push_perl($val);
924             }
925             }
926              
927 518 100       731 if ($isNew) {
928 136 50       1074 if ( $duk->pnew($len) != 0 ) {
929 0         0 croak $duk->last_error_string();
930             }
931             }
932             else {
933 382 50       1935 if ( $duk->pcall_method($len) != 0 ) {
934 0         0 croak $duk->last_error_string();
935             }
936             }
937              
938 518         550 my $ret;
939             ##getting function call values
940 518         799 my $type = $duk->get_type(-1);
941 518 100 66     1180 if ( $type == JavaScript::Embedded::DUK_TYPE_OBJECT
942             || $type == JavaScript::Embedded::DUK_TYPE_BUFFER )
943             {
944 205         339 $ret = $duk->to_perl_object(-1);
945             }
946             else {
947 313         429 $ret = $duk->to_perl(-1);
948             }
949 518         1161 $duk->pop();
950 518         2589 return $ret;
951 387         1395 };
952              
953 387 100       853 return $sub->(@_) if $doCall;
954 7         38 return bless $sub, "JavaScript::Embedded::Function";
955             }
956              
957             my $REFCOUNT = 0;
958              
959             sub jsObject {
960 245     245   257 my $options = shift;
961              
962 245         274 my $duk = $options->{duk};
963 245         238 my $heapptr = $options->{heapptr};
964 245   33     561 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 245         690 my $refcount = ( ++$REFCOUNT ) + ( rand(3) );
976              
977 245         567 $duk->push_global_stash();
978 245         523 $duk->get_prop_string( -1, "PerlGlobalStash" );
979 245         468 $duk->push_number($refcount);
980 245         368 $duk->push_heapptr($heapptr);
981 245         1729 $duk->put_prop(-3); #PerlGlobalStash[heapptr] = object
982 245         451 $duk->pop_2();
983              
984 245         328 my $type = $duk->get_type(-1);
985              
986 245 100       495 if ( $duk->is_function(-1) ) {
987 5         18 return JavaScript::Embedded::Util::jsFunction( 'anon', $duk, $heapptr, $constructor );
988             }
989              
990 240         720 return bless {
991             refcount => $refcount,
992             duk => $duk,
993             heapptr => $heapptr
994             }, "JavaScript::Embedded::Object";
995             }
996             }
997              
998             1;
999              
1000             __END__