This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make utf8::encode respect magic
[perl5.git] / universal.c
index 1190e97..676c39b 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
  */
 
 STATIC bool
-S_isa_lookup(pTHX_ HV *stash, const char * const name)
+S_isa_lookup(pTHX_ HV *stash, const char * const name, STRLEN len, U32 flags)
 {
     dVAR;
     const struct mro_meta *const meta = HvMROMETA(stash);
-    HV *const isa = meta->isa ? meta->isa : S_get_isa_hash(aTHX_ stash);
-    STRLEN len = strlen(name);
+    HV *isa = meta->isa;
     const HV *our_stash;
 
     PERL_ARGS_ASSERT_ISA_LOOKUP;
 
-    if (hv_common(isa, NULL, name, len, 0 /* No "UTF-8" flag possible with only
-                                            a char * argument*/,
+    if (!isa) {
+       (void)mro_get_linear_isa(stash);
+       isa = meta->isa;
+    }
+
+    if (hv_common(isa, NULL, name, len, ( flags & SVf_UTF8 ? HVhek_UTF8 : 0),
                  HV_FETCH_ISEXISTS, NULL, 0)) {
        /* Direct name lookup worked.  */
        return TRUE;
     }
 
     /* A stash/class can go by many names (ie. User == main::User), so 
-       we use the name in the stash itself, which is canonical.  */
-    our_stash = gv_stashpvn(name, len, 0);
+       we use the HvENAME in the stash itself, which is canonical, falling
+       back to HvNAME if necessary.  */
+    our_stash = gv_stashpvn(name, len, flags);
 
     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),
@@ -111,22 +81,76 @@ S_isa_lookup(pTHX_ HV *stash, const char * const name)
 /*
 =head1 SV Manipulation Functions
 
-=for apidoc sv_derived_from
+=for apidoc sv_derived_from_pvn
 
 Returns a boolean indicating whether the SV is derived from the specified class
 I<at the C level>.  To check derivation at the Perl level, call C<isa()> as a
 normal Perl method.
 
+Currently, the only significant value for C<flags> is SVf_UTF8.
+
+=cut
+
+=for apidoc sv_derived_from_sv
+
+Exactly like L</sv_derived_from_pvn>, but takes the name string in the form
+of an SV instead of a string/length pair.
+
+=cut
+
+*/
+
+bool
+Perl_sv_derived_from_sv(pTHX_ SV *sv, SV *namesv, U32 flags)
+{
+    char *namepv;
+    STRLEN namelen;
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM_SV;
+    namepv = SvPV(namesv, namelen);
+    if (SvUTF8(namesv))
+       flags |= SVf_UTF8;
+    return sv_derived_from_pvn(sv, namepv, namelen, flags);
+}
+
+/*
+=for apidoc sv_derived_from
+
+Exactly like L</sv_derived_from_pv>, but doesn't take a C<flags> parameter.
+
 =cut
 */
 
 bool
 Perl_sv_derived_from(pTHX_ SV *sv, const char *const name)
 {
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM;
+    return sv_derived_from_pvn(sv, name, strlen(name), 0);
+}
+
+/*
+=for apidoc sv_derived_from_pv
+
+Exactly like L</sv_derived_from_pvn>, but takes a nul-terminated string 
+instead of a string/length pair.
+
+=cut
+*/
+
+
+bool
+Perl_sv_derived_from_pv(pTHX_ SV *sv, const char *const name, U32 flags)
+{
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM_PV;
+    return sv_derived_from_pvn(sv, name, strlen(name), flags);
+}
+
+bool
+Perl_sv_derived_from_pvn(pTHX_ SV *sv, const char *const name, const STRLEN len, U32 flags)
+{
     dVAR;
     HV *stash;
 
-    PERL_ARGS_ASSERT_SV_DERIVED_FROM;
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM_PVN;
 
     SvGETMAGIC(sv);
 
@@ -140,13 +164,15 @@ Perl_sv_derived_from(pTHX_ SV *sv, const char *const name)
     }
     else {
         stash = gv_stashsv(sv, 0);
+        if (!stash)
+            stash = gv_stashpv("UNIVERSAL", 0);
     }
 
-    return stash ? isa_lookup(stash, name) : FALSE;
+    return stash ? isa_lookup(stash, name, len, flags) : FALSE;
 }
 
 /*
-=for apidoc sv_does
+=for apidoc sv_does_sv
 
 Returns a boolean indicating whether the SV performs a specific, named role.
 The SV can be a Perl object or the name of a Perl class.
@@ -157,40 +183,41 @@ The SV can be a Perl object or the name of a Perl class.
 #include "XSUB.h"
 
 bool
-Perl_sv_does(pTHX_ SV *sv, const char *const name)
+Perl_sv_does_sv(pTHX_ SV *sv, SV *namesv, U32 flags)
 {
-    const char *classname;
+    SV *classname;
     bool does_it;
     SV *methodname;
     dSP;
 
-    PERL_ARGS_ASSERT_SV_DOES;
+    PERL_ARGS_ASSERT_SV_DOES_SV;
+    PERL_UNUSED_ARG(flags);
 
     ENTER;
     SAVETMPS;
 
     SvGETMAGIC(sv);
 
-    if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))
-           || (SvGMAGICAL(sv) && SvPOKp(sv) && SvCUR(sv)))) {
+    if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv)))) {
        LEAVE;
        return FALSE;
     }
 
     if (sv_isobject(sv)) {
-       classname = sv_reftype(SvRV(sv),TRUE);
+       classname = sv_ref(NULL,SvRV(sv),TRUE);
     } else {
-       classname = SvPV_nolen(sv);
+       classname = sv;
     }
 
-    if (strEQ(name,classname)) {
+    if (sv_eq(classname, namesv)) {
        LEAVE;
        return TRUE;
     }
 
     PUSHMARK(SP);
-    XPUSHs(sv);
-    mXPUSHs(newSVpv(name, 0));
+    EXTEND(SP, 2);
+    PUSHs(sv);
+    PUSHs(namesv);
     PUTBACK;
 
     methodname = newSVpvs_flags("isa", SVs_TEMP);
@@ -209,6 +236,53 @@ Perl_sv_does(pTHX_ SV *sv, const char *const name)
 }
 
 /*
+=for apidoc sv_does
+
+Like L</sv_does_pv>, but doesn't take a C<flags> parameter.
+
+=cut
+*/
+
+bool
+Perl_sv_does(pTHX_ SV *sv, const char *const name)
+{
+    PERL_ARGS_ASSERT_SV_DOES;
+    return sv_does_sv(sv, newSVpvn_flags(name, strlen(name), SVs_TEMP), 0);
+}
+
+/*
+=for apidoc sv_does_pv
+
+Like L</sv_does_sv>, but takes a nul-terminated string instead of an SV.
+
+=cut
+*/
+
+
+bool
+Perl_sv_does_pv(pTHX_ SV *sv, const char *const name, U32 flags)
+{
+    PERL_ARGS_ASSERT_SV_DOES_PV;
+    return sv_does_sv(sv, newSVpvn_flags(name, strlen(name), SVs_TEMP | flags), flags);
+}
+
+/*
+=for apidoc sv_does_pvn
+
+Like L</sv_does_sv>, but takes a string/length pair instead of an SV.
+
+=cut
+*/
+
+bool
+Perl_sv_does_pvn(pTHX_ SV *sv, const char *const name, const STRLEN len, U32 flags)
+{
+    PERL_ARGS_ASSERT_SV_DOES_PVN;
+
+    return sv_does_sv(sv, newSVpvn_flags(name, len, flags | SVs_TEMP), flags);
+}
+
+/*
 =for apidoc croak_xs_usage
 
 A specialised variant of C<croak()> for emitting the usage message for xsubs
@@ -218,7 +292,7 @@ A specialised variant of C<croak()> for emitting the usage message for xsubs
 works out the package name and subroutine name from C<cv>, and then calls
 C<croak()>. Hence if C<cv> is C<&ouch::awk>, it would call C<croak> as:
 
-    Perl_croak(aTHX_ "Usage: %s::%s(%s)", "ouch" "awk", "eee_yow");
+    Perl_croak(aTHX_ "Usage: %"SVf"::%"SVf"(%s)", "ouch" "awk", "eee_yow");
 
 =cut
 */
@@ -231,14 +305,16 @@ Perl_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
     PERL_ARGS_ASSERT_CROAK_XS_USAGE;
 
     if (gv) {
-       const char *const gvname = GvNAME(gv);
        const HV *const stash = GvSTASH(gv);
-       const char *const hvname = stash ? HvNAME_get(stash) : NULL;
 
-       if (hvname)
-           Perl_croak(aTHX_ "Usage: %s::%s(%s)", hvname, gvname, params);
+       if (HvNAME_get(stash))
+           Perl_croak(aTHX_ "Usage: %"HEKf"::%"HEKf"(%s)",
+                                HEKfARG(HvNAME_HEK(stash)),
+                                HEKfARG(GvNAME_HEK(gv)),
+                                params);
        else
-           Perl_croak(aTHX_ "Usage: %s(%s)", gvname, params);
+           Perl_croak(aTHX_ "Usage: %"HEKf"(%s)",
+                                HEKfARG(GvNAME_HEK(gv)), params);
     } else {
        /* Pants. I don't think that it should be possible to get here. */
        Perl_croak(aTHX_ "Usage: CODE(0x%"UVxf")(%s)", PTR2UV(cv), params);
@@ -254,17 +330,13 @@ XS(XS_UNIVERSAL_isa)
        croak_xs_usage(cv, "reference, kind");
     else {
        SV * const sv = ST(0);
-       const char *name;
 
        SvGETMAGIC(sv);
 
-       if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))
-                   || (SvGMAGICAL(sv) && SvPOKp(sv) && SvCUR(sv))))
+       if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))))
            XSRETURN_UNDEF;
 
