File Coverage

blib/lib/FFI/TinyCC.pm
Criterion Covered Total %
statement 127 162 78.4
branch 24 54 44.4
condition 1 9 11.1
subroutine 32 35 91.4
pod 15 15 100.0
total 199 275 72.3


line stmt bran cond sub pod time code
1             package FFI::TinyCC;
2              
3 23     23   4826609 use strict;
  23         151  
  23         706  
4 23     23   150 use warnings;
  23         62  
  23         533  
5 23     23   495 use 5.008001;
  23         93  
6 23     23   168 use Config;
  23         53  
  23         1066  
7 23     23   14367 use FFI::Platypus;
  23         149329  
  23         793  
8 23     23   10896 use FFI::Platypus::Memory qw( malloc free );
  23         691721  
  23         1984  
9 23     23   258 use Carp qw( croak carp );
  23         54  
  23         1127  
10 23     23   158 use File::Spec;
  23         51  
  23         500  
11 23     23   10566 use File::ShareDir::Dist qw( dist_share );
  23         19856  
  23         135  
12              
13             # ABSTRACT: Tiny C Compiler for FFI
14             our $VERSION = '0.29'; # VERSION
15              
16              
17             sub _dlext ()
18             {
19 25 50   25   10171 $^O eq 'MSWin32' ? 'dll' : $Config{dlext};
20             }
21              
22             our $ffi = FFI::Platypus->new;
23             $ffi->lib(
24             File::Spec->catfile(dist_share( 'FFI-TinyCC' ), 'libtcc.' . _dlext)
25             );
26              
27             $ffi->custom_type( tcc_t => {
28             perl_to_native => sub {
29             $_[0]->{handle},
30             },
31            
32             native_to_perl => sub {
33             {
34             handle => $_[0],
35             relocate => 0,
36             error => [],
37             };
38             },
39              
40             });
41              
42             do {
43             my %output_type = qw(
44             memory 0
45             exe 1
46             dll 2
47             obj 3
48             );
49              
50             $ffi->custom_type( output_t => {
51             native_type => 'int',
52             perl_to_native => sub { $output_type{$_[0]} },
53             });
54             };
55              
56             $ffi->type('int' => 'error_t');
57             $ffi->type('(opaque,string)->void' => 'error_handler_t');
58              
59             $ffi->attach([tcc_new => '_new'] => [] => 'tcc_t');
60             $ffi->attach([tcc_delete => '_delete'] => ['tcc_t'] => 'void');
61             $ffi->attach([tcc_set_error_func => '_set_error_func'] => ['tcc_t', 'opaque', 'error_handler_t'] => 'void');
62             $ffi->attach([tcc_add_symbol => '_add_symbol'] => ['tcc_t', 'string', 'opaque'] => 'int');
63             $ffi->attach([tcc_get_symbol => '_get_symbol'] => ['tcc_t', 'string'] => 'opaque');
64             $ffi->attach([tcc_relocate => '_relocate'] => ['tcc_t', 'opaque'] => 'int');
65             $ffi->attach([tcc_run => '_run'] => ['tcc_t', 'int', 'opaque'] => 'int');
66              
67             sub _method ($;@)
68             {
69 230     230   644 my($name, @args) = @_;
70 230         1241 $ffi->attach(["tcc_$name" => "_$name"] => ['tcc_t', @args] => 'error_t');
71 230         42713 eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) .qq{
72             sub $name
73             {
74 13     13 1 4223 my \$r = _$name (\@_);
  1     1 1 6277  
  2     2 1 723  
  1     1 1 323  
  7     7 1 6279  
  20     20 1 23761  
  10     10 1 5815  
  0     0 1 0  
  9     9 1 63  
  14     14 1 2940  
75 13 50       1086 die FFI::TinyCC::Exception->new(\$_[0]) if \$r == -1;
  1 50       5  
  2 50       15  
  1 50       8  
  7 50       20  
  20 100       150  
  10 50       74  
  0 0       0  
  9 50       30  
  14 50       59  
76 13         42 \$_[0];
  1         3  
  2         7  
  1         3  
  7         15  
  19         72  
  10         36  
  0         0  
  9         18  
  14         35  
77             }
78             };
79 230 50       1148 die $@ if $@;
80             }
81              
82              
83             sub new
84             {
85 34     34 1 122275 my($class, %opt) = @_;
86              
87 34         11753 my $self = bless _new(), $class;
88            
89             $self->{error_cb} = $ffi->closure(sub {
90 1     1   3 push @{ $self->{error} }, $_[1];
  1         15  
91 34         501 });
92 34         23892 _set_error_func($self, undef, $self->{error_cb});
93            
94 34 50       6212 if($^O eq 'MSWin32')
95             {
96 0         0 require File::Basename;
97 0         0 require File::Spec;
98 0         0 my $path = File::Spec->catdir(File::Basename::dirname($ffi->lib), 'lib');
99 0         0 $self->add_library_path($path);
100             }
101            
102 34 50       146 $self->{no_free_store} = 1 if $opt{_no_free_store};
103            
104 34         151 $self;
105             }
106              
107             sub _error
108             {
109 0     0   0 my($self, $msg) = @_;
110 0         0 push @{ $self->{error} }, $msg;
  0         0  
111 0         0 $self;
112             }
113              
114             if(defined ${^GLOBAL_PHASE})
115             {
116             *DESTROY = sub
117             {
118 0     0   0 my($self) = @_;
119 0 0       0 return if ${^GLOBAL_PHASE} eq 'DESTRUCT';
120 0         0 _delete($self);
121             # TODO: should we do this?
122 0         0 free($self->{store});
123             }
124             }
125             else
126             {
127             require Devel::GlobalDestruction;
128             *DESTROY = sub
129             {
130             my($self) = @_;
131             return if Devel::GlobalDestruction::in_global_destruction();
132             _delete($self);
133             # TODO: should we do this?
134             free($self->{store});
135             }
136             }
137              
138              
139             _method set_options => qw( string );
140              
141              
142             _method add_file => qw( string );
143              
144              
145             _method compile_string => qw( string );
146              
147              
148             sub add_symbol
149             {
150 1     1 1 27153 my($self, $name, $ptr) = @_;
151 1         2 my $r;
152 1         8 $r = _add_symbol($self, $name, $ptr);
153 1 50       6 die FFI::TinyCC::Exception->new($self) if $r == -1;
154 1         3 $self;
155             }
156              
157              
158             sub detect_sysinclude_path
159             {
160 1     1 1 13 my($self) = @_;
161            
162 1         2 my @path_list;
163            
164 1 50       92 if($^O eq 'MSWin32')
    50          
    0          
165             {
166 0         0 require File::Spec;
167 0         0 push @path_list, File::Spec->catdir(dist_share('Alien-TinyCC'), 'include');
168             }
169             elsif($Config{incpth})
170             {
171 1         467 require Alien::TinyCC;
172 1         28463 require File::Spec;
173 1         5 push @path_list, File::Spec->catdir(Alien::TinyCC->libtcc_library_path, qw( tcc include ));
174 1         27 push @path_list, split /\s+/, $Config{incpth};
175             }
176             elsif($Config{ccname} eq 'gcc')
177             {
178 0         0 require File::Temp;
179 0         0 my($fh, $filename) = File::Temp::tempfile( "tryXXXX", SUFFIX => '.c', UNLINK => 1 );
180 0         0 close $fh;
181            
182 0         0 my @lines = `$Config{cpp} -v $filename 2>&1`;
183            
184 0   0     0 shift @lines while defined $lines[0] && $lines[0] !~ /^#include
185 0         0 shift @lines;
186 0   0     0 pop @lines while defined $lines[-1] && $lines[-1] !~ /^End of search /;
187 0         0 pop @lines;
188            
189 0 0       0 croak "Cannot detect sysinclude path" unless @lines;
190            
191 0         0 require Alien::TinyCC;
192 0         0 require File::Spec;
193            
194 0         0 push @path_list, File::Spec->catdir(Alien::TinyCC->libtcc_library_path, qw( tcc include ));
195 0         0 push @path_list, map { chomp; s/^ //; $_ } @lines;
  0         0  
  0         0  
  0         0  
196             }
197             else
198             {
199 0         0 croak "Cannot detect sysinclude path";
200             }
201            
202 1 50       5 croak "Cannot detect sysinclude path" unless grep { -d $_ } @path_list;
  6         92  
203            
204 1         7 $self->add_sysinclude_path($_) for @path_list;
205            
206 1         8 @path_list;
207             }
208              
209              
210              
211             _method add_include_path => qw( string );
212              
213              
214             _method add_sysinclude_path => qw( string );
215              
216              
217             _method set_lib_path => qw( string );
218              
219              
220             $ffi->attach([tcc_define_symbol=>'define_symbol'] => ['tcc_t', 'string', 'string'] => 'void');
221              
222              
223             $ffi->attach([tcc_undefine_symbol=>'undefine_symbol'] => ['tcc_t', 'string', 'string'] => 'void');
224              
225              
226             _method set_output_type => qw( output_t );
227              
228              
229             _method add_library => qw( string );
230              
231              
232             _method add_library_path => qw( string );
233              
234              
235             sub run
236             {
237 14     14 1 6733 my($self, @args) = @_;
238            
239 14 50       66 croak "unable to use run method after get_symbol" if $self->{relocate};
240            
241 14         34 my $argc = scalar @args;
242 14         39 my @c_strings = map { "$_\0" } @args;
  2         7  
243 14         73 my $ptrs = pack 'P' x $argc, @c_strings;
244 14         103 my $argv = unpack('L!', pack('P', $ptrs));
245              
246 14         69 my $r = _run($self, $argc, $argv);
247 14 50       131 die FFI::TinyCC::Exception->new($self) if $r == -1;
248 14         109 $r;
249             }
250              
251              
252             sub get_symbol
253             {
254 4     4 1 7525 my($self, $symbol_name) = @_;
255            
256 4 50       18 unless($self->{relocate})
257             {
258 4         22 my $size = _relocate($self, undef);
259 4         39 $self->{store} = malloc($size);
260 4         18 my $r = _relocate($self, $self->{store});
261 4 50       43 die FFI::TinyCC::Exception->new($self) if $r == -1;
262 4         15 $self->{relocate} = 1;
263             }
264 4         18 _get_symbol($self, $symbol_name);
265             }
266              
267              
268             _method output_file => qw( string );
269              
270             package
271             FFI::TinyCC::Exception;
272              
273             use overload '""' => sub {
274 2     2   6768 my $self = shift;
275 2 50       4 if(@{ $self->{fault} } == 2)
  2         8  
276             {
277             join(' ', $self->as_string,
278             at => $self->{fault}->[0],
279 2         7 line => $self->{fault}->[1],
280             );
281             }
282             else
283             {
284 0         0 $self->as_string . "\n";
285             }
286 23     23   40986 };
  23         61  
  23         269  
287 23     23   1801 use overload fallback => 1;
  23         56  
  23         90  
288              
289             sub new
290             {
291 1     1   4 my($class, $tcc) = @_;
292            
293 1         2 my @errors = @{ $tcc->{error} };
  1         7  
294 1         3 $tcc->{errors} = [];
295 1         2 my @stack;
296             my @fault;
297            
298 1         3 my $i=2;
299 1         9 while(my @frame = caller($i++))
300             {
301 1         3 push @stack, \@frame;
302 1 50 33     11 if(@fault == 0 && $frame[0] !~ /^FFI::TinyCC/)
303             {
304 1         7 @fault = ($frame[1], $frame[2]);
305             }
306             }
307            
308 1         8 my $self = bless {
309             errors => \@errors,
310             stack => \@stack,
311             fault => \@fault,
312             }, $class;
313            
314 1         8 $self;
315             }
316              
317 2     2   13 sub errors { shift->{errors} }
318              
319             sub as_string
320             {
321 2     2   4 my($self) = @_;
322 2         3 join "\n", @{ $self->{errors} };
  2         20  
323             }
324              
325             1;
326              
327             __END__