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   369388 use strict;
  56         380  
  56         1271  
3 56     56   267 use warnings;
  56         83  
  56         1107  
4 56     56   223 use Carp;
  56         83  
  56         4485  
5 56     56   21235 use Data::Dumper;
  56         260074  
  56         2898  
6 56     56   316 use Scalar::Util qw( weaken );
  56         1468  
  56         7138  
7             our $VERSION = '2.4.2';
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   340 use base qw/Exporter/;
  56         95  
  56         14286  
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         64999 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   346 };
  56         116  
131              
132             sub new {
133 74     74 0 1273446 my $class = shift;
134 74         205 my %options = @_;
135              
136 74   100     398 my $max_memory = $options{max_memory} || 0;
137 74   100     303 my $timeout = $options{timeout} || 0;
138              
139 74 100       196 if ($timeout){
140 2 100       100 croak "timeout option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $timeout );
141             }
142              
143 73 100       186 if ( $max_memory ){
144 6 100       144 croak "max_memory option must be a number" if !JavaScript::Duktape::Vm::duk_sv_is_number( $max_memory );
145 5 100       185 croak "max_memory must be at least 256k (256 * 1024)" if $max_memory < 256 * 1024;
146             }
147              
148 71         175 my $self = bless {}, $class;
149              
150 71         42744 my $duk = $self->{duk} = JavaScript::Duktape::Vm->perl_duk_new( $max_memory, $timeout );
151              
152 71         343 $self->{pid} = $$;
153 71         152 $self->{max_memory} = $max_memory;
154              
155             # Initialize global stash 'PerlGlobalStash'
156             # this will be used to store some perl refs
157 71         544 $duk->push_global_stash();
158 71         267 $duk->push_object();
159 71         319 $duk->put_prop_string( -2, "PerlGlobalStash" );
160 71         249 $duk->pop();
161              
162 71         405 $THIS = bless { duk => $duk, heapptr => 0 }, "JavaScript::Duktape::Object";
163              
164             ##finalizer method
165             $self->{finalizer} = sub {
166 274     274   1713 my $ref = $duk->get_string(0);
167 274         3557 delete $GlobalRef->{$ref};
168 274         10638 return 1;
169 71         390 };
170              
171 71         321 weaken $GlobalRef;
172              
173 71         362 $duk->perl_push_function( $self->{finalizer}, 1 );
174 71         286 $duk->put_global_string('perlFinalizer');
175              
176 71         227 return $self;
177             }
178              
179 2     2 1 13 sub null { $JavaScript::Duktape::NULL::null; }
180 9     9 1 12124 sub true { $JavaScript::Duktape::Bool::true; }
181 7     7 1 27 sub false { $JavaScript::Duktape::Bool::false }
182 3     3   26 sub JavaScript::Duktape::_ { $NOARGS }
183 4     4 1 31 sub this { $THIS }
184              
185             sub set {
186 46     46 1 11608 my $self = shift;
187 46         67 my $name = shift;
188 46         56 my $val = shift;
189 46         85 my $duk = $self->vm;
190              
191 46 100       148 if ( $name =~ /\./ ) {
192              
193 2         6 my @props = split /\./, $name;
194 2         3 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         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         6 $duk->put_prop(-3);
209 2         5 $duk->pop();
210 2         5 return 1;
211             }
212              
213 44         119 $duk->push_perl($val);
214 44         139 $duk->put_global_string($name);
215 44         81 return 1;
216             }
217              
218             sub get {
219 1     1 1 5 my $self = shift;
220 1         2 my $name = shift;
221 1         1 my $duk = $self->vm;
222 1         5 $duk->push_string($name);
223 1 50       27 if ( $duk->peval() != 0 ) {
224 0         0 croak $duk->last_error_string();
225             }
226 1         3 my $ret = $duk->to_perl(-1);
227 1         2 $duk->pop();
228 1         2 return $ret;
229             }
230              
231             sub get_object {
232 6     6 1 146 my $self = shift;
233 6         7 my $name = shift;
234 6         9 my $duk = $self->vm;
235 6         16 $duk->push_string($name);
236 6 50       132 if ( $duk->peval() != 0 ) {
237 0         0 croak $duk->last_error_string();
238             }
239 6         19 my $ret = $duk->to_perl_object(-1);
240 6         18 $duk->pop();
241 6         14 return $ret;
242             }
243              
244             ##FIXME : should pop here?
245             sub eval {
246 31     31 1 1629 my $self = shift;
247 31         41 my $string = shift;
248 31         58 my $duk = $self->duk;
249              
250 31 100       3776276 if ( $duk->peval_string($string) != 0 ) {
251 9         36 croak $duk->last_error_string();
252             }
253              
254 22         79 return $duk->to_perl(-1);
255             }
256              
257 53     53 0 77 sub vm { shift->{duk}; }
258 83     83 0 333 sub duk { shift->{duk}; }
259              
260             sub set_timeout {
261 2     2 0 8 my $self = shift;
262 2         3 $self->duk->set_timeout( shift );
263             }
264              
265             sub resize_memory {
266 2     2 1 1960500 my $self = shift;
267 2         7 $self->duk->resize_memory( shift );
268             }
269              
270             sub destroy {
271 71     71 0 114 local $@;
272 71         109 my $self = shift;
273 71         152 my $duk = delete $self->{duk};
274 71 50       695 return if !$duk;
275 71         308 $duk->free_perl_duk();
276 71         22719 $duk->destroy_heap();
277             }
278              
279             sub DESTROY {
280 71     71   2999643 my $self = shift;
281 71 50 33     595 if ( $self->{pid} && $self->{pid} == $$ ) {
282 71         465 $self->destroy();
283             }
284             }
285              
286             package JavaScript::Duktape::Vm;
287 56     56   348 use strict;
  56         69  
  56         1312  
