File Coverage

lib/JavaScript/Duktape.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::Duktape;
2 56     56   435381 use strict;
  56         503  
  56         1573  
3 56     56   282 use warnings;
  56         95  
  56         1401  
4 56     56   263 use Carp;
  56         112  
  56         5718  
5 56     56   27165 use Data::Dumper;
  56         298394  
  56         3779  
6 56     56   2449 use Scalar::Util qw( weaken );
  56         98  
  56         9048  
7             our $VERSION = '2.5.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   398 use base qw/Exporter/;
  56         111  
  56         18345  
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         82703 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   478 };
  56         134  
131              
132             sub new {
133 74     74 0 1611527 my $class = shift;
134 74         236 my %options = @_;
135              
136 74   100     523 my $max_memory = $options{max_memory} || 0;
137 74   100     370 my $timeout = $options{timeout} || 0;
138              
139 74 100       287 if ($timeout){
140 2 100       133 croak "timeout option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $timeout );
141             }
142              
143 73 100       224 if ( $max_memory ){
144 6 100       183 croak "max_memory option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $max_memory );
145 5 100       226 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
146             }
147              
148 71         205 my $self = bless {}, $class;
149              
150 71         55576 my $duk = $self->{duk} = JavaScript::Duktape::Vm->perl_duk_new( $max_memory, $timeout );
151              
152 71         438 $self->{pid} = $$;
153 71         199 $self->{max_memory} = $max_memory;
154              
155             # Initialize global stash 'PerlGlobalStash'
156             # this will be used to store some perl refs
157 71         831 $duk->push_global_stash();
158 71         364 $duk->push_object();
159 71         419 $duk->put_prop_string( -2, "PerlGlobalStash" );
160 71         341 $duk->pop();
161              
162 71         506 $THIS = bless { duk => $duk, heapptr => 0 }, "JavaScript::Duktape::Object";
163              
164             ##finalizer method
165             $self->{finalizer} = sub {
166 274     274   2163 my $ref = $duk->get_string(0);
167 274         4772 delete $GlobalRef->{$ref};
168 274         14769 return 1;
169 71         493 };
170              
171 71         414 weaken $GlobalRef;
172              
173 71         481 $duk->perl_push_function( $self->{finalizer}, 1 );
174 71         363 $duk->put_global_string('perlFinalizer');
175              
176 71         304 return $self;
177             }
178              
179 2     2 1 16 sub null { $JavaScript::Duktape::NULL::null; }
180 9     9 1 16247 sub true { $JavaScript::Duktape::Bool::true; }
181 7     7 1 38 sub false { $JavaScript::Duktape::Bool::false }
182 3     3   35 sub JavaScript::Duktape::_ { $NOARGS }
183 4     4 1 50 sub this { $THIS }
184              
185             sub set {
186 46     46 1 14253 my $self = shift;
187 46         86 my $name = shift;
188 46         63 my $val = shift;
189 46         115 my $duk = $self->vm;
190              
191 46 100       187 if ( $name =~ /\./ ) {
192              
193 2         7 my @props = split /\./, $name;
194 2         4 my $last = pop @props;
195 2         6 my $others = join '.', @props;
196              
197 2 50       69 if ( $duk->peval_string($others) != 0 ) {
198 0         0 croak $others . " is not a javascript object ";
199             }
200              
201 2         11 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         8 $duk->push_string($last);
207 2         6 $duk->push_perl($val);
208 2         10 $duk->put_prop(-3);
209 2         5 $duk->pop();
210 2         8 return 1;
211             }
212              
213 44         152 $duk->push_perl($val);
214 44         200 $duk->put_global_string($name);
215 44         132 return 1;
216             }
217              
218             sub get {
219 1     1 1 7 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       35 if ( $duk->peval() != 0 ) {
224 0         0 croak $duk->last_error_string();
225             }
226 1         3 my $ret = $duk->to_perl(-1);
227 1         4 $duk->pop();
228 1         4 return $ret;
229             }
230              
231             sub get_object {
232 6     6 1 168 my $self = shift;
233 6         9 my $name = shift;
234 6         11 my $duk = $self->vm;
235 6         20 $duk->push_string($name);
236 6 50       204 if ( $duk->peval() != 0 ) {
237 0         0 croak $duk->last_error_string();
238             }
239 6         24 my $ret = $duk->to_perl_object(-1);
240 6         23 $duk->pop();
241 6         15 return $ret;
242             }
243              
244             ##FIXME : should pop here?
245             sub eval {
246 31     31 1 1871 my $self = shift;
247 31         66 my $string = shift;
248 31         79 my $duk = $self->duk;
249              
250 31 100       3722318 if ( $duk->peval_string($string) != 0 ) {
251 9         48 croak $duk->last_error_string();
252             }
253              
254 22         113 return $duk->to_perl(-1);
255             }
256              
257 53     53 0 102 sub vm { shift->{duk}; }
258 83     83 0 452 sub duk { shift->{duk}; }
259              
260             sub set_timeout {
261 2     2 0 13 my $self = shift;
262 2         6 $self->duk->set_timeout( shift );
263             }
264              
265             sub resize_memory {
266 2     2 1 2624472 my $self = shift;
267 2         10 $self->duk->resize_memory( shift );
268             }
269              
270             sub destroy {
271 71     71 0 140 local $@;
272 71         134 my $self = shift;
273 71         168 my $duk = delete $self->{duk};
274 71 50       863 return if !$duk;
275 71         382 $duk->free_perl_duk();
276 71         29573 $duk->destroy_heap();
277             }
278              
279             sub DESTROY {
280 71     71   3764810 my $self = shift;
281 71 50 33     727 if ( $self->{pid} && $self->{pid} == $$ ) {
282 71         617 $self->destroy();
283             }
284             }
285              
286             package JavaScript::Duktape::Vm;
287 56     56   444 use strict;
  56         94  
  56         1721  
