This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
new perldelta
[perl5.git] / universal.c
index 384d307..1e039d1 100644 (file)
@@ -1,3 +1,4 @@
+#line 2 "universal.c"
 /*    universal.c
  *
  *    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
@@ -29,7 +30,7 @@
 #define PERL_IN_UNIVERSAL_C
 #include "perl.h"
 
-#ifdef USE_PERLIO
+#if defined(USE_PERLIO)
 #include "perliol.h" /* For the PERLIO_F_XXX */
 #endif
 
  * The main guts of traverse_isa was actually copied from gv_fetchmeth
  */
 
+#define PERL_ARGS_ASSERT_ISA_LOOKUP \
+    assert(stash); \
+    assert(namesv || name)
+
+
 STATIC bool
-S_isa_lookup(pTHX_ HV *stash, const char * const name, STRLEN len, U32 flags)
+S_isa_lookup(pTHX_ HV *stash, SV *namesv, const char * name, STRLEN len, U32 flags)
 {
-    dVAR;
     const struct mro_meta *const meta = HvMROMETA(stash);
     HV *isa = meta->isa;
     const HV *our_stash;
@@ -49,37 +54,74 @@ S_isa_lookup(pTHX_ HV *stash, const char * const name, STRLEN len, U32 flags)
     PERL_ARGS_ASSERT_ISA_LOOKUP;
 
     if (!isa) {
-       (void)mro_get_linear_isa(stash);
-       isa = meta->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;
+    if (hv_common(isa, namesv, 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 HvENAME in the stash itself, which is canonical, falling
        back to HvNAME if necessary.  */
-    our_stash = gv_stashpvn(name, len, flags);
+    our_stash = gv_stashsvpvn_cached(namesv, name, len, flags);
 
     if (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),
-                     HV_FETCH_ISEXISTS, NULL, HEK_HASH(canon_name))) {
-           return TRUE;
-       }
+        HEK *canon_name = HvENAME_HEK(our_stash);
+        if (!canon_name) canon_name = HvNAME_HEK(our_stash);
+        assert(canon_name);
+        if (hv_common(isa, NULL, HEK_KEY(canon_name), HEK_LEN(canon_name),
+                      HEK_FLAGS(canon_name),
+                      HV_FETCH_ISEXISTS, NULL, HEK_HASH(canon_name))) {
+            return TRUE;
+        }
     }
 
     return FALSE;
 }
 
+#define PERL_ARGS_ASSERT_SV_DERIVED_FROM_SVPVN \
+    assert(sv); \
+    assert(namesv || name)
+
+STATIC bool
+S_sv_derived_from_svpvn(pTHX_ SV *sv, SV *namesv, const char * name, const STRLEN len, U32 flags)
+{
+    HV* stash;
+
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM_SVPVN;
+    SvGETMAGIC(sv);
+
+    if (SvROK(sv)) {
+        const char *type;
+        sv = SvRV(sv);
+        type = sv_reftype(sv,0);
+        if (type) {
+            if (namesv)
+                name = SvPV_nolen(namesv);
+            if (strEQ(name, type))
+                return TRUE;
+        }
+        if (!SvOBJECT(sv))
+            return FALSE;
+        stash = SvSTASH(sv);
+    }
+    else {
+        stash = gv_stashsv(sv, 0);
+    }
+
+    if (stash && isa_lookup(stash, namesv, name, len, flags))
+        return TRUE;
+
+    stash = gv_stashpvs("UNIVERSAL", 0);
+    return stash && isa_lookup(stash, namesv, name, len, flags);
+}
+
 /*
-=head1 SV Manipulation Functions
+=for apidoc_section $SV
 
 =for apidoc sv_derived_from_pvn
 
@@ -94,7 +136,7 @@ Currently, the only significant value for C<flags> is SVf_UTF8.
 =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.
+of an SV instead of a string/length pair. This is the advised form.
 
 =cut
 
@@ -103,13 +145,8 @@ of an SV instead of a string/length pair.
 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);
+    return sv_derived_from_svpvn(sv, namesv, NULL, 0, flags);
 }
 
 /*
@@ -124,7 +161,7 @@ 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);
+    return sv_derived_from_svpvn(sv, NULL, name, strlen(name), 0);
 }
 
 /*
@@ -141,32 +178,100 @@ 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);
+    return sv_derived_from_svpvn(sv, NULL, 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_PVN;
+    return sv_derived_from_svpvn(sv, NULL, name, len, flags);
+}
 
-    SvGETMAGIC(sv);
+/*
+=for apidoc sv_derived_from_hv
 
-    if (SvROK(sv)) {
-       const char *type;
-        sv = SvRV(sv);
-        type = sv_reftype(sv,0);
-       if (type && strEQ(type,name))
-           return TRUE;
-       stash = SvOBJECT(sv) ? SvSTASH(sv) : NULL;
-    }
-    else {
-        stash = gv_stashsv(sv, 0);
+Exactly like L</sv_derived_from_pvn>, but takes the name string as the
+C<HvNAME> of the given HV (which would presumably represent a stash).
+
+=cut
+*/
+
+bool
+Perl_sv_derived_from_hv(pTHX_ SV *sv, HV *hv)
+{
+    PERL_ARGS_ASSERT_SV_DERIVED_FROM_HV;
+
+    const char *hvname = HvNAME(hv);
+    if(!hvname)
+        return FALSE;
+
+    return sv_derived_from_svpvn(sv, NULL, hvname, HvNAMELEN(hv), HvNAMEUTF8(hv) ? SVf_UTF8 : 0);
+}
+
+/*
+=for apidoc sv_isa_sv
+
+Returns a boolean indicating whether the SV is an object reference and is
+derived from the specified class, respecting any C<isa()> method overloading
+it may have. Returns false if C<sv> is not a reference to an object, or is
+not derived from the specified class.
+
+This is the function used to implement the behaviour of the C<isa> operator.
+
+Does not invoke magic on C<sv>.
+
+Not to be confused with the older C<sv_isa> function, which does not use an
+overloaded C<isa()> method, nor will check subclassing.
+
+=cut
+
+*/
+
+bool
+Perl_sv_isa_sv(pTHX_ SV *sv, SV *namesv)
+{
+    GV *isagv;
+
+    PERL_ARGS_ASSERT_SV_ISA_SV;
+
+    if(!SvROK(sv) || !SvOBJECT(SvRV(sv)))
+        return FALSE;
+
+    isagv = gv_fetchmeth_pvn(SvSTASH(SvRV(sv)), "isa", 3, -1, GV_NOUNIVERSAL);
+    if(isagv) {
+        dSP;
+        CV *isacv = isGV(isagv) ? GvCV(isagv) : (CV *)isagv;
+        SV *retsv;
+        bool ret;
+
+        PUTBACK;
+
+        ENTER;
+        SAVETMPS;
+
+        EXTEND(SP, 2);
+        PUSHMARK(SP);
+        PUSHs(sv);
+        PUSHs(namesv);
+        PUTBACK;
+
+        call_sv((SV *)isacv, G_SCALAR);
+
+        SPAGAIN;
+        retsv = POPs;
+        ret = SvTRUE(retsv);
+        PUTBACK;
+
+        FREETMPS;
+        LEAVE;
+
+        return ret;
     }
 
-    return stash ? isa_lookup(stash, name, len, flags) : FALSE;
+    /* TODO: Support namesv being an HV ref to the stash directly? */
+
+    return sv_derived_from_sv(sv, namesv, 0);
 }
 
 /*
@@ -196,21 +301,20 @@ Perl_sv_does_sv(pTHX_ SV *sv, SV *namesv, U32 flags)
 
     SvGETMAGIC(sv);
 
-    if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))
-           || (SvGMAGICAL(sv) && SvPOKp(sv) && SvCUR(sv)))) {
-       LEAVE;
-       return FALSE;
+    if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv)))) {
+        LEAVE;
+        return FALSE;
     }
 
-    if (sv_isobject(sv)) {
-       classname = sv_ref(NULL,SvRV(sv),TRUE);
+    if (SvROK(sv) && SvOBJECT(SvRV(sv))) {
+        classname = sv_ref(NULL,SvRV(sv),TRUE);
     } else {
-       classname = sv;
+        classname = sv;
     }
 
     if (sv_eq(classname, namesv)) {
-       LEAVE;
-       return TRUE;
+        LEAVE;
+        return TRUE;
     }
 
     PUSHMARK(SP);
@@ -219,15 +323,17 @@ Perl_sv_does_sv(pTHX_ SV *sv, SV *namesv, U32 flags)
     PUSHs(namesv);
     PUTBACK;
 
-    methodname = newSVpvs_flags("isa", SVs_TEMP);
-    /* ugly hack: use the SvSCREAM flag so S_method_common
-     * can figure out we're calling DOES() and not isa(),
-     * and report eventual errors correctly. --rgs */
-    SvSCREAM_on(methodname);
+    /* create a PV with value "isa", but with a special address
+     * so that perl knows we're really doing "DOES" instead */
+    methodname = newSV_type_mortal(SVt_PV);
+    SvLEN_set(methodname, 0);
+    SvCUR_set(methodname, strlen(PL_isa_DOES));
+    SvPVX(methodname) = (char *)PL_isa_DOES; /* discard 'const' qualifier */
+    SvPOK_on(methodname);
     call_sv(methodname, G_SCALAR | G_METHOD);
     SPAGAIN;
 
