Clone-Closure

 view release on metacpan or  search on metacpan

lib/Clone/Closure.xs  view on Meta::CPAN


            /* closed-over lexicals need checking */
            else {
                CV *scope;

                /* start with the scope that declared the lexical... */
                scope = pad_findscope(clone, name_sv);

                /* even if this scope is unique, it may be inside one
                 * which isn't:
                 *     sub foo { eval q/my $x; sub { $x; }/; }
                 * eval STRING is always CvUNIQUE */
                while (scope && CvUNIQUE(scope)) {
                    scope = CvOUTSIDE(scope);
                    TRACE_SCOPE(scope);
                }

                /* XXX handle locating loops: see cop@269 */

                /* if this lexical was defined in a scope that can only
                 * run once it can be copied, otherwise it must be
                 * cloned */
                can_copy = (!scope || CvUNIQUE(scope));
            }
        }

        /* just in case :) */
        else {
            warn("Clone::Closure: unknown pad entry: please report a bug!");
#ifdef DEBUG_CLONE
            warn("name:\n");
            sv_dump(name_sv);
            warn("val:\n");
            sv_dump(val_sv);
#endif
            continue;
        }

        TRACE_SV("ref", name, val_sv);

        if (is_proto) {
            assert(PERL_VERSION < 9);
#ifdef CvWEAKOUTSIDE_on
            assert(CvWEAKOUTSIDE(val_sv));
#endif

            new_sv = (SV *)CC_cv_clone((CV *)val_sv);

            CvCLONE_on(new_sv);
            SvPADMY_on(new_sv);

#ifndef CvWEAKOUTSIDE_on
            {
                CV *old = CvOUTSIDE(new_sv);
                SvREFCNT_dec(old);
                TRACE_SV("ref", "outside", old);
            }
#endif
            CvOUTSIDE(new_sv) = clone;
#ifdef CvWEAKOUTSIDE_on
            TRACE_SV("weaken", name, new_sv);
            TRACE_SV("outside", name, clone);
            CvWEAKOUTSIDE_on(new_sv);
#else
            SvREFCNT_inc(clone);
            TRACE_SV("clone", "outside", clone);
#endif

            pad_clone(SEEN, (CV *)val_sv, (CV *)new_sv);
        }
        else if (can_copy) {
            new_sv = SvREFCNT_inc(val_sv);
            CLONE_STORE(val_sv, new_sv);
        }
        else {
            new_sv = sv_clone(SEEN, val_sv);
        }
         
        TRACE_SV("ref, again", name, val_sv);
        TRACE_SV(can_copy ? "copy" : "clone", name, new_sv);

        old_p    = av_fetch(padv, i, 0);
        old_sv   = old_p ? *old_p : &PL_sv_undef;

        /* can't use av_store as the refcounts get wrong:
         * pads are AvREAL even though they shouldn't be */
        (AvARRAY(padv))[i] = new_sv;

        /* XXX I don't like this: sometimes the refcnt gets too low */
        if ( SvREFCNT(old_sv) > 1 ) {
            SvREFCNT_dec(old_sv);
            TRACE_SV("drop", name, old_sv);
        }
        else
            TRACE_SV("NO DROP", name, old_sv);
    }

    TRACE_SV("clone", "pad", clone);
}

/* locate the scope in which a lexical was declared */
/* mostly stolen from pad.c:pad_findlex */

static CV *
pad_findscope(CV *scope, SV *name_sv)
{
    const char  *name = SvPVX_const(name_sv);
    U32          seq;
    CV          *last_fake = scope;

#ifdef CvOUTSIDE_SEQ
#define MOVE_OUT(scp, sq) sq = CvOUTSIDE_SEQ(scp), scp = CvOUTSIDE(scp)
#else
    seq = SvIVX(name_sv);
#define MOVE_OUT(scp, sq) scp = CvOUTSIDE(scp)
#endif

    TRACE_SCOPE(scope);

    for ( MOVE_OUT(scope, seq); scope; MOVE_OUT(scope, seq) ) {
        SV **svp, *sv;

lib/Clone/Closure.xs  view on Meta::CPAN

                    keepmg = 0;
                    break;
            }

            if (copymg)
                obj = obj ? sv_clone(SEEN, mg->mg_obj) : NULL;

            if (keepmg) {
                TRACE_MG("clone", mg->mg_type, ptr, mg->mg_len, obj);
                sv_magicext(
                    clone, 
                    obj,
                    mg->mg_type, 
                    mg->mg_virtual,
                    ptr, 
                    mg->mg_len
                );
            }
            else {
                TRACE_MG("drop", mg->mg_type, mg->mg_ptr, mg->mg_len,
                mg->mg_obj);
            }
        }

        if (shared > 0) {
#ifdef SvSHARE
            TRACE_SV("share", "SV", clone);
            SvSHARE(clone);
#else
            croak("can't share values in this version of perl");
#endif
        }
    }

    if (!recurse) {
        TRACE_SV("skip", "SV", clone);
    }
    else if ( SvTYPE(ref) == SVt_PVHV ) {
        hv_clone(SEEN, (HV *)ref, (HV *)clone);
    }
    else if ( SvTYPE(ref) == SVt_PVAV ) {
        av_clone(SEEN, (AV *)ref, (AV *)clone);
    }
    else if ( SvTYPE(ref) == SVt_PVCV ) {
        if (CvCLONED((CV *)ref)) {
            pad_clone(SEEN, (CV *)ref, (CV *)clone);
        }
    }
    /* 3: REFERENCE (inlined for speed) */
    else if (SvROK(ref)) {
        TRACE_SV("ref", "RV", ref);

        SvROK_on(clone);
        SvRV(clone) = sv_clone(SEEN, SvRV(ref));

        if (sv_isobject(ref)) {
            sv_bless(clone, SvSTASH(SvRV(ref)));
        }

        if (SvWEAKREF(ref)) {
            TRACE_SV("weaken", "RV", clone);
            sv_rvweaken(clone);
        }

        TRACE_SV("clone", "RV", clone);
    }

    if (SvREADONLY(ref))
        SvREADONLY_on(clone);

    TRACE_SV("clone", "SV", clone);
    return clone;
}

MODULE = Clone::Closure		PACKAGE = Clone::Closure

PROTOTYPES: ENABLE

void
_breakpoint()
    PPCODE:
        XSRETURN_UNDEF;

void
clone(ref)
	SV *ref
    PREINIT:
	SV *clone;
        HV *SEEN;
    PPCODE:
        SEEN = newHV();

        TRACE_SV("ref", "clone", ref);
	clone = sv_clone(SEEN, ref);
        TRACE_SV("clone", "clone", clone);

        SvREFCNT_dec(SEEN);

	EXTEND(SP,1);
	PUSHs(sv_2mortal(clone));



( run in 1.727 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )