File Coverage

lib/JavaScript/Duktape.pm
Criterion Covered Total %
statement 479 506 94.6
branch 129 156 82.6
condition 26 36 72.2
subroutine 89 92 96.7
pod 9 14 64.2
total 732 804 91.0


line stmt bran cond sub pod time code
1             package JavaScript::Duktape;
2 53     53   326403 use strict;
  53         139  
  53         1550  
3 53     53   309 use warnings;
  53         111  
  53         1523  
4 53     53   354 use Carp;
  53         119  
  53         3975  
5 53     53   25559 use Data::Dumper;
  53         375066  
  53         3902  
6 53     53   486 use Scalar::Util qw( weaken );
  53         123  
  53         8103  
7             our $VERSION = '2.2.1';
8              
9             my $GlobalRef = {};
10              
11             my $THIS;
12             my $DUKTAPE;
13             my $isNew = bless [], "NEW";
14             my $HEAP = bless [], "HEAP";
15             my $DUK = bless [], "DUK";
16             my $NOARGS = bless [], "NOARGS";
17              
18 53     53   439 use base qw/Exporter/;
  53         122  
  53         16060  
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 53         84237 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 53     53   429 };
  53         133  
131              
132             sub new {
133 71     71 0 432877 my $class = shift;
134 71         282 my %options = @_;
135              
136 71   100     544 my $max_memory = $options{max_memory} || 0;
137 71   100     438 my $timeout = $options{timeout} || 0;
138              
139 71 100       287 if ($timeout){
140 2 100       105 croak "timeout option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $timeout );
141             }
142              
143 70 100       254 if ( $max_memory ){
144 6 100       137 croak "max_memory option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $max_memory );
145 5 100       207 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
146             }
147              
148 68         261 my $self = bless {}, $class;
149              
150 68         48048 my $duk = $self->{duk} = JavaScript::Duktape::Vm->perl_duk_new( $max_memory, $timeout );
151              
152 68         441 $self->{pid} = $$;
153 68         201 $self->{max_memory} = $max_memory;
154              
155             # Initialize global stash 'PerlGlobalStash'
156             # this will be used to store some perl refs
157 68         878 $duk->push_global_stash();
158 68         361 $duk->push_object();
159 68         495 $duk->put_prop_string( -2, "PerlGlobalStash" );
160 68         386 $duk->pop();
161              
162 68         454 $THIS = bless { duk => $duk, heapptr => 0 }, "JavaScript::Duktape::Object";
163              
164             ##finalizer method
165             $self->{finalizer} = sub {
166 272     272   2352 my $ref = $duk->get_string(0);
167 272         3780 delete $GlobalRef->{$ref};
168 272         8801 return 1;
169 68         486 };
170              
171 68         662 weaken $GlobalRef;
172              
173 68         623 $duk->perl_push_function( $self->{finalizer}, 1 );
174 68         607 $duk->put_global_string('perlFinalizer');
175              
176 68         2517 return $self;
177             }
178              
179 2     2 1 22 sub null { $JavaScript::Duktape::NULL::null; }
180 2     2 1 1117 sub true { $JavaScript::Duktape::Bool::true; }
181 3     3 1 21 sub false { $JavaScript::Duktape::Bool::false }
182 3     3   39 sub JavaScript::Duktape::_ { $NOARGS }
183 4     4 1 83 sub this { $THIS }
184              
185             sub set {
186 41     41 1 7281 my $self = shift;
187 41         86 my $name = shift;
188 41         72 my $val = shift;
189 41         133 my $duk = $self->vm;
190              
191 41 100       196 if ( $name =~ /\./ ) {
192              
193 2         6 my @props = split /\./, $name;
194 2         5 my $last = pop @props;
195 2         5 my $others = join '.', @props;
196              
197 2 50       50 if ( $duk->peval_string($others) != 0 ) {
198 0         0 croak $others . " is not a javascript object ";
199             }
200              
201 2         47 my $type = $duk->get_type(-1);
202 2 50       8 if ( $type != DUK_TYPE_OBJECT ) {
203 0         0 croak $others . " isn't an object";
204             }
205              
206 2         10 $duk->push_string($last);
207 2         3 $duk->push_perl($val);
208 2         9 $duk->put_prop(-3);
209 2         6 $duk->pop();
210 2         5 return 1;
211             }
212              
213 39         166 $duk->push_perl($val);
214 39         214 $duk->put_global_string($name);
215 39         120 return 1;
216             }
217              
218             sub get {
219 1     1 1 5 my $self = shift;
220 1         3 my $name = shift;
221 1         3 my $duk = $self->vm;
222 1         7 $duk->push_string($name);
223 1 50       46 if ( $duk->peval() != 0 ) {
224 0         0 croak $duk->last_error_string();
225             }
226 1         4 my $ret = $duk->to_perl(-1);
227 1         3 $duk->pop();
228 1         3 return $ret;
229             }
230              
231             sub get_object {
232 6     6 1 183 my $self = shift;
233 6         14 my $name = shift;
234 6         17 my $duk = $self->vm;
235 6         29 $duk->push_string($name);
236 6 50       221 if ( $duk->peval() != 0 ) {
237 0         0 croak $duk->last_error_string();
238             }
239 6         29 my $ret = $duk->to_perl_object(-1);
240 6         28 $duk->pop();
241 6         18 return $ret;
242             }
243              
244             ##FIXME : should pop here?
245             sub eval {
246 28     28 1 1835 my $self = shift;
247 28         60 my $string = shift;
248 28         83 my $duk = $self->duk;
249              
250 28 100       3762782 if ( $duk->peval_string($string) != 0 ) {
251 9         58 croak $duk->last_error_string();
252             }
253              
254 19         99 return $duk->to_perl(-1);
255             }
256              
257 48     48 0 115 sub vm { shift->{duk}; }
258 77     77 0 1360 sub duk { shift->{duk}; }
259              
260             sub set_timeout {
261 2     2 0 16 my $self = shift;
262 2         8 $self->duk->set_timeout( shift );
263             }
264              
265             sub resize_memory {
266 2     2 1 1377967 my $self = shift;
267 2         9 $self->duk->resize_memory( shift );
268             }
269              
270             sub destroy {
271 68     68 0 163 local $@;
272 68         158 my $self = shift;
273 68         197 my $duk = delete $self->{duk};
274 68 50       1031 return if !$duk;
275 68         615 $duk->free_perl_duk();
276 68         24046 $duk->destroy_heap();
277             }
278              
279             sub DESTROY {
280 68     68   2142525 my $self = shift;
281 68 50 33     759 if ( $self->{pid} && $self->{pid} == $$ ) {
282 68         316 $self->destroy();
283             }
284             }
285              
286             package JavaScript::Duktape::Vm;
287 53     53   492 use strict;
  53         135  
  53         1852  
