File Coverage

blib/lib/XS/JIT/Builder.pm
Criterion Covered Total %
statement 11 17 64.7
branch 0 6 0.0
condition n/a
subroutine 4 5 80.0
pod 1 1 100.0
total 16 29 55.1


line stmt bran cond sub pod time code
1             package XS::JIT::Builder;
2              
3 46     46   2679732 use strict;
  46         1597  
  46         3099  
4 46     46   1720 use warnings;
  46         1544  
  46         2049  
5 46     46   628 use 5.010;
  46         115  
6              
7             our $VERSION = '0.18';
8              
9 46     46   191 use Exporter 'import';
  46         77  
  46         21692  
10             our @EXPORT_OK = qw(
11             INLINE_NONE INLINE_GETTER INLINE_SETTER
12             INLINE_HV_GETTER INLINE_HV_SETTER
13             TYPE_ANY TYPE_DEFINED TYPE_INT TYPE_NUM TYPE_STR
14             TYPE_REF TYPE_ARRAYREF TYPE_HASHREF TYPE_CODEREF
15             TYPE_OBJECT TYPE_BLESSED
16             );
17             our %EXPORT_TAGS = (
18             inline => [qw(INLINE_NONE INLINE_GETTER INLINE_SETTER
19             INLINE_HV_GETTER INLINE_HV_SETTER)],
20             types => [qw(TYPE_ANY TYPE_DEFINED TYPE_INT TYPE_NUM TYPE_STR
21             TYPE_REF TYPE_ARRAYREF TYPE_HASHREF TYPE_CODEREF
22             TYPE_OBJECT TYPE_BLESSED)],
23             );
24              
25             require XSLoader;
26             XSLoader::load('XS::JIT::Builder', $VERSION);
27              
28             sub write_file {
29 0     0 1   my ($self, $filename) = @_;
30              
31 0 0         die "write_file requires a filename" unless defined $filename;
32              
33 0 0         open my $fh, '>', $filename
34             or die "Cannot open '$filename' for writing: $!";
35 0           print $fh $self->code;
36 0 0         close $fh
37             or die "Cannot close '$filename': $!";
38              
39 0           return $self;
40             }
41              
42             =head1 NAME
43              
44             XS::JIT::Builder - interface for building XS/C code strings
45              
46             =head1 SYNOPSIS
47              
48             use XS::JIT;
49             use XS::JIT::Builder;
50             use File::Temp qw(tempdir);
51              
52             my $cache_dir = tempdir(CLEANUP => 1);
53             my $b = XS::JIT::Builder->new;
54            
55             # Build a simple accessor
56             $b->xs_function('get_name')
57             ->xs_preamble
58             ->get_self_hv
59             ->hv_fetch('hv', 'name', 4, 'val')
60             ->if('val != NULL')
61             ->return_sv('*val')
62             ->endif
63             ->return_undef
64             ->xs_end;
65            
66             # Compile and use
67             XS::JIT->compile(
68             code => $b->code,
69             name => 'MyClass',
70             cache_dir => $cache_dir,
71             functions => {
72             'MyClass::name' => { source => 'get_name', is_xs_native => 1 },
73             },
74             );
75            
76             my $obj = bless { name => 'Alice' }, 'MyClass';
77             print $obj->name; # prints "Alice"
78              
79             =head1 DESCRIPTION
80              
81             This module is experimental, the api and the code generated will evolve over time.
82              
83             XS::JIT::Builder provides a interface for generating C code dynamically.
84             It handles indentation automatically and provides convenient methods for
85             common XS patterns.
86              
87             All methods return C<$self> to enable method chaining. The generated code
88             can be retrieved with C and passed to Ccompile()>.
89              
90             =head1 METHODS
91              
92             =head2 Lifecycle
93              
94             =over 4
95              
96             =item new(%options)
97              
98             Create a new builder.
99              
100             my $b = XS::JIT::Builder->new;
101             my $b = XS::JIT::Builder->new(use_tabs => 1);
102             my $b = XS::JIT::Builder->new(indent_width => 2);
103              
104             Options:
105              
106             =over 4
107              
108             =item use_tabs
109              
110             Use tabs for indentation instead of spaces (default: false).
111              
112             =item indent_width
113              
114             Number of spaces per indentation level when not using tabs (default: 4).
115              
116             =back
117              
118             =item code()
119              
120             Return the accumulated C code as a string.
121              
122             my $c_code = $b->code;
123              
124             =item reset()
125              
126             Clear the builder and reset state for reuse.
127              
128             $b->reset;
129             # Builder is now empty, can build new code
130              
131             =item write_file($filename)
132              
133             Write the accumulated C code to a file. This is useful for:
134              
135             =over 4
136              
137             =item * Distribution - Generate the XS file once, ship it with your module
138              
139             =item * Debugging - Inspect the generated code
140              
141             =item * Performance - Skip runtime JIT compilation entirely
142              
143             =back
144              
145             $b->write_file('MyModule.c');
146             # Now you can compile MyModule.c as a regular XS/C extension
147              
148             Returns C<$self> to enable method chaining.
149              
150             =back
151              
152             =head2 Low-level Output
153              
154             =over 4
155              
156             =item line($text)
157              
158             Add a line with current indentation.
159              
160             $b->line('int x = 42;');
161              
162             =item raw($text)
163              
164             Add raw text without automatic indentation or newline.
165              
166             $b->raw('/* inline comment */');
167              
168             =item comment($text)
169              
170             Add a C comment.
171              
172             $b->comment('This is a comment');
173             # Generates: /* This is a comment */
174              
175             =item blank()
176              
177             Add a blank line.
178              
179             =back
180              
181             =head2 XS Structure
182              
183             =over 4
184              
185             =item xs_function($name)
186              
187             Start an XS function definition.
188              
189             $b->xs_function('my_function');
190             # Generates: XS_EUPXS(my_function) {
191              
192             =item xs_preamble()
193              
194             Add standard XS preamble (dVAR, dXSARGS, etc.).
195              
196             $b->xs_function('my_func')
197             ->xs_preamble;
198              
199             =item xs_end()
200              
201             Close an XS function.
202              
203             $b->xs_end;
204             # Generates: }
205              
206             =item xs_return($count)
207              
208             Add XSRETURN statement.
209              
210             $b->xs_return('1');
211             # Generates: XSRETURN(1);
212              
213             =item xs_return_undef()
214              
215             Return undef from XS function.
216              
217             $b->xs_return_undef;
218             # Generates: ST(0) = &PL_sv_undef; XSRETURN(1);
219              
220             =item method_start($name, $min_args, $max_args, $usage)
221              
222             Start a method with argument checking.
223              
224             $b->method_start('set_value', 2, 2, '$self, $value');
225             # Generates function with items check and usage croak
226              
227             =back
228              
229             =head2 Control Flow
230              
231             =over 4
232              
233             =item if($condition)
234              
235             Start an if block.
236              
237             $b->if('items > 1');
238              
239             =item elsif($condition)
240              
241             Add an elsif clause.
242              
243             $b->elsif('items == 1');
244              
245             =item else()
246              
247             Add an else clause.
248              
249             $b->else;
250              
251             =item endif()
252              
253             Close an if/elsif/else block.
254              
255             $b->if('x > 0')
256             ->return_pv('positive')
257             ->endif;
258              
259             =item for($init, $cond, $incr)
260              
261             Start a for loop.
262              
263             $b->for('int i = 0', 'i < 10', 'i++');
264              
265             =item while($condition)
266              
267             Start a while loop.
268              
269             $b->while('ptr != NULL');
270              
271             =item endloop() / endfor() / endwhile()
272              
273             Close a loop. All three are aliases.
274              
275             $b->for('int i = 0', 'i < n', 'i++')
276             ->raw('sum += array[i];')
277             ->endfor;
278              
279             =item block()
280              
281             Start a bare block.
282              
283             $b->block;
284             # Generates: {
285              
286             =item endblock()
287              
288             Close a bare block.
289              
290             $b->block
291             ->declare_iv('temp', 'x')
292             ->raw('x = y; y = temp;')
293             ->endblock;
294              
295             =back
296              
297             =head2 Variable Declarations
298              
299             =over 4
300              
301             =item declare($type, $name, $value)
302              
303             Declare a variable of any type.
304              
305             $b->declare('int', 'count', '0');
306             # Generates: int count = 0;
307              
308             =item declare_sv($name, $value)
309              
310             Declare an SV* variable.
311              
312             $b->declare_sv('arg', 'ST(1)');
313             # Generates: SV* arg = ST(1);
314              
315             =item declare_iv($name, $value)
316              
317             Declare an IV (integer) variable.
318              
319             $b->declare_iv('count', 'items - 1');
320             # Generates: IV count = items - 1;
321              
322             =item declare_nv($name, $value)
323              
324             Declare an NV (double) variable.
325              
326             $b->declare_nv('total', '0.0');
327             # Generates: NV total = 0.0;
328              
329             =item declare_pv($name, $value)
330              
331             Declare a const char* variable.
332              
333             $b->declare_pv('str', 'SvPV_nolen(ST(0))');
334             # Generates: const char* str = SvPV_nolen(ST(0));
335              
336             =item declare_hv($name, $value)
337              
338             Declare an HV* variable.
339              
340             $b->declare_hv('hash', '(HV*)SvRV(arg)');
341              
342             =item declare_av($name, $value)
343              
344             Declare an AV* variable.
345              
346             $b->declare_av('array', '(AV*)SvRV(arg)');
347              
348             =item new_hv($name)
349              
350             Declare and create a new hash.
351              
352             $b->new_hv('hv');
353             # Generates: HV* hv = newHV();
354              
355             =item new_av($name)
356              
357             Declare and create a new array.
358              
359             $b->new_av('results');
360             # Generates: AV* results = newAV();
361              
362             =item assign($var, $value)
363              
364             Assign a value to an existing variable.
365              
366             $b->assign('count', 'count + 1');
367             # Generates: count = count + 1;
368              
369             =back
370              
371             =head2 Type Checking
372              
373             =over 4
374              
375             =item check_items($min, $max, $usage)
376              
377             Check argument count, croak if wrong. Use max=-1 for no upper limit.
378              
379             $b->check_items(1, 2, '$self, $value?');
380              
381             =item check_defined($sv, $error_msg)
382              
383             Croak if SV is not defined.
384              
385             $b->check_defined('ST(1)', 'value required');
386              
387             =item check_hashref($sv, $error_msg)
388              
389             Croak if SV is not a hash reference.
390              
391             $b->check_hashref('ST(0)', 'expected hashref');
392              
393             =item check_arrayref($sv, $error_msg)
394              
395             Croak if SV is not an array reference.
396              
397             $b->check_arrayref('arg', 'expected arrayref');
398              
399             =back
400              
401             =head2 SV Conversion
402              
403             =over 4
404              
405             =item sv_to_iv($result_var, $sv)
406              
407             Convert SV to integer.
408              
409             $b->sv_to_iv('count', 'ST(1)');
410             # Generates: IV count = SvIV(ST(1));
411              
412             =item sv_to_nv($result_var, $sv)
413              
414             Convert SV to double.
415              
416             $b->sv_to_nv('value', 'ST(1)');
417              
418             =item sv_to_pv($result_var, $len_var, $sv)
419              
420             Convert SV to string with length.
421              
422             $b->sv_to_pv('str', 'len', 'ST(1)');
423             # Generates: STRLEN len; const char* str = SvPV(ST(1), len);
424              
425             =item sv_to_bool($result_var, $sv)
426              
427             Convert SV to boolean.
428              
429             $b->sv_to_bool('flag', 'ST(1)');
430              
431             =item new_sv_iv($result_var, $value)
432              
433             Create new SV from integer.
434              
435             $b->new_sv_iv('result', 'count');
436             # Generates: SV* result = newSViv(count);
437              
438             =item new_sv_nv($result_var, $value)
439              
440             Create new SV from double.
441              
442             $b->new_sv_nv('result', 'total');
443              
444             =item new_sv_pv($result_var, $str, $len)
445              
446             Create new SV from string.
447              
448             $b->new_sv_pv('result', 'buffer', 'len');
449             # Generates: SV* result = newSVpvn(buffer, len);
450              
451             =back
452              
453             =head2 Return Helpers
454              
455             =over 4
456              
457             =item return_iv($value)
458              
459             Return an integer value.
460              
461             $b->return_iv('42');
462             $b->return_iv('count');
463              
464             =item return_nv($value)
465              
466             Return a double value.
467              
468             $b->return_nv('3.14');
469             $b->return_nv('result');
470              
471             =item return_pv($str)
472              
473             Return a string value. The string is automatically quoted if it doesn't
474             start with a letter/underscore (i.e., if it's not a C variable name).
475              
476             $b->return_pv('hello'); # Returns literal "hello"
477             $b->return_pv('buffer'); # Returns C variable buffer
478             $b->return_pv('"quoted"'); # Returns literal "quoted"
479              
480             =item return_sv($sv)
481              
482             Return an SV.
483              
484             $b->return_sv('result');
485             $b->return_sv('sv_2mortal(newSViv(42))');
486              
487             =item return_yes()
488              
489             Return true (C<&PL_sv_yes>).
490              
491             =item return_no()
492              
493             Return false (C<&PL_sv_no>).
494              
495             =item return_undef()
496              
497             Return undef.
498              
499             =item return_self()
500              
501             Return the invocant (C<$self>).
502              
503             $b->return_self;
504             # For method chaining
505              
506             =back
507              
508             =head2 Self/Object Methods
509              
510             =over 4
511              
512             =item get_self()
513              
514             Get the invocant as SV*.
515              
516             $b->get_self;
517             # Generates: SV* self = ST(0);
518              
519             =item get_self_hv()
520              
521             Get the invocant and its underlying hash.
522              
523             $b->get_self_hv;
524             # Generates: SV* self = ST(0); HV* hv = (HV*)SvRV(self);
525              
526             =item mortal($result_var, $sv_expr)
527              
528             Create a mortal copy of an SV.
529              
530             $b->mortal('result', 'newSViv(42)');
531             # Generates: SV* result = sv_2mortal(newSViv(42));
532              
533             =back
534              
535             =head2 Hash Operations
536              
537             =over 4
538              
539             =item hv_fetch($hv, $key, $len, $result_var)
540              
541             Fetch from a hash with a literal string key.
542              
543             $b->hv_fetch('hv', 'name', 4, 'val');
544             # Generates: SV** val = hv_fetch(hv, "name", 4, 0);
545              
546             =item hv_fetch_sv($hv, $key_expr, $len_expr, $result_var)
547              
548             Fetch from a hash with a C expression key.
549              
550             $b->hv_fetch_sv('hv', 'key', 'key_len', 'val');
551             # Generates: SV** val = hv_fetch(hv, key, key_len, 0);
552              
553             =item hv_store($hv, $key, $len, $value)
554              
555             Store into a hash with a literal string key.
556              
557             $b->hv_store('hv', 'name', 4, 'newSVpv("Alice", 0)');
558             # Generates: (void)hv_store(hv, "name", 4, newSVpv("Alice", 0), 0);
559              
560             =item hv_store_sv($hv, $key_expr, $len_expr, $value)
561              
562             Store into a hash with a C expression key.
563              
564             $b->hv_store_sv('hv', 'key', 'key_len', 'newSVsv(val)');
565              
566             =item hv_store_weak($hv, $key, $key_len, $value_expr)
567              
568             Store a value into a hash, weakening it if it's a reference.
569             Useful for parent/owner pointers that should not hold strong references.
570              
571             $b->hv_store_weak('hv', 'parent', 6, 'newSVsv(ST(1))');
572              
573             =item hv_fetch_return($hv, $key, $len)
574              
575             Fetch from hash and return the value (or undef if not found).
576              
577             $b->hv_fetch_return('hv', 'name', 4);
578              
579             =item hv_delete($hv, $key, $len)
580              
581             Delete a key from a hash.
582              
583             $b->hv_delete('hv', 'name', 4);
584              
585             =item hv_exists($hv, $key, $len, $result_var)
586              
587             Check if a key exists in a hash.
588              
589             $b->hv_exists('hv', 'name', 4, 'found');
590              
591             =back
592              
593             =head2 Array Operations
594              
595             =over 4
596              
597             =item av_fetch($av, $index, $result_var)
598              
599             Fetch from an array.
600              
601             $b->av_fetch('av', 'i', 'elem');
602             # Generates: SV** elem = av_fetch(av, i, 0);
603              
604             =item av_store($av, $index, $value)
605              
606             Store into an array.
607              
608             $b->av_store('av', 'i', 'newSViv(42)');
609              
610             =item av_push($av, $value)
611              
612             Push onto an array.
613              
614             $b->av_push('results', 'newSViv(n)');
615              
616             =item av_len($av, $result_var)
617              
618             Get the highest index of an array.
619              
620             $b->av_len('av', 'last_idx');
621             # Generates: SSize_t last_idx = av_len(av);
622              
623             =back
624              
625             =head2 Callbacks & Triggers
626              
627             Methods for calling Perl code from generated XS functions.
628              
629             =over 4
630              
631             =item call_sv($cv_expr, \@args)
632              
633             Generate code to call a coderef with given arguments. Uses G_DISCARD so
634             return values are discarded. Useful for calling callbacks, event handlers,
635             or triggers.
636              
637             $b->call_sv('cb', ['self', 'new_val']);
638             # Generates: dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(self); ...
639              
640             =item call_method($method_name, $invocant, \@args)
641              
642             Generate code to call a method by name on an invocant. Uses G_DISCARD
643             so return values are discarded. Useful for calling hooks like BUILD,
644             DEMOLISH, triggers, or callbacks.
645              
646             $b->call_method('_on_change', 'self', ['old_val', 'new_val']);
647             # Generates: call_method("_on_change", G_DISCARD);
648              
649             =item rw_accessor_trigger($func_name, $attr, $len, $trigger_method)
650              
651             Generate a read/write accessor that calls a trigger method after setting.
652             The trigger method is called with the object and new value as arguments.
653              
654             $b->rw_accessor_trigger('MyClass::name', 'name', 4, '_on_name_change');
655             # Accessor calls $self->_on_name_change($new_val) after setting
656              
657             =item accessor_lazy_builder($func_name, $attr, $len, $builder_method)
658              
659             Generate a read/write accessor with lazy initialization. On first read,
660             if the value is undefined, calls the builder method to compute and cache
661             the value.
662              
663             $b->accessor_lazy_builder('MyClass::computed', 'computed', 8, '_build_computed');
664             # First read calls $self->_build_computed() and caches result
665              
666             =item destroy_with_demolish($func_name)
667              
668             Generate a DESTROY method that checks for and calls DEMOLISH if it exists.
669             Passes C<$in_global_destruction> as the second argument to DEMOLISH.
670              
671             $b->destroy_with_demolish('MyClass::DESTROY');
672             # DESTROY calls $self->DEMOLISH($in_global_destruction) if defined
673              
674             =back
675              
676             =head2 Control Flow & Extended Patterns
677              
678             Advanced control flow helpers and expression builders.
679              
680             =over 4
681              
682             =item do_loop() / end_do_while($condition)
683              
684             Generate a do-while loop that executes at least once.
685              
686             $b->do_loop
687             ->line('/* loop body */')
688             ->end_do_while('ptr != NULL');
689             # Generates: do { ... } while (ptr != NULL);
690              
691             =item if_list_context()
692              
693             Branch on list context. Use with C and C.
694              
695             $b->if_list_context
696             ->return_list(['key_sv', 'val_sv'])
697             ->else
698             ->line('ST(0) = val_sv;')
699             ->line('XSRETURN(1);')
700             ->endif;
701              
702             =item if_scalar_context()
703              
704             Branch on scalar context. Use with C and C.
705              
706             $b->if_scalar_context
707             ->line('ST(0) = count_sv;')
708             ->else
709             ->return_list(['@items'])
710             ->endif;
711              
712             =item extend_stack($count_expr)
713              
714             Extend the stack to hold more return values.
715              
716             $b->extend_stack('num_items');
717             # Generates: EXTEND(SP, num_items);
718              
719             =item return_list(\@values)
720              
721             Return multiple values from XS, handling stack extension and mortalization.
722              
723             $b->return_list(['newSViv(1)', 'newSViv(2)', 'newSViv(3)']);
724             # Generates: EXTEND(SP, 3); ST(0) = sv_2mortal(...); ... XSRETURN(3);
725              
726             =item declare_ternary($type, $name, $cond, $true_expr, $false_expr)
727              
728             Declare a variable with ternary initialization.
729              
730             $b->declare_ternary('SV*', 'val', 'items > 1', 'ST(1)', '&PL_sv_undef');
731             # Generates: SV* val = (items > 1) ? ST(1) : &PL_sv_undef;
732              
733             =item assign_ternary($var, $cond, $true_expr, $false_expr)
734              
735             Ternary assignment to existing variable.
736              
737             $b->assign_ternary('result', 'found', '*svp', '&PL_sv_undef');
738             # Generates: result = (found) ? *svp : &PL_sv_undef;
739              
740             =item delegate_method($func_name, $attr, $len, $target_method)
741              
742             Generate a method that delegates to an attribute's method.
743             Passes through all arguments and returns the result.
744              
745             $b->delegate_method('get_name', 'delegate_obj', 12, 'name');
746             # Generates: sub get_name { $self->{delegate_obj}->name(@_) }
747              
748             =back
749              
750             =head2 Singleton Pattern
751              
752             Methods for implementing the singleton design pattern.
753              
754             =over 4
755              
756             =item singleton_accessor($func_name, $class_name)
757              
758             Generate a class method that returns the singleton instance, creating it on
759             first access. The instance is stored in a package variable C<$Class::_instance>.
760              
761             $b->singleton_accessor('instance', 'MyApp::Config');
762             # MyApp::Config->instance always returns same object
763              
764             =item singleton_reset($func_name, $class_name)
765              
766             Generate a class method that clears the singleton instance. The next call to
767             the singleton accessor will create a fresh instance.
768              
769             $b->singleton_reset('reset_instance', 'MyApp::Config');
770             # MyApp::Config->reset_instance clears the singleton
771              
772             =back
773              
774             =head2 Registry Pattern
775              
776             These methods generate registry pattern accessors for storing and retrieving
777             items in a hash attribute by key.
778              
779             =over 4
780              
781             =item registry_add($func_name, $registry_attr)
782              
783             Generate a method to add an item to a registry hash. Creates the registry
784             hash automatically if it doesn't exist. Returns $self for chaining.
785              
786             $b->registry_add('register_handler', '_handlers');
787             # Usage: $obj->register_handler(click => sub { ... });
788              
789             =item registry_get($func_name, $registry_attr)
790              
791             Generate a method to retrieve an item from a registry hash by key.
792             Returns undef if the key doesn't exist.
793              
794             $b->registry_get('get_handler', '_handlers');
795             # Usage: my $handler = $obj->get_handler('click');
796              
797             =item registry_remove($func_name, $registry_attr)
798              
799             Generate a method to remove and return an item from a registry hash.
800             Returns undef if the key doesn't exist.
801              
802             $b->registry_remove('unregister_handler', '_handlers');
803             # Usage: my $old = $obj->unregister_handler('click');
804              
805             =item registry_all($func_name, $registry_attr)
806              
807             Generate a context-aware method to retrieve all registry items.
808             In list context, returns key-value pairs. In scalar context, returns a
809             shallow copy of the registry as a hashref.
810              
811             $b->registry_all('all_handlers', '_handlers');
812             # List context: my %handlers = $obj->all_handlers;
813             # Scalar context: my $hashref = $obj->all_handlers;
814              
815             =back
816              
817             =head2 Method Modifiers
818              
819             These methods generate Moose/Moo-style method modifiers that wrap existing
820             methods with before, after, or around hooks.
821              
822             =over 4
823              
824             =item wrap_before($func_name, $orig_name, $before_cv_name)
825              
826             Generate a wrapper that calls a "before" hook before the original method.
827             The before hook receives the same arguments as the original. Its return
828             value is discarded. The original's return value is preserved.
829              
830             $b->wrap_before('save_with_log', 'MyClass::_orig_save', 'MyClass::_log_before');
831             # Calls _log_before(@args), then _orig_save(@args)
832              
833             =item wrap_after($func_name, $orig_name, $after_cv_name)
834              
835             Generate a wrapper that calls an "after" hook after the original method.
836             The after hook receives the same arguments as the original. Its return
837             value is discarded. The original's return value is preserved.
838              
839             $b->wrap_after('save_with_notify', 'MyClass::_orig_save', 'MyClass::_notify_after');
840             # Calls _orig_save(@args), then _notify_after(@args)
841              
842             =item wrap_around($func_name, $orig_name, $around_cv_name)
843              
844             Generate a wrapper with "around" semantics. The around hook receives the
845             original coderef as its first argument, followed by the original arguments.
846             The around hook has full control and can modify arguments, skip the original,
847             or modify return values.
848              
849             $b->wrap_around('save_cached', 'MyClass::_orig_save', 'MyClass::_cache_around');
850             # _cache_around receives ($orig, @args)
851             # Can call: $orig->(@args) or skip or modify
852              
853             =back
854              
855             =head2 Role/Mixin Composer
856              
857             Generate multiple related methods that compose behavioral patterns.
858              
859             =over 4
860              
861             =item role($role_name, \%opts)
862              
863             Generate all methods for a single role. Available roles:
864              
865             B - Comparison methods (compare by a key attribute):
866              
867             $b->role('Comparable'); # Uses 'id' as compare key
868             $b->role('Comparable', { compare_key => 'name' }); # Uses 'name'
869              
870             Generates: C, C, C, C,
871             C, C.
872              
873             B - Object cloning:
874              
875             $b->role('Cloneable');
876              
877             Generates: C - shallow clone of hash-based object.
878              
879             B - Serialization methods:
880              
881             $b->role('Serializable');
882              
883             Generates: C, C - return hashref copy (JSON::XS compatible).
884              
885             B - Observer pattern:
886              
887             $b->role('Observable');
888             $b->role('Observable', { observers_attr => '_watchers' });
889              
890             Generates: C, C,
891             C.
892              
893             =item with_roles(\@roles, \%opts)
894              
895             Compose multiple roles in a single call:
896              
897             $b->with_roles(['Comparable', 'Cloneable', 'Serializable']);
898              
899             # With options:
900             $b->with_roles(['Comparable', 'Observable'], {
901             compare_key => 'name',
902             observers_attr => '_listeners',
903             });
904              
905             This is a convenience method that calls C for each role in the list.
906              
907             =back
908              
909             =head2 Prebuilt Patterns
910              
911             These methods generate complete XS functions for common patterns.
912              
913             =head3 Constructors
914              
915             =over 4
916              
917             =item new_simple($func_name)
918              
919             Generate a minimal constructor: C.
920             Fastest possible constructor with no argument processing.
921              
922             $b->new_simple('new');
923             # Generates: my $obj = Class->new;
924              
925             =item new_hash($func_name)
926              
927             Generate a flexible constructor that accepts either flat hash or hashref args.
928             All provided args are copied into the object.
929              
930             $b->new_hash('new');
931             # Supports both:
932             # my $obj = Class->new(name => 'Alice', age => 30);
933             # my $obj = Class->new({ name => 'Alice', age => 30 });
934              
935             =item new_array($func_name, $num_slots)
936              
937             Generate an array-based constructor (Meow-style). Creates a blessed arrayref
938             with pre-allocated slots initialized to undef.
939              
940             $b->new_array('new', 5);
941             # my $obj = Class->new; # $obj->[0..4] are undef
942              
943             =item new_with_required($func_name, \@required_attrs)
944              
945             Generate a constructor that validates required attributes. Croaks if any
946             required attribute is missing or undef.
947              
948             $b->new_with_required('new', ['name', 'id']);
949             # Class->new(name => 'Alice'); # croaks: "Missing required attribute 'id'"
950             # Class->new(name => 'Alice', id => 123); # OK
951              
952             Accepts either flat hash or hashref arguments, like C.
953              
954             =item new_with_build($func_name)
955              
956             Generate a constructor that calls BUILD if it exists. Moose/Moo compatible.
957             Accepts flat hash or hashref, and passes the args hash to BUILD.
958              
959             $b->new_with_build('new');
960             # Supports:
961             # my $obj = Class->new(name => 'Alice');
962             # Calls: $obj->BUILD(\%args) if BUILD is defined
963              
964             =item new_complete($func_name, \@attr_specs, $call_build)
965              
966             Generate a unified constructor with full Moose/Moo-like attribute handling.
967             Supports required, defaults, basic types, weak refs, coercion, and BUILD in one call.
968              
969             use XS::JIT::Builder qw(:types);
970            
971             $b->new_complete('new', [
972             {
973             name => 'id',
974             required => 1,
975             type => TYPE_INT,
976             type_msg => 'id must be an integer',
977             },
978             {
979             name => 'name',
980             default_pv => 'anonymous',
981             },
982             {
983             name => 'items',
984             default_av => 1, # empty []
985             type => TYPE_ARRAYREF,
986             },
987             {
988             name => 'meta',
989             default_hv => 1, # empty {}
990             },
991             {
992             name => 'count',
993             default_iv => 0,
994             },
995             {
996             name => 'parent',
997             weak => 1, # weaken stored reference
998             },
999             {
1000             name => 'age',
1001             coerce => 'to_int', # call Class->to_int($val)
1002             },
1003             ], 1); # 1 = call BUILD if exists
1004              
1005             Attribute spec options:
1006              
1007             name - (required) Attribute name
1008             required - Croak if missing or undef
1009             type - TYPE_* constant for validation
1010             type_msg - Error message for type failure
1011             weak - Weaken stored reference (prevents circular refs)
1012             coerce - Method name to call for coercion
1013             default_iv - Default integer value
1014             default_nv - Default numeric value
1015             default_pv - Default string value
1016             default_av - If true, default to empty []
1017             default_hv - If true, default to empty {}
1018              
1019             Processing order: coercion → required check → type validation →
1020             defaults → weak refs → BUILD.
1021              
1022             =item constructor($func_name, \@attrs)
1023              
1024             Generate a constructor with specific attributes. Attrs is arrayref of
1025             C<[$name, $len]> pairs or simple attribute name strings.
1026              
1027             $b->constructor('new', [
1028             ['name', 4],
1029             ['age', 3],
1030             ]);
1031            
1032             # Or with auto-calculated lengths:
1033             $b->constructor('new', ['name', 'age']);
1034              
1035             =back
1036              
1037             =head3 Accessors
1038              
1039             =over 4
1040              
1041             =item accessor($attr_name, \%options)
1042              
1043             Generate a read-write accessor (or read-only with C 1>).
1044             The function name is the attribute name.
1045              
1046             $b->accessor('name'); # read-write
1047             $b->accessor('id', { readonly => 1 }); # read-only
1048              
1049             =item ro_accessor($func_name, $attr_name, $attr_len)
1050              
1051             Generate a complete read-only accessor function.
1052              
1053             $b->ro_accessor('get_name', 'name', 4);
1054              
1055             =item rw_accessor($func_name, $attr_name, $attr_len)
1056              
1057             Generate a complete read-write accessor function.
1058              
1059             $b->rw_accessor('name', 'name', 4);
1060              
1061             =item rw_accessor_typed($func_name, $attr_name, $attr_len, $type, $error_msg)
1062              
1063             Generate a read-write accessor with inline type validation. On set, validates
1064             the value against the specified type and croaks with C<$error_msg> if invalid.
1065              
1066             use XS::JIT::Builder qw(:types);
1067            
1068             $b->rw_accessor_typed('age', 'age', 3, TYPE_INT, 'age must be an integer');
1069             $b->rw_accessor_typed('items', 'items', 5, TYPE_ARRAYREF, 'items must be an arrayref');
1070              
1071             Type constants (from C<:types> export tag):
1072              
1073             TYPE_ANY - No validation
1074             TYPE_DEFINED - Must be defined (not undef)
1075             TYPE_INT - Must be an integer
1076             TYPE_NUM - Must be a number
1077             TYPE_STR - Must be a string (not a reference)
1078             TYPE_REF - Must be a reference
1079             TYPE_ARRAYREF - Must be an arrayref
1080             TYPE_HASHREF - Must be a hashref
1081             TYPE_CODEREF - Must be a coderef
1082             TYPE_OBJECT - Must be a blessed object
1083              
1084             Note: C values bypass type checking (except for C).
1085              
1086             =item rw_accessor_weak($func_name, $attr_name, $attr_len)
1087              
1088             Generate a read-write accessor that auto-weakens stored references.
1089             Use this for parent/owner references to prevent circular reference leaks.
1090              
1091             $b->rw_accessor_weak('parent', 'parent', 6);
1092             # $child->parent($parent); # stored as weak reference
1093             # Prevents: $parent->{children} = [$child]; $child->{parent} = $parent;
1094              
1095             The reference is weakened only if it's actually a reference. Scalars and
1096             undef are stored normally.
1097              
1098             =item predicate($attr_name)
1099              
1100             Generate a predicate method (C).
1101              
1102             $b->predicate('name');
1103             # Generates has_name() that returns true if 'name' key exists
1104              
1105             =item clearer($attr_name)
1106              
1107             Generate a clearer method (C).
1108              
1109             $b->clearer('cache');
1110             # Generates clear_cache() that deletes 'cache' key
1111              
1112             =back
1113              
1114             =head3 Cloning
1115              
1116             =over 4
1117              
1118             =item clone_hash($func_name)
1119              
1120             Generate a shallow clone method for hash-based objects. Creates a new object
1121             with copies of all key/value pairs, blessed into the same class.
1122              
1123             $b->clone_hash('clone');
1124             # my $copy = $obj->clone;
1125             # $copy is independent - modifying it doesn't affect $obj
1126              
1127             Note: This is a shallow clone. Nested references point to the same data.
1128              
1129             =item clone_array($func_name)
1130              
1131             Generate a shallow clone method for array-based objects. Creates a new object
1132             with copies of all elements, blessed into the same class.
1133              
1134             $b->clone_array('clone');
1135             # my $copy = $obj->clone; # blessed arrayref copy
1136              
1137             Note: This is a shallow clone. Nested references point to the same data.
1138              
1139             =back
1140              
1141             =head1 COMPLETE EXAMPLES
1142              
1143             =head2 Simple Class with Accessors
1144              
1145             use XS::JIT;
1146             use XS::JIT::Builder;
1147             use File::Temp qw(tempdir);
1148              
1149             my $cache = tempdir(CLEANUP => 1);
1150             my $b = XS::JIT::Builder->new;
1151              
1152             # Constructor
1153             $b->xs_function('person_new')
1154             ->xs_preamble
1155             ->new_hv('hv')
1156             ->if('items >= 2')
1157             ->hv_store('hv', 'name', 4, 'newSVsv(ST(1))')
1158             ->endif
1159             ->if('items >= 3')
1160             ->hv_store('hv', 'age', 3, 'newSVsv(ST(2))')
1161             ->endif
1162             ->raw('SV* self = newRV_noinc((SV*)hv);')
1163             ->raw('sv_bless(self, gv_stashpv("Person", GV_ADD));')
1164             ->return_sv('self')
1165             ->xs_end
1166             ->blank;
1167              
1168             # Name accessor (read-write)
1169             $b->rw_accessor('person_name', 'name', 4)->blank;
1170              
1171             # Age accessor (read-only)
1172             $b->ro_accessor('person_age', 'age', 3);
1173              
1174             XS::JIT->compile(
1175             code => $b->code,
1176             name => 'Person',
1177             cache_dir => $cache,
1178             functions => {
1179             'Person::new' => { source => 'person_new', is_xs_native => 1 },
1180             'Person::name' => { source => 'person_name', is_xs_native => 1 },
1181             'Person::age' => { source => 'person_age', is_xs_native => 1 },
1182             },
1183             );
1184              
1185             my $p = Person->new('Alice', 30);
1186             say $p->name; # Alice
1187             $p->name('Bob'); # set name
1188             say $p->age; # 30
1189              
1190             =head2 Class with Inheritance
1191              
1192             my $b = XS::JIT::Builder->new;
1193            
1194             # Base class constructor
1195             $b->xs_function('animal_new')
1196             ->xs_preamble
1197             ->new_hv('hv')
1198             ->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
1199             ->raw('SV* self = newRV_noinc((SV*)hv);')
1200             ->raw('sv_bless(self, gv_stashpv(SvPV_nolen(ST(0)), GV_ADD));')
1201             ->return_sv('self')
1202             ->xs_end
1203             ->blank;
1204            
1205             # Dog constructor (overrides Animal)
1206             $b->xs_function('dog_new')
1207             ->xs_preamble
1208             ->new_hv('hv')
1209             ->hv_store('hv', 'name', 4, 'items > 1 ? newSVsv(ST(1)) : newSVpv("", 0)')
1210             ->hv_store('hv', 'breed', 5, 'items > 2 ? newSVsv(ST(2)) : newSVpv("mutt", 0)')
1211             ->raw('SV* self = newRV_noinc((SV*)hv);')
1212             ->raw('sv_bless(self, gv_stashpv("Dog", GV_ADD));')
1213             ->return_sv('self')
1214             ->xs_end;
1215            
1216             # Compile and set up inheritance
1217             XS::JIT->compile(...);
1218            
1219             package Dog;
1220             use parent -norequire => 'Animal';
1221              
1222             =head2 Ultra-fast Array-based Objects with Inline Ops
1223              
1224             For maximum performance, use array-based objects with inline ops.
1225             This bypasses XS call overhead entirely at compile time:
1226              
1227             use XS::JIT;
1228             use XS::JIT::Builder qw(:inline);
1229            
1230             # Generate op-based accessors
1231             my $b = XS::JIT::Builder->new;
1232             $b->op_ro_accessor('get_name', 0); # slot 0 is read-only
1233             $b->op_rw_accessor('age', 1); # slot 1 is read-write
1234            
1235             XS::JIT->compile(
1236             code => $b->code,
1237             name => 'Cat',
1238             cache_dir => $cache,
1239             functions => {
1240             'Cat::name' => { source => 'get_name', is_xs_native => 1 },
1241             'Cat::age' => { source => 'age', is_xs_native => 1 },
1242             },
1243             );
1244            
1245             # Register inline ops for compile-time optimization
1246             XS::JIT::Builder::inline_init();
1247             XS::JIT::Builder::inline_register(\&Cat::name, INLINE_GETTER, 0);
1248             XS::JIT::Builder::inline_register(\&Cat::age, INLINE_SETTER, 1);
1249            
1250             # Now function calls are replaced with custom ops at compile time!
1251             package Cat;
1252             sub new { bless [$_[1], $_[2]], $_[0] }
1253            
1254             package main;
1255             my $cat = Cat->new('Whiskers', 3);
1256             say $cat->name; # Inline op - no XS call overhead
1257             $cat->age(4); # Inline setter
1258              
1259             =head2 Inline Op Types
1260              
1261             The following constants are available via C<:inline> export tag:
1262              
1263             =over 4
1264              
1265             =item INLINE_NONE (0)
1266              
1267             No inlining.
1268              
1269             =item INLINE_GETTER (1)
1270              
1271             Read-only slot accessor. Replaces C<< $obj->name >> with a custom op
1272             that reads directly from C<< $obj->[slot] >>.
1273              
1274             =item INLINE_SETTER (2)
1275              
1276             Read-write slot accessor. Supports both getter and setter modes.
1277              
1278             =item INLINE_HV_GETTER (3)
1279              
1280             Read-only hash accessor (not yet implemented).
1281              
1282             =item INLINE_HV_SETTER (4)
1283              
1284             Read-write hash accessor (not yet implemented).
1285              
1286             =back
1287              
1288             =head2 Custom Op Builder Methods
1289              
1290             These methods generate code for building custom Perl ops, enabling
1291             compile-time optimization through call checkers.
1292              
1293             =head3 PP Function Builders
1294              
1295             PP (push-pop) functions are the runtime handlers for custom ops.
1296              
1297             =over 4
1298              
1299             =item pp_start($name)
1300              
1301             Start a pp function definition.
1302              
1303             $b->pp_start('pp_my_op');
1304             # Generates: OP* pp_my_op(pTHX) {
1305              
1306             =item pp_end()
1307              
1308             End a pp function with C.
1309              
1310             $b->pp_end;
1311             # Generates: return NORMAL; }
1312              
1313             =item pp_dsp()
1314              
1315             Add dSP declaration for stack access.
1316              
1317             $b->pp_dsp;
1318             # Generates: dSP;
1319              
1320             =item pp_get_self()
1321              
1322             Get the invocant from the stack without popping.
1323              
1324             $b->pp_get_self;
1325             # Generates: SV* self = TOPs;
1326              
1327             =item pp_pop_self()
1328              
1329             Pop the invocant from the stack.
1330              
1331             $b->pp_pop_self;
1332             # Generates: SV* self = POPs;
1333              
1334             =item pp_pop_sv($name)
1335              
1336             Pop an SV from the stack.
1337              
1338             $b->pp_pop_sv('value');
1339             # Generates: SV* value = POPs;
1340              
1341             =item pp_pop_nv($name)
1342              
1343             Pop a numeric value from the stack.
1344              
1345             $b->pp_pop_nv('amount');
1346             # Generates: NV amount = POPn;
1347              
1348             =item pp_pop_iv($name)
1349              
1350             Pop an integer value from the stack.
1351              
1352             $b->pp_pop_iv('count');
1353             # Generates: IV count = POPi;
1354              
1355             =item pp_get_slots()
1356              
1357             Get the slots array from self (for array-based objects).
1358              
1359             $b->pp_get_slots;
1360             # Generates: AV* slots = (AV*)SvRV(self);
1361              
1362             =item pp_slot($name, $index)
1363              
1364             Access a specific slot from the slots array.
1365              
1366             $b->pp_slot('name_sv', 0);
1367             # Generates: SV* name_sv = *av_fetch(slots, 0, 0);
1368              
1369             =item pp_return_sv($expr)
1370              
1371             Return an SV to the stack.
1372              
1373             $b->pp_return_sv('name_sv');
1374             # Generates: SETs(name_sv); RETURN;
1375              
1376             =item pp_return_nv($expr)
1377              
1378             Return a numeric value.
1379              
1380             $b->pp_return_nv('result');
1381             # Generates: SETn(result); RETURN;
1382              
1383             =item pp_return_iv($expr)
1384              
1385             Return an integer value.
1386              
1387             $b->pp_return_iv('count');
1388             # Generates: SETi(count); RETURN;
1389              
1390             =item pp_return_pv($expr)
1391              
1392             Return a string value.
1393              
1394             $b->pp_return_pv('str');
1395             # Generates: SETs(sv_2mortal(newSVpv(str, 0))); RETURN;
1396              
1397             =item pp_return()
1398              
1399             Return without modifying the stack.
1400              
1401             $b->pp_return;
1402             # Generates: return NORMAL;
1403              
1404             =back
1405              
1406             =head3 Call Checker Builders
1407              
1408             Call checkers run at compile time to replace subroutine calls with custom ops.
1409              
1410             =over 4
1411              
1412             =item ck_start($name)
1413              
1414             Start a call checker function.
1415              
1416             $b->ck_start('ck_my_method');
1417             # Generates: OP* ck_my_method(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
1418              
1419             =item ck_end()
1420              
1421             End a call checker with fallback.
1422              
1423             $b->ck_end;
1424             # Generates: return ck_entersub_args_proto_or_list(...); }
1425              
1426             =item ck_preamble()
1427              
1428             Add standard call checker preamble to extract arguments.
1429              
1430             $b->ck_preamble;
1431             # Extracts pushmark, args, method from the optree
1432              
1433             =item ck_build_unop($pp_func, $targ_expr)
1434              
1435             Build a custom unary op (one argument).
1436              
1437             $b->ck_build_unop('pp_my_getter', 'slot_num');
1438             # Generates code to create OP_CUSTOM with the specified pp function
1439              
1440             =item ck_build_binop($pp_func, $targ_expr)
1441              
1442             Build a custom binary op (two arguments).
1443              
1444             $b->ck_build_binop('pp_my_setter', 'slot_num');
1445              
1446             =item ck_fallback()
1447              
1448             Fall through to default call checking.
1449              
1450             $b->ck_fallback;
1451             # Generates: return ck_entersub_args_proto_or_list(...);
1452              
1453             =back
1454              
1455             =head3 XOP Helpers
1456              
1457             XOP (extended op) declarations register custom ops with Perl.
1458              
1459             =over 4
1460              
1461             =item xop_declare($name, $pp_func, $desc)
1462              
1463             Declare a custom op descriptor.
1464              
1465             $b->xop_declare('my_xop', 'pp_my_op', 'my custom op');
1466             # Generates static XOP declaration and registration
1467              
1468             =item register_checker($cv_expr, $ck_func, $ckobj_expr)
1469              
1470             Register a call checker for a CV.
1471              
1472             $b->register_checker('cv', 'ck_my_method', 'newSViv(slot)');
1473             # Generates: cv_set_call_checker(cv, ck_my_method, ckobj);
1474              
1475             =back
1476              
1477             =head3 Custom Op Example
1478              
1479             my $b = XS::JIT::Builder->new;
1480            
1481             # Declare the XOP
1482             $b->xop_declare('slot_getter_xop', 'pp_slot_getter', 'slot getter');
1483            
1484             # Build the pp function
1485             $b->pp_start('pp_slot_getter')
1486             ->pp_dsp
1487             ->pp_get_self
1488             ->pp_get_slots
1489             ->line('IV slot = PL_op->op_targ;')
1490             ->pp_slot('val', 'slot')
1491             ->pp_return_sv('val')
1492             ->pp_end;
1493            
1494             # Build the call checker
1495             $b->ck_start('ck_slot_getter')
1496             ->ck_preamble
1497             ->ck_build_unop('pp_slot_getter', 'SvIV(ckobj)')
1498             ->ck_end;
1499              
1500             =head2 Direct AvARRAY Access
1501              
1502             These methods generate code for ultra-fast array slot access, bypassing av_fetch/av_store.
1503             Inspired by Meow's optimized accessors.
1504              
1505             =over 4
1506              
1507             =item av_direct($result_var, $av_expr)
1508              
1509             Get direct array pointer for fast slot access.
1510              
1511             $b->av_direct('slots', '(AV*)SvRV(self)');
1512             # Generates: SV** slots = AvARRAY((AV*)SvRV(self));
1513              
1514             =item av_slot_read($result_var, $slots_var, $slot)
1515              
1516             Read a slot value with undef fallback.
1517              
1518             $b->av_slot_read('val', 'slots', 0);
1519             # Generates: SV* val = slots[0] ? slots[0] : &PL_sv_undef;
1520              
1521             =item av_slot_write($slots_var, $slot, $value)
1522              
1523             Write a value to a slot with ref counting.
1524              
1525             $b->av_slot_write('slots', 0, 'new_val');
1526             # Generates proper SvREFCNT_dec and SvREFCNT_inc
1527              
1528             =back
1529              
1530             =head2 Type Checking
1531              
1532             Generate type validation code. Constants can be exported via C<:types> tag.
1533              
1534             use XS::JIT::Builder qw(:types);
1535            
1536             $b->check_value_type('val', TYPE_ARRAYREF, undef, 'value must be an arrayref');
1537              
1538             =over 4
1539              
1540             =item check_value_type($sv, $type, $classname, $error_msg)
1541              
1542             Generate a type check with croak on failure.
1543              
1544             $b->check_value_type('ST(1)', TYPE_HASHREF, undef, 'Expected hashref');
1545             # Generates: if (!(SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVHV)) croak(...);
1546              
1547             Type constants:
1548              
1549             TYPE_ANY - No check
1550             TYPE_DEFINED - SvOK
1551             TYPE_INT - SvIOK
1552             TYPE_NUM - SvNOK or SvIOK
1553             TYPE_STR - SvPOK
1554             TYPE_REF - SvROK
1555             TYPE_ARRAYREF - SvROK + SVt_PVAV
1556             TYPE_HASHREF - SvROK + SVt_PVHV
1557             TYPE_CODEREF - SvROK + SVt_PVCV
1558             TYPE_OBJECT - sv_isobject
1559             TYPE_BLESSED - sv_derived_from($classname)
1560              
1561             =back
1562              
1563             =head2 Lazy Initialization
1564              
1565             Generate accessors that lazily initialize with a default value.
1566              
1567             =over 4
1568              
1569             =item lazy_init_dor($func_name, $attr_name, $attr_len, $default_expr, $is_mortal)
1570              
1571             Generate lazy init accessor using //= (defined-or-assign).
1572              
1573             $b->lazy_init_dor('MyClass_items', 'items', 5, 'newRV_noinc((SV*)newAV())', 0);
1574             # sub items { $self->{items} //= [] }
1575              
1576             =item lazy_init_or($func_name, $attr_name, $attr_len, $default_expr, $is_mortal)
1577              
1578             Generate lazy init accessor using ||= (or-assign).
1579              
1580             $b->lazy_init_or('MyClass_name', 'name', 4, 'newSVpvn("default", 7)', 0);
1581             # sub name { $self->{name} ||= 'default' }
1582              
1583             =item slot_lazy_init_dor($func_name, $slot, $default_expr, $is_mortal)
1584              
1585             Slot-based lazy init with //=.
1586              
1587             $b->slot_lazy_init_dor('get_cache', 2, 'newRV_noinc((SV*)newHV())', 0);
1588              
1589             =item slot_lazy_init_or($func_name, $slot, $default_expr, $is_mortal)
1590              
1591             Slot-based lazy init with ||=.
1592              
1593             =back
1594              
1595             =head2 Setter Patterns
1596              
1597             =over 4
1598              
1599             =item setter_chain($func_name, $attr_name, $attr_len)
1600              
1601             Generate a setter that returns $self for chaining.
1602              
1603             $b->setter_chain('set_name', 'name', 4);
1604             # $obj->set_name('foo')->set_age(42)
1605              
1606             =item slot_setter_chain($func_name, $slot)
1607              
1608             Slot-based setter chain.
1609              
1610             =item setter_return_value($func_name, $attr_name, $attr_len)
1611              
1612             Generate a setter that returns the value set.
1613              
1614             $b->setter_return_value('set_name', 'name', 4);
1615             # my $v = $obj->set_name('foo'); # $v is 'foo'
1616              
1617             =back
1618              
1619             =head2 Array Attribute Operations
1620              
1621             Generate methods for manipulating array attributes in hash-based objects.
1622              
1623             =over 4
1624              
1625             =item attr_push($func_name, $attr_name, $attr_len)
1626              
1627             $b->attr_push('add_item', 'items', 5);
1628             # push @{$self->{items}}, @values
1629              
1630             =item attr_pop($func_name, $attr_name, $attr_len)
1631              
1632             $b->attr_pop('pop_item', 'items', 5);
1633             # pop @{$self->{items}}
1634              
1635             =item attr_shift($func_name, $attr_name, $attr_len)
1636              
1637             $b->attr_shift('shift_item', 'items', 5);
1638             # shift @{$self->{items}}
1639              
1640             =item attr_unshift($func_name, $attr_name, $attr_len)
1641              
1642             $b->attr_unshift('prepend_item', 'items', 5);
1643             # unshift @{$self->{items}}, @values
1644              
1645             =item attr_count($func_name, $attr_name, $attr_len)
1646              
1647             $b->attr_count('item_count', 'items', 5);
1648             # scalar @{$self->{items}}
1649              
1650             =item attr_clear($func_name, $attr_name, $attr_len)
1651              
1652             $b->attr_clear('clear_items', 'items', 5);
1653             # @{$self->{items}} = ()
1654              
1655             =back
1656              
1657             =head2 Hash Attribute Operations
1658              
1659             Generate methods for manipulating hash attributes.
1660              
1661             =over 4
1662              
1663             =item attr_keys($func_name, $attr_name, $attr_len)
1664              
1665             $b->attr_keys('cache_keys', 'cache', 5);
1666             # keys %{$self->{cache}}
1667              
1668             =item attr_values($func_name, $attr_name, $attr_len)
1669              
1670             $b->attr_values('cache_values', 'cache', 5);
1671             # values %{$self->{cache}}
1672              
1673             =item attr_delete($func_name, $attr_name, $attr_len)
1674              
1675             $b->attr_delete('delete_cache', 'cache', 5);
1676             # delete $self->{cache}{$key}
1677              
1678             =item attr_hash_clear($func_name, $attr_name, $attr_len)
1679              
1680             $b->attr_hash_clear('clear_cache', 'cache', 5);
1681             # %{$self->{cache}} = ()
1682              
1683             =back
1684              
1685             =head2 Conditional DSL (Struct::Conditional Format)
1686              
1687             Generate conditional C code from declarative Perl data structures. This uses
1688             the same format as L, allowing you to define conditionals
1689             as data rather than imperative code.
1690              
1691             =over 4
1692              
1693             =item conditional(\%struct)
1694              
1695             Generate C code from a Struct::Conditional-compatible hashref. Supports
1696             C and C patterns.
1697              
1698             B
1699              
1700             $b->conditional({
1701             if => {
1702             key => 'num', # C variable to test
1703             gt => 0, # expression: greater than
1704             then => {
1705             line => 'XSRETURN_IV(1);'
1706             }
1707             },
1708             else => {
1709             then => {
1710             line => 'XSRETURN_IV(0);'
1711             }
1712             }
1713             });
1714              
1715             B
1716              
1717             $b->conditional({
1718             if => {
1719             key => 'type_sv',
1720             eq => 'int',
1721             then => { line => 'handle_int(val);' }
1722             },
1723             elsif => {
1724             key => 'type_sv',
1725             eq => 'str',
1726             then => { line => 'handle_str(val);' }
1727             },
1728             else => {
1729             then => { line => 'handle_default(val);' }
1730             }
1731             });
1732              
1733             B
1734              
1735             $b->conditional({
1736             if => { key => 'n', gt => 100, then => { return_iv => '3' } },
1737             elsif => [
1738             { key => 'n', gt => 50, then => { return_iv => '2' } },
1739             { key => 'n', gt => 0, then => { return_iv => '1' } },
1740             ],
1741             else => { then => { return_iv => '0' } }
1742             });
1743              
1744             B
1745              
1746             $b->conditional({
1747             given => {
1748             key => 'type_sv',
1749             when => {
1750             int => { line => 'XSRETURN_IV(1);' },
1751             str => { line => 'XSRETURN_IV(2);' },
1752             array => { line => 'XSRETURN_IV(3);' },
1753             default => { line => 'XSRETURN_IV(0);' }
1754             }
1755             }
1756             });
1757              
1758             B
1759              
1760             $b->conditional({
1761             given => {
1762             key => 'country',
1763             when => [
1764             { m => 'Thai', then => { return_iv => '1' } },
1765             { m => 'Indo', then => { return_iv => '2' } },
1766             ],
1767             default => { return_iv => '0' }
1768             }
1769             });
1770              
1771             B
1772              
1773             $b->conditional({
1774             if => {
1775             key => 'x',
1776             gt => 0,
1777             and => {
1778             key => 'y',
1779             gt => 0
1780             },
1781             then => { line => 'handle_positive_quadrant();' }
1782             }
1783             });
1784              
1785             $b->conditional({
1786             if => {
1787             key => 'status',
1788             eq => 'active',
1789             or => {
1790             key => 'status',
1791             eq => 'pending'
1792             },
1793             then => { line => 'process();' }
1794             }
1795             });
1796              
1797             B
1798              
1799             gt => N # SvIV(key) > N
1800             lt => N # SvIV(key) < N
1801             gte => N # SvIV(key) >= N
1802             lte => N # SvIV(key) <= N
1803             eq => 'str' # strEQ(SvPV_nolen(key), "str")
1804             ne => 'str' # !strEQ(...)
1805             m => 'pat' # strstr(SvPV_nolen(key), "pat") != NULL
1806             im => 'pat' # case-insensitive match
1807             nm => 'pat' # NOT match
1808             inm => 'pat' # case-insensitive NOT match
1809             exists => 1 # SvOK(key)
1810             true => 1 # SvTRUE(key)
1811              
1812             B
1813              
1814             then => { line => '...' } # raw C line
1815             then => { return_iv => 'N' } # XSRETURN_IV(N)
1816             then => { return_nv => 'N' } # return double
1817             then => { return_pv => '"str"' } # XSRETURN_PV(str)
1818             then => { return_sv => 'expr' } # return SV expression
1819             then => { croak => 'message' } # croak("message")
1820             then => [ {...}, {...} ] # multiple actions
1821              
1822             =back
1823              
1824             =head3 Complete Conditional Example
1825              
1826             use XS::JIT;
1827             use XS::JIT::Builder;
1828              
1829             my $b = XS::JIT::Builder->new;
1830              
1831             $b->xs_function('classify_number')
1832             ->xs_preamble
1833             ->declare_iv('num', 'SvIV(ST(0))')
1834             ->conditional({
1835             if => {
1836             key => 'num',
1837             gt => 0,
1838             then => { return_pv => '"positive"' }
1839             },
1840             elsif => {
1841             key => 'num',
1842             lt => 0,
1843             then => { return_pv => '"negative"' }
1844             },
1845             else => {
1846             then => { return_pv => '"zero"' }
1847             }
1848             })
1849             ->xs_end;
1850              
1851             XS::JIT->compile(
1852             code => $b->code,
1853             name => 'NumClass',
1854             functions => {
1855             'NumClass::classify' => { source => 'classify_number', is_xs_native => 1 }
1856             }
1857             );
1858              
1859             say NumClass::classify(42); # "positive"
1860             say NumClass::classify(-5); # "negative"
1861             say NumClass::classify(0); # "zero"
1862              
1863             =head2 Switch Statement Helper
1864              
1865             The C method provides an optimized way to generate multi-branch conditionals
1866             on a single key, avoiding Perl's hash duplicate-key limitation with C. It
1867             automatically detects whether all cases use the same comparison type and applies
1868             optimizations.
1869              
1870             =over 4
1871              
1872             =item switch($key, \@cases, [\%default])
1873              
1874             Generate optimized switch-style conditional code. The key is a C variable name,
1875             cases is an arrayref of clause hashrefs, and default is an optional hashref of
1876             actions for the default case.
1877              
1878             B
1879              
1880             $b->switch('type_str', [
1881             { eq => 'int', then => { return_iv => '1' } },
1882             { eq => 'str', then => { return_iv => '2' } },
1883             { eq => 'array', then => { return_iv => '3' } },
1884             ], { return_iv => '0' });
1885              
1886             This generates optimized C code that:
1887              
1888             =over 4
1889              
1890             =item * Caches C once at the start
1891              
1892             =item * Uses C with length pre-check for efficient string comparison
1893              
1894             =item * Generates an if/elsif/else chain
1895              
1896             =back
1897              
1898             B
1899              
1900             $b->switch('code', [
1901             { eq => 200, then => { return_pv => '"OK"' } },
1902             { eq => 404, then => { return_pv => '"Not Found"' } },
1903             { eq => 500, then => { return_pv => '"Server Error"' } },
1904             ], { return_pv => '"Unknown"' });
1905              
1906             For numeric comparisons, this generates code that:
1907              
1908             =over 4
1909              
1910             =item * Caches C once at the start
1911              
1912             =item * Uses direct numeric comparison
1913              
1914             =back
1915              
1916             B
1917              
1918             $b->switch('score', [
1919             { gte => 90, then => { return_pv => '"A"' } },
1920             { gte => 80, then => { return_pv => '"B"' } },
1921             { gte => 70, then => { return_pv => '"C"' } },
1922             { gte => 60, then => { return_pv => '"D"' } },
1923             ], { return_pv => '"F"' });
1924              
1925             B
1926              
1927             $b->switch('status_code', [
1928             { gte => 200, lte => 299, then => { return_pv => '"success"' } },
1929             { gte => 300, lte => 399, then => { return_pv => '"redirect"' } },
1930             { gte => 400, lte => 499, then => { return_pv => '"client_error"' } },
1931             { gte => 500, lte => 599, then => { return_pv => '"server_error"' } },
1932             ], { return_pv => '"unknown"' });
1933              
1934             =back
1935              
1936             =head3 Complete Switch Example
1937              
1938             use XS::JIT;
1939             use XS::JIT::Builder;
1940              
1941             my $b = XS::JIT::Builder->new;
1942              
1943             $b->xs_function('get_type_id')
1944             ->xs_preamble
1945             ->declare_sv('type', 'ST(0)')
1946             ->switch('type', [
1947             { eq => 'integer', then => { return_iv => '1' } },
1948             { eq => 'string', then => { return_iv => '2' } },
1949             { eq => 'float', then => { return_iv => '3' } },
1950             { eq => 'boolean', then => { return_iv => '4' } },
1951             { eq => 'array', then => { return_iv => '5' } },
1952             { eq => 'hash', then => { return_iv => '6' } },
1953             ], { return_iv => '0' })
1954             ->xs_end;
1955              
1956             XS::JIT->compile(
1957             code => $b->code,
1958             name => 'TypeID',
1959             functions => {
1960             'TypeID::get' => { source => 'get_type_id', is_xs_native => 1 }
1961             }
1962             );
1963              
1964             say TypeID::get('integer'); # 1
1965             say TypeID::get('string'); # 2
1966             say TypeID::get('unknown'); # 0
1967              
1968             =head3 Switch vs Conditional
1969              
1970             Use C when:
1971              
1972             =over 4
1973              
1974             =item * You have multiple conditions on the same key
1975              
1976             =item * All cases compare the same variable
1977              
1978             =item * You want automatic optimization for string/numeric comparisons
1979              
1980             =item * You want cleaner syntax without repeating the key
1981              
1982             =back
1983              
1984             Use C when:
1985              
1986             =over 4
1987              
1988             =item * Conditions involve different variables
1989              
1990             =item * You need complex nested AND/OR logic
1991              
1992             =item * You're matching patterns with C/C
1993              
1994             =item * You prefer the Struct::Conditional format
1995              
1996             =back
1997              
1998             =head2 Bulk Code Generators
1999              
2000             These methods generate multiple related functions from a single declarative specification.
2001              
2002             =head3 Enum/Constant Generator
2003              
2004             =over 4
2005              
2006             =item enum($name, \@values, [\%options])
2007              
2008             Generate a set of related constants with validation functions. This is useful for
2009             creating type-safe enumerated values with minimal boilerplate.
2010              
2011             $b->enum('Status', [qw(PENDING ACTIVE INACTIVE DELETED)]);
2012              
2013             B
2014              
2015             =over 4
2016              
2017             =item * C - returns 0
2018              
2019             =item * C - returns 1
2020              
2021             =item * C - returns 2
2022              
2023             =item * C - returns 3
2024              
2025             =item * C - returns true if value is valid enum (0-3)
2026              
2027             =item * C - returns string name for numeric value
2028              
2029             =back
2030              
2031             B
2032              
2033             =over 4
2034              
2035             =item * C - Starting numeric value (default: 0)
2036              
2037             =item * C - Prefix for constant names (default: uc($name) . '_')
2038              
2039             =back
2040              
2041             # Custom start value
2042             $b->enum('Priority', [qw(LOW MEDIUM HIGH CRITICAL)], { start => 1 });
2043             # Generates: PRIORITY_LOW => 1, PRIORITY_MEDIUM => 2, etc.
2044              
2045             # Custom prefix
2046             $b->enum('Color', [qw(RED GREEN BLUE)], { prefix => 'CLR_' });
2047             # Generates: CLR_RED => 0, CLR_GREEN => 1, CLR_BLUE => 2
2048              
2049             =item enum_functions($name, $package)
2050              
2051             Get a hashref of function definitions for use with Ccompile()>.
2052             This returns the correct mapping for all functions generated by C.
2053              
2054             my $b = XS::JIT::Builder->new;
2055             $b->enum('Status', [qw(PENDING ACTIVE INACTIVE DELETED)]);
2056              
2057             my $functions = $b->enum_functions('Status', 'MyApp::Status');
2058              
2059             XS::JIT->compile(
2060             code => $b->code,
2061             name => 'MyApp::Status',
2062             functions => $functions,
2063             );
2064              
2065             # Now you can use:
2066             print MyApp::Status::STATUS_PENDING(); # 0
2067             print MyApp::Status::STATUS_ACTIVE(); # 1
2068             print MyApp::Status::is_valid_status(2); # true
2069             print MyApp::Status::status_name(1); # "ACTIVE"
2070              
2071             =back
2072              
2073             =head3 Complete Enum Example
2074              
2075             use XS::JIT;
2076             use XS::JIT::Builder;
2077             use File::Temp qw(tempdir);
2078              
2079             my $cache_dir = tempdir(CLEANUP => 1);
2080             my $b = XS::JIT::Builder->new;
2081              
2082             # Generate enum for HTTP status categories
2083             $b->enum('HttpCategory', [qw(INFORMATIONAL SUCCESS REDIRECT CLIENT_ERROR SERVER_ERROR)]);
2084              
2085             XS::JIT->compile(
2086             code => $b->code,
2087             name => 'HTTP',
2088             cache_dir => $cache_dir,
2089             functions => $b->enum_functions('HttpCategory', 'HTTP'),
2090             );
2091              
2092             # Use the generated functions
2093             use constant {
2094             HTTP_INFORMATIONAL => HTTP::HTTPCATEGORY_INFORMATIONAL(),
2095             HTTP_SUCCESS => HTTP::HTTPCATEGORY_SUCCESS(),
2096             HTTP_REDIRECT => HTTP::HTTPCATEGORY_REDIRECT(),
2097             HTTP_CLIENT_ERROR => HTTP::HTTPCATEGORY_CLIENT_ERROR(),
2098             HTTP_SERVER_ERROR => HTTP::HTTPCATEGORY_SERVER_ERROR(),
2099             };
2100              
2101             sub categorize_status {
2102             my ($code) = @_;
2103             return HTTP_INFORMATIONAL if $code >= 100 && $code < 200;
2104             return HTTP_SUCCESS if $code >= 200 && $code < 300;
2105             return HTTP_REDIRECT if $code >= 300 && $code < 400;
2106             return HTTP_CLIENT_ERROR if $code >= 400 && $code < 500;
2107             return HTTP_SERVER_ERROR if $code >= 500 && $code < 600;
2108             return -1;
2109             }
2110              
2111             my $cat = categorize_status(404);
2112             print HTTP::is_valid_httpcategory($cat) ? "Valid" : "Invalid"; # "Valid"
2113             print HTTP::httpcategory_name($cat); # "CLIENT_ERROR"
2114              
2115             =head3 Enum Use Cases
2116              
2117             =over 4
2118              
2119             =item * B - Track record states (pending, active, deleted)
2120              
2121             =item * B - Define valid states with validation
2122              
2123             =item * B - Type-safe config values
2124              
2125             =item * B - HTTP status codes, error codes, message types
2126              
2127             =back
2128              
2129             =head3 Memoization Wrapper
2130              
2131             Generate cached versions of expensive methods. The cache is stored in an object
2132             attribute and can be cleared at any time.
2133              
2134             =over 4
2135              
2136             =item memoize($func_name, [\%options])
2137              
2138             Generate a memoized wrapper for a method. The original method must be renamed
2139             to C<_orig_$func_name> before compilation. The wrapper checks a cache before
2140             calling the original, stores results, and optionally supports TTL expiration.
2141              
2142             $b->memoize('expensive_calc');
2143              
2144             # With options
2145             $b->memoize('fetch_data', {
2146             cache => '_fetch_cache', # custom cache attribute name
2147             ttl => 300, # expire after 300 seconds
2148             });
2149              
2150             B
2151              
2152             =over 4
2153              
2154             =item * C<$func_name()> - The memoized wrapper that checks cache first
2155              
2156             =item * C - Clears the cache for this function
2157              
2158             =back
2159              
2160             B
2161              
2162             =over 4
2163              
2164             =item * C - Name of the hash attribute to store cache (default: '_memoize_cache')
2165              
2166             =item * C - Time-to-live in seconds. If set, cached values expire after this duration.
2167              
2168             =back
2169              
2170             B
2171              
2172             The cache key is built by concatenating all arguments (except $self) with
2173             ASCII field separator (0x1C). This handles most argument types correctly.
2174              
2175             B
2176              
2177             =over 4
2178              
2179             =item 1. Arguments are joined to form a cache key
2180              
2181             =item 2. Cache is checked for existing value
2182              
2183             =item 3. If TTL is set, timestamp is verified
2184              
2185             =item 4. On cache miss, calls C<$self-E_orig_$func_name(@args)>
2186              
2187             =item 5. Result is stored in cache and returned
2188              
2189             =back
2190              
2191             =item memoize_functions($func_name, $package)
2192              
2193             Get a hashref of function definitions for use with Ccompile()>.
2194              
2195             my $functions = $b->memoize_functions('expensive_calc', 'MyClass');
2196              
2197             XS::JIT->compile(
2198             code => $b->code,
2199             name => 'MyClass',
2200             functions => $functions,
2201             );
2202              
2203             =back
2204              
2205             =head3 Complete Memoization Example
2206              
2207             use XS::JIT;
2208             use XS::JIT::Builder;
2209             use File::Temp qw(tempdir);
2210              
2211             my $cache_dir = tempdir(CLEANUP => 1);
2212             my $b = XS::JIT::Builder->new;
2213              
2214             # Generate memoized wrapper for 'compute' method
2215             $b->memoize('compute', { ttl => 60 }); # 60 second TTL
2216              
2217             XS::JIT->compile(
2218             code => $b->code,
2219             name => 'Calculator',
2220             cache_dir => $cache_dir,
2221             functions => $b->memoize_functions('compute', 'Calculator'),
2222             );
2223              
2224             package Calculator;
2225              
2226             # The original method - renamed with _orig_ prefix
2227             sub _orig_compute {
2228             my ($self, $x, $y) = @_;
2229             # Expensive calculation...
2230             sleep 1; # simulate slow operation
2231             return $x * $y;
2232             }
2233              
2234             sub new { bless {}, shift }
2235              
2236             package main;
2237              
2238             my $calc = Calculator->new;
2239              
2240             # First call - slow (calls _orig_compute)
2241             my $result1 = $calc->compute(6, 7); # 42
2242              
2243             # Second call - instant (from cache)
2244             my $result2 = $calc->compute(6, 7); # 42 (cached)
2245              
2246             # Different args - slow again
2247             my $result3 = $calc->compute(3, 4); # 12
2248              
2249             # Clear cache
2250             $calc->clear_compute_cache;
2251              
2252             # Next call will be slow again
2253             my $result4 = $calc->compute(6, 7); # 42 (recalculated)
2254              
2255             =head3 Memoization Use Cases
2256              
2257             =over 4
2258              
2259             =item * B - Cache expensive DB lookups
2260              
2261             =item * B - Cache external API calls with TTL
2262              
2263             =item * B - Cache results of CPU-intensive calculations
2264              
2265             =item * B - Cache parsed config values
2266              
2267             =back
2268              
2269             =head1 SEE ALSO
2270              
2271             L - The main JIT compiler module
2272              
2273             The C API is available in F for direct use from XS code.
2274              
2275             =head1 AUTHOR
2276              
2277             LNATION
2278              
2279             =head1 LICENSE
2280              
2281             This library is free software; you can redistribute it and/or modify
2282             it under the same terms as Perl itself.
2283              
2284             =cut
2285              
2286             1;