-    does_it = SvTRUE( TOPs );
+    does_it = SvTRUE_NN( TOPs );
     FREETMPS;
     LEAVE;
 
@@ -289,76 +395,115 @@ A specialised variant of C<croak()> for emitting the usage message for xsubs
     croak_xs_usage(cv, "eee_yow");
 
 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:
+C<croak()>.  Hence if C<cv> is C<&ouch::awk>, it would call C<croak> as:
 
-    Perl_croak(aTHX_ "Usage: %"SVf"::%"SVf"(%s)", "ouch" "awk", "eee_yow");
+ diag_listed_as: SKIPME
+ Perl_croak(aTHX_ "Usage: %" SVf "::%" SVf "(%s)", "ouch" "awk",
+                                                     "eee_yow");
 
 =cut
 */
 
 void
-Perl_croak_xs_usage(pTHX_ const CV *const cv, const char *const params)
+Perl_croak_xs_usage(const CV *const cv, const char *const params)
 {
-    const GV *const gv = CvGV(cv);
+    /* Avoid CvGV as it requires aTHX.  */
+    const GV *gv = CvNAMED(cv) ? NULL : cv->sv_any->xcv_gv_u.xcv_gv;
 
     PERL_ARGS_ASSERT_CROAK_XS_USAGE;
 
-    if (gv) {
-       const HV *const stash = GvSTASH(gv);
+    if (gv) got_gv: {
+        const HV *const stash = GvSTASH(gv);
 
-       if (HvNAME_get(stash))
-           Perl_croak(aTHX_ "Usage: %"HEKf"::%"HEKf"(%s)",
+        if (HvNAME_get(stash))
+            /* diag_listed_as: SKIPME */
+            Perl_croak_nocontext("Usage: %" HEKf "::%" HEKf "(%s)",
                                 HEKfARG(HvNAME_HEK(stash)),
                                 HEKfARG(GvNAME_HEK(gv)),
                                 params);
-       else
-           Perl_croak(aTHX_ "Usage: %"HEKf"(%s)",
+        else
+            /* diag_listed_as: SKIPME */
+            Perl_croak_nocontext("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);
+        dTHX;
+        if ((gv = CvGV(cv))) goto got_gv;
+
+        /* Pants. I don't think that it should be possible to get here. */
+        /* diag_listed_as: SKIPME */
+        Perl_croak(aTHX_ "Usage: CODE(0x%" UVxf ")(%s)", PTR2UV(cv), params);
     }
 }
 
+XS(XS_UNIVERSAL_isa); /* prototype to pass -Wmissing-prototypes */
 XS(XS_UNIVERSAL_isa)
 {
-    dVAR;
     dXSARGS;
 
     if (items != 2)
-       croak_xs_usage(cv, "reference, kind");
+        croak_xs_usage(cv, "reference, kind");
     else {
-       SV * const sv = ST(0);
+        SV * const sv = ST(0);
 
-       SvGETMAGIC(sv);
+        SvGETMAGIC(sv);
 
-       if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))
-                   || (SvGMAGICAL(sv) && SvPOKp(sv) && SvCUR(sv))))
-           XSRETURN_UNDEF;
+        if (!SvOK(sv) || !(SvROK(sv) || (SvPOK(sv) && SvCUR(sv))))
+            XSRETURN_UNDEF;
 
-       ST(0) = boolSV(sv_derived_from_sv(sv, ST(1), 0));
-       XSRETURN(1);
+        ST(0) = boolSV(sv_derived_from_sv(sv, ST(1), 0));
+        XSRETURN(1);
     }
 }
 
+XS(XS_UNIVERSAL_import_unimport); /* prototype to pass -Wmissing-prototypes */
+XS(XS_UNIVERSAL_import_unimport)
+{
+    dXSARGS;
+    dXSI32;
+
+    if (items > 1) {
+        char *class_pv= SvPV_nolen(ST(0));
+        if (strEQ(class_pv,"UNIVERSAL"))
+            Perl_croak(aTHX_ "UNIVERSAL does not export anything");
+        /* _charnames is special - ignore it for now as the code that
+         * depends on it has its own "no import" logic that produces better
+         * warnings than this does. */
+        if (strNE(class_pv,"_charnames"))
+            Perl_ck_warner_d(aTHX_
+                packWARN(WARN_DEPRECATED__MISSING_IMPORT_CALLED_WITH_ARGS),
+                "Attempt to call undefined %s method with arguments "
+                "(%" SVf_QUOTEDPREFIX "%s) via package "
+                "%" SVf_QUOTEDPREFIX " (Perhaps you forgot to load the package?)",
+                ix ? "unimport" : "import", 
+                SVfARG(ST(1)), 
+                (items > 2 ? " ..." : ""),
+                SVfARG(ST(0)));
+    }
+    XSRETURN_EMPTY;
+}
+
+
+XS(XS_UNIVERSAL_can); /* prototype to pass -Wmissing-prototypes */
 XS(XS_UNIVERSAL_can)
 {
-    dVAR;
     dXSARGS;
     SV   *sv;
     SV   *rv;
     HV   *pkg = NULL;
+    GV   *iogv;
 
     if (items != 2)
-       croak_xs_usage(cv, "object-ref, method");
+        croak_xs_usage(cv, "object-ref, method");
 
     sv = ST(0);
 
     SvGETMAGIC(sv);
 
-    if (!SvOK(sv) || !(SvROK(sv) || SvNIOK(sv) || (SvPOK(sv) && SvCUR(sv))
-       || (SvGMAGICAL(sv) && (SvNIOKp(sv) || (SvPOKp(sv) && SvCUR(sv))))))
-       XSRETURN_UNDEF;
+    /* Reject undef and empty string.  Note that the string form takes
+       precedence here over the numeric form, as (!1)->foo treats the
+       invocant as the empty string, though it is a dualvar. */
+    if (!SvOK(sv) || (SvPOK(sv) && !SvCUR(sv)))
+        XSRETURN_UNDEF;
 
     rv = &PL_sv_undef;
 
@@ -366,531 +511,185 @@ XS(XS_UNIVERSAL_can)
         sv = MUTABLE_SV(SvRV(sv));
         if (SvOBJECT(sv))
             pkg = SvSTASH(sv);
+        else if (isGV_with_GP(sv) && GvIO(sv))
+            pkg = SvSTASH(GvIO(sv));
     }
+    else if (isGV_with_GP(sv) && GvIO(sv))
+        pkg = SvSTASH(GvIO(sv));
+    else if ((iogv = gv_fetchsv_nomg(sv, 0, SVt_PVIO)) && GvIO(iogv))
+        pkg = SvSTASH(GvIO(iogv));
     else {
         pkg = gv_stashsv(sv, 0);
+        if (!pkg)
+            pkg = gv_stashpvs("UNIVERSAL", 0);
     }
 
     if (pkg) {
-       GV * const gv = gv_fetchmethod_sv_flags(pkg, ST(1), 0);
+        GV * const gv = gv_fetchmethod_sv_flags(pkg, ST(1), 0);
         if (gv && isGV(gv))
-           rv = sv_2mortal(newRV(MUTABLE_SV(GvCV(gv))));
+            rv = sv_2mortal(newRV(MUTABLE_SV(GvCV(gv))));
     }
 
     ST(0) = rv;
     XSRETURN(1);
 }
 
