expand constant stash entries on get
[gitmo/Package-Stash-XS.git] / Stash.xs
CommitLineData
59017825 1#include "EXTERN.h"
2#include "perl.h"
3#include "XSUB.h"
4
a382a84b 5typedef enum {
6 VAR_NONE = 0,
7 VAR_SCALAR,
8 VAR_ARRAY,
9 VAR_HASH,
10 VAR_CODE,
11 VAR_IO,
12 VAR_GLOB, /* TODO: unimplemented */
13 VAR_FORMAT /* TODO: unimplemented */
14} vartype_t;
15
16typedef struct {
17 vartype_t type;
18 char sigil;
19 char *name;
20} varspec_t;
21
76f7c306 22const char *vartype_to_string(vartype_t type)
23{
24 switch (type) {
25 case VAR_SCALAR:
26 return "SCALAR";
27 case VAR_ARRAY:
28 return "ARRAY";
29 case VAR_HASH:
30 return "HASH";
31 case VAR_CODE:
32 return "CODE";
33 case VAR_IO:
34 return "IO";
35 default:
36 return "unknown";
37 }
38}
39
40I32 vartype_to_svtype(vartype_t type)
41{
42 switch (type) {
43 case VAR_SCALAR:
44 return SVt_PV; /* or whatever */
45 case VAR_ARRAY:
46 return SVt_PVAV;
47 case VAR_HASH:
48 return SVt_PVHV;
49 case VAR_CODE:
50 return SVt_PVCV;
51 case VAR_IO:
52 return SVt_PVIO;
53 default:
54 return SVt_NULL;
55 }
56}
57
a382a84b 58vartype_t string_to_vartype(char *vartype)
59{
60 if (strEQ(vartype, "SCALAR")) {
61 return VAR_SCALAR;
62 }
63 else if (strEQ(vartype, "ARRAY")) {
64 return VAR_ARRAY;
65 }
66 else if (strEQ(vartype, "HASH")) {
67 return VAR_HASH;
68 }
69 else if (strEQ(vartype, "CODE")) {
70 return VAR_CODE;
71 }
72 else if (strEQ(vartype, "IO")) {
73 return VAR_IO;
74 }
75 else {
76 croak("Type must be one of 'SCALAR', 'ARRAY', 'HASH', 'CODE', or 'IO'");
77 }
78}
79
80void _deconstruct_variable_name(char *variable, varspec_t *varspec)
81{
82 if (!variable || !variable[0])
83 croak("You must pass a variable name");
84
85 varspec->type = VAR_NONE;
86
87 switch (variable[0]) {
88 case '$':
89 varspec->type = VAR_SCALAR;
90 break;
91 case '@':
92 varspec->type = VAR_ARRAY;
93 break;
94 case '%':
95 varspec->type = VAR_HASH;
96 break;
97 case '&':
98 varspec->type = VAR_CODE;
99 break;
100 }
101
102 if (varspec->type != VAR_NONE) {
103 varspec->sigil = variable[0];
104 varspec->name = &variable[1];
105 }
106 else {
107 varspec->type = VAR_IO;
108 varspec->sigil = '\0';
109 varspec->name = variable;
110 }
111}
112
113void _deconstruct_variable_hash(HV *variable, varspec_t *varspec)
114{
115 SV **val;
116 char *type;
117
118 val = hv_fetch(variable, "name", 4, 0);
119 if (!val)
120 croak("The 'name' key is required in variable specs");
121
122 varspec->name = savesvpv(*val);
123
124 val = hv_fetch(variable, "sigil", 5, 0);
125 if (!val)
126 croak("The 'sigil' key is required in variable specs");
127
128 varspec->sigil = (SvPV_nolen(*val))[0];
129
130 val = hv_fetch(variable, "type", 4, 0);
131 if (!val)
132 croak("The 'type' key is required in variable specs");
133
134 varspec->type = string_to_vartype(SvPV_nolen(*val));
135}
136
137int _valid_for_type(SV *value, vartype_t type)
138{
139 svtype sv_type = SvROK(value) ? SvTYPE(SvRV(value)) : SVt_NULL;
140
141 switch (type) {
142 case VAR_SCALAR:
143 return sv_type == SVt_NULL ||
144 sv_type == SVt_IV ||
145 sv_type == SVt_NV ||
146 sv_type == SVt_PV ||
147 sv_type == SVt_RV;
148 case VAR_ARRAY:
149 return sv_type == SVt_PVAV;
150 case VAR_HASH:
151 return sv_type == SVt_PVHV;
152 case VAR_CODE:
153 return sv_type == SVt_PVCV;
154 case VAR_IO:
76f7c306 155 return sv_type == SVt_PVIO;
a382a84b 156 default:
157 return 0;
158 }
159}
160
3fd56b4d 161HV *_get_namespace(SV *self)
162{
163 dSP;
164 SV *ret;
165
166 PUSHMARK(SP);
167 XPUSHs(self);
168 PUTBACK;
169
170 call_method("namespace", G_SCALAR);
171
172 SPAGAIN;
173 ret = POPs;
174 PUTBACK;
175
176 return (HV*)SvRV(ret);
177}
178
76f7c306 179SV *_get_name(SV *self)
180{
181 dSP;
182 SV *ret;
183
184 PUSHMARK(SP);
185 XPUSHs(self);
186 PUTBACK;
187
188 call_method("name", G_SCALAR);
189
190 SPAGAIN;
191 ret = POPs;
192 PUTBACK;
193
194 return ret;
195}
196
59017825 197MODULE = Package::Stash PACKAGE = Package::Stash
198
be2a7e99 199PROTOTYPES: DISABLE
200
59017825 201SV*
202new(class, package_name)
203 char *class
204 SV *package_name
3496f1e8 205 PREINIT:
59017825 206 HV *instance;
207 HV *namespace;
208 CODE:
209 if (!SvPOK(package_name))
210 croak("The constructor argument must be the name of a package");
211
212 instance = newHV();
213
214 hv_store(instance, "name", 4, package_name, 0);
215 namespace = gv_stashpv(SvPV_nolen(package_name), GV_ADD);
216 hv_store(instance, "namespace", 9, newRV((SV*)namespace), 0);
217
218 RETVAL = sv_bless(newRV((SV*)instance), gv_stashpv(class, 0));
219 OUTPUT:
220 RETVAL
194acf47 221
222SV*
223name(self)
224 SV *self
3496f1e8 225 PREINIT:
194acf47 226 SV **slot;
227 CODE:
228 if (!sv_isobject(self))
229 croak("Can't call name as a class method");
230 slot = hv_fetch((HV*)SvRV(self), "name", 4, 0);
231 RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
232 OUTPUT:
233 RETVAL
234
235SV*
236namespace(self)
237 SV *self
3496f1e8 238 PREINIT:
194acf47 239 SV **slot;
240 CODE:
241 if (!sv_isobject(self))
242 croak("Can't call namespace as a class method");
243 slot = hv_fetch((HV*)SvRV(self), "namespace", 9, 0);
244 RETVAL = slot ? SvREFCNT_inc(*slot) : &PL_sv_undef;
245 OUTPUT:
246 RETVAL
3fd56b4d 247
248void
76f7c306 249add_package_symbol(self, variable, initial=NULL, ...)
250 SV *self
251 varspec_t variable
252 SV *initial
253 PREINIT:
254 SV *name;
255 GV *glob;
256 CODE:
257 if (initial && !_valid_for_type(initial, variable.type))
258 croak("%s is not of type %s",
259 SvPV_nolen(initial), vartype_to_string(variable.type));
260
261 name = newSVsv(_get_name(self));
262 sv_catpvs(name, "::");
263 sv_catpv(name, variable.name);
264
265 /* XXX: come back to this when i feel like reimplementing caller() */
266/*
267 my $filename = $opts{filename};
268 my $first_line_num = $opts{first_line_num};
269
270 (undef, $filename, $first_line_num) = caller
271 if not defined $filename;
272
273 my $last_line_num = $opts{last_line_num} || ($first_line_num ||= 0);
274
275 # http://perldoc.perl.org/perldebguts.html#Debugger-Internals
276 $DB::sub{$pkg . '::' . $name} = "$filename:$first_line_num-$last_line_num";
277*/
278/*
279 if (items > 2 && (PL_perldb & 0x10) && variable.type == VAR_CODE) {
280 int i;
281 char *filename = NULL, *name;
282 I32 first_line_num, last_line_num;
283
284 if ((items - 3) % 2)
285 croak("add_package_symbol: Odd number of elements in %%opts");
286
287 for (i = 3; i < items; i += 2) {
288 char *key;
289 key = SvPV_nolen(ST(i));
290 if (strEQ(key, "filename")) {
291 if (!SvPOK(ST(i + 1)))
292 croak("add_package_symbol: filename must be a string");
293 filename = SvPV_nolen(ST(i + 1));
294 }
295 else if (strEQ(key, "first_line_num")) {
296 if (!SvIOK(ST(i + 1)))
297 croak("add_package_symbol: first_line_num must be an integer");
298 first_line_num = SvIV(ST(i + 1));
299 }
300 else if (strEQ(key, "last_line_num")) {
301 if (!SvIOK(ST(i + 1)))
302 croak("add_package_symbol: last_line_num must be an integer");
303 last_line_num = SvIV(ST(i + 1));
304 }
305 }
306
307 if (!filename) {
308 }
309 }
310*/
311
312 glob = gv_fetchsv(name, GV_ADD, vartype_to_svtype(variable.type));
313
314 if (initial) {
315 SV *val;
316
317 if (SvROK(initial)) {
318 val = SvRV(initial);
319 SvREFCNT_inc(val);
320 }
321 else {
322 val = newSVsv(initial);
323 }
324
325 switch (variable.type) {
326 case VAR_SCALAR:
327 GvSV(glob) = val;
328 break;
329 case VAR_ARRAY:
330 GvAV(glob) = (AV*)val;
331 break;
332 case VAR_HASH:
333 GvHV(glob) = (HV*)val;
334 break;
335 case VAR_CODE:
336 GvCV(glob) = (CV*)val;
337 break;
338 case VAR_IO:
339 GvIOp(glob) = (IO*)val;
340 break;
341 }
342 }
343
344void
3fd56b4d 345remove_package_glob(self, name)
346 SV *self
347 char *name
3fd56b4d 348 CODE:
349 hv_delete(_get_namespace(self), name, strlen(name), G_DISCARD);
9b608466 350
34805376 351int
352has_package_symbol(self, variable)
353 SV *self
354 varspec_t variable
355 PREINIT:
356 HV *namespace;
357 SV **entry;
358 CODE:
359 namespace = _get_namespace(self);
360 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
361 if (!entry)
362 XSRETURN_UNDEF;
363
364 if (isGV(*entry)) {
365 GV *glob = (GV*)(*entry);
366 switch (variable.type) {
367 case VAR_SCALAR:
368 RETVAL = GvSV(glob) ? 1 : 0;
369 break;
370 case VAR_ARRAY:
371 RETVAL = GvAV(glob) ? 1 : 0;
372 break;
373 case VAR_HASH:
374 RETVAL = GvHV(glob) ? 1 : 0;
375 break;
376 case VAR_CODE:
377 RETVAL = GvCV(glob) ? 1 : 0;
378 break;
379 case VAR_IO:
380 RETVAL = GvIO(glob) ? 1 : 0;
381 break;
382 }
383 }
384 else {
385 RETVAL = (variable.type == VAR_CODE);
386 }
387 OUTPUT:
388 RETVAL
389
e3ad44fd 390SV*
391get_package_symbol(self, variable, ...)
392 SV *self
393 varspec_t variable
394 PREINIT:
395 HV *namespace;
396 SV **entry;
cb4df463 397 GV *glob;
e3ad44fd 398 CODE:
399 namespace = _get_namespace(self);
400
401 if (!hv_exists(namespace, variable.name, strlen(variable.name))) {
402 int i, vivify = 0;
403 if ((items - 2) % 2)
404 croak("get_package_symbol: Odd number of elements in %%opts");
405
406 for (i = 2; i < items; i += 2) {
407 char *key;
408 key = SvPV_nolen(ST(i));
409 if (strEQ(key, "vivify")) {
410 vivify = SvTRUE(ST(i + 1));
411 }
412 }
413
414 if (vivify) {
415 /* XXX: vivify */
416 }
417 }
418
419 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
420 if (!entry)
421 XSRETURN_UNDEF;
422
cb4df463 423 glob = (GV*)(*entry);
424
425 if (!isGV(*entry)) {
426 SV *namesv;
427 char *name;
428 int len;
429
430 namesv = newSVsv(_get_name(self));
431 sv_catpvs(namesv, "::");
432 sv_catpv(namesv, variable.name);
433
434 name = SvPV(namesv, len);
435
436 gv_init(glob, namespace, name, len, 1);
e3ad44fd 437 }
cb4df463 438
439 switch (variable.type) {
440 case VAR_SCALAR:
441 RETVAL = newRV(GvSV(glob));
442 break;
443 case VAR_ARRAY:
444 RETVAL = newRV((SV*)GvAV(glob));
445 break;
446 case VAR_HASH:
447 RETVAL = newRV((SV*)GvHV(glob));
448 break;
449 case VAR_CODE:
450 RETVAL = newRV((SV*)GvCV(glob));
451 break;
452 case VAR_IO:
453 RETVAL = newRV((SV*)GvIO(glob));
454 break;
e3ad44fd 455 }
456 OUTPUT:
457 RETVAL
458
9b608466 459void
215f49f8 460remove_package_symbol(self, variable)
461 SV *self
462 varspec_t variable
463 PREINIT:
464 HV *namespace;
465 SV **entry;
466 CODE:
467 namespace = _get_namespace(self);
468 entry = hv_fetch(namespace, variable.name, strlen(variable.name), 0);
469 if (!entry)
470 XSRETURN_EMPTY;
471
472 if (isGV(*entry)) {
473 GV *glob = (GV*)(*entry);
474 switch (variable.type) {
475 case VAR_SCALAR:
476 GvSV(glob) = Nullsv;
477 break;
478 case VAR_ARRAY:
479 GvAV(glob) = Nullav;
480 break;
481 case VAR_HASH:
482 GvHV(glob) = Nullhv;
483 break;
484 case VAR_CODE:
485 GvCV(glob) = Nullcv;
486 break;
487 case VAR_IO:
76f7c306 488 GvIOp(glob) = Null(IO*);
215f49f8 489 break;
490 }
491 }
492 else {
493 if (variable.type == VAR_CODE) {
494 hv_delete(namespace, variable.name, strlen(variable.name), G_DISCARD);
495 }
496 }
497
498void
9b608466 499list_all_package_symbols(self, vartype=VAR_NONE)
500 SV *self
501 vartype_t vartype
502 PPCODE:
503 if (vartype == VAR_NONE) {
504 HV *namespace;
505 HE *entry;
506 int keys;
507
508 namespace = _get_namespace(self);
509 keys = hv_iterinit(namespace);
510 EXTEND(SP, keys);
511 while (entry = hv_iternext(namespace)) {
512 mPUSHs(newSVhek(HeKEY_hek(entry)));
513 }
514 }
515 else {
516 HV *namespace;
517 HE *entry;
518 SV *val;
519 char *key;
520 int len;
521
522 namespace = _get_namespace(self);
523 hv_iterinit(namespace);
524 while (val = hv_iternextsv(namespace, &key, &len)) {
525 GV *gv = (GV*)val;
526 if (isGV(gv)) {
527 switch (vartype) {
528 case VAR_SCALAR:
529 if (GvSV(val))
530 mXPUSHp(key, len);
531 break;
532 case VAR_ARRAY:
533 if (GvAV(val))
534 mXPUSHp(key, len);
535 break;
536 case VAR_HASH:
537 if (GvHV(val))
538 mXPUSHp(key, len);
539 break;
540 case VAR_CODE:
541 if (GvCVu(val))
542 mXPUSHp(key, len);
543 break;
544 case VAR_IO:
545 if (GvIO(val))
546 mXPUSHp(key, len);
547 break;
548 }
549 }
550 else if (vartype == VAR_CODE) {
551 mXPUSHp(key, len);
552 }
553 }
554 }