288 53     53   353 use warnings;
  53         144  
  53         2349  
289 53     53   975 no warnings 'redefine';
  53         126  
  53         2333  
290 53     53   331 use Data::Dumper;
  53         116  
  53         2862  
291 53     53   647 use Config qw( %Config );
  53         123  
  53         2236  
292 53     53   21960 use JavaScript::Duktape::C::libPath;
  53         143  
  53         1709  
293 53     53   369 use Carp;
  53         123  
  53         6856  
294              
295             my $Duklib;
296              
297             BEGIN {
298 53     53   290 my $FunctionsMap = _get_path("FunctionsMap.pl");
299 53         24997 require $FunctionsMap;
300              
301 318     318   1146 sub _get_path { &JavaScript::Duktape::C::libPath::getPath }
302              
303 53 50       557 $Duklib =
304             $^O eq 'MSWin32'
305             ? _get_path('duktape.dll')
306             : _get_path('duktape.so');
307             }
308              
309 53         501 use Inline C => config =>
310             typemaps => _get_path('typemap'),
311 53     53   35584 INC => '-I' . _get_path('../C') . ' -I' . _get_path('../C/lib');
  53         1020029  
312             # myextlib => $Duklib,
313             # LIBS => '-L'. _get_path('../C/lib') . ' -lduktape';
314              
315 53     53   11836 use Inline C => _get_path('duk_perl.c');
  53         146  
  53         188  