+XS(XS_UNIVERSAL_DOES); /* prototype to pass -Wmissing-prototypes */
 XS(XS_UNIVERSAL_DOES)
 {
-    dVAR;
     dXSARGS;
     PERL_UNUSED_ARG(cv);
 
     if (items != 2)
-       Perl_croak(aTHX_ "Usage: invocant->DOES(kind)");
+        Perl_croak(aTHX_ "Usage: invocant->DOES(kind)");
     else {
-       SV * const sv = ST(0);
-       if (sv_does_sv( sv, ST(1), 0 ))
-           XSRETURN_YES;
-
-       XSRETURN_NO;
-    }
-}
-
-XS(XS_UNIVERSAL_VERSION)
-{
-    dVAR;
-    dXSARGS;
-    HV *pkg;
-    GV **gvp;
-    GV *gv;
-    SV *sv;
-    const char *undef;
-    PERL_UNUSED_ARG(cv);
+        SV * const sv = ST(0);
+        if (sv_does_sv( sv, ST(1), 0 ))
+            XSRETURN_YES;
 
-    if (SvROK(ST(0))) {
-        sv = MUTABLE_SV(SvRV(ST(0)));
-        if (!SvOBJECT(sv))
-            Perl_croak(aTHX_ "Cannot find version of an unblessed reference");
-        pkg = SvSTASH(sv);
-    }
-    else {
-        pkg = gv_stashsv(ST(0), 0);
-    }
-
-    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_isobject(sv) || !sv_derived_from(sv, "version"))
-           upg_version(sv, FALSE);
-
-        undef = NULL;
-    }
-    else {
-        sv = &PL_sv_undef;
-        undef = "(undef)";
-    }
-
-    if (items > 1) {
-       SV *req = ST(1);
-
-       if (undef) {
-           if (pkg) {
-               const HEK * const name = HvNAME_HEK(pkg);
-               Perl_croak(aTHX_
-                          "%"HEKf" does not define $%"HEKf
-                          "::VERSION--version check failed",
-                          HEKfARG(name), HEKfARG(name));
-           } else {
-               Perl_croak(aTHX_
-                            "%"SVf" defines neither package nor VERSION--version check failed",
-                            SVfARG(ST(0)) );
-            }
-       }
-
-       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_ "%"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_ "%"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) = sv_2mortal(vstringify(sv));
-    } else {
-       ST(0) = sv;
-    }
-
-    XSRETURN(1);
-}
-
-XS(XS_version_new)
-{
-    dVAR;
-    dXSARGS;
-    if (items > 3)
-       croak_xs_usage(cv, "class, version");
-    SP -= items;
-    {
-        SV *vs = ST(1);
-       SV *rv;
-        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 */
-           vs = sv_newmortal();
-           sv_setpvs(vs, "0");
-       }
-       else if ( items == 3 ) {
-           vs = sv_newmortal();
-           Perl_sv_setpvf(aTHX_ vs,"v%s",SvPV_nolen_const(ST(2)));
-       }
-
-       rv = new_version(vs);
-       if ( strnNE(classname,"version", len) ) /* inherited new() */
-           sv_bless(rv, gv_stashpvn(classname, len, GV_ADD | flags));
-
-       mPUSHs(rv);
-       PUTBACK;
-       return;
-    }
-}
-
-XS(XS_version_stringify)
-{
-     dVAR;
-     dXSARGS;
-     if (items < 1)
-        croak_xs_usage(cv, "lobj, ...");
-     SP -= items;
-     {
-         SV *  lobj = ST(0);
-
-         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
-              lobj = SvRV(lobj);
-         }
-         else
-              Perl_croak(aTHX_ "lobj is not of type version");
-
-         mPUSHs(vstringify(lobj));
-
-         PUTBACK;
-         return;
-     }
-}
-
-XS(XS_version_numify)
-{
-     dVAR;
-     dXSARGS;
-     if (items < 1)
-        croak_xs_usage(cv, "lobj, ...");
-     SP -= items;
-     {
-         SV *  lobj = ST(0);
-
-         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
-              lobj = SvRV(lobj);
-         }
-         else
-              Perl_croak(aTHX_ "lobj is not of type version");
-
-         mPUSHs(vnumify(lobj));
-
-         PUTBACK;
-         return;
-     }
-}
-
-XS(XS_version_normal)
-{
-     dVAR;
-     dXSARGS;
-     if (items < 1)
-        croak_xs_usage(cv, "lobj, ...");
-     SP -= items;
-     {
-         SV *  lobj = ST(0);
-
-         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
-              lobj = SvRV(lobj);
-         }
-         else
-              Perl_croak(aTHX_ "lobj is not of type version");
-
-         mPUSHs(vnormal(lobj));
-
-         PUTBACK;
-         return;
-     }
-}
-
-XS(XS_version_vcmp)
-{
-     dVAR;
-     dXSARGS;
-     if (items < 1)
-        croak_xs_usage(cv, "lobj, ...");
-     SP -= items;
-     {
-         SV *  lobj = ST(0);
-
-         if (sv_isobject(lobj) && sv_derived_from(lobj, "version")) {
-              lobj = SvRV(lobj);
-         }
-         else
-              Perl_croak(aTHX_ "lobj is not of type version");
-
-         {
-              SV       *rs;
-              SV       *rvs;
-              SV * robj = ST(1);
-              const IV  swap = (IV)SvIV(ST(2));
-
-              if ( !sv_isobject(robj) || !sv_derived_from(robj, "version") )
-              {
-                   robj = new_version(SvOK(robj) ? robj : newSVpvs_flags("0", SVs_TEMP));
-                   sv_2mortal(robj);
-              }
-              rvs = SvRV(robj);
-
-              if ( swap )
-              {
-                   rs = newSViv(vcmp(rvs,lobj));
-              }
-              else
-              {
-                   rs = newSViv(vcmp(lobj,rvs));
-              }
-
-              mPUSHs(rs);
-         }
-
-         PUTBACK;
-         return;
-     }
-}
-
-XS(XS_version_boolean)
-{
-    dVAR;
-    dXSARGS;
-    if (items < 1)
-       croak_xs_usage(cv, "lobj, ...");
-    SP -= items;
-    if (sv_isobject(ST(0)) && sv_derived_from(ST(0), "version")) {
-       SV * const lobj = SvRV(ST(0));
-       SV * const rs =
-           newSViv( vcmp(lobj,
-                         sv_2mortal(new_version(
-                                       sv_2mortal(newSVpvs("0"))
-                                   ))
-                        )
-                  );
-       mPUSHs(rs);
-       PUTBACK;
-       return;
-    }
-    else
-       Perl_croak(aTHX_ "lobj is not of type version");
-}
-
-XS(XS_version_noop)
-{
-    dVAR;
-    dXSARGS;
-    if (items < 1)
-       croak_xs_usage(cv, "lobj, ...");
-    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");
-#ifndef HASATTRIBUTE_NORETURN
-    XSRETURN_EMPTY;
-#endif
-}
-
-XS(XS_version_is_alpha)
-{
-    dVAR;
-    dXSARGS;
-    if (items != 1)
-       croak_xs_usage(cv, "lobj");
-    SP -= items;
-    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;
-       else
-           XSRETURN_NO;
-       PUTBACK;
-       return;
-    }
-    else
-       Perl_croak(aTHX_ "lobj is not of type version");
-}
-
-XS(XS_version_qv)
-{
-    dVAR;
-    dXSARGS;
-    PERL_UNUSED_ARG(cv);
-    SP -= items;
-    {
-       SV * ver = ST(0);
-       SV * rv;
-        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 */
-           upg_version(rv, TRUE);
-       } else {
-           rv = sv_2mortal(new_version(ver));
-       }
-       if ( items == 2
-                && strnNE(classname,"version", len) ) { /* inherited new() */
-           sv_bless(rv, gv_stashpvn(classname, len, GV_ADD | flags));
-        }
-       PUSHs(rv);
-    }
-    PUTBACK;
-    return;
-}
-
-XS(XS_version_is_qv)
-{
-    dVAR;
-    dXSARGS;
-    if (items != 1)
-       croak_xs_usage(cv, "lobj");
-    SP -= items;
-    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;
-       else
-           XSRETURN_NO;
-       PUTBACK;
-       return;
+        XSRETURN_NO;
     }
-    else
-       Perl_croak(aTHX_ "lobj is not of type version");
 }
 
+XS(XS_utf8_is_utf8); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_is_utf8)
 {
-     dVAR;
      dXSARGS;
      if (items != 1)
-        croak_xs_usage(cv, "sv");
+         croak_xs_usage(cv, "sv");
      else {
-       SV * const sv = ST(0);
-       SvGETMAGIC(sv);
-           if (SvUTF8(sv))
-               XSRETURN_YES;
-           else
-               XSRETURN_NO;
+        SV * const sv = ST(0);
+        SvGETMAGIC(sv);
+            if (SvUTF8(sv))
+                XSRETURN_YES;
+            else
+                XSRETURN_NO;
      }
      XSRETURN_EMPTY;
 }
 
