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