File Coverage

Declare.xs
Criterion Covered Total %
statement 539 560 96.2
branch 327 412 79.3
condition n/a
subroutine n/a
pod n/a
total 866 972 89.0


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #include "ppport.h" /* backports the OpSIBLING / OpLASTSIB_set op-tree macros to
5             perl 5.14-5.20 (added to core in 5.22) */
6              
7             /* pad_add_name_pvn was a 5.15.1 rename of pad_add_name; the 5.14 function has the
8             * identical (name, len, flags, typestash, ourstash) */
9             #if PERL_VERSION < 16
10             # define pad_add_name_pvn(name, len, flags, typestash, ourstash) \
11             Perl_pad_add_name(aTHX_ (name), (len), (flags), (typestash), (ourstash))
12             #endif
13              
14             #define MAX_ARMS 4096
15              
16             /* Previous keyword plugin in the chain. */
17             static int (*sd_next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
18              
19             /* Read a bareword identifier from the lexer (or NULL if none). */
20 483           static SV *sd_lex_read_ident(pTHX) {
21 483           SV *buf = newSVpvs("");
22             I32 c;
23             while (1) {
24 2994           c = lex_peek_unichar(0);
25 2994 50         if (c == -1) break;
26 2994 50         if (!isALNUM(c) && c != '_') break;
    100          
    50          
27 2511           sv_catpvf(buf, "%c", (int)c);
28 2511           lex_read_unichar(0);
29             }
30 483 50         if (SvCUR(buf) == 0) {
31 0           SvREFCNT_dec(buf);
32 0           return NULL;
33             }
34 483           return buf;
35             }
36              
37             /* Read a possibly package-qualified sub name (Foo::bar, Foo'bar). */
38 138           static SV *sd_lex_subname(pTHX) {
39 138           SV *buf = newSVpvs("");
40             I32 c;
41             while (1) {
42 688           c = lex_peek_unichar(0);
43 688 50         if (c == -1) break;
44 688 50         if (isALNUM(c) || c == '_') {
    100          
    50          
45 548           sv_catpvf(buf, "%c", (int)c);
46 548           lex_read_unichar(0);
47 140 100         } else if (c == ':' && PL_parser->bufptr[0] == ':'
    50          
48 2 50         && PL_parser->bufptr[1] == ':') {
49 2           sv_catpvs(buf, "::");
50 2           lex_read_unichar(0);
51 2           lex_read_unichar(0);
52             } else break;
53             }
54 138 100         if (SvCUR(buf) == 0) {
55 1           SvREFCNT_dec(buf);
56 1           return NULL;
57             }
58 137           return buf;
59             }
60              
61             /* Hand-lex a numeric literal (optional sign, integer/float/exponent). */
62 113           static SV *sd_lex_number(pTHX) {
63 113           SV *buf = newSVpvs("");
64 113           I32 c = lex_peek_unichar(0);
65 113           int seen_dot = 0, seen_digit = 0;
66 113 100         if (c == '-' || c == '+') {
    100          
67 5           sv_catpvf(buf, "%c", (int)c);
68 5           lex_read_unichar(0);
69             }
70             while (1) {
71 255           c = lex_peek_unichar(0);
72 255 100         if (c >= '0' && c <= '9') { seen_digit = 1; }
    100          
73 118 100         else if (c == '.' && !seen_dot) {
    50          
74             /* Only a decimal point if followed by a digit; otherwise it is
75             * the '..' range operator (or a terminator) - leave it alone. */
76 5 50         char next = (PL_parser->bufptr[0] == '.') ? PL_parser->bufptr[1] : '\0';
77 5 100         if (next < '0' || next > '9') break;
    50          
78 4           seen_dot = 1;
79             }
80 113 100         else if ((c == 'e' || c == 'E') && seen_digit) {
    50          
    50          
81 1           sv_catpvf(buf, "%c", (int)c);
82 1           lex_read_unichar(0);
83 1           c = lex_peek_unichar(0);
84 1 50         if (c == '-' || c == '+') {
    50          
85 0           sv_catpvf(buf, "%c", (int)c);
86 0           lex_read_unichar(0);
87             }
88 1           continue;
89             }
90             else break;
91 141           sv_catpvf(buf, "%c", (int)c);
92 141           lex_read_unichar(0);
93             }
94 113 50         if (!seen_digit) {
95 0           SvREFCNT_dec(buf);
96 0           croak("switch: malformed numeric case pattern");
97             }
98 113           return buf;
99             }
100              
101             /* Hand-lex a quoted string literal ('...' or "..."), basic backslash escapes. */
102 93           static SV *sd_lex_string(pTHX) {
103 93           I32 quote = lex_read_unichar(0);
104 93           SV *sv = newSVpvs("");
105             I32 c;
106             while (1) {
107 238           c = lex_read_unichar(0);
108 238 100         if (c == -1) croak("switch: unterminated string in case pattern");
109 237 100         if (c == '\\') {
110 2           I32 next = lex_read_unichar(0);
111 2 50         if (next == -1) croak("switch: unterminated string in case pattern");
112 2 100         if (quote == '"') {
113 1           switch (next) {
114 0           case 'n': sv_catpvs(sv, "\n"); break;
115 1           case 't': sv_catpvs(sv, "\t"); break;
116 0           case 'r': sv_catpvs(sv, "\r"); break;
117 0           case '0': sv_catpvs(sv, "\0"); break;
118 0           default: sv_catpvf(sv, "%c", (int)next); break;
119             }
120             } else {
121             /* single quotes: only \\ and \' are special */
122 1 50         if (next != '\\' && next != '\'')
    50          
123 1           sv_catpvf(sv, "%c", '\\');
124 1           sv_catpvf(sv, "%c", (int)next);
125             }
126 235 100         } else if (c == quote) {
127 92           break;
128             } else {
129 143           sv_catpvf(sv, "%c", (int)c);
130             }
131             }
132 92           return sv;
133             }
134              
135             /* Describes where the matched topic comes from. When the scrutinee is already
136             * a plain lexical or a constant, each case test re-reads it directly (just like
137             * a hand-written if/elsif chain) and no temp / do-block is needed. Otherwise
138             * the scrutinee is stored once in a pad temp. */
139             #define SDT_TEMP 0 /* stored once in pad temp `off` (do-block) */
140             #define SDT_PAD 1 /* re-read scrutinee's own lexical at `off` */
141             #define SDT_CONST 2 /* re-read a constant value */
142              
143             /* How the numeric looks_like_number($topic) guard is sourced. For a defined
144             * constant scrutinee it folds to a compile-time boolean; otherwise it is
145             * computed once into a pad temp and each numeric arm just reads that temp. */
146             #define LLN_CONST 0 /* compile-time constant in `lln_const` */
147             #define LLN_PAD 1 /* computed once into pad temp `lln_off` */
148              
149             typedef struct {
150             int kind;
151             PADOFFSET off;
152             SV *sv;
153             int lln_mode; /* LLN_CONST or LLN_PAD */
154             int lln_const; /* folded looks_like_number value (LLN_CONST) */
155             PADOFFSET lln_off; /* pad temp holding the guard (LLN_PAD) */
156             int lln_used; /* a numeric arm referenced the LLN_PAD temp */
157             } SDTopic;
158              
159             /* A fresh op yielding the topic value. */
160 400           static OP *sd_topic(pTHX_ SDTopic *t) {
161 400 100         if (t->kind == SDT_CONST)
162 199           return newSVOP(OP_CONST, 0, newSVsv(t->sv));
163             {
164 201           OP *o = newOP(OP_PADSV, 0);
165 201           o->op_targ = t->off;
166 201           return o;
167             }
168             }
169              
170             /* ---- looks_like_number($topic) as a fast custom op ---------------------
171             * A numeric pattern (==, range, list) must only fire for a topic that really
172             * is a number. Without this, `switch("one") { case 1 {...} }` would warn
173             * ("Argument isn't numeric in numeric eq") and - worse - "one" == 0 would
174             * *match* a `case 0`. Guarding each numeric compare with looks_like_number()
175             * makes a non-numeric topic simply not match (and not warn), mirroring how an
176             * undef topic is handled. It compiles to a single custom op: no sub call, no
177             * module dependency. */
178 216           static OP *sd_pp_looks_number(pTHX) {
179 216           dSP;
180 216           SV *sv = TOPs;
181 216 50         SETs(boolSV(sv && SvOK(sv) && looks_like_number(sv)));
    100          
    100          
182 216           RETURN;
183             }
184              
185             static XOP sd_looks_number_xop;
186              
187             /* The raw looks_like_number($topic) custom op (one pp call, no sub call).
188             * Built as an OP_NULL unop (always accepted by newUNOP on every perl) then
189             * retyped to our registered custom op; newUNOP sets OPf_KIDS so op_free still
190             * reclaims the child topic op. */
191 19           static OP *sd_looks_number_op(pTHX_ SDTopic *t) {
192 19           OP *o = newUNOP(OP_NULL, 0, sd_topic(aTHX_ t));
193 19           o->op_type = OP_CUSTOM;
194 19           o->op_ppaddr = sd_pp_looks_number;
195 19           return o;
196             }
197              
198             /* looks_like_number(OPERAND) over an arbitrary operand op (not just the topic).
199             * Used to guard a `case num $var` operand so a non-numeric $var neither matches
200             * nor warns - mirroring the topic guard. */
201 11           static OP *sd_lln_raw(pTHX_ OP *operand) {
202 11           OP *o = newUNOP(OP_NULL, 0, operand);
203 11           o->op_type = OP_CUSTOM;
204 11           o->op_ppaddr = sd_pp_looks_number;
205 11           return o;
206             }
207              
208             /* The numeric guard expression used by each numeric arm. For a defined constant
209             * topic it folds to a compile-time boolean; otherwise the guard is computed once
210             * per switch (see the LLN_PAD prelude in sd_parse_switch) and each arm just reads
211             * that pad temp - so a switch with N numeric arms calls looks_like_number once,
212             * not N times. */
213 127           static OP *sd_looks_number(pTHX_ SDTopic *t) {
214 127 100         if (t->lln_mode == LLN_CONST)
215 97 100         return newSVOP(OP_CONST, 0, boolSV(t->lln_const));
216 30           t->lln_used = 1;
217             {
218 30           OP *o = newOP(OP_PADSV, 0);
219 30           o->op_targ = t->lln_off;
220 30           return o;
221             }
222             }
223              
224             /* ---- reftype($topic) as a fast custom op -------------------------------
225             * Like ref(), but reports the underlying type ("ARRAY"/"HASH"/...) even for a
226             * blessed reference, and undef for a non-reference. Used by the reftype(TYPE)
227             * pattern. Same OP_NULL->OP_CUSTOM construction as the looks_number op. */
228 11           static OP *sd_pp_reftype(pTHX) {
229 11           dSP;
230 11           SV *sv = TOPs;
231             /* Like ref(), a non-reference yields a defined empty/false value (not
232             * undef) so `reftype(TYPE)` compares as `"" eq "TYPE"` without warning and
233             * bare `reftype` is simply false. */
234 11 100         SETs(SvROK(sv) ? sv_2mortal(newSVpv(sv_reftype(SvRV(sv), 0), 0))
235             : &PL_sv_no);
236 11           RETURN;
237             }
238              
239             static XOP sd_reftype_xop;
240              
241 11           static OP *sd_reftype_op(pTHX_ SDTopic *t) {
242 11           OP *o = newUNOP(OP_NULL, 0, sd_topic(aTHX_ t));
243 11           o->op_type = OP_CUSTOM;
244 11           o->op_ppaddr = sd_pp_reftype;
245 11           return o;
246             }
247              
248             /* When every arm maps a distinct string-literal key to a constant value (a
249             * lookup table) and there are at least this many of them, the whole switch is
250             * lowered to a single O(1) hash lookup against a compile-time constant hash
251             * instead of an O(n) chain of eq tests. */
252             #define SD_DISPATCH_MIN 4
253              
254             /* What a case pattern was, for dispatch eligibility. */
255             typedef struct {
256             int str_key; /* 1 if an exact string-literal pattern (eq) */
257             SV *key; /* the literal (owned) when str_key */
258             int is_undef; /* 1 if the pattern was the `undef` keyword */
259             int undef_safe; /* 1 if the pattern can't match/warn on an undef topic */
260             } SDPat;
261              
262             /* A fresh $PKGHASH{ topic } element op, where the hash is the package
263             * variable named by `gv`. Referencing the hash through a GV (rather than an
264             * op-constant hashref) keeps the dispatch table thread-safe: op constants are
265             * cloned per-thread, which would dangle a reference to a shared HV.
266             *
267             * When `sentinel` is non-NULL the topic may be undef, so the key is guarded as
268             * defined($topic) ? $topic : SENTINEL
269             * with SENTINEL a string known not to be in the table - an undef topic then
270             * misses cleanly (-> default) instead of warning on an undef hash key. */
271 8           static OP *sd_helem(pTHX_ GV *gv, SDTopic *t, SV *sentinel) {
272 8           OP *gvop = newGVOP(OP_GV, 0, gv);
273 8           OP *deref = newUNOP(OP_RV2HV, OPf_REF, gvop);
274             OP *key;
275 8 100         if (sentinel)
276 5           key = newCONDOP(0, newUNOP(OP_DEFINED, 0, sd_topic(aTHX_ t)),
277             sd_topic(aTHX_ t),
278             newSVOP(OP_CONST, 0, newSVsv(sentinel)));
279             else
280 3           key = sd_topic(aTHX_ t);
281 8           return newBINOP(OP_HELEM, 0, deref, key);
282             }
283              
284             /* Read a literal (number or string) into an OP_CONST, setting *is_num. */
285 204           static OP *sd_lex_literal(pTHX_ int *is_num) {
286 204           I32 c = lex_peek_unichar(0);
287 204 100         if (c == '"' || c == '\'') {
    100          
288 91           *is_num = 0;
289 91           return newSVOP(OP_CONST, 0, sd_lex_string(aTHX));
290             }
291 113 100         if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.') {
    50          
    100          
    50          
    0          
292 113           *is_num = 1;
293 113           return newSVOP(OP_CONST, 0, sd_lex_number(aTHX));
294             }
295 0           croak("switch: expected a number or string literal");
296             return NULL; /* not reached */
297             }
298              
299             /* topic CMP const, where CMP is numeric (==/>=/<=) or string (eq/ge/le).
300             * Numeric comparisons are guarded by looks_like_number($topic) so a non-numeric
301             * topic never matches or warns; string comparisons (eq/ge/le) never warn and
302             * need no guard. */
303 225           static OP *sd_cmp(pTHX_ SDTopic *t, int is_num, I32 numop, I32 strop, OP *konst) {
304 225 100         OP *cmp = newBINOP(is_num ? numop : strop, 0, sd_topic(aTHX_ t), konst);
305 225 100         if (is_num)
306 127           cmp = newLOGOP(OP_AND, 0, sd_looks_number(aTHX_ t), cmp);
307 225           return cmp;
308             }
309              
310             /* ---- $topic =~ PATTERN as a fast custom op ----------------------------
311             * The `=~ $var` pattern matches the topic against a *runtime* pattern held in a
312             * scalar (a qr// or a string), so it cannot be compiled once at compile time
313             * like the `/literal/` form. Rather than hand-wire an OP_REGCOMP/OP_MATCH pair
314             * (fragile to thread correctly), it is a self-contained custom op in the same
315             * family as the looks_number / reftype ops: pop the pattern and the topic, run
316             * the match, push a boolean. A qr// operand reuses its compiled program; a bare
317             * string is compiled per evaluation (and freed). Both operands are guaranteed
318             * defined here - the pattern is guarded with defined() and an undef topic is
319             * guarded by the chain - so the match itself never warns. */
320 6           static OP *sd_pp_rxmatch(pTHX) {
321 6           dSP;
322 6           SV *pat = POPs;
323 6           SV *str = TOPs;
324 6           bool matched = FALSE;
325 6 100         REGEXP *rx = SvRXOK(pat) ? SvRX(pat) : NULL;
326 6           int owned = 0;
327 6 100         if (!rx) { rx = pregcomp(pat, 0); owned = 1; }
328 6 50         if (rx) {
329             STRLEN len;
330 6           char *s = SvPV(str, len);
331 6 100         if (pregexec(rx, s, s + len, s, 0, str, 1))
332 5           matched = TRUE;
333 6 100         if (owned) ReREFCNT_dec(rx);
334             }
335 6 100         SETs(boolSV(matched));
336 6           RETURN;
337             }
338              
339             static XOP sd_rxmatch_xop;
340              
341             /* topic and pattern are pushed by the two child ops; the custom op pops both.
342             * A native OP_MATCH cannot be built here - perl's pattern builders (pmruntime)
343             * are entangled with the parser's own pattern-lexing state and crash when driven
344             * from a keyword plugin - so this is a self-contained custom op. The trade-off
345             * is that it is a pure membership test: it sets no capture variables ($1, @+).
346             * For captures from a runtime pattern, use a predicate arm: case sub { $_[0] =~ $rx }. */
347 8           static OP *sd_rxmatch_op(pTHX_ SDTopic *t, OP *patop) {
348 8           OP *o = newBINOP(OP_NULL, 0, sd_topic(aTHX_ t), patop);
349 8           o->op_type = OP_CUSTOM;
350 8           o->op_ppaddr = sd_pp_rxmatch;
351 8           return o;
352             }
353              
354             /* Build CALLEE->( topic ) as an entersub. The OP_ENTERSUB checker inserts the
355             * pushmark itself, so we must NOT add one - a second pushmark leaves a dangling
356             * mark that corrupts a surrounding list/aassign at runtime. */
357 21           static OP *sd_predicate_call(pTHX_ OP *callee, SDTopic *t) {
358 21           OP *args = op_append_elem(OP_LIST, sd_topic(aTHX_ t), callee);
359 21           return newUNOP(OP_ENTERSUB, OPf_STACKED, args);
360             }
361              
362             /* Build Switch::Declare::_isa($topic, "Class") -> true iff the topic is a
363             * blessed object derived from Class (a fast @ISA check; see the XS _isa below).
364             * An entersub rather than a custom op keeps the two-argument call portable
365             * across the supported perls. */
366 12           static OP *sd_isa_call(pTHX_ SDTopic *t, SV *klass) {
367 12           GV *gv = gv_fetchpvs("Switch::Declare::_isa", GV_ADD, SVt_PVCV);
368 12           OP *cvop = newUNOP(OP_RV2CV, 0, newGVOP(OP_GV, 0, gv));
369 12           OP *args = op_append_elem(OP_LIST, sd_topic(aTHX_ t),
370             newSVOP(OP_CONST, 0, klass));
371 12           args = op_append_elem(OP_LIST, args, cvop);
372 12           return newUNOP(OP_ENTERSUB, OPf_STACKED, args);
373             }
374              
375             /* Read an optional/required ( NAME ) argument after a pattern keyword, where
376             * NAME is a package-qualified bareword (ARRAY, Foo::Bar) or a quoted string.
377             * Returns the name SV (caller owns) or NULL when there is no '('. */
378 40           static SV *sd_lex_paren_arg(pTHX) {
379             I32 c;
380             SV *name;
381 40           lex_read_space(0);
382 40 100         if (lex_peek_unichar(0) != '(') return NULL;
383 32           lex_read_unichar(0);
384 32           lex_read_space(0);
385 32           c = lex_peek_unichar(0);
386 32 100         if (c == '"' || c == '\'')
    50          
387 2           name = sd_lex_string(aTHX);
388             else
389 30           name = sd_lex_subname(aTHX);
390 32 50         if (!name) croak("switch: expected a name inside (...)");
391 32           lex_read_space(0);
392 32 50         if (lex_peek_unichar(0) != ')') {
393 0           SvREFCNT_dec(name);
394 0           croak("switch: expected ')' after pattern argument");
395             }
396 32           lex_read_unichar(0);
397 32           return name;
398             }
399              
400             /* A parsed scalar-variable operand for `case num $x` / `case str $x`. It is
401             * either an in-scope lexical (off != NOT_IN_PAD) or a package scalar (gv), and
402             * is rebuilt fresh by sd_var_op on each use so the operand can be referenced by
403             * both the type guard and the comparison without sharing an op. */
404             typedef struct { PADOFFSET off; GV *gv; } SDVar;
405              
406             /* Hand-lex a plain scalar variable - the leading '$' is still unread. Restricted
407             * to a bare `$name` / `$Pkg::name` (no `[...]`/`{...}` element access) so the
408             * arm's opening `{` is never misparsed as a `$x{...}` hash subscript. */
409 26           static void sd_lex_scalar_var(pTHX_ SDVar *v) {
410             SV *name, *withsig;
411             PADOFFSET off;
412 26 100         if (lex_peek_unichar(0) != '$')
413 1           croak("switch: expected a scalar variable ($name) after ==, eq, or =~");
414 25           lex_read_unichar(0);
415 25           name = sd_lex_subname(aTHX);
416 25 50         if (!name) croak("switch: expected a variable name after '$'");
417 25           withsig = newSVpvs("$");
418 25           sv_catsv(withsig, name);
419 25           off = pad_findmy_pvn(SvPVX(withsig), SvCUR(withsig), 0);
420 25           SvREFCNT_dec(withsig);
421 25 100         if (off != NOT_IN_PAD) {
422             /* An `our` variable has a pad entry, but the slot aliases a GV rather
423             * than holding the value - reading it as a plain PADSV is wrong. Resolve
424             * it to the package scalar it really is, qualified by the `our`'s own
425             * stash so the lookup neither trips strict 'vars' nor guesses the wrong
426             * package. */
427 24           PADNAME *pn = PadnamelistARRAY(PL_comppad_name)[off];
428 24 50         if (pn && PadnameIsOUR(pn)) {
    100          
429 2           HV *stash = PadnameOURSTASH(pn);
430 2           SV *q = newSVpvs("");
431 2 50         if (stash && HvNAME(stash))
    50          
    50          
    50          
    50          
    50          
    50          
432 2 50         sv_catpvf(q, "%s::", HvNAME(stash));
    50          
    50          
    0          
    50          
    50          
433 2           sv_catsv(q, name);
434 2           v->off = NOT_IN_PAD;
435 2           v->gv = gv_fetchpv(SvPV_nolen(q), GV_ADD, SVt_PV);
436 2           SvREFCNT_dec(q);
437 2           SvREFCNT_dec(name);
438 2           return;
439             }
440 22           v->off = off;
441 22           v->gv = NULL;
442             } else {
443             /* A plain package global: sd_lex_subname keeps any `Pkg::` qualifier, so
444             * gv_fetchpv sees a qualified name and strict 'vars' stays satisfied. */
445 1           v->off = NOT_IN_PAD;
446 1           v->gv = gv_fetchpv(SvPV_nolen(name), GV_ADD, SVt_PV);
447             }
448 23           SvREFCNT_dec(name);
449             }
450              
451             /* A fresh op yielding the variable operand's value. */
452 50           static OP *sd_var_op(pTHX_ SDVar *v) {
453 50 100         if (v->off != NOT_IN_PAD) {
454 44           OP *o = newOP(OP_PADSV, 0);
455 44           o->op_targ = v->off;
456 44           return o;
457             }
458 6           return newUNOP(OP_RV2SV, 0, newGVOP(OP_GV, 0, v->gv));
459             }
460              
461             /* Parse one case PATTERN from the lexer and return its boolean condition op,
462             * testing the topic. Fills *pat describing the pattern (for dispatch). */
463 290           static OP *sd_parse_case_cond(pTHX_ SDTopic *t, SDPat *pat) {
464 290           I32 c = lex_peek_unichar(0);
465 290           pat->str_key = 0;
466 290           pat->key = NULL;
467 290           pat->is_undef = 0;
468 290           pat->undef_safe = 0;
469              
470             /* regex: /PATTERN/flags -> native topic =~ /PATTERN/flags
471             * The pattern is compiled once, here at compile time, and bound to a
472             * standard OP_MATCH - no runtime helper, no per-match recompilation. */
473 290 100         if (c == '/') {
474 16           SV *pat = newSVpvs("");
475 16           U32 rxflags = 0;
476             REGEXP *rx;
477             PMOP *pmop;
478             OP *target;
479 16           lex_read_unichar(0);
480             while (1) {
481 92           c = lex_read_unichar(0);
482 92 100         if (c == -1) croak("switch: unterminated regex in case pattern");
483 91 100         if (c == '\\') {
484 5           I32 n = lex_read_unichar(0);
485 5 50         if (n == -1) croak("switch: unterminated regex in case pattern");
486 5           sv_catpvf(pat, "%c", '\\');
487 5           sv_catpvf(pat, "%c", (int)n);
488 5           continue;
489             }
490 86 100         if (c == '/') break;
491 71           sv_catpvf(pat, "%c", (int)c);
492             }
493 22 100         while (isALPHA((c = lex_peek_unichar(0)))) {
494 8           switch (c) {
495 3           case 'i': rxflags |= PMf_FOLD; break;
496 2           case 'm': rxflags |= PMf_MULTILINE; break;
497 1           case 's': rxflags |= PMf_SINGLELINE; break;
498 1           case 'x': rxflags |= PMf_EXTENDED; break;
499 1           default: croak("switch: unsupported regex flag '%c' in case pattern", (int)c);
500             }
501 7           lex_read_unichar(0);
502             }
503 14           rx = pregcomp(pat, rxflags);
504 14           SvREFCNT_dec(pat);
505 14           pmop = (PMOP *)newPMOP(OP_MATCH, 0);
506 14           PM_SETRE(pmop, rx);
507             /* bind the topic as the match target ($topic =~ ...) */
508 14           target = sd_topic(aTHX_ t);
509 14           ((PMOP *)pmop)->op_first = target;
510 14           ((PMOP *)pmop)->op_last = target;
511 14           OpLASTSIB_set(target, (OP *)pmop);
512 14           pmop->op_flags |= OPf_KIDS | OPf_STACKED;
513 14           return (OP *)pmop;
514             }
515              
516             /* predicate: \&name -> name($topic) */
517 274 100         if (c == '\\') {
518             SV *name;
519             GV *gv;
520             OP *cvop;
521 11           lex_read_unichar(0);
522 11 100         if (lex_peek_unichar(0) != '&')
523 1           croak("switch: expected '&' after '\\' in case predicate");
524 10           lex_read_unichar(0);
525 10           name = sd_lex_subname(aTHX);
526 10 100         if (!name) croak("switch: expected sub name after '\\&'");
527 9           gv = gv_fetchpv(SvPV_nolen(name), GV_ADD, SVt_PVCV);
528 9           SvREFCNT_dec(name);
529 9           cvop = newUNOP(OP_RV2CV, 0, newGVOP(OP_GV, 0, gv));
530 9           return sd_predicate_call(aTHX_ cvop, t);
531             }
532              
533             /* bracket: [LO .. HI] range, or [a, b, c] membership list */
534 263 100         if (c == '[') {
535             int is_num;
536             OP *first;
537 26           lex_read_unichar(0);
538 26           lex_read_space(0);
539 26           first = sd_lex_literal(aTHX_ &is_num);
540 26           lex_read_space(0);
541 26 100         if (lex_peek_unichar(0) == '.') {
542             /* range: [LO .. HI] */
543             OP *hi;
544             int hi_num;
545 14           lex_read_unichar(0);
546 14 100         if (lex_peek_unichar(0) != '.')
547 1           croak("switch: expected '..' in range case pattern");
548 13           lex_read_unichar(0);
549 13           lex_read_space(0);
550 13           hi = sd_lex_literal(aTHX_ &hi_num);
551 13           lex_read_space(0);
552 13 50         if (lex_peek_unichar(0) != ']')
553 0           croak("switch: expected ']' to close range case pattern");
554 13           lex_read_unichar(0);
555 13           return newLOGOP(OP_AND, 0,
556             sd_cmp(aTHX_ t, is_num, OP_GE, OP_SGE, first),
557             sd_cmp(aTHX_ t, hi_num, OP_LE, OP_SLE, hi));
558             }
559             /* membership list: [a, b, c] -> OR-chain of equality tests */
560             {
561 12           OP *chain = sd_cmp(aTHX_ t, is_num, OP_EQ, OP_SEQ, first);
562 22           while (1) {
563             OP *elt;
564             int en;
565 34           lex_read_space(0);
566 34 100         if (lex_peek_unichar(0) == ',') lex_read_unichar(0);
567 34           lex_read_space(0);
568 34 100         if (lex_peek_unichar(0) == ']') { lex_read_unichar(0); break; }
569 22           elt = sd_lex_literal(aTHX_ &en);
570 22           chain = newLOGOP(OP_OR, 0, chain,
571             sd_cmp(aTHX_ t, en, OP_EQ, OP_SEQ, elt));
572             }
573 12           return chain;
574             }
575             }
576              
577             /* == $var : numeric comparison against a runtime scalar.
578             * =~ $var : regex match against a runtime pattern (a qr// or string in a
579             * variable). Because a variable's type/pattern is unknown at
580             * compile time, the operator is written out - `==` for numeric,
581             * `=~` for match (here); `eq` for string is a bareword, below. */
582 237 100         if (c == '=') {
583             SDVar v;
584             I32 c2;
585 21           lex_read_unichar(0);
586 21           c2 = lex_peek_unichar(0);
587 21 100         if (c2 == '=') {
588 11           lex_read_unichar(0);
589 11           lex_read_space(0);
590 11           sd_lex_scalar_var(aTHX_ &v);
591             /* looks_like_number($var) && looks_like_number($topic)
592             * && $topic == $var - undef-safe both sides. */
593 11           pat->undef_safe = 1;
594 11           return newLOGOP(OP_AND, 0,
595             sd_lln_raw(aTHX_ sd_var_op(aTHX_ &v)),
596             sd_cmp(aTHX_ t, 1, OP_EQ, OP_SEQ, sd_var_op(aTHX_ &v)));
597             }
598 10 100         if (c2 == '~') {
599 9           lex_read_unichar(0);
600 9           lex_read_space(0);
601 9           sd_lex_scalar_var(aTHX_ &v);
602             /* defined($var) && $topic =~ $var. An undef topic is guarded by the
603             * chain (undef_safe = 0); guarding $var keeps an undef pattern from
604             * warning (it simply does not match). */
605 8           pat->undef_safe = 0;
606 8           return newLOGOP(OP_AND, 0,
607             newUNOP(OP_DEFINED, 0, sd_var_op(aTHX_ &v)),
608             sd_rxmatch_op(aTHX_ t, sd_var_op(aTHX_ &v)));
609             }
610 1           croak("switch: expected '==' or '=~' in case comparison");
611             }
612              
613             /* inline predicate: sub { ... } -> (sub { ... })->($topic)
614             * undef keyword: undef -> !defined($topic)
615             * ref / ref(TYPE): ref($topic) [eq "TYPE"]
616             * reftype / (TYPE): reftype($topic) [eq "TYPE"] (sees through blessing)
617             * isa(Class): blessed object derived from Class
618             * eq $var: $topic eq $var (string compare vs a runtime scalar) */
619 216 100         if (isALPHA(c) || c == '_') {
    50          
620             /* read a possibly package-qualified bareword (so a constant may be
621             * `Foo::BAR`); keyword names never contain '::' so they still match. */
622 73           SV *word = sd_lex_subname(aTHX);
623 73 50         const char *wp = word ? SvPV_nolen(word) : "";
624 73           int is_sub = strEQ(wp, "sub");
625 73           int is_undef = strEQ(wp, "undef");
626 73           int is_ref = strEQ(wp, "ref");
627 73           int is_reftype = strEQ(wp, "reftype");
628 73           int is_isa = strEQ(wp, "isa");
629 73           int is_eq = strEQ(wp, "eq");
630             /* Eagerly resolve the bareword as an inlinable constant while `word` is
631             * still alive. cv_const_sv returns the value owned by the (persistent)
632             * constant sub, so it stays valid after we release `word`. */
633 73 50         CV *cv = word ? get_cvn_flags(SvPVX(word), SvCUR(word), 0) : NULL;
634 73 100         SV *const_val = cv ? cv_const_sv(cv) : NULL;
635             char errbuf[128];
636 73           errbuf[0] = '\0';
637 73 50         if (word) {
638 73           my_strlcpy(errbuf, SvPV_nolen(word), sizeof(errbuf));
639 73           SvREFCNT_dec(word);
640             }
641             /* eq $var : string comparison against a runtime scalar. Honoured only
642             * when a '$' actually follows, so a bareword `eq` otherwise falls through
643             * to constant / error handling. (Its numeric sibling `== $var` is parsed
644             * above, as it does not begin with a word character.) */
645 73 100         if (is_eq) {
646 6           lex_read_space(0);
647 6 50         if (lex_peek_unichar(0) == '$') {
648             SDVar v;
649 6           sd_lex_scalar_var(aTHX_ &v);
650             /* defined($var) && $topic eq $var. An undef topic is guarded by
651             * the chain (undef_safe = 0), so a string variable matches
652             * exactly like a string literal. */
653 6           pat->undef_safe = 0;
654 6           return newLOGOP(OP_AND, 0,
655             newUNOP(OP_DEFINED, 0, sd_var_op(aTHX_ &v)),
656             sd_cmp(aTHX_ t, 0, OP_EQ, OP_SEQ, sd_var_op(aTHX_ &v)));
657             }
658             /* no '$' - fall through to constant / error handling */
659             }
660 67 100         if (is_undef) {
661 7           pat->is_undef = 1;
662 7           pat->undef_safe = 1;
663 7           return newUNOP(OP_NOT, 0,
664             newUNOP(OP_DEFINED, 0, sd_topic(aTHX_ t)));
665             }
666 60 100         if (is_ref) {
667             /* ref($topic) [eq "TYPE"] - pure ops, never warns. */
668 17           SV *type = sd_lex_paren_arg(aTHX);
669 17           pat->undef_safe = 1;
670 17 100         if (!type)
671 5           return newUNOP(OP_REF, 0, sd_topic(aTHX_ t));
672 12           return newBINOP(OP_SEQ, 0,
673             newUNOP(OP_REF, 0, sd_topic(aTHX_ t)),
674             newSVOP(OP_CONST, 0, type));
675             }
676 43 100         if (is_reftype) {
677             /* reftype($topic) [eq "TYPE"] - underlying type, blessing aside. */
678 11           SV *type = sd_lex_paren_arg(aTHX);
679 11           pat->undef_safe = 1;
680 11 100         if (!type)
681 3           return sd_reftype_op(aTHX_ t);
682 8           return newBINOP(OP_SEQ, 0, sd_reftype_op(aTHX_ t),
683             newSVOP(OP_CONST, 0, type));
684             }
685 32 100         if (is_isa) {
686 12           SV *klass = sd_lex_paren_arg(aTHX);
687 12 50         if (!klass)
688 0           croak("switch: isa requires a class: case isa(Class)");
689 12           pat->undef_safe = 1;
690 12           return sd_isa_call(aTHX_ t, klass);
691             }
692 20 100         if (is_sub) {
693             I32 floor;
694             OP *body, *anon;
695 13           lex_read_space(0);
696 13 100         if (lex_peek_unichar(0) != '{')
697 1           croak("switch: expected '{' after 'sub' in case predicate");
698 12           floor = start_subparse(FALSE, CVf_ANON);
699 12           body = parse_block(0);
700 12           anon = newANONATTRSUB(floor, NULL, NULL, body);
701 12           return sd_predicate_call(aTHX_ anon, t);
702             }
703 7 100         if (const_val) {
704             /* An inlinable constant (use constant FOO => ...) folds to its value
705             * and is classified just like the literal it holds: a number compiles
706             * to ==, anything with a string form to eq (dispatch-eligible). */
707 6 100         int cnum = (SvIOK(const_val) || SvNOK(const_val)) && !SvPOK(const_val);
    100          
    50          
708 6           OP *konst = newSVOP(OP_CONST, 0, newSVsv(const_val));
709 6 100         if (cnum) {
710 4           pat->undef_safe = 1;
711             } else {
712 2           pat->str_key = 1;
713 2           pat->key = newSVsv(const_val);
714             }
715 6           return sd_cmp(aTHX_ t, cnum, OP_EQ, OP_SEQ, konst);
716             }
717 1           croak("switch: unexpected bareword '%s' in case pattern (expected a number, "
718             "string, /regex/, [range or list], \\&name, sub {...}, a constant, "
719             "or == / eq $var)", errbuf);
720             }
721              
722             /* scalar literal: number -> ==, string -> eq */
723             {
724             int is_num;
725 143           OP *konst = sd_lex_literal(aTHX_ &is_num);
726 142 100         if (!is_num) {
727             /* exact string match: hash lookup is exactly eq semantics, so
728             * this arm is eligible for dispatch-table lowering. */
729 80           pat->str_key = 1;
730 80           pat->key = newSVsv(((SVOP *)konst)->op_sv);
731             } else {
732             /* numeric == is already looks_like_number-guarded: undef-safe. */
733 62           pat->undef_safe = 1;
734             }
735 142           return sd_cmp(aTHX_ t, is_num, OP_EQ, OP_SEQ, konst);
736             }
737             }
738              
739             /* True if any op in the tree introduces a lexical (my/our/local): such a block
740             * needs its own scope and must not be unwrapped. */
741 518           static int sd_has_intro(pTHX_ OP *o) {
742             OP *kid;
743 518 50         if (!o) return 0;
744 518 50         if (o->op_private & OPpLVAL_INTRO) return 1;
745 518 100         if (o->op_flags & OPf_KIDS) {
746 83 100         for (kid = cUNOPx(o)->op_first; kid; kid = OpSIBLING(kid))
    100          
747 56 50         if (sd_has_intro(aTHX_ kid)) return 1;
748             }
749 518           return 0;
750             }
751              
752             /* If `block` is a trivial `{ EXPR }` - a lineseq of exactly [nextstate, EXPR]
753             * with no lexical introductions - return the bare EXPR (freeing the wrapper)
754             * and set *simple. Such a branch carries no nextstate, so it is safe as a bare
755             * conditional arm and needs no enclosing scope. Otherwise return block as-is. */
756 469           static OP *sd_simplify_block(pTHX_ OP *block, int *simple) {
757 469           *simple = 0;
758 469 50         if (block->op_type == OP_LINESEQ) {
759 469           OP *first = cLISTOPx(block)->op_first;
760 469 50         if (first
761 469 50         && (first->op_type == OP_NEXTSTATE || first->op_type == OP_DBSTATE)
    0          
762 469 50         && OpSIBLING(first) && !OpSIBLING(OpSIBLING(first))
    50          
    50          
    100          
    50          
    50          
763 462 50         && !sd_has_intro(aTHX_ OpSIBLING(first))) {
    50          
764 462 50         OP *expr = OpSIBLING(first);
765             /* Lift EXPR out of the lineseq, leaving { nextstate } to be
766             * freed. Portable equivalent of
767             * op_sibling_splice(block, first, 1, NULL)
768             * which is not provided by core (or ppport.h) before 5.22.
769             * OpLASTSIB_set is backported by ppport.h to 5.14. */
770 462           OpLASTSIB_set(expr, NULL); /* detach EXPR: no parent, no sibling */
771 462           cLISTOPx(block)->op_last = first;
772 462           OpLASTSIB_set(first, block); /* nextstate is now the sole/last kid */
773 462           op_free(block);
774 462           *simple = 1;
775 462           return expr;
776             }
777             }
778 7           return block;
779             }
780              
781             /* Parse a whole `switch (EXPR) { ... }` construct (the lexer is positioned
782             * just after the `switch` keyword) and return its value-producing op tree. */
783 208           static OP *sd_parse_switch(pTHX) {
784             OP *scrutinee;
785             OP *conds[MAX_ARMS];
786             OP *blocks[MAX_ARMS];
787             SV *keys[MAX_ARMS]; /* string-literal key per arm, or NULL */
788             SV *vals[MAX_ARMS]; /* constant value per arm (borrowed), or NULL */
789 208           OP *default_block = NULL;
790 208           int narms = 0;
791             int i;
792             I32 c;
793 208           int all_simple = 1; /* every block is a bare-expression `{ EXPR }` */
794 208           int dispatchable = 1; /* every arm is (string key -> constant value) */
795             int topic_maybe_undef; /* scrutinee could be undef at run time */
796             SDTopic topic;
797             OP *assign;
798             OP *lhs;
799             OP *chain;
800             OP *seq;
801             OP *body;
802 208           OP *lln_prelude = NULL; /* `$lln = looks_like_number($topic)`, when hoisted */
803              
804             /* switch ( EXPR ) */
805 208           lex_read_space(0);
806 208 100         if (lex_peek_unichar(0) != '(')
807 1           croak("switch: expected '(' after 'switch'");
808 207           lex_read_unichar(0);
809 207           scrutinee = parse_fullexpr(0);
810 207           lex_read_space(0);
811 207 50         if (lex_peek_unichar(0) != ')')
812 0           croak("switch: expected ')' after switch expression");
813 207           lex_read_unichar(0);
814              
815             /* Decide how case tests will obtain the topic. A plain lexical or a
816             * constant scrutinee is side-effect-free to re-read, so each test reads it
817             * directly and the whole switch lowers to a bare conditional expression -
818             * no temp, no do-block - exactly like a hand-written if/elsif chain.
819             * Anything else (a call, an expression, a possibly-magical global) is
820             * stored once in a pad temp inside a value-returning block. */
821 207 100         if (scrutinee->op_type == OP_PADSV
822 41 50         && !(scrutinee->op_private & OPpLVAL_INTRO)) {
823 41           topic.kind = SDT_PAD;
824 41           topic.off = scrutinee->op_targ;
825 41           op_free(scrutinee);
826 41           scrutinee = NULL;
827             }
828 166 100         else if (scrutinee->op_type == OP_CONST) {
829 143           topic.kind = SDT_CONST;
830 143           topic.sv = newSVsv(((SVOP *)scrutinee)->op_sv);
831 143           op_free(scrutinee);
832 143           scrutinee = NULL;
833             }
834             else {
835             /* A unique pad-temp name per switch avoids "masks earlier
836             * declaration" warnings when switches share a lexical scope. */
837             static unsigned long sd_seq = 0;
838             char namebuf[64];
839 23           int n = my_snprintf(namebuf, sizeof(namebuf),
840             "$_Switch_Declare_topic_%lu", sd_seq++);
841 23           topic.kind = SDT_TEMP;
842 23           topic.off = pad_add_name_pvn(namebuf, (STRLEN)n, 0, NULL, NULL);
843             }
844              
845             /* A constant scrutinee whose value is defined can never be undef at run
846             * time, so its case tests need no undef guarding (and stay exactly as fast
847             * as before). Any other scrutinee might be undef. */
848 207 100         topic_maybe_undef = !(topic.kind == SDT_CONST && SvOK(topic.sv));
    50          
849              
850             /* Numeric guard sourcing: a defined constant topic folds looks_like_number
851             * at compile time; otherwise it is computed once into a pad temp and shared
852             * by every numeric arm (see the LLN_PAD prelude after the body is parsed). */
853 207           topic.lln_used = 0;
854 207 100         if (topic.kind == SDT_CONST && SvOK(topic.sv)) {
    50          
855 143           topic.lln_mode = LLN_CONST;
856 143           topic.lln_const = looks_like_number(topic.sv) ? 1 : 0;
857             } else {
858             static unsigned long sd_lln_seq = 0;
859             char lbuf[64];
860 64           int ln = my_snprintf(lbuf, sizeof(lbuf),
861             "$_Switch_Declare_lln_%lu", sd_lln_seq++);
862 64           topic.lln_mode = LLN_PAD;
863 64           topic.lln_off = pad_add_name_pvn(lbuf, (STRLEN)ln, 0, NULL, NULL);
864             }
865              
866             /* { ... } */
867 207           lex_read_space(0);
868 207 50         if (lex_peek_unichar(0) != '{')
869 0           croak("switch: expected '{' to open switch body");
870 207           lex_read_unichar(0);
871              
872 469           while (1) {
873             SV *kw;
874 676           lex_read_space(0);
875 676           c = lex_peek_unichar(0);
876 676 100         if (c == '}') { lex_read_unichar(0); break; }
877 483 50         if (c == -1) croak("switch: unexpected end of input in switch body");
878              
879 483           kw = sd_lex_read_ident(aTHX);
880 483 50         if (!kw) croak("switch: expected 'case' or 'default'");
881              
882 483 100         if (strEQ(SvPV_nolen(kw), "case")) {
883             OP *cond, *blk;
884             SDPat pat;
885             int simple;
886 291 100         if (default_block)
887 1           croak("switch: 'case' after 'default' is not allowed");
888 290 50         if (narms >= MAX_ARMS)
889 0           croak("switch: too many case arms");
890 290           lex_read_space(0);
891 290           cond = sd_parse_case_cond(aTHX_ &topic, &pat);
892             /* A value pattern that could match or warn on an undef topic
893             * (string eq / regex / list / range) is guarded with defined($topic)
894             * so undef can only be caught by an explicit `case undef` (or fall
895             * through to default). Patterns that are already undef-safe - numeric
896             * (looks_like_number-guarded), ref/reftype/isa, and undef itself -
897             * skip the guard. The shared default stays the single chain tail. */
898 280 100         if (topic_maybe_undef && !pat.undef_safe)
    100          
899 53           cond = newLOGOP(OP_AND, 0,
900             newUNOP(OP_DEFINED, 0, sd_topic(aTHX_ &topic)),
901             cond);
902 280           lex_read_space(0);
903 280 100         if (lex_peek_unichar(0) != '{')
904 1           croak("switch: expected '{' after case pattern");
905 279           blk = sd_simplify_block(aTHX_ parse_block(0), &simple);
906 279           all_simple &= simple;
907 279           conds[narms] = cond;
908 279           blocks[narms] = blk;
909 279           keys[narms] = pat.key; /* NULL unless an exact string key */
910 558           vals[narms] = (blk->op_type == OP_CONST)
911 279 100         ? ((SVOP *)blk)->op_sv : NULL;
912             /* dispatchable iff string key AND constant-valued block */
913 279 100         if (!pat.key || !vals[narms]) dispatchable = 0;
    100          
914 279           narms++;
915             }
916 192 100         else if (strEQ(SvPV_nolen(kw), "default")) {
917 191 100         if (default_block)
918 1           croak("switch: multiple 'default' blocks");
919 190           lex_read_space(0);
920 190 50         if (lex_peek_unichar(0) != '{')
921 0           croak("switch: expected '{' after 'default'");
922             {
923             int simple;
924 190           default_block = sd_simplify_block(aTHX_ parse_block(0), &simple);
925 190           all_simple &= simple;
926             }
927             }
928             else {
929 1           croak("switch: expected 'case' or 'default', got '%s'",
930             SvPV_nolen(kw));
931             }
932 469           SvREFCNT_dec(kw);
933             }
934              
935 193 100         if (narms == 0 && !default_block)
    100          
936 1           croak("switch: empty switch body");
937              
938             /* Try the O(1) dispatch-table lowering: every arm maps a distinct string
939             * literal to a constant value, and there are enough of them to beat a
940             * linear eq chain. Build a compile-time constant hash and emit a single
941             * lookup: exists $H{topic} ? $H{topic} : DEFAULT . */
942 192           chain = NULL;
943 192 100         if (dispatchable && narms >= SD_DISPATCH_MIN) {
    100          
944             static unsigned long sd_dt_seq = 0;
945             char hashname[64];
946 9           int hn = my_snprintf(hashname, sizeof(hashname),
947             "Switch::Declare::_dt%lu", sd_dt_seq++);
948 9           GV *gv = gv_fetchpvn(hashname, (STRLEN)hn, GV_ADD, SVt_PVHV);
949 9 50         HV *hv = GvHVn(gv);
950 9           int dup = 0, any_undef = 0;
951             /* If the topic could be undef, look it up under a guarded key so an
952             * undef topic misses the table (-> default) instead of warning. The
953             * sentinel is a binary string we treat as reserved; in the wildly
954             * unlikely event a real key collides with it, fall back to the chain. */
955 9           SV *sentinel = topic_maybe_undef
956 9 100         ? newSVpvn("\1\0Switch::Declare::undef-key\0\1", 28) : NULL;
957 44 100         for (i = 0; i < narms; i++) {
958             STRLEN klen;
959 36           const char *kpv = SvPV(keys[i], klen);
960 36 100         if (hv_exists(hv, kpv, klen)) { dup = 1; break; }
961 35 50         if (!SvOK(vals[i])) any_undef = 1;
962 35           (void)hv_store(hv, kpv, klen, newSVsv(vals[i]), 0);
963             }
964 9 100         if (!dup && sentinel) {
    100          
965             STRLEN sl;
966 5           const char *sp = SvPV(sentinel, sl);
967 5 50         if (hv_exists(hv, sp, sl)) dup = 1;
968             }
969 9 100         if (dup) {
970 1           hv_clear(hv); /* duplicate keys: fall back to chain */
971             } else {
972 8           OP *deflt = default_block ? default_block
973 8 100         : newOP(OP_UNDEF, 0);
974 8 50         if (any_undef) {
975             /* a value can be undef, so a miss is indistinguishable from a
976             * hit by definedness: test membership explicitly. */
977 0           OP *cond = newUNOP(OP_EXISTS, 0, sd_helem(aTHX_ gv, &topic, sentinel));
978 0           chain = newCONDOP(0, cond, sd_helem(aTHX_ gv, &topic, sentinel), deflt);
979             } else {
980             /* all values are defined: one lookup, miss -> default via //. */
981 8           chain = newLOGOP(OP_DOR, 0, sd_helem(aTHX_ gv, &topic, sentinel), deflt);
982             }
983             /* the unused per-arm condition and block ops are now dead */
984 41 100         for (i = 0; i < narms; i++) { op_free(conds[i]); op_free(blocks[i]); }
985             }
986 9 100         if (sentinel) SvREFCNT_dec(sentinel);
987             }
988              
989             /* Otherwise (or on fall-back), build the conditional chain right-to-left. */
990 192 100         if (!chain) {
991 184           chain = default_block ? default_block
992 184 100         : newOP(OP_UNDEF, 0);
993 429 100         for (i = narms - 1; i >= 0; i--)
994 245           chain = newCONDOP(0, conds[i], blocks[i], chain);
995             }
996              
997 470 100         for (i = 0; i < narms; i++)
998 278 100         if (keys[i]) SvREFCNT_dec(keys[i]);
999              
1000             /* If any numeric arm used the hoisted guard, build the one-time
1001             * `$lln = looks_like_number($topic)` that they all read. */
1002 192 100         if (topic.lln_mode == LLN_PAD && topic.lln_used) {
    100          
1003 19           OP *llhs = newOP(OP_PADSV, 0);
1004 19           llhs->op_targ = topic.lln_off;
1005 19           llhs->op_private |= OPpLVAL_INTRO; /* my */
1006 19           lln_prelude = newASSIGNOP(OPf_STACKED, llhs, 0,
1007             sd_looks_number_op(aTHX_ &topic));
1008             }
1009              
1010 192 100         if (topic.kind != SDT_TEMP) {
1011 169 100         if (topic.kind == SDT_CONST) SvREFCNT_dec(topic.sv);
1012 169 100         if (!lln_prelude && all_simple)
    100          
1013             /* Fastest path: a plain lexical/constant topic, and either a
1014             * dispatch lookup or a chain of bare `{ EXPR }` arms. The whole
1015             * switch is literally an expression - no temp, no scope, no
1016             * nextstate - as fast as hand-written code. */
1017 150           return chain;
1018 19 100         if (lln_prelude) {
1019             /* Hoisted numeric guard over a plain lexical: wrap in a block that
1020             * computes $lln once, then runs the chain (which reads $lln). */
1021 14           seq = op_append_list(OP_LINESEQ, newSTATEOP(0, NULL, lln_prelude),
1022             newSTATEOP(0, NULL, chain));
1023 14           seq->op_flags |= OPf_PARENS;
1024 14           return op_scope(seq);
1025             }
1026             /* Some arm is a multi-statement block (carries a nextstate). Wrap in a
1027             * single enter/leave (OPf_PARENS makes op_scope emit OP_LEAVE) so that
1028             * nextstate cannot reset the stack base of a surrounding expression. */
1029 5           chain->op_flags |= OPf_PARENS;
1030 5           return op_scope(chain);
1031             }
1032              
1033             /* General path: my $topic = SCRUTINEE; [my $lln = looks_like_number($topic);]
1034             * , wrapped as a value-returning block so the scrutinee is evaluated
1035             * exactly once. OPf_PARENS forces op_scope down its enter/leave path
1036             * (OP_LEAVE) for a proper stack frame; without it a bare OP_SCOPE lets the
1037             * inner nextstate reset the stack base and clobber a surrounding expression
1038             * (e.g. N + switch(...)). */
1039 23           lhs = newOP(OP_PADSV, 0);
1040 23           lhs->op_targ = topic.off;
1041 23           lhs->op_private |= OPpLVAL_INTRO; /* my */
1042 23           assign = newASSIGNOP(OPf_STACKED, lhs, 0, scrutinee);
1043 23           seq = newSTATEOP(0, NULL, assign);
1044 23 100         if (lln_prelude)
1045             /* $lln computed after $topic is stored, before the chain reads it. */
1046 5           seq = op_append_list(OP_LINESEQ, seq,
1047             newSTATEOP(0, NULL, lln_prelude));
1048 23           seq = op_append_list(OP_LINESEQ, seq, newSTATEOP(0, NULL, chain));
1049 23           seq->op_flags |= OPf_PARENS;
1050 23           body = op_scope(seq);
1051              
1052 23           return body;
1053             }
1054              
1055             /* True if the Switch::Declare lexical pragma is in scope at the current
1056             * point of compilation (set by import via $^H{'Switch::Declare'}). */
1057 211           static int sd_in_scope(pTHX) {
1058 211           HV *hints = GvHV(PL_hintgv);
1059             SV **ent;
1060 211 50         if (!hints) return 0;
1061 211           ent = hv_fetchs(hints, "Switch::Declare", 0);
1062 211 100         return ent && SvTRUE(*ent);
    50          
1063             }
1064              
1065 118308           static int sd_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op_ptr) {
1066 118308 100         if (kwlen == 6 && memEQ(kw, "switch", 6) && sd_in_scope(aTHX)) {
    100          
    100          
1067 208           *op_ptr = sd_parse_switch(aTHX);
1068 192           return KEYWORD_PLUGIN_EXPR;
1069             }
1070 118100           return sd_next_keyword_plugin(aTHX_ kw, kwlen, op_ptr);
1071             }
1072              
1073             MODULE = Switch::Declare PACKAGE = Switch::Declare
1074             PROTOTYPES: DISABLE
1075              
1076             BOOT:
1077 22           sd_next_keyword_plugin = PL_keyword_plugin;
1078 22           PL_keyword_plugin = sd_keyword_plugin;
1079 22           XopENTRY_set(&sd_looks_number_xop, xop_name, "sd_looks_number");
1080 22           XopENTRY_set(&sd_looks_number_xop, xop_desc, "Switch::Declare numeric topic guard");
1081 22           XopENTRY_set(&sd_looks_number_xop, xop_class, OA_UNOP);
1082 22           Perl_custom_op_register(aTHX_ sd_pp_looks_number, &sd_looks_number_xop);
1083 22           XopENTRY_set(&sd_reftype_xop, xop_name, "sd_reftype");
1084 22           XopENTRY_set(&sd_reftype_xop, xop_desc, "Switch::Declare reftype()");
1085 22           XopENTRY_set(&sd_reftype_xop, xop_class, OA_UNOP);
1086 22           Perl_custom_op_register(aTHX_ sd_pp_reftype, &sd_reftype_xop);
1087 22           XopENTRY_set(&sd_rxmatch_xop, xop_name, "sd_rxmatch");
1088 22           XopENTRY_set(&sd_rxmatch_xop, xop_desc, "Switch::Declare =~ match");
1089 22           XopENTRY_set(&sd_rxmatch_xop, xop_class, OA_BINOP);
1090 22           Perl_custom_op_register(aTHX_ sd_pp_rxmatch, &sd_rxmatch_xop);
1091              
1092             bool
1093             _isa(obj, klass)
1094             SV *obj
1095             SV *klass
1096             CODE:
1097             /* fast @ISA check: a blessed object derived from `klass`. sv_isobject
1098             * keeps plain strings/non-objects from matching; never dies. */
1099 24 100         RETVAL = (sv_isobject(obj)
1100 12 100         && sv_derived_from(obj, SvPV_nolen(klass))) ? 1 : 0;
    100          
1101             OUTPUT:
1102             RETVAL