File Coverage

Wanted.xs
Criterion Covered Total %
statement 281 332 84.6
branch 210 316 66.4
condition n/a
subroutine n/a
pod n/a
total 491 648 75.7


line stmt bran cond sub pod time code
1             /*
2             *----------------------------------------------------------------------------
3             * Wanted - ~/Wanted.xs
4             * Version v0.1.0
5             * Copyright(c) 2025 DEGUEST Pte. Ltd.
6             * Original author: Robin Houston
7             * Modified by: Jacques Deguest <jack@deguest.jp>
8             * Created 2025/05/16
9             * Modified 2025/05/24
10             * All rights reserved
11             *
12             * This program is free software; you can redistribute it and/or modify it
13             * under the same terms as Perl itself.
14             *
15             * Description:
16             * XS implementation for the Wanted Perl module, providing low-level
17             * functions to inspect and manipulate Perl's context stack and op tree.
18             *----------------------------------------------------------------------------
19             */
20             #include "EXTERN.h"
21             #include "perl.h"
22             #include "XSUB.h"
23              
24             /* Between 5.9.1 and 5.9.2 the retstack was removed, and the return op is now stored on the cxstack. */
25             #define HAS_RETSTACK (\
26             PERL_REVISION < 5 || \
27             (PERL_REVISION == 5 && PERL_VERSION < 9) || \
28             (PERL_REVISION == 5 && PERL_VERSION == 9 && PERL_SUBVERSION < 2) \
29             )
30              
31             /* Define PERL_VERSION_GE, PERL_VERSION_LT, PERL_VERSION_LE if not already defined (Perl < 5.24.0) */
32             #ifndef PERL_VERSION_GE
33             #define PERL_VERSION_GE(major, minor, patch) \
34             (PERL_REVISION > (major) || \
35             (PERL_REVISION == (major) && (PERL_VERSION > (minor) || \
36             (PERL_VERSION == (minor) && PERL_SUBVERSION >= (patch)))))
37             #endif
38              
39             #ifndef PERL_VERSION_LT
40             #define PERL_VERSION_LT(major, minor, patch) \
41             (PERL_REVISION < (major) || \
42             (PERL_REVISION == (major) && (PERL_VERSION < (minor) || \
43             (PERL_VERSION == (minor) && PERL_SUBVERSION < (patch)))))
44             #endif
45              
46             #ifndef PERL_VERSION_LE
47             #define PERL_VERSION_LE(major, minor, patch) \
48             (PERL_REVISION < (major) || \
49             (PERL_REVISION == (major) && (PERL_VERSION < (minor) || \
50             (PERL_VERSION == (minor) && PERL_SUBVERSION <= (patch)))))
51             #endif
52              
53             #define PERL_HAS_FREE_OS_BUG (PERL_VERSION_GE(5, 22, 0) && PERL_VERSION_LE(5, 24, 0))
54              
55             #define ENABLE_DOUBLE_RETURN_HACKS 1
56              
57             /* After 5.10, the CxLVAL macro was added. */
58             #ifndef CxLVAL
59             # define CxLVAL(cx) cx->blk_sub.lval
60             #endif
61              
62             #ifndef OpSIBLING
63             # define OpSIBLING(o) o->op_sibling
64             #endif
65              
66             /* Stolen from B.xs */
67             #ifdef PERL_OBJECT
68             #undef PL_op_name
69             #undef PL_opargs
70             #undef PL_op_desc
71             #define PL_op_name (get_op_names())
72             #define PL_opargs (get_opargs())
73             #define PL_op_desc (get_op_descs())
74             #endif
75              
76             /* Define oplist and numop types */
77             #define OPLIST_MAX 50
78             typedef struct {
79             U16 numop_num;
80             OP* numop_op;
81             } numop;
82              
83             typedef struct {
84             U16 length;
85             numop ops[OPLIST_MAX];
86             } oplist;
87              
88             #define new_oplist (oplist*) malloc(sizeof(oplist))
89             #define init_oplist(l) l->length = 0
90              
91             /* Function declarations */
92             numop* lastnumop(oplist* l);
93             OP* lastop(oplist* l);
94             oplist* pushop(oplist* l, OP* o, U16 i);
95             oplist* find_ancestors_from(OP* start, OP* next, oplist* l);
96             I32 count_list (OP* parent, OP* returnop);
97             I32 count_slice (OP* o);
98              
99             /* Stolen from pp_ctl.c (with modifications) */
100             /*
101             * dopoptosub_at - Scans the given context stack for the nearest subroutine or format block.
102             *
103             * Arguments:
104             * PERL_CONTEXT *cxstk - The context stack to search.
105             * I32 startingblock - The starting index from which to scan downward.
106             *
107             * Return:
108             * I32 - The index of the found subroutine or format block, or -1 if none is found.
109             *
110             * Description:
111             * This is a helper function to locate the closest CXt_SUB or CXt_FORMAT in a given stack.
112             * It is used in walking the context stack and is central to call depth resolution.
113             *
114             * Internal:
115             * Used by dopoptosub() to implement context stack traversal.
116             */
117             I32
118 9479           dopoptosub_at(pTHX_ PERL_CONTEXT *cxstk, I32 startingblock)
119             {
120             dTHR;
121             I32 i;
122             PERL_CONTEXT *cx;
123 9479 50         if (!cxstk) return -1;
124 14489 100         for (i = startingblock; i >= 0; i--)
125             {
126 12976           cx = &cxstk[i];
127 12976 100         switch (CxTYPE(cx))
128             {
129 5010           default:
130 5010           continue;
131 7966           case CXt_SUB:
132             case CXt_FORMAT:
133             DEBUG_l( Perl_deb(aTHX_ "(Found sub #%ld)\n", (long)i));
134 7966           return i;
135             }
136             }
137 1513           return i;
138             }
139              
140             /*
141             * dopoptosub - Convenience wrapper around dopoptosub_at using the current cxstack.
142             *
143             * Arguments:
144             * I32 startingblock - Start index into cxstack to scan for a subroutine context.
145             *
146             * Return:
147             * I32 - The index of the found subroutine or format block, or -1 if not found.
148             *
149             * Description:
150             * This function uses the current 'cxstack' and is typically used to locate
151             * the active subroutine context for the current execution stack.
152             *
153             * Internal:
154             * Used by upcontext() and upcontext_plus() to traverse the context stack.
155             */
156             I32
157 1737           dopoptosub(pTHX_ I32 startingblock)
158             {
159             dTHR;
160 1737 50         if (!cxstack) return -1;
161 1737           return dopoptosub_at(aTHX_ cxstack, startingblock);
162             }
163              
164             /*
165             * upcontext - Retrieves the subroutine context 'count' levels up the stack.
166             *
167             * Arguments:
168             * I32 count - The number of subroutine contexts to go up.
169             *
170             * Return:
171             * PERL_CONTEXT* - Pointer to the located context or NULL if not found.
172             *
173             * Description:
174             * This searches up through the Perl call stack, accounting for DB::sub wrappers,
175             * and returns the context frame corresponding to the requested call depth.
176             *
177             * Internal:
178             * Used by want_gimme(), want_lvalue(), find_return_op(), and other context-inspection functions.
179             */
180             PERL_CONTEXT*
181 1009           upcontext(pTHX_ I32 count)
182             {
183 1009           PERL_SI *top_si = PL_curstackinfo;
184 1009           I32 cxix = dopoptosub(aTHX_ cxstack_ix);
185             PERL_CONTEXT *cx;
186 1009           PERL_CONTEXT *ccstack = cxstack;
187             I32 dbcxix;
188              
189 1009 50         if (!top_si || !ccstack || cxix < 0)
    50          
    50          
190             {
191 0           return (PERL_CONTEXT *)0;
192             }
193              
194             for (;;)
195             {
196 4269 100         while (cxix < 0 && top_si->si_type != PERLSI_MAIN)
    50          
197             {
198 0           top_si = top_si->si_prev;
199 0 0         if (!top_si)
200             {
201 0           return (PERL_CONTEXT *)0;
202             }
203 0           ccstack = top_si->si_cxstack;
204 0           cxix = dopoptosub_at(aTHX_ ccstack, top_si->si_cxix);
205             }
206 4269 100         if (cxix < 0)
207             {
208 5           return (PERL_CONTEXT *)0;
209             }
210 4264 50         if (PL_DBsub && cxix >= 0 &&
    50          
211 4264 50         ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))
212 0           count++;
213 4264 100         if (!count--)
214 1004           break;
215 3260           cxix = dopoptosub_at(aTHX_ ccstack, cxix - 1);
216             }
217 1004           cx = &ccstack[cxix];
218 1004 50         if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
    0          
