File: cv_copy_flags.c.inc

package info (click to toggle)
libfuture-asyncawait-perl 0.70-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 528 kB
  • sloc: perl: 2,647; ansic: 118; pascal: 34; makefile: 3
file content (172 lines) | stat: -rw-r--r-- 5,314 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/* vi: set ft=c : */

#define padname_is_normal_lexical(pname)  MY_padname_is_normal_lexical(aTHX_ pname)
static bool MY_padname_is_normal_lexical(pTHX_ PADNAME *pname)
{
  /* PAD slots without names are certainly not lexicals */
  if(PadnameIsNULL(pname) || !PadnameLEN(pname))
    return FALSE;

  /* Outer lexical captures are not lexicals */
  if(PadnameOUTER(pname))
    return FALSE;

  /* state variables are not lexicals */
  if(PadnameIsSTATE(pname))
    return FALSE;

  /* Protosubs for closures are not lexicals */
  if(PadnamePV(pname)[0] == '&')
    return FALSE;

  /* anything left is a normal lexical */
  return TRUE;
}

enum {
  CV_COPY_NULL_LEXICALS = (1<<0), /* regular lexicals end up NULL */
};

#define cv_copy_flags(orig, flags)  MY_cv_copy_flags(aTHX_ orig, flags)
static CV *MY_cv_copy_flags(pTHX_ CV *orig, U32 flags)
{
  /* Parts of this code stolen from S_cv_clone() in pad.c
   */
  CV *new = MUTABLE_CV(newSV_type(SVt_PVCV));
  CvFLAGS(new) = CvFLAGS(orig) & ~CVf_CVGV_RC;

  CvFILE(new) = CvDYNFILE(orig) ? savepv(CvFILE(orig)) : CvFILE(orig);
#if HAVE_PERL_VERSION(5, 18, 0)
  if(CvNAMED(orig)) {
    /* Perl core uses CvNAME_HEK_set() here, but that involves a call to a
     * non-public function unshare_hek(). The latter is only needed in the
     * case where an old value needs to be removed, but since we've only just
     * created the CV we know it will be empty, so we can just set the field
     * directly
     */
    ((XPVCV*)MUTABLE_PTR(SvANY(new)))->xcv_gv_u.xcv_hek = share_hek_hek(CvNAME_HEK(orig));
    CvNAMED_on(new);
  }
  else
#endif
    CvGV_set(new, CvGV(orig));

  CvSTASH_set(new, CvSTASH(orig));
  {
    OP_REFCNT_LOCK;
    CvROOT(new) = OpREFCNT_inc(CvROOT(orig));
    OP_REFCNT_UNLOCK;
  }
  CvSTART(new) = CvSTART(orig);
  CvOUTSIDE(new) = MUTABLE_CV(SvREFCNT_inc(CvOUTSIDE(orig)));
  CvOUTSIDE_SEQ(new) = CvOUTSIDE_SEQ(orig);

  /* No need to bother with SvPV slot because that's the prototype, and it's
   * too late for that here
   */
  /* TODO: Consider what to do about SvPVX */

  {
    ENTER_with_name("cv_copy_flags");

    SAVESPTR(PL_compcv);
    PL_compcv = new;

    SAVESPTR(PL_comppad_name);
    PL_comppad_name = PadlistNAMES(CvPADLIST(orig));
    CvPADLIST_set(new, pad_new(padnew_CLONE|padnew_SAVE));
#if HAVE_PERL_VERSION(5, 22, 0)
    CvPADLIST(new)->xpadl_id = CvPADLIST(orig)->xpadl_id;
#endif

    PADNAMELIST *padnames = PadlistNAMES(CvPADLIST(orig));
    const PADOFFSET fnames = PadnamelistMAX(padnames);
    const PADOFFSET fpad = AvFILLp(PadlistARRAY(CvPADLIST(orig))[1]);
    int depth = CvDEPTH(orig);
    if(!depth)
      depth = 1;
    SV **origpad = AvARRAY(PadlistARRAY(CvPADLIST(orig))[depth]);

#if !HAVE_PERL_VERSION(5, 18, 0)
    /* Perls before 5.18.0 didn't copy the padnameslist
     */
    SvREFCNT_dec(PadlistNAMES(CvPADLIST(new)));
    PadlistNAMES(CvPADLIST(new)) = (PADNAMELIST *)SvREFCNT_inc(PadlistNAMES(CvPADLIST(orig)));
#endif

    av_fill(PL_comppad, fpad);
    PL_curpad = AvARRAY(PL_comppad);

    PADNAME **pnames = PadnamelistARRAY(padnames);
    PADOFFSET padix;

    /* TODO: What about padix 0? */

    for(padix = 1; padix <= fpad; padix++) {
      PADNAME *pname = (padix <= fnames) ? pnames[padix] : NULL;
      SV *newval = NULL;

      if(padname_is_normal_lexical(pname)) {
        if(flags & CV_COPY_NULL_LEXICALS)
          continue;

        switch(PadnamePV(pname)[0]) {
          case '$': newval = newSV(0); break;
          case '@': newval = MUTABLE_SV(newAV()); break;
          case '%': newval = MUTABLE_SV(newHV()); break;
          default:
            croak("ARGH unsure how to handle pname=<%s> in cv_copy_flags\n",
              PadnamePV(pname));
            break;
        }
      }
      else if(!origpad[padix])
        newval = NULL;
      else if(SvPADTMP(origpad[padix])) {
        /* We still have to copy the value, in case it is live. Also core perl
        * is known to set SvPADTMP on non-temporaries, like folded constants
        *   https://rt.cpan.org/Ticket/Display.html?id=142468
        */
        newval = newSVsv(origpad[padix]);
        SvPADTMP_on(newval);
      }
      else {
#if !HAVE_PERL_VERSION(5, 18, 0)
        /* Before perl 5.18.0, inner anon subs didn't find the right CvOUTSIDE
         * at runtime, so we'll have to patch them up here
         */
        CV *origproto;
        if(pname && PadnamePV(pname)[0] == '&' && 
           CvOUTSIDE(origproto = MUTABLE_CV(origpad[padix])) == orig) {
          /* quiet any "Variable $FOO is not available" warnings about lexicals
           * yet to be introduced
           */
          ENTER_with_name("find_cv_outside");
          SAVEINT(CvDEPTH(origproto));
          CvDEPTH(origproto) = 1;

          CV *newproto = cv_copy_flags(origproto, flags);
          CvPADLIST_set(newproto, CvPADLIST(origproto));
          CvSTART(newproto) = CvSTART(origproto);

          SvREFCNT_dec(CvOUTSIDE(newproto));
          CvOUTSIDE(newproto) = MUTABLE_CV(SvREFCNT_inc_simple_NN(new));

          LEAVE_with_name("find_cv_outside");

          newval = MUTABLE_SV(newproto);
        }
        else
#endif
        if(origpad[padix])
          newval = SvREFCNT_inc_NN(origpad[padix]);
      }

      PL_curpad[padix] = newval;
    }

    LEAVE_with_name("cv_copy_flags");
  }

  return new;
}