288 56     56   298 use warnings;
  56         103  
  56         2191  
289 56     56   336 no warnings 'redefine';
  56         125  
  56         2330  
290 56     56   373 use Data::Dumper;
  56         150  
  56         3231  
291 56     56   399 use Config qw( %Config );
  56         129  
  56         2316  
292 56     56   22042 use JavaScript::Duktape::C::libPath;
  56         120  
  56         1693  
293 56     56   340 use Carp;
  56         100  
  56         7182  
294              
295             my $Duklib;
296              
297             my $BOOL_PACKAGES = {
298             'JavaScript::Duktape::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   271 my $FunctionsMap = _get_path("FunctionsMap.pl");
307 56         30447 require $FunctionsMap;
308              
309 336     336   861 sub _get_path { &JavaScript::Duktape::C::libPath::getPath }
310              
311 56 50       542 $Duklib =
312             $^O eq 'MSWin32'
313             ? _get_path('duktape.dll')
314             : _get_path('duktape.so');
315             }
316              
317 56         272 use Inline C => config =>
318             typemaps => _get_path('typemap'),
319 56     56   37233 INC => '-I' . _get_path('../C') . ' -I' . _get_path('../C/lib');
  56         1074256  
320             # myextlib => $Duklib,
321             # LIBS => '-L'. _get_path('../C/lib') . ' -lduktape';
322              
323 56     56   9081 use Inline C => _get_path('duk_perl.c');
  56         122  
  56         139  
