This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #95544] Make UNIVERSAL::VERSION return $VERSION
[perl5.git] / universal.c
index 07a0aa6..c891b54 100644 (file)
@@ -21,7 +21,7 @@
  *
  * It is also used to store XS functions that need to be present in
  * miniperl for a lack of a better place to put them. It might be
- * clever to move them to seperate XS files which would then be pulled
+ * clever to move them to separate XS files which would then be pulled
  * in by some to-be-written build process.
  */
 
 #include "perliol.h" /* For the PERLIO_F_XXX */
 #endif
 
-static HV *
-S_get_isa_hash(pTHX_ HV *const stash)
-{
-    dVAR;
-    struct mro_meta *const meta = HvMROMETA(stash);
-
-    PERL_ARGS_ASSERT_GET_ISA_HASH;
-
-    if (!meta->isa) {
-       AV *const isa = mro_get_linear_isa(stash);
-       if (!meta->isa) {
-           HV *const isa_hash = newHV();
-           /* Linearisation didn't build it for us, so do it here.  */
-           SV *const *svp = AvARRAY(isa);
-           SV *const *const svp_end = svp + AvFILLp(isa) + 1;
-           const HEK *const canon_name = HvNAME_HEK(stash);
-
-           while (svp < svp_end) {
-               (void) hv_store_ent(isa_hash, *svp++, &PL_sv_undef, 0);
-           }
-
-           (void) hv_common(isa_hash, NULL, HEK_KEY(canon_name),
-                            HEK_LEN(canon_name), HEK_FLAGS(canon_name),
-                            HV_FETCH_ISSTORE, &PL_sv_undef,
-                            HEK_HASH(canon_name));
-           (void) hv_store(isa_hash, "UNIVERSAL", 9, &PL_sv_undef, 0);
-
-           SvREADONLY_on(isa_hash);
-
-           meta->isa = isa_hash;
-       }
-    }
-    return meta->isa;
-}
-
 /*
  * Contributed by Graham Barr  <Graham.Barr@tiuk.ti.com>
  * The main guts of traverse_isa was actually copied from gv_fetchmeth
@@ -78,12 +43,17 @@ S_isa_lookup(pTHX_ HV *stash, const char * const name)
 {
     dVAR;
     const struct mro_meta *const meta = HvMROMETA(stash);
-    HV *const isa = meta->isa ? meta->isa : S_get_isa_hash(aTHX_ stash);
+    HV *isa = meta->isa;
     STRLEN len = strlen(name);
     const HV *our_stash;
 
     PERL_ARGS_ASSERT_ISA_LOOKUP;
 
+    if (!isa) {
+       (void)mro_get_linear_isa(stash);
+       isa = meta->isa;
+    }
+
     if (hv_common(isa, NULL, name, len, 0 /* No "UTF-8" flag possible with only
                                             a char * argument*/,
                  HV_FETCH_ISEXISTS, NULL, 0)) {
@@ -92,11 +62,13 @@ S_isa_lookup(pTHX_ HV *stash, const char * const name)
     }
 
     /* A stash/class can go by many names (ie. User == main::User), so 
-       we use the name in the stash itself, which is canonical.  */
+       we use the HvENAME in the stash itself, which is canonical, falling
+       back to HvNAME if necessary.  */
     our_stash = gv_stashpvn(name, len, 0);
 
     if (our_stash) {
-       HEK *const canon_name = HvNAME_HEK(our_stash);
+       HEK *canon_name = HvENAME_HEK(our_stash);
+       if (!canon_name) canon_name = HvNAME_HEK(our_stash);
 
        if (hv_common(isa, NULL, HEK_KEY(canon_name), HEK_LEN(canon_name),
                      HEK_FLAGS(canon_name),
@@ -339,6 +311,7 @@ XS(XS_UNIVERSAL_VERSION)
     GV **gvp;
     GV *gv;
     SV *sv;
+    SV *ret;
     const char *undef;
     PERL_UNUSED_ARG(cv);
 
@@ -355,15 +328,12 @@ XS(XS_UNIVERSAL_VERSION)
     gvp = pkg ? (GV**)hv_fetchs(pkg, "VERSION", FALSE) : NULL;
 
     if (gvp && isGV(gv = *gvp) && (sv = GvSV(gv)) && SvOK(sv)) {
-        SV * const nsv = sv_newmortal();
-        sv_setsv(nsv, sv);
-        sv = nsv;
-       if ( !sv_derived_from(sv, "version"))
-           upg_version(sv, FALSE);
+        ret = sv_newmortal();
+        sv_setsv(ret, sv);
         undef = NULL;
     }
     else {
-        sv = &PL_sv_undef;
+        sv = ret = &PL_sv_undef;
         undef = "(undef)";
     }
 
@@ -383,6 +353,9 @@ XS(XS_UNIVERSAL_VERSION)
             }
        }
 
+       if ( !sv_derived_from(sv, "version"))
+           upg_version(sv, FALSE);
+
        if ( !sv_derived_from(req, "version")) {
            /* req may very well be R/O, so create a new object */
            req = sv_2mortal( new_version(req) );
@@ -404,11 +377,7 @@ XS(XS_UNIVERSAL_VERSION)
 
     }
 
-    if ( SvOK(sv) && sv_derived_from(sv, "version") ) {
-       ST(0) = sv_2mortal(vstringify(sv));
-    } else {
-       ST(0) = sv;
-    }
+    ST(0) = ret;
 
     XSRETURN(1);
 }
