File Coverage

blib/lib/types.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package types;
2              
3 5     5   370432 use 5.008;
  5         22  
  5         1375  
4 5     5   35 use strict;
  5         14  
  5         273  
5 5     5   26 use warnings;
  5         18  
  5         169  
6 5     5   8153 use optimize;
  0            
  0            
7              
8             our $VERSION;
9             $VERSION = do { my @r = (q$Revision: 1.5 $ =~ /\d+/g); $r[0] = 0; sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker
10              
11              
12             my %typed;
13             my %op_returns;
14             my %function_returns;
15             my %function_params;
16             use constant SVpad_TYPED => 0x40000000;
17             use B qw(OPpTARGET_MY OPf_MOD SVf_POK);
18             use B::Utils;
19              
20             our %const_map = (
21             "B::NV" => 'float',
22             "B::IV" => 'int',
23             "B::PV" => 'string',
24             );
25              
26              
27             sub compare_type {
28             my($a,$b) = @_;
29             if($a eq $b) {
30             return 1;
31             }
32             return 0;
33             }
34              
35             sub B::NULL::name { "void" }
36             use Data::Dumper;
37             sub check {
38             my $class = shift;
39             my $op = shift;
40             my $cv = $op->find_cv();
41              
42             #if($^H & 0x00000010) {
43             unless($optimize::state->private & 0x00000010) {
44             return;
45             }
46              
47              
48             # if($op->name eq 'padsv') {
49             # print $op->flags ."\n";
50             # }
51              
52             if(ref($op) eq 'B::PADOP' && $op->name eq 'gv') {
53             # $op->dump;
54             my $target = (($cv->PADLIST->ARRAY)[1]->ARRAY)[$op->padix];
55             # $target->dump;
56             # exit;
57             }
58              
59             if($op->name eq 'int') {
60             $op_returns{$op->seq}->{type} = 'int';
61             $op_returns{$op->seq}->{name} = 'int()';
62             }
63              
64             if($op->name eq 'padsv') {
65             my $target = (($cv->PADLIST->ARRAY)[0]->ARRAY)[$op->targ];
66             if(UNIVERSAL::isa($target,'B::SV') && $target->FLAGS & SVpad_TYPED) {
67             $typed{$cv->ROOT->seq}->{$op->targ}->{type} = $target->SvSTASH->NAME;
68             $typed{$cv->ROOT->seq}->{$op->targ}->{name} = $target->PV;
69             } elsif(UNIVERSAL::isa($target,'B::SV') &&
70             exists($typed{$cv->ROOT->seq}->{$target->PV})) {
71             $typed{$cv->ROOT->seq}->{$op->targ} = $typed{$cv->ROOT->seq}->{$target->PV};
72             }
73             }
74             if($cv->FLAGS & SVf_POK && !$function_params{$cv->START->seq}) {
75             #we have, we have, we have arguments
76             my @type;
77             my @name;
78             my $i = 1;
79             foreach (split ",", $cv->PV) {
80             my ($type, $sigil, $name) = split /\b/, $_;
81             # print "$type - $sigil - $name \n";
82             push @type, $type;
83             if($sigil && $name) {
84             push @name, $sigil.$name;
85             $typed{$cv->ROOT->seq}->{"$sigil$name"}->{type} = $type;
86             $typed{$cv->ROOT->seq}->{"$sigil$name"}->{name} = $sigil.$name;
87             } else {
88             push @name, "Argument $i";
89             }
90             $i++;
91             }
92              
93             $function_params{$cv->START->seq}->{name} = \@name;
94             $function_params{$cv->START->seq}->{type} = \@type;
95              
96              
97             #print $cv->PV . "\n";
98             $cv->PV(";@");
99              
100             }
101              
102             if(ref($op->next) ne 'B::NULL' &&
103             ($op->next->name =~/2cv$/ ||
104             ($op->next->name eq 'null' && $op->next->oldname =~/2cv$/))) {
105             my $entersub = $op->next;
106             my $i = 1;
107              
108             while($entersub->name ne 'entersub' &&
109             ref($entersub->next) ne 'B::NULL') {
110             $i++ if($entersub->name ne 'null');
111             $entersub = $entersub->next;
112              
113             }
114             if($entersub->name eq 'entersub') {
115             my $sv;
116             if(ref($op) eq 'B::PADOP') {
117             $sv = (($cv->PADLIST->ARRAY)[1]->ARRAY)[$op->padix];
118             } else {
119             die;
120             }
121             if(ref($sv->CV) ne 'B::SPECIAL') {
122             my $foo = $sv->CV->START->seq;
123             if(exists($function_returns{$foo})) {
124             $op_returns{$op->seq + $i}->{type} = $function_returns{$foo}->{type};
125             $op_returns{$op->seq + $i}->{name} = $sv->STASH->NAME . "::" . $sv->SAFENAME."()";
126             # print "AND IT HAS A RETURN VALUE $i\n";
127             }
128             if(exists($function_params{$foo})) {
129             my $param_list = $entersub->first();
130             get_list_proto($param_list, $cv);
131             $param_list = delete($op_returns{$param_list->seq});
132             pop(@{$param_list->{type}});
133             pop(@{$param_list->{name}});
134             # print Data::Dumper::Dumper($function_params{$foo});
135             # print Data::Dumper::Dumper($param_list);
136             match_protos($function_params{$foo}, $param_list);
137             }
138             # $sv->CV->dump();
139             }
140             }
141             }
142              
143             sub match_protos {
144             my ($target, $source) = @_;
145             my $targets = scalar @{$target->{name}} - 1;
146             my $sources = scalar @{$source->{name}} - 1;
147              
148             if($sources < $targets) {
149             die "Not enough items in list at " .
150             $optimize::state->file . ":" .
151             $optimize::state->line . "\n";
152             }
153             foreach my $i (0..$targets) {
154             my ($target_name, $target_type) =
155             ($target->{name}->[$i], $target->{type}->[$i]);
156             my ($source_name, $source_type) =
157             ($source->{name}->[$i], $source->{type}->[$i]);
158             if((!$target_type->isa($source_type) and !$source_type->isa($target_type)) or ($target_type->can('check') && !$target_type->check($source_type))) {
159             die "Type mismatch in list for" .
160             " $source_type ($source_name) to $target_type ($target_name) at " .
161             $optimize::state->file . ":" .
162             $optimize::state->line . "\n";
163             }
164              
165            
166             }
167             }
168            
169             if(ref($op->next) ne 'B::NULL') {
170             # print $op->name . " - " . $op->next->name . "\n";
171             }
172             if(ref($op->next) ne 'B::NULL' &&
173             ref($op->next->next) ne 'B::NULL' &&
174             $op->next->next->name eq 'entersub') {
175             # print "sub entry\n";
176             }
177              
178              
179             if(ref($op) eq 'B::LISTOP' && $op->first->name eq 'pushmark') {
180             get_list_proto($op,$cv);
181             }
182            
183              
184             if(ref($op->next) ne 'B::NULL' &&
185             ref($cv->START) ne 'B::NULL' &&
186             ($op->next->name eq 'lineseq' &&
187             $op->next->next->name =~/^leave/) ||
188             $op->next->name eq 'return') {
189             my ($type, $value, $const) = get_type($op, $cv);
190             my $lineseq = $op->next;
191             my $leave = $lineseq->next;
192              
193             if(exists($function_returns{$cv->START->seq}) &&
194             $function_returns{$cv->START->seq}->{type} ne $type) {
195             die "Return type mismatch: " . $op->name .
196             " $type at " .
197             $optimize::state->file . ":" .
198             $optimize::state->line . " does not match" .
199             " return value $function_returns{$cv->START->seq}->{type}".
200             " at $function_returns{$cv->START->seq}->{file}\n";
201            
202            
203            
204             }
205            
206            
207             my $subname = "";
208            
209             if(ref($cv->GV) ne 'B::SPECIAL' && $cv->GV->SAFENAME ne '__ANON__') {
210             $subname = $cv->GV->STASH->NAME . "::" . $cv->GV->SAFENAME;
211             }
212             if($subname && exists($function_returns{$subname}) &&
213             $function_returns{$subname}->{type} ne $type) {
214             die "Function $subname redefined with a different type (was $function_returns{$subname}->{type} now $type) at " . $optimize::state->file . ":" . $optimize::state->line . "\n";
215             }
216              
217              
218            
219             $function_returns{$cv->START->seq}->{type} = $type;
220             $function_returns{$cv->START->seq}->{name} = $value;
221             $function_returns{$cv->START->seq}->{file} = $optimize::state->file . ":" . $optimize::state->line;
222              
223             if($subname) {
224             $function_returns{$subname} = $function_returns{$cv->START->seq};
225             # print "GOT subname $subname\n";
226             }
227             # print "scope leave retval ($type, $value): " . $op->name . "-" . $lineseq->next->name . "\n";
228              
229              
230             }
231              
232              
233              
234              
235              
236             if(ref($op) eq 'B::BINOP') {
237            
238             my ($lhs, $rhs, $target, $expr, $const, $mod);
239             my ($lhs_v, $rhs_v, $target_v, $expr_v);
240            
241              
242             if($op->private & OPpTARGET_MY &&
243             exists($typed{$cv->ROOT->seq}->{$op->targ})) {
244             $target = $typed{$cv->ROOT->seq}->{$op->targ}->{type};
245             $target_v = $typed{$cv->ROOT->seq}->{$op->targ}->{name};
246             }
247              
248             if($op->first->name eq 'padsv'
249             && exists($typed{$cv->ROOT->seq}->{$op->first->targ})) {
250             $rhs = $typed{$cv->ROOT->seq}->{$op->first->targ}->{type};
251             $rhs_v = $typed{$cv->ROOT->seq}->{$op->first->targ}->{name};
252             } elsif(exists($op_returns{$op->first->seq})) {
253             $rhs = $op_returns{$op->first->seq}->{type};
254             $rhs_v = $op_returns{$op->first->seq}->{name};
255             } elsif($op->first->name eq 'const' &&
256             exists($const_map{ref($op->first->sv)})) {
257             $rhs = $const_map{ref($op->first->sv)};
258             $rhs_v = "constant '" . $op->first->sv->sv."'";
259             $const++;
260             } elsif($op->first->name eq 'null' &&
261             $op->first->oldname eq 'list') {
262             get_list_proto($op->first,$cv);
263             }
264              
265             if($op->last->name eq 'padsv'
266             && exists($typed{$cv->ROOT->seq}->{$op->last->targ})) {
267             $lhs = $typed{$cv->ROOT->seq}->{$op->last->targ}->{type};
268             $lhs_v = $typed{$cv->ROOT->seq}->{$op->last->targ}->{name};
269             if($op->last->flags & OPf_MOD) {
270             die "target should be empty" if($target);
271             $target = $lhs;
272             $target_v = $lhs_v;
273             $mod++;
274             }
275             } elsif(exists($op_returns{$op->last->seq})) {
276             $lhs = $op_returns{$op->last->seq}->{type};
277             $lhs_v = $op_returns{$op->last->seq}->{name};
278             } elsif($op->last->name eq 'const' &&
279             exists($const_map{ref($op->last->sv)})) {
280             $lhs = $const_map{ref($op->last->sv)};
281             $lhs_v = "constant '" . $op->last->sv->sv."'";
282              
283             } elsif($op->last->name eq 'null' &&
284             $op->last->oldname eq 'list') {
285             get_list_proto($op->first,$cv);
286             }
287              
288              
289             $lhs_v = $lhs = "unknown" unless($lhs);
290             $rhs_v = $rhs = "unknown" unless($rhs);
291            
292             $target_v = $target = "" unless($target);
293              
294              
295             return if($target eq '' && $const);
296              
297              
298             #first lets deterimne what the expression returns
299             # if they are equal the expression returns that
300             # otherwise it returns what is higher on he inclusion team
301              
302            
303             {
304             my($is_lhs, $is_rhs) = (0,0);
305             if($lhs->can("check") && $lhs->check($rhs)) {
306             $is_lhs = 1;
307             } elsif($lhs->isa($rhs)) {
308             $is_lhs = 1;
309             }
310              
311             if($rhs->can("check") && $rhs->check($lhs)) {
312             $is_rhs = 1;
313             } elsif($rhs->isa($lhs)) {
314             $is_rhs = 1;
315             }
316             if($is_lhs && $is_rhs) {
317             $expr = $lhs;
318              
319             } elsif($is_lhs) {
320             $expr = $lhs;
321             # print "$lhs < $rhs\n";
322             } elsif($is_rhs) {
323             $expr = $rhs;
324             # print "$rhs < $lhs\n";
325             } else {
326             die "Type mismatch, can't " . $op->name .
327             " $rhs ($rhs_v) to $lhs ($lhs_v) at " .
328             $optimize::state->file . ":" .
329             $optimize::state->line . "\n";
330             }
331             $expr_v = "$lhs_v, $rhs_v";
332             # print "Expression returns ($expr) ($expr_v)" .
333             # $optimize::state->file . ": . " .
334             # $optimize::state->line . "\n";
335             }
336            
337              
338             # return if(!$lhs and $op->first->name eq 'const');
339            
340             #
341              
342             unless($target) {
343             #the target is empty
344             $op_returns{$op->seq}->{type} = $expr;
345             $op_returns{$op->seq}->{name} = $expr_v;
346             return;
347             }
348            
349             # print "$expr - $target\n";
350             # print "$target->isa($expr): ". $target->isa($expr) . "\n";
351             # print "$expr->isa($target): ". $expr->isa($target) . "\n";
352              
353              
354              
355             if((!$target->isa($expr) and !$expr->isa($target)) or ($target->can('check') && !$target->check($expr))) {
356             if($mod) {
357             die "Type mismatch, can't " . $op->name .
358             " $rhs ($rhs_v) to $lhs ($lhs_v) at " .
359             $optimize::state->file . ":" .
360             $optimize::state->line . "\n";
361             } else {
362             die "Type mismatch, can't assign result of $lhs $lhs_v "
363             . $op->name . " $rhs $rhs_v to $target ($target_v) at "
364             . $optimize::state->file . ":"
365             . $optimize::state->line . "\n";
366             }
367             }
368             $op_returns{$op->seq}->{type} = $target;
369             $op_returns{$op->seq}->{name} = $target_v;
370             }
371              
372             }
373              
374             BEGIN { $optimize::loaded{"types"} = __PACKAGE__ }
375              
376             sub import {
377             my ($package, $filename, $line) = caller;
378             #$^H |= 0x00020000;
379             #$^H{"use_types"}++;
380             $^H |= 0x00000010;
381             # optimize->register(\&entry, $package, $filename, $line);
382             }
383              
384             sub unimport {
385             my ($package, $filename, $line) = caller;
386             $^H &= ~ 0x00000010;
387             #$^H |= 0x00020000;
388             #delete($^H{"use_types"});
389             # optimize->unregister($package);
390             }
391              
392             sub get_type {
393             my($op, $cv) = @_;
394             my ($type, $value, $const) = ("","",0);
395             if($op->name eq 'padsv'
396             && exists($typed{$cv->ROOT->seq}->{$op->targ})) {
397             $type = $typed{$cv->ROOT->seq}->{$op->targ}->{type};
398             $value = $typed{$cv->ROOT->seq}->{$op->targ}->{name};
399             } elsif(exists($op_returns{$op->seq})) {
400             $type = $op_returns{$op->seq}->{type};
401             $value = $op_returns{$op->seq}->{name};
402             } elsif($op->name eq 'const' &&
403             exists($const_map{ref($op->sv)})) {
404             $type = $const_map{ref($op->sv)};
405             $value = "constant '" . $op->sv->sv."'";
406             $const++;
407             } elsif($op->name eq 'null' &&
408             $op->oldname eq 'list') {
409             get_list_proto($op,$cv);
410             } else {
411             $type = $value = "unknown";
412             }
413             return ($type, $value, $const);
414             }
415              
416             sub get_list_proto {
417             my ($op, $cv) = @_;
418             my $o = $op->first->sibling();
419             # print "start\n";
420             my @type;
421             my @name;
422             while(ref($o) ne 'B::NULL') {
423             my $kid = $o;
424             if($o->name eq 'null') {
425             $kid = $o->first;
426             }
427             if($kid->name eq 'padsv' &&
428             exists($typed{$cv->ROOT->seq}->{$kid->targ})) {
429             push @type, $typed{$cv->ROOT->seq}->{$kid->targ}->{type};
430             push @name, $typed{$cv->ROOT->seq}->{$kid->targ}->{name};
431             } elsif(exists($op_returns{$kid->seq})) {
432             push @type, $op_returns{$kid->seq}->{type};
433             push @name, $op_returns{$kid->seq}->{name};
434             } elsif($kid->name eq 'const' &&
435             exists($const_map{ref($kid->sv)})) {
436             push @type, $const_map{ref($kid->sv)};
437             push @name, $kid->sv->sv;
438             } else {
439             push @type, "unknown";
440             push @name, "unknown";
441             }
442             # print $kid->name . "\n";
443             $o = $o->sibling;
444             }
445              
446             if(@type > 1) {
447             $op_returns{$op->seq}->{type} = \@type;
448             $op_returns{$op->seq}->{name} = \@name;
449             } else {
450             $op_returns{$op->seq}->{type} = $type[0];
451             $op_returns{$op->seq}->{name} = $name[0];
452             }
453             use Data::Dumper;
454             # print Dumper(\@type);
455              
456             }
457              
458             package unknown;
459             our $dummy = 1;
460              
461              
462             package int;
463             our $dummy = 1;
464             sub check {
465             return 0 if($_[0] eq 'int' && ($_[1] ne 'number' && $_[1] ne 'int'));
466             return 1;
467             }
468             package float;
469             use base qw(int);
470             sub check {
471             return 0 if($_[1] eq 'string');
472             return 1;
473             }
474             our $dummy = 1;
475             package number;
476             use base qw(float);
477             sub check {
478             return 0 if($_[1] eq 'string');
479             return 1;
480             }
481             our $dummy = 1;
482             package string;
483             use base qw(number);
484             sub check { return 1};
485             1;
486             __END__