one more todo
[gitmo/Package-Stash-XS.git] / Stash.xs
1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #define NEED_newRV_noinc
6 #define NEED_sv_2pv_flags
7 #include "ppport.h"
8
9 #ifndef gv_fetchsv
10 #define gv_fetchsv(n,f,t) gv_fetchpv(SvPV_nolen(n), f, t)
11 #endif
12
13 #ifndef mro_method_changed_in
14 #define mro_method_changed_in(x) PL_sub_generation++
15 #endif
16
17 #ifdef newSVhek
18 #define newSVhe(he) newSVhek(HeKEY_hek(he))
19 #else
20 #define newSVhe(he) newSVpvn(HePV(he))
21 #endif
22
23 #ifndef savesvpv
24 #define savesvpv(s) savepv(SvPV_nolen(s))
25 #endif
26
27 /* HACK: scalar slots are always populated on perl < 5.10, so treat undef
28  * as nonexistent. this is consistent with the previous behavior of the pure
29  * perl version of this module (since this is the behavior that perl sees
30  * in all versions */
31 #if PERL_VERSION < 10
32 #define GvSVOK(g) (GvSV(g) && SvTYPE(GvSV(g)) != SVt_NULL)
33 #else
34 #define GvSVOK(g) GvSV(g)
35 #endif
36
37 #define GvAVOK(g) GvAV(g)
38 #define GvHVOK(g) GvHV(g)
39 #define GvCVOK(g) GvCVu(g) /* XXX: should this really be GvCVu? or GvCV? */
40 #define GvIOOK(g) GvIO(g)
41
42 #define GvSetSV(g,v) do {               \
43     SvREFCNT_dec(GvSV(g));              \
44     if ((GvSV(g) = (SV*)(v)))           \
45         GvIMPORTED_SV_on(g);            \
46 } while (0)
47 #define GvSetAV(g,v) do {               \
48     SvREFCNT_dec(GvAV(g));              \
49     if ((GvAV(g) = (AV*)(v)))           \
50         GvIMPORTED_AV_on(g);            \
51 } while (0)
52 #define GvSetHV(g,v) do {               \
53     SvREFCNT_dec(GvHV(g));              \
54     if ((GvHV(g) = (HV*)(v)))           \
55         GvIMPORTED_HV_on(g);            \
56 } while (0)
57 #define GvSetCV(g,v) do {               \
58     SvREFCNT_dec(GvCV(g));              \
59     if ((GvCV(g) = (CV*)(v))) {         \
60         GvIMPORTED_CV_on(g);            \
61         GvASSUMECV_on(g);               \
62     }                                   \
63     GvCVGEN(g) = 0;                     \
64     mro_method_changed_in(GvSTASH(g));  \
65 } while (0)
66 #define GvSetIO(g,v) do {               \
67     SvREFCNT_dec(GvIO(g));              \
68     GvIOp(g) = (IO*)(v);                \
69 } while (0)
70
71 typedef enum {
72     VAR_NONE = 0,
73     VAR_SCALAR,
74     VAR_ARRAY,
75     VAR_HASH,
76     VAR_CODE,
77     VAR_IO,
78     VAR_GLOB,  /* TODO: unimplemented */
79     VAR_FORMAT /* TODO: unimplemented */
80 } vartype_t;
81
82 typedef struct {
83     vartype_t type;
84     char sigil;
85     char *name;
86 } varspec_t;
87
88 const char *vartype_to_string(vartype_t type)
89 {
90     switch (type) {
91     case VAR_SCALAR:
92         return "SCALAR";
93     case VAR_ARRAY:
94         return "ARRAY";
95     case VAR_HASH:
96         return "HASH";
97     case VAR_CODE:
98         return "CODE";
99     case VAR_IO:
100         return "IO";
101     default:
102         return "unknown";
103     }
104 }
105
106 I32 vartype_to_svtype(vartype_t type)
107 {
108     switch (type) {
109     case VAR_SCALAR:
110         return SVt_PV; /* or whatever */
111     case VAR_ARRAY:
112         return SVt_PVAV;
113     case VAR_HASH:
114         return SVt_PVHV;
115     case VAR_CODE:
116         return SVt_PVCV;
117     case VAR_IO:
118         return SVt_PVIO;
119     default:
120         return SVt_NULL;
121     }
122 }
123
124 vartype_t string_to_vartype(char *vartype)
125 {
126     if (strEQ(vartype, "SCALAR")) {
127         return VAR_SCALAR;
128     }
129     else if (strEQ(vartype, "ARRAY")) {
130         return VAR_ARRAY;
131     }
132     else if (strEQ(vartype, "HASH")) {
133         return VAR_HASH;
134     }
135     else if (strEQ(vartype, "CODE")) {
136         return VAR_CODE;
137     }
138     else if (strEQ(vartype, "IO")) {
139         return VAR_IO;
140     }
141     else {
142         croak("Type must be one of 'SCALAR', 'ARRAY', 'HASH', 'CODE', or 'IO'");
143     }
144 }
145
146 void _deconstruct_variable_name(char *variable, varspec_t *varspec)
147 {
148     if (!variable || !variable[0])
149         croak("You must pass a variable name");
150
151     varspec->type = VAR_NONE;
152
153     switch (variable[0]) {
154     case '$':
155         varspec->type = VAR_SCALAR;
156         break;
157     case '@':
158         varspec->type = VAR_ARRAY;
159         break;
160     case '%':
161         varspec->type = VAR_HASH;
162         break;
163     case '&':
164         varspec->type = VAR_CODE;
165         break;
166     }
167
168     if (varspec->type != VAR_NONE) {
169         varspec->sigil = variable[0];
170         varspec->name = &variable[1];
171     }
172     else {
173         varspec->type = VAR_IO;
174         varspec->sigil = '\0';
175         varspec->name = variable;
176     }
177 }
178
179 void _deconstruct_variable_hash(HV *variable, varspec_t *varspec)
180 {
181     SV **val;
182
183     val = hv_fetch(variable, "name", 4, 0);
184     if (!val)
185         croak("The 'name' key is required in variable specs");
186
187     varspec->name = savesvpv(*val);
188     SAVEFREEPV(varspec->name);
189
190     val = hv_fetch(variable, "sigil", 5, 0);
191     if (!val)
192         croak("The 'sigil' key is required in variable specs");
193
194     varspec->sigil = (SvPV_nolen(*val))[0];
195
196     val = hv_fetch(variable, "type", 4, 0);
197     if (!val)
198         croak("The 'type' key is required in variable specs");
199
200     varspec->type = string_to_vartype(SvPV_nolen(*val));
201 }
202
203 int _valid_for_type(SV *value, vartype_t type)
204 {
205     svtype sv_type = SvROK(value) ? SvTYPE(SvRV(value)) : SVt_NULL;
206
207     switch (type) {
208     case VAR_SCALAR:
209         return sv_type == SVt_NULL ||
210                sv_type == SVt_IV   ||
211                sv_type == SVt_NV   ||
212                sv_type == SVt_PV   ||
213                sv_type == SVt_RV;
214     case VAR_ARRAY:
215         return sv_type == SVt_PVAV;
216     case VAR_HASH:
217         return sv_type == SVt_PVHV;
218     case VAR_CODE:
219         return sv_type == SVt_PVCV;
220     case VAR_IO:
221         return sv_type == SVt_PVIO;
222     default:
223         return 0;
224     }
225 }
226
227 HV *_get_namespace(SV *self)
228 {
229     dSP;
230     SV *ret;
231
232     PUSHMARK(SP);
233     XPUSHs(self);
234     PUTBACK;
235
236     call_method("namespace", G_SCALAR);
237
238     SPAGAIN;
239     ret = POPs;
240     PUTBACK;
241
242     return (HV*)SvRV(ret);
243 }
244
245 SV *_get_name(SV *self)
246 {
247     dSP;
248     SV *ret;
249
250     PUSHMARK(SP);
251     XPUSHs(self);
252     PUTBACK;
253
254     call_method("name", G_SCALAR);
255
256     SPAGAIN;
257     ret = POPs;
258     PUTBACK;
259
260     return ret;
261 }
262
263 SV *_get_symbol(SV *self, varspec_t *variable, int vivify)
264 {
265     HV *namespace;
266     SV **entry;
267     GV *glob;
268
269     namespace = _get_namespace(self);
270     entry = hv_fetch(namespace, variable->name, strlen(variable->name), vivify);
271     if (!entry)
272         return NULL;
273
274     glob = (GV*)(*entry);
275     if (!isGV(glob)) {
276         SV *namesv;
277
278         namesv = newSVsv(_get_name(self));
279         sv_catpvs(namesv, "::");
280         sv_catpv(namesv, variable->name);
281
282         /* can't use gv_init here, because it screws up @ISA in a way that I
283          * can't reproduce, but that CMOP triggers */
284         gv_fetchsv(namesv, GV_ADD, vartype_to_svtype(variable->type));
285         SvREFCNT_dec(namesv);
286     }
287
288     if (vivify) {
289         switch (variable->type) {
290         case VAR_SCALAR:
291             if (!GvSVOK(glob))
292                 GvSetSV(glob, newSV(0));
293             break;
294         case VAR_ARRAY:
295             if (!GvAVOK(glob))
296                 GvSetAV(glob, newAV());
297             break;
298         case VAR_HASH:
299             if (!GvHVOK(glob))
300                 GvSetHV(glob, newHV());
301             break;
302         case VAR_CODE:
303             croak("Don't know how to vivify CODE variables");
304         case VAR_IO:
305             if (!GvIOOK(glob))
306                 GvSetIO(glob, newIO());
307             break;
308         default:
309             croak("Unknown type in vivication");
310         }
311     }
312
313     switch (variable->type) {
314     case VAR_SCALAR:
315         return GvSV(glob);
316     case VAR_ARRAY:
317         return (SV*)GvAV(glob);
318     case VAR_HASH:
319         return (SV*)GvHV(glob);
320     case VAR_CODE:
321         return (SV*)GvCV(glob);
322     case VAR_IO:
323         return (SV*)GvIO(glob);
324     default:
325         return NULL;
326     }
327 }
328
329 MODULE = Package::Stash  PACKAGE = Package::Stash
330
331 PROTOTYPES: DISABLE
332
333 SV*
334 new(class, package_name)
335     char *class
336     SV *package_name
337   PREINIT:
338     HV *instance;
339     HV *namespace;
340     SV *nsref;
341   CODE:
342     if (!SvPOK(package_name))
343         croak("The constructor argument must be the name of a package");
344
345     instance = newHV();
346
347     if (!hv_store(instance, "name", 4, SvREFCNT_inc_simple_NN(package_name), 0)) {
348         SvREFCNT_dec(package_name);
349         SvREFCNT_dec(instance);
350         croak("Couldn't initialize the 'name' key, hv_store failed");
351     }
352     namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
353     nsref = newRV_inc((SV*)namespace);
354     if (!hv_store(instance, "namespace", 9, nsref, 0)) {
355         SvREFCNT_dec(nsref);
356         SvREFCNT_dec(instance);
357         croak("Couldn't initialize the 'namespace' key, hv_store failed");
358     }
359
360     RETVAL = sv_bless(newRV_noinc((SV*)instance), gv_stashpv(class, 0));
361   OUTPUT:
362     RETVAL
363
364 SV*
365 name(self)
366     SV *self
367   PREINIT:
368     SV **slot;
369   CODE:
370     if (!sv_isobject(self))
371         croak("Can't call name as a class method");
372     slot = hv_fetch((HV*)SvRV(self), "name", 4, 0);
373     RETVAL = slot ? SvREFCNT_inc_simple_NN(*slot) : &PL_sv_undef;
374   OUTPUT:
375     RETVAL
376
377 SV*
378 namespace(self)
379     SV *self
380   PREINIT:
381     SV **slot;
382   CODE:
383     if (!sv_isobject(self))
384         croak("Can't call namespace as a class method");
385     slot = hv_fetch((HV*)SvRV(self), "namespace", 9, 0);
386     RETVAL = slot ? SvREFCNT_inc_simple_NN(*slot) : &PL_sv_undef;
387   OUTPUT:
388     RETVAL
389
390 void
391 add_symbol(self, variable, initial=NULL, ...)
392     SV *self
393     varspec_t variable
394     SV *initial
395   PREINIT:
396     SV *name;
397     GV *glob;
398   CODE:
399     if (initial && !_valid_for_type(initial, variable.type))
400         croak("%s is not of type %s",
401               SvPV_nolen(initial), vartype_to_string(variable.type));
402
403     name = newSVsv(_get_name(self));
404     sv_catpvs(name, "::");
405     sv_catpv(name, variable.name);
406
407     /* XXX: come back to this when i feel like reimplementing caller() */
408 /*
409     my $filename = $opts{filename};
410     my $first_line_num = $opts{first_line_num};
411
412     (undef, $filename, $first_line_num) = caller
413         if not defined $filename;
414
415     my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
416
417     # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
418     $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
419 */
420 /*
421     if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
422         int i;
423         char *filename = NULL, *name;
424         I32 first_line_num, last_line_num;
425
426         if ((items - 3) % 2)
427             croak("add_symbol: Odd number of elements in %%opts");
428
429         for (i = 3; i < items; i += 2) {
430             char *key;
431             key = SvPV_nolen(ST(i));
432             if (strEQ(key, "filename")) {
433                 if (!SvPOK(ST(i + 1)))
434                     croak("add_symbol: filename must be a string");
435                 filename = SvPV_nolen(ST(i + 1));
436             }
437             else if (strEQ(key, "first_line_num")) {
438                 if (!SvIOK(ST(i + 1)))
439                     croak("add_symbol: first_line_num must be an integer");
440                 first_line_num = SvIV(ST(i + 1));
441             }
442             else if (strEQ(key, "last_line_num")) {
443                 if (!SvIOK(ST(i + 1)))
444                     croak("add_symbol: last_line_num must be an integer");
445                 last_line_num = SvIV(ST(i + 1));
446             }
447         }
448
449         if (!filename) {
450         }
451     }
452 */
453
454     glob = gv_fetchsv(name, GV_ADDMULTI, vartype_to_svtype(variable.type));
455
456     if (initial) {
457         SV *val;
458
459         if (SvROK(initial)) {
460             val = SvRV(initial);
461             SvREFCNT_inc_simple_void_NN(val);
462         }
463         else {
464             val = newSVsv(initial);
465         }
466
467         switch (variable.type) {
468         case VAR_SCALAR:
469             GvSetSV(glob, val);
470             break;
471         case VAR_ARRAY:
472             GvSetAV(glob, val);
473             break;
474         case VAR_HASH:
475             GvSetHV(glob, val);
476             break;
477         case VAR_CODE:
478             GvSetCV(glob, val);
479             break;
480         case VAR_IO:
481             GvSetIO(glob, val);
482             break;
483         }
484     }
485
486     SvREFCNT_dec(name);
487
488 void
489 remove_glob(self, name)
490     SV *self
491     char *name
492   CODE:
493     hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
494
495 int
496 has_symbol(self, variable)
497     SV *self
498     varspec_t variable
499   PREINIT:
500     HV *namespace;
501     SV **entry;
502   CODE:
503     namespace = _get_namespace(self);
504     entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
505     if (!entry)
506         XSRETURN_UNDEF;
507
508     if (isGV(*entry)) {
509         GV *glob = (GV*)(*entry);
510         switch (variable.type) {
511         case VAR_SCALAR:
512             RETVAL = GvSVOK(glob) ? 1 : 0;
513             break;
514         case VAR_ARRAY:
515             RETVAL = GvAVOK(glob) ? 1 : 0;
516             break;
517         case VAR_HASH:
518             RETVAL = GvHVOK(glob) ? 1 : 0;
519             break;
520         case VAR_CODE:
521             RETVAL = GvCVOK(glob) ? 1 : 0;
522             break;
523         case VAR_IO:
524             RETVAL = GvIOOK(glob) ? 1 : 0;
525             break;
526         }
527     }
528     else {
529         RETVAL = (variable.type == VAR_CODE);
530     }
531   OUTPUT:
532     RETVAL
533
534 SV*
535 get_symbol(self, variable)
536     SV *self
537     varspec_t variable
538   PREINIT:
539     SV *val;
540   CODE:
541     val = _get_symbol(self, &variable, 0);
542     if (!val)
543         XSRETURN_UNDEF;
544     RETVAL = newRV_inc(val);
545   OUTPUT:
546     RETVAL
547
548 SV*
549 get_or_add_symbol(self, variable)
550     SV *self
551     varspec_t variable
552   PREINIT:
553     SV *val;
554   CODE:
555     val = _get_symbol(self, &variable, 1);
556     if (!val)
557         XSRETURN_UNDEF;
558     RETVAL = newRV_inc(val);
559   OUTPUT:
560     RETVAL
561
562 void
563 remove_symbol(self, variable)
564     SV *self
565     varspec_t variable
566   PREINIT:
567     HV *namespace;
568     SV **entry;
569   CODE:
570     namespace = _get_namespace(self);
571     entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
572     if (!entry)
573         XSRETURN_EMPTY;
574
575     if (isGV(*entry)) {
576         GV *glob = (GV*)(*entry);
577         switch (variable.type) {
578         case VAR_SCALAR:
579             GvSetSV(glob, NULL);
580             break;
581         case VAR_ARRAY:
582             GvSetAV(glob, NULL);
583             break;
584         case VAR_HASH:
585             GvSetHV(glob, NULL);
586             break;
587         case VAR_CODE:
588             GvSetCV(glob, NULL);
589             break;
590         case VAR_IO:
591             GvSetIO(glob, NULL);
592             break;
593         }
594     }
595     else {
596         if (variable.type == VAR_CODE) {
597             hv_delete(namespace, variable.name, strlen(variable.name), G_DISCARD);
598         }
599     }
600
601 void
602 list_all_symbols(self, vartype=VAR_NONE)
603     SV *self
604     vartype_t vartype
605   PPCODE:
606     if (vartype == VAR_NONE) {
607         HV *namespace;
608         HE *entry;
609         int keys;
610
611         namespace = _get_namespace(self);
612         keys = hv_iterinit(namespace);
613         EXTEND(SP, keys);
614         while ((entry = hv_iternext(namespace))) {
615             mPUSHs(newSVhe(entry));
616         }
617     }
618     else {
619         HV *namespace;
620         SV *val;
621         char *key;
622         int len;
623
624         namespace = _get_namespace(self);
625         hv_iterinit(namespace);
626         while ((val = hv_iternextsv(namespace, &key, &len))) {
627             GV *gv = (GV*)val;
628             if (isGV(gv)) {
629                 switch (vartype) {
630                 case VAR_SCALAR:
631                     if (GvSVOK(val))
632                         mXPUSHp(key, len);
633                     break;
634                 case VAR_ARRAY:
635                     if (GvAVOK(val))
636                         mXPUSHp(key, len);
637                     break;
638                 case VAR_HASH:
639                     if (GvHVOK(val))
640                         mXPUSHp(key, len);
641                     break;
642                 case VAR_CODE:
643                     if (GvCVOK(val))
644                         mXPUSHp(key, len);
645                     break;
646                 case VAR_IO:
647                     if (GvIOOK(val))
648                         mXPUSHp(key, len);
649                     break;
650                 }
651             }
652             else if (vartype == VAR_CODE) {
653                 mXPUSHp(key, len);
654             }
655         }
656     }