File Coverage

blib/lib/ExtUtils/XSpp/Typemap.pm
Criterion Covered Total %
statement 75 93 80.6
branch 15 20 75.0
condition 7 12 58.3
subroutine 21 27 77.7
pod 10 23 43.4
total 128 175 73.1


line stmt bran cond sub pod time code
1             package ExtUtils::XSpp::Typemap;
2 21     21   1806267 use strict;
  21         43  
  21         773  
3 21     21   124 use warnings;
  21         45  
  21         706  
4              
5 21     21   23907 use ExtUtils::Typemaps;
  21         1096995  
  21         32744  
6              
7             require ExtUtils::XSpp::Node::Type;
8             require ExtUtils::XSpp::Typemap::parsed;
9             require ExtUtils::XSpp::Typemap::simple;
10             require ExtUtils::XSpp::Typemap::reference;
11              
12             my %TypemapsByName;
13              
14             =head1 NAME
15              
16             ExtUtils::XSpp::Typemap - map types
17              
18             =cut
19              
20             sub new {
21 3131     3131 0 4239 my $class = shift;
22 3131         8063 my $this = bless {}, $class;
23              
24 3131         9196 $this->init( @_ );
25              
26 3131         7583 return $this;
27             }
28              
29             sub create {
30 28     28 0 80 my( $name, @args ) = @_;
31              
32 28 50       92 if( my $template = $TypemapsByName{$name} ) {
33 0         0 my $package = ref $template;
34              
35 0         0 return $package->new( base => $template, @args );
36             } else {
37 28         79 my $package = "ExtUtils::XSpp::Typemap::" . $name;
38              
39 28         234 return $package->new( @args );
40             }
41             }
42              
43             =head1 METHODS
44              
45             =head2 ExtUtils::XSpp::Typemap::type
46              
47             Returns the ExtUtils::XSpp::Node::Type that is used for this typemap.
48              
49             =cut
50              
51 122     122 1 494 sub type { $_[0]->{TYPE} }
52              
53             =head2 ExtUtils::XSpp::Typemap::xs_type()
54              
55             (Optional) XS typemap identifier (e.g. T_IV) for this C++ type.
56              
57             =head2 ExtUtils::XSpp::Typemap::xs_input_code()
58              
59             (Optional) XS input code for the associated XS typemap.
60              
61             =head2 ExtUtils::XSpp::Typemap::xs_output_code()
62              
63             (Optional) XS output code for the associated XS typemap.
64              
65             =head2 ExtUtils::XSpp::Typemap::cpp_type()
66              
67             Returns the C++ type to be used for the local variable declaration.
68              
69             =head2 ExtUtils::XSpp::Typemap::input_code( perl_argument_name, cpp_var_name1, ... )
70              
71             Code to put the contents of the perl_argument (typically ST(x)) into
72             the C++ variable(s).
73              
74             =head2 ExtUtils::XSpp::Typemap::output_code( perl_variable, c_variable )
75              
76             =head2 ExtUtils::XSpp::Typemap::cleanup_code( perl_variable, c_variable )
77              
78             =head2 ExtUtils::XSpp::Typemap::call_parameter_code( parameter_name )
79              
80             =head2 ExtUtils::XSpp::Typemap::call_function_code( function_call_code, return_variable )
81              
82             Allows modifying the code used in the function/method call. The first
83             parameter has the form Cmethod( )>, the second
84             parameter is a variable to hold the return value.
85              
86             =cut
87              
88 0     0 0 0 sub init { }
89              
90 1609     1609 1 5473 sub xs_type { $_[0]->{XS_TYPE} }
91 13     13 1 53 sub xs_input_code { $_[0]->{XS_INPUT_CODE} }
92 13     13 1 52 sub xs_output_code { $_[0]->{XS_OUTPUT_CODE} }
93 3333     3333 0 12190 sub name { $_[0]->{NAME} }
94 0     0 1 0 sub cpp_type { die; }
95 0     0 1 0 sub input_code { die; }
96 111     111 0 264 sub precall_code { undef }
97 0     0 1 0 sub output_code { undef }
98 71     71 1 318 sub cleanup_code { undef }
99 0     0 1 0 sub call_parameter_code { undef }
100 0     0 1 0 sub call_function_code { undef }
101 140     140 0 2597 sub output_list { undef }
102              
103             my @Typemaps;
104             my $Default_output_code = 'sv_setref_pv( $arg, xsp_constructor_class("${my $ntt = $type; $ntt =~ s{^const\s+|[ \t*]+$}{}g; \\$ntt}"), (void*)$var );';
105             my $Default_input_code = <<'INPUTCODE';
106             if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) )
107             $var = ($type)SvIV((SV*)SvRV( $arg ));
108             else{
109             warn( \"${Package}::$func_name() -- $var is not a blessed SV reference\" );
110             XSRETURN_UNDEF;
111             }
112             INPUTCODE
113              
114              
115             # add typemaps for basic C types
116             add_default_typemaps();
117              
118             sub add_typemap_for_type {
119 3021     3021 0 4731 my( $type, $typemap ) = @_;
120              
121 3021         6260 unshift @Typemaps, [ $type, $typemap ];
122 3021 100       6488 $TypemapsByName{$typemap->name} = $typemap if $typemap->name;
123             }
124              
125             sub reset_typemaps {
126 166     166 0 242547 @Typemaps = ();
127 166         592 add_default_typemaps();
128             }
129              
130             # a weak typemap does not override an already existing typemap for the
131             # same type
132             sub add_weak_typemap_for_type {
133 108     108 0 174 my( $type, $typemap ) = @_;
134 108         278 push @Typemaps, [ $type, $typemap ];
135 108 50 0     413 $TypemapsByName{$typemap->name} ||= $typemap if $typemap->name;
136             }
137              
138             sub get_typemap_for_type {
139 227     227 0 384 my $type = shift;
140              
141 227         433 foreach my $t ( @Typemaps ) {
142 2407 100       6707 return ${$t}[1] if $t->[0]->equals( $type );
  227         1370  
143             }
144              
145             # construct verbose error message:
146 0         0 my $errmsg = "No typemap for type " . $type->print
147             . "\nThere are typemaps for the following types:\n";
148 0         0 my @types;
149 0         0 foreach my $t (@Typemaps) {
150 0         0 push @types, " - " . $t->[0]->print . "\n";
151             }
152              
153 0 0       0 if (@types) {
154 0         0 $errmsg .= join('', @types);
155             }
156             else {
157 0         0 $errmsg .= " (none)\n";
158             }
159 0         0 $errmsg .= "Did you forget to declare your type in an XS++ typemap?";
160              
161 0         0 Carp::confess( $errmsg );
162             }
163              
164             sub get_xs_typemap_code_for_all_typemaps {
165 87     87 0 940 my $typemaps = ExtUtils::Typemaps->new;
166              
167             # process typemaps in reverse order, so newer ones take precedence
168 87         2988 my @xs_typemaps = grep $_->[1]->xs_type, reverse @Typemaps;
169 87 100       818 return unless @xs_typemaps;
170              
171 4         7 my %xs_types;
172 4   100     23 foreach my $typemap (grep $_->[1]->cpp_type && $_->[1]->cpp_type ne '_', @xs_typemaps) {
173 7         479 my $xstype = $typemap->[1]->xs_type;
174              
175 7         25 $xs_types{$typemap->[1]->cpp_type} = $xstype;
176 7         27 $typemaps->add_typemap(
177             ctype => $typemap->[1]->cpp_type,
178             xstype => $xstype,
179             replace => 1,
180             );
181             }
182              
183             # avoid adding INPUT/OUTPUT sections for unused mappings
184 4         481 %xs_types = reverse %xs_types;
185 4   50     18 foreach my $typemap (grep $xs_types{$_->[1]->xs_type || ''}, @xs_typemaps) {
186 9         256 my $xstype = $typemap->[1]->xs_type;
187              
188 9 100       41 $typemaps->add_inputmap(
189             xstype => $xstype,
190             code => $typemap->[1]->xs_input_code,
191             replace => 1,
192             ) if $typemap->[1]->xs_input_code;
193              
194 9 100       397 $typemaps->add_outputmap(
195             xstype => $xstype,
196             code => $typemap->[1]->xs_output_code,
197             replace => 1,
198             ) if $typemap->[1]->xs_output_code;
199             }
200              
201 4 50       350 return '' if $typemaps->is_empty;
202 4         41 my $code = $typemaps->as_string;
203 4         224 my $end_marker = 'END';
204 4         65 while ($code =~ /^\Q$end_marker\E\s*$/m) {
205 0         0 $end_marker .= '_';
206             }
207 4         61 return "TYPEMAP: <<$end_marker\n$code\n$end_marker\n";
208             }
209              
210             # adds default typemaps for C* and C&
211             sub add_class_default_typemaps {
212 54     54 0 151 my( $name ) = @_;
213              
214 54         278 my $ptr = ExtUtils::XSpp::Node::Type->new
215             ( base => $name,
216             pointer => 1,
217             );
218 54         240 my $ref = ExtUtils::XSpp::Node::Type->new
219             ( base => $name,
220             reference => 1,
221             );
222              
223 54         397 my $xs_type = $TypemapsByName{object}->xs_type;
224              
225 54         569 add_weak_typemap_for_type
226             ( $ptr, ExtUtils::XSpp::Typemap::simple->new( type => $ptr, xs_type => $xs_type ) );
227 54         455 add_weak_typemap_for_type
228             ( $ref, ExtUtils::XSpp::Typemap::reference->new( type => $ref, xs_type => $xs_type ) );
229             }
230              
231             sub add_default_typemaps {
232             # void, integral and floating point types
233 187     187 0 617 foreach my $t ( 'char', 'short', 'int', 'long', 'bool',
234             'unsigned char', 'unsigned short', 'unsigned int',
235             'unsigned long', 'void',
236             'float', 'double', 'long double' ) {
237 2431         7383 my $type = ExtUtils::XSpp::Node::Type->new( base => $t );
238              
239 2431         7117 ExtUtils::XSpp::Typemap::add_typemap_for_type
240             ( $type, ExtUtils::XSpp::Typemap::simple->new( type => $type ) );
241             }
242              
243             # char*, const char*
244 187         780 my $char_p = ExtUtils::XSpp::Node::Type->new
245             ( base => 'char',
246             pointer => 1,
247             );
248              
249 187         595 ExtUtils::XSpp::Typemap::add_typemap_for_type
250             ( $char_p, ExtUtils::XSpp::Typemap::simple->new( type => $char_p ) );
251              
252 187         718 my $const_char_p = ExtUtils::XSpp::Node::Type->new
253             ( base => 'char',
254             pointer => 1,
255             const => 1,
256             );
257              
258 187         554 ExtUtils::XSpp::Typemap::add_typemap_for_type
259             ( $const_char_p, ExtUtils::XSpp::Typemap::simple->new( type => $const_char_p ) );
260              
261             # objects
262 187         681 my $dummy_type = ExtUtils::XSpp::Node::Type->new( base => '' );
263 187         1349 my $obj_typemap = ExtUtils::XSpp::Typemap::parsed->new(
264             name => 'object',
265             type => $dummy_type,
266             xs_input_code => $Default_input_code,
267             xs_output_code => $Default_output_code,
268             );
269              
270 187         440 ExtUtils::XSpp::Typemap::add_typemap_for_type( $dummy_type, $obj_typemap )
271             }
272              
273             sub _enable_default_xs_typemaps {
274 1     1   4 foreach my $t ( reverse @Typemaps ) {
275 16 100 100     41 if( ($t->[1]->name || '') eq 'object' ) {
276 1   50     8 $t->[1]{XS_TYPE} ||= 'O_OBJECT';
277 1         4 last;
278             }
279             }
280             }
281              
282             1;