Commit | Line | Data |
6d7fb585 |
1 | package ExtUtils::Constant::ProxySubs; |
2 | |
3 | use strict; |
4 | use vars qw($VERSION @ISA %type_to_struct %type_to_sv %type_to_C_value |
5 | %type_is_a_problem %type_num_args); |
6 | use Carp; |
7 | require ExtUtils::Constant::XS; |
8 | use ExtUtils::Constant::Utils qw(C_stringify); |
9 | use ExtUtils::Constant::XS qw(%XS_TypeSet); |
10 | |
11 | $VERSION = '0.01'; |
12 | @ISA = 'ExtUtils::Constant::XS'; |
13 | |
14 | %type_to_struct = |
15 | ( |
16 | IV => '{const char *name; I32 namelen; IV value;}', |
17 | '' => '{const char *name; I32 namelen;} ', |
18 | ); |
19 | |
20 | %type_to_sv = |
21 | ( |
22 | IV => sub { 'newSViv(' . $_[0] . '->value)' }, |
23 | '' => sub { '&PL_sv_yes' }, |
24 | ); |
25 | |
26 | %type_to_C_value = |
27 | ( |
28 | '' => sub {}, |
29 | ); |
30 | |
31 | %type_is_a_problem = |
32 | ( |
33 | SV => 1, |
34 | ); |
35 | |
36 | while (my ($type, $value) = each %XS_TypeSet) { |
37 | $type_num_args{$type} = ref $value ? scalar @$value : 1; |
38 | } |
39 | $type_num_args{''} = 0; |
40 | |
41 | sub partition_names { |
42 | my ($self, $default_type, @items) = @_; |
43 | my (%found, @notfound, @trouble); |
44 | |
45 | while (my $item = shift @items) { |
46 | my $default = delete $item->{default}; |
47 | if ($default) { |
48 | # If we find a default value, convert it into a regular item and |
49 | # append it to the queue of items to process |
50 | my $default_item = {%$item}; |
51 | $default_item->{invert_macro} = 1; |
52 | $default_item->{pre} = delete $item->{def_pre}; |
53 | $default_item->{post} = delete $item->{def_post}; |
54 | $default_item->{type} = shift @$default; |
55 | $default_item->{value} = $default; |
56 | push @items, $default_item; |
57 | } else { |
58 | # It can be "not found" unless it's the default (invert the macro) |
59 | # or the "macro" is an empty string (ie no macro) |
60 | push @notfound, $item unless $item->{invert_macro} |
61 | or !$self->macro_to_ifdef($self->macro_from_name($item)); |
62 | } |
63 | |
64 | if ($item->{pre} or $item->{post} or $type_is_a_problem{$item->{type}}) { |
65 | push @trouble, $item; |
66 | } else { |
67 | push @{$found{$item->{type}}}, $item; |
68 | } |
69 | } |
70 | # use Data::Dumper; print Dumper \%found; |
71 | (\%found, \@notfound, \@trouble); |
72 | } |
73 | |
74 | sub boottime_iterator { |
75 | my ($self, $type, $iterator, $hash, $subname) = @_; |
76 | my $generator = $type_to_sv{$type}; |
77 | die "Can't find generator code for type $type" |
78 | unless defined $generator; |
79 | |
80 | my $athx = $self->C_constant_prefix_param(); |
81 | |
82 | return sprintf <<"EOBOOT", &$generator($iterator); |
83 | while ($iterator->name) { |
84 | $subname($athx $hash, $iterator->name, |
85 | $iterator->namelen, %s); |
86 | ++$iterator; |
87 | } |
88 | EOBOOT |
89 | } |
90 | |
91 | sub WriteConstants { |
92 | my $self = shift; |
93 | my $ARGS = shift; |
94 | |
95 | my ($c_fh, $xs_fh, $c_subname, $xs_subname, $default_type, $package) |
96 | = @{$ARGS}{qw(c_fh xs_fh c_subname xs_subname default_type package)}; |
97 | |
98 | $xs_subname ||= 'constant'; |
99 | |
100 | croak("Package name '$package' contains % characters") if $package =~ /%/; |
101 | |
102 | # All the types we see |
103 | my $what = {}; |
104 | # A hash to lookup items with. |
105 | my $items = {}; |
106 | |
107 | my @items = $self->normalise_items ({disable_utf8_duplication => 1}, |
108 | $default_type, $what, $items, @_); |
109 | |
110 | # Partition the values by type. Also include any defaults in here |
111 | # Everything that doesn't have a default needs alternative code for |
112 | # "I'm missing" |
113 | # And everything that has pre or post code ends up in a private block |
114 | my ($found, $notfound, $trouble) |
115 | = $self->partition_names($default_type, @items); |
116 | |
117 | die "Can't cope with trouble yet" if @$trouble; |
118 | |
119 | my $pthx = $self->C_constant_prefix_param_defintion(); |
120 | my $athx = $self->C_constant_prefix_param(); |
121 | my $symbol_table = C_stringify($package) . '::'; |
122 | |
123 | print $c_fh <<"EOADD"; |
124 | void ${c_subname}_add_symbol($pthx HV *hash, const char *name, I32 namelen, SV *value) { |
125 | SV *rv = newRV_noinc(value); |
126 | if (!hv_store(hash, name, namelen, rv, TRUE)) { |
127 | SvREFCNT_dec(rv); |
128 | Perl_croak("Couldn't add key '%s' to %%%s", name, "$package"); |
129 | } |
130 | } |
131 | |
132 | static HV *${c_subname}_missing = NULL; |
133 | |
134 | EOADD |
135 | |
136 | print $xs_fh <<"EOBOOT"; |
137 | BOOT: |
138 | { |
139 | #ifdef dTHX |
140 | dTHX; |
141 | #endif |
142 | HV *symbol_table = get_hv("$symbol_table", TRUE); |
143 | EOBOOT |
144 | |
145 | my %iterator; |
146 | |
147 | $found->{''} |
148 | = [map {{%$_, type=>'', invert_macro => 1}} @$notfound]; |
149 | |
150 | foreach my $type (sort keys %$found) { |
151 | my $struct = $type_to_struct{$type}; |
152 | my $type_to_value = $type_to_C_value{$type} |
153 | || sub {return map {ref $_ ? @$_ : $_} @_}; |
154 | my $number_of_args = $type_num_args{$type}; |
155 | die "Can't find structure definition for type $type" |
156 | unless defined $struct; |
157 | |
158 | my $struct_type = $type ? lc($type) . '_s' : 'notfound_s'; |
159 | print $c_fh "struct $struct_type $struct;\n"; |
160 | |
161 | my $array_name = 'values_for_' . ($type ? lc $type : 'notfound'); |
162 | print $xs_fh <<"EOBOOT"; |
163 | |
164 | static const struct $struct_type $array_name\[] = |
165 | { |
166 | EOBOOT |
167 | |
168 | |
169 | foreach my $item (@{$found->{$type}}) { |
170 | my $name = $item->{name}; |
171 | my $value = $item->{value}; |
172 | $value = $item->{name} unless defined $value; |
173 | |
174 | my $namelen = length $name; |
175 | if ($name =~ tr/\0-\377// != $namelen) { |
176 | # the hash API signals UTF-8 by passing the length negated. |
177 | utf8::encode($name); |
178 | $namelen = -length $name; |
179 | } |
180 | $name = C_stringify($name); |
181 | |
182 | my $macro = $self->macro_from_name($item); |
183 | my $ifdef = $self->macro_to_ifdef($macro); |
184 | if (!$ifdef && $item->{invert_macro}) { |
185 | carp("Attempting to supply a default for '$name' which has no conditional macro"); |
186 | next; |
187 | } |
188 | print $xs_fh $ifdef; |
189 | if ($item->{invert_macro}) { |
190 | print $xs_fh |
191 | " /* This is the default value: */\n" if $type; |
192 | print $xs_fh "#else\n"; |
193 | } |
194 | print $xs_fh " { ", join (', ', "\"$name\"", $namelen, |
195 | &$type_to_value($value)), " },\n", |
196 | $self->macro_to_endif($macro); |
197 | } |
198 | |
199 | |
200 | # Terminate the list with a NULL |
201 | print $xs_fh " { NULL, 0", (", 0" x $number_of_args), " } };\n"; |
202 | |
203 | $iterator{$type} = "value_for_" . ($type ? lc $type : 'notfound'); |
204 | |
205 | print $xs_fh <<"EOBOOT"; |
206 | const struct $struct_type *$iterator{$type} = $array_name; |
207 | |
208 | EOBOOT |
209 | } |
210 | |
211 | delete $found->{''}; |
212 | foreach my $type (sort keys %$found) { |
213 | print $xs_fh $self->boottime_iterator($type, $iterator{$type}, |
214 | 'symbol_table', |
215 | "${c_subname}_add_symbol"); |
216 | } |
217 | print $xs_fh <<"EOBOOT"; |
218 | |
219 | ${c_subname}_missing = newHV(); |
220 | while (value_for_notfound->name) { |
221 | if (!hv_store(${c_subname}_missing, value_for_notfound->name, |
222 | value_for_notfound->namelen, &PL_sv_yes, TRUE)) |
223 | Perl_croak("Couldn't add key '%s' to missing_hash", |
224 | value_for_notfound->name); |
225 | ++value_for_notfound; |
226 | } |
227 | } |
228 | EOBOOT |
229 | |
230 | print $xs_fh <<EOCONSTANT |
231 | |
232 | void |
233 | $xs_subname(sv) |
234 | PREINIT: |
235 | STRLEN len; |
236 | INPUT: |
237 | SV * sv; |
238 | const char * s = SvPV(sv, len); |
239 | PPCODE: |
240 | if (hv_exists(${c_subname}_missing, s, SvUTF8(sv) ? -len : len)) { |
241 | sv = newSVpvf("Your vendor has not defined $package macro %" SVf |
242 | ", used", sv); |
243 | } else { |
244 | sv = newSVpvf("%" SVf " is not a valid $package macro", sv); |
245 | } |
246 | PUSHs(sv_2mortal(sv)); |
247 | EOCONSTANT |
248 | } |
249 | |
250 | 1; |