NAME
XS::JIT::Builder - interface for building XS/C code strings
SYNOPSIS
use XS::JIT;
use XS::JIT::Builder;
use File::Temp qw(tempdir);
my $cache_dir = tempdir(CLEANUP => 1);
my $b = XS::JIT::Builder->new;
# Build a simple accessor
$b->xs_function('get_name')
->xs_preamble
->get_self_hv
->hv_fetch('hv', 'name', 4, 'val')
->if('val != NULL')
->return_sv('*val')
->endif
->return_undef
->xs_end;
# Compile and use
XS::JIT->compile(
code => $b->code,
name => 'MyClass',
cache_dir => $cache_dir,
functions => {
'MyClass::name' => { source => 'get_name', is_xs_native => 1 },
},
);
my $obj = bless { name => 'Alice' }, 'MyClass';
print $obj->name; # prints "Alice"
DESCRIPTION
XS::JIT::Builder provides a interface for generating C code dynamically. It handles indentation automatically and provides convenient methods for common XS patterns.
All methods return $self to enable method chaining. The generated code can be retrieved with code() and passed to XS::JIT->compile().
METHODS
Lifecycle
- new(%options)
-
Create a new builder.
my $b = XS::JIT::Builder->new; my $b = XS::JIT::Builder->new(use_tabs => 1); my $b = XS::JIT::Builder->new(indent_width => 2);Options:
- use_tabs
-
Use tabs for indentation instead of spaces (default: false).
- indent_width
-
Number of spaces per indentation level when not using tabs (default: 4).
- code()
-
Return the accumulated C code as a string.
my $c_code = $b->code; - reset()
-
Clear the builder and reset state for reuse.
$b->reset; # Builder is now empty, can build new code
Low-level Output
- line($text)
-
Add a line with current indentation.
$b->line('int x = 42;'); - raw($text)
-
Add raw text without automatic indentation or newline.
$b->raw('/* inline comment */'); - comment($text)
-
Add a C comment.
$b->comment('This is a comment'); # Generates: /* This is a comment */ - blank()
-
Add a blank line.
XS Structure
- xs_function($name)
-
Start an XS function definition.
$b->xs_function('my_function'); # Generates: XS_EUPXS(my_function) { - xs_preamble()
-
Add standard XS preamble (dVAR, dXSARGS, etc.).
$b->xs_function('my_func') ->xs_preamble; - xs_end()
-
Close an XS function.
$b->xs_end; # Generates: } - xs_return($count)
-
Add XSRETURN statement.
$b->xs_return('1'); # Generates: XSRETURN(1); - xs_return_undef()
-
Return undef from XS function.
$b->xs_return_undef; # Generates: ST(0) = &PL_sv_undef; XSRETURN(1); - method_start($name, $min_args, $max_args, $usage)
-
Start a method with argument checking.
$b->method_start('set_value', 2, 2, '$self, $value'); # Generates function with items check and usage croak
Control Flow
- if($condition)
-
Start an if block.
$b->if('items > 1'); - elsif($condition)
-
Add an elsif clause.
$b->elsif('items == 1'); - else()
-
Add an else clause.
$b->else; - endif()
-
Close an if/elsif/else block.
$b->if('x > 0') ->return_pv('positive') ->endif; - for($init, $cond, $incr)
-
Start a for loop.
$b->for('int i = 0', 'i < 10', 'i++'); - while($condition)
-
Start a while loop.
$b->while('ptr != NULL'); - endloop() / endfor() / endwhile()
-
Close a loop. All three are aliases.
$b->for('int i = 0', 'i < n', 'i++') ->raw('sum += array[i];') ->endfor; - block()
-
Start a bare block.
$b->block; # Generates: { - endblock()
-
Close a bare block.
$b->block ->declare_iv('temp', 'x') ->raw('x = y; y = temp;') ->endblock;
Variable Declarations
- declare($type, $name, $value)
-
Declare a variable of any type.
$b->declare('int', 'count', '0'); # Generates: int count = 0; - declare_sv($name, $value)
-
Declare an SV* variable.
$b->declare_sv('arg', 'ST(1)'); # Generates: SV* arg = ST(1); - declare_iv($name, $value)
-
Declare an IV (integer) variable.
$b->declare_iv('count', 'items - 1'); # Generates: IV count = items - 1; - declare_nv($name, $value)
-
Declare an NV (double) variable.
$b->declare_nv('total', '0.0'); # Generates: NV total = 0.0; - declare_pv($name, $value)
-
Declare a const char* variable.
$b->declare_pv('str', 'SvPV_nolen(ST(0))'); # Generates: const char* str = SvPV_nolen(ST(0)); - declare_hv($name, $value)
-
Declare an HV* variable.
$b->declare_hv('hash', '(HV*)SvRV(arg)'); - declare_av($name, $value)
-
Declare an AV* variable.
$b->declare_av('array', '(AV*)SvRV(arg)'); - new_hv($name)
-
Declare and create a new hash.
$b->new_hv('hv'); # Generates: HV* hv = newHV(); - new_av($name)
-
Declare and create a new array.
$b->new_av('results'); # Generates: AV* results = newAV(); - assign($var, $value)
-
Assign a value to an existing variable.
$b->assign('count', 'count + 1'); # Generates: count = count + 1;
Type Checking
- check_items($min, $max, $usage)
-
Check argument count, croak if wrong. Use max=-1 for no upper limit.
$b->check_items(1, 2, '$self, $value?'); - check_defined($sv, $error_msg)
-
Croak if SV is not defined.
$b->check_defined('ST(1)', 'value required'); - check_hashref($sv, $error_msg)
-
Croak if SV is not a hash reference.
$b->check_hashref('ST(0)', 'expected hashref'); - check_arrayref($sv, $error_msg)
-
Croak if SV is not an array reference.
$b->check_arrayref('arg', 'expected arrayref');
SV Conversion
- sv_to_iv($result_var, $sv)
-
Convert SV to integer.
$b->sv_to_iv('count', 'ST(1)'); # Generates: IV count = SvIV(ST(1)); - sv_to_nv($result_var, $sv)
-
Convert SV to double.
$b->sv_to_nv('value', 'ST(1)'); - sv_to_pv($result_var, $len_var, $sv)
-
Convert SV to string with length.
$b->sv_to_pv('str', 'len', 'ST(1)'); # Generates: STRLEN len; const char* str = SvPV(ST(1), len); - sv_to_bool($result_var, $sv)
-
Convert SV to boolean.
$b->sv_to_bool('flag', 'ST(1)'); - new_sv_iv($result_var, $value)
-
Create new SV from integer.
$b->new_sv_iv('result', 'count'); # Generates: SV* result = newSViv(count); - new_sv_nv($result_var, $value)
-
Create new SV from double.
$b->new_sv_nv('result', 'total'); - new_sv_pv($result_var, $str, $len)
-
Create new SV from string.
$b->new_sv_pv('result', 'buffer', 'len'); # Generates: SV* result = newSVpvn(buffer, len);
Return Helpers
- return_iv($value)
-
Return an integer value.
$b->return_iv('42'); $b->return_iv('count'); - return_nv($value)
-
Return a double value.
$b->return_nv('3.14'); $b->return_nv('result'); - return_pv($str)
-
Return a string value. The string is automatically quoted if it doesn't start with a letter/underscore (i.e., if it's not a C variable name).
$b->return_pv('hello'); # Returns literal "hello" $b->return_pv('buffer'); # Returns C variable buffer $b->return_pv('"quoted"'); # Returns literal "quoted" - return_sv($sv)
-
Return an SV.
$b->return_sv('result'); $b->return_sv('sv_2mortal(newSViv(42))'); - return_yes()
-
Return true (
&PL_sv_yes). - return_no()
-
Return false (
&PL_sv_no). - return_undef()
-
Return undef.
- return_self()
-
Return the invocant (
$self).$b->return_self; # For method chaining
Self/Object Methods
- get_self()
-
Get the invocant as SV*.
$b->get_self; # Generates: SV* self = ST(0); - get_self_hv()
-
Get the invocant and its underlying hash.
$b->get_self_hv; # Generates: SV* self = ST(0); HV* hv = (HV*)SvRV(self); - mortal($result_var, $sv_expr)
-
Create a mortal copy of an SV.
$b->mortal('result', 'newSViv(42)'); # Generates: SV* result = sv_2mortal(newSViv(42));
Hash Operations
- hv_fetch($hv, $key, $len, $result_var)
-
Fetch from a hash with a literal string key.
$b->hv_fetch('hv', 'name', 4, 'val'); # Generates: SV** val = hv_fetch(hv, "name", 4, 0); - hv_fetch_sv($hv, $key_expr, $len_expr, $result_var)
-
Fetch from a hash with a C expression key.
$b->hv_fetch_sv('hv', 'key', 'key_len', 'val'); # Generates: SV** val = hv_fetch(hv, key, key_len, 0); - hv_store($hv, $key, $len, $value)
-
Store into a hash with a literal string key.
$b->hv_store('hv', 'name', 4, 'newSVpv("Alice", 0)'); # Generates: (void)hv_store(hv, "name", 4, newSVpv("Alice", 0), 0); - hv_store_sv($hv, $key_expr, $len_expr, $value)
-
Store into a hash with a C expression key.
$b->hv_store_sv('hv', 'key', 'key_len', 'newSVsv(val)'); - hv_fetch_return($hv, $key, $len)
-
Fetch from hash and return the value (or undef if not found).
$b->hv_fetch_return('hv', 'name', 4); - hv_delete($hv, $key, $len)
-
Delete a key from a hash.
$b->hv_delete('hv', 'name', 4); - hv_exists($hv, $key, $len, $result_var)
-
Check if a key exists in a hash.
$b->hv_exists('hv', 'name', 4, 'found');
Array Operations
- av_fetch($av, $index, $result_var)
-
Fetch from an array.
$b->av_fetch('av', 'i', 'elem'); # Generates: SV** elem = av_fetch(av, i, 0); - av_store($av, $index, $value)
-
Store into an array.
$b->av_store('av', 'i', 'newSViv(42)'); - av_push($av, $value)
-
Push onto an array.
$b->av_push('results', 'newSViv(n)'); - av_len($av, $result_var)
-
Get the highest index of an array.
$b->av_len('av', 'last_idx'); # Generates: SSize_t last_idx = av_len(av);
Prebuilt Patterns
These methods generate complete XS functions for common patterns.
- accessor($attr_name, \%options)
-
Generate a read-write accessor (or read-only with
readonly => 1). The function name is the attribute name.$b->accessor('name'); # read-write $b->accessor('id', { readonly => 1 }); # read-only - ro_accessor($func_name, $attr_name, $attr_len)
-
Generate a complete read-only accessor function.
$b->ro_accessor('get_name', 'name', 4); - rw_accessor($func_name, $attr_name, $attr_len)
-
Generate a complete read-write accessor function.
$b->rw_accessor('name', 'name', 4); - predicate($attr_name)
-
Generate a predicate method (
has_$attr_name).$b->predicate('name'); # Generates has_name() that returns true if 'name' key exists - clearer($attr_name)
-
Generate a clearer method (
clear_$attr_name).$b->clearer('cache'); # Generates clear_cache() that deletes 'cache' key - constructor($func_name, \@attrs)
-
Generate a constructor. Attrs is arrayref of
[$name, $len]pairs or simple attribute name strings.$b->constructor('new', [ ['name', 4], ['age', 3], ]); # Or with auto-calculated lengths: $b->constructor('new', ['name', 'age']);
COMPLETE EXAMPLES
Simple Class with Accessors
use XS::JIT;
use XS::JIT::Builder;
use File::Temp qw(tempdir);
my $cache = tempdir(CLEANUP => 1);
my $b = XS::JIT::Builder->new;
# Constructor
$b->xs_function('person_new')
->xs_preamble
->new_hv('hv')
->if('items >= 2')
->hv_store('hv', 'name', 4, 'newSVsv(ST(1))')
->endif
->if('items >= 3')
->hv_store('hv', 'age', 3, 'newSVsv(ST(2))')
->endif
->raw('SV* self = newRV_noinc((SV*)hv);')
->raw('sv_bless(self, gv_stashpv("Person", GV_ADD));')
->return_sv('self')
->xs_end
->blank;
# Name accessor (read-write)
$b->rw_accessor('person_name', 'name', 4)->blank;
# Age accessor (read-only)
$b->ro_accessor('person_age', 'age', 3);
XS::JIT->compile(
code => $b->code,
name => 'Person',
cache_dir => $cache,
functions => {
'Person::new' => { source => 'person_new', is_xs_native => 1 },
'Person::name' => { source => 'person_name', is_xs_native => 1 },
'Person::age' => { source => 'person_age', is_xs_native => 1 },
},
);
my $p = Person->new('Alice', 30);
say $p->name; # Alice
$p->name('Bob'); # set name
say $p->age; # 30
Class with Inheritance
my $b = XS::JIT::Builder->new;
# Base class constructor
$b->xs_function('animal_new')
->xs_preamble
->new_hv('hv')
->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
->raw('SV* self = newRV_noinc((SV*)hv);')
->raw('sv_bless(self, gv_stashpv(SvPV_nolen(ST(0)), GV_ADD));')
->return_sv('self')
->xs_end
->blank;
# Dog constructor (overrides Animal)
$b->xs_function('dog_new')
->xs_preamble
->new_hv('hv')
->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
->hv_store('hv', 'breed', 5, 'items > 2 ? newSVsv(ST(2)) : newSVpv("mutt", 0)')
->raw('SV* self = newRV_noinc((SV*)hv);')
->raw('sv_bless(self, gv_stashpv("Dog", GV_ADD));')
->return_sv('self')
->xs_end;
# Compile and set up inheritance
XS::JIT->compile(...);
package Dog;
use parent -norequire => 'Animal';
SEE ALSO
XS::JIT - The main JIT compiler module
The C API is available in xs_jit_builder.h for direct use from XS code.
AUTHOR
LNATION <email@lnation.org>
LICENSE
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.