File Coverage

Declare.xs
Criterion Covered Total %
statement 318 337 94.3
branch 203 262 77.4
condition n/a
subroutine n/a
pod n/a
total 521 599 86.9


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             #define MAX_ARMS 4096
8              
9             /* Previous keyword plugin in the chain. */
10             static int (*sd_next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
11              
12             /* Read a bareword identifier from the lexer (or NULL if none). */
13 286           static SV *sd_lex_read_ident(pTHX) {
14 286           SV *buf = newSVpvs("");
15             I32 c;
16             while (1) {
17 1743           c = lex_peek_unichar(0);
18 1743 50         if (c == -1) break;
19 1743 50         if (!isALNUM(c) && c != '_') break;
    100          
    50          
20 1457           sv_catpvf(buf, "%c", (int)c);
21 1457           lex_read_unichar(0);
22             }
23 286 50         if (SvCUR(buf) == 0) {
24 0           SvREFCNT_dec(buf);
25 0           return NULL;
26             }
27 286           return buf;
28             }
29              
30             /* Read a possibly package-qualified sub name (Foo::bar, Foo'bar). */
31 10           static SV *sd_lex_subname(pTHX) {
32 10           SV *buf = newSVpvs("");
33             I32 c;
34             while (1) {
35 69           c = lex_peek_unichar(0);
36 69 50         if (c == -1) break;
37 69 50         if (isALNUM(c) || c == '_') {
    100          
    50          
38 58           sv_catpvf(buf, "%c", (int)c);
39 58           lex_read_unichar(0);
40 11 100         } else if (c == ':' && PL_parser->bufptr[0] == ':'
    50          
41 1 50         && PL_parser->bufptr[1] == ':') {
42 1           sv_catpvs(buf, "::");
43 1           lex_read_unichar(0);
44 1           lex_read_unichar(0);
45             } else break;
46             }
47 10 100         if (SvCUR(buf) == 0) {
48 1           SvREFCNT_dec(buf);
49 1           return NULL;
50             }
51 9           return buf;
52             }
53              
54             /* Hand-lex a numeric literal (optional sign, integer/float/exponent). */
55 79           static SV *sd_lex_number(pTHX) {
56 79           SV *buf = newSVpvs("");
57 79           I32 c = lex_peek_unichar(0);
58 79           int seen_dot = 0, seen_digit = 0;
59 79 100         if (c == '-' || c == '+') {
    100          
60 5           sv_catpvf(buf, "%c", (int)c);
61 5           lex_read_unichar(0);
62             }
63             while (1) {
64 183           c = lex_peek_unichar(0);
65 183 100         if (c >= '0' && c <= '9') { seen_digit = 1; }
    100          
66 83 100         else if (c == '.' && !seen_dot) {
    50          
67             /* Only a decimal point if followed by a digit; otherwise it is
68             * the '..' range operator (or a terminator) - leave it alone. */
69 4 50         char next = (PL_parser->bufptr[0] == '.') ? PL_parser->bufptr[1] : '\0';
70 4 100         if (next < '0' || next > '9') break;
    50          
71 3           seen_dot = 1;
72             }
73 79 100         else if ((c == 'e' || c == 'E') && seen_digit) {
    50          
    50          
74 1           sv_catpvf(buf, "%c", (int)c);
75 1           lex_read_unichar(0);
76 1           c = lex_peek_unichar(0);
77 1 50         if (c == '-' || c == '+') {
    50          
78 0           sv_catpvf(buf, "%c", (int)c);
79 0           lex_read_unichar(0);
80             }
81 1           continue;
82             }
83             else break;
84 103           sv_catpvf(buf, "%c", (int)c);
85 103           lex_read_unichar(0);
86             }
87 79 50         if (!seen_digit) {
88 0           SvREFCNT_dec(buf);
89 0           croak("switch: malformed numeric case pattern");
90             }
91 79           return buf;
92             }
93              
94             /* Hand-lex a quoted string literal ('...' or "..."), basic backslash escapes. */
95 75           static SV *sd_lex_string(pTHX) {
96 75           I32 quote = lex_read_unichar(0);
97 75           SV *sv = newSVpvs("");
98             I32 c;
99             while (1) {
100 193           c = lex_read_unichar(0);
101 193 100         if (c == -1) croak("switch: unterminated string in case pattern");
102 192 100         if (c == '\\') {
103 2           I32 next = lex_read_unichar(0);
104 2 50         if (next == -1) croak("switch: unterminated string in case pattern");
105 2 100         if (quote == '"') {
106 1           switch (next) {
107 0           case 'n': sv_catpvs(sv, "\n"); break;
108 1           case 't': sv_catpvs(sv, "\t"); break;
109 0           case 'r': sv_catpvs(sv, "\r"); break;
110 0           case '0': sv_catpvs(sv, "\0"); break;
111 0           default: sv_catpvf(sv, "%c", (int)next); break;
112             }
113             } else {
114             /* single quotes: only \\ and \' are special */
115 1 50         if (next != '\\' && next != '\'')
    50          
116 1           sv_catpvf(sv, "%c", '\\');
117 1           sv_catpvf(sv, "%c", (int)next);
118             }
119 190 100         } else if (c == quote) {
120 74           break;
121             } else {
122 116           sv_catpvf(sv, "%c", (int)c);
123             }
124             }
125 74           return sv;
126             }
127              
128             /* Describes where the matched topic comes from. When the scrutinee is already
129             * a plain lexical or a constant, each case test re-reads it directly (just like
130             * a hand-written if/elsif chain) and no temp / do-block is needed. Otherwise
131             * the scrutinee is stored once in a pad temp. */
132             #define SDT_TEMP 0 /* stored once in pad temp `off` (do-block) */
133             #define SDT_PAD 1 /* re-read scrutinee's own lexical at `off` */
134             #define SDT_CONST 2 /* re-read a constant value */
135             typedef struct {
136             int kind;
137             PADOFFSET off;
138             SV *sv;
139             } SDTopic;
140              
141             /* A fresh op yielding the topic value. */
142 189           static OP *sd_topic(pTHX_ SDTopic *t) {
143 189 100         if (t->kind == SDT_CONST)
144 136           return newSVOP(OP_CONST, 0, newSVsv(t->sv));
145             {
146 53           OP *o = newOP(OP_PADSV, 0);
147 53           o->op_targ = t->off;
148 53           return o;
149             }
150             }
151              
152             /* When every arm maps a distinct string-literal key to a constant value (a
153             * lookup table) and there are at least this many of them, the whole switch is
154             * lowered to a single O(1) hash lookup against a compile-time constant hash
155             * instead of an O(n) chain of eq tests. */
156             #define SD_DISPATCH_MIN 4
157              
158             /* What a case pattern was, for dispatch eligibility. */
159             typedef struct {
160             int str_key; /* 1 if an exact string-literal pattern (eq) */
161             SV *key; /* the literal (owned) when str_key */
162             } SDPat;
163              
164             /* A fresh $PKGHASH{ topic } element op, where the hash is the package
165             * variable named by `gv`. Referencing the hash through a GV (rather than an
166             * op-constant hashref) keeps the dispatch table thread-safe: op constants are
167             * cloned per-thread, which would dangle a reference to a shared HV. */
168 6           static OP *sd_helem(pTHX_ GV *gv, SDTopic *t) {
169 6           OP *gvop = newGVOP(OP_GV, 0, gv);
170 6           OP *deref = newUNOP(OP_RV2HV, OPf_REF, gvop);
171 6           return newBINOP(OP_HELEM, 0, deref, sd_topic(aTHX_ t));
172             }
173              
174             /* Read a literal (number or string) into an OP_CONST, setting *is_num. */
175 154           static OP *sd_lex_literal(pTHX_ int *is_num) {
176 154           I32 c = lex_peek_unichar(0);
177 154 100         if (c == '"' || c == '\'') {
    100          
178 75           *is_num = 0;
179 75           return newSVOP(OP_CONST, 0, sd_lex_string(aTHX));
180             }
181 79 100         if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.') {
    50          
    100          
    50          
    0          
182 79           *is_num = 1;
183 79           return newSVOP(OP_CONST, 0, sd_lex_number(aTHX));
184             }
185 0           croak("switch: expected a number or string literal");
186             return NULL; /* not reached */
187             }
188              
189             /* topic CMP const, where CMP is numeric (==/>=/<=) or string (eq/ge/le). */
190 152           static OP *sd_cmp(pTHX_ SDTopic *t, int is_num, I32 numop, I32 strop, OP *konst) {
191 152 100         return newBINOP(is_num ? numop : strop, 0, sd_topic(aTHX_ t), konst);
192             }
193              
194             /* Build CALLEE->( topic ) as an entersub. The OP_ENTERSUB checker inserts the
195             * pushmark itself, so we must NOT add one - a second pushmark leaves a dangling
196             * mark that corrupts a surrounding list/aassign at runtime. */
197 19           static OP *sd_predicate_call(pTHX_ OP *callee, SDTopic *t) {
198 19           OP *args = op_append_elem(OP_LIST, sd_topic(aTHX_ t), callee);
199 19           return newUNOP(OP_ENTERSUB, OPf_STACKED, args);
200             }
201              
202             /* Parse one case PATTERN from the lexer and return its boolean condition op,
203             * testing the topic. Fills *pat describing the pattern (for dispatch). */
204 167           static OP *sd_parse_case_cond(pTHX_ SDTopic *t, SDPat *pat) {
205 167           I32 c = lex_peek_unichar(0);
206 167           pat->str_key = 0;
207 167           pat->key = NULL;
208              
209             /* regex: /PATTERN/flags -> native topic =~ /PATTERN/flags
210             * The pattern is compiled once, here at compile time, and bound to a
211             * standard OP_MATCH - no runtime helper, no per-match recompilation. */
212 167 100         if (c == '/') {
213 14           SV *pat = newSVpvs("");
214 14           U32 rxflags = 0;
215             REGEXP *rx;
216             PMOP *pmop;
217             OP *target;
218 14           lex_read_unichar(0);
219             while (1) {
220 85           c = lex_read_unichar(0);
221 85 100         if (c == -1) croak("switch: unterminated regex in case pattern");
222 84 100         if (c == '\\') {
223 5           I32 n = lex_read_unichar(0);
224 5 50         if (n == -1) croak("switch: unterminated regex in case pattern");
225 5           sv_catpvf(pat, "%c", '\\');
226 5           sv_catpvf(pat, "%c", (int)n);
227 5           continue;
228             }
229 79 100         if (c == '/') break;
230 66           sv_catpvf(pat, "%c", (int)c);
231             }
232 20 100         while (isALPHA((c = lex_peek_unichar(0)))) {
233 8           switch (c) {
234 3           case 'i': rxflags |= PMf_FOLD; break;
235 2           case 'm': rxflags |= PMf_MULTILINE; break;
236 1           case 's': rxflags |= PMf_SINGLELINE; break;
237 1           case 'x': rxflags |= PMf_EXTENDED; break;
238 1           default: croak("switch: unsupported regex flag '%c' in case pattern", (int)c);
239             }
240 7           lex_read_unichar(0);
241             }
242 12           rx = pregcomp(pat, rxflags);
243 12           SvREFCNT_dec(pat);
244 12           pmop = (PMOP *)newPMOP(OP_MATCH, 0);
245 12           PM_SETRE(pmop, rx);
246             /* bind the topic as the match target ($topic =~ ...) */
247 12           target = sd_topic(aTHX_ t);
248 12           ((PMOP *)pmop)->op_first = target;
249 12           ((PMOP *)pmop)->op_last = target;
250 12           OpLASTSIB_set(target, (OP *)pmop);
251 12           pmop->op_flags |= OPf_KIDS | OPf_STACKED;
252 12           return (OP *)pmop;
253             }
254              
255             /* predicate: \&name -> name($topic) */
256 153 100         if (c == '\\') {
257             SV *name;
258             GV *gv;
259             OP *cvop;
260 11           lex_read_unichar(0);
261 11 100         if (lex_peek_unichar(0) != '&')
262 1           croak("switch: expected '&' after '\\' in case predicate");
263 10           lex_read_unichar(0);
264 10           name = sd_lex_subname(aTHX);
265 10 100         if (!name) croak("switch: expected sub name after '\\&'");
266 9           gv = gv_fetchpv(SvPV_nolen(name), GV_ADD, SVt_PVCV);
267 9           SvREFCNT_dec(name);
268 9           cvop = newUNOP(OP_RV2CV, 0, newGVOP(OP_GV, 0, gv));
269 9           return sd_predicate_call(aTHX_ cvop, t);
270             }
271              
272             /* bracket: [LO .. HI] range, or [a, b, c] membership list */
273 142 100         if (c == '[') {
274             int is_num;
275             OP *first;
276 18           lex_read_unichar(0);
277 18           lex_read_space(0);
278 18           first = sd_lex_literal(aTHX_ &is_num);
279 18           lex_read_space(0);
280 18 100         if (lex_peek_unichar(0) == '.') {
281             /* range: [LO .. HI] */
282             OP *hi;
283             int hi_num;
284 10           lex_read_unichar(0);
285 10 100         if (lex_peek_unichar(0) != '.')
286 1           croak("switch: expected '..' in range case pattern");
287 9           lex_read_unichar(0);
288 9           lex_read_space(0);
289 9           hi = sd_lex_literal(aTHX_ &hi_num);
290 9           lex_read_space(0);
291 9 50         if (lex_peek_unichar(0) != ']')
292 0           croak("switch: expected ']' to close range case pattern");
293 9           lex_read_unichar(0);
294 9           return newLOGOP(OP_AND, 0,
295             sd_cmp(aTHX_ t, is_num, OP_GE, OP_SGE, first),
296             sd_cmp(aTHX_ t, hi_num, OP_LE, OP_SLE, hi));
297             }
298             /* membership list: [a, b, c] -> OR-chain of equality tests */
299             {
300 8           OP *chain = sd_cmp(aTHX_ t, is_num, OP_EQ, OP_SEQ, first);
301 14           while (1) {
302             OP *elt;
303             int en;
304 22           lex_read_space(0);
305 22 100         if (lex_peek_unichar(0) == ',') lex_read_unichar(0);
306 22           lex_read_space(0);
307 22 100         if (lex_peek_unichar(0) == ']') { lex_read_unichar(0); break; }
308 14           elt = sd_lex_literal(aTHX_ &en);
309 14           chain = newLOGOP(OP_OR, 0, chain,
310             sd_cmp(aTHX_ t, en, OP_EQ, OP_SEQ, elt));
311             }
312 8           return chain;
313             }
314             }
315              
316             /* inline predicate: sub { ... } -> (sub { ... })->($topic) */
317 124 100         if (isALPHA(c) || c == '_') {
    50          
318 11           SV *word = sd_lex_read_ident(aTHX);
319 11 50         int is_sub = word && strEQ(SvPV_nolen(word), "sub");
    50          
320 11 50         if (word) SvREFCNT_dec(word);
321 11 50         if (is_sub) {
322             I32 floor;
323             OP *body, *anon;
324 11           lex_read_space(0);
325 11 100         if (lex_peek_unichar(0) != '{')
326 1           croak("switch: expected '{' after 'sub' in case predicate");
327 10           floor = start_subparse(FALSE, CVf_ANON);
328 10           body = parse_block(0);
329 10           anon = newANONATTRSUB(floor, NULL, NULL, body);
330 10           return sd_predicate_call(aTHX_ anon, t);
331             }
332 0           croak("switch: unexpected bareword in case pattern (expected a number, "
333             "string, /regex/, [range or list], \\&name, or sub {...})");
334             }
335              
336             /* scalar literal: number -> ==, string -> eq */
337             {
338             int is_num;
339 113           OP *konst = sd_lex_literal(aTHX_ &is_num);
340 112 100         if (!is_num) {
341             /* exact string match: hash lookup is exactly eq semantics, so
342             * this arm is eligible for dispatch-table lowering. */
343 65           pat->str_key = 1;
344 65           pat->key = newSVsv(((SVOP *)konst)->op_sv);
345             }
346 112           return sd_cmp(aTHX_ t, is_num, OP_EQ, OP_SEQ, konst);
347             }
348             }
349              
350             /* True if any op in the tree introduces a lexical (my/our/local): such a block
351             * needs its own scope and must not be unwrapped. */
352 309           static int sd_has_intro(pTHX_ OP *o) {
353             OP *kid;
354 309 50         if (!o) return 0;
355 309 50         if (o->op_private & OPpLVAL_INTRO) return 1;
356 309 100         if (o->op_flags & OPf_KIDS) {
357 76 100         for (kid = cUNOPx(o)->op_first; kid; kid = OpSIBLING(kid))
    100          
358 52 50         if (sd_has_intro(aTHX_ kid)) return 1;
359             }
360 309           return 0;
361             }
362              
363             /* If `block` is a trivial `{ EXPR }` - a lineseq of exactly [nextstate, EXPR]
364             * with no lexical introductions - return the bare EXPR (freeing the wrapper)
365             * and set *simple. Such a branch carries no nextstate, so it is safe as a bare
366             * conditional arm and needs no enclosing scope. Otherwise return block as-is. */
367 264           static OP *sd_simplify_block(pTHX_ OP *block, int *simple) {
368 264           *simple = 0;
369 264 50         if (block->op_type == OP_LINESEQ) {
370 264           OP *first = cLISTOPx(block)->op_first;
371 264 50         if (first
372 264 50         && (first->op_type == OP_NEXTSTATE || first->op_type == OP_DBSTATE)
    0          
373 264 50         && OpSIBLING(first) && !OpSIBLING(OpSIBLING(first))
    50          
    50          
    100          
    50          
    50          
374 257 50         && !sd_has_intro(aTHX_ OpSIBLING(first))) {
    50          
375 257 50         OP *expr = OpSIBLING(first);
376             /* Lift EXPR out of the lineseq, leaving { nextstate } to be
377             * freed. Portable equivalent of
378             * op_sibling_splice(block, first, 1, NULL)
379             * which is not provided by core (or ppport.h) before 5.22.
380             * OpLASTSIB_set is backported by ppport.h to 5.14. */
381 257           OpLASTSIB_set(expr, NULL); /* detach EXPR: no parent, no sibling */
382 257           cLISTOPx(block)->op_last = first;
383 257           OpLASTSIB_set(first, block); /* nextstate is now the sole/last kid */
384 257           op_free(block);
385 257           *simple = 1;
386 257           return expr;
387             }
388             }
389 7           return block;
390             }
391              
392             /* Parse a whole `switch (EXPR) { ... }` construct (the lexer is positioned
393             * just after the `switch` keyword) and return its value-producing op tree. */
394 120           static OP *sd_parse_switch(pTHX) {
395             OP *scrutinee;
396             OP *conds[MAX_ARMS];
397             OP *blocks[MAX_ARMS];
398             SV *keys[MAX_ARMS]; /* string-literal key per arm, or NULL */
399             SV *vals[MAX_ARMS]; /* constant value per arm (borrowed), or NULL */
400 120           OP *default_block = NULL;
401 120           int narms = 0;
402             int i;
403             I32 c;
404 120           int all_simple = 1; /* every block is a bare-expression `{ EXPR }` */
405 120           int dispatchable = 1; /* every arm is (string key -> constant value) */
406             SDTopic topic;
407             OP *assign;
408             OP *lhs;
409             OP *chain;
410             OP *seq;
411             OP *body;
412              
413             /* switch ( EXPR ) */
414 120           lex_read_space(0);
415 120 100         if (lex_peek_unichar(0) != '(')
416 1           croak("switch: expected '(' after 'switch'");
417 119           lex_read_unichar(0);
418 119           scrutinee = parse_fullexpr(0);
419 119           lex_read_space(0);
420 119 50         if (lex_peek_unichar(0) != ')')
421 0           croak("switch: expected ')' after switch expression");
422 119           lex_read_unichar(0);
423              
424             /* Decide how case tests will obtain the topic. A plain lexical or a
425             * constant scrutinee is side-effect-free to re-read, so each test reads it
426             * directly and the whole switch lowers to a bare conditional expression -
427             * no temp, no do-block - exactly like a hand-written if/elsif chain.
428             * Anything else (a call, an expression, a possibly-magical global) is
429             * stored once in a pad temp inside a value-returning block. */
430 119 100         if (scrutinee->op_type == OP_PADSV
431 13 50         && !(scrutinee->op_private & OPpLVAL_INTRO)) {
432 13           topic.kind = SDT_PAD;
433 13           topic.off = scrutinee->op_targ;
434 13           op_free(scrutinee);
435 13           scrutinee = NULL;
436             }
437 106 100         else if (scrutinee->op_type == OP_CONST) {
438 94           topic.kind = SDT_CONST;
439 94           topic.sv = newSVsv(((SVOP *)scrutinee)->op_sv);
440 94           op_free(scrutinee);
441 94           scrutinee = NULL;
442             }
443             else {
444             /* A unique pad-temp name per switch avoids "masks earlier
445             * declaration" warnings when switches share a lexical scope. */
446             static unsigned long sd_seq = 0;
447             char namebuf[64];
448 12           int n = my_snprintf(namebuf, sizeof(namebuf),
449             "$_Switch_Declare_topic_%lu", sd_seq++);
450 12           topic.kind = SDT_TEMP;
451 12           topic.off = pad_add_name_pvn(namebuf, (STRLEN)n, 0, NULL, NULL);
452             }
453              
454             /* { ... } */
455 119           lex_read_space(0);
456 119 50         if (lex_peek_unichar(0) != '{')
457 0           croak("switch: expected '{' to open switch body");
458 119           lex_read_unichar(0);
459              
460 264           while (1) {
461             SV *kw;
462 383           lex_read_space(0);
463 383           c = lex_peek_unichar(0);
464 383 100         if (c == '}') { lex_read_unichar(0); break; }
465 275 50         if (c == -1) croak("switch: unexpected end of input in switch body");
466              
467 275           kw = sd_lex_read_ident(aTHX);
468 275 50         if (!kw) croak("switch: expected 'case' or 'default'");
469              
470 275 100         if (strEQ(SvPV_nolen(kw), "case")) {
471             OP *cond, *blk;
472             SDPat pat;
473             int simple;
474 168 100         if (default_block)
475 1           croak("switch: 'case' after 'default' is not allowed");
476 167 50         if (narms >= MAX_ARMS)
477 0           croak("switch: too many case arms");
478 167           lex_read_space(0);
479 167           cond = sd_parse_case_cond(aTHX_ &topic, &pat);
480 160           lex_read_space(0);
481 160 100         if (lex_peek_unichar(0) != '{')
482 1           croak("switch: expected '{' after case pattern");
483 159           blk = sd_simplify_block(aTHX_ parse_block(0), &simple);
484 159           all_simple &= simple;
485 159           conds[narms] = cond;
486 159           blocks[narms] = blk;
487 159           keys[narms] = pat.key; /* NULL unless an exact string key */
488 318           vals[narms] = (blk->op_type == OP_CONST)
489 159 100         ? ((SVOP *)blk)->op_sv : NULL;
490             /* dispatchable iff string key AND constant-valued block */
491 159 100         if (!pat.key || !vals[narms]) dispatchable = 0;
    100          
492 159           narms++;
493             }
494 107 100         else if (strEQ(SvPV_nolen(kw), "default")) {
495 106 100         if (default_block)
496 1           croak("switch: multiple 'default' blocks");
497 105           lex_read_space(0);
498 105 50         if (lex_peek_unichar(0) != '{')
499 0           croak("switch: expected '{' after 'default'");
500             {
501             int simple;
502 105           default_block = sd_simplify_block(aTHX_ parse_block(0), &simple);
503 105           all_simple &= simple;
504             }
505             }
506             else {
507 1           croak("switch: expected 'case' or 'default', got '%s'",
508             SvPV_nolen(kw));
509             }
510 264           SvREFCNT_dec(kw);
511             }
512              
513 108 100         if (narms == 0 && !default_block)
    100          
514 1           croak("switch: empty switch body");
515              
516             /* Try the O(1) dispatch-table lowering: every arm maps a distinct string
517             * literal to a constant value, and there are enough of them to beat a
518             * linear eq chain. Build a compile-time constant hash and emit a single
519             * lookup: exists $H{topic} ? $H{topic} : DEFAULT . */
520 107           chain = NULL;
521 107 100         if (dispatchable && narms >= SD_DISPATCH_MIN) {
    100          
522             static unsigned long sd_dt_seq = 0;
523             char hashname[64];
524 7           int hn = my_snprintf(hashname, sizeof(hashname),
525             "Switch::Declare::_dt%lu", sd_dt_seq++);
526 7           GV *gv = gv_fetchpvn(hashname, (STRLEN)hn, GV_ADD, SVt_PVHV);
527 7 50         HV *hv = GvHVn(gv);
528 7           int dup = 0, any_undef = 0;
529 34 100         for (i = 0; i < narms; i++) {
530             STRLEN klen;
531 28           const char *kpv = SvPV(keys[i], klen);
532 28 100         if (hv_exists(hv, kpv, klen)) { dup = 1; break; }
533 27 50         if (!SvOK(vals[i])) any_undef = 1;
534 27           (void)hv_store(hv, kpv, klen, newSVsv(vals[i]), 0);
535             }
536 7 100         if (dup) {
537 1           hv_clear(hv); /* duplicate keys: fall back to chain */
538             } else {
539 6           OP *deflt = default_block ? default_block
540 6 100         : newOP(OP_UNDEF, 0);
541 6 50         if (any_undef) {
542             /* a value can be undef, so a miss is indistinguishable from a
543             * hit by definedness: test membership explicitly. */
544 0           OP *cond = newUNOP(OP_EXISTS, 0, sd_helem(aTHX_ gv, &topic));
545 0           chain = newCONDOP(0, cond, sd_helem(aTHX_ gv, &topic), deflt);
546             } else {
547             /* all values are defined: one lookup, miss -> default via //. */
548 6           chain = newLOGOP(OP_DOR, 0, sd_helem(aTHX_ gv, &topic), deflt);
549             }
550             /* the unused per-arm condition and block ops are now dead */
551 31 100         for (i = 0; i < narms; i++) { op_free(conds[i]); op_free(blocks[i]); }
552             }
553             }
554              
555             /* Otherwise (or on fall-back), build the conditional chain right-to-left. */
556 107 100         if (!chain) {
557 101           chain = default_block ? default_block
558 101 100         : newOP(OP_UNDEF, 0);
559 234 100         for (i = narms - 1; i >= 0; i--)
560 133           chain = newCONDOP(0, conds[i], blocks[i], chain);
561             }
562              
563 265 100         for (i = 0; i < narms; i++)
564 158 100         if (keys[i]) SvREFCNT_dec(keys[i]);
565              
566 107 100         if (topic.kind != SDT_TEMP) {
567 95 100         if (topic.kind == SDT_CONST) SvREFCNT_dec(topic.sv);
568 95 100         if (all_simple)
569             /* Fastest path: a plain lexical/constant topic, and either a
570             * dispatch lookup or a chain of bare `{ EXPR }` arms. The whole
571             * switch is literally an expression - no temp, no scope, no
572             * nextstate - as fast as hand-written code. */
573 90           return chain;
574             /* Some arm is a multi-statement block (carries a nextstate). Wrap in a
575             * single enter/leave (OPf_PARENS makes op_scope emit OP_LEAVE) so that
576             * nextstate cannot reset the stack base of a surrounding expression. */
577 5           chain->op_flags |= OPf_PARENS;
578 5           return op_scope(chain);
579             }
580              
581             /* General path: my $topic = SCRUTINEE; , wrapped as a value-returning
582             * block so the scrutinee is evaluated exactly once. OPf_PARENS forces
583             * op_scope down its enter/leave path (OP_LEAVE) for a proper stack frame;
584             * without it a bare OP_SCOPE lets the inner nextstate reset the stack base
585             * and clobber a surrounding expression (e.g. N + switch(...)). */
586 12           lhs = newOP(OP_PADSV, 0);
587 12           lhs->op_targ = topic.off;
588 12           lhs->op_private |= OPpLVAL_INTRO; /* my */
589 12           assign = newASSIGNOP(OPf_STACKED, lhs, 0, scrutinee);
590 12           seq = op_append_list(OP_LINESEQ, newSTATEOP(0, NULL, assign),
591             newSTATEOP(0, NULL, chain));
592 12           seq->op_flags |= OPf_PARENS;
593 12           body = op_scope(seq);
594              
595 12           return body;
596             }
597              
598             /* True if the Switch::Declare lexical pragma is in scope at the current
599             * point of compilation (set by import via $^H{'Switch::Declare'}). */
600 123           static int sd_in_scope(pTHX) {
601 123           HV *hints = GvHV(PL_hintgv);
602             SV **ent;
603 123 50         if (!hints) return 0;
604 123           ent = hv_fetchs(hints, "Switch::Declare", 0);
605 123 100         return ent && SvTRUE(*ent);
    50          
606             }
607              
608 85814           static int sd_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op_ptr) {
609 85814 100         if (kwlen == 6 && memEQ(kw, "switch", 6) && sd_in_scope(aTHX)) {
    100          
    100          
610 120           *op_ptr = sd_parse_switch(aTHX);
611 107           return KEYWORD_PLUGIN_EXPR;
612             }
613 85694           return sd_next_keyword_plugin(aTHX_ kw, kwlen, op_ptr);
614             }
615              
616             MODULE = Switch::Declare PACKAGE = Switch::Declare
617             PROTOTYPES: DISABLE
618              
619             BOOT:
620 16           sd_next_keyword_plugin = PL_keyword_plugin;
621 16           PL_keyword_plugin = sd_keyword_plugin;