Data-Recursive

 view release on metacpan or  search on metacpan

src/xs/clone.cc  view on Meta::CPAN

    CrossData* crossdata = NULL;
    if (flags & CloneFlags::TRACK_REFS) {
        CrossData data;
        crossdata = &data;
        _clone(aTHX_ ret, source, crossdata, 0);
        auto end = data.map.end();
        for (const auto& row : data.weakrefs) { // post process weak refs that appeared before their strong refs
            auto it = data.map.find(row.key);
            if (it == end) continue;
            SvSetSV_nosteal(row.dest, it->second);
            sv_rvweaken(row.dest);
        }
    }
    else _clone(aTHX_ ret, source, crossdata, 0);

    return ret;
}

static void _clone (pTHX_ SV* dest, SV* source, CrossData*& xdata, I32 depth) {
    if (depth > CLONE_MAX_DEPTH) throw std::invalid_argument(
        std::string("clone: max depth (") + std::to_string(CLONE_MAX_DEPTH) + ") reached, it looks like you passed a cycled structure"
    );

    if (SvROK(source)) { // reference
        SV* source_val = SvRV(source);
        svtype val_type = SvTYPE(source_val);

        if (val_type == SVt_PVCV || val_type == SVt_PVIO) { // CV and IO cannot be copied - just set reference to the same SV
            SvSetSV_nosteal(dest, source);
            if (SvWEAKREF(source)) sv_rvweaken(dest);
            return;
        }

        uint64_t id = PTR2UV(source_val);
        if (xdata) {
            auto it = xdata->map.find(id);
            if (it != xdata->map.end()) {
                SvSetSV_nosteal(dest, it->second);
                if (SvWEAKREF(source)) sv_rvweaken(dest);
                return;
            }
            if (SvWEAKREF(source)) {
                // we can't clone object weakref points to right now, because no strong refs for the object cloned so far, we must wait until the end
                xdata->weakrefs.push_back({dest, id});
                return;
            }
        }

        GV* cloneGV;

t/clone-weakref.t  view on Meta::CPAN

use 5.012;
use warnings;
use Test::More;
use Test::Deep;
use Data::Recursive qw/clone lclone/;
use Scalar::Util qw/weaken isweak/;

subtest 'lclone makes all weak refs strong refs' => sub {
    my $data = [1,2,3];
    my $val = {data => $data};
    weaken($val->{data});
    my $copy = lclone($val);
    cmp_deeply($copy, {data => [1,2,3]});
    isnt $copy->{data}, $val->{data};
    ok !isweak($copy->{data});
};

subtest 'weakref to CV/IO' => sub {
    my $sub = sub { return 123};
    my $io = *STDERR{IO};
    my $val = [$sub, $io];
    weaken($val->[0]); weaken($val->[1]);
    my $copy = clone($val);
    cmp_deeply $copy, [$sub, $io], "data ok";
    ok isweak($copy->[0]) && isweak($copy->[1]), "ref copied as weak";
};

subtest 'alone weak ref dissapears' => sub {
    my $data = [1,2,3];
    my $val = {data => $data};
    weaken($val->{data});
    my $copy = clone($val);
    my @a = ({}) x 200;
    is $copy->{data}, undef;
};

subtest 'cloning strong before weak' => sub {
    my $data = {a => 1};
    my $val = [$data, $data];
    weaken($val->[1]);
    my $copy = clone($val);
    cmp_deeply $copy, $val, "data ok";
    ok isweak($copy->[1]), "ref copied as weak";
};

subtest 'cloning weak before strong' => sub {
    my $data = {a => 1};
    my $val = [$data, $data];
    weaken($val->[0]);
    my $copy = clone($val);
    cmp_deeply $copy, $val, "data ok";
    ok isweak($copy->[0]), "ref copied as weak";
};

done_testing();



( run in 0.579 second using v1.01-cache-2.11-cpan-65fba6d93b7 )