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   5399315 use strict;
  23         155  
  23         697  
4 23     23   122 use warnings;
  23         44  
  23         608  
5 23     23   526 use 5.008001;
  23         79  
6 23     23   114 use Config;
  23         77  
  23         1041  
7 23     23   15629 use FFI::Platypus;
  23         168814  
  23         798  
8 23     23   11993 use FFI::Platypus::Memory qw( malloc free );
  23         293210  
  23         2065  
9 23     23   214 use Carp qw( croak carp );
  23         50  
  23         1079  
10 23     23   129 use File::Spec;
  23         62  
  23         558  
11 23     23   11159 use File::ShareDir::Dist qw( dist_share );
  23         20518  
  23         113  
12              
13             # ABSTRACT: Tiny C Compiler for FFI
14             our $VERSION = '0.30'; # VERSION
15              
16              
17             sub _dlext ()
18             {
19 25 50   25   10239 $^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   625 my($name, @args) = @_;
70 230         1291 $ffi->attach(["tcc_$name" => "_$name"] => ['tcc_t', @args] => 'error_t');
71 230         54023 eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) .qq{
72             sub $name
73             {
74 13     13 1 4714 my \$r = _$name (\@_);
  1     1 1 6576  
  2     2 1 838  
  1     1 1 420  
  7     7 1 6389  
  20     20 1 22281  
  10     10 1 6174  
  0     0 1 0  
  9     9 1 63  
  14     14 1 3199  
75 13 50       1290 die FFI::TinyCC::Exception->new(\$_[0]) if \$r == -1;
  1 50       6  
  2 50       16  
  1 50       12  
  7 50       21  
  20 100       133  
  10 50       72  
  0 0       0  
  9 50       32  
  14 50       54  
76 13         47 \$_[0];
  1         3  
  2         8  
  1         2  
  7         16  
  19         66  
  10         39  
  0         0  
  9         22  
  14         41  
77             }
78             };
79 230 50       1242 die $@ if $@;
80             }
81              
82              
83             sub new
84             {
85 34     34 1 139997 my($class, %opt) = @_;
86              
87 34         12814 my $self = bless _new(), $class;
88            
89             $self->{error_cb} = $ffi->closure(sub {
90 1     1   4 push @{ $self->{error} }, $_[1];
  1         15  
91 34         552 });
92 34         25477 _set_error_func($self, undef, $self->{error_cb});
93            
94 34 50       6231 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       139 $self->{no_free_store} = 1 if $opt{_no_free_store};
103            
104 34         124 $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 2120 my($self, $name, $ptr) = @_;
151 1         2 my $r;
152 1         6 $r = _add_symbol($self, $name, $ptr);
153 1 50       5 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 9 my($self) = @_;
161            
162 1         2 my @path_list;
163            
164 1 50       78 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         2439 require Alien::TinyCC;
172 1         30836 require File::Spec;
173 1         6 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{cpprun} -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       7 croak "Cannot detect sysinclude path" unless grep { -d $_ } @path_list;
  6         105  
203            
204 1         10 $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 6698 my($self, @args) = @_;
238            
239 14 50       69 croak "unable to use run method after get_symbol" if $self->{relocate};
240            
241 14         33 my $argc = scalar @args;
242 14         41 my @c_strings = map { "$_\0" } @args;
  2         7  
243 14         84 my $ptrs = pack 'P' x $argc, @c_strings;
244 14         106 my $argv = unpack('L!', pack('P', $ptrs));
245              
246 14         83 my $r = _run($self, $argc, $argv);
247 14 50       137 die FFI::TinyCC::Exception->new($self) if $r == -1;
248 14         108 $r;
249             }
250              
251              
252             sub get_symbol
253             {
254 4     4 1 10266 my($self, $symbol_name) = @_;
255            
256 4 50       21 unless($self->{relocate})
257             {
258 4         25 my $size = _relocate($self, undef);
259 4         39 $self->{store} = malloc($size);
260 4         20 my $r = _relocate($self, $self->{store});
261 4 50       39 die FFI::TinyCC::Exception->new($self) if $r == -1;
262 4         15 $self->{relocate} = 1;
263             }
264 4         25 _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   7013 my $self = shift;
275 2 50       5 if(@{ $self->{fault} } == 2)
  2         8  
276             {
277             join(' ', $self->as_string,
278             at => $self->{fault}->[0],
279 2         6 line => $self->{fault}->[1],
280             );
281             }
282             else
283             {
284 0         0 $self->as_string . "\n";
285             }
286 23     23   42924 };
  23         92  
  23         244  
287 23     23   1892 use overload fallback => 1;
  23         69  
  23         117  
288              
289             sub new
290             {
291 1     1   3 my($class, $tcc) = @_;
292            
293 1         3 my @errors = @{ $tcc->{error} };
  1         4  
294 1         4 $tcc->{errors} = [];
295 1         2 my @stack;
296             my @fault;
297            
298 1         2 my $i=2;
299 1         10 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         6 @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   12 sub errors { shift->{errors} }
318              
319             sub as_string
320             {
321 2     2   5 my($self) = @_;
322 2         4 join "\n", @{ $self->{errors} };
  2         21  
323             }
324              
325             1;
326              
327             __END__