File Coverage

blib/lib/Inline/Struct.pm
Criterion Covered Total %
statement 113 131 86.2
branch 30 46 65.2
condition n/a
subroutine 7 8 87.5
pod 0 4 0.0
total 150 189 79.3


line stmt bran cond sub pod time code
1             package Inline::Struct;
2              
3 15     15   1202056 use strict;
  15         62  
  15         491  
4 15     15   85 use warnings;
  15         37  
  15         446  
5 15     15   118 use Carp;
  15         31  
  15         1225  
6             require Inline;
7             require Inline::Struct::grammar;
8 15     15   89 use Data::Dumper;
  15         31  
  15         29180  
9              
10             our $VERSION = '0.28';
11              
12             #=============================================================================
13             # Inline::Struct is NOT an ILSM: no register() function
14             #=============================================================================
15              
16             #=============================================================================
17             # parse -- gets all C/C++ struct definitions and binds them to Perl
18             #=============================================================================
19             sub parse {
20 15     15 0 2491227 require Parse::RecDescent;
21 15         53 my $o = shift;
22 15 50       136 return if $o->{STRUCT}{'.parser'};
23 15 50       89 return unless $o->{STRUCT}{'.any'};
24              
25             # Figure out whether to grab all structs
26 68         279 my $nstructs = scalar grep { $_ =~ /^[_a-z][_0-9a-z]*$/i }
27 15         43 keys %{$o->{STRUCT}};
  15         109  
28 15 100       97 $o->{STRUCT}{'.all'} = 1
29             if $nstructs == 0;
30              
31             # Load currently-defined types (stored in $o->{ILSM}{typeconv})
32 15         188 $o->get_maps;
33 15         2544 $o->get_types;
34              
35             # Parse structs
36 15         219945 $::RD_HINT++;
37 15 50       150 my $grammar = Inline::Struct::grammar::grammar()
38             or croak "Can't find Struct grammar!\n";
39 15         172 my $parser = $o->{STRUCT}{'.parser'} = Parse::RecDescent->new($grammar);
40 15         1477522 $parser->{data}{typeconv} = $o->{ILSM}{typeconv};
41 15         320 $parser->code($o->{ILSM}{code});
42 15         84005 $o->{ILSM}{typeconv} = $parser->{data}{typeconv};
43              
44 15         71 $o->{STRUCT}{'.xs'} = "";
45 15         122 $o->{STRUCT}{'.macros'} = <
46             #define NEW_INLINE_STRUCT(_IS_targ,_IS_type) INLINE_STRUCT_NEW_##_IS_type(_IS_targ)
47             #define INLINE_STRUCT_FIELDS(_IS_type) INLINE_STRUCT_FIELDS_##_IS_type
48             #define INLINE_STRUCT_INIT_LIST(_IS_targ,_IS_type) INLINE_STRUCT_INITL_##_IS_type(_IS_targ)
49             #define INLINE_STRUCT_ARRAY(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_ARRAY_##_IS_type(_IS_src,_IS_targ)
50             #define INLINE_STRUCT_VALUES(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_ARRAY_##_IS_type(_IS_src,_IS_targ)
51             #define INLINE_STRUCT_HASH(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_HASH_##_IS_type(_IS_src,_IS_targ)
52             #define INLINE_STRUCT_KEYS(_IS_src,_IS_targ,_IS_type) INLINE_STRUCT_KEYS_##_IS_type(_IS_src,_IS_targ)
53             END
54              
55 15         32 my @struct_list;
56 15 100       77 if ($o->{STRUCT}{'.all'}) {
57 10 50       55 die "No valid structs found" unless $parser->{data}{structs};
58 10         23 @struct_list = @{$parser->{data}{structs}};
  10         41  
59             }
60             else {
61 33         117 @struct_list = grep { $_ =~ /^[_a-z][_a-z0-9]*$/i }
62 5         16 keys %{$o->{STRUCT}}
  5         31  
63             }
64 15         71 for my $struct (@struct_list) {
65 19 50       89 unless (defined $parser->{data}{struct}{$struct}) {
66 0 0       0 warn "Struct $struct requested but not found" if $^W;
67 0         0 next;
68             }
69 19         72 $o->{STRUCT}{'.bound'}{$struct}++;
70 19         84 my $cname = $parser->{data}{struct}{$struct}{cname};
71 19         53 my ($NEW, $FIELDS, $INITL, $HASH, $ARRAY, $KEYS);
72              
73             # Set up the initial part of the macros
74 19         131 $NEW = <
75             #define INLINE_STRUCT_NEW_${struct}(_IS_targ) { \\
76             HV *hv = get_hv("Inline::Struct::${struct}::_map_", 1); \\
77             HV *entry = newHV(); \\
78             SV *entrv = (SV*)newRV((SV*)entry); \\
79             SV *lookup; \\
80             char *key; \\
81             STRLEN klen; \\
82             ENTER; \\
83             SAVETMPS; \\
84             Newz(1564,_IS_targ,1,$cname); \\
85             lookup = newSViv((IV)_IS_targ); \\
86             key = SvPV(lookup, klen); \\
87             sv_2mortal(lookup); \\
88             hv_store(entry, "REFCNT", 6, newSViv(0), 0); \\
89             hv_store(entry, "FREE", 4, newSViv(1), 0); \\
90             hv_store(hv, key, klen, entrv, 0); \\
91             FREETMPS; \\
92             LEAVE; \\
93             }
94             END
95             $FIELDS = "#define INLINE_STRUCT_FIELDS_$struct " .
96 19         64 (scalar @{$parser->{data}{struct}{$struct}{fields}}) . "\n";
  19         129  
97 19         66 $INITL = "#define INLINE_STRUCT_INITL_$struct(_IS_targ) {\\\n";
98 19         60 $HASH = <
99             #define INLINE_STRUCT_HASH_$struct(_IS_src,_IS_targ) \\
100             hv_clear(_IS_targ); \\
101             END
102 19         54 $ARRAY = <
103             #define INLINE_STRUCT_ARRAY_$struct(_IS_src,_IS_targ) \\
104             av_clear(_IS_targ); \\
105             END
106              
107 19         53 $KEYS = <
108             #define INLINE_STRUCT_KEYS_$struct(_IS_src,_IS_targ) \\
109             av_clear(_IS_targ); \\
110             END
111              
112 19         36 my $maxi = scalar @{$parser->{data}{struct}{$struct}{fields}};
  19         53  
113 19 50       86 next unless $maxi > 0;
114              
115 19         262 $o->{STRUCT}{'.xs'} .= <
116              
117             MODULE = $o->{API}{module} PACKAGE = Inline::Struct::$struct
118              
119             PROTOTYPES: DISABLE
120              
121             $cname *
122             new(klass, ...)
123             char *klass
124             PREINIT:
125             int _items = items - 1;
126             CODE:
127             NEW_INLINE_STRUCT(RETVAL,$struct);
128             if (_items == 0) { }
129             else {
130             INLINE_STRUCT_INIT_LIST(RETVAL,$struct);
131             }
132             OUTPUT:
133             RETVAL
134              
135             void
136             DESTROY(object)
137             $cname *object
138             PREINIT:
139             HV *map = get_hv("Inline::Struct::${struct}::_map_", 1);
140             SV *lookup;
141             STRLEN klen;
142             char *key;
143             CODE:
144             ENTER;
145             SAVETMPS;
146             lookup = newSViv((IV)object);
147             key = SvPV(lookup, klen);
148             sv_2mortal(lookup);
149             if (hv_exists(map, key, klen)) {
150             HV *info = (HV*)SvRV(*hv_fetch(map, key, klen, 0));
151             SV *refcnt = *hv_fetch(info, "REFCNT", 6, 0);
152             int tofree = SvIV(*hv_fetch(info, "FREE", 4, 0));
153             if (tofree && SvIV(refcnt) == 1) {
154             Safefree(object);
155             hv_delete(map, key, klen, 0);
156             }
157             else
158             sv_dec(refcnt);
159             }
160             FREETMPS;
161             LEAVE;
162              
163             HV *
164             _HASH(object)
165             $cname *object
166             CODE:
167             RETVAL = newHV();
168             INLINE_STRUCT_HASH(object, RETVAL, $struct);
169             OUTPUT:
170             RETVAL
171              
172             AV *
173             _VALUES(object)
174             $cname *object
175             CODE:
176             RETVAL = newAV();
177             INLINE_STRUCT_VALUES(object, RETVAL, $struct);
178             OUTPUT:
179             RETVAL
180              
181             AV *
182             _ARRAY(object)
183             $cname *object
184             CODE:
185             RETVAL = newAV();
186             INLINE_STRUCT_ARRAY(object, RETVAL, $struct);
187             OUTPUT:
188             RETVAL
189              
190             AV *
191             _KEYS(object)
192             $cname *object
193             CODE:
194             RETVAL = newAV();
195             INLINE_STRUCT_KEYS(object, RETVAL, $struct);
196             OUTPUT:
197             RETVAL
198              
199             END
200              
201 19         40 my $i=1;
202 19         37 for my $field (@{$parser->{data}{struct}{$struct}{fields}}) {
  19         66  
203 41         97 my $flen = length $field;
204 41         128 my $type = $parser->{data}{struct}{$struct}{field}{$field};
205 41 100       126 my $q = ($i == 1 ? 'if' : 'else if');
206 41         163 my $t =
207             typeconv($o, "_IS_targ->$field",
208             "val",
209             $type,
210             "input_expr",
211             1,
212             '_KEYS',
213             );
214 41         154 my $s =
215             typeconv($o, "_IS_src->$field",
216             "tmp",
217             $type,
218             "output_expr",
219             1,
220             '_KEYS',
221             );
222 41         211 $INITL .=
223             (typeconv($o, "_IS_targ->$field",
224             "ST($i)",
225             $type,
226             "input_expr",
227             1,
228             '_KEYS',
229             ) .
230             "; \\\n");
231 41 100       312 $HASH .= (qq{{\\\n\tSV*tmp=newSViv(0);\\\n$s \\
232             \thv_store(_IS_targ, "$field", $flen, tmp, 0); \\\n}} .
233             ($i == $maxi ? "" : "\\") .
234             "\n"
235             );
236 41 100       170 $ARRAY .= (qq{{\\\n\tSV*tmp=newSViv(0);\\\n$s \\
237             \tav_push(_IS_targ, tmp); \\\n}} .
238             ($i == $maxi ? "" : "\\") .
239             "\n"
240             );
241 41 100       154 $KEYS .= (qq{av_push(_IS_targ, newSVpv("$field", 0));} .
242             ($i == $maxi ? "" : "\\") .
243             "\n"
244             );
245 41         108 my $is_sv = $type =~ /^SV\s*\*$/;
246 41         141 $o->{STRUCT}{'.xs'} .= <
247             void
248             $field(object, ...)
249             $cname *object
250             PREINIT:
251             SV *retval = newSViv(0);
252             int mortalise_retval = 0;
253             PPCODE:
254             ENTER;
255             SAVETMPS;
256             if (items == 1) {
257 41         129 @{[typeconv($o, "object->$field", "retval", $type, "output_expr", undef, $field)]}
258             @{[
259             # mortalise if not an SV *
260 41 100       177 $is_sv ? '' : 'mortalise_retval = 1;'
261             ]}
262             }
263             else {
264             @{[
265 41 100       155 $is_sv ?
266             qq{if (object->$field && SvOK(object->$field)) {
267             SvREFCNT_dec(object->$field);
268             }} : ""
269             ]}
270 41         136 @{[typeconv($o, "object->$field", "ST(1)", $type, "input_expr", undef, $field)]};
271             @{[
272 41 100       186 $is_sv ?
273             qq{if (object->$field && SvOK(object->$field)) {
274             SvREFCNT_inc(object->$field);
275             }} : ""
276             ]}
277 41         141 @{[typeconv($o, "object", "retval", "$cname *", "output_expr", undef, $field)]};
278             mortalise_retval = 1;
279             }
280             FREETMPS;
281             LEAVE;
282             if (mortalise_retval) sv_2mortal(retval);
283             XPUSHs(retval);
284              
285             EOF
286 41         202 $i++;
287             }
288 19         53 $INITL .= "}\n";
289              
290 19         178 $o->{STRUCT}{'.macros'} .= <
291             $NEW
292             $FIELDS
293             $INITL
294             $HASH
295             $ARRAY
296             $KEYS
297             END
298              
299             }
300              
301             # Write a typemap file containing typemaps for each thingy
302 15         86 write_typemap($o);
303             }
304              
305             sub write_typemap {
306 15     15 0 38 my $o = shift;
307 15         47 my $data = $o->{STRUCT}{'.parser'}{data};
308              
309 15         110 my ($TYPEMAP, $INPUT, $OUTPUT);
310 15         53 for my $struct (@{$data->{structs}}) {
  15         56  
311 19         60 my $type = "O_OBJECT_$struct";
312 1011         1875 my @ctypes = grep { $data->{typeconv}{type_kind}{$_} eq $type }
313 19         76 keys %{$data->{typeconv}{type_kind}};
  19         245  
314 19         130 $TYPEMAP .= join "", map { "$_\t\t$type\n" } @ctypes;
  26         118  
315 19         94 $INPUT .= $type."\n".$data->{typeconv}{input_expr}{$type};
316 19         107 $OUTPUT .= $type."\n".$data->{typeconv}{output_expr}{$type};
317             }
318              
319             $o->mkpath($o->{API}{build_dir})
320 15 50       906 unless -d $o->{API}{build_dir};
321 15         51 my $fh;
322 15         80 my $fname = $o->{API}{build_dir}.'/Struct.map';
323 15 50       2610 open $fh, ">$fname"
324             or die $!;
325 15         235 print $fh <
326             TYPEMAP
327             $TYPEMAP
328             INPUT
329             $INPUT
330              
331             OUTPUT
332             $OUTPUT
333             END
334              
335 15         1385 close $fh;
336 15         177 $o->validate( TYPEMAPS => $fname );
337             }
338              
339             sub typeconv {
340 246     246 0 393 my $o = shift;
341 246         357 my $var = shift;
342 246         344 my $arg = shift;
343 246         334 my $type = shift;
344 246         328 my $dir = shift;
345 246         325 my $preproc = shift;
346 246         336 my $pname = shift;
347 246         566 my $tkind = $o->{ILSM}{typeconv}{type_kind}{$type};
348 246 50       535 die "Error: unknown type '$type'" if !$tkind;
349 246         637 my $compile = qq{qq{$o->{ILSM}{typeconv}{$dir}{$tkind}}};
350 246         13404 my $ret = eval $compile;
351 246 50       956 die "Error while compiling: >>>$compile<<<\n$@" if $@;
352 246         456 chomp $ret;
353 246 100       546 $ret =~ s/\n/\\\n/g if $preproc;
354 246         1015 return $ret;
355             }
356              
357             #=============================================================================
358             # Return a little info about the structs we bound to.
359             #=============================================================================
360             sub info {
361 0     0 0   my $o = shift;
362 0           my $info = "";
363 0 0         parse($o) unless defined $o->{STRUCT}{'.parser'};
364 0           my $data = $o->{STRUCT}{'.parser'}{data};
365 0 0         if (defined $o->{STRUCT}{'.bound'}) {
366 0           $info .= "The following structs have been bound to Perl:\n";
367 0           for my $struct (keys %{$o->{STRUCT}{'.bound'}}) {
  0            
368 0           $info .= "\tstruct $struct {\n";
369 0           for my $field (@{$data->{struct}{$struct}{fields}}) {
  0            
370 0           my $type = $data->{struct}{$struct}{field}{$field};
371 0           $info .= "\t\t$type $field;\n";
372             }
373 0           $info .= "\t};\n";
374             }
375             }
376             else {
377 0           $info .= "No structs were bound to Perl.\n";
378             }
379 0           return $info;
380             }
381              
382             1;
383              
384             __END__