]> git.vpit.fr Git - perl/modules/Variable-Magic.git/blob - Magic.xs
Really don't try to read the context before we're sure we are not dirty
[perl/modules/Variable-Magic.git] / Magic.xs
1 /* This file is part of the Variable::Magic Perl module.
2  * See http://search.cpan.org/dist/Variable-Magic/ */
3
4 #include <stdarg.h> /* <va_list>, va_{start,arg,end}, ... */
5
6 #include <stdio.h>  /* sprintf() */
7
8 #define PERL_NO_GET_CONTEXT
9 #include "EXTERN.h"
10 #include "perl.h"
11 #include "XSUB.h"
12
13 #define __PACKAGE__ "Variable::Magic"
14
15 #define PERL_VERSION_GE(R, V, S) (PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S))))))
16
17 #define PERL_VERSION_LE(R, V, S) (PERL_REVISION < (R) || (PERL_REVISION == (R) && (PERL_VERSION < (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION <= (S))))))
18
19 #ifndef VMG_PERL_PATCHLEVEL
20 # ifdef PERL_PATCHNUM
21 #  define VMG_PERL_PATCHLEVEL PERL_PATCHNUM
22 # else
23 #  define VMG_PERL_PATCHLEVEL 0
24 # endif
25 #endif
26
27 #define VMG_HAS_PERL_OR(P, R, V, S) ((VMG_PERL_PATCHLEVEL >= (P)) || (!VMG_PERL_PATCHLEVEL && PERL_VERSION_GE((R), (V), (S))))
28
29 #define VMG_HAS_PERL_AND(P, R, V, S) (PERL_VERSION_GE((R), (V), (S)) && (!VMG_PERL_PATCHLEVEL || (VMG_PERL_PATCHLEVEL >= (P))))
30
31 /* --- Threads and multiplicity -------------------------------------------- */
32
33 #ifndef NOOP
34 # define NOOP
35 #endif
36
37 #ifndef dNOOP
38 # define dNOOP
39 #endif
40
41 #if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT)
42 # define VMG_MULTIPLICITY 1
43 # ifndef tTHX
44 #  define tTHX PerlInterpreter*
45 # endif
46 #else
47 # define VMG_MULTIPLICITY 0
48 #endif
49
50 #if VMG_MULTIPLICITY && defined(dMY_CXT) && defined(MY_CXT) && defined(START_MY_CXT) && defined(MY_CXT_INIT) && defined(MY_CXT_CLONE)
51 # define VMG_THREADSAFE 1
52 #else
53 # define VMG_THREADSAFE 0
54 # undef  dMY_CXT
55 # define dMY_CXT      dNOOP
56 # undef  MY_CXT
57 # define MY_CXT vmg_globaldata
58 # undef  START_MY_CXT
59 # define START_MY_CXT STATIC my_cxt_t MY_CXT;
60 # undef  MY_CXT_INIT
61 # define MY_CXT_INIT  NOOP
62 # undef  MY_CXT_CLONE
63 # define MY_CXT_CLONE NOOP
64 #endif
65
66 #if VMG_MULTIPLICITY
67
68 STATIC SV *vmg_clone(pTHX_ SV *sv, tTHX owner) {
69 #define vmg_clone(P, O) vmg_clone(aTHX_ (P), (O))
70  CLONE_PARAMS param;
71  param.stashes    = NULL; /* don't need it unless sv is a PVHV */
72  param.flags      = 0;
73  param.proto_perl = owner;
74  return sv_dup(sv, &param);
75 }
76
77 #endif /* VMG_MULTIPLICITY */
78
79 /* --- Compatibility ------------------------------------------------------- */
80
81 #ifndef Newx
82 # define Newx(v, n, c) New(0, v, n, c)
83 #endif
84
85 #ifndef SvMAGIC_set
86 # define SvMAGIC_set(sv, val) (SvMAGIC(sv) = (val))
87 #endif
88
89 #ifndef mPUSHi
90 # define mPUSHi(I) PUSHs(sv_2mortal(newSViv(I)))
91 #endif
92
93 #ifndef PERL_MAGIC_ext
94 # define PERL_MAGIC_ext '~'
95 #endif
96
97 #ifndef MGf_COPY
98 # define MGf_COPY 0
99 #endif
100
101 #ifndef MGf_DUP
102 # define MGf_DUP 0
103 #endif
104
105 #ifndef MGf_LOCAL
106 # define MGf_LOCAL 0
107 #endif
108
109 /* uvar magic and Hash::Util::FieldHash were commited with p28419 */
110 #if VMG_HAS_PERL_AND(28419, 5, 9, 4)
111 # define VMG_UVAR 1
112 #else
113 # define VMG_UVAR 0
114 #endif
115
116 #if !defined(VMG_COMPAT_ARRAY_PUSH_NOLEN) && VMG_HAS_PERL_OR(25854, 5, 9, 3)
117 # define VMG_COMPAT_ARRAY_PUSH_NOLEN 1
118 #else
119 # define VMG_COMPAT_ARRAY_PUSH_NOLEN 0
120 #endif
121
122 /* since 5.9.5 - see #43357 */
123 #if VMG_HAS_PERL_OR(31473, 5, 9, 5)
124 # define VMG_COMPAT_ARRAY_UNDEF_CLEAR 1
125 #else
126 # define VMG_COMPAT_ARRAY_UNDEF_CLEAR 0
127 #endif
128
129 #if VMG_HAS_PERL_OR(32969, 5, 11, 0)
130 # define VMG_COMPAT_SCALAR_LENGTH_NOLEN 1
131 #else
132 # define VMG_COMPAT_SCALAR_LENGTH_NOLEN 0
133 #endif
134
135 #if VMG_UVAR
136
137 /* Bug-free mg_magical - see http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/2008-01/msg00036.html - but specialized to our needs. */
138 STATIC void vmg_sv_magicuvar(pTHX_ SV *sv, const char *uf, I32 len) {
139 #define vmg_sv_magicuvar(S, U, L) vmg_sv_magicuvar(aTHX_ (S), (U), (L))
140  const MAGIC* mg;
141  sv_magic(sv, NULL, PERL_MAGIC_uvar, uf, len);
142  /* uvar magic has set and get magic, hence this has set SVs_GMG and SVs_SMG. */
143  PERL_UNUSED_CONTEXT;
144  if ((mg = SvMAGIC(sv))) {
145   SvRMAGICAL_off(sv);
146   do {
147    const MGVTBL* const vtbl = mg->mg_virtual;
148    if (vtbl) {
149     if (vtbl->svt_clear) {
150      SvRMAGICAL_on(sv);
151      break;
152     }
153    }
154   } while ((mg = mg->mg_moremagic));
155  }
156 }
157
158 #endif /* VMG_UVAR */
159
160 /* --- Context-safe global data -------------------------------------------- */
161
162 #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION
163
164 typedef struct {
165  HV *wizz;
166  U16 count;
167 } my_cxt_t;
168
169 START_MY_CXT
170
171 /* --- Signatures ---------------------------------------------------------- */
172
173 #define SIG_MIN ((U16) (1u << 8))
174 #define SIG_MAX ((U16) ((1u << 16) - 1))
175 #define SIG_NBR (SIG_MAX - SIG_MIN + 1)
176 #define SIG_WIZ ((U16) ((1u << 8) - 1))
177
178 /* ... Generate signatures ................................................. */
179
180 STATIC U16 vmg_gensig(pTHX) {
181 #define vmg_gensig() vmg_gensig(aTHX)
182  U16 sig;
183  char buf[8];
184  dMY_CXT;
185
186  do {
187   sig = SIG_NBR * Drand01() + SIG_MIN;
188  } while (hv_exists(MY_CXT.wizz, buf, sprintf(buf, "%u", sig)));
189
190  return sig;
191 }
192
193 /* --- MGWIZ structure ----------------------------------------------------- */
194
195 typedef struct {
196  MGVTBL *vtbl;
197  U16 sig;
198  U16 uvar;
199  SV *cb_data;
200  SV *cb_get, *cb_set, *cb_len, *cb_clear, *cb_free;
201 #if MGf_COPY
202  SV *cb_copy;
203 #endif /* MGf_COPY */
204 #if MGf_DUP
205  SV *cb_dup;
206 #endif /* MGf_DUP */
207 #if MGf_LOCAL
208  SV *cb_local;
209 #endif /* MGf_LOCAL */
210 #if VMG_UVAR
211  SV *cb_fetch, *cb_store, *cb_exists, *cb_delete;
212 #endif /* VMG_UVAR */
213 #if VMG_MULTIPLICITY
214  tTHX owner;
215 #endif /* VMG_MULTIPLICITY */
216 } MGWIZ;
217
218 #define MGWIZ2SV(W) (newSVuv(PTR2UV(W)))
219 #define SV2MGWIZ(S) (INT2PTR(MGWIZ*, SvUVX((SV *) (S))))
220
221 /* ... Construct private data .............................................. */
222
223 STATIC SV *vmg_data_new(pTHX_ SV *ctor, SV *sv, AV *args) {
224 #define vmg_data_new(C, S, A) vmg_data_new(aTHX_ (C), (S), (A))
225  SV *nsv;
226  I32 i, alen = (args == NULL) ? 0 : av_len(args);
227
228  dSP;
229  int count;
230
231  ENTER;
232  SAVETMPS;
233
234  PUSHMARK(SP);
235  EXTEND(SP, alen + 1);
236  PUSHs(sv_2mortal(newRV_inc(sv)));
237  for (i = 0; i < alen; ++i)
238   PUSHs(*av_fetch(args, i, 0));
239  PUTBACK;
240
241  count = call_sv(ctor, G_SCALAR);
242
243  SPAGAIN;
244
245  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
246  nsv = POPs;
247 #if PERL_VERSION_LE(5, 8, 2)
248  nsv = sv_newref(nsv); /* Workaround some bug in SvREFCNT_inc() */
249 #else
250  SvREFCNT_inc(nsv);    /* Or it will be destroyed in FREETMPS */
251 #endif
252
253  PUTBACK;
254
255  FREETMPS;
256  LEAVE;
257
258  return nsv;
259 }
260
261 STATIC SV *vmg_data_get(SV *sv, U16 sig) {
262  MAGIC *mg, *moremagic;
263
264  if (SvTYPE(sv) >= SVt_PVMG) {
265   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
266    moremagic = mg->mg_moremagic;
267    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == sig)) { break; }
268   }
269   if (mg) { return mg->mg_obj; }
270  }
271
272  return NULL;
273
274
275 /* ... Magic cast/dispell .................................................. */
276
277 #if VMG_UVAR
278 STATIC I32 vmg_svt_val(pTHX_ IV, SV *);
279
280 STATIC void vmg_uvar_del(SV *sv, MAGIC *prevmagic, MAGIC *mg, MAGIC *moremagic) {
281  if (prevmagic) {
282   prevmagic->mg_moremagic = moremagic;
283  } else {
284   SvMAGIC_set(sv, moremagic);
285  }
286  mg->mg_moremagic = NULL;
287  Safefree(mg->mg_ptr);
288  Safefree(mg);
289 }
290 #endif /* VMG_UVAR */
291
292 STATIC UV vmg_cast(pTHX_ SV *sv, SV *wiz, AV *args) {
293 #define vmg_cast(S, W, A) vmg_cast(aTHX_ (S), (W), (A))
294  MAGIC *mg = NULL, *moremagic = NULL;
295  MGWIZ *w;
296  SV *data;
297
298  w = SV2MGWIZ(wiz);
299
300  if (SvTYPE(sv) >= SVt_PVMG) {
301   for (mg = SvMAGIC(sv); mg; mg = moremagic) {
302    moremagic = mg->mg_moremagic;
303    if ((mg->mg_type == PERL_MAGIC_ext) && (mg->mg_private == w->sig)) { break; }
304   }
305   if (mg) { return 1; }
306  }
307
308  data = (w->cb_data) ? vmg_data_new(w->cb_data, sv, args) : NULL;
309  mg = sv_magicext(sv, data, PERL_MAGIC_ext, w->vtbl, (const char *) wiz, HEf_SVKEY);
310  mg->mg_private = w->sig;
311 #if MGf_COPY
312  if (w->cb_copy)
313   mg->mg_flags |= MGf_COPY;
314 #endif /* MGf_COPY */
315 #if MGf_DUP
316  if (w->cb_dup)
317   mg->mg_flags |= MGf_DUP;
318 #endif /* MGf_DUP */
319 #if MGf_LOCAL
320  if (w->cb_local)
321   mg->mg_flags |= MGf_LOCAL;
322 #endif /* MGf_LOCAL */
323
324 #if VMG_UVAR
325  if (w->uvar && SvTYPE(sv) >= SVt_PVHV) {
326   MAGIC *prevmagic;
327   int add_uvar = 1;
328   struct ufuncs uf[2];
329
330   uf[0].uf_val   = vmg_svt_val;
331   uf[0].uf_set   = NULL;
332   uf[0].uf_index = 0;
333   uf[1].uf_val   = NULL;
334   uf[1].uf_set   = NULL;
335   uf[1].uf_index = 0;
336
337   /* One uvar magic in the chain is enough. */
338   for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
339    moremagic = mg->mg_moremagic;
340    if (mg->mg_type == PERL_MAGIC_uvar) { break; }
341   }
342
343   if (mg) { /* Found another uvar magic. */
344    struct ufuncs *olduf = (struct ufuncs *) mg->mg_ptr;
345    if (olduf->uf_val == vmg_svt_val) {
346     /* It's our uvar magic, nothing to do. */
347     add_uvar = 0;
348    } else {
349     /* It's another uvar magic, backup it and replace it by ours. */
350     uf[1] = *olduf;
351     vmg_uvar_del(sv, prevmagic, mg, moremagic);
352    }
353   }
354
355   if (add_uvar) {
356    vmg_sv_magicuvar(sv, (const char *) &uf, sizeof(uf));
357   }
358
359  }
360 #endif /* VMG_UVAR */
361
362  return 1;
363 }
364
365 STATIC UV vmg_dispell(pTHX_ SV *sv, U16 sig) {
366 #define vmg_dispell(S, Z) vmg_dispell(aTHX_ (S), (Z))
367 #if VMG_UVAR
368  U32 uvars = 0;
369 #endif /* VMG_UVAR */
370  MAGIC *mg, *prevmagic, *moremagic = NULL;
371
372  if (SvTYPE(sv) < SVt_PVMG) { return 0; }
373
374  for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic) {
375   moremagic = mg->mg_moremagic;
376   if (mg->mg_type == PERL_MAGIC_ext) {
377    if (mg->mg_private == sig) {
378 #if VMG_UVAR
379     /* If the current has no uvar, short-circuit uvar deletion. */
380     uvars = (SV2MGWIZ(mg->mg_ptr)->uvar) ? (uvars + 1) : 0;
381 #endif /* VMG_UVAR */
382     break;
383 #if VMG_UVAR
384    } else if ((mg->mg_private >= SIG_MIN) &&
385               (mg->mg_private <= SIG_MAX) &&
386                SV2MGWIZ(mg->mg_ptr)->uvar) {
387     ++uvars;
388     /* We can't break here since we need to find the ext magic to delete. */
389 #endif /* VMG_UVAR */
390    }
391   }
392  }
393  if (!mg) { return 0; }
394
395  if (prevmagic) {
396   prevmagic->mg_moremagic = moremagic;
397  } else {
398   SvMAGIC_set(sv, moremagic);
399  }
400  mg->mg_moremagic = NULL;
401
402  if (mg->mg_obj != sv) { SvREFCNT_dec(mg->mg_obj); } /* Destroy private data */
403  SvREFCNT_dec((SV *) mg->mg_ptr); /* Unreference the wizard */
404  Safefree(mg);
405
406 #if VMG_UVAR
407  if (uvars == 1 && SvTYPE(sv) >= SVt_PVHV) {
408   /* mg was the first ext magic in the chain that had uvar */
409
410   for (mg = moremagic; mg; mg = mg->mg_moremagic) {
411    if ((mg->mg_type == PERL_MAGIC_ext) &&
412        (mg->mg_private >= SIG_MIN) &&
413        (mg->mg_private <= SIG_MAX) &&
414         SV2MGWIZ(mg->mg_ptr)->uvar) {
415     ++uvars;
416     break;
417    }
418   }
419
420   if (uvars == 1) {
421    struct ufuncs *uf;
422    for (prevmagic = NULL, mg = SvMAGIC(sv); mg; prevmagic = mg, mg = moremagic){
423     moremagic = mg->mg_moremagic;
424     if (mg->mg_type == PERL_MAGIC_uvar) { break; }
425    }
426    /* assert(mg); */
427    uf = (struct ufuncs *) mg->mg_ptr;
428    /* assert(uf->uf_val == vmg_svt_val); */
429    if (uf[1].uf_val || uf[1].uf_set) {
430     /* Revert the original uvar magic. */
431     uf[0] = uf[1];
432     Renew(uf, 1, struct ufuncs);
433     mg->mg_ptr = (char *) uf;
434     mg->mg_len = sizeof(struct ufuncs);
435    } else {
436     /* Remove the uvar magic. */
437     vmg_uvar_del(sv, prevmagic, mg, moremagic);
438    }
439   }
440  }
441 #endif /* VMG_UVAR */
442
443  return 1;
444 }
445
446 /* ... svt callbacks ....................................................... */
447
448 STATIC int vmg_cb_call(pTHX_ SV *cb, SV *sv, SV *data, unsigned int args, ...) {
449  va_list ap;
450  SV *svr;
451  int ret;
452  unsigned int i;
453
454  dSP;
455  int count;
456
457  ENTER;
458  SAVETMPS;
459
460  PUSHMARK(SP);
461  EXTEND(SP, args + 2);
462  PUSHs(sv_2mortal(newRV_inc(sv)));
463  PUSHs(data ? data : &PL_sv_undef);
464  va_start(ap, args);
465  for (i = 0; i < args; ++i) {
466   SV *sva = va_arg(ap, SV *);
467   PUSHs(sva ? sva : &PL_sv_undef);
468  }
469  va_end(ap);
470  PUTBACK;
471
472  count = call_sv(cb, G_SCALAR);
473
474  SPAGAIN;
475
476  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
477  svr = POPs;
478  ret = SvOK(svr) ? SvIV(svr) : 0;
479
480  PUTBACK;
481
482  FREETMPS;
483  LEAVE;
484
485  return ret;
486 }
487
488 #define vmg_cb_call1(I, S, D)         vmg_cb_call(aTHX_ (I), (S), (D), 0)
489 #define vmg_cb_call2(I, S, D, S2)     vmg_cb_call(aTHX_ (I), (S), (D), 1, (S2))
490 #define vmg_cb_call3(I, S, D, S2, S3) vmg_cb_call(aTHX_ (I), (S), (D), 2, (S2), (S3))
491
492 STATIC int vmg_svt_get(pTHX_ SV *sv, MAGIC *mg) {
493  return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_get, sv, mg->mg_obj);
494 }
495
496 STATIC int vmg_svt_set(pTHX_ SV *sv, MAGIC *mg) {
497  return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_set, sv, mg->mg_obj);
498 }
499
500 STATIC U32 vmg_svt_len(pTHX_ SV *sv, MAGIC *mg) {
501  SV *svr;
502  I32 len;
503  U32 ret;
504
505  dSP;
506  int count;
507
508  ENTER;
509  SAVETMPS;
510
511  PUSHMARK(SP);
512  EXTEND(SP, 3);
513  PUSHs(sv_2mortal(newRV_inc(sv)));
514  PUSHs(mg->mg_obj ? mg->mg_obj : &PL_sv_undef);
515  if (SvTYPE(sv) == SVt_PVAV) {
516   len = av_len((AV *) sv) + 1;
517   mPUSHi(len);
518  } else {
519   len = 1;
520   PUSHs(&PL_sv_undef);
521  }
522  PUTBACK;
523
524  count = call_sv(SV2MGWIZ(mg->mg_ptr)->cb_len, G_SCALAR);
525
526  SPAGAIN;
527
528  if (count != 1) { croak("Callback needs to return 1 scalar\n"); }
529  svr = POPs;
530  ret = SvOK(svr) ? SvUV(svr) : len;
531
532  PUTBACK;
533
534  FREETMPS;
535  LEAVE;
536
537  return ret - 1;
538 }
539
540 STATIC int vmg_svt_clear(pTHX_ SV *sv, MAGIC *mg) {
541  return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_clear, sv, mg->mg_obj);
542 }
543
544 STATIC int vmg_svt_free(pTHX_ SV *sv, MAGIC *mg) {
545  /* So that it can survive tmp cleanup in vmg_cb_call */
546  SvREFCNT_inc(sv);
547  /* Perl_mg_free will get rid of the magic and decrement mg->mg_obj and
548   * mg->mg_ptr reference count */
549  return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_free, sv, mg->mg_obj);
550 }
551
552 #if MGf_COPY
553 STATIC int vmg_svt_copy(pTHX_ SV *sv, MAGIC *mg, SV *nsv, const char *key,
554 # if VMG_HAS_PERL_AND(33256, 5, 11, 0)
555   I32 keylen
556 # else
557   int keylen
558 # endif
559  ) {
560  SV *keysv;
561  int ret;
562
563  if (keylen == HEf_SVKEY) {
564   keysv = (SV *) key;
565  } else {
566   keysv = newSVpvn(key, keylen);
567  }
568
569  ret = vmg_cb_call3(SV2MGWIZ(mg->mg_ptr)->cb_copy, sv, mg->mg_obj, keysv, nsv);
570
571  if (keylen != HEf_SVKEY) {
572   SvREFCNT_dec(keysv);
573  }
574
575  return ret;
576 }
577 #endif /* MGf_COPY */
578
579 #if 0 /*  MGf_DUP */
580 STATIC int vmg_svt_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param) {
581  return 0;
582 }
583 #endif /* MGf_DUP */
584
585 #if MGf_LOCAL
586 STATIC int vmg_svt_local(pTHX_ SV *nsv, MAGIC *mg) {
587  return vmg_cb_call1(SV2MGWIZ(mg->mg_ptr)->cb_local, nsv, mg->mg_obj);
588 }
589 #endif /* MGf_LOCAL */
590
591 #if VMG_UVAR
592 STATIC I32 vmg_svt_val(pTHX_ IV action, SV *sv) {
593  struct ufuncs *uf;
594  MAGIC *mg;
595  SV *key = NULL;
596
597  mg  = mg_find(sv, PERL_MAGIC_uvar);
598  /* mg can't be NULL or we wouldn't be there. */
599  key = mg->mg_obj;
600  uf  = (struct ufuncs *) mg->mg_ptr;
601
602  if (uf[1].uf_val != NULL) { uf[1].uf_val(aTHX_ action, sv); }
603  if (uf[1].uf_set != NULL) { uf[1].uf_set(aTHX_ action, sv); }
604
605  action &= HV_FETCH_ISSTORE | HV_FETCH_ISEXISTS | HV_FETCH_LVALUE | HV_DELETE;
606  for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) {
607   MGWIZ *w;
608   if ((mg->mg_type != PERL_MAGIC_ext)
609    || (mg->mg_private < SIG_MIN)
610    || (mg->mg_private > SIG_MAX)) { continue; }
611   w = SV2MGWIZ(mg->mg_ptr);
612   if (!w->uvar) { continue; }
613   switch (action) {
614    case 0:
615     if (w->cb_fetch)  { vmg_cb_call2(w->cb_fetch,  sv, mg->mg_obj, key); }
616     break;
617    case HV_FETCH_ISSTORE:
618    case HV_FETCH_LVALUE:
619    case (HV_FETCH_ISSTORE|HV_FETCH_LVALUE):
620     if (w->cb_store)  { vmg_cb_call2(w->cb_store,  sv, mg->mg_obj, key); }
621     break;
622    case HV_FETCH_ISEXISTS:
623     if (w->cb_exists) { vmg_cb_call2(w->cb_exists, sv, mg->mg_obj, key); }
624     break;
625    case HV_DELETE:
626     if (w->cb_delete) { vmg_cb_call2(w->cb_delete, sv, mg->mg_obj, key); }
627     break;
628   }
629  }
630
631  return 0;
632 }
633 #endif /* VMG_UVAR */
634
635 /* ... Wizard destructor ................................................... */
636
637 STATIC int vmg_wizard_free(pTHX_ SV *wiz, MAGIC *mg) {
638  char buf[8];
639  MGWIZ *w;
640
641  if (PL_dirty) /* during global destruction, the context is already freed */
642   return 0;
643
644  w = SV2MGWIZ(wiz);
645 #if VMG_MULTIPLICITY
646  if (w->owner != aTHX)
647   return 0;
648  w->owner = NULL;
649 #endif /* VMG_MULTIPLICITY */
650
651  dMY_CXT;
652  if (hv_delete(MY_CXT.wizz, buf, sprintf(buf, "%u", w->sig), 0)) {
653   --MY_CXT.count;
654  }
655  SvFLAGS(wiz) |= SVf_BREAK;
656  FREETMPS;
657
658  if (w->cb_data  != NULL) { SvREFCNT_dec(SvRV(w->cb_data)); }
659  if (w->cb_get   != NULL) { SvREFCNT_dec(SvRV(w->cb_get)); }
660  if (w->cb_set   != NULL) { SvREFCNT_dec(SvRV(w->cb_set)); }
661  if (w->cb_len   != NULL) { SvREFCNT_dec(SvRV(w->cb_len)); }
662  if (w->cb_clear != NULL) { SvREFCNT_dec(SvRV(w->cb_clear)); }
663  if (w->cb_free  != NULL) { SvREFCNT_dec(SvRV(w->cb_free)); }
664 #if MGf_COPY
665  if (w->cb_copy  != NULL) { SvREFCNT_dec(SvRV(w->cb_copy)); }
666 #endif /* MGf_COPY */
667 #if MGf_DUP
668  if (w->cb_dup   != NULL) { SvREFCNT_dec(SvRV(w->cb_dup)); }
669 #endif /* MGf_DUP */
670 #if MGf_LOCAL
671  if (w->cb_local != NULL) { SvREFCNT_dec(SvRV(w->cb_local)); }
672 #endif /* MGf_LOCAL */
673 #if VMG_UVAR
674  if (w->cb_fetch  != NULL) { SvREFCNT_dec(SvRV(w->cb_fetch)); }
675  if (w->cb_store  != NULL) { SvREFCNT_dec(SvRV(w->cb_store)); }
676  if (w->cb_exists != NULL) { SvREFCNT_dec(SvRV(w->cb_exists)); }
677  if (w->cb_delete != NULL) { SvREFCNT_dec(SvRV(w->cb_delete)); }
678 #endif /* VMG_UVAR */
679
680  Safefree(w->vtbl);
681  Safefree(w);
682
683  return 0;
684 }
685
686 STATIC MGVTBL vmg_wizard_vtbl = {
687  NULL,            /* get */
688  NULL,            /* set */
689  NULL,            /* len */
690  NULL,            /* clear */
691  vmg_wizard_free, /* free */
692 #if MGf_COPY
693  NULL,            /* copy */
694 #endif /* MGf_COPY */
695 #if MGf_DUP
696  NULL,            /* dup */
697 #endif /* MGf_DUP */
698 #if MGf_LOCAL
699  NULL,            /* local */
700 #endif /* MGf_LOCAL */
701 };
702
703 STATIC const char vmg_invalid_wiz[]    = "Invalid wizard object";
704 STATIC const char vmg_invalid_sv[]     = "Invalid variable";
705 STATIC const char vmg_invalid_sig[]    = "Invalid numeric signature";
706 STATIC const char vmg_wrongargnum[]    = "Wrong number of arguments";
707 STATIC const char vmg_toomanysigs[]    = "Too many magic signatures used";
708 STATIC const char vmg_argstorefailed[] = "Error while storing arguments";
709
710 STATIC U16 vmg_sv2sig(pTHX_ SV *sv) {
711 #define vmg_sv2sig(S) vmg_sv2sig(aTHX_ (S))
712  U16 sig;
713
714  if (SvIOK(sv)) {
715   sig = SvUVX(sv);
716  } else if (SvNOK(sv)) {
717   sig = SvNVX(sv);
718  } else if ((SvPOK(sv) && grok_number(SvPVX(sv), SvCUR(sv), NULL))) {
719   sig = SvUV(sv);
720  } else {
721   croak(vmg_invalid_sig);
722  }
723  if (sig < SIG_MIN) { sig += SIG_MIN; }
724  if (sig > SIG_MAX) { sig %= SIG_MAX + 1; }
725
726  return sig;
727 }
728
729 STATIC U16 vmg_wizard_sig(pTHX_ SV *wiz) {
730 #define vmg_wizard_sig(W) vmg_wizard_sig(aTHX_ (W))
731  char buf[8];
732  U16 sig;
733
734  if (SvROK(wiz)) {
735   sig = SV2MGWIZ(SvRV(wiz))->sig;
736  } else if (SvOK(wiz)) {
737   sig = vmg_sv2sig(wiz);
738  } else {
739   croak(vmg_invalid_wiz);
740  }
741
742  dMY_CXT;
743
744  if (!hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))
745   sig = 0;
746
747  return sig;
748 }
749
750 STATIC SV *vmg_wizard_wiz(pTHX_ SV *wiz) {
751 #define vmg_wizard_wiz(W) vmg_wizard_wiz(aTHX_ (W))
752  char buf[8];
753  SV **old;
754  U16 sig;
755
756  if (SvROK(wiz)) {
757   wiz = SvRV(wiz);
758 #if VMG_MULTIPLICITY
759   if (SV2MGWIZ(wiz)->owner == aTHX)
760    return wiz;
761 #endif /* VMG_MULTIPLICITY */
762   sig = SV2MGWIZ(wiz)->sig;
763  } else if (SvOK(wiz)) {
764   sig = vmg_sv2sig(wiz);
765  } else {
766   croak(vmg_invalid_wiz);
767  }
768
769  dMY_CXT;
770
771  return (old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))
772          ? *old : NULL;
773 }
774
775 #define VMG_SET_CB(S, N)              \
776  cb = (S);                            \
777  w->cb_ ## N = (SvOK(cb) && SvROK(cb)) ? newRV_inc(SvRV(cb)) : NULL;
778
779 #define VMG_SET_SVT_CB(S, N)          \
780  cb = (S);                            \
781  if (SvOK(cb) && SvROK(cb)) {         \
782   t->svt_ ## N = vmg_svt_ ## N;       \
783   w->cb_  ## N = newRV_inc(SvRV(cb)); \
784  } else {                             \
785   t->svt_ ## N = NULL;                \
786   w->cb_  ## N = NULL;                \
787  }
788
789 #if VMG_MULTIPLICITY
790
791 #define VMG_CLONE_CB(N) \
792  z->cb_ ## N = (w->cb_ ## N) ? newRV_noinc(vmg_clone(SvRV(w->cb_ ## N), \
793                                            w->owner))                   \
794                              : NULL;
795
796 STATIC MGWIZ *vmg_wizard_clone(pTHX_ const MGWIZ *w) {
797 #define vmg_wizard_clone(W) vmg_wizard_clone(aTHX_ (W))
798  MGVTBL *t;
799  MGWIZ *z;
800
801  Newx(t, 1, MGVTBL);
802  Copy(w->vtbl, t, 1, MGVTBL);
803
804  Newx(z, 1, MGWIZ);
805  VMG_CLONE_CB(data);
806  VMG_CLONE_CB(get);
807  VMG_CLONE_CB(set);
808  VMG_CLONE_CB(len);
809  VMG_CLONE_CB(clear);
810  VMG_CLONE_CB(free);
811 #if MGf_COPY
812  VMG_CLONE_CB(copy);
813 #endif /* MGf_COPY */
814 #if MGf_DUP
815  VMG_CLONE_CB(dup);
816 #endif /* MGf_DUP */
817 #if MGf_LOCAL
818  VMG_CLONE_CB(local);
819 #endif /* MGf_LOCAL */
820 #if VMG_UVAR
821  VMG_CLONE_CB(fetch);
822  VMG_CLONE_CB(store);
823  VMG_CLONE_CB(exists);
824  VMG_CLONE_CB(delete);
825 #endif /* VMG_UVAR */
826  z->owner = aTHX;
827  z->vtbl  = t;
828  z->sig   = w->sig;
829  z->uvar  = w->uvar;
830
831  return z;
832 }
833
834 #endif /* VMG_MULTIPLICITY */
835
836 /* --- XS ------------------------------------------------------------------ */
837
838 MODULE = Variable::Magic            PACKAGE = Variable::Magic
839
840 PROTOTYPES: ENABLE
841
842 BOOT:
843 {
844  HV *stash;
845  MY_CXT_INIT;
846  MY_CXT.wizz = newHV();
847  hv_iterinit(MY_CXT.wizz); /* Allocate iterator */
848  MY_CXT.count = 0;
849  stash = gv_stashpv(__PACKAGE__, 1);
850  newCONSTSUB(stash, "SIG_MIN",   newSVuv(SIG_MIN));
851  newCONSTSUB(stash, "SIG_MAX",   newSVuv(SIG_MAX));
852  newCONSTSUB(stash, "SIG_NBR",   newSVuv(SIG_NBR));
853  newCONSTSUB(stash, "MGf_COPY",  newSVuv(MGf_COPY));
854  newCONSTSUB(stash, "MGf_DUP",   newSVuv(MGf_DUP));
855  newCONSTSUB(stash, "MGf_LOCAL", newSVuv(MGf_LOCAL));
856  newCONSTSUB(stash, "VMG_UVAR",  newSVuv(VMG_UVAR));
857  newCONSTSUB(stash, "VMG_COMPAT_ARRAY_PUSH_NOLEN",
858                     newSVuv(VMG_COMPAT_ARRAY_PUSH_NOLEN));
859  newCONSTSUB(stash, "VMG_COMPAT_ARRAY_UNDEF_CLEAR",
860                     newSVuv(VMG_COMPAT_ARRAY_UNDEF_CLEAR));
861  newCONSTSUB(stash, "VMG_COMPAT_SCALAR_LENGTH_NOLEN",
862                     newSVuv(VMG_COMPAT_SCALAR_LENGTH_NOLEN));
863  newCONSTSUB(stash, "VMG_PERL_PATCHLEVEL", newSVuv(VMG_PERL_PATCHLEVEL));
864  newCONSTSUB(stash, "VMG_THREADSAFE",      newSVuv(VMG_THREADSAFE));
865 }
866
867 void
868 CLONE(...)
869 PROTOTYPE: DISABLE
870 PREINIT:
871  HV *hv;
872  U16 count;
873 CODE:
874 #if VMG_THREADSAFE
875  {
876   HE *key;
877   dMY_CXT;
878   count = MY_CXT.count;
879   hv = newHV();
880   hv_iterinit(hv); /* Allocate iterator */
881   hv_iterinit(MY_CXT.wizz);
882   while (key = hv_iternext(MY_CXT.wizz)) {
883    STRLEN len;
884    char *sig = HePV(key, len);
885    SV *sv;
886    MAGIC *mg;
887    MGWIZ *w;
888    sv = MGWIZ2SV(vmg_wizard_clone(SV2MGWIZ(HeVAL(key))));
889    mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, 0);
890    mg->mg_private = SIG_WIZ;
891    SvREADONLY_on(sv);
892    hv_store(hv, sig, len, sv, HeHASH(key));
893   }
894  }
895  {
896   MY_CXT_CLONE;
897   MY_CXT.wizz  = hv;
898   MY_CXT.count = count;
899  }
900 #endif /* VMG_THREADSAFE */
901
902 SV *_wizard(...)
903 PROTOTYPE: DISABLE
904 PREINIT:
905  I32 i = 0;
906  U16 sig;
907  char buf[8];
908  MGWIZ *w;
909  MGVTBL *t;
910  MAGIC *mg;
911  SV *sv;
912  SV *svsig;
913  SV *cb;
914 CODE:
915  dMY_CXT;
916
917  if (items != 7
918 #if MGf_COPY
919               + 1
920 #endif /* MGf_COPY */
921 #if MGf_DUP
922               + 1
923 #endif /* MGf_DUP */
924 #if MGf_LOCAL
925               + 1
926 #endif /* MGf_LOCAL */
927 #if VMG_UVAR
928               + 4
929 #endif /* VMG_UVAR */
930               ) { croak(vmg_wrongargnum); }
931
932  svsig = ST(i++);
933  if (SvOK(svsig)) {
934   SV **old;
935   sig = vmg_sv2sig(svsig);
936   if ((old = hv_fetch(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), 0))) {
937    ST(0) = sv_2mortal(newRV_inc(*old));
938    XSRETURN(1);
939   }
940  } else {
941   if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
942   sig = vmg_gensig();
943  }
944  
945  Newx(t, 1, MGVTBL);
946  Newx(w, 1, MGWIZ);
947
948  VMG_SET_CB(ST(i++), data);
949  VMG_SET_SVT_CB(ST(i++), get);
950  VMG_SET_SVT_CB(ST(i++), set);
951  VMG_SET_SVT_CB(ST(i++), len);
952  VMG_SET_SVT_CB(ST(i++), clear);
953  VMG_SET_SVT_CB(ST(i++), free);
954 #if MGf_COPY
955  VMG_SET_SVT_CB(ST(i++), copy);
956 #endif /* MGf_COPY */
957 #if MGf_DUP
958  /* VMG_SET_SVT_CB(ST(i++), dup); */
959  i++;
960  t->svt_dup = NULL;
961  w->cb_dup  = NULL;
962 #endif /* MGf_DUP */
963 #if MGf_LOCAL
964  VMG_SET_SVT_CB(ST(i++), local);
965 #endif /* MGf_LOCAL */
966 #if VMG_UVAR
967  VMG_SET_CB(ST(i++), fetch);
968  VMG_SET_CB(ST(i++), store);
969  VMG_SET_CB(ST(i++), exists);
970  VMG_SET_CB(ST(i++), delete);
971 #endif /* VMG_UVAR */
972 #if VMG_MULTIPLICITY
973  w->owner = aTHX;
974 #endif /* VMG_MULTIPLICITY */
975
976  w->vtbl = t;
977  w->sig  = sig;
978 #if VMG_UVAR
979  w->uvar = (w->cb_fetch || w->cb_store || w->cb_exists || w->cb_delete);
980 #endif /* VMG_UVAR */
981
982  sv = MGWIZ2SV(w);
983  mg = sv_magicext(sv, NULL, PERL_MAGIC_ext, &vmg_wizard_vtbl, NULL, 0);
984  mg->mg_private = SIG_WIZ;
985  SvREADONLY_on(sv);
986
987  hv_store(MY_CXT.wizz, buf, sprintf(buf, "%u", sig), sv, 0);
988  ++MY_CXT.count;
989
990  RETVAL = newRV_noinc(sv);
991 OUTPUT:
992  RETVAL
993
994 SV *gensig()
995 PROTOTYPE:
996 CODE:
997  dMY_CXT;
998  if (MY_CXT.count >= SIG_NBR) { croak(vmg_toomanysigs); }
999  RETVAL = newSVuv(vmg_gensig());
1000 OUTPUT:
1001  RETVAL
1002
1003 SV *getsig(SV *wiz)
1004 PROTOTYPE: $
1005 CODE:
1006  if (!SvROK(wiz)) { croak(vmg_invalid_wiz); }
1007  RETVAL = newSVuv(SV2MGWIZ(SvRV(wiz))->sig);
1008 OUTPUT:
1009  RETVAL
1010
1011 SV *cast(SV *sv, SV *wiz, ...)
1012 PROTOTYPE: \[$@%&*]$@
1013 PREINIT:
1014  AV *args = NULL;
1015  SV *ret;
1016 CODE:
1017  wiz = vmg_wizard_wiz(wiz);
1018  if (!wiz)
1019   XSRETURN_UNDEF;
1020  if (items > 2) {
1021   I32 i;
1022   args = newAV();
1023   av_fill(args, items - 2);
1024   for (i = 2; i < items; ++i) {
1025    SV *arg = ST(i);
1026    SvREFCNT_inc(arg);
1027    if (av_store(args, i - 2, arg) == NULL) { croak(vmg_argstorefailed); }
1028   }
1029  }
1030  ret = newSVuv(vmg_cast(SvRV(sv), wiz, args));
1031  SvREFCNT_dec(args);
1032  RETVAL = ret;
1033 OUTPUT:
1034  RETVAL
1035
1036 SV *getdata(SV *sv, SV *wiz)
1037 PROTOTYPE: \[$@%&*]$
1038 PREINIT:
1039  SV *data;
1040  U16 sig;
1041 CODE:
1042  sig = vmg_wizard_sig(wiz);
1043  if (!sig)
1044   XSRETURN_UNDEF;
1045  data = vmg_data_get(SvRV(sv), sig);
1046  if (!data) { XSRETURN_UNDEF; }
1047  ST(0) = data;
1048  XSRETURN(1);
1049
1050 SV *dispell(SV *sv, SV *wiz)
1051 PROTOTYPE: \[$@%&*]$
1052 PREINIT:
1053  U16 sig;
1054 CODE:
1055  sig = vmg_wizard_sig(wiz);
1056  if (!sig)
1057   XSRETURN_UNDEF;
1058  RETVAL = newSVuv(vmg_dispell(SvRV(sv), sig));
1059 OUTPUT:
1060  RETVAL