C-TinyCompiler

 view release on metacpan or  search on metacpan

lib/C/TinyCompiler.pm  view on Meta::CPAN


Three simple methods to inquire about the status of a package. These return
boolean values indicating whether the package (1) is currently being applied, 
(2) is currently blocked, or (3) is either being applied or blocked.

=cut

sub is_package_applied {
	my ($self, $package) = @_;
	return exists $self->{applied_package}->{$package};
}

sub is_package_blocked {
	my ($self, $package) = @_;
	return exists $self->{blocked_package}->{$package};
}

sub is_package_known {
	my ($self, $package) = @_;
	return ($self->is_package_applied($package)
		or $self->is_package_blocked($package));
}

=head2 block_package

Blocks the given package and removes its args from the applied package list if
it was previously applied.

=cut

sub block_package {
	my ($self, $package) = @_;
	delete $self->{applied_package}->{$package};
	$self->{blocked_package}->{$package} = 1;
}

=head2 get_package_args

Returns the array ref containing the package arguments that were supplied when
the package was applied (or an empty array ref if the package was never applied
or has subsequently been blocked). This is the actual array reference, so any
manipulations to this array reference will effect the reference returned in
future calls to C<get_package_args>.

=cut

sub get_package_args {
	my ($self, $package) = shift;
	return $self->{applied_package}->{$package} || [];
}

=head1 COMPILE METHODS

These are methods related to compiling your source code. Apart from C<compile>,
you need not worry about these methods unless you are trying to create a C::TinyCompiler
package.

=head2 compile

Concatenates the text of the three code sections, jit-compiles them, applies all
symbols from the included packages, and relocates the code so that symbols can
be retrieved. In short, this is the transformative step that converts your code
from ascii into machine.

This step does far more than simply invoke libtcc's compile function. At the
time of writing, tcc only supports a single uncompiled compiler state at a time.
To properly handle this, C::TinyCompiler defers creating the actuall TCCState
object as long as possible. Calling the C<compile> method on your compiler
context actually performs these steps:

=over

=item 1. Create TCCState

An actual TCCState struct is created, to which the following operations are
applied.

=item 2. Apply preprocessor definitions, paths, libraries

All preprocessor defintions, include paths, library paths, and libraries are
added to the compiler state.

=item 3. Invoke preprocessing methods of all C::TinyCompiler packages

Packages can perform preprocessing on the compiler context (and in particular,
the code strings) just before the actual compilation step. This allows them to
dynmically add or remove elements to your code, like source-filters. Or they
could hold off to perform other changes to the compiler context until just
before the compilation step, although this is generally not needed.

=item 4. Code assembly and compilation

The code is assembled and compiled.

=item 5. Apply symbols and relocate the machine code

Symbols (such as dynamically loaded functions) are applied, the final machine
code is relocated, and the memory pages holding that code are marked as
executable.

=back

This means that nearly all of the interaction with libtcc itself is deferred
until you call this function. As each of those interactions could encounter
trouble, this function may croak for many reasons.

=over

=item This context has already been compiled

You are only allowed to compile a context once.

=item Error defining processor symbol <name>: <message>

tcc encountered trouble while trying to define the given preprocessor symbol.
Duplicate preprocessor symbols should not occurr at this stage, so this error
likely means that your definition is malformed.

=item Error adding include path(s): <message>
=item Error adding library path(s): <message>

An include path, sysinclude path, or library path gave trouble. The tcc source
code has no code path that should issue this error, so this should never happen.
If it does, either you really messed something up, or there's a bug in this
module. :-)

=item Error adding library(s): <message>

tcc encountered trouble adding one or more of your specified libraries. Hopefully
the message explains the trouble well enough.

=item Unable to compile ...

If your code has a syntax error or some other issue, you will get this message.
If the reported line numbers do not help, consider using L</line_numbers> to
improve line number reporting.

=item Error adding symbol(s): <message>

If you specify symbols that have already been defined elsewhere, the compiler
will thwart your attempts with this message. Make sure that you have not defined
a like-named symbol already. In particular, be sure not to define a symbol that
was defined already by one of your packages.

=item Unable to relocate: <message>

The last step in converting your C code to machine-executable code is relocating
the bytecode. This could fail, though I do not understand compilers well enough
to explain why. If I had to guess, I would say you probably ran out of memory.
(Sorry I cannot provide more insight into how to fix this sort of problem.
Feedback for a better explanation would be much appreciated. :-)

=back

=cut

