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   3350283 use strict;
  23         119  
  23         450  
4 23     23   91 use warnings;
  23         36  
  23         377  
5 23     23   363 use 5.008001;
  23         55  
6 23     23   91 use Config;
  23         28  
  23         821  
7 23     23   11011 use FFI::Platypus;
  23         88359  
  23         596  
8 23     23   7954 use FFI::Platypus::Memory qw( malloc free );
  23         603864  
  23         1384  
9 23     23   171 use Carp qw( croak carp );
  23         32  
  23         775  
10 23     23   103 use File::Spec;
  23         63  
  23         368  
11 23     23   7862 use File::ShareDir::Dist qw( dist_share );
  23         12210  
  23         95  
12              
13             # ABSTRACT: Tiny C Compiler for FFI
14             our $VERSION = '0.28'; # VERSION
15              
16              
17             sub _dlext ()
18             {
19 25 50   25   7270 $^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   424 my($name, @args) = @_;
70 230         857 $ffi->attach(["tcc_$name" => "_$name"] => ['tcc_t', @args] => 'error_t');
71 230         26302 eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) .qq{
72             sub $name
73             {
74 13     13 1 3093 my \$r = _$name (\@_);
  1     1 1 4133  
  2     2 1 536  
  1     1 1 274  
  7     7 1 4322  
  20     20 1 15512  
  10     10 1 4266  
  0     0 1 0  
  9     9 1 46  
  14     14 1 2161  
75 13 50       954 die FFI::TinyCC::Exception->new(\$_[0]) if \$r == -1;
  1 50       4  
  2 50       12  
  1 50       9  
  7 50       43  
  20 100       102  
  10 50       58  
  0 0       0  
  9 50       17  
  14 50       43  
76 13         32 \$_[0];
  1         3  
  2         5  
  1         2  
  7         15  
  19         46  
  10         28  
  0         0  
  9         18  
  14         26  
77             }
78             };
79 230 50       744 die $@ if $@;
80             }
81              
82              
83             sub new
84             {
85 34     34 1 86538 my($class, %opt) = @_;
86              
87 34         8881 my $self = bless _new(), $class;
88            
89             $self->{error_cb} = $ffi->closure(sub {
90 1     1   2 push @{ $self->{error} }, $_[1];
  1         10  
91 34         363 });
92 34         766 _set_error_func($self, undef, $self->{error_cb});
93            
94 34 50       5079 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       104 $self->{no_free_store} = 1 if $opt{_no_free_store};
103            
104 34         83 $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 19881 my($self, $name, $ptr) = @_;
151 1         2 my $r;
152 1         4 $r = _add_symbol($self, $name, $ptr);
153 1 50       4 die FFI::TinyCC::Exception->new($self) if $r == -1;
154 1         2 $self;
155             }
156              
157              
158             sub detect_sysinclude_path
159             {
160 1     1 1 6 my($self) = @_;
161            
162 1         1 my @path_list;
163            
164 1 50       50 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         382 require Alien::TinyCC;
172 1         3269 require File::Spec;
173 1         4 push @path_list, File::Spec->catdir(Alien::TinyCC->libtcc_library_path, qw( tcc include ));
174 1         19 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       6 croak "Cannot detect sysinclude path" unless grep { -d $_ } @path_list;
  6         86  
203            
204 1         5 $self->add_sysinclude_path($_) for @path_list;
205            
206 1         5 @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 4636 my($self, @args) = @_;
238            
239 14 50       51 croak "unable to use run method after get_symbol" if $self->{relocate};
240            
241 14         23 my $argc = scalar @args;
242 14         32 my @c_strings = map { "$_\0" } @args;
  2         5  
243 14         63 my $ptrs = pack 'P' x $argc, @c_strings;
244 14         93 my $argv = unpack('L!', pack('P', $ptrs));
245              
246 14         49 my $r = _run($self, $argc, $argv);
247 14 50       112 die FFI::TinyCC::Exception->new($self) if $r == -1;
248 14         89 $r;
249             }
250              
251              
252             sub get_symbol
253             {
254 4     4 1 4964 my($self, $symbol_name) = @_;
255            
256 4 50       15 unless($self->{relocate})
257             {
258 4         16 my $size = _relocate($self, undef);
259 4         29 $self->{store} = malloc($size);
260 4         14 my $r = _relocate($self, $self->{store});
261 4 50       28 die FFI::TinyCC::Exception->new($self) if $r == -1;
262 4         10 $self->{relocate} = 1;
263             }
264 4         14 _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 5     5   4182 my $self = shift;
275 5 50       5 if(@{ $self->{fault} } == 2)
  5         9  
276             {
277             join(' ', $self->as_string,
278             at => $self->{fault}->[0],
279 5         8 line => $self->{fault}->[1],
280             );
281             }
282             else
283             {
284 0         0 $self->as_string . "\n";
285             }
286 23     23   30140 };
  23         43  
  23         199  
287 23     23   1300 use overload fallback => 1;
  23         62  
  23         89  
288              
289             sub new
290             {
291 1     1   3 my($class, $tcc) = @_;
292            
293 1         1 my @errors = @{ $tcc->{error} };
  1         3  
294 1         3 $tcc->{errors} = [];
295 1         1 my @stack;
296             my @fault;
297            
298 1         2 my $i=2;
299 1         6 while(my @frame = caller($i++))
300             {
301 1         2 push @stack, \@frame;
302 1 50 33     8 if(@fault == 0 && $frame[0] !~ /^FFI::TinyCC/)
303             {
304 1         4 @fault = ($frame[1], $frame[2]);
305             }
306             }
307            
308 1         3 my $self = bless {
309             errors => \@errors,
310             stack => \@stack,
311             fault => \@fault,
312             }, $class;
313            
314 1         5 $self;
315             }
316              
317 2     2   9 sub errors { shift->{errors} }
318              
319             sub as_string
320             {
321 5     5   6 my($self) = @_;
322 5         6 join "\n", @{ $self->{errors} };
  5         19  
323             }
324              
325             1;
326              
327             __END__