Medusa-XS

 view release on metacpan or  search on metacpan

lib/Medusa/XS.xs  view on Meta::CPAN

# ------------------------------------------------------------------ #
# XSUB: Clean dumper output (legacy compat - passes through to Loo)    #
# ------------------------------------------------------------------ #

SV *
clean_dumper(input)
        SV *input
    CODE:
        /* Legacy: with Loo, dump output is already clean.
         * This just returns the input for backward compatibility. */
        RETVAL = newSVsv(input);
    OUTPUT:
        RETVAL

# ------------------------------------------------------------------ #
# XSUB: SV serializer via Loo                                         #
# ------------------------------------------------------------------ #

SV *
dump_sv(value)
        SV *value
    PREINIT:
        DDCStyle style;
        HV *log_config;
        HV *options = NULL;
        int use_colour = 0;
        const char *theme = NULL;
    CODE:
        /* Read colour settings from %LOG{OPTIONS} */
        log_config = get_hv("Medusa::XS::LOG", 0);
        if (log_config) {
            SV **svp = hv_fetchs(log_config, "OPTIONS", 0);
            if (svp && SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) {
                options = (HV *)SvRV(*svp);
                use_colour = medusa_resolve_colour(aTHX_ options);
                theme = medusa_resolve_theme(aTHX_ options);
            }
        }
        medusa_loo_style_init(aTHX_ &style, use_colour, theme);
        RETVAL = medusa_dump_param(aTHX_ value, &style);
        ddc_style_destroy(aTHX_ &style);
    OUTPUT:
        RETVAL

# ------------------------------------------------------------------ #
# XSUB: Wrap a subroutine with auditing                                #
# ------------------------------------------------------------------ #

void
wrap_sub(coderef, method_name = NULL)
        SV *coderef
        SV *method_name
    PREINIT:
        CV *cv;
        CV *wrapper;
        const char *method;
        STRLEN method_len;
        const char *pkg;
        STRLEN pkg_len;
        GV *gv;
    PPCODE:
        /* Get the CV from the coderef */
        if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
            croak("wrap_sub: argument must be a code reference");
        }
        cv = (CV *)SvRV(coderef);
        
        /* Get method name from CV or argument */
        if (method_name && SvOK(method_name)) {
            method = SvPV(method_name, method_len);
        } else {
            gv = CvGV(cv);
            if (gv) {
                method = GvNAME(gv);
                method_len = GvNAMELEN(gv);
            } else {
                method = "__ANON__";
                method_len = 8;
            }
        }
        
        /* Get package name from CV */
        gv = CvGV(cv);
        if (gv && GvSTASH(gv)) {
            pkg = HvNAME(GvSTASH(gv));
            pkg_len = pkg ? strlen(pkg) : 0;
        } else {
            pkg = "main";
            pkg_len = 4;
        }
        
        /* Inject the audit wrapper */
        inject_audit_wrapper(aTHX_ cv, method, method_len, pkg, pkg_len);
        
        /* Return the wrapped CV */
        XPUSHs(coderef);
        XSRETURN(1);

bool
is_audited(coderef)
        SV *coderef
    PREINIT:
        CV *cv;
    CODE:
        if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
            RETVAL = FALSE;
        } else {
            cv = (CV *)SvRV(coderef);
            RETVAL = CV_IS_AUDITED(cv) ? TRUE : FALSE;
        }
    OUTPUT:
        RETVAL

SV *
_make_test_audit_op(method, package)
        const char *method
        const char *package
    CODE:
        /* Legacy test hook — AUDITOP struct removed, return confirmation */
        RETVAL = newSVpvf("AUDIT_CACHE@%s::%s", package, method);
    OUTPUT:
        RETVAL

# ------------------------------------------------------------------ #
# XSUB: _apply_deferred_wraps - Re-install wrappers queued on 5.10    #
# ------------------------------------------------------------------ #

void
_apply_deferred_wraps()
    PPCODE:
        medusa_check_apply_wraps(aTHX_ NULL);
        XSRETURN(0);

# ------------------------------------------------------------------ #
# XSUB: log_message - Full XS logging implementation                  #
# ------------------------------------------------------------------ #

void
log_message(...)
    PREINIT:
        HV *params;
        int i;
    PPCODE:
        /* Build params hash from @_ */
        if (items % 2 != 0) {
            croak("log_message: odd number of arguments");
        }
        params = newHV();
        for (i = 0; i < items; i += 2) {
            SV *key = ST(i);
            SV *val = ST(i+1);
            STRLEN len;
            const char *k = SvPV(key, len);
            hv_store(params, k, len, SvREFCNT_inc(val), 0);
        }
        medusa_xs_log_message(aTHX_ params);
        SvREFCNT_dec((SV *)params);
        XSRETURN(0);

