|
Packit |
8df772 |
#include <assert.h>
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#include "EXTERN.h"
|
|
Packit |
8df772 |
#include "perl.h"
|
|
Packit |
8df772 |
#include "XSUB.h"
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#define CLONE_KEY(x) ((char *) &x)
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#define CLONE_STORE(x,y) \
|
|
Packit |
8df772 |
do { \
|
|
Packit |
8df772 |
if (!hv_store(hseen, CLONE_KEY(x), PTRSIZE, SvREFCNT_inc(y), 0)) { \
|
|
Packit |
8df772 |
SvREFCNT_dec(y); /* Restore the refcount */ \
|
|
Packit |
8df772 |
croak("Can't store clone in seen hash (hseen)"); \
|
|
Packit |
8df772 |
} \
|
|
Packit |
8df772 |
else { \
|
|
Packit |
8df772 |
TRACEME(("storing ref = 0x%x clone = 0x%x\n", ref, clone)); \
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone))); \
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x(%d)\n", ref, SvREFCNT(ref))); \
|
|
Packit |
8df772 |
} \
|
|
Packit |
8df772 |
} while (0)
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#define CLONE_FETCH(x) (hv_fetch(hseen, CLONE_KEY(x), PTRSIZE, 0))
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
static SV *hv_clone (SV *, SV *, HV *, int);
|
|
Packit |
8df772 |
static SV *av_clone (SV *, SV *, HV *, int);
|
|
Packit |
8df772 |
static SV *sv_clone (SV *, HV *, int);
|
|
Packit |
8df772 |
static SV *rv_clone (SV *, HV *, int);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#ifdef DEBUG_CLONE
|
|
Packit |
8df772 |
#define TRACEME(a) printf("%s:%d: ",__FUNCTION__, __LINE__) && printf a;
|
|
Packit |
8df772 |
#else
|
|
Packit |
8df772 |
#define TRACEME(a)
|
|
Packit |
8df772 |
#endif
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
static SV *
|
|
Packit |
8df772 |
hv_clone (SV * ref, SV * target, HV* hseen, int depth)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
HV *clone = (HV *) target;
|
|
Packit |
8df772 |
HV *self = (HV *) ref;
|
|
Packit |
8df772 |
HE *next = NULL;
|
|
Packit |
8df772 |
int recur = depth ? depth - 1 : 0;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
assert(SvTYPE(ref) == SVt_PVHV);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x(%d)\n", ref, SvREFCNT(ref)));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
hv_iterinit (self);
|
|
Packit |
8df772 |
while ((next = hv_iternext (self)))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
SV *key = hv_iterkeysv (next);
|
|
Packit |
8df772 |
TRACEME(("clone item %s\n", SvPV_nolen(key) ));
|
|
Packit |
8df772 |
hv_store_ent (clone, key,
|
|
Packit |
8df772 |
sv_clone (hv_iterval (self, next), hseen, recur), 0);
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone)));
|
|
Packit |
8df772 |
return (SV *) clone;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
static SV *
|
|
Packit |
8df772 |
av_clone (SV * ref, SV * target, HV* hseen, int depth)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
AV *clone = (AV *) target;
|
|
Packit |
8df772 |
AV *self = (AV *) ref;
|
|
Packit |
8df772 |
SV **svp;
|
|
Packit |
8df772 |
SV *val = NULL;
|
|
Packit |
8df772 |
I32 arrlen = 0;
|
|
Packit |
8df772 |
int i = 0;
|
|
Packit |
8df772 |
int recur = depth ? depth - 1 : 0;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
assert(SvTYPE(ref) == SVt_PVAV);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x(%d)\n", ref, SvREFCNT(ref)));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
/* The following is a holdover from a very old version */
|
|
Packit |
8df772 |
/* possible cause of memory leaks */
|
|
Packit |
8df772 |
/* if ( (SvREFCNT(ref) > 1) ) */
|
|
Packit |
8df772 |
/* CLONE_STORE(ref, (SV *)clone); */
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
arrlen = av_len (self);
|
|
Packit |
8df772 |
av_extend (clone, arrlen);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
for (i = 0; i <= arrlen; i++)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
svp = av_fetch (self, i, 0);
|
|
Packit |
8df772 |
if (svp)
|
|
Packit |
8df772 |
av_store (clone, i, sv_clone (*svp, hseen, recur));
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone)));
|
|
Packit |
8df772 |
return (SV *) clone;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
static SV *
|
|
Packit |
8df772 |
rv_clone (SV * ref, HV* hseen, int depth)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
SV *clone = NULL;
|
|
Packit |
8df772 |
SV *rv = NULL;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
assert(SvROK(ref));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x(%d)\n", ref, SvREFCNT(ref)));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if (!SvROK (ref))
|
|
Packit |
8df772 |
return NULL;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if (sv_isobject (ref))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
clone = newRV_noinc(sv_clone (SvRV(ref), hseen, depth));
|
|
Packit |
8df772 |
sv_2mortal (sv_bless (clone, SvSTASH (SvRV (ref))));
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
else
|
|
Packit |
8df772 |
clone = newRV_inc(sv_clone (SvRV(ref), hseen, depth));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone)));
|
|
Packit |
8df772 |
return clone;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
static SV *
|
|
Packit |
8df772 |
sv_clone (SV * ref, HV* hseen, int depth)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
SV *clone = ref;
|
|
Packit |
8df772 |
SV **seen = NULL;
|
|
Packit |
8df772 |
UV visible;
|
|
Packit |
8df772 |
int magic_ref = 0;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if (!ref)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
TRACEME(("NULL\n"));
|
|
Packit |
8df772 |
return NULL;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
#if PERL_REVISION >= 5 && PERL_VERSION > 8
|
|
Packit |
8df772 |
/* This is a hack for perl 5.9.*, save everything */
|
|
Packit |
8df772 |
/* until I find out why mg_find is no longer working */
|
|
Packit |
8df772 |
visible = 1;
|
|
Packit |
8df772 |
#else
|
|
Packit |
8df772 |
visible = (SvREFCNT(ref) > 1) || (SvMAGICAL(ref) && mg_find(ref, '<'));
|
|
Packit |
8df772 |
#endif
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x(%d)\n", ref, SvREFCNT(ref)));
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if (depth == 0)
|
|
Packit |
8df772 |
return SvREFCNT_inc(ref);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if (visible && (seen = CLONE_FETCH(ref)))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
TRACEME(("fetch ref (0x%x)\n", ref));
|
|
Packit |
8df772 |
return SvREFCNT_inc(*seen);
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("switch: (0x%x)\n", ref));
|
|
Packit |
8df772 |
switch (SvTYPE (ref))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
case SVt_NULL: /* 0 */
|
|
Packit |
8df772 |
TRACEME(("sv_null\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case SVt_IV: /* 1 */
|
|
Packit |
8df772 |
TRACEME(("int scalar\n"));
|
|
Packit |
8df772 |
case SVt_NV: /* 2 */
|
|
Packit |
8df772 |
TRACEME(("double scalar\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
#if PERL_VERSION <= 10
|
|
Packit |
8df772 |
case SVt_RV: /* 3 */
|
|
Packit |
8df772 |
TRACEME(("ref scalar\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
#endif
|
|
Packit |
8df772 |
case SVt_PV: /* 4 */
|
|
Packit |
8df772 |
TRACEME(("string scalar\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case SVt_PVIV: /* 5 */
|
|
Packit |
8df772 |
TRACEME (("PVIV double-type\n"));
|
|
Packit |
8df772 |
case SVt_PVNV: /* 6 */
|
|
Packit |
8df772 |
TRACEME (("PVNV double-type\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case SVt_PVMG: /* 7 */
|
|
Packit |
8df772 |
TRACEME(("magic scalar\n"));
|
|
Packit |
8df772 |
clone = newSVsv (ref);
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case SVt_PVAV: /* 10 */
|
|
Packit |
8df772 |
clone = (SV *) newAV();
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case SVt_PVHV: /* 11 */
|
|
Packit |
8df772 |
clone = (SV *) newHV();
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
#if PERL_VERSION <= 8
|
|
Packit |
8df772 |
case SVt_PVBM: /* 8 */
|
|
Packit |
8df772 |
#elif PERL_VERSION >= 11
|
|
Packit |
8df772 |
case SVt_REGEXP: /* 8 */
|
|
Packit |
8df772 |
#endif
|
|
Packit |
8df772 |
case SVt_PVLV: /* 9 */
|
|
Packit |
8df772 |
case SVt_PVCV: /* 12 */
|
|
Packit |
8df772 |
case SVt_PVGV: /* 13 */
|
|
Packit |
8df772 |
case SVt_PVFM: /* 14 */
|
|
Packit |
8df772 |
case SVt_PVIO: /* 15 */
|
|
Packit |
8df772 |
TRACEME(("default: type = 0x%x\n", SvTYPE (ref)));
|
|
Packit |
8df772 |
clone = SvREFCNT_inc(ref); /* just return the ref */
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
default:
|
|
Packit |
8df772 |
croak("unknown type: 0x%x", SvTYPE(ref));
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
/**
|
|
Packit |
8df772 |
* It is *vital* that this is performed *before* recursion,
|
|
Packit |
8df772 |
* to properly handle circular references. cb 2001-02-06
|
|
Packit |
8df772 |
*/
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
if ( visible )
|
|
Packit |
8df772 |
CLONE_STORE(ref,clone);
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
/*
|
|
Packit |
8df772 |
* We'll assume (in the absence of evidence to the contrary) that A) a
|
|
Packit |
8df772 |
* tied hash/array doesn't store its elements in the usual way (i.e.
|
|
Packit |
8df772 |
* the mg->mg_object(s) take full responsibility for them) and B) that
|
|
Packit |
8df772 |
* references aren't tied.
|
|
Packit |
8df772 |
*
|
|
Packit |
8df772 |
* If theses assumptions hold, the three options below are mutually
|
|
Packit |
8df772 |
* exclusive.
|
|
Packit |
8df772 |
*
|
|
Packit |
8df772 |
* More precisely: 1 & 2 are probably mutually exclusive; 2 & 3 are
|
|
Packit |
8df772 |
* definitely mutually exclusive; we have to test 1 before giving 2
|
|
Packit |
8df772 |
* a chance; and we'll assume that 1 & 3 are mutually exclusive unless
|
|
Packit |
8df772 |
* and until we can be test-cased out of our delusion.
|
|
Packit |
8df772 |
*
|
|
Packit |
8df772 |
* chocolateboy: 2001-05-29
|
|
Packit |
8df772 |
*/
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
/* 1: TIED */
|
|
Packit |
8df772 |
if (SvMAGICAL(ref) )
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
MAGIC* mg;
|
|
Packit |
8df772 |
MGVTBL *vtable = 0;
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
for (mg = SvMAGIC(ref); mg; mg = mg->mg_moremagic)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
SV *obj = (SV *) NULL;
|
|
Packit |
8df772 |
/* we don't want to clone a qr (regexp) object */
|
|
Packit |
8df772 |
/* there are probably other types as well ... */
|
|
Packit |
8df772 |
TRACEME(("magic type: %c\n", mg->mg_type));
|
|
Packit |
8df772 |
/* Some mg_obj's can be null, don't bother cloning */
|
|
Packit |
8df772 |
if ( mg->mg_obj != NULL )
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
switch (mg->mg_type)
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
case 'r': /* PERL_MAGIC_qr */
|
|
Packit |
8df772 |
obj = mg->mg_obj;
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case 't': /* PERL_MAGIC_taint */
|
|
Packit |
8df772 |
continue;
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case '<': /* PERL_MAGIC_backref */
|
|
Packit |
8df772 |
continue;
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case '@': /* PERL_MAGIC_arylen_p */
|
|
Packit |
8df772 |
continue;
|
|
Packit |
8df772 |
break;
|
|
Packit |
8df772 |
case 'P': /* PERL_MAGIC_tied */
|
|
Packit |
8df772 |
case 'p': /* PERL_MAGIC_tiedelem */
|
|
Packit |
8df772 |
case 'q': /* PERL_MAGIC_tiedscalar */
|
|
Packit |
8df772 |
magic_ref++;
|
|
Packit |
8df772 |
/* fall through */
|
|
Packit |
8df772 |
default:
|
|
Packit |
8df772 |
obj = sv_clone(mg->mg_obj, hseen, -1);
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
} else {
|
|
Packit |
8df772 |
TRACEME(("magic object for type %c in NULL\n", mg->mg_type));
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
/* this is plain old magic, so do the same thing */
|
|
Packit |
8df772 |
sv_magic(clone,
|
|
Packit |
8df772 |
obj,
|
|
Packit |
8df772 |
mg->mg_type,
|
|
Packit |
8df772 |
mg->mg_ptr,
|
|
Packit |
8df772 |
mg->mg_len);
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
/* major kludge - why does the vtable for a qr type need to be null? */
|
|
Packit |
8df772 |
if ( (mg = mg_find(clone, 'r')) )
|
|
Packit |
8df772 |
mg->mg_virtual = (MGVTBL *) NULL;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
/* 2: HASH/ARRAY - (with 'internal' elements) */
|
|
Packit |
8df772 |
if ( magic_ref )
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
;;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
else if ( SvTYPE(ref) == SVt_PVHV )
|
|
Packit |
8df772 |
clone = hv_clone (ref, clone, hseen, depth);
|
|
Packit |
8df772 |
else if ( SvTYPE(ref) == SVt_PVAV )
|
|
Packit |
8df772 |
clone = av_clone (ref, clone, hseen, depth);
|
|
Packit |
8df772 |
/* 3: REFERENCE (inlined for speed) */
|
|
Packit |
8df772 |
else if (SvROK (ref))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone)));
|
|
Packit |
8df772 |
SvREFCNT_dec(SvRV(clone));
|
|
Packit |
8df772 |
SvRV(clone) = sv_clone (SvRV(ref), hseen, depth); /* Clone the referent */
|
|
Packit |
8df772 |
if (sv_isobject (ref))
|
|
Packit |
8df772 |
{
|
|
Packit |
8df772 |
sv_bless (clone, SvSTASH (SvRV (ref)));
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
if (SvWEAKREF(ref)) {
|
|
Packit |
8df772 |
sv_rvweaken(clone);
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
TRACEME(("clone = 0x%x(%d)\n", clone, SvREFCNT(clone)));
|
|
Packit |
8df772 |
return clone;
|
|
Packit |
8df772 |
}
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
MODULE = Clone PACKAGE = Clone
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
PROTOTYPES: ENABLE
|
|
Packit |
8df772 |
|
|
Packit |
8df772 |
void
|
|
Packit |
8df772 |
clone(self, depth=-1)
|
|
Packit |
8df772 |
SV *self
|
|
Packit |
8df772 |
int depth
|
|
Packit |
8df772 |
PREINIT:
|
|
Packit |
8df772 |
SV *clone = &PL_sv_undef;
|
|
Packit |
8df772 |
HV *hseen = newHV();
|
|
Packit |
8df772 |
PPCODE:
|
|
Packit |
8df772 |
TRACEME(("ref = 0x%x\n", self));
|
|
Packit |
8df772 |
clone = sv_clone(self, hseen, depth);
|
|
Packit |
8df772 |
hv_clear(hseen); /* Free HV */
|
|
Packit |
8df772 |
SvREFCNT_dec((SV *)hseen);
|
|
Packit |
8df772 |
EXTEND(SP,1);
|
|
Packit |
8df772 |
PUSHs(sv_2mortal(clone));
|