| 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 |