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.