File Coverage

lib/Data/Clone.xs
Criterion Covered Total %
statement 136 146 93.1
branch 89 124 71.7
condition n/a
subroutine n/a
pod n/a
total 225 270 83.3


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #define NO_XSLOCKS /* for exceptions */
3             #include "xshelper.h"
4              
5             #include "data_clone.h"
6              
7             #ifndef SvRXOK
8             #define SvRXOK(sv) (SvROK(sv) && SvMAGICAL(SvRV(sv)) && mg_find(SvRV(sv), PERL_MAGIC_qr))
9             #endif
10              
11             #define REINTERPRET_CAST(T, value) ((T)value)
12              
13             #define PTR2STR(ptr) REINTERPRET_CAST(const char*, (&ptr))
14              
15             #define MY_CXT_KEY "Data::Clone::_guts" XS_VERSION
16             typedef struct {
17             U32 depth;
18             HV* seen;
19             CV* caller_cv;
20             GV* my_clone;
21             GV* object_callback;
22              
23             SV* clone_method; /* "clone" */
24             SV* tieclone_method; /* "TIECLONE" */
25             } my_cxt_t;
26             START_MY_CXT
27              
28             static SV*
29             clone_rv(pTHX_ pMY_CXT_ SV* const cloning);
30              
31             static SV*
32 371           clone_sv(pTHX_ pMY_CXT_ SV* const cloning) {
33             assert(cloning);
34              
35 371 100         SvGETMAGIC(cloning);
36              
37 371 100         if(SvROK(cloning)){
38 267           return clone_rv(aTHX_ aMY_CXT_ cloning);
39             }
40             else{
41 104           SV* const cloned = newSV(0);
42             /* no need to set SV_GMAGIC */
43 104           sv_setsv_flags(cloned, cloning, SV_NOSTEAL);
44 104           return cloned;
45             }
46             }
47              
48             static void
49 92           clone_hv_to(pTHX_ pMY_CXT_ HV* const cloning, HV* const cloned) {
50             HE* iter;
51              
52             assert(cloning);
53             assert(cloned);
54              
55 92           hv_iterinit(cloning);
56 200 100         while((iter = hv_iternext(cloning))){
57 110           SV* const key = hv_iterkeysv(iter);
58 110           SV* const val = clone_sv(aTHX_ aMY_CXT_ hv_iterval(cloning, iter));
59 108           (void)hv_store_ent(cloned, key, val, 0U);
60             }
61 90           }
62              
63             static void
64 52           clone_av_to(pTHX_ pMY_CXT_ AV* const cloning, AV* const cloned) {
65             I32 last, i;
66              
67             assert(cloning);
68             assert(cloned);
69              
70 52           last = av_len(cloning);
71 52           av_extend(cloned, last);
72              
73 144 100         for(i = 0; i <= last; i++){
74 92           SV** const svp = av_fetch(cloning, i, FALSE);
75 92 50         if(svp){
76 92           (void)av_store(cloned, i, clone_sv(aTHX_ aMY_CXT_ *svp));
77             }
78             }
79 52           }
80              
81              
82             static GV*
83 151           find_method_sv(pTHX_ HV* const stash, SV* const name) {
84 151           HE* const he = hv_fetch_ent(stash, name, FALSE, 0U);
85              
86 151 100         if(he && isGV(HeVAL(he)) && GvCV((GV*)HeVAL(he))){ /* shortcut */
    50          
    100          
87             return (GV*)HeVAL(he);
88             }
89              
90             assert(SvPOKp(name));
91 16           return gv_fetchmeth_autoload(stash, SvPVX(name), SvCUR(name), 0);
92             }
93              
94             static int
95 141           sv_has_backrefs(pTHX_ SV* const sv) {
96 141 100         if(SvRMAGICAL(sv) && mg_find(sv, PERL_MAGIC_backref)) {
    50          
97             return TRUE;
98             }
99             #ifdef HvAUX
100 138 100         else if(SvTYPE(sv) == SVt_PVHV){
101 105 100         return SvOOK(sv) && HvAUX((HV*)sv)->xhv_backreferences != NULL;
    100          
102             }
103             #endif
104             return FALSE;
105             }
106              
107             /* my_dopoptosub_at() and caller_cv() are stolen from pp_ctl.c */
108             static I32
109             my_dopoptosub_at(pTHX_ const PERL_CONTEXT* const cxstk, I32 const startingblock) {
110             I32 i;
111              
112             assert(cxstk);
113              
114 428 100         for (i = startingblock; i >= 0; i--) {
    0          
    0          
115 329           const PERL_CONTEXT* const cx = &cxstk[i];
116 329 100         if(CxTYPE(cx) == CXt_SUB){
    0          
    0          
117             break;
118             }
119             }
120             return i;
121             }
122              
123             static CV*
124 169           caller_cv(pTHX) {
125             const PERL_CONTEXT* cx;
126 169           const PERL_CONTEXT* ccstack = cxstack;
127             const PERL_SI *si = PL_curstackinfo;
128 169           I32 cxix = my_dopoptosub_at(aTHX_ ccstack, cxstack_ix);
129             I32 count = 0;
130              
131             for (;;) {
132             /* we may be in a higher stacklevel, so dig down deeper */
133 169 100         while (cxix < 0 && si->si_type != PERLSI_MAIN) {
    50          
134 0           si = si->si_prev;
135 0           ccstack = si->si_cxstack;
136 0           cxix = my_dopoptosub_at(aTHX_ ccstack, si->si_cxix);
137             }
138             if (cxix < 0) {
139             return NULL;
140             }
141             /* skip &DB::sub */
142 70 50         if (PL_DBsub && GvCV(PL_DBsub) &&
    50          
143 0 0         ccstack[cxix].blk_sub.cv == GvCV(PL_DBsub))
144 0           count++;
145 70 50         if (!count--)
146             break;
147              
148 0           cxix = my_dopoptosub_at(aTHX_ ccstack, cxix - 1);
149             }
150              
151 70           cx = &ccstack[cxix];
152 70           return cx->blk_sub.cv;
153             }
154              
155             static void
156 78           store_to_seen(pTHX_ pMY_CXT_ SV* const sv, SV* const proto) {
157 78           (void)hv_store(MY_CXT.seen, PTR2STR(sv), sizeof(sv), proto, 0U);
158 78           SvREFCNT_inc_simple_void_NN(proto);
159 78           }
160              
161             static SV*
162 69           dc_call_sv1(pTHX_ SV* const proc, SV* const arg1) {
163 69           dSP;
164             SV* ret;
165              
166             assert(proc);
167             assert(arg1);
168              
169 69           ENTER;
170 69           SAVETMPS;
171              
172 69 50         PUSHMARK(SP);
173 69 50         XPUSHs(arg1);
174 69           PUTBACK;
175              
176 69           call_sv(proc, G_SCALAR);
177              
178 55           SPAGAIN;
179 55           ret = POPs;
180 55           PUTBACK;
181              
182 55           SvREFCNT_inc_simple_void_NN(ret);
183              
184 55 50         FREETMPS;
185 55           LEAVE;
186              
187 55           return sv_2mortal(ret);
188             }
189              
190             static int
191             dc_need_to_call(pTHX_ pMY_CXT_ const CV* const method) {
192             //warn("dc_need_co_call 0x%p 0x%p 0x%p", method, GvCV(MY_CXT.my_clone), MY_CXT.caller_cv);
193              
194 119 100         return method != GvCV(MY_CXT.my_clone) && method != MY_CXT.caller_cv;
195             }
196              
197              
198             static SV*
199 151           dc_clone_object(pTHX_ pMY_CXT_ SV* const cloning, SV* const method_sv) {
200 151           SV* const sv = SvRV(cloning);
201 151           GV* const method = find_method_sv(aTHX_ SvSTASH(sv), method_sv);
202              
203 151 100         if(!method){ /* not a clonable object */
204 14 50         SV* const object_callback = GvSVn(MY_CXT.object_callback);
205             /* try to $Data::Clone::ObjectCallback->($cloning) */
206              
207 14 50         SvGETMAGIC(object_callback);
208              
209 14 100         if(SvOK(object_callback)){
210 8           SV* const x = dc_call_sv1(aTHX_ object_callback, cloning);
211              
212 2 50         if(!SvROK(x)){
213 0 0         croak("ObjectCallback function returned %s, but it must return a reference",
214             SvOK(x) ? SvPV_nolen_const(x) : "undef");
215             }
216              
217             return x;
218             }
219              
220 6           return sv_mortalcopy(cloning);
221             croak("Non-clonable object %"SVf" found (missing a %"SVf" method)",
222             cloning, method_sv);
223             }
224              
225             /* has its own clone method */
226 137 100         if(dc_need_to_call(aTHX_ aMY_CXT_ GvCV(method))){
227 61           SV* const x = dc_call_sv1(aTHX_ (SV*)GvCV(method), cloning);
228              
229 53 50         if(!SvROK(x)){
230 0 0         croak("Cloning method '%"SVf"' returned %s, but it must return a reference",
231             method_sv, SvOK(x) ? SvPV_nolen_const(x) : "undef");
232             }
233              
234             return x;
235             }
236             else { /* default clone() behavior: deep copy */
237             return NULL;
238             }
239             }
240              
241              
242             static SV*
243 267           clone_rv(pTHX_ pMY_CXT_ SV* const cloning) {
244             int may_be_circular;
245             SV* sv;
246             SV* proto;
247             SV* cloned;
248             MAGIC* mg;
249             //CV* old_cv;
250              
251             assert(cloning);
252             assert(SvROK(cloning));
253              
254 267           sv = SvRV(cloning);
255 267 100         may_be_circular = (SvREFCNT(sv) > 1 || sv_has_backrefs(aTHX_ sv) );
    100          
256              
257             if(may_be_circular){
258 132           SV** const svp = hv_fetch(MY_CXT.seen, PTR2STR(sv), sizeof(sv), FALSE);
259 132 100         if(svp){
260 14           proto = *svp;
261 14           goto finish;
262             }
263             }
264              
265 253 100         if(SvOBJECT(sv) && !SvRXOK(cloning)){
    100          
266 139           proto = dc_clone_object(aTHX_ aMY_CXT_ cloning, MY_CXT.clone_method);
267              
268 129 100         if(proto){
269 57           proto = SvRV(proto);
270 57           goto finish;
271             }
272              
273             /* fall through to make a deep copy */
274             }
275 114 100         else if((mg = SvTIED_mg(sv, PERL_MAGIC_tied))){
    100          
276             assert(SvTYPE(sv) == SVt_PVAV || SvTYPE(sv) == SVt_PVHV);
277 12 50         proto = dc_clone_object(aTHX_ aMY_CXT_ SvTIED_obj(sv, mg), MY_CXT.tieclone_method);
278              
279 8 100         if(proto){
280 4           SV* const varsv = (SvTYPE(sv) == SVt_PVHV
281 2           ? (SV*)newHV()
282 4 100         : (SV*)newAV()); // can we use newSV_type()?
283 4           sv_magic(varsv, proto, PERL_MAGIC_tied, NULL, 0);
284             proto = varsv;
285 4           goto finish;
286             }
287              
288             /* fall through to make a deep copy */
289             }
290              
291             /* XXX: need to save caller_cv, or not? */
292             //old_cv = MY_CXT.caller_cv;
293 178           MY_CXT.caller_cv = NULL;
294              
295 178 100         if(SvTYPE(sv) == SVt_PVAV){
296 52           proto = sv_2mortal((SV*)newAV());
297 52 100         if(may_be_circular){
298 22           store_to_seen(aTHX_ aMY_CXT_ sv, proto);
299             }
300 52           clone_av_to(aTHX_ aMY_CXT_ (AV*)sv, (AV*)proto);
301             }
302 126 100         else if(SvTYPE(sv) == SVt_PVHV){
303 92           proto = sv_2mortal((SV*)newHV());
304 92 100         if(may_be_circular){
305 56           store_to_seen(aTHX_ aMY_CXT_ sv, proto);
306             }
307 92           clone_hv_to(aTHX_ aMY_CXT_ (HV*)sv, (HV*)proto);
308             }
309             else {
310             proto = sv; /* do nothing */
311             }
312              
313             //MY_CXT.caller_cv = old_cv;
314              
315 251           finish:
316 251           cloned = newRV_inc(proto);
317              
318 251 100         if(SvOBJECT(sv)){
319 131           sv_bless(cloned, SvSTASH(sv));
320             }
321              
322 251 100         return SvWEAKREF(cloning) ? sv_rvweaken(cloned) : cloned;
323             }
324              
325             /* as SV* sv_clone(SV* sv) */
326             SV*
327 169           Data_Clone_sv_clone(pTHX_ SV* const sv) {
328 169           SV* VOL retval = NULL;
329             CV* VOL old_cv;
330             dMY_CXT;
331             dXCPT;
332              
333 169 50         if(++MY_CXT.depth == U32_MAX){
334 0           croak("Depth overflow on clone()");
335             }
336              
337 169           old_cv = MY_CXT.caller_cv;
338 169           MY_CXT.caller_cv = caller_cv(aTHX);
339              
340 183 100         XCPT_TRY_START {
341 169           retval = sv_2mortal(clone_sv(aTHX_ aMY_CXT_ sv));
342 169           } XCPT_TRY_END
343              
344 169           MY_CXT.caller_cv = old_cv;
345              
346 169 100         if(--MY_CXT.depth == 0){
347 113           hv_undef(MY_CXT.seen);
348             }
349              
350 169 100         XCPT_CATCH {
351 14 50         XCPT_RETHROW;
    0          
352             }
353 155           return retval;
354             }
355              
356             static void
357 9           my_cxt_initialize(pTHX_ pMY_CXT) {
358 9           MY_CXT.depth = 0;
359 9           MY_CXT.seen = newHV();
360 9           MY_CXT.my_clone = CvGV(get_cvs("Data::Clone::clone", GV_ADD));
361              
362 9           MY_CXT.object_callback = gv_fetchpvs("Data::Clone::ObjectCallback", GV_ADDMULTI, SVt_PV);
363              
364 9           MY_CXT.clone_method = newSVpvs_share("clone");
365 9           MY_CXT.tieclone_method = newSVpvs_share("TIECLONE");
366 9           }
367              
368             MODULE = Data::Clone PACKAGE = Data::Clone
369              
370             PROTOTYPES: DISABLE
371              
372             BOOT:
373             {
374             MY_CXT_INIT;
375 9           my_cxt_initialize(aTHX_ aMY_CXT);
376             }
377              
378             #ifdef USE_ITHREADS
379              
380             void
381             CLONE(...)
382             CODE:
383             {
384             MY_CXT_CLONE;
385             my_cxt_initialize(aTHX_ aMY_CXT);
386             PERL_UNUSED_VAR(items);
387             }
388              
389             #endif
390              
391             void
392             clone(SV* sv)
393             CODE:
394             {
395 169           sv = sv_clone(sv);
396 155           ST(0) = sv;
397 155           XSRETURN(1);
398             }
399              
400             bool
401             is_cloning()
402             CODE:
403             {
404             dMY_CXT;
405 0 0         RETVAL = (MY_CXT.depth != 0);
406             }
407             OUTPUT:
408             RETVAL