This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Make sure expand-macro.pl also works for macros in headers
[perl5.git] / sv.c
diff --git a/sv.c b/sv.c
index 9f456c1..e39a2c9 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -192,13 +192,23 @@ Perl_offer_nice_chunk(pTHX_ void *const chunk, const U32 chunk_size)
 #  define POSION_SV_HEAD(sv)
 #endif
 
+/* Mark an SV head as unused, and add to free list.
+ *
+ * If SVf_BREAK is set, skip adding it to the free list, as this SV had
+ * its refcount artificially decremented during global destruction, so
+ * there may be dangling pointers to it. The last thing we want in that
+ * case is for it to be reused. */
+
 #define plant_SV(p) \
     STMT_START {                                       \
+       const U32 old_flags = SvFLAGS(p);                       \
        FREE_SV_DEBUG_FILE(p);                          \
        POSION_SV_HEAD(p);                              \
-       SvARENA_CHAIN(p) = (void *)PL_sv_root;          \
        SvFLAGS(p) = SVTYPEMASK;                        \
-       PL_sv_root = (p);                               \
+       if (!(old_flags & SVf_BREAK)) {         \
+           SvARENA_CHAIN(p) = (void *)PL_sv_root;      \
+           PL_sv_root = (p);                           \
+       }                                               \
        --PL_sv_count;                                  \
     } STMT_END
 
@@ -249,13 +259,12 @@ S_new_SV(pTHX)
     SvREFCNT(sv) = 1;
     SvFLAGS(sv) = 0;
     sv->sv_debug_optype = PL_op ? PL_op->op_type : 0;
-    sv->sv_debug_line = (U16) (PL_parser
-           ?  PL_parser->copline == NOLINE
-               ?  PL_curcop
+    sv->sv_debug_line = (U16) (PL_parser && PL_parser->copline != NOLINE
+               ? PL_parser->copline
+               :  PL_curcop
                    ? CopLINE(PL_curcop)
                    : 0
-               : PL_parser->copline
-           : 0);
+           );
     sv->sv_debug_inpad = 0;
     sv->sv_debug_cloned = 0;
     sv->sv_debug_file = PL_curcop ? savepv(CopFILE(PL_curcop)): NULL;