316              
317 53         350 use Inline C => q{
318             void poke_buffer(IV to, IV from, IV sz) {
319             memcpy( to, from, sz );
320             }
321 53     53   32357549 };
  53         148  
322              
323             my $ptr_format = do {
324             my $ptr_size = $Config{ptrsize};
325             $ptr_size == 4 ? "L"
326             : $ptr_size == 8 ? "Q"
327             : die("Unrecognized pointer size");
328             };
329              
330 5     5   5773 sub peek { unpack 'P' . $_[1], pack $ptr_format, $_[0] }
331 6     6   2256 sub pv_address { unpack( $ptr_format, pack( "p", $_[0] ) ) }
332              
333             sub push_perl {
334 147074     147074   188299 my $self = shift;
335 147074         175972 my $val = shift;
336 147074   100     484014 my $stash = shift || {};
337              
338 147074 100       280383 if ( my $ref = ref $val ) {
339 90 100       553 if ( $ref eq 'JavaScript::Duktape::NULL' ) {
    100          
    100          
    100          
    100          
    100          
    50          
    50          
    50          
340 1         7 $self->push_null();
341             }
342              
343             elsif ( $ref eq 'JavaScript::Duktape::Bool' ) {
344 2 100       4 if ($val) {
345 1         7 $self->push_true();
346             }
347             else {
348 1         7 $self->push_false();
349             }
350             }
351              
352             elsif ( $ref eq 'ARRAY' ) {
353 12         51 my $arr_idx = $self->push_array();
354 12         61 $stash->{$val} = $self->get_heapptr(-1);
355 12         19 my $len = scalar @{$val};
  12         24  
356 12         48 for ( my $idx = 0 ; $idx < $len ; $idx++ ) {
357 32 100       83 if ( $stash->{ $val->[$idx] } ) {
358 2         7 $self->push_heapptr( $stash->{ $val->[$idx] } );
359             }
360             else {
361 30         94 $self->push_perl( $val->[$idx], $stash );
362             }
363 32         158 $self->put_prop_index( $arr_idx, $idx );
364             }
365             }
366              
367             elsif ( $ref eq 'HASH' ) {
368 14         41 $self->push_object();
369 14         61 $stash->{$val} = $self->get_heapptr(-1);
370 14         24 while ( my ( $k, $v ) = each %{$val} ) {
  37         132  
371 23         67 $self->push_string($k);
372 23 100 100     138 if ( $v && $stash->{$v} ) {
373 1         3 $self->push_heapptr( $stash->{$v} );
374             }
375             else {
376 22         63 $self->push_perl( $v, $stash );
377             }
378 23         78 $self->put_prop(-3);
379             }
380             }
381              
382             elsif ( $ref eq 'CODE' ) {
383 43         159 $self->push_function($val);
384             }
385              
386             elsif ( $ref eq 'JavaScript::Duktape::Object' ) {
387 12         46 $self->push_heapptr( $val->{heapptr} );
388             }
389              
390             elsif ( $ref eq 'JavaScript::Duktape::Function' ) {
391 0         0 $self->push_heapptr( $val->($HEAP) );
392             }
393              
394             elsif ( $ref eq 'JavaScript::Duktape::Pointer' ) {
395 0         0 $self->push_pointer($$val);
396             }
397              
398             elsif ( $ref eq 'JavaScript::Duktape::Buffer' ) {
399 6 100       19 my $len = defined $$val ? length($$val) : 0;
400 6         3477 my $ptr = $self->push_fixed_buffer($len);
401 6         25 poke_buffer( $ptr, pv_address($$val), $len );
402             }
403             }
404             else {
405 146984 100       374933 if ( !defined $val ) {
    100          
406 3         13 $self->push_undefined();
407             }
408             elsif ( duk_sv_is_number($val) ) {
409 146551         335802 $self->push_number($val);
410             }
411             else {
412 430         7204 $self->push_string($val);
413             }
414             }
415             }
416              
417             sub to_perl_object {
418 239     239   715 my $self = shift;
419 239         313 my $index = shift;
420 239         528 my $heapptr = $self->get_heapptr($index);
421 239 50       481 if ( !$heapptr ) { croak "value at stack $index is not an object" }
  0         0  
422 239         823 return JavaScript::Duktape::Util::jsObject(
423             {
424             duk => $self,
425             heapptr => $heapptr
426             }
427             );
428             }
429              
430             sub to_perl {
431 12310     12310   14280 my $self = shift;
432 12310         13268 my $index = shift;
433 12310   100     32850 my $stash = shift || {};
434              
435 12310         14648 my $ret;
436              
437 12310         19914 my $type = $self->get_type($index);
438              
439 12310 100       24956 if ( $type == JavaScript::Duktape::DUK_TYPE_UNDEFINED ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
440 182         276 $ret = undef;
441             }
442              
443             elsif ( $type == JavaScript::Duktape::DUK_TYPE_STRING ) {
444 8501         17158 $ret = $self->get_utf8_string($index);
445             }
446              
447             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NUMBER ) {
448 2314         4147 $ret = $self->get_number($index);
449             }
450              
451             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BUFFER ) {
452 5         22 my $ptr = $self->get_buffer_data( $index, my $sz );
453 5         18 $ret = peek( $ptr, $sz );
454             }
455              
456             elsif ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
457              
458 1286 100       3266 if ( $self->is_function($index) ) {
459 444         807 my $ptr = $self->get_heapptr($index);
460             return sub {
461 9     9   107 $self->push_heapptr($ptr);
462 9         37 $self->push_this();
463 9         21 my $len = 0 + @_;
464 9         42 for ( my $i = 0 ; $i < $len ; $i++ ) {
465 9         54 $self->push_perl( $_[$i] );
466             }
467 9 100       151 if ( $self->pcall_method($len) == 1 ) {
468 5         24 croak $self->last_error_string();
469             }
470 4         16 my $ret = $self->to_perl(-1);
471 4         17 $self->pop();
472 4         11 return $ret;
473 444         2233 };
474             }
475              
476 842         1596 my $isArray = $self->is_array($index);
477              
478 842         1588 my $heapptr = $self->require_heapptr($index);
479 842 50       1409 if ( $stash->{$heapptr} ) {
480 0         0 $ret = $stash->{$heapptr};
481             }
482             else {
483 842 100       1480 $ret = $isArray ? [] : {};
484 842         1701 $stash->{$heapptr} = $ret;
485             }
486              
487 842         4923 $self->enum( $index, JavaScript::Duktape::DUK_ENUM_OWN_PROPERTIES_ONLY );
488              
489 842         2864 while ( $self->next( -1, 1 ) ) {
490 5744         8050 my ( $key, $val );
491              
492 5744         8677 $key = $self->to_perl(-2);
493              
494 5744 100       11939 if ( $self->get_type(-1) == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
495 1309         2403 my $heapptr = $self->get_heapptr(-1);
496 1309 100       2678 if ( $stash->{$heapptr} ) {
497 63         90 $val = $stash->{$heapptr};
498             }
499             else {
500 1246         2045 $val = $self->to_perl( -1, $stash );
501             }
502             }
503             else {
504 4435         6397 $val = $self->to_perl(-1);
505             }
506              
507 5744         11029 $self->pop_n(2);
508              
509 5744 100       8191 if ($isArray) {
510 133         690 $ret->[$key] = $val;
511             }
512             else {
513 5611         23572 $ret->{$key} = $val;
514             }
515             }
516              
517 842         2034 $self->pop();
518             }
519              
520             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BOOLEAN ) {
521 16         48 my $bool = $self->get_boolean($index);
522 16 100       40 if ( $bool == 1 ) {
523 11         30 $ret = JavaScript::Duktape::Bool::true();
524             }
525             else {
526 5         15 $ret = JavaScript::Duktape::Bool::false();
527             }
528             }
529              
530             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NULL ) {
531 3         17 $ret = JavaScript::Duktape::NULL::null();
532             }
533              
534             elsif ( $type == JavaScript::Duktape::DUK_TYPE_POINTER ) {
535 3         10 my $p = $self->get_pointer($index);
536 3         10 $ret = bless \$p, 'JavaScript::Duktape::Pointer';
537             }
538              
539 11866         22050 return $ret;
540             }
541              
542             ##############################################
543             # push functions
544             ##############################################
545             sub push_function {
546 269     269   5313 my $self = shift;
547 269         406 my $sub = shift;
548 269   100     1021 my $nargs = shift || -1;
549              
550             $self->push_c_function(
551             sub {
552 146651     146651   163665 my @args;
553 146651         246210 my $top = $self->get_top();
554 146651         308252 for ( my $i = 0 ; $i < $top ; $i++ ) {
555 401         911 push @args, $self->to_perl($i);
556             }
557              
558 146651         292384 $self->push_this();
559 146651         263927 my $heap = $self->get_heapptr(-1);
560 146651         290233 $self->pop();
561              
562 146651 100       276899 if ( !$heap ) {
563 146543         271727 $self->push_global_object();
564 146543         237732 $heap = $self->get_heapptr(-1);
565 146543         229845 $self->pop();
566             }
567              
568 146651         217313 $THIS->{heapptr} = $heap;
569 146651         184301 $THIS->{duk} = $self;
570              
571 146651         273822 my $ret = $sub->(@args);
572 146628         670285 $self->push_perl($ret);
573 146628         211452 return 1;
574             },
575 269         1421 $nargs
576             );
577             }
578              
579             #####################################################################
580             # safe call
581             #####################################################################
582             sub push_c_function {
583 272     272   451 my $self = shift;
584 272         364 my $sub = shift;
585 272   100     650 my $nargs = shift || -1;
586              
587             $GlobalRef->{"$sub"} = sub {
588 146664     146664   274719 my @args = @_;
589 146664         281664 my $top = $self->get_top();
590 146664         177263 my $ret = 1;
591              
592             my $err = $self->safe_call(
593             sub {
594 146664         219298 $ret = $sub->(@args);
595 146641         214452 return 1;
596             },
597 146664         502452 $top,
598             1
599             );
600              
601 146664 100       345329 if ($err) {
602 23         84 croak $self->last_error_string();
603             }
604 146641         1473007 return $ret;
605 272         1490 };
606              
607 272         1642 $self->perl_push_function( $GlobalRef->{"$sub"}, $nargs );
608 272         11629 $self->eval_string("(function(){perlFinalizer('$sub')})");
609 272         1787 $self->set_finalizer(-2);
610             }
611              
612             #####################################################################
613             # safe call
614             #####################################################################
615             sub safe_call {
616 146710     146710   218840 my $self = shift;
617 146710         174761 my $sub = shift;
618 146710         165541 my $ret;
619             my $safe = sub {
620 146710     146710   188902 local $@;
621 146710         211285 eval { $ret = $sub->($self) };
  146710         230605  
622 146710 100       325703 if ( my $error = $@ ) {
623 39 100       235 if ( $error =~ /^Duk::Error/i ) {
624 22         94 croak $self->last_error_string();
625             }
626             else {
627 17         999 $self->eval_string('(function (e){ throw new Error(e) })');
628 17         106 $self->push_string($error);
629 17         321 $self->call(1);
630             }
631             }
632              
633 146671 50       367093 return defined $ret ? $ret : 1;
634 146710         318011 };
635              
636 146710         217265 eval { $ret = $self->perl_duk_safe_call( $safe, @_ ) };
  146710         324211  
637 146710 100       448564 return defined $ret ? $ret : 1;
638             }
639              
640             sub set_timeout {
641 4     4   1489 my $self = shift;
642 4         7 my $timeout = shift;
643              
644 4 100       213 croak "timeout must be a number" if !duk_sv_is_number($timeout);
645 3         23 $self->perl_duk_set_timeout($timeout);
646             }
647              
648             sub resize_memory {
649 2     2   4 my $self = shift;
650 2   50     9 my $max_memory = shift || 0;
651              
652 2 50       10 croak "max_memory should be a number" if !duk_sv_is_number( $max_memory );
653 2 100       142 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
654              
655 1         5 $self->perl_duk_resize_memory($max_memory);
656             }
657              
658             ##############################################
659             # custom functions
660             ##############################################
661             *get_utf8_string = \&perl_duk_get_utf8_string;
662             *push_perl_function = \&push_c_function;
663             *push_light_function = \&perl_push_function;
664              
665             ##############################################
666             # overridden functions
667             ##############################################
668             *require_context = \&perl_duk_require_context;
669              
670             ##############################################
671             # helper functions
672             ##############################################
673             *reset_top = \&perl_duk_reset_top;
674              
675             sub last_error_string {
676 59     59   130 my $self = shift;
677 59         254 $self->dup(-1);
678 59         718 my $error_str = $self->safe_to_string(-1);
679 59         254 $self->pop();
680 59         10508 return $error_str;
681             }
682              
683             sub dump {
684 4     4   35 my $self = shift;
685 4   100     22 my $name = shift || "Duktape";
686 4   50     26 my $fh = shift || \*STDOUT;
687 4         16 my $n = $self->get_top();
688 4         254 printf $fh "%s (top=%ld):", $name, $n;
689 4         26 for ( my $i = 0 ; $i < $n ; $i++ ) {
690 5         67 printf $fh " ";
691 5         30 $self->dup($i);
692 5         116 printf $fh "%s", $self->safe_to_string(-1);
693 5         36 $self->pop();
694             }
695 4         295 printf $fh "\n";
696             }
697              
698       0     sub DESTROY { }
699              
700             package JavaScript::Duktape::Bool;
701             {
702 53     53   2038094 use warnings;
  53         174  
  53         1916  
703 53     53   362 use strict;
  53         148  
  53         5310  
704             our ( $true, $false );
705             use overload
706 5     5   232 '""' => sub { ${ $_[0] } },
  5         39  
707 13 100   13   40 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  13         50  
708 53     53   395 fallback => 1;
  53         145  
  53         1065  
709              
710             BEGIN {
711 53     53   9020 my $t = 1;
712 53         139 my $f = 0;
713 53         166 $true = bless \$t, 'JavaScript::Duktape::Bool';
714 53         4170 $false = bless \$f, 'JavaScript::Duktape::Bool';
715             }
716              
717 11     11   18 sub true { $true }
718 5     5   11 sub false { $false }
719             }
720              
721             package JavaScript::Duktape::NULL;
722             {
723 53     53   398 use warnings;
  53         129  
  53         1792  
724 53     53   354 use strict;
  53         198  
  53         4583  
725             our ($null);
726             use overload
727 2     2   100 '""' => sub { ${ $_[0] } },
  2         15  
728 5 50   5   11 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  5         23  
729 53     53   384 fallback => 1;
  53         152  
  53         469  
730              
731             BEGIN {
732 53     53   5716 my $n = '';
733 53         2847 $null = bless \$n, 'JavaScript::Duktape::NULL';
734             }
735              
736 3     3   6 sub null { $null }
737             }
738              
739             package JavaScript::Duktape::Object;
740             {
741 53     53   415 use warnings;
  53         149  
  53         1502  
742 53     53   318 use strict;
  53         145  
  53         1253  
743 53     53   337 use Carp;
  53         134  
  53         4388  
744 53     53   388 use Data::Dumper;
  53         142  
  53         3160  
745             my $CONSTRUCTORS = {};
746 53     53   491 use Scalar::Util 'weaken';
  53         127  
  53         4307  
747             use overload '""' => sub {
748 3     3   2495 my $self = shift;
749 3         14 $self->inspect();
750             },
751 53     53   377 fallback => 1;
  53         143  
  53         443  
752              
753             sub inspect {
754 3     3   9 my $self = shift;
755 3         78 my $heapptr = $self->{heapptr};
756 3         10 my $duk = $self->{duk};
757 3         13 $duk->push_heapptr($heapptr);
758 3         11 my $ret = $duk->to_perl(-1);
759 3         11 $duk->pop();
760 3         20 return $ret;
761             }
762              
763             our $AUTOLOAD;
764              
765             sub AUTOLOAD {
766 522     522   11097 my $self = shift;
767 522         965 my $heapptr = $self->{heapptr};
768 522         723 my $duk = $self->{duk};
769 522         3314 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
770 522 50       1459 return if $method eq 'DESTROY';
771 522         1200 return JavaScript::Duktape::Util::autoload( $self, $method, $duk, $heapptr, @_ );
772             }
773              
774             DESTROY {
775 251     251   66200 my $self = shift;
776 251         500 my $duk = $self->{duk};
777              
778 251         474 my $refcount = delete $self->{refcount};
779 251 100       730 return if ( !$refcount );
780 234         972 $duk->push_global_stash();
781 234         704 $duk->get_prop_string( -1, "PerlGlobalStash" );
782 234         578 $duk->push_number($refcount);
783 234         2656 $duk->del_prop(-2);
784 234         1071 $duk->pop_2();
785             }
786             }
787              
788             package JavaScript::Duktape::Function;
789             {
790 53     53   19281 use strict;
  53         155  
  53         1313  
791 53     53   301 use warnings;
  53         131  
  53         1353  
792 53     53   301 use Data::Dumper;
  53         129  
  53         11606  
793              
794             sub new {
795 136     136   7014 my $self = shift;
796 136         320 $self->( $isNew, @_ );
797             }
798              
799             our $AUTOLOAD;
800              
801             sub AUTOLOAD {
802 0     0   0 my $self = shift;
803 0         0 my $heapptr = $self->($HEAP);
804 0         0 my $duk = $self->($DUK);
805              
806 0         0 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
807 0 0       0 return if $method eq 'DESTROY';
808 0         0 return JavaScript::Duktape::Util::autoload( $self, $method, $duk, $heapptr, @_ );
809             }
810              
811       0     sub DESTROY { }
812             };
813              
814             package JavaScript::Duktape::Util;
815             {
816 53     53   410 use strict;
  53         123  
  53         1316  
817 53     53   306 use warnings;
  53         138  
  53         1415  
818 53     53   317 use Data::Dumper;
  53         129  
  53         2436  
819 53     53   350 use Carp;
  53         117  
  53         45344  
820              
821             sub autoload {
822 522     522   720 my $self = shift;
823 522         726 my $method = shift;
824 522         658 my $duk = shift;
825 522         651 my $heapptr = shift;
826              
827 522         1434 $duk->push_heapptr($heapptr);
828 522 50       1001 if ( $method eq 'new' ) {
829 0         0 my $len = @_ + 0;
830 0         0 foreach my $val (@_) {
831 0         0 $duk->push_perl($val);
832             }
833 0 0       0 if ( $duk->pnew($len) != 0 ) {
834 0         0 croak $duk->last_error_string();
835             }
836 0         0 my $val = $duk->to_perl_object(-1);
837 0         0 $duk->pop();
838 0         0 return $val;
839             }
840              
841 522         722 my $val = undef;
842 522         1902 $duk->get_prop_string( -1, $method );
843              
844 522         1190 my $type = $duk->get_type(-1);
845 522 100 66     1713 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
846             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
847             {
848              
849 379 50       982 if ( $duk->is_function(-1) ) {
850 379         753 my $function_heap = $duk->get_heapptr(-1);
851              
852 379 100       702 if (@_) {
853             #called with special no arg _
854 377 100       809 shift if ( ref $_[0] eq 'NOARGS' );
855 377         774 $val = jsFunction( $method, $duk, $function_heap, $heapptr, 'call', @_ );
856             }
857             else {
858 2         8 $val = jsFunction( $method, $duk, $function_heap, $heapptr );
859             }
860             }
861             else {
862 0         0 $val = $duk->to_perl_object(-1);
863             }
864             }
865             else {
866 143         343 $val = $duk->to_perl(-1);
867             }
868 522         1366 $duk->pop_2();
869 522         1821 return $val;
870             }
871              
872             sub jsFunction {
873 384     384   603 my $methodname = shift;
874 384         487 my $duk = shift;
875 384         501 my $heapptr = shift;
876 384   33     747 my $constructor = shift || $heapptr;
877 384         556 my $doCall = shift;
878             my $sub = sub {
879              
880             # check first value, if it a ref of NEW
881             # then this is a constructor call, other wise
882             # it's just a normal call
883 515     515   746 my $isNew;
884 515         896 my $ref = ref $_[0];
885 515 100       1470 if ( $ref eq "NEW" ) {
    50          
    50          
886 136         202 shift;
887 136         188 $isNew = 1;
888             }
889             elsif ( $ref eq "HEAP" ) {
890 0         0 return $heapptr;
891             }
892             elsif ( $ref eq "DUK" ) {
893 0         0 return $duk;
894             }
895              
896 515         800 my $len = @_ + 0;
897 515         1230 $duk->push_heapptr($heapptr);
898 515 100       1374 $duk->push_heapptr($constructor) if !$isNew;
899 515         1009 foreach my $val (@_) {
900 543 100       1070 if ( ref $val eq 'CODE' ) {
901 211         449 $duk->push_function($val);
902             }
903             else {
904 332         644 $duk->push_perl($val);
905             }
906             }
907              
908 515 100       1065 if ($isNew) {
909 136 50       1306 if ( $duk->pnew($len) != 0 ) {
910 0         0 croak $duk->last_error_string();
911             }
912             }
913             else {
914 379 50       2622 if ( $duk->pcall_method($len) != 0 ) {
915 0         0 croak $duk->last_error_string();
916             }
917             }
918              
919 515         801 my $ret;
920             ##getting function call values
921 515         1160 my $type = $duk->get_type(-1);
922 515 100 66     1813 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
923             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
924             {
925 205         454 $ret = $duk->to_perl_object(-1);
926             }
927             else {
928 310         657 $ret = $duk->to_perl(-1);
929             }
930 515         1431 $duk->pop();
931 515         2983 return $ret;
932 384         1675 };
933              
934 384 100       1190 return $sub->(@_) if $doCall;
935 7         40 return bless $sub, "JavaScript::Duktape::Function";
936             }
937              
938             my $REFCOUNT = 0;
939              
940             sub jsObject {
941 239     239   324 my $options = shift;
942              
943 239         369 my $duk = $options->{duk};
944 239         320 my $heapptr = $options->{heapptr};
945 239   33     1109 my $constructor = $options->{constructor} || $heapptr;
946              
947             #We may push same heapptr on the global stack more
948             #than once, this results in segmentation fault when
949             #we destroy the object and delete heapptr from the
950             #global stash then trying to use it again
951             #TODO : this is really a poor man solution
952             #for this problem, we use a refcounter to create
953             #a unique id for each heapptr, a better solution
954             #would be making sure same heapptr pushed once and not to
955             #be free unless all gone
956 239         773 my $refcount = ( ++$REFCOUNT ) + ( rand(3) );
957              
958 239         766 $duk->push_global_stash();
959 239         700 $duk->get_prop_string( -1, "PerlGlobalStash" );
960 239         574 $duk->push_number($refcount);
961 239         529 $duk->push_heapptr($heapptr);
962 239         2098 $duk->put_prop(-3); #PerlGlobalStash[heapptr] = object
963 239         608 $duk->pop_2();
964              
965 239         479 my $type = $duk->get_type(-1);
966              
967 239 100       763 if ( $duk->is_function(-1) ) {
968 5         22 return JavaScript::Duktape::Util::jsFunction( 'anon', $duk, $heapptr, $constructor );
969             }
970              
971 234         1012 return bless {
972             refcount => $refcount,
973             duk => $duk,
974             heapptr => $heapptr
975             }, "JavaScript::Duktape::Object";
976             }
977             }
978              
979             1;
980              
981             __END__