-       name = SvPV_nolen_const(ST(1));
-
-       ST(0) = boolSV(sv_derived_from(sv, name));
+       ST(0) = boolSV(sv_derived_from_sv(sv, ST(1), 0));
        XSRETURN(1);
     }
 }
@@ -274,7 +346,6 @@ XS(XS_UNIVERSAL_can)
     dVAR;
     dXSARGS;
     SV   *sv;
-    const char *name;
     SV   *rv;
     HV   *pkg = NULL;
 
@@ -285,11 +356,10 @@ XS(XS_UNIVERSAL_can)
 
     SvGETMAGIC(sv);
 
-    if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))
-               || (SvGMAGICAL(sv) && SvPOKp(sv) && SvCUR(sv))))
+    if (!SvOK(sv) || !(SvROK(sv) || SvNIOK(sv) || (SvPOK(sv) && SvCUR(sv))
+       ))
        XSRETURN_UNDEF;
 
-    name = SvPV_nolen_const(ST(1));
     rv = &PL_sv_undef;
 
     if (SvROK(sv)) {
@@ -299,10 +369,12 @@ XS(XS_UNIVERSAL_can)
     }
     else {
         pkg = gv_stashsv(sv, 0);
+        if (!pkg)
+            pkg = gv_stashpv("UNIVERSAL", 0);
     }
 
     if (pkg) {
-       GV * const gv = gv_fetchmethod_autoload(pkg, name, FALSE);
+       GV * const gv = gv_fetchmethod_sv_flags(pkg, ST(1), 0);
         if (gv && isGV(gv))
            rv = sv_2mortal(newRV(MUTABLE_SV(GvCV(gv))));
     }
