gv_efullname3() could return NULL, so mro::_nextcan() must cope (and croak()).
[gitmo/Class-C3-XS.git] / XS.xs
CommitLineData
8995e827 1
2#include "EXTERN.h"
3#include "perl.h"
4#include "XSUB.h"
8995e827 5
25dc7e96 6/* *********** ppport stuff */
7
8#ifndef PERL_UNUSED_VAR
9# define PERL_UNUSED_VAR(x) ((void)x)
10#endif
11
12#if defined(PERL_GCC_PEDANTIC)
13# ifndef PERL_GCC_BRACE_GROUPS_FORBIDDEN
14# define PERL_GCC_BRACE_GROUPS_FORBIDDEN
15# endif
16#endif
17
18#if defined(__GNUC__) && !defined(PERL_GCC_BRACE_GROUPS_FORBIDDEN) && !defined(__cplusplus)
19# ifndef PERL_USE_GCC_BRACE_GROUPS
20# define PERL_USE_GCC_BRACE_GROUPS
21# endif
22#endif
23
24#ifndef SvREFCNT_inc
25# ifdef PERL_USE_GCC_BRACE_GROUPS
6ed110b7 26# define SvREFCNT_inc(sv) \
27 ({ \
28 SV * const _sv = (SV*)(sv); \
29 if (_sv) \
30 (SvREFCNT(_sv))++; \
31 _sv; \
25dc7e96 32 })
33# else
6ed110b7 34# define SvREFCNT_inc(sv) \
25dc7e96 35 ((PL_Sv=(SV*)(sv)) ? (++(SvREFCNT(PL_Sv)),PL_Sv) : NULL)
36# endif
37#endif
38
39#ifndef dAX
40# define dAX I32 ax = MARK - PL_stack_base + 1
41#endif
42
43#ifndef dVAR
44# define dVAR dNOOP
45#endif
46
47#ifndef packWARN
48# define packWARN(a) (a)
49#endif
50
51/* *********** end ppport.h stuff */
52
68fe9f29 53#ifndef SVfARG
54# define SVfARG(p) ((void*)(p))
55#endif
56
29e61e10 57/* Most of this code is backported from the bleadperl patch's
b23e9cb9 58 mro.c, and then modified to work with Class::C3's
59 internals.
60*/
8995e827 61
b23e9cb9 62AV*
63__mro_linear_isa_c3(pTHX_ HV* stash, HV* cache, I32 level)
64{
65 AV* retval;
66 GV** gvp;
67 GV* gv;
68 AV* isa;
69 const char* stashname;
70 STRLEN stashname_len;
bb82f0c1 71 I32 made_mortal_cache = 0;
8995e827 72
b23e9cb9 73 assert(stash);
8995e827 74
b23e9cb9 75 stashname = HvNAME(stash);
76 stashname_len = strlen(stashname);
77 if (!stashname)
78 Perl_croak(aTHX_
79 "Can't linearize anonymous symbol table");
8995e827 80
b23e9cb9 81 if (level > 100)
82 Perl_croak(aTHX_ "Recursive inheritance detected in package '%s'",
83 stashname);
8995e827 84
b23e9cb9 85 if(!cache) {
f0294f1b 86 cache = (HV*)sv_2mortal((SV*)newHV());
bb82f0c1 87 made_mortal_cache = 1;
b23e9cb9 88 }
89 else {
6bf46d18 90 SV** cache_entry = hv_fetch(cache, stashname, stashname_len, 0);
91 if(cache_entry)
92 return (AV*)SvREFCNT_inc(*cache_entry);
b23e9cb9 93 }
94
95 /* not in cache, make a new one */
96
b23e9cb9 97 gvp = (GV**)hv_fetch(stash, "ISA", 3, FALSE);
98 isa = (gvp && (gv = *gvp) && gv != (GV*)&PL_sv_undef) ? GvAV(gv) : NULL;
b23e9cb9 99 if(isa && AvFILLp(isa) >= 0) {
100 SV** seqs_ptr;
101 I32 seqs_items;
139e8d8c 102 HV* tails;
bb82f0c1 103 AV* const seqs = (AV*)sv_2mortal((SV*)newAV());
104 I32* heads;
105
053556af 106 /* This builds @seqs, which is an array of arrays.
107 The members of @seqs are the MROs of
108 the members of @ISA, followed by @ISA itself.
109 */
b23e9cb9 110 I32 items = AvFILLp(isa) + 1;
111 SV** isa_ptr = AvARRAY(isa);
112 while(items--) {
bb82f0c1 113 SV* const isa_item = *isa_ptr++;
114 HV* const isa_item_stash = gv_stashsv(isa_item, 0);
b23e9cb9 115 if(!isa_item_stash) {
053556af 116 /* if no stash, make a temporary fake MRO
117 containing just itself */
bb82f0c1 118 AV* const isa_lin = newAV();
b23e9cb9 119 av_push(isa_lin, newSVsv(isa_item));
bb82f0c1 120 av_push(seqs, (SV*)isa_lin);
b23e9cb9 121 }
122 else {
053556af 123 /* recursion */
124 AV* const isa_lin = __mro_linear_isa_c3(aTHX_ isa_item_stash, cache, level + 1);
139e8d8c 125
126 if(items == 0 && AvFILLp(seqs) == -1) {
127 /* Only one parent class. For this case, the C3
128 linearisation is this class followed by the parent's
129 linearisation, so don't bother with the expensive
130 calculation. */
131 SV **svp;
132 I32 subrv_items = AvFILLp(isa_lin) + 1;
133 SV *const *subrv_p = AvARRAY(isa_lin);
134
135 /* Hijack the allocated but unused array seqs to be the
136 return value. It's currently mortalised. */
137
138 retval = seqs;
139
140 av_extend(retval, subrv_items);
141 AvFILLp(retval) = subrv_items;
142 svp = AvARRAY(retval);
143
144 /* First entry is this class. */
145 *svp++ = newSVpvn(stashname, stashname_len);
146
147 while(subrv_items--) {
148 /* These values are unlikely to be shared hash key
149 scalars, so no point in adding code to optimising
150 for a case that is unlikely to be true.
151 (Or prove me wrong and do it.) */
152
153 SV *const val = *subrv_p++;
154 *svp++ = newSVsv(val);
155 }
156
157 SvREFCNT_dec(isa_lin);
158 SvREFCNT_inc(retval);
159
160 goto done;
161 }
bb82f0c1 162 av_push(seqs, (SV*)isa_lin);
b23e9cb9 163 }
b23e9cb9 164 }
bb82f0c1 165 av_push(seqs, SvREFCNT_inc((SV*)isa));
139e8d8c 166 tails = (HV*)sv_2mortal((SV*)newHV());
bb82f0c1 167
168 /* This builds "heads", which as an array of integer array
169 indices, one per seq, which point at the virtual "head"
170 of the seq (initially zero) */
bb82f0c1 171 Newz(0xdead, heads, AvFILLp(seqs)+1, I32);
b23e9cb9 172
bb82f0c1 173 /* This builds %tails, which has one key for every class
174 mentioned in the tail of any sequence in @seqs (tail meaning
175 everything after the first class, the "head"). The value
176 is how many times this key appears in the tails of @seqs.
177 */
b23e9cb9 178 seqs_ptr = AvARRAY(seqs);
179 seqs_items = AvFILLp(seqs) + 1;
180 while(seqs_items--) {
bb82f0c1 181 AV* const seq = (AV*)*seqs_ptr++;
b23e9cb9 182 I32 seq_items = AvFILLp(seq);
183 if(seq_items > 0) {
184 SV** seq_ptr = AvARRAY(seq) + 1;
185 while(seq_items--) {
bb82f0c1 186 SV* const seqitem = *seq_ptr++;
3277bbc4 187 /* LVALUE fetch will create a new undefined SV if necessary
188 */
189 HE* const he = hv_fetch_ent(tails, seqitem, 1, 0);
190 if(he) {
bb82f0c1 191 SV* const val = HeVAL(he);
3277bbc4 192 /* This will increment undef to 1, which is what we
193 want for a newly created entry. */
b23e9cb9 194 sv_inc(val);
3277bbc4 195 } else {
196 croak("failed to store value in hash");
b23e9cb9 197 }
198 }
199 }
200 }
201
bb82f0c1 202 /* Initialize retval to build the return value in */
203 retval = newAV();
204 av_push(retval, newSVpvn(stashname, stashname_len)); /* us first */
205
053556af 206 /* This loop won't terminate until we either finish building
207 the MRO, or get an exception. */
b23e9cb9 208 while(1) {
b23e9cb9 209 SV* cand = NULL;
210 SV* winner = NULL;
bb82f0c1 211 int s;
212
053556af 213 /* "foreach $seq (@seqs)" */
bb82f0c1 214 SV** const avptr = AvARRAY(seqs);
215 for(s = 0; s <= AvFILLp(seqs); s++) {
b23e9cb9 216 SV** svp;
bb82f0c1 217 AV * const seq = (AV*)(avptr[s]);
218 SV* seqhead;
053556af 219 if(!seq) continue; /* skip empty seqs */
bb82f0c1 220 svp = av_fetch(seq, heads[s], 0);
053556af 221 seqhead = *svp; /* seqhead = head of this seq */
b23e9cb9 222 if(!winner) {
bb82f0c1 223 HE* tail_entry;
224 SV* val;
053556af 225 /* if we haven't found a winner for this round yet,
226 and this seqhead is not in tails (or the count
227 for it in tails has dropped to zero), then this
228 seqhead is our new winner, and is added to the
229 final MRO immediately */
b23e9cb9 230 cand = seqhead;
231 if((tail_entry = hv_fetch_ent(tails, cand, 0, 0))
232 && (val = HeVAL(tail_entry))
053556af 233 && (SvIVX(val) > 0))
b23e9cb9 234 continue;
235 winner = newSVsv(cand);
236 av_push(retval, winner);
053556af 237 /* note however that even when we find a winner,
238 we continue looping over @seqs to do housekeeping */
b23e9cb9 239 }
240 if(!sv_cmp(seqhead, winner)) {
053556af 241 /* Once we have a winner (including the iteration
242 where we first found him), inc the head ptr
243 for any seq which had the winner as a head,
244 NULL out any seq which is now empty,
245 and adjust tails for consistency */
246
bb82f0c1 247 const int new_head = ++heads[s];
248 if(new_head > AvFILLp(seq)) {
249 SvREFCNT_dec(avptr[s]);
250 avptr[s] = NULL;
251 }
252 else {
253 HE* tail_entry;
254 SV* val;
255 /* Because we know this new seqhead used to be
256 a tail, we can assume it is in tails and has
257 a positive value, which we need to dec */
258 svp = av_fetch(seq, new_head, 0);
259 seqhead = *svp;
260 tail_entry = hv_fetch_ent(tails, seqhead, 0, 0);
261 val = HeVAL(tail_entry);
262 sv_dec(val);
263 }
b23e9cb9 264 }
265 }
053556af 266
267 /* if we found no candidates, we are done building the MRO.
268 !cand means no seqs have any entries left to check */
bb82f0c1 269 if(!cand) {
270 Safefree(heads);
271 break;
272 }
053556af 273
274 /* If we had candidates, but nobody won, then the @ISA
275 hierarchy is not C3-incompatible */
62eb9d08 276 if(!winner) {
b6bdbb4c 277 SV *errmsg;
278 I32 i;
053556af 279 /* we have to do some cleanup before we croak */
280
b6bdbb4c 281 errmsg = newSVpvf("Inconsistent hierarchy during C3 merge of class '%s':\n\t"
282 "current merge results [\n", stashname);
283 for (i = 0; i <= av_len(retval); i++) {
284 SV **elem = av_fetch(retval, i, 0);
285 sv_catpvf(errmsg, "\t\t%"SVf",\n", SVfARG(*elem));
286 }
287 sv_catpvf(errmsg, "\t]\n\tmerging failed on '%"SVf"'", SVfARG(cand));
288
62eb9d08 289 SvREFCNT_dec(retval);
bb82f0c1 290 Safefree(heads);
053556af 291
b6bdbb4c 292 croak("%"SVf, SVfARG(errmsg));
62eb9d08 293 }
b23e9cb9 294 }
295 }
053556af 296 else { /* @ISA was undefined or empty */
297 /* build a retval containing only ourselves */
bb82f0c1 298 retval = newAV();
053556af 299 av_push(retval, newSVpvn(stashname, stashname_len));
bb82f0c1 300 }
b23e9cb9 301
139e8d8c 302done:
053556af 303 /* we don't want anyone modifying the cache entry but us,
304 and we do so by replacing it completely */
b23e9cb9 305 SvREADONLY_on(retval);
bb82f0c1 306
307 if(!made_mortal_cache) {
308 SvREFCNT_inc(retval);
ddc85d9f 309 if(!hv_store(cache, stashname, stashname_len, (SV*)retval, 0)) {
310 croak("failed to store value in hash");
311 }
bb82f0c1 312 }
313
314 return retval;
b23e9cb9 315}
316
317STATIC I32
318__dopoptosub_at(const PERL_CONTEXT *cxstk, I32 startingblock) {
319 I32 i;
320 for (i = startingblock; i >= 0; i--) {
321 if(CxTYPE((PERL_CONTEXT*)(&cxstk[i])) == CXt_SUB) return i;
322 }
323 return i;
324}
325
2605e591 326XS(XS_Class_C3_XS_nextcan);
327XS(XS_Class_C3_XS_nextcan)
b23e9cb9 328{
2605e591 329 dVAR; dXSARGS;
330
e5e121c4 331 SV* self = ST(0);
332 const I32 throw_nomethod = SvIVX(ST(1));
5efa8a5b 333 register I32 cxix = cxstack_ix;
b23e9cb9 334 register const PERL_CONTEXT *ccstack = cxstack;
335 const PERL_SI *top_si = PL_curstackinfo;
336 HV* selfstash;
337 GV* cvgv;
338 SV *stashname;
339 const char *fq_subname;
340 const char *subname;
341 STRLEN fq_subname_len;
342 STRLEN stashname_len;
343 STRLEN subname_len;
344 SV* sv;
345 GV** gvp;
346 AV* linear_av;
347 SV** linear_svp;
b23e9cb9 348 HV* cstash;
349 GV* candidate = NULL;
350 CV* cand_cv = NULL;
351 const char *hvname;
2605e591 352 I32 entries;
6c88cc6a 353 HV* nmcache;
b23e9cb9 354 HE* cache_entry;
22c6f594 355 SV* cachekey;
54d13ec6 356 I32 i;
b23e9cb9 357
2605e591 358 SP -= items;
359
b23e9cb9 360 if(sv_isobject(self))
361 selfstash = SvSTASH(SvRV(self));
362 else
363 selfstash = gv_stashsv(self, 0);
364
365 assert(selfstash);
366
367 hvname = HvNAME(selfstash);
368 if (!hvname)
625e16df 369 Perl_croak(aTHX_ "Can't use anonymous symbol table for method lookup");
b23e9cb9 370
b23e9cb9 371 /* This block finds the contextually-enclosing fully-qualified subname,
372 much like looking at (caller($i))[3] until you find a real sub that
5efa8a5b 373 isn't ANON, etc (also skips over pureperl next::method, etc) */
374 for(i = 0; i < 2; i++) {
375 cxix = __dopoptosub_at(ccstack, cxix);
376 for (;;) {
377 /* we may be in a higher stacklevel, so dig down deeper */
378 while (cxix < 0) {
379 if(top_si->si_type == PERLSI_MAIN)
380 Perl_croak(aTHX_ "next::method/next::can/maybe::next::method must be used in method context");
381 top_si = top_si->si_prev;
382 ccstack = top_si->si_cxstack;
383 cxix = __dopoptosub_at(ccstack, top_si->si_cxix);
384 }
b23e9cb9 385
5efa8a5b 386 if(CxTYPE((PERL_CONTEXT*)(&ccstack[cxix])) != CXt_SUB
387 || (PL_DBsub && GvCV(PL_DBsub) && ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))) {
388 cxix = __dopoptosub_at(ccstack, cxix - 1);
389 continue;
390 }
b23e9cb9 391
5efa8a5b 392 {
393 const I32 dbcxix = __dopoptosub_at(ccstack, cxix - 1);
394 if (PL_DBsub && GvCV(PL_DBsub) && dbcxix >= 0 && ccstack[dbcxix].blk_sub.cv == GvCV(PL_DBsub)) {
395 if(CxTYPE((PERL_CONTEXT*)(&ccstack[dbcxix])) != CXt_SUB) {
396 cxix = dbcxix;
397 continue;
398 }
b23e9cb9 399 }
400 }
b23e9cb9 401
5efa8a5b 402 cvgv = CvGV(ccstack[cxix].blk_sub.cv);
b23e9cb9 403
5efa8a5b 404 if(!isGV(cvgv)) {
405 cxix = __dopoptosub_at(ccstack, cxix - 1);
406 continue;
407 }
b23e9cb9 408
5efa8a5b 409 /* we found a real sub here */
eac662db 410 sv = sv_newmortal();
b23e9cb9 411
5efa8a5b 412 gv_efullname3(sv, cvgv, NULL);
b23e9cb9 413
a2051bf9 414 if (SvPOK(sv)) {
415 fq_subname = SvPVX(sv);
416 fq_subname_len = SvCUR(sv);
417
418 subname = strrchr(fq_subname, ':');
419 } else {
420 subname = NULL;
421 }
b23e9cb9 422
5efa8a5b 423 subname = strrchr(fq_subname, ':');
424 if(!subname)
425 Perl_croak(aTHX_ "next::method/next::can/maybe::next::method cannot find enclosing method");
b23e9cb9 426
5efa8a5b 427 subname++;
428 subname_len = fq_subname_len - (subname - fq_subname);
429 if(subname_len == 8 && strEQ(subname, "__ANON__")) {
430 cxix = __dopoptosub_at(ccstack, cxix - 1);
431 continue;
432 }
433 break;
b23e9cb9 434 }
5efa8a5b 435 cxix--;
b23e9cb9 436 }
437
438 /* If we made it to here, we found our context */
439
22c6f594 440 /* cachekey = "objpkg|context::method::name" */
441 cachekey = sv_2mortal(newSVpv(hvname, 0));
442 sv_catpvn(cachekey, "|", 1);
443 sv_catsv(cachekey, sv);
b23e9cb9 444
6c88cc6a 445 nmcache = get_hv("next::METHOD_CACHE", 1);
22c6f594 446 if((cache_entry = hv_fetch_ent(nmcache, cachekey, 0, 0))) {
b23e9cb9 447 SV* val = HeVAL(cache_entry);
448 if(val == &PL_sv_undef) {
449 if(throw_nomethod)
625e16df 450 Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
2605e591 451 XSRETURN_EMPTY;
b23e9cb9 452 }
2605e591 453 XPUSHs(sv_2mortal(newRV_inc(val)));
454 XSRETURN(1);
b23e9cb9 455 }
b23e9cb9 456
457 /* beyond here is just for cache misses, so perf isn't as critical */
458
459 stashname_len = subname - fq_subname - 2;
460 stashname = sv_2mortal(newSVpvn(fq_subname, stashname_len));
461
62eb9d08 462 linear_av = __mro_linear_isa_c3(aTHX_ selfstash, NULL, 0);
b23e9cb9 463
464 linear_svp = AvARRAY(linear_av);
2605e591 465 entries = AvFILLp(linear_av) + 1;
b23e9cb9 466
2605e591 467 while (entries--) {
053556af 468 SV* const linear_sv = *linear_svp++;
b23e9cb9 469 assert(linear_sv);
470 if(sv_eq(linear_sv, stashname))
471 break;
472 }
473
2605e591 474 if(entries > 0) {
29e61e10 475 SV* sub_sv = sv_2mortal(newSVpv(subname, subname_len));
476 HV* cc3_mro = get_hv("Class::C3::MRO", 0);
29e61e10 477
2605e591 478 while (entries--) {
053556af 479 SV* const linear_sv = *linear_svp++;
b23e9cb9 480 assert(linear_sv);
29e61e10 481
482 if(cc3_mro) {
483 HE* he_cc3_mro_class = hv_fetch_ent(cc3_mro, linear_sv, 0, 0);
484 if(he_cc3_mro_class) {
3499e05c 485 SV* cc3_mro_class_sv = HeVAL(he_cc3_mro_class);
486 if(SvROK(cc3_mro_class_sv)) {
487 HV* cc3_mro_class = (HV*)SvRV(cc3_mro_class_sv);
488 SV** svp_cc3_mro_class_methods = hv_fetch(cc3_mro_class, "methods", 7, 0);
489 if(svp_cc3_mro_class_methods) {
490 SV* cc3_mro_class_methods_sv = *svp_cc3_mro_class_methods;
491 if(SvROK(cc3_mro_class_methods_sv)) {
492 HV* cc3_mro_class_methods = (HV*)SvRV(cc3_mro_class_methods_sv);
493 if(hv_exists_ent(cc3_mro_class_methods, sub_sv, 0))
494 continue;
495 }
496 }
29e61e10 497 }
498 }
499 }
500
b23e9cb9 501 cstash = gv_stashsv(linear_sv, FALSE);
502
503 if (!cstash) {
504 if (ckWARN(WARN_MISC))
505 Perl_warner(aTHX_ packWARN(WARN_MISC), "Can't locate package %"SVf" for @%s::ISA",
506 (void*)linear_sv, hvname);
507 continue;
508 }
509
510 assert(cstash);
511
b23e9cb9 512 gvp = (GV**)hv_fetch(cstash, subname, subname_len, 0);
513 if (!gvp) continue;
514
515 candidate = *gvp;
516 assert(candidate);
517
518 if (SvTYPE(candidate) != SVt_PVGV)
519 gv_init(candidate, cstash, subname, subname_len, TRUE);
520 if (SvTYPE(candidate) == SVt_PVGV && (cand_cv = GvCV(candidate)) && !GvCVGEN(candidate)) {
62eb9d08 521 SvREFCNT_dec(linear_av);
b23e9cb9 522 SvREFCNT_inc((SV*)cand_cv);
d581cf05 523 if (!hv_store_ent(nmcache, cachekey, (SV*)cand_cv, 0)) {
ddc85d9f 524 croak("failed to store value in hash");
525 }
2605e591 526 XPUSHs(sv_2mortal(newRV_inc((SV*)cand_cv)));
527 XSRETURN(1);
b23e9cb9 528 }
529 }
530 }
531
62eb9d08 532 SvREFCNT_dec(linear_av);
d581cf05 533 if (!hv_store_ent(nmcache, cachekey, &PL_sv_undef, 0)) {
ddc85d9f 534 croak("failed to store value in hash");
535 }
b23e9cb9 536 if(throw_nomethod)
625e16df 537 Perl_croak(aTHX_ "No next::method '%s' found for %s", subname, hvname);
2605e591 538 XSRETURN_EMPTY;
b23e9cb9 539}
540
541XS(XS_Class_C3_XS_calculateMRO);
542XS(XS_Class_C3_XS_calculateMRO)
543{
b23e9cb9 544 dVAR; dXSARGS;
b23e9cb9 545
546 SV* classname;
547 HV* class_stash;
548 HV* cache = NULL;
549 AV* res;
550 I32 res_items;
551 I32 ret_items;
552 SV** res_ptr;
553
554 if(items < 1 || items > 2)
555 croak("Usage: calculateMRO(classname[, cache])");
556
557 classname = ST(0);
f0294f1b 558 if(items == 2) cache = (HV*)SvRV(ST(1));
b23e9cb9 559
560 class_stash = gv_stashsv(classname, 0);
625e16df 561 if(!class_stash)
562 Perl_croak(aTHX_ "No such class: '%s'!", SvPV_nolen(classname));
b23e9cb9 563
62eb9d08 564 res = __mro_linear_isa_c3(aTHX_ class_stash, cache, 0);
b23e9cb9 565
b23e9cb9 566 res_items = ret_items = AvFILLp(res) + 1;
567 res_ptr = AvARRAY(res);
568
569 SP -= items;
570
571 while(res_items--) {
572 SV* res_item = *res_ptr++;
62eb9d08 573 XPUSHs(sv_2mortal(newSVsv(res_item)));
b23e9cb9 574 }
62eb9d08 575 SvREFCNT_dec(res);
b23e9cb9 576
577 PUTBACK;
578
579 return;
580}
581
8feecaf9 582XS(XS_Class_C3_XS_plsubgen);
583XS(XS_Class_C3_XS_plsubgen)
584{
25dc7e96 585 dVAR; dXSARGS;
8feecaf9 586
587 SP -= items;
588 XPUSHs(sv_2mortal(newSViv(PL_sub_generation)));
589 PUTBACK;
590 return;
591}
592
625e16df 593XS(XS_Class_C3_XS_calc_mdt);
594XS(XS_Class_C3_XS_calc_mdt)
595{
625e16df 596 dVAR; dXSARGS;
625e16df 597
598 SV* classname;
599 HV* cache;
600 HV* class_stash;
601 AV* class_mro;
602 HV* our_c3mro; /* $Class::C3::MRO{classname} */
79782a49 603 SV* has_ovf = NULL;
625e16df 604 HV* methods;
605 I32 mroitems;
606
607 /* temps */
608 HV* hv;
609 HE* he;
625e16df 610 SV** svp;
611
612 if(items < 1 || items > 2)
613 croak("Usage: calculate_method_dispatch_table(classname[, cache])");
614
615 classname = ST(0);
616 class_stash = gv_stashsv(classname, 0);
617 if(!class_stash)
618 Perl_croak(aTHX_ "No such class: '%s'!", SvPV_nolen(classname));
619
620 if(items == 2) cache = (HV*)SvRV(ST(1));
621
622 class_mro = __mro_linear_isa_c3(aTHX_ class_stash, cache, 0);
623
624 our_c3mro = newHV();
ddc85d9f 625 if(!hv_store(our_c3mro, "MRO", 3, (SV*)newRV_noinc((SV*)class_mro), 0)) {
626 croak("failed to store value in hash");
627 }
625e16df 628
629 hv = get_hv("Class::C3::MRO", 1);
ddc85d9f 630 if(!hv_store_ent(hv, classname, (SV*)newRV_noinc((SV*)our_c3mro), 0)) {
631 croak("failed to store value in hash");
632 }
625e16df 633
634 methods = newHV();
635
636 /* skip first entry */
637 mroitems = AvFILLp(class_mro);
638 svp = AvARRAY(class_mro) + 1;
639 while(mroitems--) {
640 SV* mro_class = *svp++;
641 HV* mro_stash = gv_stashsv(mro_class, 0);
642
643 if(!mro_stash) continue;
644
79782a49 645 if(!has_ovf) {
625e16df 646 SV** ovfp = hv_fetch(mro_stash, "()", 2, 0);
647 if(ovfp) has_ovf = *ovfp;
79782a49 648 }
625e16df 649
650 hv_iterinit(mro_stash);
ad2debb2 651 while((he = hv_iternext(mro_stash))) {
625e16df 652 CV* code;
653 SV* mskey;
79782a49 654 SV* msval;
655 HE* ourent;
656 HV* meth_hash;
657 SV* orig;
625e16df 658
659 mskey = hv_iterkeysv(he);
660 if(hv_exists_ent(methods, mskey, 0)) continue;
79782a49 661
662 msval = hv_iterval(mro_stash, he);
663 if(SvTYPE(msval) != SVt_PVGV || !(code = GvCVu(msval)))
664 continue;
665
666 if((ourent = hv_fetch_ent(class_stash, mskey, 0, 0))) {
667 SV* val = HeVAL(ourent);
668 if(val && SvTYPE(val) == SVt_PVGV && GvCVu(val))
625e16df 669 continue;
670 }
671
79782a49 672 meth_hash = newHV();
673 orig = newSVsv(mro_class);
674 sv_catpvn(orig, "::", 2);
675 sv_catsv(orig, mskey);
ddc85d9f 676 if( !hv_store(meth_hash, "orig", 4, orig, 0)
677 || !hv_store(meth_hash, "code", 4, newRV_inc((SV*)code), 0)
678 || !hv_store_ent(methods, mskey, newRV_noinc((SV*)meth_hash), 0) ) {
679 croak("failed to store value in hash");
680 }
625e16df 681 }
682 }
683
ddc85d9f 684 if(!hv_store(our_c3mro, "methods", 7, newRV_noinc((SV*)methods), 0)) {
685 croak("failed to store value in hash");
686 }
687 if(has_ovf) {
688 if(!hv_store(our_c3mro, "has_overload_fallback", 21, SvREFCNT_inc(has_ovf), 0)) {
689 croak("failed to store value in hash");
690 }
691 }
625e16df 692 XSRETURN_EMPTY;
693}
694
6ed110b7 695MODULE = Class::C3::XS PACKAGE = Class::C3::XS
b23e9cb9 696
df0af0e2 697PROTOTYPES: DISABLED
698
b23e9cb9 699BOOT:
700 newXS("Class::C3::XS::calculateMRO", XS_Class_C3_XS_calculateMRO, __FILE__);
8feecaf9 701 newXS("Class::C3::XS::_plsubgen", XS_Class_C3_XS_plsubgen, __FILE__);
625e16df 702 newXS("Class::C3::XS::_calculate_method_dispatch_table", XS_Class_C3_XS_calc_mdt, __FILE__);
2605e591 703 newXS("Class::C3::XS::_nextcan", XS_Class_C3_XS_nextcan, __FILE__);
625e16df 704