+XS(XS_utf8_valid); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_valid)
 {
-     dVAR;
      dXSARGS;
      if (items != 1)
-        croak_xs_usage(cv, "sv");
+         croak_xs_usage(cv, "sv");
     else {
-       SV * const sv = ST(0);
-       STRLEN len;
-       const char * const s = SvPV_const(sv,len);
-       if (!SvUTF8(sv) || is_utf8_string((const U8*)s,len))
-           XSRETURN_YES;
-       else
-           XSRETURN_NO;
+        SV * const sv = ST(0);
+        STRLEN len;
+        const char * const s = SvPV_const(sv,len);
+        if (!SvUTF8(sv) || is_utf8_string((const U8*)s,len))
+            XSRETURN_YES;
+        else
+            XSRETURN_NO;
     }
      XSRETURN_EMPTY;
 }
 
+XS(XS_utf8_encode); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_encode)
 {
-    dVAR;
     dXSARGS;
     if (items != 1)
-       croak_xs_usage(cv, "sv");
+        croak_xs_usage(cv, "sv");
     sv_utf8_encode(ST(0));
+    SvSETMAGIC(ST(0));
     XSRETURN_EMPTY;
 }
 
+XS(XS_utf8_decode); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_decode)
 {
-    dVAR;
     dXSARGS;
     if (items != 1)
-       croak_xs_usage(cv, "sv");
+        croak_xs_usage(cv, "sv");
     else {
-       SV * const sv = ST(0);
-       bool RETVAL;
-       SvPV_force_nolen(sv);
-       RETVAL = sv_utf8_decode(sv);
-       ST(0) = boolSV(RETVAL);
+        SV * const sv = ST(0);
+        bool RETVAL;
+        SvPV_force_nolen(sv);
+        RETVAL = sv_utf8_decode(sv);
+        SvSETMAGIC(sv);
+        ST(0) = boolSV(RETVAL);
     }
     XSRETURN(1);
 }
 
+XS(XS_utf8_upgrade); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_upgrade)
 {
-    dVAR;
     dXSARGS;
     if (items != 1)
-       croak_xs_usage(cv, "sv");
+        croak_xs_usage(cv, "sv");
     else {
-       SV * const sv = ST(0);
-       STRLEN  RETVAL;
-       dXSTARG;
+        SV * const sv = ST(0);
+        STRLEN RETVAL = 0;
+        dXSTARG;
+
+        XSprePUSH;
+        if (UNLIKELY(! sv)) {
+            XSRETURN_UNDEF;
+        }
 
-       RETVAL = sv_utf8_upgrade(sv);
-       XSprePUSH; PUSHi((IV)RETVAL);
+        SvGETMAGIC(sv);
+        if (UNLIKELY(! SvOK(sv))) {
+            XSRETURN_UNDEF;
+        }
+
+        RETVAL = sv_utf8_upgrade_nomg(sv);
+        PUSHi( (IV) RETVAL);
     }
     XSRETURN(1);
 }
 
+XS(XS_utf8_downgrade); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_downgrade)
 {
-    dVAR;
     dXSARGS;
     if (items < 1 || items > 2)
-       croak_xs_usage(cv, "sv, failok=0");
+        croak_xs_usage(cv, "sv, failok=0");
     else {
-       SV * const sv = ST(0);
-        const bool failok = (items < 2) ? 0 : (int)SvIV(ST(1));
-        const bool RETVAL = sv_utf8_downgrade(sv, failok);
+        SV * const sv0 = ST(0);
+        SV * const sv1 = ST(1);
+        const bool failok = (items < 2) ? 0 : SvTRUE_NN(sv1) ? 1 : 0;
+        const bool RETVAL = sv_utf8_downgrade(sv0, failok);
 
-       ST(0) = boolSV(RETVAL);
+        ST(0) = boolSV(RETVAL);
     }
     XSRETURN(1);
 }
 
+XS(XS_utf8_native_to_unicode); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_native_to_unicode)
 {
- dVAR;
  dXSARGS;
  const UV uv = SvUV(ST(0));
 
  if (items > 1)
      croak_xs_usage(cv, "sv");
 
- ST(0) = sv_2mortal(newSViv(NATIVE_TO_UNI(uv)));
+ ST(0) = sv_2mortal(newSVuv(NATIVE_TO_UNI(uv)));
  XSRETURN(1);
 }
 
+XS(XS_utf8_unicode_to_native); /* prototype to pass -Wmissing-prototypes */
 XS(XS_utf8_unicode_to_native)
 {
- dVAR;
  dXSARGS;
  const UV uv = SvUV(ST(0));
 
  if (items > 1)
      croak_xs_usage(cv, "sv");
 
- ST(0) = sv_2mortal(newSViv(UNI_TO_NATIVE(uv)));
+ ST(0) = sv_2mortal(newSVuv(UNI_TO_NATIVE(uv)));
  XSRETURN(1);
 }
 
+XS(XS_Internals_SvREADONLY); /* prototype to pass -Wmissing-prototypes */
 XS(XS_Internals_SvREADONLY)    /* This is dangerous stuff. */
 {
-    dVAR;
     dXSARGS;
     SV * const svz = ST(0);
     SV * sv;
-    PERL_UNUSED_ARG(cv);
 
     /* [perl #77776] - called as &foo() not foo() */
     if (!SvROK(svz))
@@ -899,235 +698,240 @@ XS(XS_Internals_SvREADONLY)     /* This is dangerous stuff. */
     sv = SvRV(svz);
 
     if (items == 1) {
-        if (SvREADONLY(sv) && !SvIsCOW(sv))
-            XSRETURN_YES;
-        else
-            XSRETURN_NO;
+         if (SvREADONLY(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. */
-           if (!SvIsCOW(sv)) SvREADONLY_off(sv);
-           XSRETURN_NO;
-       }
+        SV *sv1 = ST(1);
+        if (SvTRUE_NN(sv1)) {
+            SvFLAGS(sv) |= SVf_READONLY;
+            XSRETURN_YES;
+        }
+        else {
+            /* I hope you really know what you are doing. */
+            SvFLAGS(sv) &=~ SVf_READONLY;
+            XSRETURN_NO;
+        }
     }
     XSRETURN_UNDEF; /* Can't happen. */
 }
 
+XS(XS_constant__make_const); /* prototype to pass -Wmissing-prototypes */
+XS(XS_constant__make_const)    /* This is dangerous stuff. */
+{
+    dXSARGS;
+    SV * const svz = ST(0);
+    SV * sv;
+
+    /* [perl #77776] - called as &foo() not foo() */
+    if (!SvROK(svz) || items != 1)
+        croak_xs_usage(cv, "SCALAR");
+
+    sv = SvRV(svz);
+
+    SvREADONLY_on(sv);
+    if (SvTYPE(sv) == SVt_PVAV && AvFILLp(sv) != -1) {
+        /* for constant.pm; nobody else should be calling this
+           on arrays anyway. */
+        SV **svp;
+        for (svp = AvARRAY(sv) + AvFILLp(sv)
+           ; svp >= AvARRAY(sv)
+           ; --svp)
+            if (*svp) SvPADTMP_on(*svp);
+    }
+    XSRETURN(0);
+}
+
+XS(XS_Internals_SvREFCNT); /* prototype to pass -Wmissing-prototypes */
 XS(XS_Internals_SvREFCNT)      /* This is dangerous stuff. */
 {
-    dVAR;
     dXSARGS;
     SV * const svz = ST(0);
     SV * sv;
-    PERL_UNUSED_ARG(cv);
+    U32 refcnt;
 
     /* [perl #77776] - called as &foo() not foo() */
-    if (!SvROK(svz))
+    if ((items != 1 && items != 2) || !SvROK(svz))
         croak_xs_usage(cv, "SCALAR[, REFCOUNT]");
 
     sv = SvRV(svz);
 
-    if (items == 1)
-        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) = SvUV(ST(1)) + 1; /* we free one ref on exit */
-        XSRETURN_UV(SvREFCNT(sv) - 1);
-    }
-    XSRETURN_UNDEF; /* Can't happen. */
+    /* idea is for SvREFCNT(sv) to be accessed only once */
+    refcnt = items == 2 ?
+                /* we free one ref on exit */
+                (SvREFCNT(sv) = SvUV(ST(1)) + 1)
+                : SvREFCNT(sv);
+    XSRETURN_UV(refcnt - 1); /* Minus the ref created for us. */        
+
 }
 
+XS(XS_Internals_hv_clear_placehold); /* prototype to pass -Wmissing-prototypes */
 XS(XS_Internals_hv_clear_placehold)
 {
-    dVAR;
     dXSARGS;
 
     if (items != 1 || !SvROK(ST(0)))
-       croak_xs_usage(cv, "hv");
+        croak_xs_usage(cv, "hv");
     else {
-       HV * const hv = MUTABLE_HV(SvRV(ST(0)));
-       hv_clear_placeholders(hv);
-       XSRETURN(0);
+        HV * const hv = MUTABLE_HV(SvRV(ST(0)));
+        hv_clear_placeholders(hv);
+        XSRETURN(0);
     }
 }
 