sub compile {
	my $self = shift;
	
	# Make sure we haven't already compiled with this context:
	croak('This context has already been compiled') if $self->has_compiled;
	
	# Create the actual TCCState object:
	$self->_create_state;
	
	# Apply the #defines and add the #include paths
	my %defs = %{$self->{pp_defs}};
	while (my ($name, $value) = each %defs) {
		$self->_define($name, $value);
		$self->report_if_trouble("defining preprocessor symbol [$name]: MESSAGE");
	}
	$self->_add_include_paths(@{$self->{include_paths}});
	$self->_add_sysinclude_paths(@{$self->{sysinclude_paths}});
	$self->report_if_trouble("adding include path(s): MESSAGE");
	
	# Add the library stuff:
	$self->_add_library_paths(@{$self->{library_paths}});
	$self->report_if_trouble("adding library path(s): MESSAGE");
	$self->_add_libraries(@{$self->{libraries}});
	$self->report_if_trouble("adding library(s): MESSAGE");
	
	# Allow packages to perform any preprocessing they may want:
	while (my ($package, $options) = each %{$self->{applied_package}}) {
		$package->preprocess($self, @$options);
	}
	
	# Assemble the code (with primitive section indicators) and compile!
	eval {
		my $code = '';
		for my $section (qw(Head Body Foot)) {
			$code .= "#line 1 \"$section\"\n" . $self->{$section};
		}
		$self->_compile($code);
		1;
	} or do {
		# We ran into a problem! This exception will only get tripped if
		# libtcc's compile function returned nonzero, which means there was
		# an error. Warnings do not cause _compile to throw exceptions. So,
		# report the compiler issue as reported from the compiled line:
		my $message = $self->get_error_message;
		if ($message) {
			# Fix the rather terse line number notation:
			$message =~ s/:(\d+:)/ line $1/g;
			# Change "In file included..." to "in file included..."
			$message =~ s/^I/i/;
			# Remove "error" in "... 13: error: ..."
			$message =~ s/: error:/:/;
			# Finally, die:
			die "Unable to compile $message\n";
		}
		
		# Otherwise report an unknown compiler issue, indicating the line in the
		# Perl script that called for the compile action:
		croak("C::TinyCompiler weird internal error: Unable to compile for unknown reasons");
	};
	# Report any warnings
	$self->report_if_trouble('compiling: MESSAGE');
	
	# Apply the pre-compiled symbols (function pointers, etc):
	while (my ($package, $options) = each %{$self->{applied_package}}) {
		$package->apply_symbols($self, @$options);
	}
	# Apply any other symbols that were added:
	$self->_add_symbols(%{$self->{symbols}});
	$self->report_if_trouble("adding symbol(s): MESSAGE");

	# Relocate
	eval {
		$self->_relocate;
		1;
	} or do {
		# We ran into a problem! Report the relocation issue, if known:
		$self->report_if_trouble("relocating: MESSAGE");
		# Report an unknown relocation issue if not known:
		croak("C::TinyCompiler weird internal error: Unable to relocate for unknown reasons");
	};
	
	# Mark the compiler as post-compile
	$self->{has_compiled} = 1;
}

=head2 add_symbols

Adds symbols to a compiler context. This function expects the symbols as

 symbol_name => pointer

pairs. By I<symbol>, I mean any C thing that you want to give a name in your
compiler context. That is, you can add a function to your compiler context that
was compiled elsewhere, or tell the compiler context the location of some
variable that you wish it to access as a global variable.

This function requires that you send a true C pointer that points to your
symbol. This only makes sense if you have a way to get C pointers to your
symbols. This would be the case if you have compiled code with a separate C::TinyCompiler
context (in which case you would use L</get_symbols> to retrieve that pointer),
or if you have XS code that can retrieve a pointer to a function or global
variable for you.

working here - add examples, and make sure we can have two compiler contexts at
the same time.

For example, the input should look like this:

 $context->add_symbols( func1 => $f_pointer, max_N => $N_pointer);

If you fail to provide key/value pairs, this function will croak saying

 You must supply key => value pairs to add_symbols

=cut

sub add_symbols {
	my $self = shift;
	
	# working here - not sure if it's safe to add symbols after relocation.
	
	croak('You must supply key => value pairs to add_symbols')
		unless @_ % 2 == 0;
	
	my %symbols = @_;
	while (my ($symbol, $pointer) = each %symbols) {
		# Track the symbols, warning on redefinitions
		warnings::warnif("Redefining $symbol")
			if exists $self->{symbols}->{$symbol};
		$self->{symbols}->{$symbol} = $pointer;
	}
}

=head1 POST-COMPILE METHODS

These are methods you can call on your context after you have compiled the
associated code.

=head2 get_symbols

Retrieves the pointers to a given list of symbols and returns a key/value list
of pairs as

 symbol_name => pointer

=cut

sub get_symbols {
	croak('Cannot retrieve symbols before compiling') unless $_[0]->has_compiled;
	goto &_get_symbols;
}

=head2 get_symbol

Like L</get_symbols>, but only expects a single symbol name and only returns the
pointer (rather than the symbol name/pointer pair). For example,

 $context->code('Body') .= q{
     void my_func() {
         printf("Hello!\n");
     }
 };
 $context->compile;
 my $func_pointer = $context->get_symbol('my_func');

=cut

sub get_symbol {
	my ($self, $symbol_name) = @_;
	croak("Cannot retrieve symbol $symbol_name before compiling")
		unless $self->has_compiled;
	my (undef, $to_return) = $self->get_symbols($symbol_name);
	return $to_return;
}

=head2 call_void_function

Takes the name of a compiled function and calls it without passing any
arguments. In other words, this assumes that your function has the following
definition:



( run in 0.453 second using v1.01-cache-2.11-cpan-71847e10f99 )