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.065';
4              
5 101     101   38720 use strict;
  101         142  
  101         3067  
6 101     101   380 use warnings; no warnings 'utf8', 'recursion';
  101     101   144  
  101         2276  
  101         342  
  101         117  
  101         3216  
7              
8             #use Data::Dumper;
9 101     101   442 use Carp 1.01 'shortmess';
  101         2293  
  101         5301  
10 101     101   449 use Exporter 5.57 'import';
  101         1305  
  101         2896  
11 101     101   445 use Scalar::Util 'tainted';
  101         138  
  101         9192  
12              
13             our @CARP_NOT = 'JE';
14             our @EXPORT_OK = 'add_line_number';
15              
16 101     101   471 use constant T => ${^TAINT}; # perl doesn’t optimise if(${AINT}) away
  101         137  
  101         128373  
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 702 my($global, $src, $file, $line) = @_;
38              
39 351         1391 ($src, my ($tree, $vars)) = JE::Parser::_parse(
40             program => $src, $global, $file, $line
41             );
42              
43 351 100       908 $@ and return;
44              
45             #print Dumper $tree;
46              
47 328 100       2190 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     1261 $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         1409 $r;
66             }
67              
68              
69              
70              
71             sub execute_till { # ~~~ Should this be made public?
72 2     2 0 4 (my $code, local our $counting) = (shift,shift);
73 2         9 local our $ops = 0;
74 2         6 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         187 $@ = shortmess "max_ops ($counting) exceeded";
79 1         51 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 4678 local $code = shift;
138 2206         4014 local $global = $$code{global};
139              
140             # We check $ops’ definedness to avoid resetting the op count when
141             # called recursively.
142 2206 100 100     8795 if(!defined our $ops and my $max_ops = $global->max_ops) {
143 2         5 unshift @_, $code, $max_ops;
144 2         5 goto &execute_till;
145             }
146              
147 2204 100       4576 local $this = defined $_[0] ? $_[0] : $global;
148 2204         1999 shift;
149              
150 2204   100     5399 local $scope = shift || bless [$global], 'JE::Scope';
151              
152 2204   100     4461 my $code_type = shift || 0;
153              
154 2204         2026 local our $taint = substr(${$$code{source}},0,0) if T;
  2204         9557  
155              
156 2204         2571 my $rv;
157 2204         2867 eval {
158             # passing these values around is too
159             # cumbersome
160 2204         2938 local $JE::Code::parser = $code->{parser}; # might be
161 2204         2080 local our $pos; # undef
162 2204         2295 local our $code = $code;
163 2204         2881 local $JE::Code::Expression::_eval = $code_type == 1;
164              
165             package JE::Code::Statement;
166 2204         1993 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         2045 local $return;
176 2204   100     6266 local $cache = $$code{cache}||=[];
177              
178 2204         4278 RETURN: {
179 2204         2265 BREAK: {
180 2204         2086 CONT: {
181 2204         1970 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       9951 &{$$code{sub} =
    50          
187 16   0     60 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     1919 $code_type == 2 # function
194             or defined $return && ($rv = $return);
195 500         6978 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         2001 $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         4862 };
224              
225 2203 100 100     12438 T and defined $rv and tainted $taint and $rv->can('taint')
      100        
226             and $rv = taint $rv $taint;
227              
228 2203 100 100     8846 if(ref $@ eq '' and $@ eq '') {
229 2165 100       5650 !defined $rv and $rv = $scope->undefined;
230             }
231             else {
232             # Catch-all for any errors not dealt with elsewhere
233 38         189 $@ = _objectify_error($@);
234             }
235              
236 2187         26879 $rv;
237             }
238              
239             sub add_line_number {
240 313     313 1 487 my $msg = shift;
241 313 100       700 my $code = @_ ? shift : $code;
242 313 100       650 my $pos = @_ ? shift : $pos ;
243 313 100       1004 $msg =~ /\n\z/ and return $msg;
244 295 100 100     1808 defined(my $file = ($code || return $msg)->{file})
      100        
245             or defined $pos or return $msg;
246 285         454 my $first_line = $code->{line};
247 285 100       601 defined $first_line or $first_line = 1;
248 285 100       510 if(defined $pos) {
249 101     101   570 no warnings 'uninitialized';
  101         119  
  101         29797  
250 283         421696 "$msg at $file" . ', ' x defined($file) . 'line ' .
251 283         1162 ($first_line + (() = substr(${$code->{source}},0,$pos) =~
252             /\cm\cj?|[\cj\x{2028}\x{2029}]/g))
253             . ".\n";
254             } else {
255 2         11 "$msg in $file.\n"
256             }
257             }
258              
259             sub _objectify_error {
260 312     312   411 my $msg = shift;
261              
262 312 100       1241 ref $msg and return $global->upgrade($msg);
263              
264 36         56 my $class = 'JE::Object::Error';
265              
266 36 100       160 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         26 $class = 'JE::Object::Error::TypeError';
270 18         24 $msg = "Argument to new is not a constructor";
271             }
272              
273 36         94 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.065';
288              
289 101     101   26415 use subs qw'_eval_term';
  101         27243  
  101         401  
290 101     101   4294 use List::Util 'first';
  101         183  
  101         21727  
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   13964 my $stm = shift;
308              
309 13225         16188 my $type = $$stm[1];
310 13225 100 100     47259 $type eq 'empty' || $type eq 'function' and return;
311              
312 12854         12192 my @labels;
313 12854         16040 $pos = $$stm[0][0];
314              
315 12854 100       28055 if ($type eq 'labelled') {
316 28         95 @labels = @$stm[2..$#$stm-1];
317 28 100       192 if ($$stm[-1][1] =~ /^(?:do|while|for|switch)\z/) {
318 14         22 $stm = $$stm[-1];
319 14         19 $type = $$stm[1];
320 101     101   531 no warnings 'deprecated';
  101         137  
  101         13592  
321 14         331 goto LOOPS; # skip unnecessary if statements
322             }
323              
324             BREAK: {
325 14         18 my $returned = $$stm[-1]->eval;
  14         45  
326 6 50       12 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   87 if(! defined $_label || first {$_ eq $_label} @labels) {
  7         29  
334 12         14 undef $_label;
335 12         45 return;
336             } else {
337 101     101   486 no warnings 'exiting';
  101         137  
  101         26380  
338 1         4 last BREAK;
339             }
340             }
341              
342 12826 100       21787 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         8344 my $returned;
347 9501         22529 for (@$stm[2..$#$stm]) {
348 21757 50       53036 next if $_ eq 'empty';
349 21757 100 66     38554 defined($returned = $_->eval) and
350             $return = $returned,
351             ref $return eq 'JE::LValue'
352             && get $return;
353             }
354 7470         26131 return;
355             }
356 3325 100       5998 if ($type eq 'var') {
357 607 100       1364 for (@$stm[2..$#$stm]) { if (@$_ == 2) {
  653         1638  
358 455         1002 my $ret = _eval_term $$_[1];
359 454 100       1150 ref $ret eq'JE::LValue' and $ret = get $ret;
360 452         1387 $scope->find_var($$_[0])->set($ret);
361             }}
362 604         2312 return;
363             }
364 2718 100       4648 if ($type eq 'if') {
365             # 2 3 4
366             # we have: expr statement statement?
367 212         213 my $returned;
368 212 100       513 if ($$stm[2]->eval->to_boolean->value) {
369 62 50       288 $$stm[3] eq 'empty' or $returned = $$stm[3]->eval;
370             }
371             else {
372 148 100 66     479 exists $$stm[4]
373             && $$stm[4] ne 'empty'
374             and $returned = $$stm[4]->eval;
375             }
376 168 100       434 defined $returned and $return = $returned;
377             return
378 168         528 }
379 2520 100       10063 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   497 no warnings 'exiting';
  101         129  
  101         64093  
394              
395 422         560 LOOPS:
396             my $returned;
397            
398             BREAK: {
399 422 100 100     438 if ($type eq 'do') {
  422 100       3075  
    100          
    100          
400 26         30 do {
401 31 50       112 CONT: {
    100          
402 31         31 defined ($returned = ref $$stm[2]
403             ? $$stm[2]->eval : undef)
404             and $return = $returned;
405             }
406              
407 24 100 100 1   103 if($_label and
  1         6  
408             !first {$_ eq $_label} @labels) {
409 2         82 goto NEXT;
410             }
411 22         98 undef $_label;
412             } while $$stm[3]->eval->to_boolean->value;
413             }
414             elsif ($type eq 'while') {
415 23         66 CONT: while ($$stm[2]->eval->to_boolean->value) {
416 55 50       168 defined ($returned = ref $$stm[3]
    100          
417             ? $$stm[3]->eval : undef)
418             and $return = $returned;
419             }
420             continue {
421 49 100 100 5   257 if($_label and
  5         34  
422             !first {$_ eq $_label} @labels) {
423 2         83 goto NEXT;
424             }
425             }
426 14         37 undef $_label;
427             }
428             elsif ($type eq 'for' and $$stm[3] eq 'in') {
429 40         67 my $left_side = $$stm[2];
430 40 100       111 if ($left_side->[1] eq 'var') {
431 17         93 $left_side->eval;
432 17         37 $left_side = $left_side->[2][0];
433             # now contains the identifier
434             }
435 40         111 my $obj = $$stm[4]->eval;
436 40 100       169 $obj = $obj->get if ref $obj eq 'JE::LValue';
437 40 50       191 ref($obj) =~ /^JE::(?:Undefined|Null)\z/
438             # ~~~ Do we need undef $_label here?
439             and undef $_label, return;
440 40         163 my @keys = $obj->keys;
441 40         244 CONT: for(@keys) {
442 5229 50 33 0   11183 if($_label and
  0         0  
443             !first {$_ eq $_label} @labels) {
444 0         0 goto NEXT;
445             }
446 5229         4872 undef $_label;
447              
448 5229 100       14000 next if not defined $obj->prop($_);
449             # in which case it's been deleted
450            
451 5227 100       17943 (ref $left_side ? $left_side->eval :
452             $scope->find_var($left_side))
453             ->set(_new JE::String $global, $_);
454              
455 5227 50       19889 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   148 if($_label and
  0         0  
463             !first {$_ eq $_label} @labels) {
464 0         0 next CONT;
465             }
466 40         282 undef $_label;
467              
468             }
469             elsif ($type eq 'for') { # for(;;)
470 317         412 my $tmp;
471 317 100 100 7   1204 CONT: for (
  7   33     25  
      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     6467 do{if($_label and
480             !first {$_ eq $_label} @labels) {
481 4         186 goto NEXT;
482             }
483 2853         10320 undef $_label;
484             },
485             $tmp = ref $$stm[4] && $$stm[4]->eval,
486             ref $tmp eq 'JE::LValue' && get $tmp
487             ) {
488 2911 50       8928 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         33 my $given = $$stm[2]->eval;
505 16 50       37 $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   555 no strict 'refs';
  101         131  
  101         19975  
512              
513 16         21 my($n, $default) = 1;
514 16         36 while (($n+=2) < @$stm) {
515 34 100       74 if($$stm[$n] eq 'default') {
516 10         12 $default = $n; next;
  10         21  
517             }
518              
519             # Execute the statements if we have a match
520 24 100       39 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         5 undef $default;
528 4         5 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         11 $n = $default +1;
537 10         11 do { $$stm[$n]->eval }
  20         41  
538             while ($n+=2) < @$stm;
539             }
540             } # switch
541              
542             } # end of BREAK
543              
544              
545 402 100 100 8   2463 if(!$_label || first {$_ eq $_label} @labels) {
  8         19  
546 398         446 undef $_label;
547 398         1716 return;
548             } else {
549 4         13 last BREAK;
550             }
551            
552 8         43 NEXT: next CONT;
553             }
554 2098 100       3770 if ($type eq 'continue') {
555 101     101   842 no warnings 'exiting';
  101         164  
  101         5667  
556 20 100       54 $_label = exists $$stm[2] ? $$stm[2] : '';
557 20         75 next CONT;
558             }
559 2078 100       3450 if ($type eq 'break') {
560 101     101   407 no warnings 'exiting';
  101         113  
  101         6691  
561 65 100       177 $_label = exists $$stm[2] ? $$stm[2] : '';
562 65         200 last BREAK;
563             }
564 2013 100       3580 if ($type eq 'return') {
565 101     101   636 no warnings 'exiting';
  101         126  
  101         19063  
566 1666 100       3119 if (exists $$stm[2]) {
567 1664 100       3610 ref ($return = $$stm[2]->eval) eq 'JE::LValue'
568             and $return = get $return;
569 2         4 } else { $return = undef }
570 1665         6614 last RETURN;
571             }
572 347 100       745 if ($type eq 'with') {
573 14         39 local $scope = bless [
574             @$scope, $$stm[2]->eval->to_object
575             ], 'JE::Scope';
576 14         64 my $returned = $$stm[3]->eval;
577 14 100       40 defined $returned and $return = $returned;
578 14         74 return;
579             }
580 333 100       788 if ($type eq 'throw') {
581 17         24 my $excep;
582 17 50       47 if (exists $$stm[2]) {
583 17 100       44 ref ($excep = $$stm[2]->eval) eq 'JE::LValue'
584             and $excep = get $excep;
585             }
586 17 50       142 die defined $excep? $excep : $global->undefined;
587             }
588 316 50       840 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         358 my $result;
596             my $propagate;
597              
598 316         417 eval { # try
599 316         377 local $return;
600 101     101   495 no warnings 'exiting';
  101         145  
  101         22083  
601 316         906 RETURN: {
602 316         314 BREAK: {
603 316         322 CONT: {
604 316         308 $result = $$stm[2]->eval;
605 26         118 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       70 SAVERESULT:
611             defined $result or $result = $return;
612 26         111 goto FINALLY;
613             };
614             # check ref first to avoid the overhead of overloading
615 290 50 66     9554 if (ref $@ || $@ ne '' and !ref $$stm[3]) { # catch
      33        
616 290         394 undef $result; # prevent { 3; throw ... } from
617             # returning 3
618              
619             # Turn miscellaneous errors into Error objects
620 290         681 $@ = JE'Code'_objectify_error($@);
621              
622 290         1118 (my $new_obj = new JE::Object $global)
623             ->prop({
624             name => $$stm[3],
625             value => $@,
626             dontdel => 1,
627             });
628 290         986 local $scope = bless [
629             @$scope, $new_obj
630             ], 'JE::Scope';
631            
632 290         435 eval { # in case the catch block ends abruptly
633 290         312 local $return;
634 101     101   463 no warnings 'exiting';
  101         125  
  101         49272  
635 290         765 RETURN: {
636 290         322 BREAK: {
637 290         278 CONT: {
638 290         290 $result = $$stm[4]->eval;
639 290         1361 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       598 SAVE:
645             defined $result or $result = $return;
646 290         1723 $@ = '';
647             }
648             }
649             # In case the 'finally' block resets $@:
650 290         927 my $exception = $@;
651             FINALLY:
652 316 100 100     1564 if ($#$stm == 3 or $#$stm == 5) {
653 6         14 $$stm[-1]->eval;
654             }
655 316 50 33     1743 defined $exception and ref $exception || $exception ne ''
      66        
656             and die $exception;
657 316 100       722 $return = $result if defined $result;
658 316 50       1949 $propagate and &$propagate();
659             }
660             }
661              
662             sub _create_vars { # Process var and function declarations
663 2204     2204   3022 my $vars = $code->{vars};
664 2204         4944 for(@$vars) {
665 773 100       1296 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         806 $scope->[-1]->delete($$_[2], 1);
671 168         163 my $new_code_obj;
672 168 50       429 if(ref $$_[4] eq 'JE::Code') {
673 0         0 $new_code_obj = $$_[4]
674             }
675             else {
676 168         1242 ($new_code_obj = bless {
677             map+($_=>$code->{$_}),
678             qw/global source file line/
679             }, 'JE::Code')
680             ->{tree} = $$_[4];
681 168         366 $new_code_obj->{vars} = $$_[5];
682             }
683 168         1034 $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         1535 $scope->new_var($_);
692             }
693             }
694             }
695              
696              
697              
698              
699             package JE::Code::Expression;
700              
701             our $VERSION = '0.065';
702              
703             # B::Deparse showed me how to get these values.
704 101     101   663 use constant nan => sin 9**9**9;
  101         171  
  101         6127  
705 101     101   444 use constant inf => 9**9**9;
  101         123  
  101         4229  
706              
707 101     101   421 use subs qw'_eval_term';
  101         204  
  101         343  
708 101     101   27644 use POSIX 'fmod';
  101         503803  
  101         503  
709 101     101   82446 use Scalar::Util 'tainted';
  101         149  
  101         6921  
710              
711             import JE::Code 'add_line_number';
712             sub add_line_number;
713              
714 101     101   2591 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   458 no strict 'refs';
  101         122  
  101         40846  
749             *{'predelete'} = sub {
750 209 100   209   702 ref(my $term = shift) eq 'JE::LValue' or return
751             new JE::Boolean $global, 1;
752 203         545 my $base = $term->base;
753 203 100       922 new JE::Boolean $global,
754             defined $base ? $base->delete($term->property) : 1;
755             };
756             *{'prevoid'} = sub {
757 318     318   381 my $term = shift;
758 318         822 $term = get $term while ref $term eq 'JE::LValue';
759 317         868 return $global->undefined;
760             };
761             *{'pretypeof'} = sub {
762 166     166   284 my $term = shift;
763 166 100 100     890 ref $term eq 'JE::LValue' and
764             ref base $term eq '' and
765             return _new JE::String $global, 'undefined';
766 165         804 _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   4467 my $term = shift;
773 2886         7220 $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   26 my $term = shift;
781 12         39 $term->set(new JE::Number $global,
782             get $term->to_number->value - 1);
783             };
784             *{'pre+'} = sub {
785 594     594   1565 shift->to_number;
786             };
787             *{'pre-'} = sub {
788 7307     7307   17865 new JE::Number $global, -shift->to_number->value;
789             };
790             *{'pre~'} = sub {
791 38     38   108 my $num = shift->to_number->value;
792 38 100 100     249 $num =
793             $num != $num || abs($num) == inf # nan/+-inf
794             ? 0
795             : int($num) % 2**32;
796              
797 38 100       80 $num -= 2**32 if $num >= 2**31;
798              
799 101     101   26097 { use integer; # for signed bitwise negation
  101         25361  
  101         379  
  38         32  
800 38         50 $num = ~$num; }
801            
802 38         84 new JE::Number $global, $num;
803             };
804             *{'pre!'} = sub {
805 709     709   2031 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   205 my($num,$denom) = map to_number $_->value, @_[0,1];
814 50 100 66     240 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   178 my($num,$denom) = map to_number $_->value,
824             @_[0,1];
825 46 100 100     433 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   23298 my($x, $y) = @_;
833 19803         45263 $x = $x->to_primitive;
834 19803         59154 $y = $y->to_primitive;
835 19801 100 100     38961 if($x->typeof eq 'string' or
836             $y->typeof eq 'string') {
837 19494         37475 return _new JE::String $global,
838             $x->to_string->value16 .
839             $y->to_string->value16;
840             }
841 307         794 return new JE::Number $global,
842             $x->to_number->value +
843             $y->to_number->value;
844             };
845             *{'in-'} = sub {
846 57     57   192 new JE::Number $global,
847             shift->to_number->value -
848             shift->to_number->value;
849             };
850             *{'in<<'} = sub {
851 897     897   2281 my $num = shift->to_number->value;
852 897 100 100     4373 $num =
853             $num != $num || abs($num) == inf # nan/+-inf
854             ? $num = 0
855             : int($num) % 2**32;
856 897 100       1787 $num -= 2**32 if $num >= 2**31;
857              
858 897         3836 my $shift_by = shift->to_number->value;
859 897 100 100     4023 $shift_by =
860             $shift_by != $shift_by || abs($shift_by) == inf
861             ? 0
862             : int($shift_by) % 32;
863              
864 897         1140 my $ret = ($num << $shift_by) % 2**32;
865 897 100       1492 $ret -= 2**32 if $ret >= 2**31;
866              
867 897         2084 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   2179 my $num = shift->to_number->value;
876 897 100 100     4055 $num =
877             $num != $num || abs($num) == inf # nan/+-inf
878             ? $num = 0
879             : int($num) % 2**32;
880 897 100       1891 $num -= 2**32 if $num >= 2**31;
881              
882 897         3482 my $shift_by = shift->to_number->value;
883 897 100 100     3658 $shift_by =
884             $shift_by != $shift_by || abs($shift_by) == inf
885             ? 0
886             : int($shift_by) % 32;
887              
888 101     101   58273 use integer;
  101         154  
  101         312  
889 897         2051 new JE::Number $global,
890             $num >> $shift_by;
891             };
892             *{'in>>>'} = sub {
893 897     897   2156 my $num = shift->to_number->value;
894 897 100 100     4226 $num =
895             $num != $num || abs($num) == inf # nan/+-inf
896             ? $num = 0
897             : int($num) % 2**32;
898              
899 897         3529 my $shift_by = shift->to_number->value;
900 897 100 100     5523 $shift_by =
901             $shift_by != $shift_by || abs($shift_by) == inf
902             ? 0
903             : int($shift_by) % 32;
904              
905 897         2139 new JE::Number $global,
906             $num >> $shift_by;
907             };
908             *{'in<'} = sub {
909 1541     1541   5460 my($x,$y) = map to_primitive $_, @_[0,1];
910 1541 100 100     3906 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   264 my($x,$y) = map to_primitive $_, @_[0,1];
918 80 100 100     211 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   5608 my($x,$y) = map to_primitive $_, @_[0,1];
926 1574 100 100     3484 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   235 my($x,$y) = map to_primitive $_, @_[0,1];
934 72 100 100     177 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   336 my($obj,$func) = @_;
942 216 100       983 die new JE::Object::Error::TypeError $global,
943             add_line_number "$func is not an object"
944             if $func->primitive;
945              
946 214 100       788 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       646 return new JE::Boolean $global, 0 if $obj->primitive;
951              
952 208         793 my $proto_id = $func->prop('prototype');
953 208 100 100     806 !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         562 $proto_id = $proto_id->id;
957              
958 206   100     503 0 while (defined($obj = $obj->prototype)
959             or return new JE::Boolean $global, 0),
960             $obj->id ne $proto_id;
961            
962 204         766 new JE::Boolean $global, 1;
963             };
964             *{'inin'} = sub {
965 309     309   710 my($prop,$obj) = @_;
966 309 100       1174 die new JE::Object::Error::TypeError $global,
967             add_line_number "$obj is not an object"
968             if $obj->primitive;
969 308         1005 new JE::Boolean $global, defined $obj->prop($prop);
970             };
971             *{'in=='} = sub {
972 2724     2724   3454 my($x,$y) = @_;
973 2724         6454 my($xt,$yt) = (typeof $x, typeof $y);
974 2724         6157 my($xi,$yi) = ( id $x, id $y);
975 2724 100 100     18201 $xt eq $yt and return new JE::Boolean $global,
976             $xi eq $yi && $xi ne 'num:nan';
977              
978 146 100       305 $xi eq 'null' and
979             return new JE::Boolean $global,
980             $yi eq 'undef';
981 138 100       330 $xi eq 'undef' and
982             return new JE::Boolean $global,
983             $yi eq 'null';
984 98 100       208 $yi eq 'null' and
985             return new JE::Boolean $global,
986             $xi eq 'undef';
987 92 100       213 $yi eq 'undef' and
988             return new JE::Boolean $global,
989             $xi eq 'null';
990              
991 82 100       237 if($xt eq 'boolean') {
    100          
992 12         29 $x = to_number $x;
993 12         16 $xt = 'number';
994             }
995             elsif($yt eq 'boolean') {
996 8         20 $y = to_number $y;
997 8         12 $yt = 'number';
998             }
999              
1000 82 100 100     637 if($xt eq 'string' || $xt eq 'number' and !primitive $y)
    100 100        
      66        
      66        
1001 12         29 { $y = to_primitive $y; $yt = typeof $y }
  12         26  
1002             elsif
1003             ($yt eq 'string' || $yt eq 'number' and !primitive $x)
1004 42         140 { $x = to_primitive $x; $xt = typeof $x }
  42         93  
1005              
1006 82 50 66     568 ($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     123 $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   1753 new JE::Boolean $global, !&{'in=='}->[0];
  1460         3275  
1021             };
1022             *{'in==='} = sub {
1023 8959     8959   10613 my($x,$y) = @_;
1024 8959         19195 my($xi,$yi) = ( id $x, id $y);
1025 8959   100     49636 return new JE::Boolean $global,
1026             $xi eq $yi && $xi ne 'num:nan';
1027             };
1028             *{'in!=='} = sub {
1029 68     68   76 new JE::Boolean $global, !&{'in==='}->[0];
  68         148  
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   2248 my $num = shift->to_number->[0];
1037 899 100 100     4406 $num =
1038             $num != $num || abs($num) == inf
1039             ? 0
1040             : int($num) % 2**32;
1041 899 100       1761 $num -= 2**32 if $num >= 2**31;
1042              
1043 899         3690 my $num2 = shift->to_number->[0];
1044 899 100 100     3880 $num2 =
1045             $num2 != $num2 || abs($num2) == inf
1046             ? 0
1047             : int($num2) % 2**32;
1048 899 100       1658 $num2 -= 2**32 if $num2 >= 2**31;
1049              
1050 101     101   123609 use integer;
  101         142  
  101         376  
1051 899         2560 new JE::Number $global,
1052             $num & $num2;
1053             };
1054             *{'in^'} = sub {
1055 899     899   2253 my $num = shift->to_number->[0];
1056 899 100 100     4074 $num =
1057             $num != $num || abs($num) == inf
1058             ? 0
1059             : int($num) % 2**32;
1060 899 100       1683 $num -= 2**32 if $num >= 2**31;
1061              
1062 899         3595 my $num2 = shift->to_number->[0];
1063 899 100 100     3756 $num2 =
1064             $num2 != $num2 || abs($num2) == inf
1065             ? 0
1066             : int($num2) % 2**32;
1067 899 100       1594 $num2 -= 2**32 if $num2 >= 2**31;
1068              
1069 101     101   13782 use integer;
  101         139  
  101         333  
1070 899         2325 new JE::Number $global,
1071             $num ^ $num2;
1072             };
1073             *{'in|'} = sub {
1074 900     900   2371 my $num = shift->to_number->[0];
1075 900 100 100     4199 $num =
1076             $num != $num || abs($num) == inf
1077             ? 0
1078             : int($num) % 2**32;
1079 900 100       1982 $num -= 2**32 if $num >= 2**31;
1080              
1081 900         3675 my $num2 = shift->to_number->[0];
1082 900 100 100     6004 $num2 =
1083             $num2 != $num2 || abs($num2) == inf
1084             ? 0
1085             : int($num2) % 2**32;
1086 900 100       1609 $num2 -= 2**32 if $num2 >= 2**31;
1087              
1088 101     101   12559 use integer;
  101         128  
  101         304  
1089 900         2411 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   7104 no warnings 'exiting';
  101         132  
  101         33208  
1134 194631 100 100 194631   316022 ++ $ops>$counting and last JE_Code_OP if $counting;
1135            
1136 194630         170297 my $expr = shift;
1137              
1138 194630         230552 my $type = $$expr[1];
1139 194630         162716 my @labels;
1140              
1141 194630         216661 $pos = $$expr[0][0];
1142              
1143 194630 100       320502 if ($type eq 'expr') {
1144 72832         62090 my $result;
1145 72832 100       110393 if(@$expr == 3) { # no comma
1146 72434         119525 return _eval_term $$expr[-1];
1147             }
1148             else { # comma op
1149 398         1230 for (@$expr[2..$#$expr-1]) {
1150 1362         2167 $result = _eval_term $_ ;
1151 1362 100       4584 get $result if ref $result eq 'JE::LValue';
1152             }
1153 398         1045 $result = _eval_term $$expr[-1] ;
1154 398 100       2235 return ref $result eq 'JE::LValue' ? get $result
1155             : $result;
1156             }
1157             }
1158 121798 100       191142 if ($type eq 'assign') {
1159 7465         26251 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     19126 || ${$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         8244 my @terms = _eval_term ${shift @copy};
  7465         13604  
1179 7465         13170 my @ops;
1180 7465         14465 while(@copy) {
1181 6927         7071 push @ops, ${shift @copy};
  6927         10333  
1182 6927         6995 push @terms, _eval_term ${shift @copy};
  6927         11414  
1183             }
1184              
1185 7464         9786 my $val = pop @terms;
1186              
1187             # Now apply ? : if it's there
1188 561         1441 @qc_terms and $val = _eval_term
1189 7464 100       13216 ${$qc_terms[$val->to_boolean->[0]]};
1190              
1191 7464         12824 for (reverse @ops) {
1192 101     101   541 no strict 'refs';
  101         135  
  101         22534  
1193 164         665 length > 1 and $val =
1194 6926 100       17502 &{'in'.substr $_,0,-1}(
1195             $terms[-1], $val
1196             );
1197 6926 100       22304 $val = $val->get if ref $val eq 'JE::LValue';
1198 6926 50 33     23191 T and tainted $taint and $val->can('taint')
1199             and $val = taint $val $taint;
1200 6926         8240 eval { (pop @terms)->set($val) };
  6926         15973  
1201 6926 100       23628 if (my $err = $@) {
1202 1 50       7 die $err if UNIVERSAL::isa($err, 'JE::Object::Error');
1203 1         3 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       13631 if(!@ops) { # If we only have ? : and no assignment
1212 553 100       1314 $val = $val->get if ref $val eq 'JE::LValue';
1213             }
1214 7463         54595 return $val;
1215             }
1216 114333 100       180142 if($type eq 'lassoc') { # left-associative
1217 32141         85445 my @copy = \(@$expr[2..$#$expr]);
1218 32141         36795 my $result = _eval_term ${shift @copy};
  32141         55589  
1219 32141         65738 while(@copy) {
1220 101     101   493 no strict 'refs';
  101         118  
  101         15684  
1221             # We have to deal with || && here for the sake of
1222             # short-circuiting
1223 41298         40343 my $op = ${$copy[0]};
  41298         56631  
1224 41298 100       95821 if ($op eq '&&') {
    100          
1225 485 100       1197 $result = _eval_term(${$copy[1]}) if
  458         767  
1226             $result->to_boolean->[0];
1227 485 100       1415 $result = $result->get
1228             if ref $result eq 'JE::LValue';
1229             }
1230             elsif($op eq '||') {
1231 110 100       455 $result = _eval_term(${$copy[1]}) unless
  25         56  
1232             $result->to_boolean->[0];
1233 110 100       449 $result = $result->get
1234             if ref $result eq 'JE::LValue';
1235             }
1236             else {
1237 40703 100       99457 $result = $result->get
1238             if ref $result eq 'JE::LValue';
1239 40702         132768 $result = &{"in$op"}(
  40702         72775  
1240 40702         44574 $result, _eval_term ${$copy[1]}
1241             );
1242             }
1243 41289         164353 splice @copy, 0, 2; # double shift
1244             }
1245 32132         95456 return $result;
1246             }
1247 82192 100       130416 if ($type eq 'prefix') {
1248             # $$expr[1] -- 'prefix'
1249             # @$expr[2..-2] -- prefix ops
1250             # $$expr[-1] -- operand
1251 12091         19028 my $term = _eval_term $$expr[-1];
1252              
1253 101     101   502 no strict 'refs';
  101         119  
  101         116968  
1254 12091         31843 $term = &{"pre$_"}($term) for reverse @$expr[2..@$expr-2];
  12239         39171  
1255 12081         36707 return $term;
1256             }
1257 70101 100       109404 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         361 my $ret = (my $term = _eval_term $$expr[2])
1263             ->to_number;
1264 145         468 $term->set(new JE::Number $global,
1265             $ret->value + (-1,1)[$$expr[3] eq '++']);
1266 145         533 return $ret;
1267             }
1268 69956 100       106208 if ($type eq 'new') {
1269 1118 50       2079 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 68838 100       113764 if($type eq 'member/call') {
1279 62931         89557 my $obj = _eval_term $$expr[2];
1280 62931         142414 for (@$expr[3..$#$expr]) {
1281 69246 100       135442 if(ref eq 'JE::Code::Subscript') {
1282 46977 100       139890 $obj = get $obj
1283             if ref $obj eq 'JE::LValue';
1284 46977         104564 $obj = new JE::LValue $obj, $_->str_val;
1285             }
1286             else {
1287 22269 0       63200 $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 62764         245736 return $obj; # which may be an lvalue
1306             }
1307 5907 100       9939 if($type eq 'array') {
1308 5260         4414 my @ary;
1309 5260         8837 for (2..$#$expr) {
1310 36225 100       58013 if(ref $$expr[$_] eq 'comma') {
1311 15614 100 100     60725 ref $$expr[$_-1] eq 'comma' || $_ == 2
1312             and ++$#ary
1313             }
1314             else {
1315 20611         28300 push @ary, _eval_term $$expr[$_];
1316 20611 100       51507 $ary[-1] = $ary[-1]->get
1317             if ref $ary[-1] eq 'JE::LValue';
1318             }
1319             }
1320              
1321 5259         14001 my $ary = new JE::Object::Array $global;
1322 5259         7504 $$$ary{array} = \@ary; # sticking it in like this
1323             # makes 'undef' elements non-
1324             # existent, rather
1325             # than undefined
1326 5259         13607 return $ary;
1327             }
1328 647 100       1313 if($type eq 'hash') {
1329 463         1397 my $obj = new JE::Object $global;
1330 463         1523 local @_ = \(@$expr[2..$#$expr]);
1331 463         552 my (@keys, $key, $value);
1332 463         1105 while(@_) { # I have to loop through them to keep
1333             # the order.
1334 94         87 $key = ${+shift};
  94         147  
1335 94         122 $value = _eval_term ${shift;};
  94         159  
1336 94 100       258 $value = get $value if ref $value eq 'JE::LValue';
1337 94         224 $obj->prop($key, $value);
1338             }
1339 463         1590 return $obj;
1340             }
1341 184 50       559 if ($type eq 'func') {
1342             # format: [[...], function=> 'name',
1343             # [ params ], $statements_obj, \@vars]
1344             # or: [[...], function =>
1345             # [ params ], $statements_obj, \@vars]
1346 184 100       633 my($name,$params,$statements) = ref $$expr[2] ?
1347             (undef, @$expr[2,3]) : @$expr[2..4];
1348 184 100       341 my $func_scope = $name
1349             ? bless([@$scope, my $obj=new JE::Object $global],
1350             'JE::Scope')
1351             : $scope;
1352 184         1701 (my $new_code_obj = bless {
1353             map+($_=>$code->{$_}),qw/global source file line/
1354             }, 'JE::Code')
1355             ->{tree} = $statements;
1356 184         477 $new_code_obj->{vars} = $$expr[-1];
1357 184 100       1125 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       690 if($name) {
1364 7         35 $obj->prop({
1365             name => $name,
1366             value => $f,
1367             readonly => 1,
1368             dontdel => 1,
1369             });
1370             }
1371 184         638 return $f;
1372             }
1373             }
1374             sub _eval_term {
1375 300789     300789   312047 my $term = $_[0];
1376              
1377 300789 100       625330 return $term->eval if ref $term eq 'JE::Code::Expression';
1378              
1379 175104 50       1095637 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.065';
1400              
1401             sub str_val {
1402 46977     46977   60333 my $val = (my $self = shift)->[1];
1403 46977 100       112145 ref $val ? ''.$val->eval : $val;
1404             }
1405              
1406              
1407              
1408              
1409             package JE::Code::Arguments;
1410              
1411             our $VERSION = '0.065';
1412              
1413             sub list {
1414 23217     23217   23636 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 23217         20707 if(1) {
1422 23217         20129 my @result;
1423 23217         40134 for(@$self[1..$#$self]) {
1424 40871         58890 my $val = JE::Code::Expression::_eval_term($_);
1425 40868 100       117273 push @result, ref $val eq 'JE::LValue' ? $val->get : $val
1426             }
1427 23214         83874 @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__