324              
325 56         294 use Inline C => q{
326             void poke_buffer(IV to, IV from, IV sz) {
327             memcpy( to, from, sz );
328             }
329 56     56   27970471 };
  56         145  
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   11701 sub peek { unpack 'P' . $_[1], pack $ptr_format, $_[0] }
339 6     6   2471 sub pv_address { unpack( $ptr_format, pack( "p", $_[0] ) ) }
340              
341             sub push_perl {
342 152122     152122   159254 my $self = shift;
343 152122         156493 my $val = shift;
344 152122   100     366417 my $stash = shift || {};
345              
346 152122 100       246190 if ( my $ref = ref $val ) {
347 100 100       653 if ( $ref eq 'JavaScript::Duktape::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         23 $self->push_true();
354             }
355             else {
356 3         22 $self->push_false();
357             }
358             }
359              
360             elsif ( $ref eq 'ARRAY' ) {
361 12         39 my $arr_idx = $self->push_array();
362 12         55 $stash->{$val} = $self->get_heapptr(-1);
363 12         21 my $len = scalar @{$val};
  12         19  
364 12         37 for ( my $idx = 0 ; $idx < $len ; $idx++ ) {
365 32 100       77 if ( $stash->{ $val->[$idx] } ) {
366 2         5 $self->push_heapptr( $stash->{ $val->[$idx] } );
367             }
368             else {
369 30         66 $self->push_perl( $val->[$idx], $stash );
370             }
371 32         151 $self->put_prop_index( $arr_idx, $idx );
372             }
373             }
374              
375             elsif ( $ref eq 'HASH' ) {
376 16         45 $self->push_object();
377 16         58 $stash->{$val} = $self->get_heapptr(-1);
378 16         28 while ( my ( $k, $v ) = each %{$val} ) {
  45         166  
379 29         109 $self->push_string($k);
380 29 100 100     193 if ( $v && $stash->{$v} ) {
381 1         3 $self->push_heapptr( $stash->{$v} );
382             }
383             else {
384 28         149 $self->push_perl( $v, $stash );
385             }
386 29         103 $self->put_prop(-3);
387             }
388             }
389              
390             elsif ( $ref eq 'CODE' ) {
391 46         154 $self->push_function($val);
392             }
393              
394             elsif ( $ref eq 'JavaScript::Duktape::Object' ) {
395 11         31 $self->push_heapptr( $val->{heapptr} );
396             }
397              
398             elsif ( $ref eq 'JavaScript::Duktape::Function' ) {
399 0         0 $self->push_heapptr( $val->($HEAP) );
400             }
401              
402             elsif ( $ref eq 'JavaScript::Duktape::Pointer' ) {
403 0         0 $self->push_pointer($$val);
404             }
405              
406             elsif ( $ref eq 'JavaScript::Duktape::Buffer' ) {
407 6 100       37 my $len = defined $$val ? length($$val) : 0;
408 6         10067 my $ptr = $self->push_fixed_buffer($len);
409 6         24 poke_buffer( $ptr, pv_address($$val), $len );
410             }
411              
412             elsif ( $ref eq 'SCALAR' ) {
413 2 100       13 $$val ? $self->push_true() : $self->push_false()
414             }
415              
416             else {
417 0         0 $self->push_undefined();
418             }
419             }
420             else {
421 152022 100       324109 if ( !defined $val ) {
    100          
422 3         12 $self->push_undefined();
423             }
424             elsif ( duk_sv_is_number($val) ) {
425 151589         322761 $self->push_number($val);
426             }
427             else {
428 430         11716 $self->push_string($val);
429             }
430             }
431             }
432              
433             sub to_perl_object {
434 237     237   635 my $self = shift;
435 237         264 my $index = shift;
436 237         470 my $heapptr = $self->get_heapptr($index);
437 237 50       391 if ( !$heapptr ) { croak "value at stack $index is not an object" }
  0         0  
438 237         750 return JavaScript::Duktape::Util::jsObject(
439             {
440             duk => $self,
441             heapptr => $heapptr
442             }
443             );
444             }
445              
446             sub to_perl {
447 12334     12334   12820 my $self = shift;
448 12334         11529 my $index = shift;
449 12334   100     26740 my $stash = shift || {};
450              
451 12334         13043 my $ret;
452              
453 12334         18302 my $type = $self->get_type($index);
454              
455 12334 100       20520 if ( $type == JavaScript::Duktape::DUK_TYPE_UNDEFINED ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
456 173         250 $ret = undef;
457             }
458              
459             elsif ( $type == JavaScript::Duktape::DUK_TYPE_STRING ) {
460 8505         16647 $ret = $self->get_utf8_string($index);
461             }
462              
463             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NUMBER ) {
464 2318         3671 $ret = $self->get_number($index);
465             }
466              
467             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BUFFER ) {
468 5         24 my $ptr = $self->get_buffer_data( $index, my $sz );
469 5         15 $ret = peek( $ptr, $sz );
470             }
471              
472             elsif ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
473              
474 1288 100       2520 if ( $self->is_function($index) ) {
475 444         635 my $ptr = $self->get_heapptr($index);
476             return sub {
477 9     9   81 $self->push_heapptr($ptr);
478 9         20 $self->push_this();
479 9         17 my $len = 0 + @_;
480 9         26 for ( my $i = 0 ; $i < $len ; $i++ ) {
481 9         30 $self->push_perl( $_[$i] );
482             }
483 9 100       102 if ( $self->pcall_method($len) == 1 ) {
484 5         13 croak $self->last_error_string();
485             }
486 4         18 my $ret = $self->to_perl(-1);
487 4         35 $self->pop();
488 4         8 return $ret;
489 444         2462 };
490             }
491              
492 844         1389 my $isArray = $self->is_array($index);
493              
494 844         1430 my $heapptr = $self->require_heapptr($index);
495 844 50       1310 if ( $stash->{$heapptr} ) {
496 0         0 $ret = $stash->{$heapptr};
497             }
498             else {
499 844 100       1410 $ret = $isArray ? [] : {};
500 844         1652 $stash->{$heapptr} = $ret;
501             }
502              
503 844         5845 $self->enum( $index, JavaScript::Duktape::DUK_ENUM_OWN_PROPERTIES_ONLY );
504              
505 844         2932 while ( $self->next( -1, 1 ) ) {
506 5750         7563 my ( $key, $val );
507              
508 5750         7987 $key = $self->to_perl(-2);
509              
510 5750 100       10604 if ( $self->get_type(-1) == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
511 1309         2087 my $heapptr = $self->get_heapptr(-1);
512 1309 100       2329 if ( $stash->{$heapptr} ) {
513 63         81 $val = $stash->{$heapptr};
514             }
515             else {
516 1246         1695 $val = $self->to_perl( -1, $stash );
517             }
518             }
519             else {
520 4441         5766 $val = $self->to_perl(-1);
521             }
522              
523 5750         10182 $self->pop_n(2);
524              
525 5750 100       6933 if ($isArray) {
526 133         499 $ret->[$key] = $val;
527             }
528             else {
529 5617         24680 $ret->{$key} = $val;
530             }
531             }
532              
533 844         1953 $self->pop();
534             }
535              
536             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BOOLEAN ) {
537 39         100 my $bool = $self->get_boolean($index);
538 39 100       55 if ( $bool == 1 ) {
539 31         64 $ret = JavaScript::Duktape::Bool::true();
540             }
541             else {
542 8         22 $ret = JavaScript::Duktape::Bool::false();
543             }
544             }
545              
546             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NULL ) {
547 3         8 $ret = JavaScript::Duktape::NULL::null();
548             }
549              
550             elsif ( $type == JavaScript::Duktape::DUK_TYPE_POINTER ) {
551 3         10 my $p = $self->get_pointer($index);
552 3         12 $ret = bless \$p, 'JavaScript::Duktape::Pointer';
553             }
554              
555 11890         22160 return $ret;
556             }
557              
558             ##############################################
559             # push functions
560             ##############################################
561             sub push_function {
562 271     271   2987 my $self = shift;
563 271         322 my $sub = shift;
564 271   100     833 my $nargs = shift || -1;
565              
566             $self->push_c_function(
567             sub {
568 151688     151688   144673 my @args;
569 151688         205911 my $top = $self->get_top();
570 151688         285476 for ( my $i = 0 ; $i < $top ; $i++ ) {
571 411         796 push @args, $self->to_perl($i);
572             }
573              
574 151688         241656 $self->push_this();
575 151688         213305 my $heap = $self->get_heapptr(-1);
576 151688         256879 $self->pop();
577              
578 151688 100       214270 if ( !$heap ) {
579 151580         244197 $self->push_global_object();
580 151580         197330 $heap = $self->get_heapptr(-1);
581 151580         191150 $self->pop();
582             }
583              
584 151688         188405 $THIS->{heapptr} = $heap;
585 151688         151866 $THIS->{duk} = $self;
586              
587 151688         240024 my $ret = $sub->(@args);
588 151666         608733 $self->push_perl($ret);
589 151666         201128 return 1;
590             },
591 271         1289 $nargs
592             );
593             }
594              
595             #####################################################################
596             # safe call
597             #####################################################################
598             sub push_c_function {
599 274     274   412 my $self = shift;
600 274         349 my $sub = shift;
601 274   100     536 my $nargs = shift || -1;
602              
603             $GlobalRef->{"$sub"} = sub {
604 151701     151701   250916 my @args = @_;
605 151701         219885 my $top = $self->get_top();
606 151701         155939 my $ret = 1;
607              
608             my $err = $self->safe_call(
609             sub {
610 151701         200349 $ret = $sub->(@args);
611 151679         187920 return 1;
612             },
613 151701         417230 $top,
614             1
615             );
616              
617 151701 100       301427 if ($err) {
618 22         67 croak $self->last_error_string();
619             }
620 151679         1304435 return $ret;
621 274         1426 };
622              
623 274         1504 $self->perl_push_function( $GlobalRef->{"$sub"}, $nargs );
624 274         12305 $self->eval_string("(function(){perlFinalizer('$sub')})");
625 274         1869 $self->set_finalizer(-2);
626             }
627              
628             #####################################################################
629             # safe call
630             #####################################################################
631             sub safe_call {
632 151748     151748   183330 my $self = shift;
633 151748         154666 my $sub = shift;
634 151748         150891 my $ret;
635             my $safe = sub {
636 151748     151748   164792 local $@;
637 151748         183229 eval { $ret = $sub->($self) };
  151748         189159  
638 151748 100       275815 if ( my $error = $@ ) {
639 38 100       233 if ( $error =~ /^Duk::Error/i ) {
640 22         83 croak $self->last_error_string();
641             }
642             else {
643 16         881 $self->eval_string('(function (e){ throw new Error(e) })');
644 16         82 $self->push_string($error);
645 16         234 $self->call(1);
646             }
647             }
648              
649 151710 50       330622 return defined $ret ? $ret : 1;
650 151748         273081 };
651              
652 151748         183968 eval { $ret = $self->perl_duk_safe_call( $safe, @_ ) };
  151748         288679  
653 151748 100       419336 return defined $ret ? $ret : 1;
654             }
655              
656             sub set_timeout {
657 4     4   1326 my $self = shift;
658 4         9 my $timeout = shift;
659              
660 4 100       138 croak "timeout must be a number" if !duk_sv_is_number($timeout);
661 3         45 $self->perl_duk_set_timeout($timeout);
662             }
663              
664             sub resize_memory {
665 2     2   4 my $self = shift;
666 2   50     15 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       159 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
670              
671 1         8 $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 58     58   87 my $self = shift;
693 58         222 $self->dup(-1);
694 58         548 my $error_str = $self->safe_to_string(-1);
695 58         210 $self->pop();
696 58         9508 return $error_str;
697             }
698              
699             sub dump {
700 3     3   27 my $self = shift;
701 3   100     15 my $name = shift || "Duktape";
702 3   50     16 my $fh = shift || \*STDOUT;
703 3         12 my $n = $self->get_top();
704 3         146 printf $fh "%s (top=%ld):", $name, $n;
705 3         22 for ( my $i = 0 ; $i < $n ; $i++ ) {
706 4         40 printf $fh " ";
707 4         29 $self->dup($i);
708 4         71 printf $fh "%s", $self->safe_to_string(-1);
709 4         30 $self->pop();
710             }
711 3         34 printf $fh "\n";
712             }
713              
714       0     sub DESTROY { }
715              
716             package JavaScript::Duktape::Bool;
717             {
718 56     56   1584666 use warnings;
  56         145  
  56         3571  
719 56     56   355 use strict;
  56         138  
  56         5844  
720             our ( $true, $false );
721             use overload
722 18     18   2034 '""' => sub { ${ $_[0] } },
  18         110  
723 43 100   43   2346 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  43         142  
724 56     56   69113 fallback => 1;
  56         56588  
  56         555  
725              
726             BEGIN {
727 56     56   9298 my $use_boolean = eval { require boolean; 1; };
  56         9605  
  0         0  
728 56         255 my $t = 1;
729 56         125 my $f = 0;
730 56 50       289 $true = $use_boolean ? boolean::true() : bless \$t, 'JavaScript::Duktape::Bool';
731 56 50       6367 $false = $use_boolean ? boolean::false() : bless \$f, 'JavaScript::Duktape::Bool';
732             }
733              
734 31     31   49 sub true { $true }
735 8     8   15 sub false { $false }
736              
737 2 100   2   205 sub TO_JSON { ${$_[0]} ? \1 : \0 }
  2         33  
738             }
739              
740             package JavaScript::Duktape::NULL;
741             {
742 56     56   415 use warnings;
  56         119  
  56         1817  
743 56     56   319 use strict;
  56         163  
  56         5870  
744             our ($null);
745             use overload
746 2     2   226 '""' => sub { ${ $_[0] } },
  2         14  
747 5 50   5   8 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  5         13  
748 56     56   428 fallback => 1;
  56         152  
  56         547  
749              
750             BEGIN {
751 56     56   5861 my $n = '';
752 56         3238 $null = bless \$n, 'JavaScript::Duktape::NULL';
753             }
754              
755 3     3   8 sub null { $null }
756             }
757              
758             package JavaScript::Duktape::Object;
759             {
760 56     56   404 use warnings;
  56         120  
  56         1861  
761 56     56   308 use strict;
  56         128  
  56         1326  
762 56     56   277 use Carp;
  56         120  
  56         3807  
763 56     56   376 use Data::Dumper;
  56         142  
  56         3483  
764             my $CONSTRUCTORS = {};
765 56     56   436 use Scalar::Util 'weaken';
  56         149  
  56         5231  
766             use overload '""' => sub {
767 3     3   436 my $self = shift;
768 3         8 $self->inspect();
769             },
770 56     56   426 fallback => 1;
  56         119  
  56         422  
771              
772             sub inspect {
773 3     3   4 my $self = shift;
774 3         70 my $heapptr = $self->{heapptr};
775 3         7 my $duk = $self->{duk};
776 3         7 $duk->push_heapptr($heapptr);
777 3         9 my $ret = $duk->to_perl(-1);
778 3         7 $duk->pop();
779 3         9 return $ret;
780             }
781              
782             our $AUTOLOAD;
783              
784             sub AUTOLOAD {
785 521     521   11593 my $self = shift;
786 521         872 my $heapptr = $self->{heapptr};
787 521         634 my $duk = $self->{duk};
788 521         3740 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
789 521 50       1274 return if $method eq 'DESTROY';
790 521         1027 return JavaScript::Duktape::Util::autoload( $self, $method, $duk, $heapptr, @_ );
791             }
792              
793             DESTROY {
794 249     249   69002 my $self = shift;
795 249         431 my $duk = $self->{duk};
796              
797 249         418 my $refcount = delete $self->{refcount};
798 249 100       532 return if ( !$refcount );
799 232         849 $duk->push_global_stash();
800 232         661 $duk->get_prop_string( -1, "PerlGlobalStash" );
801 232         483 $duk->push_number($refcount);
802 232         2580 $duk->del_prop(-2);
803 232         966 $duk->pop_2();
804             }
805             }
806              
807             package JavaScript::Duktape::Function;
808             {
809 56     56   22007 use strict;
  56         122  
  56         1562  
810 56     56   297 use warnings;
  56         116  
  56         1682  
811 56     56   377 use Data::Dumper;
  56         138  
  56         13886  
812              
813             sub new {
814 136     136   7580 my $self = shift;
815 136         314 $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::Duktape::Util::autoload( $self, $method, $duk, $heapptr, @_ );
828             }
829              
830       0     sub DESTROY { }
831             };
832              
833             package JavaScript::Duktape::Util;
834             {
835 56     56   414 use strict;
  56         138  
  56         1566  
836 56     56   295 use warnings;
  56         107  
  56         1974  
837 56     56   304 use Data::Dumper;
  56         149  
  56         2563  
838 56     56   334 use Carp;
  56         135  
  56         51677  
839              
840             sub autoload {
841 521     521   675 my $self = shift;
842 521         654 my $method = shift;
843 521         523 my $duk = shift;
844 521         566 my $heapptr = shift;
845              
846 521         1275 $duk->push_heapptr($heapptr);
847 521 50       819 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 521         660 my $val = undef;
861 521         1852 $duk->get_prop_string( -1, $method );
862              
863 521         1117 my $type = $duk->get_type(-1);
864 521 100 66     1463 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
865             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
866             {
867              
868 378 50       850 if ( $duk->is_function(-1) ) {
869 378         601 my $function_heap = $duk->get_heapptr(-1);
870              
871 378 100       554 if (@_) {
872             #called with special no arg _
873 376 100       722 shift if ( ref $_[0] eq 'NOARGS' );
874 376         646 $val = jsFunction( $method, $duk, $function_heap, $heapptr, 'call', @_ );
875             }
876             else {
877 2         8 $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         326 $val = $duk->to_perl(-1);
886             }
887 521         1157 $duk->pop_2();
888 521         1596 return $val;
889             }
890              
891             sub jsFunction {
892 383     383   502 my $methodname = shift;
893 383         400 my $duk = shift;
894 383         394 my $heapptr = shift;
895 383   33     634 my $constructor = shift || $heapptr;
896 383         422 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 514     514   566 my $isNew;
903 514         851 my $ref = ref $_[0];
904 514 100       1251 if ( $ref eq "NEW" ) {
    50          
    50          
905 136         147 shift;
906 136         192 $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 514         724 my $len = @_ + 0;
916 514         1135 $duk->push_heapptr($heapptr);
917 514 100       1126 $duk->push_heapptr($constructor) if !$isNew;
918 514         818 foreach my $val (@_) {
919 542 100       852 if ( ref $val eq 'CODE' ) {
920 211         412 $duk->push_function($val);
921             }
922             else {
923 331         553 $duk->push_perl($val);
924             }
925             }
926              
927 514 100       882 if ($isNew) {
928 136 50       1226 if ( $duk->pnew($len) != 0 ) {
929 0         0 croak $duk->last_error_string();
930             }
931             }
932             else {
933 378 50       2224 if ( $duk->pcall_method($len) != 0 ) {
934 0         0 croak $duk->last_error_string();
935             }
936             }
937              
938 514         706 my $ret;
939             ##getting function call values
940 514         1038 my $type = $duk->get_type(-1);
941 514 100 66     2287 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
942             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
943             {
944 205         422 $ret = $duk->to_perl_object(-1);
945             }
946             else {
947 309         626 $ret = $duk->to_perl(-1);
948             }
949 514         1323 $duk->pop();
950 514         3539 return $ret;
951 383         1617 };
952              
953 383 100       971 return $sub->(@_) if $doCall;
954 7         48 return bless $sub, "JavaScript::Duktape::Function";
955             }
956              
957             my $REFCOUNT = 0;
958              
959             sub jsObject {
960 237     237   286 my $options = shift;
961              
962 237         321 my $duk = $options->{duk};
963 237         294 my $heapptr = $options->{heapptr};
964 237   33     689 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 237         817 my $refcount = ( ++$REFCOUNT ) + ( rand(3) );
976              
977 237         684 $duk->push_global_stash();
978 237         573 $duk->get_prop_string( -1, "PerlGlobalStash" );
979 237         508 $duk->push_number($refcount);
980 237         455 $duk->push_heapptr($heapptr);
981 237         2018 $duk->put_prop(-3); #PerlGlobalStash[heapptr] = object
982 237         509 $duk->pop_2();
983              
984 237         395 my $type = $duk->get_type(-1);
985              
986 237 100       1569 if ( $duk->is_function(-1) ) {
987 5         23 return JavaScript::Duktape::Util::jsFunction( 'anon', $duk, $heapptr, $constructor );
988             }
989              
990 232         914 return bless {
991             refcount => $refcount,
992             duk => $duk,
993             heapptr => $heapptr
994             }, "JavaScript::Duktape::Object";
995             }
996             }
997              
998             1;
999              
1000             __END__