File Coverage

ext/arybase/arybase.xs
Criterion Covered Total %
statement 186 192 96.9
branch n/a
condition n/a
subroutine n/a
total 186 192 96.9


line stmt bran cond sub time code
1           #define PERL_NO_GET_CONTEXT /* we want efficiency */
2           #define PERL_EXT
3           #include "EXTERN.h"
4           #include "perl.h"
5           #include "XSUB.h"
6           #include "feature.h"
7            
8           /* ... op => info map ................................................. */
9            
10           typedef struct {
11           OP *(*old_pp)(pTHX);
12           IV base;
13           } ab_op_info;
14            
15           #define PTABLE_NAME ptable_map
16           #define PTABLE_VAL_FREE(V) PerlMemShared_free(V)
17           #include "ptable.h"
18           #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V))
19            
20           STATIC ptable *ab_op_map = NULL;
21            
22           #ifdef USE_ITHREADS
23           STATIC perl_mutex ab_op_map_mutex;
24           #endif
25            
26 294         STATIC const ab_op_info *ab_map_fetch(const OP *o, ab_op_info *oi) {
27           const ab_op_info *val;
28            
29           #ifdef USE_ITHREADS
30           MUTEX_LOCK(&ab_op_map_mutex);
31           #endif
32            
33 294         val = (ab_op_info *)ptable_fetch(ab_op_map, o);
34 294         if (val) {
35 294         *oi = *val;
36           val = oi;
37           }
38            
39           #ifdef USE_ITHREADS
40           MUTEX_UNLOCK(&ab_op_map_mutex);
41           #endif
42            
43 294         return val;
44           }
45            
46           STATIC const ab_op_info *ab_map_store_locked(
47           pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), IV base
48           ) {
49           #define ab_map_store_locked(O, PP, B) \
50           ab_map_store_locked(aPTBLMS_ (O), (PP), (B))
51           ab_op_info *oi;
52            
53 548         if (!(oi = (ab_op_info *)ptable_fetch(ab_op_map, o))) {
54 274         oi = (ab_op_info *)PerlMemShared_malloc(sizeof *oi);
55 274         ptable_map_store(ab_op_map, o, oi);
56           }
57            
58 274         oi->old_pp = old_pp;
59 274         oi->base = base;
60           return oi;
61           }
62            
63           STATIC void ab_map_store(
64           pPTBLMS_ const OP *o, OP *(*old_pp)(pTHX), IV base)
65           {
66           #define ab_map_store(O, PP, B) ab_map_store(aPTBLMS_ (O),(PP),(B))
67            
68           #ifdef USE_ITHREADS
69           MUTEX_LOCK(&ab_op_map_mutex);
70           #endif
71            
72           ab_map_store_locked(o, old_pp, base);
73            
74           #ifdef USE_ITHREADS
75           MUTEX_UNLOCK(&ab_op_map_mutex);
76           #endif
77           }
78            
79           STATIC void ab_map_delete(pTHX_ const OP *o) {
80           #define ab_map_delete(O) ab_map_delete(aTHX_ (O))
81           #ifdef USE_ITHREADS
82           MUTEX_LOCK(&ab_op_map_mutex);
83           #endif
84            
85 2774         ptable_map_store(ab_op_map, o, NULL);
86            
87           #ifdef USE_ITHREADS
88           MUTEX_UNLOCK(&ab_op_map_mutex);
89           #endif
90           }
91            
92           /* ... $[ Implementation .............................................. */
93            
94           #define hintkey "$["
95           #define hintkey_len (sizeof(hintkey)-1)
96            
97 3134         STATIC SV * ab_hint(pTHX_ const bool create) {
98           #define ab_hint(c) ab_hint(aTHX_ c)
99           dVAR;
100 3134         SV **val
101 3134         = hv_fetch(GvHV(PL_hintgv), hintkey, hintkey_len, create);
102 3134         if (!val)
103           return 0;
104 364         return *val;
105           }
106            
107           /* current base at compile time */
108           STATIC IV current_base(pTHX) {
109           #define current_base() current_base(aTHX)
110 3048         SV *hsv = ab_hint(0);
111           assert(FEATURE_ARYBASE_IS_ENABLED);
112 3048         if (!hsv || !SvOK(hsv)) return 0;
113 278         return SvIV(hsv);
114           }
115            
116           STATIC void set_arybase_to(pTHX_ IV base) {
117           #define set_arybase_to(base) set_arybase_to(aTHX_ (base))
118           dVAR;
119 86         SV *hsv = ab_hint(1);
120 86         sv_setiv_mg(hsv, base);
121           }
122            
123           #define old_ck(opname) STATIC OP *(*ab_old_ck_##opname)(pTHX_ OP *) = 0
124           old_ck(sassign);
125           old_ck(aassign);
126           old_ck(aelem);
127           old_ck(aslice);
128           old_ck(lslice);
129           old_ck(av2arylen);
130           old_ck(splice);
131           old_ck(keys);
132           old_ck(each);
133           old_ck(substr);
134           old_ck(rindex);
135           old_ck(index);
136           old_ck(pos);
137            
138           STATIC bool ab_op_is_dollar_bracket(pTHX_ OP *o) {
139           #define ab_op_is_dollar_bracket(o) ab_op_is_dollar_bracket(aTHX_ (o))
140           OP *c;
141 8812         return o->op_type == OP_RV2SV && (o->op_flags & OPf_KIDS)
142 2180         && (c = cUNOPx(o)->op_first)
143 2180         && c->op_type == OP_GV
144 2172         && GvSTASH(cGVOPx_gv(c)) == PL_defstash
145 7332         && strEQ(GvNAME(cGVOPx_gv(c)), "[");
146           }
147            
148           STATIC void ab_neuter_dollar_bracket(pTHX_ OP *o) {
149           #define ab_neuter_dollar_bracket(o) ab_neuter_dollar_bracket(aTHX_ (o))
150           OP *oldc, *newc;
151           /*
152           * Must replace the core's $[ with something that can accept assignment
153           * of non-zero value and can be local()ised. Simplest thing is a
154           * different global variable.
155           */
156 86         oldc = cUNOPx(o)->op_first;
157 86         newc = newGVOP(OP_GV, 0,
158           gv_fetchpvs("arybase::leftbrack", GV_ADDMULTI, SVt_PVGV));
159 86         cUNOPx(o)->op_first = newc;
160 86         op_free(oldc);
161           }
162            
163 6632         STATIC void ab_process_assignment(pTHX_ OP *left, OP *right) {
164           #define ab_process_assignment(l, r) \
165           ab_process_assignment(aTHX_ (l), (r))
166 6632         if (ab_op_is_dollar_bracket(left) && right->op_type == OP_CONST) {
167 86         set_arybase_to(SvIV(cSVOPx_sv(right)));
168           ab_neuter_dollar_bracket(left);
169 86         Perl_ck_warner_d(aTHX_
170           packWARN(WARN_DEPRECATED), "Use of assignment to $[ is deprecated"
171           );
172           }
173 6632         }
174            
175 102440         STATIC OP *ab_ck_sassign(pTHX_ OP *o) {
176 102440         o = (*ab_old_ck_sassign)(aTHX_ o);
177 102440         if (o->op_type == OP_SASSIGN && FEATURE_ARYBASE_IS_ENABLED) {
178 4922         OP *right = cBINOPx(o)->op_first;
179 4922         OP *left = right->op_sibling;
180 4922         if (left) ab_process_assignment(left, right);
181           }
182 102440         return o;
183           }
184            
185 1710         STATIC OP *ab_ck_aassign(pTHX_ OP *o) {
186 1710         o = (*ab_old_ck_aassign)(aTHX_ o);
187 1710         if (o->op_type == OP_AASSIGN && FEATURE_ARYBASE_IS_ENABLED) {
188 1710         OP *right = cBINOPx(o)->op_first;
189 1710         OP *left = cBINOPx(right->op_sibling)->op_first->op_sibling;
190 1710         right = cBINOPx(right)->op_first->op_sibling;
191 1710         ab_process_assignment(left, right);
192           }
193 1710         return o;
194           }
195            
196           void
197 246         tie(pTHX_ SV * const sv, SV * const obj, HV *const stash)
198           {
199 246         SV *rv = newSV_type(SVt_RV);
200            
201 246         SvRV_set(rv, obj ? SvREFCNT_inc_simple_NN(obj) : newSV(0));
202 246         SvROK_on(rv);
203 246         sv_bless(rv, stash);
204            
205 246         sv_unmagic((SV *)sv, PERL_MAGIC_tiedscalar);
206 246         sv_magic((SV *)sv, rv, PERL_MAGIC_tiedscalar, NULL, 0);
207           SvREFCNT_dec(rv); /* As sv_magic increased it by one. */
208 246         }
209            
210           /* This function converts from base-based to 0-based an index to be passed
211           as an argument. */
212           static IV
213           adjust_index(IV index, IV base)
214           {
215 334         if (index >= base || index > -1) return index-base;
216           return index;
217           }
218           /* This function converts from 0-based to base-based an index to
219           be returned. */
220           static IV
221           adjust_index_r(IV index, IV base)
222           {
223 100         return index + base;
224           }
225            
226           #define replace_sv(sv,base) \
227           ((sv) = sv_2mortal(newSViv(adjust_index(SvIV(sv),base))))
228           #define replace_sv_r(sv,base) \
229           ((sv) = sv_2mortal(newSViv(adjust_index_r(SvIV(sv),base))))
230            
231 192         static OP *ab_pp_basearg(pTHX) {
232 192         dVAR; dSP;
233           SV **firstp = NULL;
234           SV **svp;
235           UV count = 1;
236           ab_op_info oi;
237 192         ab_map_fetch(PL_op, &oi);
238          
239 192         switch (PL_op->op_type) {
240           case OP_AELEM:
241           firstp = SP;
242           break;
243           case OP_ASLICE:
244 36         firstp = PL_stack_base + TOPMARK + 1;
245 36         count = SP-firstp;
246 36         break;
247           case OP_LSLICE:
248 38         firstp = PL_stack_base + *(PL_markstack_ptr-1)+1;
249 38         count = TOPMARK - *(PL_markstack_ptr-1);
250 38         if (GIMME != G_ARRAY) {
251 8         firstp += count-1;
252           count = 1;
253           }
254           break;
255           case OP_SPLICE:
256 26         if (SP - PL_stack_base - TOPMARK >= 2)
257 22         firstp = PL_stack_base + TOPMARK + 2;
258           else count = 0;
259           break;
260           case OP_SUBSTR:
261 6         firstp = SP-(PL_op->op_private & 7)+2;
262 6         break;
263           default:
264 0         DIE(aTHX_
265           "panic: invalid op type for arybase.xs:ab_pp_basearg: %d",
266 0         PL_op->op_type);
267           }
268           svp = firstp;
269 500         while (count--) replace_sv(*svp,oi.base), svp++;
270 192         return (*oi.old_pp)(aTHX);
271           }
272            
273 38         static OP *ab_pp_av2arylen(pTHX) {
274 38         dSP; dVAR;
275           SV *sv;
276           ab_op_info oi;
277           OP *ret;
278 38         ab_map_fetch(PL_op, &oi);
279 38         ret = (*oi.old_pp)(aTHX);
280 66         if (PL_op->op_flags & OPf_MOD || LVRET) {
281 10         sv = newSV(0);
282 10         tie(aTHX_ sv, TOPs, gv_stashpv("arybase::mg",1));
283 10         SETs(sv);
284           }
285           else {
286 28         SvGETMAGIC(TOPs);
287 48         if (SvOK(TOPs)) replace_sv_r(TOPs, oi.base);
288           }
289 38         return ret;
290           }
291            
292 16         static OP *ab_pp_keys(pTHX) {
293 16         dVAR; dSP;
294           ab_op_info oi;
295           OP *retval;
296 16         const I32 offset = SP - PL_stack_base;
297           SV **svp;
298 16         ab_map_fetch(PL_op, &oi);
299 16         retval = (*oi.old_pp)(aTHX);
300 16         if (GIMME_V == G_SCALAR) return retval;
301 8         SPAGAIN;
302 8         svp = PL_stack_base + offset;
303 32         while (svp <= SP) replace_sv_r(*svp,oi.base), ++svp;
304           return retval;
305           }
306            
307 24         static OP *ab_pp_each(pTHX) {
308 24         dVAR; dSP;
309           ab_op_info oi;
310           OP *retval;
311 24         const I32 offset = SP - PL_stack_base;
312 24         ab_map_fetch(PL_op, &oi);
313 24         retval = (*oi.old_pp)(aTHX);
314 24         SPAGAIN;
315 24         if (GIMME_V == G_SCALAR) {
316 22         if (SvOK(TOPs)) replace_sv_r(TOPs,oi.base);
317           }
318 22         else if (offset < SP - PL_stack_base) replace_sv_r(TOPm1s,oi.base);
319 24         return retval;
320           }
321            
322 24         static OP *ab_pp_index(pTHX) {
323 24         dVAR; dSP;
324           ab_op_info oi;
325           OP *retval;
326 24         ab_map_fetch(PL_op, &oi);
327 44         if (MAXARG == 3 && TOPs) replace_sv(TOPs,oi.base);
328 24         retval = (*oi.old_pp)(aTHX);
329 24         SPAGAIN;
330 48         replace_sv_r(TOPs,oi.base);
331 24         return retval;
332           }
333            
334 3390         static OP *ab_ck_base(pTHX_ OP *o)
335           {
336           OP * (*old_ck)(pTHX_ OP *o) = 0;
337           OP * (*new_pp)(pTHX) = ab_pp_basearg;
338 3390         switch (o->op_type) {
339 2158         case OP_AELEM : old_ck = ab_old_ck_aelem ; break;
340 120         case OP_ASLICE : old_ck = ab_old_ck_aslice ; break;
341 70         case OP_LSLICE : old_ck = ab_old_ck_lslice ; break;
342 572         case OP_AV2ARYLEN: old_ck = ab_old_ck_av2arylen; break;
343 26         case OP_SPLICE : old_ck = ab_old_ck_splice ; break;
344 198         case OP_KEYS : old_ck = ab_old_ck_keys ; break;
345 152         case OP_EACH : old_ck = ab_old_ck_each ; break;
346 28         case OP_SUBSTR : old_ck = ab_old_ck_substr ; break;
347 12         case OP_RINDEX : old_ck = ab_old_ck_rindex ; break;
348 20         case OP_INDEX : old_ck = ab_old_ck_index ; break;
349 34         case OP_POS : old_ck = ab_old_ck_pos ; break;
350           default:
351 0         DIE(aTHX_
352           "panic: invalid op type for arybase.xs:ab_ck_base: %d",
353 0         PL_op->op_type);
354           }
355 3390         o = (*old_ck)(aTHX_ o);
356 3390         if (!FEATURE_ARYBASE_IS_ENABLED) return o;
357           /* We need two switch blocks, as the type may have changed. */
358 3378         switch (o->op_type) {
359           case OP_AELEM :
360           case OP_ASLICE :
361           case OP_LSLICE :
362           case OP_SPLICE :
363           case OP_SUBSTR : break;
364           case OP_POS :
365 602         case OP_AV2ARYLEN: new_pp = ab_pp_av2arylen ; break;
366 16         case OP_AKEYS : new_pp = ab_pp_keys ; break;
367 4         case OP_AEACH : new_pp = ab_pp_each ; break;
368           case OP_RINDEX :
369 32         case OP_INDEX : new_pp = ab_pp_index ; break;
370           default: return o;
371           }
372           {
373           IV const base = current_base();
374 3048         if (base) {
375 274         ab_map_store(o, o->op_ppaddr, base);
376 274         o->op_ppaddr = new_pp;
377           /* Break the aelemfast optimisation */
378 360         if (o->op_type == OP_AELEM &&
379 86         cBINOPo->op_first->op_sibling->op_type == OP_CONST) {
380           cBINOPo->op_first->op_sibling
381 54         = newUNOP(OP_NULL,0,cBINOPo->op_first->op_sibling);
382           }
383           }
384           else ab_map_delete(o);
385           }
386           return o;
387           }
388            
389            
390           STATIC U32 ab_initialized = 0;
391            
392           /* --- XS ------------------------------------------------------------- */
393            
394           MODULE = arybase PACKAGE = arybase
395           PROTOTYPES: DISABLE
396            
397           BOOT:
398           {
399 236         GV *const gv = gv_fetchpvn("[", 1, GV_ADDMULTI|GV_NOTQUAL, SVt_PV);
400 236         sv_unmagic(GvSV(gv), PERL_MAGIC_sv); /* This is *our* scalar now! */
401 236         tie(aTHX_ GvSV(gv), NULL, GvSTASH(CvGV(cv)));
402            
403 236         if (!ab_initialized++) {
404 236         ab_op_map = ptable_new();
405           #ifdef USE_ITHREADS
406           MUTEX_INIT(&ab_op_map_mutex);
407           #endif
408           #define check(uc,lc,ck) \
409           wrap_op_checker(OP_##uc, ab_ck_##ck, &ab_old_ck_##lc)
410 236         check(SASSIGN, sassign, sassign);
411 236         check(AASSIGN, aassign, aassign);
412 236         check(AELEM, aelem, base);
413 236         check(ASLICE, aslice, base);
414 236         check(LSLICE, lslice, base);
415 236         check(AV2ARYLEN,av2arylen,base);
416 236         check(SPLICE, splice, base);
417 236         check(KEYS, keys, base);
418 236         check(EACH, each, base);
419 236         check(SUBSTR, substr, base);
420 236         check(RINDEX, rindex, base);
421 236         check(INDEX, index, base);
422 236         check(POS, pos, base);
423           }
424           }
425            
426           void
427           FETCH(...)
428           PREINIT:
429 152         SV *ret = FEATURE_ARYBASE_IS_ENABLED
430 76         ? cop_hints_fetch_pvs(PL_curcop, "$[", 0)
431 152         : 0;
432           PPCODE:
433 76         if (!ret || !SvOK(ret)) mXPUSHi(0);
434 6         else XPUSHs(ret);
435            
436           void
437           STORE(SV *sv, IV newbase)
438           CODE:
439 12         if (FEATURE_ARYBASE_IS_ENABLED) {
440 8         SV *base = cop_hints_fetch_pvs(PL_curcop, "$[", 0);
441 8         if (SvOK(base) ? SvIV(base) == newbase : !newbase) XSRETURN_EMPTY;
442 4         Perl_croak(aTHX_ "That use of $[ is unsupported");
443           }
444 4         else if (newbase)
445 4         Perl_croak(aTHX_ "Assigning non-zero to $[ is no longer possible");
446            
447            
448           MODULE = arybase PACKAGE = arybase::mg
449           PROTOTYPES: DISABLE
450            
451           void
452           FETCH(SV *sv)
453           PPCODE:
454 14         if (!SvROK(sv) || SvTYPE(SvRV(sv)) >= SVt_PVAV)
455 0         Perl_croak(aTHX_ "Not a SCALAR reference");
456 14         {
457 28         SV *base = FEATURE_ARYBASE_IS_ENABLED
458 14         ? cop_hints_fetch_pvs(PL_curcop, "$[", 0)
459 28         : 0;
460 28         SvGETMAGIC(SvRV(sv));
461 14         if (!SvOK(SvRV(sv))) XSRETURN_UNDEF;
462 24         mXPUSHi(adjust_index_r(
463           SvIV_nomg(SvRV(sv)), base&&SvOK(base)?SvIV(base):0
464           ));
465           }
466            
467           void
468           STORE(SV *sv, SV *newbase)
469           CODE:
470 6         if (!SvROK(sv) || SvTYPE(SvRV(sv)) >= SVt_PVAV)
471 0         Perl_croak(aTHX_ "Not a SCALAR reference");
472 6         {
473 12         SV *base = FEATURE_ARYBASE_IS_ENABLED
474 6         ? cop_hints_fetch_pvs(PL_curcop, "$[", 0)
475 12         : 0;
476 6         SvGETMAGIC(newbase);
477 6         if (!SvOK(newbase)) SvSetMagicSV(SvRV(sv),&PL_sv_undef);
478           else
479 12         sv_setiv_mg(
480           SvRV(sv),
481           adjust_index(
482           SvIV_nomg(newbase), base&&SvOK(base)?SvIV(base):0
483           )
484           );
485           }