Ancient
view release on metacpan or search on metacpan
xs/object/object.c view on Meta::CPAN
svp = av_fetch(av, idx, 0);
if (svp && SvOK(*svp)) {
return *svp;
}
/* Follow prototype chain (slot 0) */
svp = av_fetch(av, 0, 0);
if (!svp || !SvROK(*svp) || SvTYPE(SvRV(*svp)) != SVt_PVAV) {
break;
}
av = (AV*)SvRV(*svp);
depth++;
}
if (depth >= MAX_PROTOTYPE_DEPTH) {
warn("Prototype chain too deep (max %d levels)", MAX_PROTOTYPE_DEPTH);
}
return &PL_sv_undef;
}
/* ============================================
Custom OP: property accessor (get)
============================================ */
static OP* pp_object_get(pTHX) {
dSP;
SV *obj = TOPs;
IV idx = PL_op->op_targ;
AV *av;
SV *result;
if (!SvROK(obj) || SvTYPE(SvRV(obj)) != SVt_PVAV) {
croak("Not an object");
}
av = (AV*)SvRV(obj);
result = resolve_property_chain(aTHX_ av, idx);
SETs(result);
RETURN;
}
/* ============================================
Custom OP: property accessor (set)
============================================ */
static OP* pp_object_set(pTHX) {
dSP;
SV *val = POPs;
SV *obj = TOPs;
IV idx = PL_op->op_targ;
AV *av;
MAGIC *mg;
if (!SvROK(obj) || SvTYPE(SvRV(obj)) != SVt_PVAV) {
croak("Not an object");
}
av = (AV*)SvRV(obj);
/* Check frozen/locked */
mg = get_object_magic(aTHX_ obj);
if (mg && (mg->mg_private & OBJ_FLAG_FROZEN)) {
croak("Cannot modify frozen object");
}
av_store(av, idx, newSVsv(val));
SETs(val);
RETURN;
}
/* ============================================
Custom OP: property accessor (set) with type check
Uses op_private to store type ID for inline check
============================================ */
/* Helper struct to pass both idx and meta through op */
typedef struct {
IV slot_idx;
ClassMeta *meta;
} SlotOpData;
/* Helper struct for function-style accessors (cross-class support) */
struct FuncAccessorData_s {
IV slot_idx;
ClassMeta *expected_class; /* Class this accessor expects */
IV registry_id; /* ID in g_func_accessor_registry */
};
/* Register a FuncAccessorData and return its ID */
static IV register_func_accessor_data(pTHX_ FuncAccessorData *data) {
if (g_func_accessor_count >= g_func_accessor_capacity) {
IV new_capacity = g_func_accessor_capacity ? g_func_accessor_capacity * 2 : 64;
Renew(g_func_accessor_registry, new_capacity, FuncAccessorData*);
g_func_accessor_capacity = new_capacity;
}
data->registry_id = g_func_accessor_count;
g_func_accessor_registry[g_func_accessor_count] = data;
return g_func_accessor_count++;
}
/* Look up FuncAccessorData by ID */
static FuncAccessorData* get_func_accessor_data(IV id) {
if (id < 0 || id >= g_func_accessor_count) return NULL;
return g_func_accessor_registry[id];
}
static OP* pp_object_set_typed(pTHX) {
dSP;
SV *val = POPs;
SV *obj = TOPs;
SlotOpData *data = INT2PTR(SlotOpData*, PL_op->op_targ);
IV idx = data->slot_idx;
ClassMeta *meta = data->meta;
SlotSpec *spec = meta->slots[idx];
AV *av;
MAGIC *mg;
if (!SvROK(obj) || SvTYPE(SvRV(obj)) != SVt_PVAV) {
croak("Not an object");
}
av = (AV*)SvRV(obj);
/* Check frozen/locked */
mg = get_object_magic(aTHX_ obj);
if (mg && (mg->mg_private & OBJ_FLAG_FROZEN)) {
croak("Cannot modify frozen object");
}
/* Readonly check */
if (spec->is_readonly) {
croak("Cannot modify readonly slot '%s'", spec->name);
}
/* Coercion (if callback exists) */
if (spec->has_coerce && spec->coerce_cb) {
dSP;
PUSHMARK(SP);
XPUSHs(val);
PUTBACK;
call_sv(spec->coerce_cb, G_SCALAR);
SPAGAIN;
val = POPs;
PUTBACK;
}
/* External XS type coercion (C function - fast path) */
if (spec->type_id == TYPE_CUSTOM && spec->registered && spec->registered->coerce) {
val = spec->registered->coerce(aTHX_ val);
}
/* Type check using helper (handles both C and Perl callbacks) */
if (spec->has_type) {
if (!check_slot_type(aTHX_ val, spec)) {
const char *type_name = (spec->type_id == TYPE_CUSTOM && spec->registered)
? spec->registered->name
: type_id_to_name(spec->type_id);
croak("Type constraint failed for '%s': expected %s",
spec->name, type_name);
}
}
/* Trigger callback (old, new) */
if (spec->has_trigger && spec->trigger_cb) {
SV *oldval = *av_fetch(av, idx, 0);
dSP;
PUSHMARK(SP);
XPUSHs(obj);
XPUSHs(oldval);
XPUSHs(val);
PUTBACK;
call_sv(spec->trigger_cb, G_DISCARD);
}
av_store(av, idx, newSVsv(val));
SETs(val);
RETURN;
}
/* ============================================
Call checker for accessor
============================================ */
static OP* accessor_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
xs/object/object.c view on Meta::CPAN
done:
XSRETURN_IV(depth);
}
static XS(xs_lock) {
dXSARGS;
MAGIC *mg;
if (items < 1) croak("Usage: object::lock($obj)");
mg = get_object_magic(aTHX_ ST(0));
if (!mg) mg = add_object_magic(aTHX_ ST(0));
if (mg->mg_private & OBJ_FLAG_FROZEN) {
croak("Object is frozen");
}
mg->mg_private |= OBJ_FLAG_LOCKED;
XSRETURN_EMPTY;
}
static XS(xs_unlock) {
dXSARGS;
MAGIC *mg;
if (items < 1) croak("Usage: object::unlock($obj)");
mg = get_object_magic(aTHX_ ST(0));
if (mg) {
if (mg->mg_private & OBJ_FLAG_FROZEN) {
croak("Cannot unlock frozen object");
}
mg->mg_private &= ~OBJ_FLAG_LOCKED;
}
XSRETURN_EMPTY;
}
static XS(xs_freeze) {
dXSARGS;
MAGIC *mg;
if (items < 1) croak("Usage: object::freeze($obj)");
mg = get_object_magic(aTHX_ ST(0));
if (!mg) mg = add_object_magic(aTHX_ ST(0));
mg->mg_private |= (OBJ_FLAG_FROZEN | OBJ_FLAG_LOCKED);
XSRETURN_EMPTY;
}
static XS(xs_is_frozen) {
dXSARGS;
MAGIC *mg;
if (items < 1) croak("Usage: object::is_frozen($obj)");
mg = get_object_magic(aTHX_ ST(0));
if (mg && (mg->mg_private & OBJ_FLAG_FROZEN)) {
XSRETURN_YES;
}
XSRETURN_NO;
}
static XS(xs_is_locked) {
dXSARGS;
MAGIC *mg;
if (items < 1) croak("Usage: object::is_locked($obj)");
mg = get_object_magic(aTHX_ ST(0));
if (mg && (mg->mg_private & OBJ_FLAG_LOCKED)) {
XSRETURN_YES;
}
XSRETURN_NO;
}
/* ============================================
Introspection API
============================================ */
/* object::clone($obj) - create shallow copy of object */
static XS(xs_clone) {
dXSARGS;
AV *src_av, *dst_av;
SV *src_obj, *dst_obj;
const char *class_name;
ClassMeta *meta;
IV i, len;
if (items < 1) croak("Usage: object::clone($obj) or $obj->clone()");
src_obj = ST(0);
if (!SvROK(src_obj) || SvTYPE(SvRV(src_obj)) != SVt_PVAV || !SvOBJECT(SvRV(src_obj))) {
croak("object::clone: argument is not an object");
}
src_av = (AV*)SvRV(src_obj);
/* Get class metadata from the blessed stash */
class_name = HvNAME(SvSTASH(SvRV(src_obj)));
meta = get_class_meta(aTHX_ class_name, strlen(class_name));
/* Create new AV with same size */
len = av_len(src_av);
dst_av = newAV();
av_extend(dst_av, len);
/* Shallow copy all slots */
for (i = 0; i <= len; i++) {
SV **svp = av_fetch(src_av, i, 0);
if (svp && SvOK(*svp)) {
av_store(dst_av, i, newSVsv(*svp));
} else {
av_store(dst_av, i, newSV(0));
}
}
/* Create blessed reference to same class (clone is NOT frozen/locked) */
dst_obj = newRV_noinc((SV*)dst_av);
sv_bless(dst_obj, meta ? meta->stash : SvSTASH(SvRV(src_obj)));
ST(0) = sv_2mortal(dst_obj);
XSRETURN(1);
}
/* object::properties($class) - return property names for a class */
static XS(xs_properties) {
dXSARGS;
STRLEN class_len;
const char *class_pv;
ClassMeta *meta;
IV i;
if (items < 1) croak("Usage: object::properties($class)");
class_pv = SvPV(ST(0), class_len);
meta = get_class_meta(aTHX_ class_pv, class_len);
if (!meta) {
/* Non-existent class: return empty list / 0 */
if (GIMME_V == G_ARRAY) {
XSRETURN_EMPTY;
} else {
XSRETURN_IV(0);
}
}
if (GIMME_V == G_ARRAY) {
/* List context: return property names */
IV count = meta->slot_count - 1; /* -1 because slot 0 is prototype */
SP -= items;
EXTEND(SP, count);
for (i = 1; i < meta->slot_count; i++) {
if (meta->idx_to_prop[i]) {
PUSHs(sv_2mortal(newSVpv(meta->idx_to_prop[i], 0)));
}
}
XSRETURN(count);
} else {
/* Scalar context: return count */
XSRETURN_IV(meta->slot_count - 1);
}
}
/* object::slot_info($class, $property) - return hashref with slot metadata */
static XS(xs_slot_info) {
dXSARGS;
STRLEN class_len, prop_len;
const char *class_pv, *prop_pv;
ClassMeta *meta;
SV **idx_svp;
IV idx;
SlotSpec *spec;
HV *info;
if (items < 2) croak("Usage: object::slot_info($class, $property)");
xs/object/object.c view on Meta::CPAN
meta->singleton_instance = NULL;
/* Install instance() class method */
snprintf(full_name, sizeof(full_name), "%s::instance", class_pv);
instance_cv = newXS(full_name, xs_singleton_instance, __FILE__);
CvXSUBANY(instance_cv).any_iv = PTR2IV(meta);
XSRETURN_EMPTY;
}
/* ============================================
Boot
============================================ */
XS_EXTERNAL(boot_object) {
dXSBOOTARGSXSAPIVERCHK;
PERL_UNUSED_VAR(items);
/* Register custom ops */
XopENTRY_set(&object_new_xop, xop_name, "object_new");
XopENTRY_set(&object_new_xop, xop_desc, "object constructor");
Perl_custom_op_register(aTHX_ pp_object_new, &object_new_xop);
XopENTRY_set(&object_get_xop, xop_name, "object_get");
XopENTRY_set(&object_get_xop, xop_desc, "object property get");
Perl_custom_op_register(aTHX_ pp_object_get, &object_get_xop);
XopENTRY_set(&object_set_xop, xop_name, "object_set");
XopENTRY_set(&object_set_xop, xop_desc, "object property set");
Perl_custom_op_register(aTHX_ pp_object_set, &object_set_xop);
XopENTRY_set(&object_set_typed_xop, xop_name, "object_set_typed");
XopENTRY_set(&object_set_typed_xop, xop_desc, "object property set with type check");
Perl_custom_op_register(aTHX_ pp_object_set_typed, &object_set_typed_xop);
XopENTRY_set(&object_func_get_xop, xop_name, "object_func_get");
XopENTRY_set(&object_func_get_xop, xop_desc, "object function-style get");
Perl_custom_op_register(aTHX_ pp_object_func_get, &object_func_get_xop);
XopENTRY_set(&object_func_set_xop, xop_name, "object_func_set");
XopENTRY_set(&object_func_set_xop, xop_desc, "object function-style set");
Perl_custom_op_register(aTHX_ pp_object_func_set, &object_func_set_xop);
/* Initialize registries */
g_class_registry = newHV();
g_type_registry = newHV();
/* Install XS functions */
newXS("object::define", xs_define, __FILE__);
newXS("object::import_accessors", xs_import_accessors, __FILE__);
newXS("object::import_accessor", xs_import_accessor, __FILE__);
newXS("object::prototype", xs_prototype, __FILE__);
newXS("object::set_prototype", xs_set_prototype, __FILE__);
newXS("object::prototype_chain", xs_prototype_chain, __FILE__);
newXS("object::has_own_property", xs_has_own_property, __FILE__);
newXS("object::prototype_depth", xs_prototype_depth, __FILE__);
newXS("object::lock", xs_lock, __FILE__);
newXS("object::unlock", xs_unlock, __FILE__);
newXS("object::freeze", xs_freeze, __FILE__);
newXS("object::is_frozen", xs_is_frozen, __FILE__);
newXS("object::is_locked", xs_is_locked, __FILE__);
/* Introspection API */
newXS("object::clone", xs_clone, __FILE__);
newXS("object::properties", xs_properties, __FILE__);
newXS("object::slot_info", xs_slot_info, __FILE__);
/* Type registry API */
newXS("object::register_type", xs_register_type, __FILE__);
newXS("object::has_type", xs_has_type, __FILE__);
newXS("object::list_types", xs_list_types, __FILE__);
/* Singleton support */
newXS("object::singleton", xs_singleton, __FILE__);
/* Role API */
newXS("object::role", xs_role, __FILE__);
newXS("object::requires", xs_requires, __FILE__);
newXS("object::with", xs_with, __FILE__);
newXS("object::does", xs_does, __FILE__);
/* Method modifier API */
newXS("object::before", xs_before, __FILE__);
newXS("object::after", xs_after, __FILE__);
newXS("object::around", xs_around, __FILE__);
/* Register cleanup for global destruction */
Perl_call_atexit(aTHX_ object_cleanup_globals, NULL);
Perl_xs_boot_epilog(aTHX_ ax);
}
( run in 0.741 second using v1.01-cache-2.11-cpan-df04353d9ac )