# ------------------------------------------------------------------ #
# XSUB: xs_format_message - XS default FORMAT_MESSAGE                 #
# ------------------------------------------------------------------ #

SV *
xs_format_message(...)
    PREINIT:
        HV *params;
        HV *log_config;
        int i;
    CODE:
        /* Build params hash from @_ */
        if (items % 2 != 0) {
            croak("xs_format_message: odd number of arguments");
        }
        params = newHV();
        for (i = 0; i < items; i += 2) {
            SV *key = ST(i);
            SV *val = ST(i+1);
            STRLEN len;
            const char *k = SvPV(key, len);
            hv_store(params, k, len, SvREFCNT_inc(val), 0);
        }
        log_config = get_hv("Medusa::XS::LOG", 0);
        if (!log_config) {
            log_config = newHV();
        }
        RETVAL = medusa_xs_format_message(aTHX_ params, log_config);
        SvREFCNT_dec((SV *)params);
    OUTPUT:
        RETVAL

# ------------------------------------------------------------------ #
# XSUB: init_logger - Initialize the logger from LOG_INIT             #
# ------------------------------------------------------------------ #

void
init_logger()
    PREINIT:
        HV *log_config;
        SV **svp;
    PPCODE:
        log_config = get_hv("Medusa::XS::LOG", 0);
        if (!log_config) {
            XSRETURN(0);
        }
        
        /* Check if LOG already initialized */
        svp = hv_fetchs(log_config, "LOG", 0);
        if (svp && SvROK(*svp)) {
            XSRETURN(0);
        }
        
        /* Call LOG_INIT */
        svp = hv_fetchs(log_config, "LOG_INIT", 0);
        if (svp && SvROK(*svp)) {
            dSP;
            SV *log_obj;
            int count;
            
            ENTER;
            SAVETMPS;
            PUSHMARK(SP);
            PUTBACK;
            count = call_sv(*svp, G_SCALAR);
            SPAGAIN;
            
            if (count >= 1) {
                log_obj = POPs;
                if (SvROK(log_obj)) {
                    hv_stores(log_config, "LOG", SvREFCNT_inc(log_obj));
                }
            }
            
            PUTBACK;
            FREETMPS;
            LEAVE;
        }
        XSRETURN(0);

# ------------------------------------------------------------------ #
# XSUB: import - Set up calling package to use Medusa::XS             #
# ------------------------------------------------------------------ #

void
import(...)
    PREINIT:
        HV *log_config;
        const char *caller_pkg;
        HV *caller_stash;
        AV *isa;
        GV *isa_gv;
        int i;
    PPCODE:
        /* Check for odd number of args (after class name) */
        if ((items - 1) % 2 != 0) {
            croak("odd number of params passed in import");
        }
        
        /* Get caller package */
        caller_pkg = CopSTASHPV(PL_curcop);
        if (!caller_pkg) caller_pkg = "main";
        
        /* Push Medusa::XS onto caller's @ISA */
        caller_stash = gv_stashpv(caller_pkg, GV_ADD);
        isa_gv = gv_fetchpvn_flags("ISA", 3, GV_ADD, SVt_PVAV);
        if (caller_stash) {
            GV *pkg_isa = gv_fetchmethod_autoload(caller_stash, "ISA", FALSE);
            SV *isa_name = newSVpvf("%s::ISA", caller_pkg);
            AV *isa_av = get_av(SvPV_nolen(isa_name), GV_ADD);
            SvREFCNT_dec(isa_name);
            av_push(isa_av, newSVpvs("Medusa::XS"));
        }
        
        /* Process key=value pairs into %LOG (make copies of values) */
        log_config = get_hv("Medusa::XS::LOG", GV_ADD);
        for (i = 1; i < items; i += 2) {
            SV *key = ST(i);
            SV *val = ST(i + 1);
            STRLEN klen;
            const char *kstr = SvPV(key, klen);
            /* Use newSVsv to copy - stack values may be read-only */
            hv_store(log_config, kstr, klen, newSVsv(val), 0);
        }
        
        XSRETURN(0);

# ------------------------------------------------------------------ #
# XSUB: MODIFY_CODE_ATTRIBUTES - Handle :Audit attribute              #
# ------------------------------------------------------------------ #

