Ancient

 view release on metacpan or  search on metacpan

xs/object/object.c  view on Meta::CPAN

    if (!SvROK(obj) || SvTYPE(SvRV(obj)) != SVt_PVAV) {
        croak("Not an object");
    }
    av = (AV*)SvRV(obj);


    /* Validate object is of expected class (stash pointer comparison) */
    if (data->expected_class) {
        if (SvSTASH(SvRV(obj)) != data->expected_class->stash) {
            croak("Expected object of class '%s', got '%s'",
                  data->expected_class->class_name,
                  HvNAME(SvSTASH(SvRV(obj))));
        }
    }

    /* Bounds check */
    if (idx > av_len(av)) {
        SETs(&PL_sv_undef);
        RETURN;
    }

    svp = av_fetch(av, idx, 0);
    SETs((svp && SvOK(*svp)) ? *svp : &PL_sv_undef);
    RETURN;
}

static OP* pp_object_func_set(pTHX) {
    dSP;
    SV *val = POPs;  /* Pop value first */
    SV *obj = TOPs;  /* Object left on stack */
    FuncAccessorData *data = get_func_accessor_data(PL_op->op_targ);
    IV idx;
    AV *av;

    if (!data) {
        croak("Internal error: invalid accessor data");
    }
    idx = data->slot_idx;

    if (!SvROK(obj) || SvTYPE(SvRV(obj)) != SVt_PVAV) {
        croak("Not an object");
    }
    av = (AV*)SvRV(obj);

    /* Validate object is of expected class (stash pointer comparison) */
    if (data->expected_class && SvSTASH(SvRV(obj)) != data->expected_class->stash) {
        croak("Expected object of class '%s', got '%s'",
              data->expected_class->class_name,
              HvNAME(SvSTASH(SvRV(obj))));
    }

    av_store(av, idx, newSVsv(val));

    SETs(val);  /* Replace object with value */
    RETURN;
}

/* Check if an op is "simple" (can be safely used in optimized accessor) */
OBJECT_INLINE bool is_simple_op(OP *op) {
    if (!op) return false;
    /* Simple ops: pad variables, constants, global variables */
    switch (op->op_type) {
        case OP_PADSV:    /* $lexical */
        case OP_CONST:    /* literal value */
        case OP_GV:       /* *glob */
        case OP_GVSV:     /* $global */
        case OP_AELEMFAST:/* $array[const] */
#if defined(OP_AELEMFAST_LEX) && OP_AELEMFAST_LEX != OP_AELEMFAST
        case OP_AELEMFAST_LEX:
#endif
        case OP_NULL:     /* Often wraps simple ops */
            return true;
        default:
            return false;
    }
}

/* Call checker for function-style accessor: name($obj) or name($obj, $val) */
static OP* func_accessor_call_checker(pTHX_ OP *entersubop, GV *namegv, SV *ckobj) {
    IV registry_id = SvIV(ckobj);
    FuncAccessorData *data = get_func_accessor_data(registry_id);
    OP *pushop, *cvop, *objop, *argop, *valop;
    OP *newop;

    PERL_UNUSED_ARG(namegv);

    if (!data) {
        return entersubop;  /* Fallback if data not found */
    }

    pushop = cUNOPx(entersubop)->op_first;
    if (!OpHAS_SIBLING(pushop)) {
        pushop = cUNOPx(pushop)->op_first;
    }

    /* Walk the op tree like the method-style accessor checker */
    objop = OpSIBLING(pushop);
    cvop = objop;
    argop = objop;
    while (OpHAS_SIBLING(cvop)) {
        argop = cvop;
        cvop = OpSIBLING(cvop);
    }

    /* Check if there's an argument after obj (setter call) */
    if (argop != objop) {
        /* Setter: name($obj, $val) - let XS fallback handle all setters
         * Force scalar context to prevent void context optimization */
        return op_contextualize(entersubop, G_SCALAR);
    }

    /* Getter: name($obj) - optimize only if objop is simple */
    if (!is_simple_op(objop)) {
        return entersubop;
    }

    OpMORESIB_set(pushop, cvop);
    OpLASTSIB_set(objop, NULL);

    newop = newUNOP(OP_CUSTOM, 0, objop);
    newop->op_ppaddr = pp_object_func_get;
    newop->op_targ = data->registry_id;  /* Store registry ID, not pointer */

    op_free(entersubop);
    return newop;
}