219             {
220 1004           dbcxix = dopoptosub_at(aTHX_ ccstack, cxix - 1);
221 1004 50         if (PL_DBsub && dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub))
    100          
    50          
222             {
223 0           cx = &ccstack[dbcxix];
224             }
225             }
226 1004           return cx;
227             }
228              
229             /*
230             * upcontext_plus - Retrieves the block or loop context enclosing the subroutine at the given depth.
231             *
232             * Arguments:
233             * I32 count - Number of subroutine levels up to inspect.
234             * bool end_of_block - Whether to return the context at the end of the enclosing block.
235             *
236             * Return:
237             * PERL_CONTEXT* - The identified context or NULL.
238             *
239             * Description:
240             * This is a more sophisticated version of 'upcontext', considering debugger issues,
241             * tie/tied ops, and whether the block context is required instead of the sub context.
242             *
243             * Internal:
244             * Used by find_start_cop() to locate the starting context op for a subroutine or block.
245             */
246             PERL_CONTEXT*
247 728           upcontext_plus(pTHX_ I32 count, bool end_of_block)
248             {
249 728           PERL_SI *top_si = PL_curstackinfo;
250 728           I32 cxix = dopoptosub(aTHX_ cxstack_ix);
251             PERL_CONTEXT *cx, *tcx;
252 728           PERL_CONTEXT *ccstack = cxstack;
253             I32 dbcxix, i;
254             bool debugger_trouble;
255              
256 728 50         if (!top_si || !ccstack || cxix < 0)
    50          
    50          
257             {
258 0           return (PERL_CONTEXT *)0;
259             }
260              
261 728 50         if (PL_op && (PL_op->op_type == OP_TIE || PL_op->op_type == OP_TIED))
    50          
    50          
262             {
263             I32 i;
264 0 0         for (i = cxix; i >= 0; i--)
265             {
266 0           cx = &ccstack[i];
267 0 0         if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_BLOCK)
    0          
268             {
269 0 0         OP *op = cx->blk_oldcop ? (OP*)cx->blk_oldcop : PL_op;
270 0 0         if (op && (op->op_type == OP_LIST || op->op_type == OP_AASSIGN))
    0          
    0          
271             {
272 0           cx->blk_gimme = G_ARRAY;
273             }
274 0           return cx;
275             }
276             }
277 0           return (PERL_CONTEXT *)0;
278             }
279              
280             for (;;)
281             {
282 3480 100         while (cxix < 0 && top_si->si_type != PERLSI_MAIN)
    50          
283             {
284 0           top_si = top_si->si_prev;
285 0 0         if (!top_si)
286             {
287 0           return (PERL_CONTEXT *)0;
288             }
289 0           ccstack = top_si->si_cxstack;
290 0           cxix = dopoptosub_at(aTHX_ ccstack, top_si->si_cxix);
291             }
292 3480 100         if (cxix < 0)
293             {
294 2           return (PERL_CONTEXT *)0;
295             }
296 3478 50         if (PL_DBsub && cxix >= 0 &&
    50          
297 3478 50         ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))
298 0           count++;
299 3478 100         if (!count--)
300 726           break;
301 2752           cxix = dopoptosub_at(aTHX_ ccstack, cxix - 1);
302             }
303 726           cx = &ccstack[cxix];
304 726 50         if (CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT)
    0          