void
MODIFY_CODE_ATTRIBUTES(pkg, coderef, ...)
        SV *pkg
        SV *coderef
    PREINIT:
        CV *cv;
        GV *gv;
        const char *method_name = "__ANON__";
        STRLEN method_len = 8;
        const char *pkg_name;
        STRLEN pkg_len;
        HV *audited;
        int i;
        bool found_audit = FALSE;
    PPCODE:
        /* Check if any attribute is "Audit" */
        for (i = 2; i < items; i++) {
            STRLEN len;
            const char *attr = SvPV(ST(i), len);
            if (len >= 5 && strncmp(attr, "Audit", 5) == 0) {
                found_audit = TRUE;
                break;
            }
        }
        
        if (!found_audit) {
            /* Return remaining attributes (none handled) */
            for (i = 2; i < items; i++) {
                XPUSHs(ST(i));
            }
            XSRETURN(items - 2);
        }
        
        /* Get the CV */
        if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
            croak("MODIFY_CODE_ATTRIBUTES: not a code reference");
        }
        cv = (CV *)SvRV(coderef);
        
        /* Initialize logger */
        {
            dSP;
            PUSHMARK(SP);
            PUTBACK;
            call_pv("Medusa::XS::init_logger", G_DISCARD);
        }
        
        /* Get method name from CV's GV */
        gv = CvGV(cv);
        if (gv) {
            method_name = GvNAME(gv);
            method_len = GvNAMELEN(gv);
        }
        
        /* Get package name */
        pkg_name = SvPV(pkg, pkg_len);
        
        /* Track in %AUDITED */
        audited = get_hv("Medusa::XS::AUDITED", GV_ADD);
        {
            char addr_key[32];
            int addr_len = snprintf(addr_key, sizeof(addr_key), "%lu", 
                                    (unsigned long)PTR2UV(cv));
            hv_store(audited, addr_key, addr_len, newSViv(1), 0);
        }
        
        /* Wrap the sub with XS auditing */
        inject_audit_wrapper(aTHX_ cv, method_name, method_len, 
                             pkg_name, pkg_len);
        
        /* Return empty list (we handled Audit, no unhandled attrs) */
        XSRETURN(0);

# ------------------------------------------------------------------ #
# XSUB: FETCH_CODE_ATTRIBUTES - Report attributes on a coderef       #
# ------------------------------------------------------------------ #

void
FETCH_CODE_ATTRIBUTES(pkg, coderef)
        SV *pkg
        SV *coderef
    PREINIT:
        CV *cv;
        HV *audited;
        bool is_audited_cv = FALSE;
    PPCODE:
        PERL_UNUSED_VAR(pkg);
        
        if (!SvROK(coderef) || SvTYPE(SvRV(coderef)) != SVt_PVCV) {
            XSRETURN(0);
        }
        cv = (CV *)SvRV(coderef);
        
        /* Check %AUDITED hash */
        audited = get_hv("Medusa::XS::AUDITED", 0);
        if (audited) {
            char addr_key[32];
            int addr_len = snprintf(addr_key, sizeof(addr_key), "%lu",
                                    (unsigned long)PTR2UV(cv));
            if (hv_exists(audited, addr_key, addr_len)) {
                is_audited_cv = TRUE;
            }
        }
        
        /* Check XS magic */
        if (!is_audited_cv && CV_IS_AUDITED(cv)) {
            is_audited_cv = TRUE;
        }
        
        if (is_audited_cv) {
            XPUSHs(sv_2mortal(newSVpvs("Audit")));
            XSRETURN(1);
        }
        
        XSRETURN(0);

# ================================================================== #
# Medusa::XS::Logger — Pure XS file logger with flock                 #
# ================================================================== #

MODULE = Medusa::XS  PACKAGE = Medusa::XS::Logger

SV *
new(pkg, ...)
        const char *pkg
    PREINIT:
        MedusaLogger *logger;
        SV *self_rv;
        SV *self_sv;
        const char *filename = "audit.log";
        STRLEN filename_len;
        HV *args;
        SV **svp;
        I32 i;
    CODE:
        /* Parse args: new(file => 'x') or new({file => 'x'}) */
        if (items == 2 && SvROK(ST(1)) && SvTYPE(SvRV(ST(1))) == SVt_PVHV) {
            args = (HV *)SvRV(ST(1));
        } else if (items > 1 && (items - 1) % 2 == 0) {
            args = newHV();
            sv_2mortal((SV *)args);
            for (i = 1; i < items; i += 2) {
                const char *key;
                STRLEN klen;
                key = SvPV(ST(i), klen);
                (void)hv_store(args, key, klen, SvREFCNT_inc(ST(i+1)), 0);



( run in 0.626 second using v1.01-cache-2.11-cpan-5511b514fd6 )