@@ -321,10 +393,7 @@ XS(XS_UNIVERSAL_DOES)
        Perl_croak(aTHX_ "Usage: invocant->DOES(kind)");
     else {
        SV * const sv = ST(0);
-       const char *name;
-
-       name = SvPV_nolen_const(ST(1));
-       if (sv_does( sv, name ))
+       if (sv_does_sv( sv, ST(1), 0 ))
            XSRETURN_YES;
 
        XSRETURN_NO;
@@ -358,8 +427,9 @@ XS(XS_UNIVERSAL_VERSION)
         SV * const nsv = sv_newmortal();
         sv_setsv(nsv, sv);
         sv = nsv;
-       if ( !sv_derived_from(sv, "version"))
+       if ( !sv_isobject(sv) || !sv_derived_from(sv, "version"))
            upg_version(sv, FALSE);
+
         undef = NULL;
     }
     else {
@@ -372,40 +442,43 @@ XS(XS_UNIVERSAL_VERSION)
 
        if (undef) {
            if (pkg) {
-               const char * const name = HvNAME_get(pkg);
+               const HEK * const name = HvNAME_HEK(pkg);
                Perl_croak(aTHX_
-                          "%s does not define $%s::VERSION--version check failed",
-                          name, name);
+                          "%"HEKf" does not define $%"HEKf
+                          "::VERSION--version check failed",
+                          HEKfARG(name), HEKfARG(name));
            } else {
                Perl_croak(aTHX_
-                            "%s defines neither package nor VERSION--version check failed",
-                            SvPVx_nolen_const(ST(0)) );
+                            "%"SVf" defines neither package nor VERSION--version check failed",
+                            SVfARG(ST(0)) );
             }
        }
 
