File Coverage

blib/lib/JE/Code.pm
Criterion Covered Total %
statement 576 639 90.1
branch 351 414 84.7
condition 170 211 80.5
subroutine 81 92 88.0
pod 4 7 57.1
total 1182 1363 86.7


line stmt bran cond sub pod time code
1             package JE::Code;
2              
3             our $VERSION = '0.064';
4              
5 101     101   36790 use strict;
  101         149  
  101         3036  
6 101     101   403 use warnings; no warnings 'utf8', 'recursion';
  101     101   136  
  101         2198  
  101         348  
  101         119  
  101         3035  
7              
8             #use Data::Dumper;
9 101     101   370 use Carp 1.01 'shortmess';
  101         2068  
  101         5202  
10 101     101   454 use Exporter 5.57 'import';
  101         1308  
  101         2882  
11 101     101   448 use Scalar::Util 'tainted';
  101         162  
  101         8943  
12              
13             our @CARP_NOT = 'JE';
14             our @EXPORT_OK = 'add_line_number';
15              
16 101     101   470 use constant T => ${^TAINT}; # perl doesn’t optimise if(${AINT}) away
  101         140  
  101         126019  
17              
18             require JE::Object::Error;
19             require JE::Object::Error::ReferenceError;
20             require JE::Object::Error::SyntaxError;
21             require JE::Object::Error::TypeError;
22             require JE::Object::Function;
23             require JE::Object::Array;
24             require JE::Boolean;
25             require JE::Object;
26             require JE::Parser;
27             require JE::Number;
28             require JE::LValue;
29             require JE::String;
30             require JE::Scope;
31              
32             sub add_line_number; # so I can call it without parentheses in sub execute
33              
34              
35             # This is documented in a POD comment at the bottom of the file.
36             sub parse {
37 351     351 1 666 my($global, $src, $file, $line) = @_;
38              
39 351         1349 ($src, my ($tree, $vars)) = JE::Parser::_parse(
40             program => $src, $global, $file, $line
41             );
42              
43 351 100       926 $@ and return;
44              
45             #print Dumper $tree;
46              
47 328 100       2316 my $r= bless { global => $global,
48             ( $JE::Parser::_parser
49             ? (parser => $JE::Parser::_parser)
50             : () ),
51             source => \$src,
52             file => $file,
53             line => $line,
54             vars => $vars,
55             tree => $tree };
56             # $self->{source} is a reference, so that we can share the same
57             # source between code objects without the extra memory overhead
58             # that copying it would have. (Some JS script files are
59             # rather large.)
60              
61 328 50 33     1301 $r->optimise
62             if $ENV{'YES_I_WANT_JE_TO_OPTIMISE'}
63             and $ENV{'YES_I_WANT_JE_TO_OPTIMISE'} ne 2;
64              
65 328         1404 $r;
66             }
67              
68              
69              
70              
71             sub execute_till { # ~~~ Should this be made public?
72 2     2 0 6 (my $code, local our $counting) = (shift,shift);
73 2         2 local our $ops = 0;
74 2         5 JE_Code_OP: {
75 2         3 return $code->execute(@_);
76             }
77             # If we get here, then we reached the max number of ops.
78 1         193 $@ = shortmess "max_ops ($counting) exceeded";
79 1         71 return undef;
80             }
81              
82             sub set_global {
83 0     0 1 0 my $code = shift;
84 0         0 my $old = $code->{global};
85 0         0 $code->{global} = $_[0];
86 0 0       0 {for(@{$code->{cache}||last}) {
  0         0  
  0         0  
87 0 0       0 ref eq 'JE::Code' and $_->set_global($_[0]);
88             }}
89 0 0       0 {for(@{$code->{vars}||last}){
  0         0  
  0         0  
90 0 0 0     0 ref && ref $$_[4] eq 'JE::Code'
91             && $$_[4]->set_global($_[0])
92             }}
93 0 0       0 defined $old or return;
94 0         0 my @stack = $code->{tree};
95 0         0 local *@;
96 0         0 while(@stack) {
97 0         0 for(shift @stack) {
98 0         0 for(@$_[1..$#$_]) {
99 0   0     0 my $r = ref || next;
100 0 0       0 $r =~ /^(?:ARRAY\z|JE::Code::)/
101             and push @stack, $_, =~ next;
102 0 0       0 $r eq 'JE::Boolean'
103             and $_ = qw(f t)[$_->value], next;
104 0 0       0 $r eq 'JE::Number'
105             and $_ = $_->value, next;
106 0 0       0 $r eq 'JE::String'
107             and $_ = "s".$_->value16, next;
108 0 0       0 $r eq 'JE::Null' and $_ = 'n', next;
109 0 0       0 $r eq 'JE::Object::RegExp'
110             and $_ = [$_->{source}->value, $$$_{regexp_flags}],
111             next;
112             }
113             }
114             }
115 0         0 return;
116             }
117              
118             sub optimise {
119 0     0 0 0 require 'JE/toperl.pl';
120 0         0 goto &{'optimise'};
  0         0  
121             }
122              
123             # Variables pertaining to the current execution context
124             our $code; # JE::Code object, not source code
125             our $this;
126             our $scope;
127             our $parser;
128             our $pos; # position within the source code; used to calculate a line no.
129             our $taint;
130             our $ops;
131             our $counting;
132             our $global;
133             our $return;
134             our $cache;
135              
136             sub execute {
137 2206     2206 1 4688 local $code = shift;
138 2206         4112 local $global = $$code{global};
139              
140             # We check $ops’ definedness to avoid resetting the op count when
141             # called recursively.
142 2206 100 100     8677 if(!defined our $ops and my $max_ops = $global->max_ops) {
143 2         4 unshift @_, $code, $max_ops;
144 2         6 goto &execute_till;
145             }
146              
147 2204 100       4690 local $this = defined $_[0] ? $_[0] : $global;
148 2204         2030 shift;
149              
150 2204   100     5414 local $scope = shift || bless [$global], 'JE::Scope';
151              
152 2204   100     4476 my $code_type = shift || 0;
153              
154 2204         2155 local our $taint = substr(${$$code{source}},0,0) if T;
  2204         9646  
155              
156 2204         2352 my $rv;
157 2204         2841 eval {
158             # passing these values around is too
159             # cumbersome
160 2204         2933 local $JE::Code::parser = $code->{parser}; # might be
161 2204         2198 local our $pos; # undef
162 2204         2463 local our $code = $code;
163 2204         2879 local $JE::Code::Expression::_eval = $code_type == 1;
164              
165             package JE::Code::Statement;
166 2204         2164 local our $_label;
167             package JE::Code;
168             # This $return variable has two uses. It holds the return
169             # value when the JS 'return' statement calls 'last RETURN'.
170             # It also is used by statements that return values. It is
171             # necessary to use this var, rather than simply returning
172             # the value (as in v0.016 and earlier), in order to make
173             # 'while(true) { 3; break }' return 3, rather than
174             # undefined.
175 2204         2357 local $return;
176 2204   100     6423 local $cache = $$code{cache}||=[];
177              
178 2204         4396 RETURN: {
179 2204         2604 BREAK: {
180 2204         2054 CONT: {
181 2204         2078 JE'Code'Statement'_create_vars();
182 0         0 $$code{sub} ? &{$$code{sub}} :
183             $$code{psrc}? (
184             # ~~~ temporary hack:
185             ($$code{psrc}) = $$code{psrc} =~/(.*)/s,
186 2188 50       9337 &{$$code{sub} =
    50          
187 16   0     57 eval{ eval("sub{$$code{psrc}}")||die }
188             || die "Internal error that should never"
189             . " happen (please report this): $@: "
190             . $$code{psrc}
191             }):
192             $$code{tree}->eval;
193 516 100 66     2084 $code_type == 2 # function
194             or defined $return && ($rv = $return);
195 500         7289 goto FINISH;
196             }
197              
198 0 0       0 if($JE::Code::Statement::_label) {
199 0         0 die new JE::Object::Error::SyntaxError $global,
200             add_line_number
201             "continue $JE::Code::Statement::_label: label " .
202             "'$JE::Code::Statement::_label' not found";
203 0         0 } else { goto FINISH; }
204              
205             } # end of BREAK
206              
207 0 0       0 if($JE::Code::Statement::_label) {
208 0         0 die new JE::Object::Error::SyntaxError $global,
209             add_line_number
210             "break $JE::Code::Statement::_label: label " .
211             "'$JE::Code::Statement::_label' not found";
212 0         0 } else { goto FINISH; }
213              
214             } # end of RETURN
215              
216 1681         1829 $rv = $return;
217              
218              
219             FINISH: # I have to put this here inside the eval,
220             # because 'eval { goto label }; label:' causes a
221             # a bus error in p5.8.8 if a tie handler is in
222             # the call stack (fixed in 5.9.5).
223 2181         5128 };
224              
225 2203 100 100     12365 T and defined $rv and tainted $taint and $rv->can('taint')
      100        
226             and $rv = taint $rv $taint;
227              
228 2203 100 100     9179 if(ref $@ eq '' and $@ eq '') {
229 2165 100       6078 !defined $rv and $rv = $scope->undefined;
230             }
231             else {
232             # Catch-all for any errors not dealt with elsewhere
233 38         197 $@ = _objectify_error($@);
234             }
235              
236 2187         27905 $rv;
237             }
238              
239             sub add_line_number {
240 313     313 1 522 my $msg = shift;
241 313 100       735 my $code = @_ ? shift : $code;
242 313 100       667 my $pos = @_ ? shift : $pos ;
243 313 100       1114 $msg =~ /\n\z/ and return $msg;
244 295 100 100     1850 defined(my $file = ($code || return $msg)->{file})
      100        
245             or defined $pos or return $msg;
246 285         519 my $first_line = $code->{line};
247 285 100       608 defined $first_line or $first_line = 1;
248 285 100       537 if(defined $pos) {
249 101     101   560 no warnings 'uninitialized';
  101         117  
  101         28582  
250 283         415312 "$msg at $file" . ', ' x defined($file) . 'line ' .
251 283         1286 ($first_line + (() = substr(${$code->{source}},0,$pos) =~
252             /\cm\cj?|[\cj\x{2028}\x{2029}]/g))
253             . ".\n";
254             } else {
255 2         9 "$msg in $file.\n"
256             }
257             }
258              
259             sub _objectify_error {
260 312     312   425 my $msg = shift;
261              
262 312 100       1218 ref $msg and return $global->upgrade($msg);
263              
264 36         53 my $class = 'JE::Object::Error';
265              
266 36 100       166 if($msg =~ /^Can't\ locate\ object\ method\
267             "(?:c(?:all|onstruct)|apply|invoke_with)"/x) {
268             # ~~~ the ‘apply’ in there is legacy and can be removed b4 v1
269 18         32 $class = 'JE::Object::Error::TypeError';
270 18         23 $msg = "Argument to new is not a constructor";
271             }
272              
273 36         96 new $class $global, add_line_number $msg;
274             }
275              
276             sub DDS_freeze {
277 0     0 0 0 my $self = shift;
278 0         0 my $copy = bless {%$self}, ref $self;
279 0         0 delete $copy->{sub};
280 0         0 $copy;
281             }
282              
283              
284              
285             package JE::Code::Statement; # This does not cover expression statements.
286              
287             our $VERSION = '0.064';
288              
289 101     101   25687 use subs qw'_eval_term';
  101         26427  
  101         387  
290 101     101   4043 use List::Util 'first';
  101         130  
  101         21105  
291              
292             our( $_label);
293              
294             *_eval_term = *JE::Code::Expression::_eval_term;
295             import JE::Code 'add_line_number';
296             sub add_line_number;
297              
298              
299             # Note: each statement object is an array ref. The elems are:
300             # [0] - an array ref containing
301             # [0] - the starting position in the source code and
302             # [1] - the ending position
303             # [1] - the type of statement
304             # [2..$#] - the various expressions/statements that make up the statement
305              
306             sub eval { # evaluate statement
307 13225     13225   14311 my $stm = shift;
308              
309 13225         15965 my $type = $$stm[1];
310 13225 100 100     45920 $type eq 'empty' || $type eq 'function' and return;
311              
312 12854         11529 my @labels;
313 12854         16154 $pos = $$stm[0][0];
314              
315 12854 100       20957 if ($type eq 'labelled') {
316 28         92 @labels = @$stm[2..$#$stm-1];
317 28 100       139 if ($$stm[-1][1] =~ /^(?:do|while|for|switch)\z/) {
318 14         25 $stm = $$stm[-1];
319 14         15 $type = $$stm[1];
320 101     101   526 no warnings 'deprecated';
  101         132  
  101         13114  
321 14         293 goto LOOPS; # skip unnecessary if statements
322             }
323              
324             BREAK: {
325 14         17 my $returned = $$stm[-1]->eval;
  14         37  
326 6 50       13 defined $returned and $return = $returned
327             }
328              
329             # Note that this has 'defined' in it, whereas the similar
330             # 'if' statement further down where the loop constructs are
331             # doesn't. This is because 'break' without a label sets
332             # $_label to '' and exits loops and switches.
333 13 100 100 7   77 if(! defined $_label || first {$_ eq $_label} @labels) {
  7         25  
334 12         11 undef $_label;
335 12         47 return;
336             } else {
337 101     101   530 no warnings 'exiting';
  101         130  
  101         25989  
338 1         4 last BREAK;
339             }
340             }
341              
342 12826 100       22217 if ($type eq 'statements') {
343              
344             # Execute the statements, one by one, and return the return
345             # value of the last statement that actually returned one.
346 9501         8301 my $returned;
347 9501         22262 for (@$stm[2..$#$stm]) {
348 21757 50       52399 next if $_ eq 'empty';
349 21757 100 66     39676 defined($returned = $_->eval) and
350             $return = $returned,
351             ref $return eq 'JE::LValue'
352             && get $return;
353             }
354 7470         26509 return;
355             }
356 3325 100       6537 if ($type eq 'var') {
357 607 100       1435 for (@$stm[2..$#$stm]) { if (@$_ == 2) {
  653         1510  
358 455         969 my $ret = _eval_term $$_[1];
359 454 100       1121 ref $ret eq'JE::LValue' and $ret = get $ret;
360 452         1545 $scope->find_var($$_[0])->set($ret);
361             }}
362 604         2283 return;
363             }
364 2718 100       4840 if ($type eq 'if') {
365             # 2 3 4
366             # we have: expr statement statement?
367 212         186 my $returned;
368 212 100       446 if ($$stm[2]->eval->to_boolean->value) {
369 62 50       251 $$stm[3] eq 'empty' or $returned = $$stm[3]->eval;
370             }
371             else {
372 148 100 66     482 exists $$stm[4]
373             && $$stm[4] ne 'empty'
374             and $returned = $$stm[4]->eval;
375             }
376 168 100       411 defined $returned and $return = $returned;
377             return
378 168         530 }
379 2520 100       9977 if ($type =~ /^(?:do|while|for|switch)\z/) {
380             # We have one of the following:
381             #
382             # 1 2 3 4 5
383             # 'do' statement expression
384             # 'while' expression statement
385             # 'for' expression 'in' expression statement
386             # 'for' var_decl 'in' expression statement
387             # 'for' expression expression expression statement
388             # 'for' var_decl expression expression statement
389             #
390             # In those last two cases, expression may be 'empty'.
391             # (See further down for 'switch').
392              
393 101     101   544 no warnings 'exiting';
  101         127  
  101         62834  
394              
395 422         567 LOOPS:
396             my $returned;
397            
398             BREAK: {
399 422 100 100     494 if ($type eq 'do') {
  422 100       3070  
    100          
    100          
400 26         30 do {
401 31 50       105 CONT: {
    100          
402 31         28 defined ($returned = ref $$stm[2]
403             ? $$stm[2]->eval : undef)
404             and $return = $returned;
405             }
406              
407 24 100 100 1   102 if($_label and
  1         5  
408             !first {$_ eq $_label} @labels) {
409 2         83 goto NEXT;
410             }
411 22         69 undef $_label;
412             } while $$stm[3]->eval->to_boolean->value;
413             }
414             elsif ($type eq 'while') {
415 23         63 CONT: while ($$stm[2]->eval->to_boolean->value) {
416 55 50       180 defined ($returned = ref $$stm[3]
    100          
417             ? $$stm[3]->eval : undef)
418             and $return = $returned;
419             }
420             continue {
421 49 100 100 5   238 if($_label and
  5         31  
422             !first {$_ eq $_label} @labels) {
423 2         73 goto NEXT;
424             }
425             }
426 14         32 undef $_label;
427             }
428             elsif ($type eq 'for' and $$stm[3] eq 'in') {
429 40         68 my $left_side = $$stm[2];
430 40 100       109 if ($left_side->[1] eq 'var') {
431 17         83 $left_side->eval;
432 17         42 $left_side = $left_side->[2][0];
433             # now contains the identifier
434             }
435 40         99 my $obj = $$stm[4]->eval;
436 40 100       160 $obj = $obj->get if ref $obj eq 'JE::LValue';
437 40 50       190 ref($obj) =~ /^JE::(?:Undefined|Null)\z/
438             # ~~~ Do we need undef $_label here?
439             and undef $_label, return;
440 40         169 my @keys = $obj->keys;
441 40         239 CONT: for(@keys) {
442 5229 50 33 0   11296 if($_label and
  0         0  
443             !first {$_ eq $_label} @labels) {
444 0         0 goto NEXT;
445             }
446 5229         4684 undef $_label;
447              
448 5229 100       14996 next if not defined $obj->prop($_);
449             # in which case it's been deleted
450            
451 5227 100       17631 (ref $left_side ? $left_side->eval :
452             $scope->find_var($left_side))
453             ->set(_new JE::String $global, $_);
454              
455 5227 50       20326 defined ($returned = ref $$stm[5]
    100          
456             ? $$stm[5]->eval : undef)
457             and $return = $returned;
458             }
459              
460             # In case 'continue LABEL' is called during the
461             # last iteration of the loop
462 40 50 33 0   147 if($_label and
  0         0  
463             !first {$_ eq $_label} @labels) {
464 0         0 next CONT;
465             }
466 40         296 undef $_label;
467              
468             }
469             elsif ($type eq 'for') { # for(;;)
470 317         359 my $tmp;
471 317 100 100 7   1216 CONT: for (
  7   33     20  
      100        
      33        
472             $tmp = ref $$stm[2] && $$stm[2]->eval,
473             ref $tmp eq 'JE::LValue' && get $tmp;
474              
475             ref $$stm[3]
476             ? $$stm[3]->eval->to_boolean->value
477             : 1;
478              
479 2857 100 100     6944 do{if($_label and
480             !first {$_ eq $_label} @labels) {
481 4         187 goto NEXT;
482             }
483 2853         10733 undef $_label;
484             },
485             $tmp = ref $$stm[4] && $$stm[4]->eval,
486             ref $tmp eq 'JE::LValue' && get $tmp
487             ) {
488 2911 50       8721 defined ($returned = ref $$stm[5]
    100          
489             ? $$stm[5]->eval : undef)
490             and $return = $returned;
491             }
492             }
493             else { # switch
494             # $stm->[2] is the parenthesized
495             # expression.
496             # Each pair of elements thereafter
497             # represents one case clause, an expr
498             # followed by statements, except for
499             # the default clause, which has the
500             # string 'default' for its first elem
501              
502            
503             # Evaluate the expression in the header
504 16         30 my $given = $$stm[2]->eval;
505 16 50       34 $given = get $given if ref $given eq 'JE::LValue';
506            
507             # Look through the case clauses to see
508             # which it matches. At the same time,
509             # look for the default clause.
510              
511 101     101   547 no strict 'refs';
  101         135  
  101         19757  
512              
513 16         22 my($n, $default) = 1;
514 16         31 while (($n+=2) < @$stm) {
515 34 100       69 if($$stm[$n] eq 'default') {
516 10         13 $default = $n; next;
  10         20  
517             }
518              
519             # Execute the statements if we have a match
520 24 100       38 if("JE::Code::Expression::in==="->(
521             $given, $$stm[$n]->eval
522             )) {
523 4         5 $n++;
524 4         4 do {
525 6         17 $$stm[$n]->eval;
526             } while ($n+=2) < @$stm;
527 4         3 undef $default;
528 4         7 last;
529             }
530             } ;
531              
532             # If we can't find a case that matches, but we
533             # did find a default (and $default was not erased
534             # when a case matched)
535 16 100       36 if(defined $default) {
536 10         13 $n = $default +1;
537 10         10 do { $$stm[$n]->eval }
  20         34  
538             while ($n+=2) < @$stm;
539             }
540             } # switch
541              
542             } # end of BREAK
543              
544              
545 402 100 100 8   1510 if(!$_label || first {$_ eq $_label} @labels) {
  8         18  
546 398         469 undef $_label;
547 398         1724 return;
548             } else {
549 4         11 last BREAK;
550             }
551            
552 8         35 NEXT: next CONT;
553             }
554 2098 100       3729 if ($type eq 'continue') {
555 101     101   852 no warnings 'exiting';
  101         147  
  101         5776  
556 20 100       52 $_label = exists $$stm[2] ? $$stm[2] : '';
557 20         66 next CONT;
558             }
559 2078 100       3883 if ($type eq 'break') {
560 101     101   429 no warnings 'exiting';
  101         108  
  101         6785  
561 65 100       129 $_label = exists $$stm[2] ? $$stm[2] : '';
562 65         200 last BREAK;
563             }
564 2013 100       3827 if ($type eq 'return') {
565 101     101   625 no warnings 'exiting';
  101         134  
  101         19080  
566 1666 100       2813 if (exists $$stm[2]) {
567 1664 100       3783 ref ($return = $$stm[2]->eval) eq 'JE::LValue'
568             and $return = get $return;
569 2         4 } else { $return = undef }
570 1665         6780 last RETURN;
571             }
572 347 100       798 if ($type eq 'with') {
573 14         43 local $scope = bless [
574             @$scope, $$stm[2]->eval->to_object
575             ], 'JE::Scope';
576 14         67 my $returned = $$stm[3]->eval;
577 14 100       39 defined $returned and $return = $returned;
578 14         67 return;
579             }
580 333 100       780 if ($type eq 'throw') {
581 17         20 my $excep;
582 17 50       51 if (exists $$stm[2]) {
583 17 100       43 ref ($excep = $$stm[2]->eval) eq 'JE::LValue'
584             and $excep = get $excep;
585             }
586 17 50       144 die defined $excep? $excep : $global->undefined;
587             }
588 316 50       858 if ($type eq 'try') {
589             # We have one of the following:
590             # 1 2 3 4 5
591             # 'try' block ident block (catch)
592             # 'try' block block (finally)
593             # 'try' block ident block block (catch & finally)
594              
595 316         359 my $result;
596             my $propagate;
597              
598 316         418 eval { # try
599 316         378 local $return;
600 101     101   477 no warnings 'exiting';
  101         154  
  101         21830  
601 316         942 RETURN: {
602 316         337 BREAK: {
603 316         302 CONT: {
604 316         316 $result = $$stm[2]->eval;
605 26         122 goto SAVERESULT;
606 0     0   0 } $propagate = sub{ next CONT }; goto SAVERESULT;
  0         0  
  0         0  
607 0     0   0 } $propagate = sub{ last BREAK }; goto SAVERESULT;
  0         0  
  0         0  
608 0     0   0 } $propagate = sub{ last RETURN }; goto SAVERESULT;
  0         0  
  0         0  
609              
610 26 50       63 SAVERESULT:
611             defined $result or $result = $return;
612 26         121 goto FINALLY;
613             };
614             # check ref first to avoid the overhead of overloading
615 290 50 66     9871 if (ref $@ || $@ ne '' and !ref $$stm[3]) { # catch
      33        
616 290         454 undef $result; # prevent { 3; throw ... } from
617             # returning 3
618              
619             # Turn miscellaneous errors into Error objects
620 290         746 $@ = JE'Code'_objectify_error($@);
621              
622 290         1153 (my $new_obj = new JE::Object $global)
623             ->prop({
624             name => $$stm[3],
625             value => $@,
626             dontdel => 1,
627             });
628 290         1088 local $scope = bless [
629             @$scope, $new_obj
630             ], 'JE::Scope';
631            
632 290         451 eval { # in case the catch block ends abruptly
633 290         356 local $return;
634 101     101   470 no warnings 'exiting';
  101         120  
  101         49224  
635 290         777 RETURN: {
636 290         345 BREAK: {
637 290         321 CONT: {
638 290         376 $result = $$stm[4]->eval;
639 290         1425 goto SAVE;
640 0     0   0 } $propagate = sub{ next CONT }; goto SAVE;
  0         0  
  0         0  
641 0     0   0 } $propagate = sub{ last BREAK }; goto SAVE;
  0         0  
  0         0  
642 0     0   0 } $propagate = sub{ last RETURN }; goto SAVE;
  0         0  
  0         0  
643              
644 290 50       667 SAVE:
645             defined $result or $result = $return;
646 290         1962 $@ = '';
647             }
648             }
649             # In case the 'finally' block resets $@:
650 290         434 my $exception = $@;
651             FINALLY:
652 316 100 100     1535 if ($#$stm == 3 or $#$stm == 5) {
653 6         13 $$stm[-1]->eval;
654             }
655 316 50 33     1727 defined $exception and ref $exception || $exception ne ''
      66        
656             and die $exception;
657 316 100       1256 $return = $result if defined $result;
658 316 50       2026 $propagate and &$propagate();
659             }
660             }
661              
662             sub _create_vars { # Process var and function declarations
663 2204     2204   3249 my $vars = $code->{vars};
664 2204         4679 for(@$vars) {
665 773 100       1363 if(ref) { # function
666             # format: [[...], function=> 'name',
667             # [ (params) ], $statements_obj, \@vars ]
668             # With optimisation on, the $statements_obj will
669             # actually be a code object.
670 168         873 $scope->[-1]->delete($$_[2], 1);
671 168         172 my $new_code_obj;
672 168 50       448 if(ref $$_[4] eq 'JE::Code') {
673 0         0 $new_code_obj = $$_[4]
674             }
675             else {
676 168         1238 ($new_code_obj = bless {
677             map+($_=>$code->{$_}),
678             qw/global source file line/
679             }, 'JE::Code')
680             ->{tree} = $$_[4];
681 168         395 $new_code_obj->{vars} = $$_[5];
682             }
683 168         1094 $scope->new_var($$_[2], new JE::Object::Function {
684             scope => $scope,
685             name => $$_[2],
686             argnames => $$_[3],
687             function => $new_code_obj
688             });
689             }
690             else {
691 605         1569 $scope->new_var($_);
692             }
693             }
694             }
695              
696              
697              
698              
699             package JE::Code::Expression;
700              
701             our $VERSION = '0.064';
702              
703             # B::Deparse showed me how to get these values.
704 101     101   576 use constant nan => sin 9**9**9;
  101         168  
  101         5695  
705 101     101   472 use constant inf => 9**9**9;
  101         121  
  101         4217  
706              
707 101     101   421 use subs qw'_eval_term';
  101         136  
  101         371  
708 101     101   26490 use POSIX 'fmod';
  101         491340  
  101         499  
709 101     101   80661 use Scalar::Util 'tainted';
  101         137  
  101         6771  
710              
711             import JE::Code 'add_line_number';
712             sub add_line_number;
713              
714 101     101   2629 BEGIN{*T = *JE::Code::T;}
715              
716              
717             #----------for reference------------#
718             #sub _to_int {
719             # call to_number first
720             # then...
721             # NaN becomes 0
722             # 0 and Infinity remain as they are
723             # other nums are rounded towards zero ($_ <=> 0) * floor(abs)
724             #}
725              
726             # Note that abs in ECMA-262
727             #sub _to_uint32 {
728             # call to_number, then ...
729              
730             # return 0 for Nan, -?inf and 0
731             # (round toward zero) % 2 ** 32
732             #}
733              
734             #sub _to_int32 {
735             # calculate _to_uint32 but subtract 2**32 if the result >= 2**31
736             #}
737              
738             #sub _to_uint16 {
739             # just like _to_uint32, except that 2**16 is used instead.
740             #}
741              
742              
743             #---------------------------------#
744              
745             { # JavaScript operators
746             # Note: some operators are not dealt with here, but inside
747             # sub eval.
748 101     101   486 no strict 'refs';
  101         122  
  101         39850  
749             *{'predelete'} = sub {
750 209 100   209   768 ref(my $term = shift) eq 'JE::LValue' or return
751             new JE::Boolean $global, 1;
752 203         618 my $base = $term->base;
753 203 100       914 new JE::Boolean $global,
754             defined $base ? $base->delete($term->property) : 1;
755             };
756             *{'prevoid'} = sub {
757 318     318   349 my $term = shift;
758 318         842 $term = get $term while ref $term eq 'JE::LValue';
759 317         858 return $global->undefined;
760             };
761             *{'pretypeof'} = sub {
762 166     166   275 my $term = shift;
763 166 100 100     909 ref $term eq 'JE::LValue' and
764             ref base $term eq '' and
765             return _new JE::String $global, 'undefined';
766 165         824 _new JE::String $global, typeof $term;
767             };
768             *{'pre++'} = sub {
769             # ~~~ These is supposed to use the same rules
770             # as the + infix op for the actual
771             # addition part. Verify that it does this.
772 2886     2886   4456 my $term = shift;
773 2886         6558 $term->set(new JE::Number $global,
774             get $term->to_number + 1);
775             };
776             *{'pre--'} = sub {
777             # ~~~ These is supposed to use the same rules
778             # as the - infix op for the actual
779             # subtraction part. Verify that it does this.
780 12     12   20 my $term = shift;
781 12         33 $term->set(new JE::Number $global,
782             get $term->to_number->value - 1);
783             };
784             *{'pre+'} = sub {
785 594     594   1593 shift->to_number;
786             };
787             *{'pre-'} = sub {
788 7307     7307   18757 new JE::Number $global, -shift->to_number->value;
789             };
790             *{'pre~'} = sub {
791 38     38   100 my $num = shift->to_number->value;
792 38 100 100     175 $num =
793             $num != $num || abs($num) == inf # nan/+-inf
794             ? 0
795             : int($num) % 2**32;
796              
797 38 100       76 $num -= 2**32 if $num >= 2**31;
798              
799 101     101   24817 { use integer; # for signed bitwise negation
  101         24498  
  101         369  
  38         26  
800 38         41 $num = ~$num; }
801            
802 38         74 new JE::Number $global, $num;
803             };
804             *{'pre!'} = sub {
805 709     709   2234 new JE::Boolean $global, !shift->to_boolean->value
806             };
807             *{'in*'} = sub {
808 70     70   221 new JE::Number $global,
809             shift->to_number->value *
810             shift->to_number->value;
811             };
812             *{'in/'} = sub {
813 50     50   201 my($num,$denom) = map to_number $_->value, @_[0,1];
814 50 100 66     210 new JE::Number $global,
    100          
815             $denom ?
816             $num/$denom :
817             # Divide by zero:
818             $num && $num == $num # not zero or nan
819             ? $num * inf
820             : nan;
821             };
822             *{'in%'} = sub {
823 46     46   169 my($num,$denom) = map to_number $_->value,
824             @_[0,1];
825 46 100 100     429 new JE::Number $global,
    100          
826             $num+1 == $num ? nan :
827             $num == $num && abs($denom) == inf ?
828             $num :
829             fmod $num, $denom;
830             };
831             *{'in+'} = sub {
832 19803     19803   23915 my($x, $y) = @_;
833 19803         45050 $x = $x->to_primitive;
834 19803         59968 $y = $y->to_primitive;
835 19801 100 100     41538 if($x->typeof eq 'string' or
836             $y->typeof eq 'string') {
837 19494         36643 return _new JE::String $global,
838             $x->to_string->value16 .
839             $y->to_string->value16;
840             }
841 307         723 return new JE::Number $global,
842             $x->to_number->value +
843             $y->to_number->value;
844             };
845             *{'in-'} = sub {
846 57     57   181 new JE::Number $global,
847             shift->to_number->value -
848             shift->to_number->value;
849             };
850             *{'in<<'} = sub {
851 897     897   2115 my $num = shift->to_number->value;
852 897 100 100     4265 $num =
853             $num != $num || abs($num) == inf # nan/+-inf
854             ? $num = 0
855             : int($num) % 2**32;
856 897 100       1666 $num -= 2**32 if $num >= 2**31;
857              
858 897         3452 my $shift_by = shift->to_number->value;
859 897 100 100     3795 $shift_by =
860             $shift_by != $shift_by || abs($shift_by) == inf
861             ? 0
862             : int($shift_by) % 32;
863              
864 897         1001 my $ret = ($num << $shift_by) % 2**32;
865 897 100       1550 $ret -= 2**32 if $ret >= 2**31;
866              
867 897         1977 new JE::Number $global, $ret;
868              
869             # Fails on 64-bit:
870             #use integer;
871             #new JE::Number $global,
872             # $num << $shift_by;
873             };
874             *{'in>>'} = sub {
875 897     897   2278 my $num = shift->to_number->value;
876 897 100 100     4143 $num =
877             $num != $num || abs($num) == inf # nan/+-inf
878             ? $num = 0
879             : int($num) % 2**32;
880 897 100       2027 $num -= 2**32 if $num >= 2**31;
881              
882 897         3627 my $shift_by = shift->to_number->value;
883 897 100 100     3590 $shift_by =
884             $shift_by != $shift_by || abs($shift_by) == inf
885             ? 0
886             : int($shift_by) % 32;
887              
888 101     101   58085 use integer;
  101         139  
  101         314  
889 897         2327 new JE::Number $global,
890             $num >> $shift_by;
891             };
892             *{'in>>>'} = sub {
893 897     897   2330 my $num = shift->to_number->value;
894 897 100 100     4090 $num =
895             $num != $num || abs($num) == inf # nan/+-inf
896             ? $num = 0
897             : int($num) % 2**32;
898              
899 897         3722 my $shift_by = shift->to_number->value;
900 897 100 100     3930 $shift_by =
901             $shift_by != $shift_by || abs($shift_by) == inf
902             ? 0
903             : int($shift_by) % 32;
904              
905 897         2354 new JE::Number $global,
906             $num >> $shift_by;
907             };
908             *{'in<'} = sub {
909 1541     1541   5634 my($x,$y) = map to_primitive $_, @_[0,1];
910 1541 100 100     4048 new JE::Boolean $global,
911             $x->typeof eq 'string' &&
912             $y->typeof eq 'string'
913             ? $x->to_string->value16 lt $y->to_string->value16
914             : $x->to_number->[0] < $y->to_number->[0];
915             };
916             *{'in>'} = sub {
917 80     80   246 my($x,$y) = map to_primitive $_, @_[0,1];
918 80 100 100     194 new JE::Boolean $global,
919             $x->typeof eq 'string' &&
920             $y->typeof eq 'string'
921             ? $x->to_string->value16 gt $y->to_string->value16
922             : $x->to_number->[0] > $y->to_number->[0];
923             };
924             *{'in<='} = sub {
925 1574     1574   5491 my($x,$y) = map to_primitive $_, @_[0,1];
926 1574 100 100     3657 new JE::Boolean $global,
927             $x->typeof eq 'string' &&
928             $y->typeof eq 'string'
929             ? $x->to_string->value16 le $y->to_string->value16
930             : $x->to_number->[0] <= $y->to_number->[0];
931             };
932             *{'in>='} = sub {
933 72     72   287 my($x,$y) = map to_primitive $_, @_[0,1];
934 72 100 100     207 new JE::Boolean $global,
935             $x->typeof eq 'string' &&
936             $y->typeof eq 'string'
937             ? $x->to_string->value16 ge $y->to_string->value16
938             : $x->to_number->[0] >= $y->to_number->[0];
939             };
940             *{'ininstanceof'} = sub {
941 216     216   324 my($obj,$func) = @_;
942 216 100       1053 die new JE::Object::Error::TypeError $global,
943             add_line_number "$func is not an object"
944             if $func->primitive;
945              
946 214 100       824 die new JE::Object::Error::TypeError $global,
947             add_line_number "$func is not a function"
948             if $func->typeof ne 'function';
949            
950 213 100       703 return new JE::Boolean $global, 0 if $obj->primitive;
951              
952 208         723 my $proto_id = $func->prop('prototype');
953 208 100 100     854 !defined $proto_id || $proto_id->primitive and die new
954             JE::Object::Error::TypeError $global,
955             add_line_number "Function $$$func{func_name} has no prototype property";
956 206         640 $proto_id = $proto_id->id;
957              
958 206   100     538 0 while (defined($obj = $obj->prototype)
959             or return new JE::Boolean $global, 0),
960             $obj->id ne $proto_id;
961            
962 204         846 new JE::Boolean $global, 1;
963             };
964             *{'inin'} = sub {
965 309     309   430 my($prop,$obj) = @_;
966 309 100       1143 die new JE::Object::Error::TypeError $global,
967             add_line_number "$obj is not an object"
968             if $obj->primitive;
969 308         991 new JE::Boolean $global, defined $obj->prop($prop);
970             };
971             *{'in=='} = sub {
972 2724     2724   3979 my($x,$y) = @_;
973 2724         7139 my($xt,$yt) = (typeof $x, typeof $y);
974 2724         5995 my($xi,$yi) = ( id $x, id $y);
975 2724 100 100     17879 $xt eq $yt and return new JE::Boolean $global,
976             $xi eq $yi && $xi ne 'num:nan';
977              
978 146 100       284 $xi eq 'null' and
979             return new JE::Boolean $global,
980             $yi eq 'undef';
981 138 100       471 $xi eq 'undef' and
982             return new JE::Boolean $global,
983             $yi eq 'null';
984 98 100       201 $yi eq 'null' and
985             return new JE::Boolean $global,
986             $xi eq 'undef';
987 92 100       194 $yi eq 'undef' and
988             return new JE::Boolean $global,
989             $xi eq 'null';
990              
991 82 100       212 if($xt eq 'boolean') {
    100          
992 12         40 $x = to_number $x;
993 12         18 $xt = 'number';
994             }
995             elsif($yt eq 'boolean') {
996 8         16 $y = to_number $y;
997 8         9 $yt = 'number';
998             }
999              
1000 82 100 100     670 if($xt eq 'string' || $xt eq 'number' and !primitive $y)
    100 100        
      66        
      66        
1001 12         28 { $y = to_primitive $y; $yt = typeof $y }
  12         25  
1002             elsif
1003             ($yt eq 'string' || $yt eq 'number' and !primitive $x)
1004 42         122 { $x = to_primitive $x; $xt = typeof $x }
  42         94  
1005              
1006 82 50 66     566 ($xt eq 'number' and $yt eq 'string' || $yt eq 'number')
      66        
      33        
      66        
      66        
1007             ||
1008             ($yt eq 'number' and $xt eq 'string' || $xt eq 'number')
1009             and
1010             return new JE::Boolean $global,
1011             to_number $x->[0] == to_number $y->[0];
1012              
1013 20 50 33     111 $xt eq 'string' && $yt eq 'string' and
1014             return new JE::Boolean $global,
1015             $x->value16 eq $y->value16;
1016            
1017 0         0 new JE::Boolean $global, 0;
1018             };
1019             *{'in!='} = sub {
1020 1460     1460   2055 new JE::Boolean $global, !&{'in=='}->[0];
  1460         3179  
1021             };
1022             *{'in==='} = sub {
1023 8959     8959   10694 my($x,$y) = @_;
1024 8959         20342 my($xi,$yi) = ( id $x, id $y);
1025 8959   100     48436 return new JE::Boolean $global,
1026             $xi eq $yi && $xi ne 'num:nan';
1027             };
1028             *{'in!=='} = sub {
1029 68     68   81 new JE::Boolean $global, !&{'in==='}->[0];
  68         146  
1030             };
1031              
1032             # ~~~ These three bitwise operators are slower than molasses. There
1033             # must be some way to speed them up, but I'm not sure the research
1034             # is worth it. Does anyone actually use these in JS?
1035             *{'in&'} = sub {
1036 899     899   2221 my $num = shift->to_number->[0];
1037 899 100 100     3889 $num =
1038             $num != $num || abs($num) == inf
1039             ? 0
1040             : int($num) % 2**32;
1041 899 100       1832 $num -= 2**32 if $num >= 2**31;
1042              
1043 899         3417 my $num2 = shift->to_number->[0];
1044 899 100 100     3861 $num2 =
1045             $num2 != $num2 || abs($num2) == inf
1046             ? 0
1047             : int($num2) % 2**32;
1048 899 100       1715 $num2 -= 2**32 if $num2 >= 2**31;
1049              
1050 101     101   120522 use integer;
  101         146  
  101         339  
1051 899         2315 new JE::Number $global,
1052             $num & $num2;
1053             };
1054             *{'in^'} = sub {
1055 899     899   2191 my $num = shift->to_number->[0];
1056 899 100 100     4141 $num =
1057             $num != $num || abs($num) == inf
1058             ? 0
1059             : int($num) % 2**32;
1060 899 100       1931 $num -= 2**32 if $num >= 2**31;
1061              
1062 899         3640 my $num2 = shift->to_number->[0];
1063 899 100 100     3892 $num2 =
1064             $num2 != $num2 || abs($num2) == inf
1065             ? 0
1066             : int($num2) % 2**32;
1067 899 100       1608 $num2 -= 2**32 if $num2 >= 2**31;
1068              
1069 101     101   13356 use integer;
  101         146  
  101         348  
1070 899         2309 new JE::Number $global,
1071             $num ^ $num2;
1072             };
1073             *{'in|'} = sub {
1074 900     900   2421 my $num = shift->to_number->[0];
1075 900 100 100     4105 $num =
1076             $num != $num || abs($num) == inf
1077             ? 0
1078             : int($num) % 2**32;
1079 900 100       1703 $num -= 2**32 if $num >= 2**31;
1080              
1081 900         3782 my $num2 = shift->to_number->[0];
1082 900 100 100     5786 $num2 =
1083             $num2 != $num2 || abs($num2) == inf
1084             ? 0
1085             : int($num2) % 2**32;
1086 900 100       1692 $num2 -= 2**32 if $num2 >= 2**31;
1087              
1088 101     101   12640 use integer;
  101         127  
  101         308  
1089 900         2307 new JE::Number $global,
1090             $num | $num2;
1091             };
1092             }
1093              
1094             =begin for-me
1095              
1096             Types of expressions:
1097              
1098             'new' term args?
1099              
1100             'member/call' term ( subscript | args) *
1101              
1102             'postfix' term op
1103              
1104             'hash' term*
1105              
1106             'array' term? (comma term?)*
1107              
1108             'prefix' op+ term
1109              
1110             'lassoc' term (op term)*
1111              
1112             'assign' term (op term)* (term term)?
1113             (the last two terms are the 2nd and 3rd terms of ? :
1114              
1115             'expr' term*
1116             (commas are omitted from the array)
1117              
1118             'function' ident? params statements
1119              
1120             =end for-me
1121              
1122             =cut
1123              
1124              
1125             # Note: each expression object is an array ref. The elems are:
1126             # [0] - an array ref containing
1127             # [0] - the starting position in the source code and
1128             # [1] - the ending position
1129             # [1] - the type of expression
1130             # [2..$#] - the various terms/tokens that make up the expr
1131              
1132             sub eval { # evalate (sub)expression
1133 101     101   7045 no warnings 'exiting';
  101         161  
  101         31940  
1134 194633 100 100 194633   301467 ++ $ops>$counting and last JE_Code_OP if $counting;
1135            
1136 194632         158821 my $expr = shift;
1137              
1138 194632         217922 my $type = $$expr[1];
1139 194632         156082 my @labels;
1140              
1141 194632         211993 $pos = $$expr[0][0];
1142              
1143 194632 100       302419 if ($type eq 'expr') {
1144 72832         59489 my $result;
1145 72832 100       108562 if(@$expr == 3) { # no comma
1146 72434         119138 return _eval_term $$expr[-1];
1147             }
1148             else { # comma op
1149 398         1314 for (@$expr[2..$#$expr-1]) {
1150 1362         2318 $result = _eval_term $_ ;
1151 1362 100       4540 get $result if ref $result eq 'JE::LValue';
1152             }
1153 398         1142 $result = _eval_term $$expr[-1] ;
1154 398 100       2441 return ref $result eq 'JE::LValue' ? get $result
1155             : $result;
1156             }
1157             }
1158 121800 100       190037 if ($type eq 'assign') {
1159 7465         21491 my @copy = \(@$expr[2..$#$expr]);
1160             # Evaluation is done left-first in JS, unlike in
1161             # Perl, so a = b = c is evaluated in this order:
1162             # - evaluate a
1163             # - evaluate b
1164             # - evaluate c
1165             # - assign c to b
1166             # - assign b to a
1167              
1168             # Check first to see whether we have the terms
1169             # of a ? : at the end:
1170             my @qc_terms = @copy >= 3 && (
1171             ref ${$copy[-2]} # avoid stringification
1172 7465 100 33     18809 || ${$copy[-2]} =~ /^(?:[tfu]|[si0-9])/
1173             )
1174             ? (pop @copy, pop @copy) : ();
1175             # @qc_terms is now in reverse order
1176              
1177             # Make a list of operands, evalling each
1178 7465         8035 my @terms = _eval_term ${shift @copy};
  7465         13049  
1179 7465         12422 my @ops;
1180 7465         14038 while(@copy) {
1181 6927         6496 push @ops, ${shift @copy};
  6927         10036  
1182 6927         6494 push @terms, _eval_term ${shift @copy};
  6927         10222  
1183             }
1184              
1185 7464         9609 my $val = pop @terms;
1186              
1187             # Now apply ? : if it's there
1188 561         1413 @qc_terms and $val = _eval_term
1189 7464 100       12687 ${$qc_terms[$val->to_boolean->[0]]};
1190              
1191 7464         12930 for (reverse @ops) {
1192 101     101   528 no strict 'refs';
  101         132  
  101         22330  
1193 164         616 length > 1 and $val =
1194 6926 100       16960 &{'in'.substr $_,0,-1}(
1195             $terms[-1], $val
1196             );
1197 6926 100       21990 $val = $val->get if ref $val eq 'JE::LValue';
1198 6926 50 33     22356 T and tainted $taint and $val->can('taint')
1199             and $val = taint $val $taint;
1200 6926         7946 eval { (pop @terms)->set($val) };
  6926         15320  
1201 6926 100       22998 if (my $err = $@) {
1202 1 50       7 die $err if UNIVERSAL::isa($err, 'JE::Object::Error');
1203 1         5 die new JE::Object::Error::ReferenceError
1204             $global, add_line_number "Cannot assign to a non-lvalue";
1205             }
1206             # ~~~ This needs to check whether it was an error
1207             # other than 'Can't locate object method "set"
1208             # since store handlers can thrown other errors.
1209            
1210             }
1211 7463 100       13226 if(!@ops) { # If we only have ? : and no assignment
1212 553 100       1330 $val = $val->get if ref $val eq 'JE::LValue';
1213             }
1214 7463         54089 return $val;
1215             }
1216 114335 100       176866 if($type eq 'lassoc') { # left-associative
1217 32141         82262 my @copy = \(@$expr[2..$#$expr]);
1218 32141         33743 my $result = _eval_term ${shift @copy};
  32141         50099  
1219 32141         64286 while(@copy) {
1220 101     101   501 no strict 'refs';
  101         111  
  101         15422  
1221             # We have to deal with || && here for the sake of
1222             # short-circuiting
1223 41298         36359 my $op = ${$copy[0]};
  41298         56305  
1224 41298 100       93508 if ($op eq '&&') {
    100          
1225 485 100       1193 $result = _eval_term(${$copy[1]}) if
  458         765  
1226             $result->to_boolean->[0];
1227 485 100       1400 $result = $result->get
1228             if ref $result eq 'JE::LValue';
1229             }
1230             elsif($op eq '||') {
1231 110 100       425 $result = _eval_term(${$copy[1]}) unless
  25         49  
1232             $result->to_boolean->[0];
1233 110 100       389 $result = $result->get
1234             if ref $result eq 'JE::LValue';
1235             }
1236             else {
1237 40703 100       96109 $result = $result->get
1238             if ref $result eq 'JE::LValue';
1239 40702         128924 $result = &{"in$op"}(
  40702         72513  
1240 40702         44281 $result, _eval_term ${$copy[1]}
1241             );
1242             }
1243 41289         160325 splice @copy, 0, 2; # double shift
1244             }
1245 32132         92594 return $result;
1246             }
1247 82194 100       127173 if ($type eq 'prefix') {
1248             # $$expr[1] -- 'prefix'
1249             # @$expr[2..-2] -- prefix ops
1250             # $$expr[-1] -- operand
1251 12091         17984 my $term = _eval_term $$expr[-1];
1252              
1253 101     101   475 no strict 'refs';
  101         108  
  101         115664  
1254 12091         33191 $term = &{"pre$_"}($term) for reverse @$expr[2..@$expr-2];
  12239         39057  
1255 12081         36125 return $term;
1256             }
1257 70103 100       106141 if ($type eq 'postfix') {
1258             # ~~~ These are supposed to use the same rules
1259             # as the + and - infix ops for the actual
1260             # addition part. Verify that they do this.
1261              
1262 145         366 my $ret = (my $term = _eval_term $$expr[2])
1263             ->to_number;
1264 145         490 $term->set(new JE::Number $global,
1265             $ret->value + (-1,1)[$$expr[3] eq '++']);
1266 145         555 return $ret;
1267             }
1268 69958 100       116338 if ($type eq 'new') {
1269 1118 50       2267 return _eval_term($$expr[2])->construct(
    50          
    100          
1270             @$expr == 4
1271             ? T && tainted $taint
1272             ? map $_->can('taint') ?taint $_ $taint:$_,
1273             $$expr[-1]->list
1274             : $$expr[-1]->list
1275             : ()
1276             );
1277             }
1278 68840 100       113355 if($type eq 'member/call') {
1279 62933         89463 my $obj = _eval_term $$expr[2];
1280 62933         133798 for (@$expr[3..$#$expr]) {
1281 69250 100       129100 if(ref eq 'JE::Code::Subscript') {
1282 46979 100       137974 $obj = get $obj
1283             if ref $obj eq 'JE::LValue';
1284 46979         101144 $obj = new JE::LValue $obj, $_->str_val;
1285             }
1286             else {
1287 22271 0       63686 $obj = $obj->call(
    50          
1288             T && tainted $taint
1289             ? map $_->can('taint')
1290             ? taint $_ $taint
1291             : $_,
1292             $_->list
1293             : $_->list
1294             );
1295             # If $obj is an lvalue,
1296             # JE::LValue::call will make
1297             # the lvalue's base object the 'this'
1298             # value. Otherwise,
1299             # JE::Object::Function::call
1300             # will make the
1301             # global object the 'this' value.
1302             }
1303             # ~~~ need some error-checking
1304             }
1305 62766         240086 return $obj; # which may be an lvalue
1306             }
1307 5907 100       10406 if($type eq 'array') {
1308 5260         4282 my @ary;
1309 5260         9277 for (2..$#$expr) {
1310 36225 100       56628 if(ref $$expr[$_] eq 'comma') {
1311 15614 100 100     59324 ref $$expr[$_-1] eq 'comma' || $_ == 2
1312             and ++$#ary
1313             }
1314             else {
1315 20611         28041 push @ary, _eval_term $$expr[$_];
1316 20611 100       51401 $ary[-1] = $ary[-1]->get
1317             if ref $ary[-1] eq 'JE::LValue';
1318             }
1319             }
1320              
1321 5259         14314 my $ary = new JE::Object::Array $global;
1322 5259         7154 $$$ary{array} = \@ary; # sticking it in like this
1323             # makes 'undef' elements non-
1324             # existent, rather
1325             # than undefined
1326 5259         14164 return $ary;
1327             }
1328 647 100       1539 if($type eq 'hash') {
1329 463         1481 my $obj = new JE::Object $global;
1330 463         1469 local @_ = \(@$expr[2..$#$expr]);
1331 463         530 my (@keys, $key, $value);
1332 463         1130 while(@_) { # I have to loop through them to keep
1333             # the order.
1334 94         85 $key = ${+shift};
  94         137  
1335 94         91 $value = _eval_term ${shift;};
  94         145  
1336 94 100       258 $value = get $value if ref $value eq 'JE::LValue';
1337 94         217 $obj->prop($key, $value);
1338             }
1339 463         1587 return $obj;
1340             }
1341 184 50       622 if ($type eq 'func') {
1342             # format: [[...], function=> 'name',
1343             # [ params ], $statements_obj, \@vars]
1344             # or: [[...], function =>
1345             # [ params ], $statements_obj, \@vars]
1346 184 100       719 my($name,$params,$statements) = ref $$expr[2] ?
1347             (undef, @$expr[2,3]) : @$expr[2..4];
1348 184 100       407 my $func_scope = $name
1349             ? bless([@$scope, my $obj=new JE::Object $global],
1350             'JE::Scope')
1351             : $scope;
1352 184         1876 (my $new_code_obj = bless {
1353             map+($_=>$code->{$_}),qw/global source file line/
1354             }, 'JE::Code')
1355             ->{tree} = $statements;
1356 184         518 $new_code_obj->{vars} = $$expr[-1];
1357 184 100       1206 my $f = new JE::Object::Function {
1358             scope => $func_scope,
1359             defined $name ? (name => $name) : (),
1360             argnames => $params,
1361             function => $new_code_obj,
1362             };
1363 184 100       574 if($name) {
1364 7         34 $obj->prop({
1365             name => $name,
1366             value => $f,
1367             readonly => 1,
1368             dontdel => 1,
1369             });
1370             }
1371 184         634 return $f;
1372             }
1373             }
1374             sub _eval_term {
1375 300791     300791   304035 my $term = $_[0];
1376              
1377 300791 100       623065 return $term->eval if ref $term eq 'JE::Code::Expression';
1378              
1379 175104 50       1107654 ref $term ? ref $term eq 'ARRAY'
    50          
    50          
    50          
    100          
    100          
    100          
    100          
1380             ? ( require JE::Object::RegExp,
1381             return JE::Object::RegExp->new(
1382             $global, @$term
1383             ) )
1384             : $term :
1385             $term eq'this'? $this :
1386             $term =~ /^s/ ? $_[0] = JE::String->_new($global,substr $term,1) :
1387             $term =~ /^i/ ? $scope->find_var(substr $term,1) :
1388             $term eq 't' ? $global->true :
1389             $term eq 'f' ? $global->false :
1390             $term eq 'n' ? $global->null :
1391             ($_[0] = JE::Number->new($global,$term));
1392             }
1393              
1394              
1395              
1396              
1397             package JE::Code::Subscript;
1398              
1399             our $VERSION = '0.064';
1400              
1401             sub str_val {
1402 46979     46979   59443 my $val = (my $self = shift)->[1];
1403 46979 100       112682 ref $val ? ''.$val->eval : $val;
1404             }
1405              
1406              
1407              
1408              
1409             package JE::Code::Arguments;
1410              
1411             our $VERSION = '0.064';
1412              
1413             sub list {
1414 23219     23219   22332 my $self = shift;
1415              
1416             # I can't use map here, because this method is called from within
1417             # a foreach loop, and an exception might be thrown from within
1418             # _eval_term, which has strange effects in perl 5.8.x (see perl
1419             # bug #24254).
1420              
1421 23219         20785 if(1) {
1422 23219         20797 my @result;
1423 23219         39651 for(@$self[1..$#$self]) {
1424 40871         57145 my $val = JE::Code::Expression::_eval_term($_);
1425 40868 100       114044 push @result, ref $val eq 'JE::LValue' ? $val->get : $val
1426             }
1427 23216         83807 @result;
1428              
1429             }else{ # original code
1430             map { my $val = JE::Code::Expression::_eval_term($_);
1431             ref $val eq 'JE::LValue' ? $val->get : $val }
1432             @$self[1..$#$self];
1433             }
1434             }
1435              
1436              
1437              
1438              
1439             1;
1440             __END__