SV *
name(FieldMeta *self)
  ALIAS:
    name  = 0
    sigil = 1
    class = 2
  CODE:
    switch(ix) {
      case 0:
        RETVAL = SvREFCNT_inc(self->name);
        break;
      case 1:
        RETVAL = newSVpvn(SvPVX(self->name), 1);
        break;
      case 2:
        RETVAL = newSV(0);
        sv_setref_uv(RETVAL, "Object::Pad::MOP::Class", PTR2UV(self->class));
        break;

      default: RETVAL = NULL;
    }
  OUTPUT:
    RETVAL

void
value(FieldMeta *self, obj)
    SV *obj
  PPCODE:
  {
    SV *objrv;

    if(!SvROK(obj) || !SvOBJECT(objrv = SvRV(obj)))
      croak("Cannot fetch field value of a non-instance");

    SV *value = get_obj_fieldsv(obj, self);

    /* We must prevent caller from assigning to non-scalar fields, in case
     * they break the SvTYPE of the value. We can't cancel the CvLVALUE but we
     * can yield a READONLY value in this case */
    if(SvPV_nolen(self->name)[0] != '$') {
      value = sv_mortalcopy(value);
      SvREADONLY_on(value);
    }

    /* stack does not contribute SvREFCNT */
    ST(0) = value;
    XSRETURN(1);
  }

bool
has_attribute(FieldMeta *self, name)
    SV *name
  CODE:
  {
    const struct FieldHook *hook = mop_field_get_attribute(self, SvPV_nolen(name));
    RETVAL = !!hook;
  }
  OUTPUT:
    RETVAL

SV *
get_attribute_value(FieldMeta *self, name)
    SV *name
  CODE:
  {
    const struct FieldHook *hook = mop_field_get_attribute(self, SvPV_nolen(name));
    if(!hook)
      croak("Field does not have an attribute called %" SVf, SVfARG(name));

    RETVAL = newSVsv(hook->attrdata);
  }
  OUTPUT:
    RETVAL

void
get_attribute_values(FieldMeta *self, name)
    SV *name
  PPCODE:
  {
    AV *values = mop_field_get_attribute_values(self, SvPV_nolen(name));
    if(!values)
      croak("Field does not have an attribute called %" SVf, SVfARG(name));

    Size_t count = av_count(values);

    EXTEND(SP, count);
    for(Size_t i = 0; i < count; i++)
      PUSHs(SvREFCNT_inc(AvARRAY(values)[i]));

    SvREFCNT_dec(values);

    XSRETURN(count);
  }