File Coverage

include/in_ops.h
Criterion Covered Total %
statement 264 387 68.2
branch 135 288 46.8
condition n/a
subroutine n/a
pod n/a
total 399 675 59.1


line stmt bran cond sub pod time code
1             /*
2             * in_ops.h - Custom ops + peephole optimizer for Colouring::In::XS
3             *
4             * Replaces entersub calls with lightweight custom ops at compile time.
5             * Requires in.h (which pulls in colouring.h) already included.
6             */
7              
8             #ifndef COLOURING_IN_OPS_H
9             #define COLOURING_IN_OPS_H
10              
11             /* compat for pre-5.22 perls */
12             #ifndef OpHAS_SIBLING
13             # define OpHAS_SIBLING(o) ((o)->op_sibling != NULL)
14             #endif
15             #ifndef OpSIBLING
16             # define OpSIBLING(o) ((o)->op_sibling)
17             #endif
18              
19             /* Peek the invocant without disturbing the mark/stack. */
20             #define COLOURING_PEEK_INVOCANT() \
21             ((PL_markstack_ptr > PL_markstack) \
22             && ((PL_stack_base + *PL_markstack_ptr + 1) <= PL_stack_sp) \
23             ? *(PL_stack_base + *PL_markstack_ptr + 1) \
24             : NULL)
25              
26             /* If the invocant isn't a Colouring::In::XS object (or subclass), fall
27             * through to the original OP_ENTERSUB pp so unrelated callers behave. */
28             #define COLOURING_GUARD_INVOCANT() STMT_START { \
29             SV * _self = COLOURING_PEEK_INVOCANT(); \
30             if (!_self || !sv_isobject(_self) \
31             || !sv_derived_from(_self, COLOURING_CLASS)) { \
32             return PL_ppaddr[OP_ENTERSUB](aTHX); \
33             } \
34             } STMT_END
35              
36             /* ── Format ops (self → string) ───────────────────────────────── */
37              
38 13           static OP * pp_colouring_toHEX(pTHX) {
39 13           dSP;
40 13 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    100          
41 12           I32 ax = POPMARK;
42 12           SV **mark = PL_stack_base + ax;
43 12           I32 items = (I32)(SP - mark);
44 12           SV * self = *(mark + 1);
45 12           int force_long = 0;
46             colouring_rgba_t c;
47             char css[8];
48              
49 12 100         if (items > 2) {
50 5           SV * fl = *(mark + 2);
51 5           force_long = SvTRUE(fl);
52             }
53              
54 12           SP = mark;
55 12           c = xs_extract_rgba(self);
56 12           colouring_fmt_hex(c, css, sizeof(css), force_long);
57 12           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
58 12           PUTBACK;
59 12           return NORMAL;
60             }
61              
62 10           static OP * pp_colouring_toRGB(pTHX) {
63 10           dSP;
64 10 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    100          
65 9           I32 ax = POPMARK;
66 9           SV **mark = PL_stack_base + ax;
67 9           SV * self = *(mark + 1);
68             colouring_rgba_t c;
69             SV * alpha_sv;
70              
71 9           SP = mark;
72 9           c = xs_extract_rgba(self);
73 9           alpha_sv = *hv_fetch((HV*)SvRV(self), "alpha", 5, 0);
74 9 50         if (numIs(alpha_sv) && SvIV(alpha_sv) != 1) {
    50          
75             char css[32];
76 0           colouring_fmt_rgba(c, css, sizeof(css));
77 0           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
78             } else {
79             char css[24];
80 9           colouring_fmt_rgb(c, css, sizeof(css));
81 9           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
82             }
83 9           PUTBACK;
84 9           return NORMAL;
85             }
86              
87 44           static OP * pp_colouring_toRGBA(pTHX) {
88 44           dSP;
89 44 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
90 44           I32 ax = POPMARK;
91 44           SV **mark = PL_stack_base + ax;
92 44           SV * self = *(mark + 1);
93             colouring_rgba_t c;
94             char css[32];
95              
96 44           SP = mark;
97 44           c = xs_extract_rgba(self);
98 44           colouring_fmt_rgba(c, css, sizeof(css));
99 44           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
100 44           PUTBACK;
101 44           return NORMAL;
102             }
103              
104 12           static OP * pp_colouring_toHSL(pTHX) {
105 12           dSP;
106 12 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
107 12           I32 ax = POPMARK;
108 12           SV **mark = PL_stack_base + ax;
109 12           SV * self = *(mark + 1);
110             colouring_rgba_t c;
111             colouring_hsl_t hsl;
112             char css[30];
113              
114 12           SP = mark;
115 12           c = xs_extract_rgba(self);
116 12           hsl = colouring_rgb2hsl(c.r, c.g, c.b, c.a);
117 12           colouring_fmt_hsl(hsl, css, sizeof(css));
118 12           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
119 12           PUTBACK;
120 12           return NORMAL;
121             }
122              
123 8           static OP * pp_colouring_toHSV(pTHX) {
124 8           dSP;
125 8 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
126 8           I32 ax = POPMARK;
127 8           SV **mark = PL_stack_base + ax;
128 8           SV * self = *(mark + 1);
129             colouring_rgba_t c;
130             colouring_hsv_t hsv;
131             char css[30];
132              
133 8           SP = mark;
134 8           c = xs_extract_rgba(self);
135 8           hsv = colouring_rgb2hsv(c.r, c.g, c.b);
136 8           colouring_fmt_hsv(hsv, css, sizeof(css));
137 8           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
138 8           PUTBACK;
139 8           return NORMAL;
140             }
141              
142 17           static OP * pp_colouring_toCSS(pTHX) {
143 17           dSP;
144 17 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
145 17           I32 ax = POPMARK;
146 17           SV **mark = PL_stack_base + ax;
147 17           SV * self = *(mark + 1);
148             colouring_rgba_t c;
149              
150 17           SP = mark;
151 17           c = xs_extract_rgba(self);
152 17 100         if (c.a == 1.0) {
153             char css[8];
154 16           colouring_fmt_hex(c, css, sizeof(css), 0);
155 16           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
156             } else {
157             char css[32];
158 1           colouring_fmt_rgba(c, css, sizeof(css));
159 1           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
160             }
161 17           PUTBACK;
162 17           return NORMAL;
163             }
164              
165 5           static OP * pp_colouring_toTerm(pTHX) {
166 5           dSP;
167 5 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
168 5           I32 ax = POPMARK;
169 5           SV **mark = PL_stack_base + ax;
170 5           SV * self = *(mark + 1);
171             colouring_rgba_t c;
172             char css[16];
173              
174 5           SP = mark;
175 5           c = xs_extract_rgba(self);
176 5           colouring_fmt_term(c, css, sizeof(css));
177 5           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
178 5           PUTBACK;
179 5           return NORMAL;
180             }
181              
182 5           static OP * pp_colouring_toOnTerm(pTHX) {
183 5           dSP;
184 5 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
185 5           I32 ax = POPMARK;
186 5           SV **mark = PL_stack_base + ax;
187 5           SV * self = *(mark + 1);
188             colouring_rgba_t c;
189             char css[20];
190              
191 5           SP = mark;
192 5           c = xs_extract_rgba(self);
193 5           colouring_fmt_on_term(c, css, sizeof(css));
194 5           PUSHs(sv_2mortal(newSVpvn(css, strlen(css))));
195 5           PUTBACK;
196 5           return NORMAL;
197             }
198              
199             /* ── Manipulation ops (self, amount → new object) ─────────────── */
200              
201 4           static OP * pp_colouring_lighten(pTHX) {
202 4           dSP;
203 4 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
204 4           I32 ax = POPMARK;
205 4           SV **mark = PL_stack_base + ax;
206 4           I32 items = (I32)(SP - mark);
207 4           SV * colour = *(mark + 1);
208 4           SV * amt_sv = *(mark + 2);
209 4           SV * class = xs_class_sv();
210             double amount;
211 4           int relative = 0;
212             colouring_rgba_t c;
213              
214 4 100         if (items > 3) {
215 3           SV * rel = *(mark + 3);
216 3 50         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    100          
217             }
218              
219 4           SP = mark;
220 4           amount = colouring_depercent(SvPV_nolen(amt_sv));
221 4           colour = xs_ensure_obj(class, colour);
222 4           c = xs_extract_rgba(colour);
223 4           c = colouring_lighten(c.r, c.g, c.b, c.a, amount, relative);
224 4           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
225 4           PUTBACK;
226 4           return NORMAL;
227             }
228              
229 3           static OP * pp_colouring_darken(pTHX) {
230 3           dSP;
231 3 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
232 3           I32 ax = POPMARK;
233 3           SV **mark = PL_stack_base + ax;
234 3           I32 items = (I32)(SP - mark);
235 3           SV * colour = *(mark + 1);
236 3           SV * amt_sv = *(mark + 2);
237 3           SV * class = xs_class_sv();
238             double amount;
239 3           int relative = 0;
240             colouring_rgba_t c;
241              
242 3 100         if (items > 3) {
243 2           SV * rel = *(mark + 3);
244 2 50         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    100          
245             }
246              
247 3           SP = mark;
248 3           amount = colouring_depercent(SvPV_nolen(amt_sv));
249 3           colour = xs_ensure_obj(class, colour);
250 3           c = xs_extract_rgba(colour);
251 3           c = colouring_darken(c.r, c.g, c.b, c.a, amount, relative);
252 3           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
253 3           PUTBACK;
254 3           return NORMAL;
255             }
256              
257 1           static OP * pp_colouring_fade(pTHX) {
258 1           dSP;
259 1 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
260 0           I32 ax = POPMARK;
261 0           SV **mark = PL_stack_base + ax;
262 0           SV * colour = *(mark + 1);
263 0           SV * amt_sv = *(mark + 2);
264 0           SV * class = xs_class_sv();
265             double amount;
266             colouring_rgba_t c;
267              
268 0           SP = mark;
269 0           amount = colouring_depercent(SvPV_nolen(amt_sv));
270 0           colour = xs_ensure_obj(class, colour);
271 0           c = xs_extract_rgba(colour);
272 0           c = colouring_fade(c.r, c.g, c.b, c.a, amount);
273 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
274 0           PUTBACK;
275 0           return NORMAL;
276             }
277              
278 3           static OP * pp_colouring_fadeout(pTHX) {
279 3           dSP;
280 3 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
281 3           I32 ax = POPMARK;
282 3           SV **mark = PL_stack_base + ax;
283 3           I32 items = (I32)(SP - mark);
284 3           SV * colour = *(mark + 1);
285 3           SV * amt_sv = *(mark + 2);
286 3           SV * class = xs_class_sv();
287             double amount;
288 3           int relative = 0;
289             colouring_rgba_t c;
290              
291 3 100         if (items > 3) {
292 2           SV * rel = *(mark + 3);
293 2 50         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    100          
294             }
295              
296 3           SP = mark;
297 3           amount = colouring_depercent(SvPV_nolen(amt_sv));
298 3           colour = xs_ensure_obj(class, colour);
299 3           c = xs_extract_rgba(colour);
300 3           c = colouring_fadeout(c.r, c.g, c.b, c.a, amount, relative);
301 3           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
302 3           PUTBACK;
303 3           return NORMAL;
304             }
305              
306 3           static OP * pp_colouring_fadein(pTHX) {
307 3           dSP;
308 3 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
309 3           I32 ax = POPMARK;
310 3           SV **mark = PL_stack_base + ax;
311 3           I32 items = (I32)(SP - mark);
312 3           SV * colour = *(mark + 1);
313 3           SV * amt_sv = *(mark + 2);
314 3           SV * class = xs_class_sv();
315             double amount;
316 3           int relative = 0;
317             colouring_rgba_t c;
318              
319 3 100         if (items > 3) {
320 2           SV * rel = *(mark + 3);
321 2 50         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    100          
322             }
323              
324 3           SP = mark;
325 3           amount = colouring_depercent(SvPV_nolen(amt_sv));
326 3           colour = xs_ensure_obj(class, colour);
327 3           c = xs_extract_rgba(colour);
328 3           c = colouring_fadein(c.r, c.g, c.b, c.a, amount, relative);
329 3           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
330 3           PUTBACK;
331 3           return NORMAL;
332             }
333              
334 0           static OP * pp_colouring_saturate(pTHX) {
335 0           dSP;
336 0 0         COLOURING_GUARD_INVOCANT();
    0          
    0          
    0          
    0          
337 0           I32 ax = POPMARK;
338 0           SV **mark = PL_stack_base + ax;
339 0           I32 items = (I32)(SP - mark);
340 0           SV * colour = *(mark + 1);
341 0           SV * amt_sv = *(mark + 2);
342 0           SV * class = xs_class_sv();
343             double amount;
344 0           int relative = 0;
345             colouring_rgba_t c;
346              
347 0 0         if (items > 3) {
348 0           SV * rel = *(mark + 3);
349 0 0         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    0          
350             }
351              
352 0           SP = mark;
353 0           amount = colouring_depercent(SvPV_nolen(amt_sv));
354 0           colour = xs_ensure_obj(class, colour);
355 0           c = xs_extract_rgba(colour);
356 0           c = colouring_saturate(c.r, c.g, c.b, c.a, amount, relative);
357 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
358 0           PUTBACK;
359 0           return NORMAL;
360             }
361              
362 0           static OP * pp_colouring_desaturate(pTHX) {
363 0           dSP;
364 0 0         COLOURING_GUARD_INVOCANT();
    0          
    0          
    0          
    0          
365 0           I32 ax = POPMARK;
366 0           SV **mark = PL_stack_base + ax;
367 0           I32 items = (I32)(SP - mark);
368 0           SV * colour = *(mark + 1);
369 0           SV * amt_sv = *(mark + 2);
370 0           SV * class = xs_class_sv();
371             double amount;
372 0           int relative = 0;
373             colouring_rgba_t c;
374              
375 0 0         if (items > 3) {
376 0           SV * rel = *(mark + 3);
377 0 0         if (SvOK(rel) && strEQ(SvPV_nolen(rel), "relative")) relative = 1;
    0          
378             }
379              
380 0           SP = mark;
381 0           amount = colouring_depercent(SvPV_nolen(amt_sv));
382 0           colour = xs_ensure_obj(class, colour);
383 0           c = xs_extract_rgba(colour);
384 0           c = colouring_desaturate(c.r, c.g, c.b, c.a, amount, relative);
385 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
386 0           PUTBACK;
387 0           return NORMAL;
388             }
389              
390 0           static OP * pp_colouring_greyscale(pTHX) {
391 0           dSP;
392 0 0         COLOURING_GUARD_INVOCANT();
    0          
    0          
    0          
    0          
393 0           I32 ax = POPMARK;
394 0           SV **mark = PL_stack_base + ax;
395 0           SV * colour = *(mark + 1);
396 0           SV * class = xs_class_sv();
397             colouring_rgba_t c;
398              
399 0           SP = mark;
400 0           colour = xs_ensure_obj(class, colour);
401 0           c = xs_extract_rgba(colour);
402 0           c = colouring_greyscale(c.r, c.g, c.b, c.a);
403 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, c)));
404 0           PUTBACK;
405 0           return NORMAL;
406             }
407              
408             /* ── Mix-family ops (self, colour2[, weight] → new object) ────── */
409              
410 1           static OP * pp_colouring_mix(pTHX) {
411 1           dSP;
412 1 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
413 0           I32 ax = POPMARK;
414 0           SV **mark = PL_stack_base + ax;
415 0           I32 items = (I32)(SP - mark);
416 0           SV * colour1 = *(mark + 1);
417 0           SV * colour2 = *(mark + 2);
418 0           SV * class = xs_class_sv();
419 0           int weight = 50;
420             colouring_rgba_t c1, c2, out;
421              
422 0 0         if (items > 3) {
423 0           SV * w = *(mark + 3);
424 0 0         if (SvOK(w) && SvIV(w) != 0) weight = SvIV(w);
    0          
425             }
426              
427 0           SP = mark;
428 0           colour1 = xs_ensure_obj(class, colour1);
429 0           colour2 = xs_ensure_obj(class, colour2);
430 0           c1 = xs_extract_rgba(colour1);
431 0           c2 = xs_extract_rgba(colour2);
432 0           out = colouring_mix(c1, c2, weight);
433 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, out)));
434 0           PUTBACK;
435 0           return NORMAL;
436             }
437              
438 1           static OP * pp_colouring_tint(pTHX) {
439 1           dSP;
440 1 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
441 0           I32 ax = POPMARK;
442 0           SV **mark = PL_stack_base + ax;
443 0           I32 items = (I32)(SP - mark);
444 0           SV * colour = *(mark + 1);
445 0           SV * class = xs_class_sv();
446 0           int weight = 50;
447             colouring_rgba_t c, out;
448              
449 0 0         if (items > 2) {
450 0           SV * w = *(mark + 2);
451 0 0         if (SvOK(w) && SvIV(w) != 0) weight = SvIV(w);
    0          
452             }
453              
454 0           SP = mark;
455 0           colour = xs_ensure_obj(class, colour);
456 0           c = xs_extract_rgba(colour);
457 0           out = colouring_tint(c, weight);
458 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, out)));
459 0           PUTBACK;
460 0           return NORMAL;
461             }
462              
463 1           static OP * pp_colouring_shade(pTHX) {
464 1           dSP;
465 1 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    50          
466 0           I32 ax = POPMARK;
467 0           SV **mark = PL_stack_base + ax;
468 0           I32 items = (I32)(SP - mark);
469 0           SV * colour = *(mark + 1);
470 0           SV * class = xs_class_sv();
471 0           int weight = 50;
472             colouring_rgba_t c, out;
473              
474 0 0         if (items > 2) {
475 0           SV * w = *(mark + 2);
476 0 0         if (SvOK(w) && SvIV(w) != 0) weight = SvIV(w);
    0          
477             }
478              
479 0           SP = mark;
480 0           colour = xs_ensure_obj(class, colour);
481 0           c = xs_extract_rgba(colour);
482 0           out = colouring_shade(c, weight);
483 0           PUSHs(sv_2mortal(xs_rgba_to_obj(class, out)));
484 0           PUTBACK;
485 0           return NORMAL;
486             }
487              
488             /* ── Accessor op ──────────────────────────────────────────────── */
489              
490 23           static OP * pp_colouring_colour(pTHX) {
491 23           dSP;
492 23 50         COLOURING_GUARD_INVOCANT();
    50          
    50          
    50          
    100          
493 22           I32 ax = POPMARK;
494 22           SV **mark = PL_stack_base + ax;
495 22           SV * self = *(mark + 1);
496 22           AV * colour = (AV*)SvRV(*hv_fetch((HV*)SvRV(self), "colour", 6, 0));
497 22           int len = av_len(colour);
498             int i;
499              
500 22           SP = mark;
501 22 50         EXTEND(SP, len + 1);
    50          
502 88 100         for (i = 0; i <= len; i++) {
503 66           PUSHs(sv_2mortal(newSVsv(*av_fetch(colour, i, 0))));
504             }
505 22           PUTBACK;
506 22           return NORMAL;
507             }
508              
509             /* ══════════════════════════════════════════════════════════════════
510             * XOP descriptors (Perl 5.14+)
511             * ══════════════════════════════════════════════════════════════════ */
512              
513             #if PERL_VERSION >= 14
514              
515             static XOP xop_toHEX;
516             static XOP xop_toRGB;
517             static XOP xop_toRGBA;
518             static XOP xop_toHSL;
519             static XOP xop_toHSV;
520             static XOP xop_toCSS;
521             static XOP xop_toTerm;
522             static XOP xop_toOnTerm;
523             static XOP xop_lighten;
524             static XOP xop_darken;
525             static XOP xop_fade;
526             static XOP xop_fadeout;
527             static XOP xop_fadein;
528             static XOP xop_saturate;
529             static XOP xop_desaturate;
530             static XOP xop_greyscale;
531             static XOP xop_mix;
532             static XOP xop_tint;
533             static XOP xop_shade;
534             static XOP xop_colour;
535              
536             #endif /* PERL_VERSION >= 14 */
537              
538             /* ══════════════════════════════════════════════════════════════════
539             * Op dispatch table — maps method name → pp function
540             * ══════════════════════════════════════════════════════════════════ */
541              
542             typedef struct {
543             const char *name;
544             Perl_ppaddr_t pp;
545             } colouring_op_entry_t;
546              
547             static colouring_op_entry_t colouring_op_table[] = {
548             { "toHEX", pp_colouring_toHEX },
549             { "toRGB", pp_colouring_toRGB },
550             { "toRGBA", pp_colouring_toRGBA },
551             { "toHSL", pp_colouring_toHSL },
552             { "toHSV", pp_colouring_toHSV },
553             { "toCSS", pp_colouring_toCSS },
554             { "toTerm", pp_colouring_toTerm },
555             { "toOnTerm", pp_colouring_toOnTerm },
556             { "lighten", pp_colouring_lighten },
557             { "darken", pp_colouring_darken },
558             { "fade", pp_colouring_fade },
559             { "fadeout", pp_colouring_fadeout },
560             { "fadein", pp_colouring_fadein },
561             { "saturate", pp_colouring_saturate },
562             { "desaturate", pp_colouring_desaturate },
563             { "greyscale", pp_colouring_greyscale },
564             { "mix", pp_colouring_mix },
565             { "tint", pp_colouring_tint },
566             { "shade", pp_colouring_shade },
567             { "colour", pp_colouring_colour },
568             { NULL, NULL }
569             };
570              
571             #define COLOURING_OP_COUNT 20
572              
573             /* ══════════════════════════════════════════════════════════════════
574             * Peephole optimizer — replace entersub with custom ops
575             * ══════════════════════════════════════════════════════════════════ */
576              
577             static peep_t colouring_prev_peep = NULL;
578              
579             /* Walk an optree looking for entersub ops that call our methods,
580             * and replace them with the corresponding custom op. */
581 13869           static void colouring_peep(pTHX_ OP *o) {
582 13869           OP *orig = o;
583              
584             /* chain to any previous peep first */
585 13869 50         if (colouring_prev_peep)
586 13869           colouring_prev_peep(aTHX_ o);
587              
588 323286 100         for (; o; o = o->op_next) {
589             OP *cv_op, *method_op, *first;
590             const char *methname;
591             STRLEN methlen;
592             int i;
593              
594 309417 100         if (o->op_type != OP_ENTERSUB)
595 303630           continue;
596 9344 100         if (!(o->op_flags & OPf_STACKED))
597 171           continue;
598              
599             /* Find the last child of entersub — should be the method op */
600 9173           first = cUNOPo->op_first;
601 9173 50         if (!first)
602 0           continue;
603              
604             /* Walk to last sibling */
605 9173           cv_op = first;
606 25995 100         while (OpHAS_SIBLING(cv_op))
607 16822 50         cv_op = OpSIBLING(cv_op);
608              
609             /* We want OP_METHOD_NAMED */
610 9173 100         if (cv_op->op_type != OP_METHOD_NAMED)
611 3386           continue;
612              
613             /* Get the method name */
614             #if PERL_VERSION >= 22
615 5787           methname = SvPV_const(cMETHOPx_meth(cv_op), methlen);
616             #else
617             methname = SvPV_const(cSVOPx(cv_op)->op_sv, methlen);
618             #endif
619              
620             /* Look up in our dispatch table */
621 120644 100         for (i = 0; i < COLOURING_OP_COUNT; i++) {
622 114925 100         if (strEQ(methname, colouring_op_table[i].name)) {
623             /* Replace the entersub with our custom op */
624 68           o->op_ppaddr = colouring_op_table[i].pp;
625 68           break;
626             }
627             }
628             }
629 13869           }
630              
631             /* ══════════════════════════════════════════════════════════════════
632             * Registration — call from BOOT
633             * ══════════════════════════════════════════════════════════════════ */
634              
635             #define COLOURING_REGISTER_XOP(xop_var, name_str, pp_fn) \
636             XopENTRY_set(&xop_var, xop_name, name_str); \
637             XopENTRY_set(&xop_var, xop_desc, "colouring " name_str); \
638             XopENTRY_set(&xop_var, xop_class, OA_UNOP); \
639             Perl_custom_op_register(aTHX_ pp_fn, &xop_var)
640              
641 19           static void colouring_register_ops(pTHX) {
642             #if PERL_VERSION >= 14
643 19           COLOURING_REGISTER_XOP(xop_toHEX, "toHEX", pp_colouring_toHEX);
644 19           COLOURING_REGISTER_XOP(xop_toRGB, "toRGB", pp_colouring_toRGB);
645 19           COLOURING_REGISTER_XOP(xop_toRGBA, "toRGBA", pp_colouring_toRGBA);
646 19           COLOURING_REGISTER_XOP(xop_toHSL, "toHSL", pp_colouring_toHSL);
647 19           COLOURING_REGISTER_XOP(xop_toHSV, "toHSV", pp_colouring_toHSV);
648 19           COLOURING_REGISTER_XOP(xop_toCSS, "toCSS", pp_colouring_toCSS);
649 19           COLOURING_REGISTER_XOP(xop_toTerm, "toTerm", pp_colouring_toTerm);
650 19           COLOURING_REGISTER_XOP(xop_toOnTerm, "toOnTerm", pp_colouring_toOnTerm);
651 19           COLOURING_REGISTER_XOP(xop_lighten, "lighten", pp_colouring_lighten);
652 19           COLOURING_REGISTER_XOP(xop_darken, "darken", pp_colouring_darken);
653 19           COLOURING_REGISTER_XOP(xop_fade, "fade", pp_colouring_fade);
654 19           COLOURING_REGISTER_XOP(xop_fadeout, "fadeout", pp_colouring_fadeout);
655 19           COLOURING_REGISTER_XOP(xop_fadein, "fadein", pp_colouring_fadein);
656 19           COLOURING_REGISTER_XOP(xop_saturate, "saturate", pp_colouring_saturate);
657 19           COLOURING_REGISTER_XOP(xop_desaturate, "desaturate", pp_colouring_desaturate);
658 19           COLOURING_REGISTER_XOP(xop_greyscale, "greyscale", pp_colouring_greyscale);
659 19           COLOURING_REGISTER_XOP(xop_mix, "mix", pp_colouring_mix);
660 19           COLOURING_REGISTER_XOP(xop_tint, "tint", pp_colouring_tint);
661 19           COLOURING_REGISTER_XOP(xop_shade, "shade", pp_colouring_shade);
662 19           COLOURING_REGISTER_XOP(xop_colour, "colour", pp_colouring_colour);
663             #endif
664              
665             /* Install peephole optimizer */
666 19           colouring_prev_peep = PL_peepp;
667 19           PL_peepp = colouring_peep;
668 19           }
669              
670             #endif /* COLOURING_IN_OPS_H */