305             {
306 726           dbcxix = dopoptosub_at(aTHX_ ccstack, cxix - 1);
307 726 50         if (PL_DBsub && dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub))
    100          
    50          
308             {
309 0           cxix = dbcxix;
310 0           cx = &ccstack[dbcxix];
311             }
312             }
313              
314 726           debugger_trouble = (cx->blk_oldcop->op_type == OP_DBSTATE);
315              
316 1348 100         for (i = cxix-1; i>=0 ; i--)
317             {
318 832           tcx = &ccstack[i];
319 832           switch (CxTYPE(tcx))
320             {
321 599           case CXt_BLOCK:
322 599 50         if (debugger_trouble && i > 0) return tcx;
    0          
323             default:
324 622           continue;
325             #ifdef CXt_LOOP_PLAIN
326 114           case CXt_LOOP_PLAIN:
327             #endif
328             #ifdef CXt_LOOP_FOR
329             case CXt_LOOP_FOR:
330             #endif
331             #ifdef CXt_LOOP_LIST
332             case CXt_LOOP_LIST:
333             #endif
334             #ifdef CXt_LOOP_ARY
335             case CXt_LOOP_ARY:
336             #endif
337             #ifdef CXt_LOOP
338             case CXt_LOOP:
339             #endif
340 114           return tcx;
341 96           case CXt_SUB:
342             case CXt_FORMAT:
343 96           return cx;
344             }
345             }
346 516 100         return ((end_of_block && cxix > 1) ? &ccstack[cxix-1] : cx);
    50          