-       if ( !sv_derived_from(req, "version")) {
+       if ( !sv_isobject(req) || !sv_derived_from(req, "version")) {
            /* req may very well be R/O, so create a new object */
            req = sv_2mortal( new_version(req) );
        }
 
        if ( vcmp( req, sv ) > 0 ) {
            if ( hv_exists(MUTABLE_HV(SvRV(req)), "qv", 2 ) ) {
-               Perl_croak(aTHX_ "%s version %"SVf" required--"
-                      "this is only version %"SVf"", HvNAME_get(pkg),
-                      SVfARG(vnormal(req)),
-                      SVfARG(vnormal(sv)));
+               Perl_croak(aTHX_ "%"HEKf" version %"SVf" required--"
+                      "this is only version %"SVf"",
+                       HEKfARG(HvNAME_HEK(pkg)),
+                      SVfARG(sv_2mortal(vnormal(req))),
+                      SVfARG(sv_2mortal(vnormal(sv))));
            } else {
-               Perl_croak(aTHX_ "%s version %"SVf" required--"
-                      "this is only version %"SVf"", HvNAME_get(pkg),
-                      SVfARG(vstringify(req)),
-                      SVfARG(vstringify(sv)));
+               Perl_croak(aTHX_ "%"HEKf" version %"SVf" required--"
+                      "this is only version %"SVf,
+                       HEKfARG(HvNAME_HEK(pkg)),
+                      SVfARG(sv_2mortal(vstringify(req))),
+                      SVfARG(sv_2mortal(vstringify(sv))));
            }
        }
 
     }
 
     if ( SvOK(sv) && sv_derived_from(sv, "version") ) {
-       ST(0) = vstringify(sv);
+       ST(0) = sv_2mortal(vstringify(sv));
     } else {
        ST(0) = sv;
     }