288 56     56   243 use warnings;
  56         81  
  56         1669  
289 56     56   259 no warnings 'redefine';
  56         97  
  56         1786  
290 56     56   270 use Data::Dumper;
  56         108  
  56         2375  
291 56     56   264 use Config qw( %Config );
  56         83  
  56         1862  
292 56     56   17363 use JavaScript::Duktape::C::libPath;
  56         93  
  56         1424  
293 56     56   263 use Carp;
  56         71  
  56         5888  
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   232 my $FunctionsMap = _get_path("FunctionsMap.pl");
307 56         23620 require $FunctionsMap;
308              
309 336     336   765 sub _get_path { &JavaScript::Duktape::C::libPath::getPath }
310              
311 56 50       355 $Duklib =
312             $^O eq 'MSWin32'
313             ? _get_path('duktape.dll')
314             : _get_path('duktape.so');
315             }
316              
317 56         207 use Inline C => config =>
318             typemaps => _get_path('typemap'),
319 56     56   28016 INC => '-I' . _get_path('../C') . ' -I' . _get_path('../C/lib');
  56         854564  
320             # myextlib => $Duklib,
321             # LIBS => '-L'. _get_path('../C/lib') . ' -lduktape';
322              
323 56     56   7221 use Inline C => _get_path('duk_perl.c');
  56         91  
  56         112  