347             }
348              
349             /*
350             * want_gimme - Returns the context type (void, scalar, or array) at the given call stack level.
351             *
352             * Arguments:
353             * I32 uplevel - The number of call frames up to check.
354             *
355             * Return:
356             * U8 - One of G_VOID, G_SCALAR, or G_ARRAY.
357             *
358             * Description:
359             * This uses the PERL_CONTEXT retrieved by 'upcontext' to determine the evaluation context
360             * of the caller. It is a low-level helper for functions like wantarray_up().
361             *
362             * Internal:
363             * Used by wantarray_up(), want_count(), and Perl-side context inspection.
364             */
365             U8
366 96           want_gimme (I32 uplevel)
367             {
368 96           PERL_CONTEXT* cx = upcontext(aTHX_ uplevel);
369 96 100         if (!cx) return G_VOID;
370 95           return cx->blk_gimme;
371             }
372              
373             /*
374             * lastnumop - Retrieves the last meaningful 'numop' from an 'oplist'.
375             *
376             * Arguments:
377             * oplist* l - Pointer to an 'oplist' structure containing a sequence of 'numop' entries.
378             *
379             * Return:
380             * numop* - A pointer to the last 'numop' whose op is not of type 'OP_NULL' or 'OP_SCOPE',
381             * or NULL if no such entry exists.
382             *
383             * Description:
384             * This function scans backward through the list of 'numop' entries and returns the last
385             * one that corresponds to a significant operation. It is used to find the operative
386             * instruction before a return or assignment analysis.
387             *
388             * Internal:
389             * Used by 'want_assign()' to determine the final operational node before returning values.
390             */
391             numop*
392 23           lastnumop(oplist* l)
393             {
394             U16 i;
395             numop* ret;
396              
397 23 50         if (!l) return (numop*)0;
398 23           i = l->length;
399 25 50         while (i-- > 0)
400             {
401 25           ret = &(l->ops)[i];
402 25 100         if (ret->numop_op->op_type != OP_NULL && ret->numop_op->op_type != OP_SCOPE)
    50          
403             {
404 23           return ret;
405             }
406             }
407 0           return (numop*)0;
408             }
409              
410             /*
411             * lastop - Returns the last significant OP from a given oplist.
412             *
413             * Arguments:
414             * oplist* l - The list of operations to search.
415             *
416             * Return:
417             * OP* - The last non-NULL, non-SCOPE, non-LEAVE op, or Nullop if none found.
418             *
419             * Description:
420             * This function scans backwards through an oplist to find the last significant operation,
421             * ignoring NULL, SCOPE, and LEAVE ops. It is used to determine the most relevant op at
422             * the end of an op chain, typically for context or assignment analysis.
423             *
424             * Internal:
425             * Used by parent_op() to identify the final operation in an op chain.
426             */
427             OP*
428 632           lastop(oplist* l)
429             {
430             U16 i;
431             OP* ret;
432              
433 632 100         if (!l) return Nullop;
434 565           i = l->length;
435 834 100         while (i-- > 0)
436             {
437 779           ret = (l->ops)[i].numop_op;
438 779 100         if (ret->op_type != OP_NULL
439 599 50         && ret->op_type != OP_SCOPE
440 599 100         && ret->op_type != OP_LEAVE)
441             {
442 510           return ret;
443             }
444             }
445 55           free(l);
446 55           return Nullop;
447             }
448              
449             /*
450             * pushop - Adds an operation to an oplist with an associated index.
451             *
452             * Arguments:
453             * oplist* l - The oplist to modify.
454             * OP* o - The op to push.
455             * U16 i - The op’s index or position.
456             *
457             * Return:
458             * oplist* - The modified list.
459             *
460             * Description:
461             * This utility is used during op tree traversal to maintain a list of encountered operations.
462             *
463             * Internal:
464             * Used by find_ancestors_from() to build the list of parent ops.
465             */
466             oplist*
467 2833           pushop(oplist* l, OP* o, U16 i)
468             {
469 2833           I16 len = l->length;
470 2833 100         if (o && len < OPLIST_MAX)
    50          
471             {
472 2177           ++ l->length;
473 2177           l->ops[len].numop_op = o;
474 2177           l->ops[len].numop_num = -1;
475             }
476 2833 100         if (len > 0)
477 2177           l->ops[len-1].numop_num = i;
478              
479 2833           return l;
480             }
481              
482             /*
483             * find_ancestors_from - Recursively traverses an op tree to find a path to a target op.
484             *
485             * Arguments:
486             * OP* start - Starting op for the tree walk.
487             * OP* next - Target op to find.
488             * oplist* l - The oplist to accumulate ops into (can be NULL).
489             *
490             * Return:
491             * oplist* - A list of parent ops leading to the target op, or NULL if not found.
492             *
493             * Description:
494             * This function recursively traverses the op tree starting from 'start' to find a path
495             * to the 'next' op, accumulating parent ops in an oplist. It is used to trace a path
496             * through the abstract syntax tree (AST) from a COP to a return op.
497             *
498             * Notes:
499             * The caller is responsible for freeing the oplist if the function returns NULL.
500             *
501             * Internal:
502             * Used by ancestor_ops() to build the list of ancestor ops for context analysis.
503             */
504             oplist*
505 2833           find_ancestors_from(OP* start, OP* next, oplist* l)
506             {
507             OP *o, *p;
508 2833           U16 cn = 0;
509             U16 ll;
510 2833           bool outer_call = FALSE;
511              
512 2833 50         if (!start || !next)
    50          
513             {
514             /* Do not free l here; let the caller handle it */
515 0           return (oplist*)0;
516             }
517              
518 2833 100         if (!l)
519             {
520 656           outer_call = TRUE;
521 656           l = new_oplist;
522 656           init_oplist(l);
523 656           ll = 0;
524             }
525 2177           else ll = l->length;
526              
527 5432 100         for (o = start; o; p = o, o = OpSIBLING(o), ++cn)
    100          
528             {
529 4839 100         if (o->op_type == OP_ENTERSUB && o->op_next == next)
    100          
530 656           return pushop(l, Nullop, cn);
531              
532 4183 100         if (o->op_flags & OPf_KIDS)
533             {
534 2177           U16 ll = l->length;
535            
536 2177           pushop(l, o, cn);
537 2177 100         if (find_ancestors_from(cUNOPo->op_first, next, l))
538 1584           return l;
539             else
540 593           l->length = ll;
541             }
542             }
543             /* Do not free l here; let the caller handle it */
544 593           return (oplist*)0;
545             }
546              
547             /*
548             * find_return_op - Resolves the return OP for the subroutine at a given depth.
549             *
550             * Arguments:
551             * I32 uplevel - The number of frames up to inspect.
552             *
553             * Return:
554             * OP* - The op that is used to return from the subroutine, or Nullop if not found.
555             *
556             * Description:
557             * This inspects the current cxstack or PL_retstack to find the return point for a sub.
558             *
559             * Internal:
560             * Used by ancestor_ops() to determine the return op for context analysis.
561             */
562             OP*
563 728           find_return_op(pTHX_ I32 uplevel)
564             {
565 728           PERL_CONTEXT *cx = upcontext(aTHX_ uplevel);
566 728 100         if (!cx)
567             {
568 2           return Nullop;
569             }
570             #if HAS_RETSTACK
571             return PL_retstack[cx->blk_oldretsp - 1];
572             #else
573 726           return cx->blk_sub.retop;
574             #endif
575             }
576              
577             /*
578             * find_start_cop - Returns the start COP (context op) for the subroutine frame.
579             *
580             * Arguments:
581             * I32 uplevel - Call stack depth to inspect.
582             * bool end_of_block - If true, return the enclosing block cop.
583             *
584             * Return:
585             * OP* - The starting COP for the sub or block context, or Nullop if not found.
586             *
587             * Description:
588             * This function determines the starting COP (context op) for a subroutine or block
589             * at the specified call stack depth, helping to identify where execution begins.
590             *
591             * Internal:
592             * Used by ancestor_ops() to find the starting point for op tree traversal.
593             */
594             OP*
595 728           find_start_cop(pTHX_ I32 uplevel, bool end_of_block)
596             {
597 728           PERL_CONTEXT* cx = upcontext_plus(aTHX_ uplevel, end_of_block);
598 728 100         if (!cx)
599             {
600 2           return Nullop;
601             }
602 726           return (OP*) cx->blk_oldcop;
603             }
604              
605             /*
606             * ancestor_ops - Produces a list of ancestor ops from sub start to return.
607             *
608             * Arguments:
609             * I32 uplevel - Stack level to inspect.
610             * OP** return_op_out - Optional pointer to capture return op.
611             *
612             * Return:
613             * oplist* - A list of operations between sub entry and return, or NULL if not found.
614             *
615             * Description:
616             * This function walks the op tree using 'find_start_cop' and 'find_return_op',
617             * storing the trace path in an oplist. It is used to analyse the operations
618             * between a subroutine's entry and return points.
619             *
620             * Notes:
621             * The caller is responsible for freeing the returned oplist.
622             *
623             * Internal:
624             * Used by want_boolean() and want_assign() for context analysis.
625             */
626             oplist*
627 728           ancestor_ops (I32 uplevel, OP** return_op_out)
628             {
629 728           OP* return_op = find_return_op(aTHX_ uplevel);
630 728           OP* start_cop = find_start_cop(aTHX_ uplevel,
631 728 100         return_op ? return_op->op_type == OP_LEAVE : FALSE);
    100          
632              
633 728 100         if (!return_op || !start_cop)
    50          
634             {
635 72 100         if (return_op_out) *return_op_out = Nullop;
636 72           return (oplist*)0;
637             }
638              
639 656 100         if (return_op_out)
640 588           *return_op_out = return_op;
641              
642             /* return find_ancestors_from(start_cop, return_op, 0); */
643 656           oplist* result = find_ancestors_from(start_cop, return_op, 0);
644 656 50         if (!result)
645             {
646             /* Free the oplist if find_ancestors_from allocated it but failed */
647 0           free(result); // This will be a no-op since result is NULL
648 0           return (oplist*)0;
649             }
650 656           return result;
651             }
652              
653             /*
654             * parent_op - Retrieves the parent OP of the current OP in the call stack.
655             *
656             * Arguments:
657             * I32 uplevel - Stack level to begin inspection.
658             * OP **retop - A pointer to receive the resolved OP.
659             *
660             * Return:
661             * OP* - The parent operation at the given level.
662             *
663             * Description:
664             * This walks the OP tree upward from the caller’s stack frame to find the relevant parent.
665             *
666             * Internal:
667             * Used by parent_op_name() and first_multideref_type().
668             */
669             OP*
670 632           parent_op (I32 uplevel, OP** return_op_out)
671             {
672 632           return lastop(ancestor_ops(uplevel, return_op_out));
673             }
674              
675             /*
676             * count_slice - Calculates the number of elements in a slice op.
677             *
678             * Arguments:
679             * OP* o - The slice op (e.g., OP_HSLICE or OP_ASLICE).
680             *
681             * Return:
682             * I32 - The number of elements being sliced, or -999 on error.
683             *
684             * Description:
685             * Recursively walks the op tree to count list elements involved in slicing,
686             * such as in array or hash slice operations.
687             *
688             * Internal:
689             * Used by count_list() to determine the size of sliced elements in assignments.
690             */
691             I32
692 9           count_slice (OP* o)
693             {
694             OP* pm;
695 9           OP* l = Nullop;
696              
697 9 50         if (!o) return -999;
698 9           pm = cUNOPo->op_first;
699 9 50         if (!pm || pm->op_type != OP_PUSHMARK)
    50          
700 0           die("%s", "Wanted panicked: slice doesn't start with pushmark\n");
701              
702 9 50         if ( (l = OpSIBLING(pm)) && (l->op_type == OP_LIST || (l->op_type == OP_NULL && l->op_targ == OP_LIST)))
    50          
    50          
    100          
    50          
703 4           return count_list(l, Nullop);
704              
705 5 50         else if (l)
706 5           switch (l->op_type)
707             {
708 1           case OP_RV2AV:
709             case OP_PADAV:
710             case OP_PADHV:
711             case OP_RV2HV:
712 1           return 0;
713 2           case OP_HSLICE:
714             case OP_ASLICE:
715 2           return count_slice(l);
716 2           case OP_STUB:
717 2           return 1;
718 0           default:
719 0           die("Wanted panicked: Unexpected op in slice (%s)\n", PL_op_name[l->op_type]);
720             }
721              
722             else
723 0           die("Wanted panicked: Nothing follows pushmark in slice\n");
724              
725             return -999;
726             }
727              
728             /*
729             * count_list - Counts the number of elements in a list op.
730             *
731             * Arguments:
732             * OP* parent - The parent list op.
733             * OP* returnop - Optional terminator to stop early.
734             *
735             * Return:
736             * I32 - The number of child ops, or 0 if none.
737             *
738             * Description:
739             * This function counts the number of child ops in a list op, helping to determine
740             * the number of left-hand-side variables in assignments (e.g., my( $a, $b ) = ...).
741             *
742             * Internal:
743             * Used by want_count() and want_assign() for assignment analysis.
744             */
745             I32
746 39           count_list (OP* parent, OP* returnop)
747             {
748             OP* o;
749 39           I32 i = 0;
750              
751 39 50         if (!parent || ! (parent->op_flags & OPf_KIDS))
    50          
752 0           return 0;
753              
754 117 100         for(o = cUNOPx(parent)->op_first; o; o=OpSIBLING(o))
    100          
755             {
756 96 50         if (returnop && o->op_type == OP_ENTERSUB && o->op_next == returnop)
    0          
    0          
757 0           return i;
758 96 100         if (o->op_type == OP_RV2AV || o->op_type == OP_RV2HV
    100          
759 90 100         || o->op_type == OP_PADAV || o->op_type == OP_PADHV
    100          
760 79 50         || o->op_type == OP_ENTERSUB)
761 17           return 0;
762            
763 79 100         if (o->op_type == OP_HSLICE || o->op_type == OP_ASLICE)
    100          
764 6           {
765 7           I32 slice_length = count_slice(o);
766 7 100         if (slice_length == 0)
767 1           return 0;
768             else
769 6           i += slice_length - 1;
770             }
771 72           else ++i;
772             }
773              
774 21           return i;
775             }
776              
777             /*
778             * countstack - Counts the number of stack values passed to a subroutine.
779             *
780             * Arguments:
781             * I32 uplevel - Stack frame level to inspect.
782             *
783             * Return:
784             * I32 - Number of items between oldmarksp and current mark, or -1 if context not found.
785             *
786             * Description:
787             * This function counts the number of values on the stack between the old mark and
788             * the current mark, used to estimate how many right-hand-side values exist in an assignment.
789             *
790             * Internal:
791             * Used by want_count() to analyse assignment contexts.
792             */
793             I32
794 35           countstack(I32 uplevel)
795             {
796 35           PERL_CONTEXT* cx = upcontext(aTHX_ uplevel);
797             I32 oldmarksp;
798             I32 mark_from;
799             I32 mark_to;
800              
801 35 50         if (!cx) return -1;
802              
803 35           oldmarksp = cx->blk_oldmarksp;
804 35           mark_from = PL_markstack[oldmarksp];
805 35           mark_to = PL_markstack[oldmarksp+1];
806 35           return (mark_to - mark_from);
807             }
808              
809             /*
810             * copy_rvals - Returns an array of stack values passed to a subroutine.
811             *
812             * Arguments:
813             * I32 uplevel - Stack level to inspect.
814             * I32 skip - Number of items to skip from the start.
815             *
816             * Return:
817             * AV* - An array of values beyond the 'skip' threshold, or Nullav if context not found.
818             *
819             * Description:
820             * This copies the right-hand-side values passed to an assignment into an AV for Perl-side use.
821             *
822             * Internal:
823             * Used by want_assign() to retrieve assignment values.
824             */
825             AV*
826 0           copy_rvals(I32 uplevel, I32 skip)
827             {
828 0           PERL_CONTEXT* cx = upcontext(aTHX_ uplevel);
829             I32 oldmarksp;
830             I32 mark_from;
831             I32 mark_to;
832             I32 i;
833             AV* a;
834              
835 0           oldmarksp = cx->blk_oldmarksp;
836 0           mark_from = PL_markstack[oldmarksp-1];
837 0           mark_to = PL_markstack[oldmarksp];
838              
839 0 0         if (!cx) return Nullav;
840 0           a = newAV();
841 0 0         for(i=mark_from+1; i<=mark_to; ++i)
842 0 0         if (skip-- <= 0) av_push(a, newSVsv(PL_stack_base[i]));
843              
844 0           return a;
845             }
846              
847             /*
848             * copy_rval - Retrieves a single scalar value passed to a subroutine.
849             *
850             * Arguments:
851             * I32 uplevel - Stack level to inspect.
852             *
853             * Return:
854             * AV* - An array containing one value, or Nullav if context not found.
855             *
856             * Description:
857             * This function retrieves the last scalar value from the stack, wrapping it in an AV
858             * for Perl-side use. It is used in OP_SASSIGN cases to retrieve the sole value.
859             *
860             * Internal:
861             * Used by want_assign() for scalar assignment contexts.
862             */
863             AV*
864 21           copy_rval(I32 uplevel)
865             {
866 21           PERL_CONTEXT* cx = upcontext(aTHX_ uplevel);
867             I32 oldmarksp;
868             AV* a;
869              
870 21           oldmarksp = cx->blk_oldmarksp;
871 21 50         if (!cx) return Nullav;
872 21           a = newAV();
873 21           av_push(a, newSVsv(PL_stack_base[PL_markstack[oldmarksp+1]]));
874              
875 21           return a;
876             }
877              
878             // NOTE: Module
879              
880             MODULE = Wanted PACKAGE = Wanted
881             PROTOTYPES: ENABLE
882              
883             =begin comment
884             // NOTE: wantarray_up
885             /*
886             * wantarray_up - Wrapper for Perl's wantarray at a given stack level.
887             *
888             * Arguments:
889             * I32 uplevel - Call stack level offset to use.
890             *
891             * Return:
892             * SV* - Returns &PL_sv_yes (true) for list context, &PL_sv_no (false) for scalar
893             * context, or &PL_sv_undef for void context.
894             *
895             * Description:
896             * This provides a consistent interface to Perl’s context detection at various call
897             * depths.
898             *
899             * Internal:
900             * Used by context(), want(), and _wantone().
901             */
902             =cut
903             SV*
904             wantarray_up(uplevel)
905             I32 uplevel;
906             PREINIT:
907 50           U8 gimme = want_gimme(uplevel);
908             CODE:
909 50           switch(gimme)
910             {
911 10           case G_ARRAY:
912 10           RETVAL = &PL_sv_yes;
913 10           break;
914 34           case G_SCALAR:
915 34           RETVAL = &PL_sv_no;
916 34           break;
917 6           default:
918 6           RETVAL = &PL_sv_undef;
919             }
920             OUTPUT:
921             RETVAL
922              
923             =begin comment
924             // NOTE: want_lvalue
925             /*
926             * want_lvalue - Detects if the current subroutine is being called in lvalue context.
927             *
928             * Arguments:
929             * I32 uplevel - Number of levels up the call stack to check.
930             *
931             * Return:
932             * int - Returns true (non-zero) if in lvalue context, false (0) otherwise.
933             *
934             * Description:
935             * This checks whether the subroutine is being evaluated in a context where the result
936             * can be assigned to, such as in `foo() = 42`.
937             *
938             * Usage:
939             * Called internally by Perl subroutines via want('LVALUE').
940             *
941             * Internal:
942             * Used by wantassign(), lnoreturn().
943             */
944             =cut
945             U8
946             want_lvalue(uplevel)
947             I32 uplevel;
948             PREINIT:
949             PERL_CONTEXT* cx;
950             CODE:
951 83           cx = upcontext(aTHX_ uplevel);
952 83 50         if (!cx) RETVAL = 0;
953            
954 83 100         if (CvLVALUE(cx->blk_sub.cv))
955 69           RETVAL = CxLVAL(cx);
956             else
957 14           RETVAL = 0;
958             OUTPUT:
959             RETVAL
960              
961             =begin comment
962             // NOTE: parent_op_name
963             /*
964             * parent_op_name - Returns the name of the parent OP at the requested level.
965             *
966             * Arguments:
967             * I32 uplevel - How far up the call stack to look.
968             *
969             * Return:
970             * In scalar context: The stringified parent op name (e.g., "aassign", "method_call", "(none)").
971             * In list context: A two-element list containing the parent op name and the return op name.
972             *
973             * Description:
974             * This function resolves the parent op name by examining the OP tree.
975             * If the op is a `leavesub`, this typically means the context is not well-defined.
976             *
977             * Internal:
978             * Used by wantref(), bump_level(), and debugging tools.
979             */
980             =cut
981             void
982             parent_op_name(uplevel)
983             I32 uplevel;
984             PREINIT:
985             OP *r;
986 579           OP *o = parent_op(uplevel, &r);
987             OP *first, *second;
988             char *retval;
989             PPCODE:
990 579 100         if (!o || !r)
    50          