@@ -423,10 +496,19 @@ XS(XS_version_new)
     {
         SV *vs = ST(1);
        SV *rv;
-       const char * const classname =
-           sv_isobject(ST(0)) /* get the class if called as an object method */
-               ? HvNAME(SvSTASH(SvRV(ST(0))))
-               : (char *)SvPV_nolen(ST(0));
+        STRLEN len;
+        const char *classname;
+        U32 flags;
+        if ( sv_isobject(ST(0)) ) { /* get the class if called as an object method */
+            const HV * stash = SvSTASH(SvRV(ST(0)));
+            classname = HvNAME(stash);
+            len       = HvNAMELEN(stash);
+            flags     = HvNAMEUTF8(stash) ? SVf_UTF8 : 0;
+        }
+        else {
+           classname = SvPV(ST(0), len);
+            flags     = SvUTF8(ST(0));
+        }
 
        if ( items == 1 || ! SvOK(vs) ) { /* no param or explicit undef */
            /* create empty object */
@@ -439,8 +521,8 @@ XS(XS_version_new)
        }
 
        rv = new_version(vs);
-       if ( strcmp(classname,"version") != 0 ) /* inherited new() */
-           sv_bless(rv, gv_stashpv(classname, GV_ADD));
+       if ( strnNE(classname,"version", len) ) /* inherited new() */
+           sv_bless(rv, gv_stashpvn(classname, len, GV_ADD | flags));
 
        mPUSHs(rv);
        PUTBACK;
@@ -456,10 +538,10 @@ XS(XS_version_stringify)
         croak_xs_usage(cv, "lobj, ...");
      SP -= items;
      {
-         SV *  lobj;
+         SV *  lobj = ST(0);
 
-         if (sv_derived_from(ST(0), "version")) {
-              lobj = SvRV(ST(0));
+         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
+              lobj = SvRV(lobj);
          }
          else
               Perl_croak(aTHX_ "lobj is not of type version");
@@ -479,10 +561,10 @@ XS(XS_version_numify)
         croak_xs_usage(cv, "lobj, ...");
      SP -= items;
      {
-         SV *  lobj;
+         SV *  lobj = ST(0);
 
-         if (sv_derived_from(ST(0), "version")) {
-              lobj = SvRV(ST(0));
+         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
+              lobj = SvRV(lobj);
          }
          else
               Perl_croak(aTHX_ "lobj is not of type version");
@@ -502,10 +584,10 @@ XS(XS_version_normal)
         croak_xs_usage(cv, "lobj, ...");
      SP -= items;
      {
-         SV *  lobj;
+         SV *  lobj = ST(0);
 
-         if (sv_derived_from(ST(0), "version")) {
-              lobj = SvRV(ST(0));
+         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
+              lobj = SvRV(lobj);
          }
          else
               Perl_croak(aTHX_ "lobj is not of type version");
@@ -525,10 +607,10 @@ XS(XS_version_vcmp)
         croak_xs_usage(cv, "lobj, ...");
      SP -= items;
      {
-         SV *  lobj;
+         SV *  lobj = ST(0);
 
-         if (sv_derived_from(ST(0), "version")) {
-              lobj = SvRV(ST(0));
+         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
+              lobj = SvRV(lobj);
          }
          else
               Perl_croak(aTHX_ "lobj is not of type version");
@@ -539,9 +621,10 @@ XS(XS_version_vcmp)
               SV * robj = ST(1);
               const IV  swap = (IV)SvIV(ST(2));
 
-              if ( ! sv_derived_from(robj, "version") )
+              if ( !sv_isobject(robj) || !sv_derived_from(robj, "version") )
               {
-                   robj = new_version(SvOK(robj) ? robj : newSVpvs("0"));
+                   robj = new_version(SvOK(robj) ? robj : newSVpvs_flags("0", SVs_TEMP));
+                   sv_2mortal(robj);
               }
               rvs = SvRV(robj);
 
@@ -569,9 +652,15 @@ XS(XS_version_boolean)
     if (items < 1)
        croak_xs_usage(cv, "lobj, ...");
     SP -= items;
-    if (sv_derived_from(ST(0), "version")) {
+    if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "version")) {
        SV * const lobj = SvRV(ST(0));
-       SV * const rs = newSViv( vcmp(lobj,new_version(newSVpvs("0"))) );
+       SV * const rs =
+           newSViv( vcmp(lobj,
+                         sv_2mortal(new_version(
+                                       sv_2mortal(newSVpvs("0"))
+                                   ))
+                        )
+                  );
        mPUSHs(rs);
        PUTBACK;
        return;
@@ -586,7 +675,7 @@ XS(XS_version_noop)
     dXSARGS;
     if (items < 1)
        croak_xs_usage(cv, "lobj, ...");
-    if (sv_derived_from(ST(0), "version"))
+    if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "version"))
        Perl_croak(aTHX_ "operation not supported with version object");
     else
        Perl_croak(aTHX_ "lobj is not of type version");
@@ -602,7 +691,7 @@ XS(XS_version_is_alpha)
     if (items != 1)
        croak_xs_usage(cv, "lobj");
     SP -= items;
-    if (sv_derived_from(ST(0), "version")) {
+    if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "version")) {
        SV * const lobj = ST(0);
        if ( hv_exists(MUTABLE_HV(SvRV(lobj)), "alpha", 5 ) )
            XSRETURN_YES;
@@ -624,15 +713,22 @@ XS(XS_version_qv)
     {
        SV * ver = ST(0);
        SV * rv;
-       const char * classname = "";
-       if ( items == 2 && SvOK(ST(1)) ) {
-           /* getting called as object or class method */
-           ver = ST(1);
-           classname = 
-               sv_isobject(ST(0)) /* class called as an object method */
-                   ? HvNAME_get(SvSTASH(SvRV(ST(0))))
-                   : (char *)SvPV_nolen(ST(0));
-       }
+        STRLEN len = 0;
+        const char * classname = "";
+        U32 flags = 0;
+        if ( items == 2 && SvOK(ST(1)) ) {
+            ver = ST(1);
+            if ( sv_isobject(ST(0)) ) { /* class called as an object method */
+                const HV * stash = SvSTASH(SvRV(ST(0)));
+                classname = HvNAME(stash);
+                len       = HvNAMELEN(stash);
+                flags     = HvNAMEUTF8(stash) ? SVf_UTF8 : 0;
+            }
+            else {
+              classname = SvPV(ST(0), len);
+                flags     = SvUTF8(ST(0));
+            }
+        }
        if ( !SvVOK(ver) ) { /* not already a v-string */
            rv = sv_newmortal();
            sv_setsv(rv,ver); /* make a duplicate */
@@ -640,9 +736,10 @@ XS(XS_version_qv)
        } else {
            rv = sv_2mortal(new_version(ver));
        }
-       if ( items == 2 && strcmp(classname,"version") ) { /* inherited new() */
-           sv_bless(rv, gv_stashpv(classname, GV_ADD));
-       }
+       if ( items == 2
+                && strnNE(classname,"version", len) ) { /* inherited new() */
+           sv_bless(rv, gv_stashpvn(classname, len, GV_ADD | flags));
+        }
        PUSHs(rv);
     }
     PUTBACK;
@@ -656,7 +753,7 @@ XS(XS_version_is_qv)
     if (items != 1)
        croak_xs_usage(cv, "lobj");
     SP -= items;
-    if (sv_derived_from(ST(0), "version")) {
+    if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "version")) {
        SV * const lobj = ST(0);
        if ( hv_exists(MUTABLE_HV(SvRV(lobj)), "qv", 2 ) )
            XSRETURN_YES;
@@ -711,6 +808,7 @@ XS(XS_utf8_encode)
     if (items != 1)
        croak_xs_usage(cv, "sv");
     sv_utf8_encode(ST(0));
+    SvSETMAGIC(ST(0));
     XSRETURN_EMPTY;
 }
 
@@ -722,9 +820,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;
+       SvPV_force_nolen(sv);
+       RETVAL = sv_utf8_decode(sv);
        ST(0) = boolSV(RETVAL);
-       sv_2mortal(ST(0));
     }
     XSRETURN(1);
 }