+XS(XS_Internals_stack_refcounted); /* prototype to pass -Wmissing-prototypes */
+XS(XS_Internals_stack_refcounted)
+{
+    dXSARGS;
+    UV val = 0;
+
+    if (items != 0)
+        croak_xs_usage(cv, "");
+#ifdef PERL_RC_STACK
+    val |= 1;
+#endif
+    XSRETURN_UV(val);
+}
+
+XS(XS_PerlIO_get_layers); /* prototype to pass -Wmissing-prototypes */
 XS(XS_PerlIO_get_layers)
 {
-    dVAR;
     dXSARGS;
     if (items < 1 || items % 2 == 0)
-       croak_xs_usage(cv, "filehandle[,args]");
-#ifdef USE_PERLIO
+        croak_xs_usage(cv, "filehandle[,args]");
+#if defined(USE_PERLIO)
     {
-       SV *    sv;
-       GV *    gv;
-       IO *    io;
-       bool    input = TRUE;
-       bool    details = FALSE;
-
-       if (items > 1) {
-            SV * const *svp;
-            for (svp = MARK + 2; svp <= SP; svp += 2) {
-                 SV * const * const varp = svp;
-                 SV * const * const valp = svp + 1;
-                 STRLEN klen;
-                 const char * const key = SvPV_const(*varp, klen);
-
-                 switch (*key) {
-                 case 'i':
-                      if (klen == 5 && memEQ(key, "input", 5)) {
-                           input = SvTRUE(*valp);
-                           break;
-                      }
-                      goto fail;
-                 case 'o': 
-                      if (klen == 6 && memEQ(key, "output", 6)) {
-                           input = !SvTRUE(*valp);
-                           break;
-                      }
-                      goto fail;
-                 case 'd':
-                      if (klen == 7 && memEQ(key, "details", 7)) {
-                           details = SvTRUE(*valp);
-                           break;
-                      }
-                      goto fail;
-                 default:
-                 fail:
-                      Perl_croak(aTHX_
-                                 "get_layers: unknown argument '%s'",
-                                 key);
-                 }
-            }
-
-            SP -= (items - 1);
-       }
-
-       sv = POPs;
-       gv = MAYBE_DEREF_GV(sv);
-
-       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 ?
-                                       IoIFP(io) : IoOFP(io));
-            I32 i;
-            const I32 last = av_len(av);
-            I32 nitem = 0;
-            
-            for (i = last; i >= 0; i -= 3) {
-                 SV * const * const namsvp = av_fetch(av, i - 2, FALSE);
-                 SV * const * const argsvp = av_fetch(av, i - 1, FALSE);
-                 SV * const * const flgsvp = av_fetch(av, i,     FALSE);
-
-                 const bool namok = namsvp && *namsvp && SvPOK(*namsvp);
-                 const bool argok = argsvp && *argsvp && SvPOK(*argsvp);
-                 const bool flgok = flgsvp && *flgsvp && SvIOK(*flgsvp);
-
-                 if (details) {
-                     /* Indents of 5? Yuck.  */
-                     /* We know that PerlIO_get_layers creates a new SV for
-                        the name and flags, so we can just take a reference
-                        and "steal" it when we free the AV below.  */
-                      XPUSHs(namok
-                             ? sv_2mortal(SvREFCNT_inc_simple_NN(*namsvp))
-                             : &PL_sv_undef);
-                      XPUSHs(argok
-                             ? newSVpvn_flags(SvPVX_const(*argsvp),
-                                              SvCUR(*argsvp),
-                                              (SvUTF8(*argsvp) ? SVf_UTF8 : 0)
-                                              | SVs_TEMP)
-                             : &PL_sv_undef);
-                      XPUSHs(flgok
-                             ? sv_2mortal(SvREFCNT_inc_simple_NN(*flgsvp))
-                             : &PL_sv_undef);
-                      nitem += 3;
-                 }
-                 else {
-                      if (namok && argok)
-                           XPUSHs(sv_2mortal(Perl_newSVpvf(aTHX_ "%"SVf"(%"SVf")",
-                                                SVfARG(*namsvp),
-                                                SVfARG(*argsvp))));
-                      else if (namok)
-                          XPUSHs(sv_2mortal(SvREFCNT_inc_simple_NN(*namsvp)));
-                      else
-                           XPUSHs(&PL_sv_undef);
-                      nitem++;
-                      if (flgok) {
-                           const IV flags = SvIVX(*flgsvp);
-
-                           if (flags & PERLIO_F_UTF8) {
-                                XPUSHs(newSVpvs_flags("utf8", SVs_TEMP));
-                                nitem++;
-                           }
-                      }
-                 }
-            }
-
-            SvREFCNT_dec(av);
-
-            XSRETURN(nitem);
-       }
+        SV *   sv;
+        GV *   gv;
+        IO *   io = NULL;
+        bool   input = TRUE;
+        bool   details = FALSE;
+
+        if (items > 1) {
+             SV * const *svp;
+             for (svp = MARK + 2; svp <= SP; svp += 2) {
+                  SV * const * const varp = svp;
+                  SV * const * const valp = svp + 1;
+                  STRLEN klen;
+                  const char * const key = SvPV_const(*varp, klen);
+
+                  switch (*key) {
+                  case 'i':
+                       if (memEQs(key, klen, "input")) {
+                            input = SvTRUE(*valp);
+                            break;
+                       }
+                       goto fail;
+                  case 'o': 
+                       if (memEQs(key, klen, "output")) {
+                            input = !SvTRUE(*valp);
+                            break;
+                       }
+                       goto fail;
+                  case 'd':
+                       if (memEQs(key, klen, "details")) {
+                            details = SvTRUE(*valp);
+                            break;
+                       }
+                       goto fail;
+                  default:
+                  fail:
+                       Perl_croak(aTHX_
+                                  "get_layers: unknown argument '%s'",
+                                  key);
+                  }
+             }
+
+             SP -= (items - 1);
+        }
+
+        sv = POPs;
+
+        /* MAYBE_DEREF_GV will call get magic */
+        if ((gv = MAYBE_DEREF_GV(sv)))
+            io = GvIO(gv);
+        else if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVIO)
+            io = (IO*)SvRV(sv);
+        else if (!SvROK(sv) && (gv = gv_fetchsv_nomg(sv, 0, SVt_PVIO)))
+            io = GvIO(gv);
+
+        if (io) {
+             AV* const av = PerlIO_get_layers(aTHX_ input ?
+                                        IoIFP(io) : IoOFP(io));
+             SSize_t i;
+             const SSize_t last = av_top_index(av);
+             SSize_t nitem = 0;
+             
+             for (i = last; i >= 0; i -= 3) {
+                  SV * const * const namsvp = av_fetch(av, i - 2, FALSE);
+                  SV * const * const argsvp = av_fetch(av, i - 1, FALSE);
+                  SV * const * const flgsvp = av_fetch(av, i,     FALSE);
+
+                  const bool namok = namsvp && *namsvp && SvPOK(*namsvp);
+                  const bool argok = argsvp && *argsvp && SvPOK(*argsvp);
+                  const bool flgok = flgsvp && *flgsvp && SvIOK(*flgsvp);
+
+                  EXTEND(SP, 3); /* Three is the max in all branches: better check just once */
+                  if (details) {
+                      /* Indents of 5? Yuck.  */
+                      /* We know that PerlIO_get_layers creates a new SV for
+                         the name and flags, so we can just take a reference
+                         and "steal" it when we free the AV below.  */
+                       PUSHs(namok
+                              ? sv_2mortal(SvREFCNT_inc_simple_NN(*namsvp))
+                              : &PL_sv_undef);
+                       PUSHs(argok
+                              ? newSVpvn_flags(SvPVX_const(*argsvp),
+                                               SvCUR(*argsvp),
+                                               (SvUTF8(*argsvp) ? SVf_UTF8 : 0)
+                                               | SVs_TEMP)
+                              : &PL_sv_undef);
+                       PUSHs(flgok
+                              ? sv_2mortal(SvREFCNT_inc_simple_NN(*flgsvp))
+                              : &PL_sv_undef);
+                       nitem += 3;
+                  }
+                  else {
+                       if (namok && argok)
+                            PUSHs(sv_2mortal(Perl_newSVpvf(aTHX_ "%" SVf "(%" SVf ")",
+                                                 SVfARG(*namsvp),
+                                                 SVfARG(*argsvp))));
+                       else if (namok)
+                            PUSHs(sv_2mortal(SvREFCNT_inc_simple_NN(*namsvp)));
+                       else
+                            PUSHs(&PL_sv_undef);
+                       nitem++;
+                       if (flgok) {
+                            const IV flags = SvIVX(*flgsvp);
+
+                            if (flags & PERLIO_F_UTF8) {
+                                 PUSHs(newSVpvs_flags("utf8", SVs_TEMP));
+                                 nitem++;
+                            }
+                       }
+                  }
+             }
+
+             SvREFCNT_dec(av);
+
+             XSRETURN(nitem);
+        }
     }
 #endif
 
     XSRETURN(0);
 }
 