xs/object/object.c  view on Meta::CPAN

        XSRETURN_UNDEF;
    }

    /* Look up property index - O(1) hash lookup */
    idx_svp = hv_fetch(meta->prop_to_idx, prop_pv, prop_len, 0);
    if (!idx_svp) {
        XSRETURN_UNDEF;
    }
    idx = SvIV(*idx_svp);

    /* Build result hashref */
    info = newHV();

    /* Basic info always present */
    hv_store(info, "name", 4, newSVpv(prop_pv, prop_len), 0);
    hv_store(info, "index", 5, newSViv(idx), 0);

    /* Get slot spec if available */
    spec = (meta->slots && idx < meta->slot_count) ? meta->slots[idx] : NULL;

    if (spec && spec->has_type) {
        const char *type_name;
        if (spec->type_id == TYPE_CUSTOM && spec->registered) {
            type_name = spec->registered->name;
        } else {
            type_name = type_id_to_name(spec->type_id);
        }
        hv_store(info, "type", 4, newSVpv(type_name, 0), 0);
    }

    /* Boolean flags */
    hv_store(info, "is_required", 11, newSViv(spec ? spec->is_required : 0), 0);
    hv_store(info, "is_readonly", 11, newSViv(spec ? spec->is_readonly : 0), 0);
    hv_store(info, "is_lazy", 7, newSViv(spec ? spec->is_lazy : 0), 0);
    hv_store(info, "has_default", 11, newSViv(spec ? spec->has_default : 0), 0);
    hv_store(info, "has_trigger", 11, newSViv(spec ? spec->has_trigger : 0), 0);
    hv_store(info, "has_coerce", 10, newSViv(spec ? spec->has_coerce : 0), 0);
    hv_store(info, "has_builder", 11, newSViv(spec ? spec->has_builder : 0), 0);
    hv_store(info, "has_clearer", 11, newSViv(spec ? spec->has_clearer : 0), 0);
    hv_store(info, "has_predicate", 13, newSViv(spec ? spec->has_predicate : 0), 0);
    hv_store(info, "has_type", 8, newSViv(spec ? spec->has_type : 0), 0);

    /* Default value (if present) */
    if (spec && spec->has_default && spec->default_sv) {
        hv_store(info, "default", 7, newSVsv(spec->default_sv), 0);
    }

    /* Builder method name */
    if (spec && spec->has_builder && spec->builder_name) {
        hv_store(info, "builder", 7, newSVsv(spec->builder_name), 0);
    }

    ST(0) = sv_2mortal(newRV_noinc((SV*)info));
    XSRETURN(1);
}

/* ============================================
   Global cleanup
   ============================================ */

/* Cleanup during global destruction */
static void object_cleanup_globals(pTHX_ void *data) {
    PERL_UNUSED_ARG(data);

    /* During global destruction, just NULL out pointers.
     * Perl handles SV cleanup. Trying to free them ourselves
     * can cause crashes due to destruction order. */
    if (PL_dirty) {
        g_type_registry = NULL;
        g_class_registry = NULL;
        g_func_accessor_registry = NULL;
        return;
    }

    /* Normal cleanup - not during global destruction */
    /* Note: Full cleanup omitted for simplicity; Perl handles SV refcounts */
    g_type_registry = NULL;
    g_class_registry = NULL;
    g_func_accessor_registry = NULL;
}

/* ============================================
   Type Registry API
   ============================================ */

/* C-level registration for external XS modules (called from BOOT)
   This is the fast path - no Perl callback overhead */
PERL_CALLCONV void object_register_type_xs(pTHX_ const char *name, 
                                           ObjectTypeCheckFunc check,
                                           ObjectTypeCoerceFunc coerce) {
    RegisteredType *type;
    STRLEN name_len = strlen(name);
    
    if (!g_type_registry) {
        g_type_registry = newHV();
    }
    
    /* Check if already registered */
    SV **existing = hv_fetch(g_type_registry, name, name_len, 0);
    if (existing) {
        croak("Type '%s' is already registered", name);
    }
    
    Newxz(type, 1, RegisteredType);
    Newx(type->name, name_len + 1, char);
    Copy(name, type->name, name_len, char);
    type->name[name_len] = '\0';
    
    type->check = check;    /* Direct C function pointer - no Perl overhead */
    type->coerce = coerce;  /* Direct C function pointer - no Perl overhead */
    type->perl_check = NULL;
    type->perl_coerce = NULL;
    
    hv_store(g_type_registry, name, name_len, newSViv(PTR2IV(type)), 0);
}

/* Getter for external modules to look up a registered type */
PERL_CALLCONV RegisteredType* object_get_registered_type(pTHX_ const char *name) {
    STRLEN name_len = strlen(name);
    if (!g_type_registry) return NULL;
    
    SV **svp = hv_fetch(g_type_registry, name, name_len, 0);
    if (svp && SvIOK(*svp)) {
        return INT2PTR(RegisteredType*, SvIV(*svp));
    }
    return NULL;
}

/* object::register_type($name, $check_cb [, $coerce_cb]) */
static XS(xs_register_type) {
    dXSARGS;
    STRLEN name_len;
    const char *name;
    RegisteredType *type;
    

xs/object/object.c  view on Meta::CPAN

    
    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 1.211 second using v1.01-cache-2.11-cpan-df04353d9ac )