324              
325 56         228 use Inline C => q{
326             void poke_buffer(IV to, IV from, IV sz) {
327             memcpy( to, from, sz );
328             }
329 56     56   20982032 };
  56         113  
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   8816 sub peek { unpack 'P' . $_[1], pack $ptr_format, $_[0] }
339 6     6   2238 sub pv_address { unpack( $ptr_format, pack( "p", $_[0] ) ) }
340              
341             sub push_perl {
342 198369     198369   167406 my $self = shift;
343 198369         159111 my $val = shift;
344 198369   100     386668 my $stash = shift || {};
345              
346 198369 100       252990 if ( my $ref = ref $val ) {
347 101 100       481 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       8 if ($val) {
353 3         16 $self->push_true();
354             }
355             else {
356 3         14 $self->push_false();
357             }
358             }
359              
360             elsif ( $ref eq 'ARRAY' ) {
361 12         30 my $arr_idx = $self->push_array();
362 12         36 $stash->{$val} = $self->get_heapptr(-1);
363 12         15 my $len = scalar @{$val};
  12         16  
364 12         32 for ( my $idx = 0 ; $idx < $len ; $idx++ ) {
365 32 100       51 if ( $stash->{ $val->[$idx] } ) {
366 2         6 $self->push_heapptr( $stash->{ $val->[$idx] } );
367             }
368             else {
369 30         56 $self->push_perl( $val->[$idx], $stash );
370             }
371 32         112 $self->put_prop_index( $arr_idx, $idx );
372             }
373             }
374              
375             elsif ( $ref eq 'HASH' ) {
376 16         35 $self->push_object();
377 16         51 $stash->{$val} = $self->get_heapptr(-1);
378 16         25 while ( my ( $k, $v ) = each %{$val} ) {
  45         139  
379 29         63 $self->push_string($k);
380 29 100 100     152 if ( $v && $stash->{$v} ) {
381 1         3 $self->push_heapptr( $stash->{$v} );
382             }
383             else {
384 28         105 $self->push_perl( $v, $stash );
385             }
386 29         75 $self->put_prop(-3);
387             }
388             }
389              
390             elsif ( $ref eq 'CODE' ) {
391 46         132 $self->push_function($val);
392             }
393              
394             elsif ( $ref eq 'JavaScript::Duktape::Object' ) {
395 12         27 $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       13 my $len = defined $$val ? length($$val) : 0;
408 6         7517 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 198268 100       310450 if ( !defined $val ) {
    100          
422 3         8 $self->push_undefined();
423             }
424             elsif ( duk_sv_is_number($val) ) {
425 197835         332485 $self->push_number($val);
426             }
427             else {
428 430         10043 $self->push_string($val);
429             }
430             }
431             }
432              
433             sub to_perl_object {
434 239     239   484 my $self = shift;
435 239         197 my $index = shift;
436 239         370 my $heapptr = $self->get_heapptr($index);
437 239 50       315 if ( !$heapptr ) { croak "value at stack $index is not an object" }
  0         0  
438 239         544 return JavaScript::Duktape::Util::jsObject(
439             {
440             duk => $self,
441             heapptr => $heapptr
442             }
443             );
444             }
445              
446             sub to_perl {
447 12341     12341   10476 my $self = shift;
448 12341         9068 my $index = shift;
449 12341   100     21198 my $stash = shift || {};
450              
451 12341         10094 my $ret;
452              
453 12341         14895 my $type = $self->get_type($index);
454              
455 12341 100       16555 if ( $type == JavaScript::Duktape::DUK_TYPE_UNDEFINED ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
456 173         175 $ret = undef;
457             }
458              
459             elsif ( $type == JavaScript::Duktape::DUK_TYPE_STRING ) {
460 8507         12814 $ret = $self->get_utf8_string($index);
461             }
462              
463             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NUMBER ) {
464 2323         2822 $ret = $self->get_number($index);
465             }
466              
467             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BUFFER ) {
468 5         18 my $ptr = $self->get_buffer_data( $index, my $sz );
469 5         11 $ret = peek( $ptr, $sz );
470             }
471              
472             elsif ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
473              
474 1288 100       2036 if ( $self->is_function($index) ) {
475 444         519 my $ptr = $self->get_heapptr($index);
476             return sub {
477 9     9   60 $self->push_heapptr($ptr);
478 9         13 $self->push_this();
479 9         13 my $len = 0 + @_;
480 9         20 for ( my $i = 0 ; $i < $len ; $i++ ) {
481 9         16 $self->push_perl( $_[$i] );
482             }
483 9 100       81 if ( $self->pcall_method($len) == 1 ) {
484 5         10 croak $self->last_error_string();
485             }
486 4         12 my $ret = $self->to_perl(-1);
487 4         10 $self->pop();
488 4         8 return $ret;
489 444         1864 };
490             }
491              
492 844         1073 my $isArray = $self->is_array($index);
493              
494 844         1044 my $heapptr = $self->require_heapptr($index);
495 844 50       1068 if ( $stash->{$heapptr} ) {
496 0         0 $ret = $stash->{$heapptr};
497             }
498             else {
499 844 100       1056 $ret = $isArray ? [] : {};
500 844         2151 $stash->{$heapptr} = $ret;
501             }
502              
503 844         4688 $self->enum( $index, JavaScript::Duktape::DUK_ENUM_OWN_PROPERTIES_ONLY );
504              
505 844         2302 while ( $self->next( -1, 1 ) ) {
506 5750         6056 my ( $key, $val );
507              
508 5750         6290 $key = $self->to_perl(-2);
509              
510 5750 100       8455 if ( $self->get_type(-1) == JavaScript::Duktape::DUK_TYPE_OBJECT ) {
511 1309         1535 my $heapptr = $self->get_heapptr(-1);
512 1309 100       1804 if ( $stash->{$heapptr} ) {
513 63         64 $val = $stash->{$heapptr};
514             }
515             else {
516 1246         1298 $val = $self->to_perl( -1, $stash );
517             }
518             }
519             else {
520 4441         4465 $val = $self->to_perl(-1);
521             }
522              
523 5750         7769 $self->pop_n(2);
524              
525 5750 100       5365 if ($isArray) {
526 133         428 $ret->[$key] = $val;
527             }
528             else {
529 5617         18849 $ret->{$key} = $val;
530             }
531             }
532              
533 844         1537 $self->pop();
534             }
535              
536             elsif ( $type == JavaScript::Duktape::DUK_TYPE_BOOLEAN ) {
537 39         68 my $bool = $self->get_boolean($index);
538 39 100       54 if ( $bool == 1 ) {
539 31         55 $ret = JavaScript::Duktape::Bool::true();
540             }
541             else {
542 8         21 $ret = JavaScript::Duktape::Bool::false();
543             }
544             }
545              
546             elsif ( $type == JavaScript::Duktape::DUK_TYPE_NULL ) {
547 3         7 $ret = JavaScript::Duktape::NULL::null();
548             }
549              
550             elsif ( $type == JavaScript::Duktape::DUK_TYPE_POINTER ) {
551 3         6 my $p = $self->get_pointer($index);
552 3         9 $ret = bless \$p, 'JavaScript::Duktape::Pointer';
553             }
554              
555 11897         16586 return $ret;
556             }
557              
558             ##############################################
559             # push functions
560             ##############################################
561             sub push_function {
562 271     271   3017 my $self = shift;
563 271         284 my $sub = shift;
564 271   100     636 my $nargs = shift || -1;
565              
566             $self->push_c_function(
567             sub {
568 197934     197934   145732 my @args;
569 197934         210210 my $top = $self->get_top();
570 197934         285732 for ( my $i = 0 ; $i < $top ; $i++ ) {
571 417         628 push @args, $self->to_perl($i);
572             }
573              
574 197934         255905 $self->push_this();
575 197934         227023 my $heap = $self->get_heapptr(-1);
576 197934         279706 $self->pop();
577              
578 197934 100       225165 if ( !$heap ) {
579 197826         258590 $self->push_global_object();
580 197826         203763 $heap = $self->get_heapptr(-1);
581 197826         200990 $self->pop();
582             }
583              
584 197934         194386 $THIS->{heapptr} = $heap;
585 197934         165949 $THIS->{duk} = $self;
586              
587 197934         253977 my $ret = $sub->(@args);
588 197912         633045 $self->push_perl($ret);
589 197912         209849 return 1;
590             },
591 271         1079 $nargs
592             );
593             }
594              
595             #####################################################################
596             # safe call
597             #####################################################################
598             sub push_c_function {
599 274     274   340 my $self = shift;
600 274         254 my $sub = shift;
601 274   100     447 my $nargs = shift || -1;
602              
603             $GlobalRef->{"$sub"} = sub {
604 197947     197947   262565 my @args = @_;
605 197947         248426 my $top = $self->get_top();
606 197947         156516 my $ret = 1;
607              
608             my $err = $self->safe_call(
609             sub {
610 197947         180612 $ret = $sub->(@args);
611 197925         191226 return 1;
612             },
613 197947         475915 $top,
614             1
615             );
616              
617 197947 100       326724 if ($err) {
618 22         50 croak $self->last_error_string();
619             }
620 197925         1152891 return $ret;
621 274         1103 };
622              
623 274         1273 $self->perl_push_function( $GlobalRef->{"$sub"}, $nargs );
624 274         9886 $self->eval_string("(function(){perlFinalizer('$sub')})");
625 274         1389 $self->set_finalizer(-2);
626             }
627              
628             #####################################################################
629             # safe call
630             #####################################################################
631             sub safe_call {
632 197994     197994   186697 my $self = shift;
633 197994         146717 my $sub = shift;
634 197994         147473 my $ret;
635             my $safe = sub {
636 197994     197994   154918 local $@;
637 197994         181278 eval { $ret = $sub->($self) };
  197994         192871  
638 197994 100       285140 if ( my $error = $@ ) {
639 38 100       199 if ( $error =~ /^Duk::Error/i ) {
640 22         76 croak $self->last_error_string();
641             }
642             else {
643 16         679 $self->eval_string('(function (e){ throw new Error(e) })');
644 16         65 $self->push_string($error);
645 16         209 $self->call(1);
646             }
647             }
648              
649 197956 50       322037 return defined $ret ? $ret : 1;
650 197994         301002 };
651              
652 197994         193852 eval { $ret = $self->perl_duk_safe_call( $safe, @_ ) };
  197994         281177  
653 197994 100       429591 return defined $ret ? $ret : 1;
654             }
655              
656             sub set_timeout {
657 4     4   1126 my $self = shift;
658 4         7 my $timeout = shift;
659              
660 4 100       106 croak "timeout must be a number" if !duk_sv_is_number($timeout);
661 3         30 $self->perl_duk_set_timeout($timeout);
662             }
663              
664             sub resize_memory {
665 2     2   2 my $self = shift;
666 2   50     8 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       138 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 58     58   88 my $self = shift;
693 58         162 $self->dup(-1);
694 58         445 my $error_str = $self->safe_to_string(-1);
695 58         143 $self->pop();
696 58         7433 return $error_str;
697             }
698              
699             sub dump {
700 3     3   20 my $self = shift;
701 3   100     13 my $name = shift || "Duktape";
702 3   50     14 my $fh = shift || \*STDOUT;
703 3         8 my $n = $self->get_top();
704 3         101 printf $fh "%s (top=%ld):", $name, $n;
705 3         33 for ( my $i = 0 ; $i < $n ; $i++ ) {
706 4         32 printf $fh " ";
707 4         18 $self->dup($i);
708 4         57 printf $fh "%s", $self->safe_to_string(-1);
709 4         19 $self->pop();
710             }
711 3         25 printf $fh "\n";
712             }
713              
714       0     sub DESTROY { }
715              
716             package JavaScript::Duktape::Bool;
717             {
718 56     56   1273924 use warnings;
  56         150  
  56         3095  
719 56     56   334 use strict;
  56         99  
  56         5046  
720             our ( $true, $false );
721             use overload
722 18     18   1796 '""' => sub { ${ $_[0] } },
  18         100  
723 43 100   43   1859 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  43         104  
724 56     56   54332 fallback => 1;
  56         43819  
  56         470  
725              
726             BEGIN {
727 56     56   7262 my $use_boolean = eval { require boolean; 1; };
  56         7495  
  0         0  
728 56         204 my $t = 1;
729 56         76 my $f = 0;
730 56 50       188 $true = $use_boolean ? boolean::true() : bless \$t, 'JavaScript::Duktape::Bool';
731 56 50       4842 $false = $use_boolean ? boolean::false() : bless \$f, 'JavaScript::Duktape::Bool';
732             }
733              
734 31     31   44 sub true { $true }
735 8     8   13 sub false { $false }
736              
737 2 100   2   147 sub TO_JSON { ${$_[0]} ? \1 : \0 }
  2         24  
738             }
739              
740             package JavaScript::Duktape::NULL;
741             {
742 56     56   324 use warnings;
  56         84  
  56         1398  
743 56     56   233 use strict;
  56         125  
  56         4348  
744             our ($null);
745             use overload
746 2     2   202 '""' => sub { ${ $_[0] } },
  2         11  
747 5 50   5   6 'bool' => sub { ${ $_[0] } ? 1 : 0 },
  5         12  
748 56     56   320 fallback => 1;
  56         110  
  56         439  
749              
750             BEGIN {
751 56     56   4562 my $n = '';
752 56         2670 $null = bless \$n, 'JavaScript::Duktape::NULL';
753             }
754              
755 3     3   4 sub null { $null }
756             }
757              
758             package JavaScript::Duktape::Object;
759             {
760 56     56   345 use warnings;
  56         106  
  56         1338  
761 56     56   266 use strict;
  56         86  
  56         1106  
762 56     56   245 use Carp;
  56         90  
  56         2904  
763 56     56   290 use Data::Dumper;
  56         105  
  56         2764  
764             my $CONSTRUCTORS = {};
765 56     56   323 use Scalar::Util 'weaken';
  56         152  
  56         3985  
766             use overload '""' => sub {
767 3     3   333 my $self = shift;
768 3         6 $self->inspect();
769             },
770 56     56   315 fallback => 1;
  56         92  
  56         327  
771              
772             sub inspect {
773 3     3   2 my $self = shift;
774 3         56 my $heapptr = $self->{heapptr};
775 3         4 my $duk = $self->{duk};
776 3         6 $duk->push_heapptr($heapptr);
777 3         4 my $ret = $duk->to_perl(-1);
778 3         6 $duk->pop();
779 3         8 return $ret;
780             }
781              
782             our $AUTOLOAD;
783              
784             sub AUTOLOAD {
785 522     522   8602 my $self = shift;
786 522         701 my $heapptr = $self->{heapptr};
787 522         539 my $duk = $self->{duk};
788 522         2651 my ($method) = ( $AUTOLOAD =~ /([^:']+$)/ );
789 522 50       1077 return if $method eq 'DESTROY';
790 522         817 return JavaScript::Duktape::Util::autoload( $self, $method, $duk, $heapptr, @_ );
791             }
792              
793             DESTROY {
794 251     251   61004 my $self = shift;
795 251         336 my $duk = $self->{duk};
796              
797 251         315 my $refcount = delete $self->{refcount};
798 251 100       445 return if ( !$refcount );
799 234         733 $duk->push_global_stash();
800 234         544 $duk->get_prop_string( -1, "PerlGlobalStash" );
801 234         395 $duk->push_number($refcount);
802 234         2013 $duk->del_prop(-2);
803 234         1257 $duk->pop_2();
804             }
805             }
806              
807             package JavaScript::Duktape::Function;
808             {
809 56     56   17014 use strict;
  56         98  
  56         1144  
810 56     56   220 use warnings;
  56         84  
  56         1233  
811 56     56   261 use Data::Dumper;
  56         81  
  56         10138  
812              
813             sub new {
814 136     136   5479 my $self = shift;
815 136         230 $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   323 use strict;
  56         103  
  56         1213  
836 56     56   246 use warnings;
  56         94  
  56         1623  
837 56     56   240 use Data::Dumper;
  56         104  
  56         2113  
838 56     56   295 use Carp;
  56         88  
  56         39788  
839              
840             sub autoload {
841 522     522   486 my $self = shift;
842 522         527 my $method = shift;
843 522         424 my $duk = shift;
844 522         445 my $heapptr = shift;
845              
846 522         960 $duk->push_heapptr($heapptr);
847 522 50       646 if ( $method eq 'new' ) {
848 0         0 my $len = @_ + 0;
849 0         0 foreach my $val (@_) {
850 0         0 $duk->push_perl($val);
851             }
852 0 0       0 if ( $duk->pnew($len) != 0 ) {
853 0         0 croak $duk->last_error_string();
854             }
855 0         0 my $val = $duk->to_perl_object(-1);
856 0         0 $duk->pop();
857 0         0 return $val;
858             }
859              
860 522         476 my $val = undef;
861 522         1617 $duk->get_prop_string( -1, $method );
862              
863 522         859 my $type = $duk->get_type(-1);
864 522 100 66     1186 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
865             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
866             {
867              
868 379 50       641 if ( $duk->is_function(-1) ) {
869 379         505 my $function_heap = $duk->get_heapptr(-1);
870              
871 379 100       487 if (@_) {
872             #called with special no arg _
873 377 100       532 shift if ( ref $_[0] eq 'NOARGS' );
874 377         590 $val = jsFunction( $method, $duk, $function_heap, $heapptr, 'call', @_ );
875             }
876             else {
877 2         12 $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         267 $val = $duk->to_perl(-1);
886             }
887 522         966 $duk->pop_2();
888 522         1260 return $val;
889             }
890              
891             sub jsFunction {
892 384     384   407 my $methodname = shift;
893 384         374 my $duk = shift;
894 384         312 my $heapptr = shift;
895 384   33     535 my $constructor = shift || $heapptr;
896 384         311 my $doCall = shift;
897             my $sub = sub {
898              
899             # check first value, if it a ref of NEW
900             # then this is a constructor call, other wise
901             # it's just a normal call
902 515     515   440 my $isNew;
903 515         663 my $ref = ref $_[0];
904 515 100       1026 if ( $ref eq "NEW" ) {
    50          
    50          
905 136         113 shift;
906 136         136 $isNew = 1;
907             }
908             elsif ( $ref eq "HEAP" ) {
909 0         0 return $heapptr;
910             }
911             elsif ( $ref eq "DUK" ) {
912 0         0 return $duk;
913             }
914              
915 515         596 my $len = @_ + 0;
916 515         874 $duk->push_heapptr($heapptr);
917 515 100       851 $duk->push_heapptr($constructor) if !$isNew;
918 515         622 foreach my $val (@_) {
919 543 100       754 if ( ref $val eq 'CODE' ) {
920 211         290 $duk->push_function($val);
921             }
922             else {
923 332         477 $duk->push_perl($val);
924             }
925             }
926              
927 515 100       681 if ($isNew) {
928 136 50       987 if ( $duk->pnew($len) != 0 ) {
929 0         0 croak $duk->last_error_string();
930             }
931             }
932             else {
933 379 50       1675 if ( $duk->pcall_method($len) != 0 ) {
934 0         0 croak $duk->last_error_string();
935             }
936             }
937              
938 515         563 my $ret;
939             ##getting function call values
940 515         811 my $type = $duk->get_type(-1);
941 515 100 66     1151 if ( $type == JavaScript::Duktape::DUK_TYPE_OBJECT
942             || $type == JavaScript::Duktape::DUK_TYPE_BUFFER )
943             {
944 205         308 $ret = $duk->to_perl_object(-1);
945             }
946             else {
947 310         417 $ret = $duk->to_perl(-1);
948             }
949 515         1016 $duk->pop();
950 515         2490 return $ret;
951 384         1378 };
952              
953 384 100       835 return $sub->(@_) if $doCall;
954 7         34 return bless $sub, "JavaScript::Duktape::Function";
955             }
956              
957             my $REFCOUNT = 0;
958              
959             sub jsObject {
960 239     239   233 my $options = shift;
961              
962 239         304 my $duk = $options->{duk};
963 239         222 my $heapptr = $options->{heapptr};
964 239   33     497 my $constructor = $options->{constructor} || $heapptr;
965              
966             #We may push same heapptr on the global stack more
967             #than once, this results in segmentation fault when
968             #we destroy the object and delete heapptr from the
969             #global stash then trying to use it again
970             #TODO : this is really a poor man solution
971             #for this problem, we use a refcounter to create
972             #a unique id for each heapptr, a better solution
973             #would be making sure same heapptr pushed once and not to
974             #be free unless all gone
975 239         605 my $refcount = ( ++$REFCOUNT ) + ( rand(3) );
976              
977 239         551 $duk->push_global_stash();
978 239         455 $duk->get_prop_string( -1, "PerlGlobalStash" );
979 239         364 $duk->push_number($refcount);
980 239         373 $duk->push_heapptr($heapptr);
981 239         1576 $duk->put_prop(-3); #PerlGlobalStash[heapptr] = object
982 239         404 $duk->pop_2();
983              
984 239         299 my $type = $duk->get_type(-1);
985              
986 239 100       464 if ( $duk->is_function(-1) ) {
987 5         19 return JavaScript::Duktape::Util::jsFunction( 'anon', $duk, $heapptr, $constructor );
988             }
989              
990 234         697 return bless {
991             refcount => $refcount,
992             duk => $duk,
993             heapptr => $heapptr
994             }, "JavaScript::Duktape::Object";
995             }
996             }
997              
998             1;
999              
1000             __END__