File Coverage

src/xs/lib/clone.cc
Criterion Covered Total %
statement 85 85 100.0
branch 73 110 66.3
condition n/a
subroutine n/a
pod n/a
total 158 195 81.0


line stmt bran cond sub pod time code
1             #include
2             #include
3             #include
4              
5             #ifndef gv_fetchmeth
6             #define gv_fetchmeth(stash,name,len,level,flags) gv_fetchmethod_autoload(stash,name,0)
7             #endif
8              
9             namespace xs { namespace lib {
10              
11             using panda::unlikely;
12              
13             static const char* HOOK_METHOD = "HOOK_CLONE";
14 11           static const int HOOK_METHLEN = strlen(HOOK_METHOD);
15              
16             typedef std::map CloneMap;
17             static const int CLONE_MAX_DEPTH = 5000;
18             static MGVTBL clone_marker;
19              
20             static void _clone (pTHX_ SV* dest, SV* source, CloneMap*& map, I32 depth);
21              
22 33           SV* clone (pTHX_ SV* source, bool cross) {
23 33           SV* ret = newSV(0);
24             try {
25 33           CloneMap* mapref = NULL;
26 33 100         if (cross) {
27 12 50         CloneMap map;
28 5           mapref = ↦
29 5 50         _clone(aTHX_ ret, source, mapref, 0);
30             }
31 28 100         else _clone(aTHX_ ret, source, mapref, 0);
32 2           } catch (int val) {
33 2 50         SvREFCNT_dec(ret);
34 2           croak("clone: max depth (%d) reached, it looks like you passed a cycled structure", CLONE_MAX_DEPTH);
35             }
36 31           return ret;
37             }
38              
39 40106           static void _clone (pTHX_ SV* dest, SV* source, CloneMap*& map, I32 depth) {
40 40106 100         if (depth > CLONE_MAX_DEPTH) throw 1;
41              
42 40104 100         if (SvROK(source)) { // reference
43 10034           SV* source_val = SvRV(source);
44 10034           svtype val_type = SvTYPE(source_val);
45              
46 10034 100         if (unlikely(val_type == SVt_PVCV || val_type == SVt_PVIO)) { // CV and IO cannot be copied - just set reference to the same SV
    100          
    100          
47 2 50         SvSetSV_nosteal(dest, source);
    50          
48 2           return;
49             }
50              
51 10032           uint64_t id = PTR2UV(source_val);
52 10032 100         if (map) {
53 15 50         CloneMap::iterator it = map->find(id);
54 15 100         if (it != map->end()) {
55 4 50         SvSetSV_nosteal(dest, it->second);
    50          
56 15           return;
57             }
58             }
59              
60             GV* cloneGV;
61 10028           bool is_object = SvOBJECT(source_val);
62              
63             // cloning an object with custom clone behavior
64 10028 100         if (is_object) {
65 5008 50         auto mg = mg_findext(source_val, PERL_MAGIC_ext, &clone_marker);
66 5008 100         if (mg) {
67 2           map = reinterpret_cast(mg->mg_ptr); // restore top-map after recursive clone() call
68             }
69 5006 50         else if ((cloneGV = gv_fetchmeth(SvSTASH(source_val), HOOK_METHOD, HOOK_METHLEN, 0))) {
    100          
70             // set cloning flag into object's magic to prevent infinite loop if user calls 'clone' again from hook
71 3 50         sv_magicext(source_val, NULL, PERL_MAGIC_ext, &clone_marker, (const char*)map, 0);
72 3 50         dSP; ENTER; SAVETMPS;
    50          
73 3 50         PUSHMARK(SP);
    0          
74 3 50         XPUSHs(source);
    0          
75 3           PUTBACK;
76 3 50         int count = call_sv((SV*)GvCV(cloneGV), G_SCALAR);
77 3           SPAGAIN;
78 3           SV* retval = NULL;
79 6 100         while (count--) retval = POPs;
80 3 50         if (retval) SvSetSV(dest, retval);
    50          
    50          
81 3           PUTBACK;
82 3 50         FREETMPS; LEAVE;
    50          
    50          
83             // remove cloning flag from object's magic
84 3 50         sv_unmagicext(source_val, PERL_MAGIC_ext, &clone_marker);
85 3 100         if (map) (*map)[id] = dest;
    50          
86 5008           return;
87             }
88             }
89              
90 10025 50         SV* refval = newSV(0);
91 10025 50         sv_upgrade(dest, SVt_RV);
92 10025           SvRV_set(dest, refval);
93 10025           SvROK_on(dest);
94              
95 10025 100         if (is_object) sv_bless(dest, SvSTASH(source_val)); // cloning an object without any specific clone behavior
    50          
96 10025 100         if (map) (*map)[id] = dest;
    50          
97 10025 100         _clone(aTHX_ refval, source_val, map, depth+1);
98              
99 5032           return;
100             }
101              
102 30070           switch (SvTYPE(source)) {
103             case SVt_IV: // integer
104             case SVt_NV: // long double
105             case SVt_PV: // string
106             case SVt_PVIV: // string + integer
107             case SVt_PVNV: // string + long double
108             case SVt_PVMG: // blessed scalar (doesn't really true, it's just vars or magic vars)
109             case SVt_PVGV: // typeglob
110             #if PERL_VERSION > 16
111             case SVt_REGEXP: // regexp
112             #endif
113 20045 50         SvSetSV_nosteal(dest, source);
114 20045           return;
115             #if PERL_VERSION <= 16 // fix bug in SvSetSV_nosteal while copying regexp SV prior to perl 5.16.0
116             case SVt_REGEXP: // regexp
117             SvSetSV_nosteal(dest, source);
118             if (SvSTASH(dest) == NULL) SvSTASH_set(dest, gv_stashpv("Regexp",0));
119             return;
120             #endif
121             case SVt_PVAV: { // array
122 5011           sv_upgrade(dest, SVt_PVAV);
123 5011           SV** srclist = AvARRAY((AV*)source);
124 5011           SSize_t srcfill = AvFILLp((AV*)source);
125 5011           av_extend((AV*)dest, srcfill); // dest is an empty array. we can set directly it's SV** array for speed
126 5011           AvFILLp((AV*)dest) = srcfill; // set array len
127 5011           SV** dstlist = AvARRAY((AV*)dest);
128 20033 100         for (SSize_t i = 0; i <= srcfill; ++i) {
129 15024           SV* srcval = *srclist++;
130 15024 50         if (srcval != NULL) { // if not empty slot
131 15024           SV* elem = newSV(0);
132 15024           dstlist[i] = elem;
133 15024           _clone(aTHX_ elem, srcval, map, depth+1);
134             }
135             }
136 5009           return;
137             }
138             case SVt_PVHV: { // hash
139 5014           sv_upgrade(dest, SVt_PVHV);
140 5014           STRLEN hvmax = HvMAX((HV*)source);
141 5014           HE** hvarr = HvARRAY((HV*)source);
142 5014 50         if (!hvarr) return;
143              
144 35124 100         for (STRLEN i = 0; i <= hvmax; ++i) {
145             const HE* entry;
146 45134 100         for (entry = hvarr[i]; entry; entry = HeNEXT(entry)) {
147 15024           HEK* hek = HeKEY_hek(entry);
148 15024           SV* elem = newSV(0);
149 15024           hv_storehek((HV*)dest, hek, elem);
150 15024           _clone(aTHX_ elem, HeVAL(entry), map, depth+1);
151             }
152             }
153              
154 16           return;
155             }
156             case SVt_NULL: // undef
157             default: // BIND, LVALUE, FORMAT - are not copied
158 30102           return;
159             }
160             }
161              
162 44 50         }}
    50