@@ -758,7 +857,6 @@ XS(XS_utf8_downgrade)
         const bool RETVAL = sv_utf8_downgrade(sv, failok);
 
        ST(0) = boolSV(RETVAL);
-       sv_2mortal(ST(0));
     }
     XSRETURN(1);
 }
@@ -793,23 +891,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;
        }
     }
@@ -820,15 +926,22 @@ 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. */
+        XSRETURN_UV(SvREFCNT(sv) - 1); /* Minus the ref created for us. */
     else if (items == 2) {
          /* I hope you really know what you are doing. */
-        SvREFCNT(sv) = SvIV(ST(1));
-        XSRETURN_IV(SvREFCNT(sv));
+        SvREFCNT(sv) = SvUV(ST(1)) + 1; /* we free one ref on exit */
+        XSRETURN_UV(SvREFCNT(sv) - 1);
     }
     XSRETURN_UNDEF; /* Can't happen. */
 }
@@ -838,7 +951,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)));
@@ -900,14 +1013,10 @@ XS(XS_PerlIO_get_layers)
        }
 
        sv = POPs;
-       gv = MUTABLE_GV(sv);
+       gv = MAYBE_DEREF_GV(sv);
 
-       if (!isGV(sv)) {
-            if (SvROK(sv) && isGV(SvRV(sv)))
-                 gv = MUTABLE_GV(SvRV(sv));
-            else if (SvPOKp(sv))
-                 gv = gv_fetchsv(sv, 0, SVt_PVIO);
-       }
+       if (!gv && !SvROK(sv))
+           gv = gv_fetchsv_nomg(sv, 0, SVt_PVIO);
 
        if (gv && (io = GvIO(gv))) {
             AV* const av = PerlIO_get_layers(aTHX_ input ?
@@ -1023,8 +1132,6 @@ XS(XS_re_is_regexp)
     if (items != 1)
        croak_xs_usage(cv, "sv");
 
-    SP -= items;
-
     if (SvRXOK(ST(0))) {
         XSRETURN_YES;
     } else {
@@ -1043,6 +1150,7 @@ XS(XS_re_regnames_count)
        croak_xs_usage(cv, "");
 
     SP -= items;
+    PUTBACK;
 
     if (!rx)
         XSRETURN_UNDEF;
@@ -1050,14 +1158,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)
@@ -1072,6 +1174,7 @@ XS(XS_re_regname)
        croak_xs_usage(cv, "name[, all ]");
 
     SP -= items;
+    PUTBACK;
 
     rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
 
@@ -1085,11 +1188,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);
 }
 
 