@@ -723,9 +692,10 @@ XS(XS_utf8_decode)
        croak_xs_usage(cv, "sv");
     else {
        SV * const sv = ST(0);
-       const bool RETVAL = sv_utf8_decode(sv);
+       bool RETVAL;
+       if (SvIsCOW(sv)) sv_force_normal(sv);
+       RETVAL = sv_utf8_decode(sv);
        ST(0) = boolSV(RETVAL);
-       sv_2mortal(ST(0));
     }
     XSRETURN(1);
 }
@@ -759,7 +729,6 @@ XS(XS_utf8_downgrade)
         const bool RETVAL = sv_utf8_downgrade(sv, failok);
 
        ST(0) = boolSV(RETVAL);
-       sv_2mortal(ST(0));
     }
     XSRETURN(1);
 }
@@ -794,23 +763,31 @@ XS(XS_Internals_SvREADONLY)       /* This is dangerous stuff. */
 {
     dVAR;
     dXSARGS;
-    SV * const sv = SvRV(ST(0));
+    SV * const svz = ST(0);
+    SV * sv;
     PERL_UNUSED_ARG(cv);
 
+    /* [perl #77776] - called as &foo() not foo() */
+    if (!SvROK(svz))
+        croak_xs_usage(cv, "SCALAR[, ON]");
+
+    sv = SvRV(svz);
+
     if (items == 1) {
-        if (SvREADONLY(sv))
+        if (SvREADONLY(sv) && !SvIsCOW(sv))
             XSRETURN_YES;
         else
             XSRETURN_NO;
     }
     else if (items == 2) {
        if (SvTRUE(ST(1))) {
+           if (SvIsCOW(sv)) sv_force_normal(sv);
            SvREADONLY_on(sv);
            XSRETURN_YES;
        }
        else {
            /* I hope you really know what you are doing. */
-           SvREADONLY_off(sv);
+           if (!SvIsCOW(sv)) SvREADONLY_off(sv);
            XSRETURN_NO;
        }
     }
@@ -821,9 +798,16 @@ XS(XS_Internals_SvREFCNT)  /* This is dangerous stuff. */
 {
     dVAR;
     dXSARGS;
-    SV * const sv = SvRV(ST(0));
+    SV * const svz = ST(0);
+    SV * sv;
     PERL_UNUSED_ARG(cv);
 
+    /* [perl #77776] - called as &foo() not foo() */
+    if (!SvROK(svz))
+        croak_xs_usage(cv, "SCALAR[, REFCOUNT]");
+
+    sv = SvRV(svz);
+
     if (items == 1)
         XSRETURN_IV(SvREFCNT(sv) - 1); /* Minus the ref created for us. */
     else if (items == 2) {
@@ -839,7 +823,7 @@ XS(XS_Internals_hv_clear_placehold)
     dVAR;
     dXSARGS;
 
-    if (items != 1)
+    if (items != 1 || !SvROK(ST(0)))
        croak_xs_usage(cv, "hv");
     else {
        HV * const hv = MUTABLE_HV(SvRV(ST(0)));
@@ -1024,8 +1008,6 @@ XS(XS_re_is_regexp)
     if (items != 1)
        croak_xs_usage(cv, "sv");
 
-    SP -= items;
-
     if (SvRXOK(ST(0))) {
         XSRETURN_YES;
     } else {
@@ -1044,6 +1026,7 @@ XS(XS_re_regnames_count)
        croak_xs_usage(cv, "");
 
     SP -= items;
+    PUTBACK;
 
     if (!rx)
         XSRETURN_UNDEF;
@@ -1051,14 +1034,8 @@ XS(XS_re_regnames_count)
     ret = CALLREG_NAMED_BUFF_COUNT(rx);
 
     SPAGAIN;
-
-    if (ret) {
-        mXPUSHs(ret);
-        PUTBACK;
-        return;
-    } else {
-        XSRETURN_UNDEF;
-    }
+    PUSHs(ret ? sv_2mortal(ret) : &PL_sv_undef);
+    XSRETURN(1);
 }
 
 XS(XS_re_regname)
@@ -1073,6 +1050,7 @@ XS(XS_re_regname)
        croak_xs_usage(cv, "name[, all ]");
 
     SP -= items;
+    PUTBACK;
 
     rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
 
@@ -1086,11 +1064,9 @@ XS(XS_re_regname)
     }
     ret = CALLREG_NAMED_BUFF_FETCH(rx, ST(0), (flags | RXapif_REGNAME));
 
-    if (ret) {
-        mXPUSHs(ret);
-        XSRETURN(1);
-    }
-    XSRETURN_UNDEF;    
+    SPAGAIN;
+    PUSHs(ret ? sv_2mortal(ret) : &PL_sv_undef);
+    XSRETURN(1);
 }
 
 
@@ -1121,13 +1097,12 @@ XS(XS_re_regnames)
     }
 
     SP -= items;
+    PUTBACK;
 
     ret = CALLREG_NAMED_BUFF_ALL(rx, (flags | RXapif_REGNAMES));
 
     SPAGAIN;
 
-    SP -= items;
-
     if (!ret)
         XSRETURN_UNDEF;
 
@@ -1167,7 +1142,7 @@ XS(XS_re_regexp_pattern)
        Otherwise in list context it returns the pattern and the
        modifiers, in scalar context it returns the pattern just as it
        would if the qr// was stringified normally, regardless as
-       to the class of the variable and any strigification overloads
+       to the class of the variable and any stringification overloads
        on the object.
     */
 
@@ -1175,19 +1150,29 @@ XS(XS_re_regexp_pattern)
     {
         /* Houston, we have a regex! */
         SV *pattern;
-        STRLEN left = 0;
-        char reflags[sizeof(INT_PAT_MODS)];
 
         if ( GIMME_V == G_ARRAY ) {
+           STRLEN left = 0;
+           char reflags[sizeof(INT_PAT_MODS) + MAX_CHARSET_NAME_LENGTH];
+            const char *fptr;
+            char ch;
+            U16 match_flags;
+
             /*
                we are in list context so stringify
                the modifiers that apply. We ignore "negative
-               modifiers" in this scenario.
+               modifiers" in this scenario, and the default character set
             */
 
-            const char *fptr = INT_PAT_MODS;
-            char ch;
-            U16 match_flags = (U16)((RX_EXTFLAGS(re) & PMf_COMPILETIME)
+           if (get_regex_charset(RX_EXTFLAGS(re)) != REGEX_DEPENDS_CHARSET) {
+               STRLEN len;
+               const char* const name = get_regex_charset_name(RX_EXTFLAGS(re),
+                                                               &len);
+               Copy(name, reflags + left, len, char);
+               left += len;
+           }
+            fptr = INT_PAT_MODS;
+            match_flags = (U16)((RX_EXTFLAGS(re) & RXf_PMf_COMPILETIME)
                                     >> RXf_PMf_STD_PMMOD_SHIFT);
 
             while((ch = *fptr++)) {
@@ -1239,239 +1224,6 @@ XS(XS_re_regexp_pattern)
     /* NOT-REACHED */
 }
 
-XS(XS_Tie_Hash_NamedCapture_FETCH)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-    SV * ret;
-
-    if (items != 2)
-       croak_xs_usage(cv, "$key, $flags");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        XSRETURN_UNDEF;
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    ret = CALLREG_NAMED_BUFF_FETCH(rx, ST(1), flags);
-
-    SPAGAIN;
-
-    if (ret) {
-        mXPUSHs(ret);
-        PUTBACK;
-        return;
-    }
-    XSRETURN_UNDEF;
-}
-
-XS(XS_Tie_Hash_NamedCapture_STORE)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-
-    if (items != 3)
-       croak_xs_usage(cv, "$key, $value, $flags");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0))) {
-        if (!PL_localizing)
-            Perl_croak_no_modify(aTHX);
-        else
-            XSRETURN_UNDEF;
-    }
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    CALLREG_NAMED_BUFF_STORE(rx,ST(1), ST(2), flags);
-}
-
-XS(XS_Tie_Hash_NamedCapture_DELETE)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-    U32 flags;
-
-    if (items != 2)
-       croak_xs_usage(cv, "$key, $flags");
-
-    if (!rx || !SvROK(ST(0)))
-        Perl_croak_no_modify(aTHX);
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    CALLREG_NAMED_BUFF_DELETE(rx, ST(1), flags);
-}
-
-XS(XS_Tie_Hash_NamedCapture_CLEAR)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-
-    if (items != 1)
-       croak_xs_usage(cv, "$flags");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        Perl_croak_no_modify(aTHX);
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    CALLREG_NAMED_BUFF_CLEAR(rx, flags);
-}
-
-XS(XS_Tie_Hash_NamedCapture_EXISTS)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-    SV * ret;
-
-    if (items != 2)
-       croak_xs_usage(cv, "$key, $flags");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        XSRETURN_UNDEF;
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    ret = CALLREG_NAMED_BUFF_EXISTS(rx, ST(1), flags);
-
-    SPAGAIN;
-
-       XPUSHs(ret);
-       PUTBACK;
-       return;
-}
-
-XS(XS_Tie_Hash_NamedCapture_FIRSTK)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-    SV * ret;
-
-    if (items != 1)
-       croak_xs_usage(cv, "");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        XSRETURN_UNDEF;
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    ret = CALLREG_NAMED_BUFF_FIRSTKEY(rx, flags);
-
-    SPAGAIN;
-
-    if (ret) {
-        mXPUSHs(ret);
-        PUTBACK;
-    } else {
-        XSRETURN_UNDEF;
-    }
-
-}
-
-XS(XS_Tie_Hash_NamedCapture_NEXTK)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-    SV * ret;
-
-    if (items != 2)
-       croak_xs_usage(cv, "$lastkey");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        XSRETURN_UNDEF;
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    ret = CALLREG_NAMED_BUFF_NEXTKEY(rx, ST(1), flags);
-
-    SPAGAIN;
-
-    if (ret) {
-        mXPUSHs(ret);
-    } else {
-        XSRETURN_UNDEF;
-    }  
-    PUTBACK;
-}
-
-XS(XS_Tie_Hash_NamedCapture_SCALAR)
-{
-    dVAR;
-    dXSARGS;
-    REGEXP * rx;
-    U32 flags;
-    SV * ret;
-
-    if (items != 1)
-       croak_xs_usage(cv, "");
-
-    rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
-
-    if (!rx || !SvROK(ST(0)))
-        XSRETURN_UNDEF;
-
-    SP -= items;
-
-    flags = (U32)INT2PTR(IV,SvIV(SvRV(MUTABLE_SV(ST(0)))));
-    ret = CALLREG_NAMED_BUFF_SCALAR(rx, flags);
-
-    SPAGAIN;
-
-    if (ret) {
-        mXPUSHs(ret);
-        PUTBACK;
-        return;
-    } else {
-        XSRETURN_UNDEF;
-    }
-}
-
-XS(XS_Tie_Hash_NamedCapture_flags)
-{
-    dVAR;
-    dXSARGS;
-
-    if (items != 0)
-       croak_xs_usage(cv, "");
-
-       mXPUSHu(RXapif_ONE);
-       mXPUSHu(RXapif_ALL);
-       PUTBACK;
-       return;
-}
-
 struct xsub_details {
     const char *name;
     XSUBADDR_t xsub;
@@ -1522,15 +1274,6 @@ struct xsub_details details[] = {
     {"re::regnames", XS_re_regnames, ";$"},
     {"re::regnames_count", XS_re_regnames_count, ""},
     {"re::regexp_pattern", XS_re_regexp_pattern, "$"},
-    {"Tie::Hash::NamedCapture::FETCH", XS_Tie_Hash_NamedCapture_FETCH, NULL},
-    {"Tie::Hash::NamedCapture::STORE", XS_Tie_Hash_NamedCapture_STORE, NULL},
-    {"Tie::Hash::NamedCapture::DELETE", XS_Tie_Hash_NamedCapture_DELETE, NULL},
-    {"Tie::Hash::NamedCapture::CLEAR", XS_Tie_Hash_NamedCapture_CLEAR, NULL},
-    {"Tie::Hash::NamedCapture::EXISTS", XS_Tie_Hash_NamedCapture_EXISTS, NULL},
-    {"Tie::Hash::NamedCapture::FIRSTKEY", XS_Tie_Hash_NamedCapture_FIRSTK, NULL},
-    {"Tie::Hash::NamedCapture::NEXTKEY", XS_Tie_Hash_NamedCapture_NEXTK, NULL},
-    {"Tie::Hash::NamedCapture::SCALAR", XS_Tie_Hash_NamedCapture_SCALAR, NULL},
-    {"Tie::Hash::NamedCapture::flags", XS_Tie_Hash_NamedCapture_flags, NULL}
 };
 
 void