-XS(XS_Internals_hash_seed)
-{
-    dVAR;
-    /* Using dXSARGS would also have dITEM and dSP,
-     * which define 2 unused local variables.  */
-    dAXMARK;
-    PERL_UNUSED_ARG(cv);
-    PERL_UNUSED_VAR(mark);
-    XSRETURN_UV(PERL_HASH_SEED);
-}
-
-XS(XS_Internals_rehash_seed)
-{
-    dVAR;
-    /* Using dXSARGS would also have dITEM and dSP,
-     * which define 2 unused local variables.  */
-    dAXMARK;
-    PERL_UNUSED_ARG(cv);
-    PERL_UNUSED_VAR(mark);
-    XSRETURN_UV(PL_rehash_seed);
-}
-
-XS(XS_Internals_HvREHASH)      /* Subject to change  */
-{
-    dVAR;
-    dXSARGS;
-    PERL_UNUSED_ARG(cv);
-    if (SvROK(ST(0))) {
-       const HV * const hv = (const HV *) SvRV(ST(0));
-       if (items == 1 && SvTYPE(hv) == SVt_PVHV) {
-           if (HvREHASH(hv))
-               XSRETURN_YES;
-           else
-               XSRETURN_NO;
-       }
-    }
-    Perl_croak(aTHX_ "Internals::HvREHASH $hashref");
-}
-
+XS(XS_re_is_regexp); /* prototype to pass -Wmissing-prototypes */
 XS(XS_re_is_regexp)
 {
-    dVAR; 
     dXSARGS;
-    PERL_UNUSED_VAR(cv);
 
     if (items != 1)
-       croak_xs_usage(cv, "sv");
+        croak_xs_usage(cv, "sv");
 
     if (SvRXOK(ST(0))) {
         XSRETURN_YES;
@@ -1136,18 +940,15 @@ XS(XS_re_is_regexp)
     }
 }
 
+XS(XS_re_regnames_count); /* prototype to pass -Wmissing-prototypes */
 XS(XS_re_regnames_count)
 {
     REGEXP *rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
     SV * ret;
-    dVAR; 
     dXSARGS;
 
     if (items != 0)
-       croak_xs_usage(cv, "");
-
-    SP -= items;
-    PUTBACK;
+        croak_xs_usage(cv, "");
 
     if (!rx)
         XSRETURN_UNDEF;
@@ -1159,16 +960,16 @@ XS(XS_re_regnames_count)
     XSRETURN(1);
 }
 