991             {
992 118 50         EXTEND(SP, 2);
993 118           PUSHs(sv_2mortal(newSVpv("(none)", 0)));
994 118           PUSHs(sv_2mortal(newSVpv("(none)", 0)));
995             }
996             else
997             {
998 461 100         if (o->op_type == OP_ENTERSUB && (first = cUNOPo->op_first)
    50          
999 30 100         && (second = OpSIBLING(first)) && OpSIBLING(second) != Nullop)
    100          
    50          
    50          
1000 7           retval = "method_call";
1001             else
1002 454           retval = (char *)PL_op_name[o->op_type];
1003 461 50         if (GIMME == G_ARRAY)
    100          
1004             {
1005 283 50         EXTEND(SP, 2);
1006 283           PUSHs(sv_2mortal(newSVpv(retval, 0)));
1007 283           PUSHs(sv_2mortal(newSVpv(PL_op_name[r->op_type], 0)));
1008             }
1009             else
1010             {
1011 178 50         EXTEND(SP, 1);
1012 178           PUSHs(sv_2mortal(newSVpv(retval, 0)));
1013             }
1014             }
1015              
1016             =begin comment
1017             // NOTE: want_count
1018             /*
1019             * want_count - Determines how many return values are expected by the caller.
1020             *
1021             * Arguments:
1022             * I32 uplevel - Number of levels up to look for the list evaluation context.
1023             *
1024             * Return:
1025             * int - A count of expected return items. Returns -1 if unlimited, 0 for void, or a positive count.
1026             *
1027             * Description:
1028             * This enables subs to detect how many return values the caller is expecting,
1029             * like in `my ($a, $b) = sub();`.
1030             *
1031             * Internal:
1032             * Used by howmany(), want('COUNT'), and _wantone().
1033             */
1034             =cut
1035             I32
1036             want_count(uplevel)
1037             I32 uplevel;
1038             PREINIT:
1039             OP* returnop;
1040 46           OP* o = parent_op(uplevel, &returnop);
1041 46           U8 gimme = want_gimme(uplevel);
1042             CODE:
1043 46 100         if (!o)
1044             {
1045 4 50         RETVAL = (gimme == G_SCALAR ? 1 : gimme == G_ARRAY ? -1 : 0);
    50          
1046             }
1047 42 100         else if (o->op_type == OP_AASSIGN)
1048             {
1049 35           I32 lhs = count_list(cBINOPo->op_last, Nullop );
1050 35           I32 rhs = countstack(uplevel);
1051 35 100         if (lhs == 0) RETVAL = -1;
1052 17 100         else if (rhs >= lhs-1) RETVAL = 0;
1053 13           else RETVAL = lhs - rhs - 1;
1054             }
1055 7           else switch(gimme)
1056             {
1057 1           case G_ARRAY:
1058 1           RETVAL = -1;
1059 1           break;
1060 6           case G_SCALAR:
1061 6           RETVAL = 1;
1062 6           break;
1063 0           default:
1064 0           RETVAL = 0;
1065             }
1066             OUTPUT:
1067             RETVAL
1068              
1069             =begin comment
1070             // NOTE: want_boolean
1071             /*
1072             * want_boolean - Determines whether the current expression is evaluated in boolean context.
1073             *
1074             * Arguments:
1075             * I32 uplevel - Stack level to examine.
1076             *
1077             * Return:
1078             * int - Boolean true/false indicating if this is boolean context.
1079             *
1080             * Description:
1081             * This inspects the op tree to determine if the result of the function is
1082             * being evaluated as a truth value (e.g., `if(foo())` or `foo() && 1`).
1083             *
1084             * Internal:
1085             * Used by want('BOOL').
1086             */
1087             =cut
1088             bool
1089             want_boolean(uplevel)
1090             I32 uplevel;
1091             PREINIT:
1092 73           oplist* l = ancestor_ops(uplevel, 0);
1093             U16 i;
1094 73           bool truebool = FALSE, pseudobool = FALSE;
1095             CODE:
1096 73 100         if (!l)
1097             {
1098 5           RETVAL = FALSE;
1099             }
1100             else
1101             {
1102 267 100         for( i=0; i < l->length; ++i )
1103             {
1104 199           OP* o = l->ops[i].numop_op;
1105 199           U16 n = l->ops[i].numop_num;
1106 199 100         bool v = (OP_GIMME(o, -1) == G_VOID);
    100          
1107 199           switch(o->op_type)
1108             {
1109 13           case OP_NOT:
1110             case OP_XOR:
1111 13           truebool = TRUE;
1112 13           break;
1113 34           case OP_AND:
1114 34 100         if (truebool || v)
    100          
1115 30           truebool = TRUE;
1116             else
1117 4 50         pseudobool = (pseudobool || n == 0);
    100          
1118 34           break;
1119 15           case OP_OR:
1120 15 100         if (truebool || v)
    100          
1121 12           truebool = TRUE;
1122             else
1123 3           truebool = FALSE;
1124 15           break;
1125 6           case OP_COND_EXPR:
1126 6 100         truebool = (truebool || n == 0);
    100          
1127 6           break;
1128 64           case OP_NULL:
1129 64           break;
1130 67           default:
1131 67           truebool = FALSE;
1132 67           pseudobool = FALSE;
1133             }
1134             }
1135 68           free(l);
1136 68 100         RETVAL = truebool || pseudobool;
    100          
1137             }
1138             OUTPUT:
1139             RETVAL
1140              
1141             =begin comment
1142             // NOTE: want_assign
1143             /*
1144             * want_assign - Retrieves the right-hand-side values in an assignment context.
1145             *
1146             * Arguments:
1147             * I32 uplevel - Number of levels up the call stack to inspect.
1148             *
1149             * Return:
1150             * SV* - A reference to an array containing the right-hand-side (RHS) values,
1151             * or &PL_sv_undef if not in assignment context.
1152             *
1153             * Description:
1154             * This XS function inspects the current call context to determine if a subroutine is
1155             * being assigned to. If so, it captures and returns the values being assigned.
1156             *
1157             * Internal:
1158             * Used by wantassign() to expose assignment RHS values to Perl.
1159             */
1160             =cut
1161             SV*
1162             want_assign(uplevel)
1163             U32 uplevel;
1164             PREINIT:
1165             AV* r;
1166             OP* returnop;
1167 23           oplist* os = ancestor_ops(uplevel, &returnop);
1168 23 50         numop* lno = os ? lastnumop(os) : (numop*)0;
1169             OPCODE type;
1170             PPCODE:
1171 23 50         if (!lno)
1172             {
1173 0           r = Nullav;
1174             }
1175             else
1176             {
1177 23           type = lno->numop_op->op_type;
1178 23 50         if (lno && (type == OP_AASSIGN || type == OP_SASSIGN) && lno->numop_num == 1)
    50          
    100          
    50          
1179             {
1180 21 50         if (type == OP_AASSIGN)
1181             {
1182 0           I32 lhs_count = count_list(cBINOPx(lno->numop_op)->op_last, returnop);
1183 0 0         if (lhs_count == 0) r = newAV();
1184             else
1185             {
1186 0           r = copy_rvals(uplevel, lhs_count-1);
1187             }
1188             }
1189 21           else r = copy_rval(uplevel);
1190             }
1191             else
1192             {
1193 2           r = Nullav;
1194             }
1195             }
1196 23 50         if (os) free(os);
1197              
1198 23 50         EXTEND(SP, 1);
1199 23 100         PUSHs(r ? sv_2mortal(newRV_noinc((SV*) r)) : &PL_sv_undef);
1200              
1201             =begin comment
1202             // NOTE: double_return
1203             /*
1204             * double_return - Restores nested return context.
1205             *
1206             * Description:
1207             * This function simulates a return from a subroutine by manipulating the context stack.
1208             * It is tightly coupled to Perl's internal context stack and was originally implemented
1209             * in version 1 of Want. It has been retained as-is for compatibility.
1210             *
1211             * Notes:
1212             * Wrapped in PERL_VERSION_GE(5, 8, 8) and ENABLE_DOUBLE_RETURN_HACKS for safety.
1213             * ⚠️ Do not modify unless you deeply understand the implications, as changes can
1214             * lead to crashes or undefined behaviour.
1215             *
1216             * Internal:
1217             * Used by rreturn() and lnoreturn() to implement early returns in Perl code.
1218             */
1219             =cut
1220             void
1221             double_return(...)
1222             PREINIT:
1223             PERL_CONTEXT *ourcx, *cx;
1224             PPCODE:
1225             # if PERL_VERSION_GE(5, 8, 8) && ENABLE_DOUBLE_RETURN_HACKS
1226 23           ourcx = upcontext(aTHX_ 0);
1227 23           cx = upcontext(aTHX_ 1);
1228 23 100         if (!cx)
1229 2           Perl_croak(aTHX_ "Can't return outside a subroutine");
1230             #ifdef POPBLOCK
1231             ourcx->cx_type = CXt_NULL;
1232             CvDEPTH(ourcx->blk_sub.cv)--;
1233             # if HAS_RETSTACK
1234             if (PL_retstack_ix > 0)
1235             --PL_retstack_ix;
1236             # endif
1237             #else
1238             /* In 5.23.8 or later, PL_curpad is saved in the context stack and
1239             * restored by cx_popsub(), rather than being saved on the savestack
1240             * and restored by LEAVE; so just CXt_NULLing the parent sub
1241             * skips the PL_curpad restore and so everything done during the
1242             * second part of the return will have the wrong PL_curpad.
1243             * So instead, fix up the first return so that it thinks the
1244             * op to continue at is iteself, forcing it to do a double return.
1245             */
1246             assert(PL_op->op_next->op_type == OP_RETURN);
1247             /* force the op following the 'return' to be 'return' again */
1248 21           ourcx->blk_sub.retop = PL_op->op_next;
1249             assert(PL_markstack + ourcx->blk_oldmarksp + 1 == PL_markstack_ptr);
1250 21           ourcx->blk_oldmarksp++;
1251 21           ourcx->blk_gimme = cx->blk_gimme;
1252             #endif
1253              
1254 21           return;
1255             # else
1256             Perl_croak(aTHX_ "double_return not supported on Perl %d.%d.%d (requires >= 5.8.8)",
1257             PERL_REVISION, PERL_VERSION, PERL_SUBVERSION);
1258             # endif /* PERL_VERSION_GE && ENABLE_DOUBLE_RETURN_HACKS */
1259              
1260             =begin comment
1261             // NOTE: disarm_temp
1262             /*
1263             * disarm_temp - Prevents premature destruction of temporary SVs.
1264             *
1265             * Arguments:
1266             * SV* sv - A scalar value which would normally be discarded or freed.
1267             *
1268             * Return:
1269             * SV* - A new scalar that holds the value of the temporary, protected from auto-cleanup.
1270             *
1271             * Description:
1272             * This is used to hold a temporary value in a persistent form for use in lvalue context.
1273             * It ensures the SV is detached from temporary cleanup scopes.
1274             *
1275             * Usage:
1276             * return disarm_temp(newSViv(0)); // safe to return from XS
1277             *
1278             * Internal:
1279             * Used by lnoreturn() to safely return placeholder values.
1280             */
1281             =cut
1282             SV *
1283             disarm_temp(sv)
1284             SV *sv;
1285             CODE:
1286 9           RETVAL = sv_2mortal(SvREFCNT_inc(SvREFCNT_inc(sv)));
1287             OUTPUT:
1288             RETVAL
1289              
1290             INCLUDE: FirstMultideref.xsh