@@ -1120,13 +1221,12 @@ XS(XS_re_regnames)
     }
 
     SP -= items;
+    PUTBACK;
 
     ret = CALLREG_NAMED_BUFF_ALL(rx, (flags | RXapif_REGNAMES));
 
     SPAGAIN;
 
-    SP -= items;
-
     if (!ret)
         XSRETURN_UNDEF;
 
@@ -1166,7 +1266,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.
     */
 
@@ -1174,19 +1274,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++)) {
@@ -1238,239 +1348,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;
@@ -1495,6 +1372,15 @@ struct xsub_details details[] = {
     {"version::vcmp", XS_version_vcmp, NULL},
     {"version::(bool", XS_version_boolean, NULL},
     {"version::boolean", XS_version_boolean, NULL},
+    {"version::(+", XS_version_noop, NULL},
+    {"version::(-", XS_version_noop, NULL},
+    {"version::(*", XS_version_noop, NULL},
+    {"version::(/", XS_version_noop, NULL},
+    {"version::(+=", XS_version_noop, NULL},
+    {"version::(-=", XS_version_noop, NULL},
+    {"version::(*=", XS_version_noop, NULL},
+    {"version::(/=", XS_version_noop, NULL},
+    {"version::(abs", XS_version_noop, NULL},
     {"version::(nomethod", XS_version_noop, NULL},
     {"version::noop", XS_version_noop, NULL},
     {"version::is_alpha", XS_version_is_alpha, NULL},
@@ -1521,15 +1407,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
@@ -1545,20 +1422,22 @@ Perl_boot_core_UNIVERSAL(pTHX)
        newXS_flags(xsub->name, xsub->xsub, file, xsub->proto, 0);
     } while (++xsub < end);
 
-    /* register the overloading (type 'A') magic */
-    PL_amagic_generation++;
-
     /* Providing a Regexp::DESTROY fixes #21347. See test in t/op/ref.t  */
-    CvFILE(newCONSTSUB(get_hv("Regexp::", GV_ADD), "DESTROY", NULL))
-       = (char *)file;
+    {
+       CV * const cv =
+           newCONSTSUB(get_hv("Regexp::", GV_ADD), "DESTROY", NULL);
+       Safefree(CvFILE(cv));
+       CvFILE(cv) = (char *)file;
+       CvDYNFILE_off(cv);
+    }
 }
 
 /*
  * Local variables:
  * c-indentation-style: bsd
  * c-basic-offset: 4
- * indent-tabs-mode: t
+ * indent-tabs-mode: nil
  * End:
  *
- * ex: set ts=8 sts=4 sw=4 noet:
+ * ex: set ts=8 sts=4 sw=4 et:
  */