@@ -516,6 +525,10 @@ static void
 do_clean_all(pTHX_ SV *const sv)
 {
     dVAR;
+    if (sv == (SV*) PL_fdpid || sv == (SV *)PL_strtab) {
+       /* don't clean pid table and strtab */
+       return;
+    }
     DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning loops: SV at 0x%"UVxf"\n", PTR2UV(sv)) ));
     SvFLAGS(sv) |= SVf_BREAK;
     SvREFCNT_dec(sv);
@@ -1488,7 +1501,7 @@ Perl_sv_grow(pTHX_ register SV *const sv, register STRLEN newlen)
        s = SvPVX_mutable(sv);
 
     if (newlen > SvLEN(sv)) {          /* need more room? */
-#ifndef MYMALLOC
+#ifndef Perl_safesysmalloc_size
        newlen = PERL_STRLEN_ROUNDUP(newlen);
 #endif
        if (SvLEN(sv) && s) {
@@ -1540,6 +1553,8 @@ Perl_sv_setiv(pTHX_ register SV *const sv, const IV i)
        break;
 
     case SVt_PVGV:
+       if (!isGV_with_GP(sv))
+           break;
     case SVt_PVAV:
     case SVt_PVHV:
     case SVt_PVCV:
@@ -1647,6 +1662,8 @@ Perl_sv_setnv(pTHX_ register SV *const sv, const NV num)
        break;
 
     case SVt_PVGV:
+       if (!isGV_with_GP(sv))
+           break;
     case SVt_PVAV:
     case SVt_PVHV:
     case SVt_PVCV:
@@ -3159,13 +3176,21 @@ Perl_sv_utf8_upgrade_flags(pTHX_ register SV *const sv, const I32 flags)
            const U8 ch = *t++;
            /* Check for hi bit */
            if (!NATIVE_IS_INVARIANT(ch)) {
-               STRLEN len = SvCUR(sv) + 1; /* Plus the \0 */
+               STRLEN len = SvCUR(sv);
+               /* *Currently* bytes_to_utf8() adds a '\0' after every string
+                  it converts. This isn't documented. It's not clear if it's
+                  a bad thing to be doing, and should be changed to do exactly
+                  what the documentation says. If so, this code will have to
+                  be changed.
+                  As is, we mustn't rely on our incoming SV being well formed
+                  and having a trailing '\0', as certain code in pp_formline
+                  can send us partially built SVs. */
                U8 * const recoded = bytes_to_utf8((U8*)s, &len);
 
                SvPV_free(sv); /* No longer using what was there before. */
                SvPV_set(sv, (char*)recoded);
-               SvCUR_set(sv, len - 1);
-               SvLEN_set(sv, len); /* No longer know the real size. */
+               SvCUR_set(sv, len);
+               SvLEN_set(sv, len + 1); /* No longer know the real size. */
                break;
            }
        }
@@ -3549,7 +3574,6 @@ Perl_sv_setsv_flags(pTHX_ SV *dstr, register SV* sstr, const I32 flags)
     {
        /* need to nuke the magic */
        mg_free(dstr);
-       SvRMAGICAL_off(dstr);
     }
 
     /* There's a lot of redundancy below but we're going for speed here */
@@ -3703,7 +3727,7 @@ Perl_sv_setsv_flags(pTHX_ SV *dstr, register SV* sstr, const I32 flags)
            Perl_croak(aTHX_ "Cannot copy to %s", type);
     } else if (sflags & SVf_ROK) {
        if (isGV_with_GP(dstr) && dtype == SVt_PVGV
-           && SvTYPE(SvRV(sstr)) == SVt_PVGV) {
+           && SvTYPE(SvRV(sstr)) == SVt_PVGV && isGV_with_GP(SvRV(sstr))) {
            sstr = SvRV(sstr);
            if (sstr == dstr) {
                if (GvIMPORTED(dstr) != GVf_IMPORTED
@@ -4373,6 +4397,7 @@ Perl_sv_chop(pTHX_ register SV *const sv, register const char *const ptr)
 #ifdef DEBUGGING
     const U8 *real_start;
 #endif
+    STRLEN max_delta;
 
     PERL_ARGS_ASSERT_SV_CHOP;
 
@@ -4383,8 +4408,17 @@ Perl_sv_chop(pTHX_ register SV *const sv, register const char *const ptr)
        /* Nothing to do.  */
        return;
     }
-    assert(ptr > SvPVX_const(sv));
+    /* SvPVX(sv) may move in SV_CHECK_THINKFIRST(sv), but after this line,
+       nothing uses the value of ptr any more.  */
+    max_delta = SvLEN(sv) ? SvLEN(sv) : SvCUR(sv);
+    if (ptr <= SvPVX_const(sv))
+       Perl_croak(aTHX_ "panic: sv_chop ptr=%p, start=%p, end=%p",
+                  ptr, SvPVX_const(sv), SvPVX_const(sv) + max_delta);
     SV_CHECK_THINKFIRST(sv);
+    if (delta > max_delta)
+       Perl_croak(aTHX_ "panic: sv_chop ptr=%p (was %p), start=%p, end=%p",
+                  SvPVX_const(sv) + delta, ptr, SvPVX_const(sv),
+                  SvPVX_const(sv) + max_delta);
 
     if (!SvOOK(sv)) {
        if (!SvLEN(sv)) { /* make copy of shared string */
@@ -4981,6 +5015,24 @@ Perl_sv_rvweaken(pTHX_ SV *const sv)
  * back-reference to sv onto the array associated with the backref magic.
  */
 
+/* A discussion about the backreferences array and its refcount:
+ *
+ * The AV holding the backreferences is pointed to either as the mg_obj of
+ * PERL_MAGIC_backref, or in the specific case of a HV that has the hv_aux
+ * structure, from the xhv_backreferences field. (A HV without hv_aux will
+ * have the standard magic instead.) The array is created with a refcount
+ * of 2. This means that if during global destruction the array gets
+ * picked on first to have its refcount decremented by the random zapper,
+ * it won't actually be freed, meaning it's still theere for when its
+ * parent gets freed.
+ * When the parent SV is freed, in the case of magic, the magic is freed,
+ * Perl_magic_killbackrefs is called which decrements one refcount, then
+ * mg_obj is freed which kills the second count.
+ * In the vase of a HV being freed, one ref is removed by
+ * Perl_hv_kill_backrefs, the other by Perl_sv_kill_backrefs, which it
+ * calls.
+ */
+
 void
 Perl_sv_add_backref(pTHX_ SV *const tsv, SV *const sv)
 {
@@ -5009,7 +5061,7 @@ Perl_sv_add_backref(pTHX_ SV *const tsv, SV *const sv)
            } else {
                av = newAV();
                AvREAL_off(av);
-               SvREFCNT_inc_simple_void(av);
+               SvREFCNT_inc_simple_void(av); /* see discussion above */
            }
            *avp = av;
        }
@@ -5022,9 +5074,7 @@ Perl_sv_add_backref(pTHX_ SV *const tsv, SV *const sv)
            av = newAV();
            AvREAL_off(av);
            sv_magic(tsv, (SV*)av, PERL_MAGIC_backref, NULL, 0);
-           /* av now has a refcnt of 2, which avoids it getting freed
-            * before us during global cleanup. The extra ref is removed
-            * by magic_killbackrefs() when tsv is being freed */
+           /* av now has a refcnt of 2; see discussion above */
        }
     }
     if (AvFILLp(av) >= AvMAX(av)) {
@@ -5060,14 +5110,11 @@ S_sv_del_backref(pTHX_ SV *const tsv, SV *const sv)
        if (mg)
            av = (AV *)mg->mg_obj;
     }
-    if (!av) {
-       if (PL_in_clean_all)
-           return;
+
+    if (!av)
        Perl_croak(aTHX_ "panic: del_backref");
-    }
 
-    if (SvIS_FREED(av))
-       return;
+    assert(!SvIS_FREED(av));
 
     svp = AvARRAY(av);
     /* We shouldn't be in here more than once, but for paranoia reasons lets
@@ -5097,9 +5144,8 @@ Perl_sv_kill_backrefs(pTHX_ SV *const sv, AV *const av)
     PERL_ARGS_ASSERT_SV_KILL_BACKREFS;
     PERL_UNUSED_ARG(sv);
 
-    /* Not sure why the av can get freed ahead of its sv, but somehow it does
-       in ext/B/t/bytecode.t test 15 (involving print <DATA>)  */
-    if (svp && !SvIS_FREED(av)) {
+    assert(!svp || !SvIS_FREED(av));
+    if (svp) {
        SV *const *const last = svp + AvFILLp(av);
 
        while (svp <= last) {
@@ -5136,14 +5182,17 @@ Perl_sv_kill_backrefs(pTHX_ SV *const sv, AV *const av)
 =for apidoc sv_insert
 
 Inserts a string at the specified offset/length within the SV. Similar to
-the Perl substr() function.
+the Perl substr() function. Handles get magic.
+
+=for apidoc sv_insert_flags
+
+Same as C<sv_insert>, but the extra C<flags> are passed the C<SvPV_force_flags> that applies to C<bigstr>.
 
 =cut
 */
 
 void
-Perl_sv_insert(pTHX_ SV *const bigstr, const STRLEN offset, const STRLEN len, 
-              const char *const little, const STRLEN littlelen)
+Perl_sv_insert_flags(pTHX_ SV *const bigstr, const STRLEN offset, const STRLEN len, const char *const little, const STRLEN littlelen, const U32 flags)
 {
     dVAR;
     register char *big;
@@ -5153,11 +5202,11 @@ Perl_sv_insert(pTHX_ SV *const bigstr, const STRLEN offset, const STRLEN len,
     register I32 i;
     STRLEN curlen;
 
-    PERL_ARGS_ASSERT_SV_INSERT;
+    PERL_ARGS_ASSERT_SV_INSERT_FLAGS;
 
     if (!bigstr)
        Perl_croak(aTHX_ "Can't modify non-existent substring");
-    SvPV_force(bigstr, curlen);
+    SvPV_force_flags(bigstr, curlen, flags);
     (void)SvPOK_only_UTF8(bigstr);
     if (offset + len > curlen) {
        SvGROW(bigstr, offset+len+1);
@@ -5882,7 +5931,8 @@ S_sv_pos_u2b_cached(pTHX_ SV *const sv, MAGIC **const mgp, const U8 *const start
        boffset = real_boffset;
     }
 
-    S_utf8_mg_pos_cache_update(aTHX_ sv, mgp, boffset, uoffset, send - start);
+    if (PL_utf8cache)
+       utf8_mg_pos_cache_update(sv, mgp, boffset, uoffset, send - start);
     return boffset;
 }
 
@@ -6237,7 +6287,8 @@ Perl_sv_pos_b2u(pTHX_ register SV *const sv, I32 *const offsetp)
     }
     *offsetp = len;
 
-    S_utf8_mg_pos_cache_update(aTHX_ sv, &mg, byte, len, blen);
+    if (PL_utf8cache)
+       utf8_mg_pos_cache_update(sv, &mg, byte, len, blen);
 }
 
 /*
@@ -6627,6 +6678,9 @@ Perl_sv_gets(pTHX_ register SV *const sv, register PerlIO *const fp, I32 append)
       I32 bytesread;
       char *buffer;
       U32 recsize;
+#ifdef VMS
+      int fd;
+#endif
 
       /* Grab the size of the record we're getting */
       recsize = SvUV(SvRV(PL_rs)); /* RsRECORD() guarantees > 0. */
@@ -6638,13 +6692,19 @@ Perl_sv_gets(pTHX_ register SV *const sv, register PerlIO *const fp, I32 append)
       /* doing, but we've got no other real choice - except avoid stdio
          as implementation - perhaps write a :vms layer ?
        */
-      bytesread = PerlLIO_read(PerlIO_fileno(fp), buffer, recsize);
+      fd = PerlIO_fileno(fp);
+      if (fd == -1) { /* in-memory file from PerlIO::Scalar */
+          bytesread = PerlIO_read(fp, buffer, recsize);
+      }
+      else {
+          bytesread = PerlLIO_read(fd, buffer, recsize);
+      }
 #else
       bytesread = PerlIO_read(fp, buffer, recsize);
 #endif
       if (bytesread < 0)
          bytesread = 0;
-      SvCUR_set(sv, bytesread += append);
+      SvCUR_set(sv, bytesread + append);
       buffer[bytesread] = '\0';
       goto return_string_or_null;
     }
@@ -7452,6 +7512,8 @@ Perl_newSVpvn_share(pTHX_ const char *src, I32 len, U32 hash)
     if (!hash)
        PERL_HASH(hash, src, len);
     new_SV(sv);
+    /* The logic for this is inlined in S_mro_get_linear_isa_dfs(), so if it
+       changes here, update it there too.  */
     sv_upgrade(sv, SVt_PV);
     SvPV_set(sv, sharepvn(src, is_utf8?-len:len, hash));
     SvCUR_set(sv, len);
@@ -7800,11 +7862,14 @@ Perl_sv_2io(pTHX_ SV *const sv)
        io = (IO*)sv;
        break;
     case SVt_PVGV:
-       gv = (GV*)sv;
-       io = GvIO(gv);
-       if (!io)
-           Perl_croak(aTHX_ "Bad filehandle: %s", GvNAME(gv));
-       break;
+       if (isGV_with_GP(sv)) {
+           gv = (GV*)sv;
+           io = GvIO(gv);
+           if (!io)
+               Perl_croak(aTHX_ "Bad filehandle: %s", GvNAME(gv));
+           break;
+       }
+       /* FALL THROUGH */
     default:
        if (!SvOK(sv))
            Perl_croak(aTHX_ PL_no_usym, "filehandle");
@@ -7857,15 +7922,18 @@ Perl_sv_2cv(pTHX_ SV *sv, HV **const st, GV **const gvp, const I32 lref)
        *gvp = NULL;
        return NULL;
     case SVt_PVGV:
-       gv = (GV*)sv;
-       *gvp = gv;
-       *st = GvESTASH(gv);
-       goto fix_gv;
+       if (isGV_with_GP(sv)) {
+           gv = (GV*)sv;
+           *gvp = gv;
+           *st = GvESTASH(gv);
+           goto fix_gv;
+       }
+       /* FALL THROUGH */
 
     default:
-       SvGETMAGIC(sv);
        if (SvROK(sv)) {
            SV * const *sp = &sv;       /* Used in tryAMAGICunDEREF macro. */
+           SvGETMAGIC(sv);
            tryAMAGICunDEREF(to_cv);
 
            sv = SvRV(sv);
@@ -7875,22 +7943,24 @@ Perl_sv_2cv(pTHX_ SV *sv, HV **const st, GV **const gvp, const I32 lref)
                *st = CvSTASH(cv);
                return cv;
            }
-           else if(isGV(sv))
+           else if(isGV_with_GP(sv))
                gv = (GV*)sv;
            else
                Perl_croak(aTHX_ "Not a subroutine reference");
        }
-       else if (isGV(sv))
+       else if (isGV_with_GP(sv)) {
+           SvGETMAGIC(sv);
            gv = (GV*)sv;
+       }
        else
-           gv = gv_fetchsv(sv, lref, SVt_PVCV);
+           gv = gv_fetchsv(sv, lref, SVt_PVCV); /* Calls get magic */
        *gvp = gv;
        if (!gv) {
            *st = NULL;
            return NULL;
        }
        /* Some flags to gv_fetchsv mean don't really create the GV  */
-       if (SvTYPE(gv) != SVt_PVGV) {
+       if (!isGV_with_GP(gv)) {
            *st = NULL;
            return NULL;
        }
@@ -7910,7 +7980,7 @@ Perl_sv_2cv(pTHX_ SV *sv, HV **const st, GV **const gvp, const I32 lref)
            LEAVE;
            if (!GvCVu(gv))
                Perl_croak(aTHX_ "Unable to create sub named \"%"SVf"\"",
-                          SVfARG(sv));
+                          SVfARG(SvOK(sv) ? sv : &PL_sv_no));
        }
        return GvCVu(gv);
     }
@@ -8105,7 +8175,8 @@ Perl_sv_reftype(pTHX_ const SV *const sv, const int ob)
        case SVt_PVAV:          return "ARRAY";
        case SVt_PVHV:          return "HASH";
        case SVt_PVCV:          return "CODE";
-       case SVt_PVGV:          return "GLOB";
+       case SVt_PVGV:          return (char *) (isGV_with_GP(sv)
+                                   ? "GLOB" : "SCALAR");
        case SVt_PVFM:          return "FORMAT";
        case SVt_PVIO:          return "IO";
        case SVt_BIND:          return "BIND";
@@ -10118,7 +10189,7 @@ Perl_parser_dup(pTHX_ const yy_parser *const proto, CLONE_PARAMS *const param)
 /* duplicate a file handle */
 
 PerlIO *
-Perl_fp_dup(pTHX_ PerlIO *fp, char type, CLONE_PARAMS *param)
+Perl_fp_dup(pTHX_ PerlIO *const fp, const char type, CLONE_PARAMS *const param)
 {
     PerlIO *ret;
 
@@ -10288,7 +10359,7 @@ Perl_ptr_table_new(pTHX)
 /* map an existing pointer using a table */
 
 STATIC PTR_TBL_ENT_t *
-S_ptr_table_find(PTR_TBL_t *tbl, const void *sv)
+S_ptr_table_find(PTR_TBL_t *const tbl, const void *const sv)
 {
     PTR_TBL_ENT_t *tblent;
     const UV hash = PTR_TABLE_HASH(sv);
@@ -10304,7 +10375,7 @@ S_ptr_table_find(PTR_TBL_t *tbl, const void *sv)
 }
 
 void *
-Perl_ptr_table_fetch(pTHX_ PTR_TBL_t *tbl, const void *sv)
+Perl_ptr_table_fetch(pTHX_ PTR_TBL_t *const tbl, const void *const sv)
 {
     PTR_TBL_ENT_t const *const tblent = ptr_table_find(tbl, sv);
 
@@ -10317,7 +10388,7 @@ Perl_ptr_table_fetch(pTHX_ PTR_TBL_t *tbl, const void *sv)
 /* add a new entry to a pointer-mapping table */
 
 void
-Perl_ptr_table_store(pTHX_ PTR_TBL_t *tbl, const void *oldsv, void *newsv)
+Perl_ptr_table_store(pTHX_ PTR_TBL_t *const tbl, const void *const oldsv, void *const newsv)
 {
     PTR_TBL_ENT_t *tblent = ptr_table_find(tbl, oldsv);
 
@@ -10344,7 +10415,7 @@ Perl_ptr_table_store(pTHX_ PTR_TBL_t *tbl, const void *oldsv, void *newsv)
 /* double the hash bucket size of an existing ptr table */
 
 void
-Perl_ptr_table_split(pTHX_ PTR_TBL_t *tbl)
+Perl_ptr_table_split(pTHX_ PTR_TBL_t *const tbl)
 {
     PTR_TBL_ENT_t **ary = tbl->tbl_ary;
     const UV oldsize = tbl->tbl_max + 1;
@@ -10379,7 +10450,7 @@ Perl_ptr_table_split(pTHX_ PTR_TBL_t *tbl)
 /* remove all the entries from a ptr table */
 
 void
-Perl_ptr_table_clear(pTHX_ PTR_TBL_t *tbl)
+Perl_ptr_table_clear(pTHX_ PTR_TBL_t *const tbl)
 {
     if (tbl && tbl->tbl_items) {
        register PTR_TBL_ENT_t * const * const array = tbl->tbl_ary;
@@ -10402,7 +10473,7 @@ Perl_ptr_table_clear(pTHX_ PTR_TBL_t *tbl)
 /* clear and free a ptr table */
 
 void
-Perl_ptr_table_free(pTHX_ PTR_TBL_t *tbl)
+Perl_ptr_table_free(pTHX_ PTR_TBL_t *const tbl)
 {
     if (!tbl) {
         return;
@@ -10415,7 +10486,7 @@ Perl_ptr_table_free(pTHX_ PTR_TBL_t *tbl)
 #if defined(USE_ITHREADS)
 
 void
-Perl_rvpv_dup(pTHX_ SV *dstr, const SV *sstr, CLONE_PARAMS* param)
+Perl_rvpv_dup(pTHX_ SV *const dstr, const SV *const sstr, CLONE_PARAMS *const param)
 {
     PERL_ARGS_ASSERT_RVPV_DUP;
 
@@ -10463,7 +10534,7 @@ Perl_rvpv_dup(pTHX_ SV *dstr, const SV *sstr, CLONE_PARAMS* param)
 /* duplicate an SV of any type (including AV, HV etc) */
 
 SV *
-Perl_sv_dup(pTHX_ const SV *sstr, CLONE_PARAMS* param)
+Perl_sv_dup(pTHX_ const SV *const sstr, CLONE_PARAMS *const param)
 {
     dVAR;
     SV *dstr;
@@ -10735,10 +10806,11 @@ Perl_sv_dup(pTHX_ const SV *sstr, CLONE_PARAMS* param)
                        daux->xhv_eiter = saux->xhv_eiter
                            ? he_dup(saux->xhv_eiter,
                                        (bool)!!HvSHAREKEYS(sstr), param) : 0;
+                       /* backref array needs refcnt=2; see sv_add_backref */
                        daux->xhv_backreferences =
                            saux->xhv_backreferences
                                ? (AV*) SvREFCNT_inc(
-                                       sv_dup((SV*)saux->xhv_backreferences, param))
+                                       sv_dup_inc((SV*)saux->xhv_backreferences, param))
                                : 0;
 
                         daux->xhv_mro_meta = saux->xhv_mro_meta
@@ -12304,8 +12376,10 @@ S_varname(pTHX_ GV *gv, const char gvtype, PADOFFSET targ,
        *SvPVX(name) = '$';
        Perl_sv_catpvf(aTHX_ name, "[%"IVdf"]", (IV)aindex);
     }
-    else if (subscript_type == FUV_SUBSCRIPT_WITHIN)
-       Perl_sv_insert(aTHX_ name, 0, 0,  STR_WITH_LEN("within "));
+    else if (subscript_type == FUV_SUBSCRIPT_WITHIN) {
+       /* We know that name has no magic, so can use 0 instead of SV_GMAGIC */
+       Perl_sv_insert_flags(aTHX_ name, 0, 0,  STR_WITH_LEN("within "), 0);
+    }
 
     return name;
 }
@@ -12563,6 +12637,7 @@ S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
     case OP_PRTF:
     case OP_PRINT:
     case OP_SAY:
+       match = 1; /* print etc can return undef on defined args */
        /* skip filehandle as it can't produce 'undef' warning  */
        o = cUNOPx(obase)->op_first;
        if ((obase->op_flags & OPf_STACKED) && o->op_type == OP_PUSHMARK)
@@ -12572,8 +12647,81 @@ S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
 
     case OP_ENTEREVAL: /* could be eval $undef or $x='$undef'; eval $x */
     case OP_RV2SV:
-    case OP_CUSTOM:
-       match = 1; /* XS or custom code could trigger random warnings */
+    case OP_CUSTOM: /* XS or custom code could trigger random warnings */
+
+       /* the following ops are capable of returning PL_sv_undef even for
+        * defined arg(s) */
+
+    case OP_BACKTICK:
+    case OP_PIPE_OP:
+    case OP_FILENO:
+    case OP_BINMODE:
+    case OP_TIED:
+    case OP_GETC:
+    case OP_SYSREAD:
+    case OP_SEND:
+    case OP_IOCTL:
+    case OP_SOCKET:
+    case OP_SOCKPAIR:
+    case OP_BIND:
+    case OP_CONNECT:
+    case OP_LISTEN:
+    case OP_ACCEPT:
+    case OP_SHUTDOWN:
+    case OP_SSOCKOPT:
+    case OP_GETPEERNAME:
+    case OP_FTRREAD:
+    case OP_FTRWRITE:
+    case OP_FTREXEC:
+    case OP_FTROWNED:
+    case OP_FTEREAD:
+    case OP_FTEWRITE:
+    case OP_FTEEXEC:
+    case OP_FTEOWNED:
+    case OP_FTIS:
+    case OP_FTZERO:
+    case OP_FTSIZE:
+    case OP_FTFILE:
+    case OP_FTDIR:
+    case OP_FTLINK:
+    case OP_FTPIPE:
+    case OP_FTSOCK:
+    case OP_FTBLK:
+    case OP_FTCHR:
+    case OP_FTTTY:
+    case OP_FTSUID:
+    case OP_FTSGID:
+    case OP_FTSVTX:
+    case OP_FTTEXT:
+    case OP_FTBINARY:
+    case OP_FTMTIME:
+    case OP_FTATIME:
+    case OP_FTCTIME:
+    case OP_READLINK:
+    case OP_OPEN_DIR:
+    case OP_READDIR:
+    case OP_TELLDIR:
+    case OP_SEEKDIR:
+    case OP_REWINDDIR:
+    case OP_CLOSEDIR:
+    case OP_GMTIME:
+    case OP_ALARM:
+    case OP_SEMGET:
+    case OP_GETLOGIN:
+    case OP_UNDEF:
+    case OP_SUBSTR:
+    case OP_AEACH:
+    case OP_EACH:
+    case OP_SORT:
+    case OP_CALLER:
+    case OP_DOFILE:
+    case OP_PROTOTYPE:
+    case OP_NCMP:
+    case OP_SMARTMATCH:
+    case OP_UNPACK:
+    case OP_SYSOPEN:
+    case OP_SYSSEEK:
+       match = 1;
        goto do_op;
 
     case OP_ENTERSUB:
@@ -12585,6 +12733,7 @@ S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
          Need a better fix at dome point. DAPM 11/2007 */
        break;
 
+
     case OP_POS:
        /* def-ness of rval pos() is independent of the def-ness of its arg */
        if ( !(obase->op_flags & OPf_MOD))