Object-Pad-SlotAttr-Isa

 view release on metacpan or  search on metacpan

lib/Object/Pad/SlotAttr/Isa.xs  view on Meta::CPAN

/*  You may distribute under the terms of either the GNU General Public License
 *  or the Artistic License (the same terms as Perl itself)
 *
 *  (C) Paul Evans, 2021 -- leonerd@leonerd.org.uk
 */
#define PERL_NO_GET_CONTEXT

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "object_pad.h"

struct Data {
  unsigned int is_weak : 1;
  SV *slotname;
  SV *classname;
};

static int magic_set(pTHX_ SV *sv, MAGIC *mg)
{
  struct Data *data = (struct Data *)mg->mg_ptr;
  SV *savesv = mg->mg_obj;

  bool ok = sv_derived_from_sv(sv, data->classname, 0);

  if(ok) {
    sv_setsv(savesv, sv);
    if(data->is_weak)
      sv_rvweaken(savesv);
    return 1;
  }

  /* Restore last known-good value */
  sv_setsv_nomg(sv, savesv);
  if(data->is_weak)
    sv_rvweaken(sv);

  croak("Slot %" SVf " requires an object of type %" SVf,
    SVfARG(data->slotname), SVfARG(data->classname));

  return 1;
}

static const MGVTBL vtbl = {
  .svt_set = &magic_set,
};

static bool isa_apply(pTHX_ SlotMeta *slotmeta, SV *value, SV **hookdata_ptr, void *_funcdata)
{
  struct Data *data;
  Newx(data, 1, struct Data);

  data->is_weak   = false;
  data->slotname  = SvREFCNT_inc(mop_slot_get_name(slotmeta));
  data->classname = SvREFCNT_inc(value);

  *hookdata_ptr = (SV *)data;

  return TRUE;
}

static void isa_seal(pTHX_ SlotMeta *slotmeta, SV *hookdata, void *_funcdata)
{
  struct Data *data = (struct Data *)hookdata;

  if(mop_slot_get_attribute(slotmeta, "weak"))
    data->is_weak = true;
}

static void isa_post_initslot(pTHX_ SlotMeta *slotmeta, SV *hookdata, void *_funcdata, SV *slot)
{
  sv_magicext(slot, newSV(0), PERL_MAGIC_ext, &vtbl, (char *)hookdata, 0);
}

static const struct SlotHookFuncs isa_hooks = {
  .ver   = OBJECTPAD_ABIVERSION,
  .flags = OBJECTPAD_FLAG_ATTR_MUST_VALUE,
  .permit_hintkey = "Object::Pad::SlotAttr::Isa/Isa",

  .apply         = &isa_apply,
  .seal_slot     = &isa_seal,
  .post_initslot = &isa_post_initslot,
};

MODULE = Object::Pad::SlotAttr::Isa    PACKAGE = Object::Pad::SlotAttr::Isa

BOOT:
  register_slot_attribute("Isa", &isa_hooks, NULL);



( run in 1.290 second using v1.01-cache-2.11-cpan-7fcb06a456a )