+XS(XS_re_regname); /* prototype to pass -Wmissing-prototypes */
 XS(XS_re_regname)
 {
-    dVAR;
     dXSARGS;
     REGEXP * rx;
     U32 flags;
     SV * ret;
 
     if (items < 1 || items > 2)
-       croak_xs_usage(cv, "name[, all ]");
+        croak_xs_usage(cv, "name[, all ]");
 
     SP -= items;
     PUTBACK;
@@ -1178,7 +979,7 @@ XS(XS_re_regname)
     if (!rx)
         XSRETURN_UNDEF;
 
-    if (items == 2 && SvTRUE(ST(1))) {
+    if (items == 2 && SvTRUE_NN(ST(1))) {
         flags = RXapif_ALL;
     } else {
         flags = RXapif_ONE;
@@ -1191,27 +992,27 @@ XS(XS_re_regname)
 }
 
 
+XS(XS_re_regnames); /* prototype to pass -Wmissing-prototypes */
 XS(XS_re_regnames)
 {
-    dVAR;
     dXSARGS;
     REGEXP * rx;
     U32 flags;
     SV *ret;
     AV *av;
-    I32 length;
-    I32 i;
+    SSize_t length;
+    SSize_t i;
     SV **entry;
 
     if (items > 1)
-       croak_xs_usage(cv, "[all]");
+        croak_xs_usage(cv, "[all]");
 
     rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
 
     if (!rx)
         XSRETURN_UNDEF;
 
-    if (items == 1 && SvTRUE(ST(0))) {
+    if (items == 1 && SvTRUE_NN(ST(0))) {
         flags = RXapif_ALL;
     } else {
         flags = RXapif_ONE;
@@ -1228,15 +1029,17 @@ XS(XS_re_regnames)
         XSRETURN_UNDEF;
 
     av = MUTABLE_AV(SvRV(ret));
-    length = av_len(av);
+    length = av_count(av);
 
-    for (i = 0; i <= length; i++) {
+    EXTEND(SP, length); /* better extend stack just once */
+    for (i = 0; i < length; i++) {
         entry = av_fetch(av, i, FALSE);
         
         if (!entry)
+            /* diag_listed_as: SKIPME */
             Perl_croak(aTHX_ "NULL array element in re::regnames()");
 
-        mXPUSHs(SvREFCNT_inc_simple_NN(*entry));
+        mPUSHs(SvREFCNT_inc_simple_NN(*entry));
     }
 
     SvREFCNT_dec(ret);
@@ -1245,16 +1048,17 @@ XS(XS_re_regnames)
     return;
 }
 
+XS(XS_re_regexp_pattern); /* prototype to pass -Wmissing-prototypes */
 XS(XS_re_regexp_pattern)
 {
-    dVAR;
     dXSARGS;
     REGEXP *re;
+    U8 const gimme = GIMME_V;
 
-    if (items != 1)
-       croak_xs_usage(cv, "sv");
-
+    EXTEND(SP, 2);
     SP -= items;
+    if (items != 1)
+        croak_xs_usage(cv, "sv");
 
     /*
        Checks if a reference is a regex or not. If the parameter is
@@ -1272,9 +1076,9 @@ XS(XS_re_regexp_pattern)
         /* Houston, we have a regex! */
         SV *pattern;
 
-        if ( GIMME_V == G_ARRAY ) {
-           STRLEN left = 0;
-           char reflags[sizeof(INT_PAT_MODS) + MAX_CHARSET_NAME_LENGTH];
+        if ( gimme == G_LIST ) {
+            STRLEN left = 0;
+            char reflags[sizeof(INT_PAT_MODS) + MAX_CHARSET_NAME_LENGTH];
             const char *fptr;
             char ch;
             U16 match_flags;
@@ -1285,13 +1089,13 @@ XS(XS_re_regexp_pattern)
                modifiers" in this scenario, and the default character set
             */
 
-           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;
-           }
+            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);
@@ -1304,29 +1108,24 @@ XS(XS_re_regexp_pattern)
             }
 
             pattern = newSVpvn_flags(RX_PRECOMP(re),RX_PRELEN(re),
-                                    (RX_UTF8(re) ? SVf_UTF8 : 0) | SVs_TEMP);
+                                     (RX_UTF8(re) ? SVf_UTF8 : 0) | SVs_TEMP);
 
             /* return the pattern and the modifiers */
-            XPUSHs(pattern);
-            XPUSHs(newSVpvn_flags(reflags, left, SVs_TEMP));
+            PUSHs(pattern);
+            PUSHs(newSVpvn_flags(reflags, left, SVs_TEMP));
             XSRETURN(2);
         } else {
             /* Scalar, so use the string that Perl would return */
-            /* return the pattern in (?msix:..) format */
-#if PERL_VERSION >= 11
+            /* return the pattern in (?msixn:..) format */
             pattern = sv_2mortal(newSVsv(MUTABLE_SV(re)));
-#else
-            pattern = newSVpvn_flags(RX_WRAPPED(re), RX_WRAPLEN(re),
-                                    (RX_UTF8(re) ? SVf_UTF8 : 0) | SVs_TEMP);
-#endif
-            XPUSHs(pattern);
+            PUSHs(pattern);
             XSRETURN(1);
         }
     } else {
         /* It ain't a regexp folks */
-        if ( GIMME_V == G_ARRAY ) {
+        if ( gimme == G_LIST ) {
             /* return the empty list */
-            XSRETURN_UNDEF;
+            XSRETURN_EMPTY;
         } else {
             /* Because of the (?:..) wrapping involved in a
                stringified pattern it is impossible to get a
@@ -1342,99 +1141,325 @@ XS(XS_re_regexp_pattern)
             XSRETURN_NO;
         }
     }
-    /* NOT-REACHED */
+    NOT_REACHED; /* NOTREACHED */
+}
+
+#if defined(HAS_GETCWD) && defined(PERL_IS_MINIPERL)
+
+XS(XS_Internals_getcwd)
+{
+    dXSARGS;
+    SV *sv = sv_newmortal();
+
+    if (items != 0)
+        croak_xs_usage(cv, "");
+
+    (void)getcwd_sv(sv);
+
+    SvTAINTED_on(sv);
+    PUSHs(sv);
+    XSRETURN(1);
+}
+
+#endif
+
+XS(XS_NamedCapture_tie_it)
+{
+    dXSARGS;
+
+    if (items != 1)
+        croak_xs_usage(cv,  "sv");
+    {
+        SV *sv = ST(0);
+        GV * const gv = (GV *)sv;
+        HV * const hv = GvHVn(gv);
+        SV *rv = newSV_type(SVt_IV);
+        const char *gv_name = GvNAME(gv);
+
+        sv_setrv_noinc(rv, newSVuv(
+            strEQ(gv_name, "-") || strEQ(gv_name, "\003APTURE_ALL")
+            ? RXapif_ALL : RXapif_ONE));
+        sv_bless(rv, GvSTASH(CvGV(cv)));
+
+        sv_unmagic((SV *)hv, PERL_MAGIC_tied);
+        sv_magic((SV *)hv, rv, PERL_MAGIC_tied, NULL, 0);
+        SvREFCNT_dec(rv); /* As sv_magic increased it by one.  */
+    }
+    XSRETURN_EMPTY;
+}
+
+XS(XS_NamedCapture_TIEHASH)
+{
+    dXSARGS;
+    if (items < 1)
+       croak_xs_usage(cv,  "package, ...");
+    {
+        const char *   package = (const char *)SvPV_nolen(ST(0));
+        UV flag = RXapif_ONE;
+        mark += 2;
+        while(mark < sp) {
+            STRLEN len;
+            const char *p = SvPV_const(*mark, len);
+            if(memEQs(p, len, "all"))
+                flag = SvTRUE(mark[1]) ? RXapif_ALL : RXapif_ONE;
+            mark += 2;
+        }
+        ST(0) = newSV_type_mortal(SVt_IV);
+        sv_setuv(newSVrv(ST(0), package), flag);
+    }
+    XSRETURN(1);
 }
 
+/* These are tightly coupled to the RXapif_* flags defined in regexp.h  */
+#define UNDEF_FATAL  0x80000
+#define DISCARD      0x40000
+#define EXPECT_SHIFT 24
+#define ACTION_MASK  0x000FF
+
+#define FETCH_ALIAS  (RXapif_FETCH  | (2 << EXPECT_SHIFT))
+#define STORE_ALIAS  (RXapif_STORE  | (3 << EXPECT_SHIFT) | UNDEF_FATAL | DISCARD)
+#define DELETE_ALIAS (RXapif_DELETE | (2 << EXPECT_SHIFT) | UNDEF_FATAL)
+#define CLEAR_ALIAS  (RXapif_CLEAR  | (1 << EXPECT_SHIFT) | UNDEF_FATAL | DISCARD)
+#define EXISTS_ALIAS (RXapif_EXISTS | (2 << EXPECT_SHIFT))
+#define SCALAR_ALIAS (RXapif_SCALAR | (1 << EXPECT_SHIFT))
+
+XS(XS_NamedCapture_FETCH)
+{
+    dXSARGS;
+    dXSI32;
+    PERL_UNUSED_VAR(cv); /* -W */
+    PERL_UNUSED_VAR(ax); /* -Wall */
+    SP -= items;
+    {
+        REGEXP *const rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+        U32 flags;
+        SV *ret;
+        const U32 action = ix & ACTION_MASK;
+        const int expect = ix >> EXPECT_SHIFT;
+        if (items != expect)
+            croak_xs_usage(cv, expect == 2 ? "$key"
+                                           : (expect == 3 ? "$key, $value"
+                                                          : ""));
+
+        if (!rx || !SvROK(ST(0))) {
+            if (ix & UNDEF_FATAL)
+                Perl_croak_no_modify();
+            else
+                XSRETURN_UNDEF;
+        }
+
+        flags = (U32)SvUV(SvRV(MUTABLE_SV(ST(0))));
+
+        PUTBACK;
+        ret = RX_ENGINE(rx)->named_buff(aTHX_ (rx), expect >= 2 ? ST(1) : NULL,
+                                    expect >= 3 ? ST(2) : NULL, flags | action);
+        SPAGAIN;
+
+        if (ix & DISCARD) {
+            /* Called with G_DISCARD, so our return stack state is thrown away.
+               Hence if we were returned anything, free it immediately.  */
+            SvREFCNT_dec(ret);
+        } else {
+            PUSHs(ret ? sv_2mortal(ret) : &PL_sv_undef);
+        }
+        PUTBACK;
+        return;
+    }
+}
+
+
+XS(XS_NamedCapture_FIRSTKEY)
+{
+    dXSARGS;
+    dXSI32;
+    PERL_UNUSED_VAR(cv); /* -W */
+    PERL_UNUSED_VAR(ax); /* -Wall */
+    SP -= items;
+    {
+        REGEXP *const rx = PL_curpm ? PM_GETRE(PL_curpm) : NULL;
+        U32 flags;
+        SV *ret;
+        const int expect = ix ? 2 : 1;
+        const U32 action = ix ? RXapif_NEXTKEY : RXapif_FIRSTKEY;
+        if (items != expect)
+            croak_xs_usage(cv, expect == 2 ? "$lastkey" : "");
+
+        if (!rx || !SvROK(ST(0)))
+            XSRETURN_UNDEF;
+
+        flags = (U32)SvUV(SvRV(MUTABLE_SV(ST(0))));
+
+        PUTBACK;
+        ret = RX_ENGINE(rx)->named_buff_iter(aTHX_ (rx),
+                                             expect >= 2 ? ST(1) : NULL,
+                                             flags | action);
+        SPAGAIN;
+
+        PUSHs(ret ? sv_2mortal(ret) : &PL_sv_undef);
+        PUTBACK;
+        return;
+    }
+}
+
+/* is this still needed? */
+XS(XS_NamedCapture_flags)
+{
+    dXSARGS;
+    PERL_UNUSED_VAR(cv); /* -W */
+    PERL_UNUSED_VAR(ax); /* -Wall */
+    SP -= items;
+    {
+        EXTEND(SP, 2);
+        mPUSHu(RXapif_ONE);
+        mPUSHu(RXapif_ALL);
+        PUTBACK;
+        return;
+    }
+}
+
+#include "vutil.h"
+#include "vxs.inc"
+
 struct xsub_details {
     const char *name;
     XSUBADDR_t xsub;
     const char *proto;
+    int ix;
 };
 
-struct xsub_details details[] = {
-    {"UNIVERSAL::isa", XS_UNIVERSAL_isa, NULL},
-    {"UNIVERSAL::can", XS_UNIVERSAL_can, NULL},
-    {"UNIVERSAL::DOES", XS_UNIVERSAL_DOES, NULL},
-    {"UNIVERSAL::VERSION", XS_UNIVERSAL_VERSION, NULL},
-    {"version::()", XS_version_noop, NULL},
-    {"version::new", XS_version_new, NULL},
-    {"version::parse", XS_version_new, NULL},
-    {"version::(\"\"", XS_version_stringify, NULL},
-    {"version::stringify", XS_version_stringify, NULL},
-    {"version::(0+", XS_version_numify, NULL},
-    {"version::numify", XS_version_numify, NULL},
-    {"version::normal", XS_version_normal, NULL},
-    {"version::(cmp", XS_version_vcmp, NULL},
-    {"version::(<=>", XS_version_vcmp, NULL},
-    {"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},
-    {"version::qv", XS_version_qv, NULL},
-    {"version::declare", XS_version_qv, NULL},
-    {"version::is_qv", XS_version_is_qv, NULL},
-    {"utf8::is_utf8", XS_utf8_is_utf8, NULL},
-    {"utf8::valid", XS_utf8_valid, NULL},
-    {"utf8::encode", XS_utf8_encode, NULL},
-    {"utf8::decode", XS_utf8_decode, NULL},
-    {"utf8::upgrade", XS_utf8_upgrade, NULL},
-    {"utf8::downgrade", XS_utf8_downgrade, NULL},
-    {"utf8::native_to_unicode", XS_utf8_native_to_unicode, NULL},
-    {"utf8::unicode_to_native", XS_utf8_unicode_to_native, NULL},
-    {"Internals::SvREADONLY", XS_Internals_SvREADONLY, "\\[$%@];$"},
-    {"Internals::SvREFCNT", XS_Internals_SvREFCNT, "\\[$%@];$"},
-    {"Internals::hv_clear_placeholders", XS_Internals_hv_clear_placehold, "\\%"},
-    {"PerlIO::get_layers", XS_PerlIO_get_layers, "*;@"},
-    {"Internals::hash_seed", XS_Internals_hash_seed, ""},
-    {"Internals::rehash_seed", XS_Internals_rehash_seed, ""},
-    {"Internals::HvREHASH", XS_Internals_HvREHASH, "\\%"},
-    {"re::is_regexp", XS_re_is_regexp, "$"},
-    {"re::regname", XS_re_regname, ";$$"},
-    {"re::regnames", XS_re_regnames, ";$"},
-    {"re::regnames_count", XS_re_regnames_count, ""},
-    {"re::regexp_pattern", XS_re_regexp_pattern, "$"},
+static const struct xsub_details these_details[] = {
+    {"UNIVERSAL::isa", XS_UNIVERSAL_isa, NULL, 0 },
+    {"UNIVERSAL::can", XS_UNIVERSAL_can, NULL, 0 },
+    {"UNIVERSAL::DOES", XS_UNIVERSAL_DOES, NULL, 0 },
+    {"UNIVERSAL::import", XS_UNIVERSAL_import_unimport, NULL, 0},
+    {"UNIVERSAL::unimport", XS_UNIVERSAL_import_unimport, NULL, 1},
+#define VXS_XSUB_DETAILS
+#include "vxs.inc"
+#undef VXS_XSUB_DETAILS
+    {"utf8::is_utf8", XS_utf8_is_utf8, NULL, 0 },
+    {"utf8::valid", XS_utf8_valid, NULL, 0 },
+    {"utf8::encode", XS_utf8_encode, NULL, 0 },
+    {"utf8::decode", XS_utf8_decode, NULL, 0 },
+    {"utf8::upgrade", XS_utf8_upgrade, NULL, 0 },
+    {"utf8::downgrade", XS_utf8_downgrade, NULL, 0 },
+    {"utf8::native_to_unicode", XS_utf8_native_to_unicode, NULL, 0 },
+    {"utf8::unicode_to_native", XS_utf8_unicode_to_native, NULL, 0 },
+    {"Internals::SvREADONLY", XS_Internals_SvREADONLY, "\\[$%@];$", 0 },
+    {"Internals::SvREFCNT", XS_Internals_SvREFCNT, "\\[$%@];$", 0 },
+    {"Internals::hv_clear_placeholders", XS_Internals_hv_clear_placehold, "\\%", 0 },
+    {"Internals::stack_refcounted", XS_Internals_stack_refcounted, NULL, 0 },
+    {"constant::_make_const", XS_constant__make_const, "\\[$@]", 0 },
+    {"PerlIO::get_layers", XS_PerlIO_get_layers, "*;@", 0 },
+    {"re::is_regexp", XS_re_is_regexp, "$", 0 },
+    {"re::regname", XS_re_regname, ";$$", 0 },
+    {"re::regnames", XS_re_regnames, ";$", 0 },
+    {"re::regnames_count", XS_re_regnames_count, "", 0 },
+    {"re::regexp_pattern", XS_re_regexp_pattern, "$", 0 },
+#if defined(HAS_GETCWD) && defined(PERL_IS_MINIPERL)
+    {"Internals::getcwd", XS_Internals_getcwd, "", 0 },
+#endif
+    {"Tie::Hash::NamedCapture::_tie_it", XS_NamedCapture_tie_it, NULL, 0 },
+    {"Tie::Hash::NamedCapture::TIEHASH", XS_NamedCapture_TIEHASH, NULL, 0 },
+    {"Tie::Hash::NamedCapture::FETCH", XS_NamedCapture_FETCH, NULL, FETCH_ALIAS },
+    {"Tie::Hash::NamedCapture::STORE", XS_NamedCapture_FETCH, NULL, STORE_ALIAS },
+    {"Tie::Hash::NamedCapture::DELETE", XS_NamedCapture_FETCH, NULL, DELETE_ALIAS },
+    {"Tie::Hash::NamedCapture::CLEAR", XS_NamedCapture_FETCH, NULL, CLEAR_ALIAS },
+    {"Tie::Hash::NamedCapture::EXISTS", XS_NamedCapture_FETCH, NULL, EXISTS_ALIAS },
+    {"Tie::Hash::NamedCapture::SCALAR", XS_NamedCapture_FETCH, NULL, SCALAR_ALIAS },
+    {"Tie::Hash::NamedCapture::FIRSTKEY", XS_NamedCapture_FIRSTKEY, NULL, 0 },
+    {"Tie::Hash::NamedCapture::NEXTKEY", XS_NamedCapture_FIRSTKEY, NULL, 1 },
+    {"Tie::Hash::NamedCapture::flags", XS_NamedCapture_flags, NULL, 0 },
 };
 
+STATIC OP*
+optimize_out_native_convert_function(pTHX_ OP* entersubop,
+                                           GV* namegv,
+                                           SV* protosv)
+{
+    /* Optimizes out an identity function, i.e., one that just returns its
+     * argument.  The passed in function is assumed to be an identity function,
+     * with no checking.  This is designed to be called for utf8_to_native()
+     * and native_to_utf8() on ASCII platforms, as they just return their
+     * arguments, but it could work on any such function.
+     *
+     * The code is mostly just cargo-culted from Memoize::Lift */
+
+    OP *pushop, *argop;
+    OP *parent;
+    SV* prototype = newSVpvs("$");
+
+    PERL_UNUSED_ARG(protosv);
+
+    assert(entersubop->op_type == OP_ENTERSUB);
+
+    entersubop = ck_entersub_args_proto(entersubop, namegv, prototype);
+    parent = entersubop;
+
+    SvREFCNT_dec(prototype);
+
+    pushop = cUNOPx(entersubop)->op_first;
+    if (! OpHAS_SIBLING(pushop)) {
+        parent = pushop;
+        pushop = cUNOPx(pushop)->op_first;
+    }
+    argop = OpSIBLING(pushop);
+
+    /* Carry on without doing the optimization if it is not something we're
+     * expecting, so continues to work */
+    if (   ! argop
+        || ! OpHAS_SIBLING(argop)
+        ||   OpHAS_SIBLING(OpSIBLING(argop))
+    ) {
+        return entersubop;
+    }
+
+    /* cut argop from the subtree */
+    (void)op_sibling_splice(parent, pushop, 1, NULL);
+
+    op_free(entersubop);
+    return argop;
+}
+
 void
 Perl_boot_core_UNIVERSAL(pTHX)
 {
-    dVAR;
     static const char file[] = __FILE__;
-    struct xsub_details *xsub = details;
-    const struct xsub_details *end
-       = details + sizeof(details) / sizeof(details[0]);
+    const struct xsub_details *xsub = these_details;
+    const struct xsub_details *end = C_ARRAY_END(these_details);
 
     do {
-       newXS_flags(xsub->name, xsub->xsub, file, xsub->proto, 0);
+        CV *cv = newXS_flags(xsub->name, xsub->xsub, file, xsub->proto, 0);
+        XSANY.any_i32 = xsub->ix;
     } while (++xsub < end);
 
+#ifndef EBCDIC
+    { /* On ASCII platforms these functions just return their argument, so can
+         be optimized away */
+
+        CV* to_native_cv = get_cv("utf8::unicode_to_native", 0);
+        CV* to_unicode_cv = get_cv("utf8::native_to_unicode", 0);
+
+        cv_set_call_checker_flags(to_native_cv,
+                            optimize_out_native_convert_function,
+                            (SV*) to_native_cv, 0);
+        cv_set_call_checker_flags(to_unicode_cv,
+                            optimize_out_native_convert_function,
+                            (SV*) to_unicode_cv, 0);
+    }
+#endif
+
     /* Providing a Regexp::DESTROY fixes #21347. See test in t/op/ref.t  */
     {
-       CV * const cv =
-           newCONSTSUB(get_hv("Regexp::", GV_ADD), "DESTROY", NULL);
-       Safefree(CvFILE(cv));
-       CvFILE(cv) = (char *)file;
-       CvDYNFILE_off(cv);
+        CV * const cv =
+            newCONSTSUB(get_hv("Regexp::", GV_ADD), "DESTROY", NULL);
+        char ** cvfile = &CvFILE(cv);
+        char * oldfile = *cvfile;
+        CvDYNFILE_off(cv);
+        *cvfile = (char *)file;
+        Safefree(oldfile);
     }
 }
 
 /*
- * Local variables:
- * c-indentation-style: bsd
- * c-basic-offset: 4
- * indent-tabs-mode: nil
- * End:
- *
  * ex: set ts=8 sts=4 sw=4 et:
  */