Blame Clone.xs

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