diff options
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | Porting/deparse-skips.txt | 2 | ||||
-rw-r--r-- | dist/Safe/t/safeops.t | 3 | ||||
-rw-r--r-- | dump.c | 193 | ||||
-rw-r--r-- | embed.fnc | 4 | ||||
-rw-r--r-- | embed.h | 3 | ||||
-rw-r--r-- | embedvar.h | 1 | ||||
-rw-r--r-- | ext/B/B.xs | 100 | ||||
-rw-r--r-- | ext/B/B/Concise.pm | 2 | ||||
-rw-r--r-- | ext/B/t/OptreeCheck.pm | 4 | ||||
-rw-r--r-- | ext/B/t/concise-xs.t | 30 | ||||
-rw-r--r-- | ext/B/t/f_sort.t | 52 | ||||
-rw-r--r-- | ext/Opcode/Opcode.pm | 2 | ||||
-rw-r--r-- | intrpvar.h | 3 | ||||
-rw-r--r-- | lib/B/Deparse.pm | 200 | ||||
-rw-r--r-- | lib/B/Deparse.t | 78 | ||||
-rw-r--r-- | lib/B/Op_private.pm | 23 | ||||
-rw-r--r-- | op.c | 891 | ||||
-rw-r--r-- | op.h | 56 | ||||
-rw-r--r-- | opcode.h | 262 | ||||
-rw-r--r-- | opnames.h | 485 | ||||
-rw-r--r-- | perl.h | 7 | ||||
-rw-r--r-- | pp.c | 7 | ||||
-rw-r--r-- | pp_hot.c | 436 | ||||
-rw-r--r-- | pp_proto.h | 1 | ||||
-rw-r--r-- | proto.h | 12 | ||||
-rw-r--r-- | regen/op_private | 16 | ||||
-rw-r--r-- | regen/opcodes | 4 | ||||
-rw-r--r-- | sv.c | 226 | ||||
-rw-r--r-- | t/lib/warnings/9uninit | 77 | ||||
-rw-r--r-- | t/op/multideref.t | 187 | ||||
-rw-r--r-- | t/op/svleak.t | 16 | ||||
-rw-r--r-- | t/perf/benchmarks | 168 | ||||
-rw-r--r-- | t/perf/opcount.t | 151 |
34 files changed, 3231 insertions, 472 deletions
@@ -5269,6 +5269,7 @@ t/op/magic-27839.t Test for #27839, skipped for minitest t/op/magic.t See if magic variables work t/op/method.t See if method calls work t/op/mkdir.t See if mkdir works +t/op/multideref.t See if "$a[0]{foo}[$i]{$k}" etc works t/op/mydef.t See if "my $_" works t/op/my_stash.t See if my Package works t/op/my.t See if lexical scoping works diff --git a/Porting/deparse-skips.txt b/Porting/deparse-skips.txt index c7aaf7e527..526bdc2ed4 100644 --- a/Porting/deparse-skips.txt +++ b/Porting/deparse-skips.txt @@ -434,7 +434,6 @@ op/closure.t op/concat2.t op/coreamp.t op/crypt.t -op/die.t op/do.t op/each.t op/eval.t @@ -455,7 +454,6 @@ op/lexsub.t op/local.t op/magic.t op/method.t -op/my.t op/mydef.t op/not.t op/ord.t diff --git a/dist/Safe/t/safeops.t b/dist/Safe/t/safeops.t index cb37445c1d..2133bde16b 100644 --- a/dist/Safe/t/safeops.t +++ b/dist/Safe/t/safeops.t @@ -56,7 +56,7 @@ foreach (@op) { if ($_->[2]) { testop @$_; } else { - local our $TODO = "No test yet for $_->[1]"; + local our $TODO = "No test yet for $_->[0] ($_->[1])"; fail(); } } @@ -235,6 +235,7 @@ exists exists $h{Key} rv2hv %h helem $h{kEy} hslice @h{kEy} +multideref SKIP (set by optimizer) unpack unpack pack pack split split /foo/ @@ -952,6 +952,18 @@ Perl_do_op_dump(pTHX_ I32 level, PerlIO *file, const OP *o) } #endif break; + + case OP_MULTIDEREF: + { + UNOP_AUX_item *items = cUNOP_AUXo->op_aux; + UV i, count = items[-1].uv; + + Perl_dump_indent(aTHX_ level, file, "ARGS = \n"); + for (i=0; i < count; i++) + Perl_dump_indent(aTHX_ level+1, file, "%"UVuf" => 0x%"UVxf"\n", + i, items[i].uv); + } + case OP_CONST: case OP_HINTSEVAL: case OP_METHOD_NAMED: @@ -2254,6 +2266,181 @@ S_deb_padvar(pTHX_ PADOFFSET off, int n, bool paren) } +/* append to the out SV, the name of the lexical at offset off in the CV + * cv */ + +void +S_append_padvar(pTHX_ PADOFFSET off, CV *cv, SV *out, int n, + bool paren, bool is_scalar) +{ + PADNAME *sv; + PADNAMELIST *namepad = NULL; + int i; + + if (cv) { + PADLIST * const padlist = CvPADLIST(cv); + namepad = PadlistNAMES(padlist); + } + + if (paren) + sv_catpvs_nomg(out, "("); + for (i = 0; i < n; i++) { + if (namepad && (sv = padnamelist_fetch(namepad, off + i))) + { + STRLEN cur = SvCUR(out); + Perl_sv_catpvf(aTHX_ out, "[%"PNf, PNfARG(sv)); + if (is_scalar) + SvPVX(out)[cur] = '$'; + } + else + Perl_sv_catpvf(aTHX_ out, "[%"UVuf"]", (UV)(off+i)); + if (i < n - 1) + sv_catpvs_nomg(out, ","); + } + if (paren) + sv_catpvs_nomg(out, "("); +} + + +void +S_print_gv_name(pTHX_ GV *gv, SV *out, char sigil) +{ + SV *sv; + if (!gv) { + sv_catpvs_nomg(out, "<NULLGV>"); + return; + } + sv = newSV(0); + gv_fullname4(sv, gv, NULL, FALSE); + Perl_sv_catpvf(aTHX_ out, "%c%-p", sigil, sv); + SvREFCNT_dec_NN(sv); +} + +#ifdef USE_ITHREADS +# define ITEM_SV(item) *av_fetch(comppad, (item)->pad_offset, FALSE); +#else +# define ITEM_SV(item) UNOP_AUX_item_sv(item) +#endif + + +/* return a temporary SV containing a stringified representation of + * the op_aux field of a UNOP_AUX op, associated with CV cv + */ + +SV* +Perl_unop_aux_stringify(pTHX_ const OP *o, CV *cv) +{ + UNOP_AUX_item *items = cUNOP_AUXo->op_aux; + UV actions = items->uv; + SV *sv; + bool last = 0; + bool is_hash = FALSE; + int derefs = 0; + SV *out = sv_2mortal(newSVpv("",0)); +#ifdef USE_ITHREADS + PADLIST * const padlist = CvPADLIST(cv); + PAD *comppad = comppad = PadlistARRAY(padlist)[1]; +#endif + + PERL_ARGS_ASSERT_UNOP_AUX_STRINGIFY; + + while (!last) { + switch (actions & MDEREF_ACTION_MASK) { + + case MDEREF_reload: + actions = (++items)->uv; + continue; + + case MDEREF_HV_padhv_helem: + is_hash = TRUE; + case MDEREF_AV_padav_aelem: + derefs = 1; + S_append_padvar(aTHX_ (++items)->pad_offset, cv, out, 1, 0, 1); + goto do_elem; + + case MDEREF_HV_gvhv_helem: + is_hash = TRUE; + case MDEREF_AV_gvav_aelem: + derefs = 1; + sv = ITEM_SV(++items); + S_print_gv_name(aTHX_ (GV*)sv, out, '$'); + goto do_elem; + + case MDEREF_HV_gvsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_gvsv_vivify_rv2av_aelem: + sv = ITEM_SV(++items); + S_print_gv_name(aTHX_ (GV*)sv, out, '$'); + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_padsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_padsv_vivify_rv2av_aelem: + S_append_padvar(aTHX_ (++items)->pad_offset, cv, out, 1, 0, 1); + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_pop_rv2hv_helem: + case MDEREF_HV_vivify_rv2hv_helem: + is_hash = TRUE; + do_vivify_rv2xv_elem: + case MDEREF_AV_pop_rv2av_aelem: + case MDEREF_AV_vivify_rv2av_aelem: + if (!derefs++) + sv_catpvs_nomg(out, "->"); + do_elem: + if ((actions & MDEREF_INDEX_MASK)== MDEREF_INDEX_none) { + sv_catpvs_nomg(out, "->"); + last = 1; + break; + } + + sv_catpvn_nomg(out, (is_hash ? "{" : "["), 1); + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_const: + if (is_hash) { + STRLEN cur; + char *s; + sv = ITEM_SV(++items); + s = SvPV(sv, cur); + pv_pretty(out, s, cur, 30, + NULL, NULL, + (PERL_PV_PRETTY_NOCLEAR + |PERL_PV_PRETTY_QUOTE + |PERL_PV_PRETTY_ELLIPSES)); + } + else + Perl_sv_catpvf(aTHX_ out, "%"IVdf, (++items)->iv); + break; + case MDEREF_INDEX_padsv: + S_append_padvar(aTHX_ (++items)->pad_offset, cv, out, 1, 0, 1); + break; + case MDEREF_INDEX_gvsv: + sv = ITEM_SV(++items); + S_print_gv_name(aTHX_ (GV*)sv, out, '$'); + break; + } + sv_catpvn_nomg(out, (is_hash ? "}" : "]"), 1); + + if (actions & MDEREF_FLAG_last) + last = 1; + is_hash = FALSE; + + break; + + default: + PerlIO_printf(Perl_debug_log, "UNKNOWN(%d)", + (int)(actions & MDEREF_ACTION_MASK)); + last = 1; + break; + + } /* switch */ + + actions >>= MDEREF_SHIFT; + } /* while */ + return out; +} + + I32 Perl_debop(pTHX_ const OP *o) { @@ -2300,11 +2487,17 @@ Perl_debop(pTHX_ const OP *o) case OP_PADHV: S_deb_padvar(aTHX_ o->op_targ, 1, 1); break; + case OP_PADRANGE: S_deb_padvar(aTHX_ o->op_targ, o->op_private & OPpPADRANGE_COUNTMASK, 1); break; + case OP_MULTIDEREF: + PerlIO_printf(Perl_debug_log, "(%-p)", + unop_aux_stringify(o, deb_curcv(cxstack_ix))); + break; + default: break; } @@ -330,6 +330,7 @@ ApR |I32 |cxinc Afp |void |deb |NN const char* pat|... Ap |void |vdeb |NN const char* pat|NULLOK va_list* args Ap |void |debprofdump +EXp |SV* |unop_aux_stringify |NN const OP* o|NN CV *cv Ap |I32 |debop |NN const OP* o Ap |I32 |debstack Ap |I32 |debstackptrs @@ -2651,7 +2652,8 @@ s |SV * |find_hash_subscript|NULLOK const HV *const hv \ s |I32 |find_array_subscript|NULLOK const AV *const av \ |NN const SV *const val sMd |SV* |find_uninit_var|NULLOK const OP *const obase \ - |NULLOK const SV *const uninit_sv|bool top + |NULLOK const SV *const uninit_sv|bool match \ + |NN const char **desc_p #endif Ap |GV* |gv_fetchpvn_flags|NN const char* name|STRLEN len|I32 flags|const svtype sv_type @@ -911,6 +911,7 @@ #define reg_temp_copy(a,b) Perl_reg_temp_copy(aTHX_ a,b) #define report_uninit(a) Perl_report_uninit(aTHX_ a) #define sv_magicext_mglob(a) Perl_sv_magicext_mglob(aTHX_ a) +#define unop_aux_stringify(a,b) Perl_unop_aux_stringify(aTHX_ a,b) #define validate_proto(a,b,c) Perl_validate_proto(aTHX_ a,b,c) #define vivify_defelem(a) Perl_vivify_defelem(aTHX_ a) #define yylex() Perl_yylex(aTHX) @@ -1662,7 +1663,7 @@ #define expect_number(a) S_expect_number(aTHX_ a) #define find_array_subscript(a,b) S_find_array_subscript(aTHX_ a,b) #define find_hash_subscript(a,b) S_find_hash_subscript(aTHX_ a,b) -#define find_uninit_var(a,b,c) S_find_uninit_var(aTHX_ a,b,c) +#define find_uninit_var(a,b,c,d) S_find_uninit_var(aTHX_ a,b,c,d) #define glob_2number(a) S_glob_2number(aTHX_ a) #define glob_assign_glob(a,b,c) S_glob_assign_glob(aTHX_ a,b,c) #define more_sv() S_more_sv(aTHX) diff --git a/embedvar.h b/embedvar.h index 9e4a910a57..32a8b9b327 100644 --- a/embedvar.h +++ b/embedvar.h @@ -205,6 +205,7 @@ #define PL_minus_p (vTHX->Iminus_p) #define PL_modcount (vTHX->Imodcount) #define PL_modglobal (vTHX->Imodglobal) +#define PL_multideref_pc (vTHX->Imultideref_pc) #define PL_my_cxt_keys (vTHX->Imy_cxt_keys) #define PL_my_cxt_list (vTHX->Imy_cxt_list) #define PL_my_cxt_size (vTHX->Imy_cxt_size) diff --git a/ext/B/B.xs b/ext/B/B.xs index 937ef2c43f..14bd7163b7 100644 --- a/ext/B/B.xs +++ b/ext/B/B.xs @@ -8,6 +8,7 @@ */ #define PERL_NO_GET_CONTEXT +#define PERL_EXT #include "EXTERN.h" #include "perl.h" #include "XSUB.h" @@ -1342,6 +1343,9 @@ string(o, cv) SV *ret; PPCODE: switch (o->op_type) { + case OP_MULTIDEREF: + ret = unop_aux_stringify(o, cv); + break; default: ret = sv_2mortal(newSVpvn("", 0)); } @@ -1359,9 +1363,105 @@ aux_list(o, cv) B::OP o B::CV cv PPCODE: + PERL_UNUSED_VAR(cv); /* not needed on unthreaded builds */ switch (o->op_type) { default: XSRETURN(0); /* by default, an empty list */ + + case OP_MULTIDEREF: +#ifdef USE_ITHREADS +# define ITEM_SV(item) *av_fetch(comppad, (item)->pad_offset, FALSE); +#else +# define ITEM_SV(item) UNOP_AUX_item_sv(item) +#endif + { + UNOP_AUX_item *items = cUNOP_AUXo->op_aux; + UV actions = items->uv; + UV len = items[-1].uv; + SV *sv; + bool last = 0; + bool is_hash = FALSE; +#ifdef USE_ITHREADS + PADLIST * const padlist = CvPADLIST(cv); + PAD *comppad = comppad = PadlistARRAY(padlist)[1]; +#endif + + EXTEND(SP, len); + PUSHs(sv_2mortal(newSViv(actions))); + + while (!last) { + switch (actions & MDEREF_ACTION_MASK) { + + case MDEREF_reload: + actions = (++items)->uv; + PUSHs(sv_2mortal(newSVuv(actions))); + continue; + + case MDEREF_HV_padhv_helem: + is_hash = TRUE; + case MDEREF_AV_padav_aelem: + PUSHs(sv_2mortal(newSVuv((++items)->pad_offset))); + goto do_elem; + + case MDEREF_HV_gvhv_helem: + is_hash = TRUE; + case MDEREF_AV_gvav_aelem: + sv = ITEM_SV(++items); + PUSHs(make_sv_object(aTHX_ sv)); + goto do_elem; + + case MDEREF_HV_gvsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_gvsv_vivify_rv2av_aelem: + sv = ITEM_SV(++items); + PUSHs(make_sv_object(aTHX_ sv)); + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_padsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_padsv_vivify_rv2av_aelem: + PUSHs(sv_2mortal(newSVuv((++items)->pad_offset))); + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_pop_rv2hv_helem: + case MDEREF_HV_vivify_rv2hv_helem: + is_hash = TRUE; + do_vivify_rv2xv_elem: + case MDEREF_AV_pop_rv2av_aelem: + case MDEREF_AV_vivify_rv2av_aelem: + do_elem: + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + last = 1; + break; + case MDEREF_INDEX_const: + if (is_hash) { + sv = ITEM_SV(++items); + PUSHs(make_sv_object(aTHX_ sv)); + } + else + PUSHs(sv_2mortal(newSViv((++items)->iv))); + break; + case MDEREF_INDEX_padsv: + PUSHs(sv_2mortal(newSVuv((++items)->pad_offset))); + break; + case MDEREF_INDEX_gvsv: + sv = ITEM_SV(++items); + PUSHs(make_sv_object(aTHX_ sv)); + break; + } + if (actions & MDEREF_FLAG_last) + last = 1; + is_hash = FALSE; + + break; + } /* switch */ + + actions >>= MDEREF_SHIFT; + } /* while */ + XSRETURN(len); + + } /* OP_MULTIDEREF */ } /* switch */ diff --git a/ext/B/B/Concise.pm b/ext/B/B/Concise.pm index 381181e6d2..311e0e738a 100644 --- a/ext/B/B/Concise.pm +++ b/ext/B/B/Concise.pm @@ -916,7 +916,7 @@ sub concise_op { } } elsif ($h{class} eq "UNOP_AUX") { - $h{arg} = "(" . $op->string . ")"; + $h{arg} = "(" . $op->string($curcv) . ")"; } $h{seq} = $h{hyphseq} = seq($op); diff --git a/ext/B/t/OptreeCheck.pm b/ext/B/t/OptreeCheck.pm index 0537a8d7a8..eac73baa73 100644 --- a/ext/B/t/OptreeCheck.pm +++ b/ext/B/t/OptreeCheck.pm @@ -5,7 +5,7 @@ use warnings; use vars qw($TODO $Level $using_open); require "test.pl"; -our $VERSION = '0.11'; +our $VERSION = '0.12'; # now export checkOptree, and those test.pl functions used by tests our @EXPORT = qw( checkOptree plan skip skip_all pass is like unlike @@ -669,6 +669,8 @@ sub mkCheckRex { $tc->{wantstr} = $str; + # make UNOP_AUX flag type literal + $str =~ s/<\+>/<\\+>/; # make targ args wild $str =~ s/\[t\d+\]/[t\\d+]/msg; diff --git a/ext/B/t/concise-xs.t b/ext/B/t/concise-xs.t index 365951d0bc..289f909cc9 100644 --- a/ext/B/t/concise-xs.t +++ b/ext/B/t/concise-xs.t @@ -159,6 +159,7 @@ my $testpkgs = { constant => [qw/ ASSIGN CVf_LVALUE CVf_METHOD LIST_CONTEXT OP_CONST OP_LIST OP_RV2SV OP_AELEM OP_CUSTOM OP_NEXTSTATE OP_DBSTATE + OP_HELEM OP_RV2AV OP_RV2HV OP_STRINGIFY OPf_KIDS OPf_MOD OPf_REF OPf_SPECIAL OPf_PARENS OPf_STACKED OPf_WANT OPf_WANT_LIST OPf_WANT_SCALAR @@ -169,6 +170,8 @@ my $testpkgs = { OPpSORT_REVERSE OPpREVERSE_INPLACE OPpTARGET_MY OPpTRANS_COMPLEMENT OPpTRANS_DELETE OPpTRANS_SQUASH OPpREPEAT_DOLIST + OPpMULTIDEREF_EXISTS + OPpMULTIDEREF_DELETE PMf_CONTINUE PMf_EVAL PMf_EXTENDED PMf_EXTENDED_MORE PMf_FOLD PMf_GLOBAL PMf_KEEP PMf_NONDESTRUCT @@ -176,7 +179,32 @@ my $testpkgs = { POSTFIX SVf_FAKE SVf_IOK SVf_NOK SVf_POK SVf_ROK SVpad_STATE SVpad_OUR SVs_RMG SVs_SMG SWAP_CHILDREN OPpPAD_STATE - OPpCONST_ARYBASE RXf_SKIPWHITE SVpad_TYPED/, + OPpCONST_ARYBASE RXf_SKIPWHITE SVpad_TYPED + + MDEREF_reload + MDEREF_AV_pop_rv2av_aelem + MDEREF_AV_gvsv_vivify_rv2av_aelem + MDEREF_AV_padsv_vivify_rv2av_aelem + MDEREF_AV_vivify_rv2av_aelem + MDEREF_AV_padav_aelem + MDEREF_AV_gvav_aelem + MDEREF_HV_pop_rv2hv_helem + MDEREF_HV_gvsv_vivify_rv2hv_helem + MDEREF_HV_padsv_vivify_rv2hv_helem + MDEREF_HV_vivify_rv2hv_helem + MDEREF_HV_padhv_helem + MDEREF_HV_gvhv_helem + MDEREF_ACTION_MASK + MDEREF_INDEX_none + MDEREF_INDEX_const + MDEREF_INDEX_padsv + MDEREF_INDEX_gvsv + MDEREF_INDEX_MASK + MDEREF_FLAG_last + MDEREF_MASK + MDEREF_SHIFT + /, + $] >= 5.015 ? qw( OP_GLOB PMf_SKIPWHITE RXf_PMf_CHARSET RXf_PMf_KEEPCOPY OPpEVAL_BYTES OPpSUBSTR_REPL_FIRST) : (), diff --git a/ext/B/t/f_sort.t b/ext/B/t/f_sort.t index 7205a94e44..55811eda93 100644 --- a/ext/B/t/f_sort.t +++ b/ext/B/t/f_sort.t @@ -510,10 +510,8 @@ checkOptree(name => q{Compound sort/map Expression }, # 5 <0> pushmark s # 6 <#> gv[*old] s # 7 <1> rv2av[t19] lKM/1 -# 8 <@> mapstart lK* < 5.017002 -# 8 <@> mapstart lK >=5.017002 -# 9 <|> mapwhile(other->a)[t20] lK < 5.019002 -# 9 <|> mapwhile(other->a)[t20] lKM >=5.019002 +# 8 <@> mapstart lK +# 9 <|> mapwhile(other->a)[t20] lKM # a <0> enter l # b <;> nextstate(main 608 (eval 34):2) v:{ # c <0> pushmark s @@ -525,21 +523,15 @@ checkOptree(name => q{Compound sort/map Expression }, # i <@> leave lKP # goto 9 # j <@> sort lKMS* -# k <@> mapstart lK* < 5.017002 -# k <@> mapstart lK >=5.017002 +# k <@> mapstart lK # l <|> mapwhile(other->m)[t26] lK -# m <#> gv[*_] s -# n <1> rv2sv sKM/DREFAV,1 -# o <1> rv2av[t4] sKR/1 -# p <$> const[IV 0] s -# q <2> aelem sK/2 -# - <@> scope lK < 5.017002 +# m <+> multideref($_->[0]) sK # goto l -# r <0> pushmark s -# s <#> gv[*new] s -# t <1> rv2av[t2] lKRM*/1 -# u <2> aassign[t27] KS/COMMON -# v <1> leavesub[1 ref] K/REFC,1 +# n <0> pushmark s +# o <#> gv[*new] s +# p <1> rv2av[t2] lKRM*/1 +# q <2> aassign[t22] KS/COMMON +# r <1> leavesub[1 ref] K/REFC,1 EOT_EOT # 1 <;> nextstate(main 609 (eval 34):3) v:{ # 2 <0> pushmark s @@ -548,10 +540,8 @@ EOT_EOT # 5 <0> pushmark s # 6 <$> gv(*old) s # 7 <1> rv2av[t10] lKM/1 -# 8 <@> mapstart lK* < 5.017002 -# 8 <@> mapstart lK >=5.017002 -# 9 <|> mapwhile(other->a)[t11] lK < 5.019002 -# 9 <|> mapwhile(other->a)[t11] lKM >=5.019002 +# 8 <@> mapstart lK +# 9 <|> mapwhile(other->a)[t11] lKM # a <0> enter l # b <;> nextstate(main 608 (eval 34):2) v:{ # c <0> pushmark s @@ -563,21 +553,15 @@ EOT_EOT # i <@> leave lKP # goto 9 # j <@> sort lKMS* -# k <@> mapstart lK* < 5.017002 -# k <@> mapstart lK >=5.017002 +# k <@> mapstart lK # l <|> mapwhile(other->m)[t12] lK -# m <$> gv(*_) s -# n <1> rv2sv sKM/DREFAV,1 -# o <1> rv2av[t2] sKR/1 -# p <$> const(IV 0) s -# q <2> aelem sK/2 -# - <@> scope lK < 5.017002 +# m <+> multideref($_->[0]) sK # goto l -# r <0> pushmark s -# s <$> gv(*new) s -# t <1> rv2av[t1] lKRM*/1 -# u <2> aassign[t13] KS/COMMON -# v <1> leavesub[1 ref] K/REFC,1 +# n <0> pushmark s +# o <$> gv(*new) s +# p <1> rv2av[t1] lKRM*/1 +# q <2> aassign[t13] KS/COMMON +# r <1> leavesub[1 ref] K/REFC,1 EONT_EONT diff --git a/ext/Opcode/Opcode.pm b/ext/Opcode/Opcode.pm index b1813e072c..b9f67dd324 100644 --- a/ext/Opcode/Opcode.pm +++ b/ext/Opcode/Opcode.pm @@ -312,7 +312,7 @@ invert_opset function. av2arylen rv2hv helem hslice kvhslice each values keys exists delete - aeach akeys avalues reach rvalues rkeys + aeach akeys avalues reach rvalues rkeys multideref preinc i_preinc predec i_predec postinc i_postinc postdec i_postdec int hex oct abs pow multiply i_multiply diff --git a/intrpvar.h b/intrpvar.h index 39eac06454..ffb1172c9f 100644 --- a/intrpvar.h +++ b/intrpvar.h @@ -70,6 +70,9 @@ PERLVARI(I, hash_rand_bits_enabled, U8, 1) /* used to randomize hash stuff 0 == PERLVARI(I, hash_rand_bits, UV, 0) /* used to randomize hash stuff */ #endif PERLVAR(I, strtab, HV *) /* shared string table */ +/* prog counter for the currently executing OP_MULTIDEREF Used to signal + * to S_find_uninit_var() where we are */ +PERLVAR(I, multideref_pc, UNOP_AUX_item *) /* Fields used by magic variables such as $@, $/ and so on */ PERLVAR(I, curpm, PMOP *) /* what to do \ interps in REs from */ diff --git a/lib/B/Deparse.pm b/lib/B/Deparse.pm index 74562c58e3..267c0cdb6a 100644 --- a/lib/B/Deparse.pm +++ b/lib/B/Deparse.pm @@ -15,12 +15,36 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring OPpLVAL_INTRO OPpOUR_INTRO OPpENTERSUB_AMPER OPpSLICE OPpCONST_BARE OPpTRANS_SQUASH OPpTRANS_DELETE OPpTRANS_COMPLEMENT OPpTARGET_MY OPpEXISTS_SUB OPpSORT_NUMERIC OPpSORT_INTEGER OPpREPEAT_DOLIST - OPpSORT_REVERSE + OPpSORT_REVERSE OPpMULTIDEREF_EXISTS OPpMULTIDEREF_DELETE SVf_IOK SVf_NOK SVf_ROK SVf_POK SVpad_OUR SVf_FAKE SVs_RMG SVs_SMG SVpad_TYPED CVf_METHOD CVf_LVALUE PMf_KEEP PMf_GLOBAL PMf_CONTINUE PMf_EVAL PMf_ONCE - PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED PMf_EXTENDED_MORE); + PMf_MULTILINE PMf_SINGLELINE PMf_FOLD PMf_EXTENDED PMf_EXTENDED_MORE + MDEREF_reload + MDEREF_AV_pop_rv2av_aelem + MDEREF_AV_gvsv_vivify_rv2av_aelem + MDEREF_AV_padsv_vivify_rv2av_aelem + MDEREF_AV_vivify_rv2av_aelem + MDEREF_AV_padav_aelem + MDEREF_AV_gvav_aelem + MDEREF_HV_pop_rv2hv_helem + MDEREF_HV_gvsv_vivify_rv2hv_helem + MDEREF_HV_padsv_vivify_rv2hv_helem + MDEREF_HV_vivify_rv2hv_helem + MDEREF_HV_padhv_helem + MDEREF_HV_gvhv_helem + MDEREF_ACTION_MASK + MDEREF_INDEX_none + MDEREF_INDEX_const + MDEREF_INDEX_padsv + MDEREF_INDEX_gvsv + MDEREF_INDEX_MASK + MDEREF_FLAG_last + MDEREF_MASK + MDEREF_SHIFT + ); + $VERSION = '1.31'; use strict; use vars qw/$AUTOLOAD/; @@ -334,7 +358,7 @@ BEGIN { BEGIN { for (qw[ const stringify rv2sv list glob pushmark null aelem - custom nextstate dbstate ]) { + nextstate dbstate rv2av rv2hv helem custom ]) { eval "sub OP_\U$_ () { " . opnumber($_) . "}" }} @@ -3729,7 +3753,7 @@ sub pp_rv2av { sub is_subscriptable { my $op = shift; - if ($op->name =~ /^[ahg]elem/) { + if ($op->name =~ /^([ahg]elem|multideref$)/) { return 1; } elsif ($op->name eq "entersub") { my $kid = $op->first; @@ -3834,6 +3858,145 @@ sub elem { } +# a simplified version of elem_or_slice_array_name() +# for the use of pp_multideref + +sub multideref_var_name { + my $self = shift; + my ($gv, $is_hash) = @_; + + my ($name, $quoted) = + $self->stash_variable_name( $is_hash ? '%' : '@', $gv); + return $quoted ? "$name->" + : $name eq '#' + ? '${#}' # avoid ${#}[1] => $#[1] + : '$' . $name; +} + + +sub pp_multideref { + my $self = shift; + my($op, $cx) = @_; + my $text = ""; + + if ($op->private & OPpMULTIDEREF_EXISTS) { + $text = $self->keyword("exists"). " "; + } + elsif ($op->private & OPpMULTIDEREF_DELETE) { + $text = $self->keyword("delete"). " "; + } + elsif ($op->private & OPpLVAL_INTRO) { + $text = $self->keyword("local"). " "; + } + + if ($op->first && ($op->first->flags & OPf_KIDS)) { + # arbitrary initial expression, e.g. f(1,2,3)->[...] + $text .= $self->deparse($op->first, 24); + } + + my @items = $op->aux_list($self->{curcv}); + my $actions = shift @items; + + my $is_hash; + my $derefs = 0; + + while (1) { + if (($actions & MDEREF_ACTION_MASK) == MDEREF_reload) { + $actions = shift @items; + next; + } + + $is_hash = ( + ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_pop_rv2hv_helem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_gvsv_vivify_rv2hv_helem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_padsv_vivify_rv2hv_helem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_vivify_rv2hv_helem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_padhv_helem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_gvhv_helem + ); + + if ( ($actions & MDEREF_ACTION_MASK) == MDEREF_AV_padav_aelem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_padhv_helem) + { + $derefs = 1; + $text .= '$' . substr($self->padname(shift @items), 1); + } + elsif ( ($actions & MDEREF_ACTION_MASK) == MDEREF_AV_gvav_aelem + || ($actions & MDEREF_ACTION_MASK) == MDEREF_HV_gvhv_helem) + { + $derefs = 1; + $text .= $self->multideref_var_name(shift @items, $is_hash); + } + else { + if ( ($actions & MDEREF_ACTION_MASK) == + MDEREF_AV_padsv_vivify_rv2av_aelem + || ($actions & MDEREF_ACTION_MASK) == + MDEREF_HV_padsv_vivify_rv2hv_helem) + { + $text .= $self->padname(shift @items); + } + elsif ( ($actions & MDEREF_ACTION_MASK) == + MDEREF_AV_gvsv_vivify_rv2av_aelem + || ($actions & MDEREF_ACTION_MASK) == + MDEREF_HV_gvsv_vivify_rv2hv_helem) + { + $text .= $self->multideref_var_name(shift @items, $is_hash); + } + elsif ( ($actions & MDEREF_ACTION_MASK) == + MDEREF_AV_pop_rv2av_aelem + || ($actions & MDEREF_ACTION_MASK) == + MDEREF_HV_pop_rv2hv_helem) + { + if ( ($op->flags & OPf_KIDS) + && ( _op_is_or_was($op->first, OP_RV2AV) + || _op_is_or_was($op->first, OP_RV2HV)) + && ($op->first->flags & OPf_KIDS) + && ( _op_is_or_was($op->first->first, OP_AELEM) + || _op_is_or_was($op->first->first, OP_HELEM)) + ) + { + $derefs++; + } + } + + $text .= '->' if !$derefs++; + } + + + if (($actions & MDEREF_INDEX_MASK) == MDEREF_INDEX_none) { + last; + } + + $text .= $is_hash ? '{' : '['; + + if (($actions & MDEREF_INDEX_MASK) == MDEREF_INDEX_const) { + my $key = shift @items; + if ($is_hash) { + $text .= $self->const($key, $cx); + } + else { + $text .= $key; + } + } + elsif (($actions & MDEREF_INDEX_MASK) == MDEREF_INDEX_padsv) { + $text .= $self->padname(shift @items); + } + elsif (($actions & MDEREF_INDEX_MASK) == MDEREF_INDEX_gvsv) { + $text .= '$' . ($self->stash_variable_name('$', shift @items))[0]; + } + + $text .= $is_hash ? '}' : ']'; + + if ($actions & MDEREF_FLAG_last) { + last; + } + $actions >>= MDEREF_SHIFT; + } + + return $text; +} + + sub pp_aelem { maybe_local(@_, elem(@_, "[", "]", "padav")) } sub pp_helem { maybe_local(@_, elem(@_, "{", "}", "padhv")) } @@ -4727,7 +4890,7 @@ sub pp_stringify { while ($kid->name eq 'null' && !null($kid->first)) { $kid = $kid->first; } - if ($kid->name =~ /^(?:const|padsv|rv2sv|av2arylen|gvsv + if ($kid->name =~ /^(?:const|padsv|rv2sv|av2arylen|gvsv|multideref |aelemfast(?:_lex)?|[ah]elem|join|concat)\z/x) { maybe_targmy(@_, \&dquote); } @@ -5075,20 +5238,23 @@ sub pure_string { elsif (is_scalar($op) || $type =~ /^[ah]elem$/) { return 1; } - elsif ($type eq "null" and $op->can('first') and not null $op->first and - ($op->first->name eq "null" and $op->first->can('first') - and not null $op->first->first and - $op->first->first->name eq "aelemfast" - or - $op->first->name =~ /^aelemfast(?:_lex)?\z/ - )) { - return 1; - } - else { - return 0; + elsif ($type eq "null" and $op->can('first') and not null $op->first) { + my $first = $op->first; + + return 1 if $first->name eq "multideref"; + return 1 if $first->name eq "aelemfast_lex"; + + if ( $first->name eq "null" + and $first->can('first') + and not null $first->first + and $first->first->name eq "aelemfast" + ) + { + return 1; + } } - return 1; + return 0; } sub code_list { diff --git a/lib/B/Deparse.t b/lib/B/Deparse.t index f14c2abf1f..ef19f7163e 100644 --- a/lib/B/Deparse.t +++ b/lib/B/Deparse.t @@ -2088,3 +2088,81 @@ $_ = join $foo, pos >>>> my $foo; $_ = join('???', pos $_); +#### +# exists $a[0] +our @a; +exists $a[0]; +#### +# my @a; exists $a[0] +my @a; +exists $a[0]; +#### +# delete $a[0] +our @a; +delete $a[0]; +#### +# my @a; delete $a[0] +my @a; +delete $a[0]; +#### +# $_[0][$_[1]] +$_[0][$_[1]]; +#### +# f($a[0]); +my @a; +f($a[0]); +#### +#qr/\Q$h{'key'}\E/; +my %h; +qr/\Q$h{'key'}\E/; +#### +# my $x = "$h{foo}"; +my %h; +my $x = "$h{'foo'}"; +#### +# weird constant hash key +my %h; +my $x = $h{"\000\t\x{100}"}; +#### +# multideref and packages +package foo; +my(%bar) = ('a', 'b'); +our(@bar) = (1, 2); +$bar{'k'} = $bar[200]; +$main::bar{'k'} = $main::bar[200]; +$foo::bar{'k'} = $foo::bar[200]; +package foo2; +$bar{'k'} = $bar[200]; +$main::bar{'k'} = $main::bar[200]; +$foo::bar{'k'} = $foo::bar[200]; +>>>> +package foo; +my(%bar) = ('a', 'b'); +our(@bar) = (1, 2); +$bar{'k'} = $bar[200]; +$main::bar{'k'} = $main::bar[200]; +$foo::bar{'k'} = $bar[200]; +package foo2; +$bar{'k'} = $foo::bar[200]; +$main::bar{'k'} = $main::bar[200]; +$foo::bar{'k'} = $foo::bar[200]; +#### +# multideref and local +my %h; +local $h{'foo'}[0] = 1; +#### +# multideref and exists +my(%h, $i); +my $e = exists $h{'foo'}[$i]; +#### +# multideref and delete +my(%h, $i); +my $e = delete $h{'foo'}[$i]; +#### +# multideref with leading expression +my $r; +my $x = ($r // [])->{'foo'}[0]; +#### +# multideref with complex middle index +my(%h, $i, $j, $k); +my $x = $h{'foo'}[$i + $j]{$k}; diff --git a/lib/B/Op_private.pm b/lib/B/Op_private.pm index e8e63a2e32..e7383644b7 100644 --- a/lib/B/Op_private.pm +++ b/lib/B/Op_private.pm @@ -129,15 +129,15 @@ $bits{$_}{4} = 'OPpFT_AFTER_t' for qw(ftatime ftbinary ftblk ftchr ftctime ftdir $bits{$_}{2} = 'OPpFT_STACKED' for qw(ftatime ftbinary ftblk ftchr ftctime ftdir fteexec fteowned fteread ftewrite ftfile ftis ftlink ftmtime ftpipe ftrexec ftrowned ftrread ftrwrite ftsgid ftsize ftsock ftsuid ftsvtx fttext fttty ftzero); $bits{$_}{3} = 'OPpFT_STACKING' for qw(ftatime ftbinary ftblk ftchr ftctime ftdir fteexec fteowned fteread ftewrite ftfile ftis ftlink ftmtime ftpipe ftrexec ftrowned ftrread ftrwrite ftsgid ftsize ftsock ftsuid ftsvtx fttext fttty ftzero); $bits{$_}{1} = 'OPpGREP_LEX' for qw(grepstart grepwhile mapstart mapwhile); -$bits{$_}{1} = 'OPpHINT_STRICT_REFS' for qw(entersub rv2av rv2cv rv2gv rv2hv rv2sv); +$bits{$_}{1} = 'OPpHINT_STRICT_REFS' for qw(entersub multideref rv2av rv2cv rv2gv rv2hv rv2sv); $bits{$_}{5} = 'OPpHUSH_VMSISH' for qw(dbstate nextstate); $bits{$_}{2} = 'OPpITER_REVERSED' for qw(enteriter iter); $bits{$_}{7} = 'OPpLVALUE' for qw(leave leaveloop); -$bits{$_}{6} = 'OPpLVAL_DEFER' for qw(aelem helem); -$bits{$_}{7} = 'OPpLVAL_INTRO' for qw(aelem aslice cond_expr delete enteriter entersub gvsv helem hslice list lvavref lvref lvrefslice padav padhv padrange padsv pushmark refassign rv2av rv2gv rv2hv rv2sv); +$bits{$_}{6} = 'OPpLVAL_DEFER' for qw(aelem helem multideref); +$bits{$_}{7} = 'OPpLVAL_INTRO' for qw(aelem aslice cond_expr delete enteriter entersub gvsv helem hslice list lvavref lvref lvrefslice multideref padav padhv padrange padsv pushmark refassign rv2av rv2gv rv2hv rv2sv); $bits{$_}{2} = 'OPpLVREF_ELEM' for qw(lvref refassign); $bits{$_}{3} = 'OPpLVREF_ITER' for qw(lvref refassign); -$bits{$_}{3} = 'OPpMAYBE_LVSUB' for qw(aassign aelem aslice av2arylen helem hslice keys kvaslice kvhslice padav padhv pos rkeys rv2av rv2gv rv2hv substr vec); +$bits{$_}{3} = 'OPpMAYBE_LVSUB' for qw(aassign aelem aslice av2arylen helem hslice keys kvaslice kvhslice multideref padav padhv pos rkeys rv2av rv2gv rv2hv substr vec); $bits{$_}{4} = 'OPpMAYBE_TRUEBOOL' for qw(padhv rv2hv); $bits{$_}{7} = 'OPpOFFBYONE' for qw(caller runcv wantarray); $bits{$_}{5} = 'OPpOPEN_IN_CRLF' for qw(backtick open); @@ -415,6 +415,7 @@ $bits{method_super}{0} = $bf[0]; @{$bits{msgget}}{3,2,1,0} = ($bf[3], $bf[3], $bf[3], $bf[3]); @{$bits{msgrcv}}{3,2,1,0} = ($bf[3], $bf[3], $bf[3], $bf[3]); @{$bits{msgsnd}}{3,2,1,0} = ($bf[3], $bf[3], $bf[3], $bf[3]); +@{$bits{multideref}}{5,4,0} = ('OPpMULTIDEREF_DELETE', 'OPpMULTIDEREF_EXISTS', $bf[0]); @{$bits{multiply}}{1,0} = ($bf[1], $bf[1]); @{$bits{ncmp}}{1,0} = ($bf[1], $bf[1]); @{$bits{ne}}{1,0} = ($bf[1], $bf[1]); @@ -610,6 +611,8 @@ our %defines = ( OPpMAYBE_LVSUB => 8, OPpMAYBE_TRUEBOOL => 16, OPpMAY_RETURN_CONSTANT => 32, + OPpMULTIDEREF_DELETE => 32, + OPpMULTIDEREF_EXISTS => 16, OPpOFFBYONE => 128, OPpOPEN_IN_CRLF => 32, OPpOPEN_IN_RAW => 16, @@ -699,6 +702,8 @@ our %labels = ( OPpMAYBE_LVSUB => 'LVSUB', OPpMAYBE_TRUEBOOL => 'BOOL?', OPpMAY_RETURN_CONSTANT => 'CONST', + OPpMULTIDEREF_DELETE => 'DELETE', + OPpMULTIDEREF_EXISTS => 'EXISTS', OPpOFFBYONE => '+1', OPpOPEN_IN_CRLF => 'INCR', OPpOPEN_IN_RAW => 'INBIN', @@ -750,17 +755,18 @@ our %ops_using = ( OPpFT_ACCESS => [qw(fteexec fteread ftewrite ftrexec ftrread ftrwrite)], OPpFT_AFTER_t => [qw(ftatime ftbinary ftblk ftchr ftctime ftdir fteexec fteowned fteread ftewrite ftfile ftis ftlink ftmtime ftpipe ftrexec ftrowned ftrread ftrwrite ftsgid ftsize ftsock ftsuid ftsvtx fttext fttty ftzero)], OPpGREP_LEX => [qw(grepstart grepwhile mapstart mapwhile)], - OPpHINT_STRICT_REFS => [qw(entersub rv2av rv2cv rv2gv rv2hv rv2sv)], + OPpHINT_STRICT_REFS => [qw(entersub multideref rv2av rv2cv rv2gv rv2hv rv2sv)], OPpHUSH_VMSISH => [qw(dbstate nextstate)], OPpITER_DEF => [qw(enteriter)], OPpITER_REVERSED => [qw(enteriter iter)], OPpLIST_GUESSED => [qw(list)], OPpLVALUE => [qw(leave leaveloop)], - OPpLVAL_DEFER => [qw(aelem helem)], - OPpLVAL_INTRO => [qw(aelem aslice cond_expr delete enteriter entersub gvsv helem hslice list lvavref lvref lvrefslice padav padhv padrange padsv pushmark refassign rv2av rv2gv rv2hv rv2sv)], + OPpLVAL_DEFER => [qw(aelem helem multideref)], + OPpLVAL_INTRO => [qw(aelem aslice cond_expr delete enteriter entersub gvsv helem hslice list lvavref lvref lvrefslice multideref padav padhv padrange padsv pushmark refassign rv2av rv2gv rv2hv rv2sv)], OPpLVREF_ELEM => [qw(lvref refassign)], - OPpMAYBE_LVSUB => [qw(aassign aelem aslice av2arylen helem hslice keys kvaslice kvhslice padav padhv pos rkeys rv2av rv2gv rv2hv substr vec)], + OPpMAYBE_LVSUB => [qw(aassign aelem aslice av2arylen helem hslice keys kvaslice kvhslice multideref padav padhv pos rkeys rv2av rv2gv rv2hv substr vec)], OPpMAYBE_TRUEBOOL => [qw(padhv rv2hv)], + OPpMULTIDEREF_DELETE => [qw(multideref)], OPpOFFBYONE => [qw(caller runcv wantarray)], OPpOPEN_IN_CRLF => [qw(backtick open)], OPpOUR_INTRO => [qw(enteriter gvsv rv2av rv2hv rv2sv split)], @@ -798,6 +804,7 @@ $ops_using{OPpFT_STACKED} = $ops_using{OPpFT_AFTER_t}; $ops_using{OPpFT_STACKING} = $ops_using{OPpFT_AFTER_t}; $ops_using{OPpLVREF_ITER} = $ops_using{OPpLVREF_ELEM}; $ops_using{OPpMAY_RETURN_CONSTANT} = $ops_using{OPpENTERSUB_NOPAREN}; +$ops_using{OPpMULTIDEREF_EXISTS} = $ops_using{OPpMULTIDEREF_DELETE}; $ops_using{OPpOPEN_IN_RAW} = $ops_using{OPpOPEN_IN_CRLF}; $ops_using{OPpOPEN_OUT_CRLF} = $ops_using{OPpOPEN_IN_CRLF}; $ops_using{OPpOPEN_OUT_RAW} = $ops_using{OPpOPEN_IN_CRLF}; @@ -797,7 +797,8 @@ void S_op_clear_gv(pTHX_ OP *o, SV**svp) #endif { - GV *gv = (o->op_type == OP_GV || o->op_type == OP_GVSV) + GV *gv = (o->op_type == OP_GV || o->op_type == OP_GVSV + || o->op_type == OP_MULTIDEREF) #ifdef USE_ITHREADS && PL_curpad ? ((GV*)PAD_SVl(*ixp)) : NULL; @@ -975,6 +976,109 @@ clear_pmop: #endif break; + + case OP_MULTIDEREF: + { + UNOP_AUX_item *items = cUNOP_AUXo->op_aux; + UV actions = items->uv; + bool last = 0; + bool is_hash = FALSE; + + while (!last) { + switch (actions & MDEREF_ACTION_MASK) { + + case MDEREF_reload: + actions = (++items)->uv; + continue; + + case MDEREF_HV_padhv_helem: + is_hash = TRUE; + case MDEREF_AV_padav_aelem: + pad_free((++items)->pad_offset); + goto do_elem; + + case MDEREF_HV_gvhv_helem: + is_hash = TRUE; + case MDEREF_AV_gvav_aelem: +#ifdef USE_ITHREADS + S_op_clear_gv(aTHX_ o, &((++items)->pad_offset)); +#else + S_op_clear_gv(aTHX_ o, &((++items)->sv)); +#endif + goto do_elem; + + case MDEREF_HV_gvsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_gvsv_vivify_rv2av_aelem: +#ifdef USE_ITHREADS + S_op_clear_gv(aTHX_ o, &((++items)->pad_offset)); +#else + S_op_clear_gv(aTHX_ o, &((++items)->sv)); +#endif + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_padsv_vivify_rv2hv_helem: + is_hash = TRUE; + case MDEREF_AV_padsv_vivify_rv2av_aelem: + pad_free((++items)->pad_offset); + goto do_vivify_rv2xv_elem; + + case MDEREF_HV_pop_rv2hv_helem: + case MDEREF_HV_vivify_rv2hv_helem: + is_hash = TRUE; + do_vivify_rv2xv_elem: + case MDEREF_AV_pop_rv2av_aelem: + case MDEREF_AV_vivify_rv2av_aelem: + do_elem: + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + last = 1; + break; + case MDEREF_INDEX_const: + if (is_hash) { +#ifdef USE_ITHREADS + /* see RT #15654 */ + pad_swipe((++items)->pad_offset, 1); +#else + SvREFCNT_dec((++items)->sv); +#endif + } + else + items++; + break; + case MDEREF_INDEX_padsv: + pad_free((++items)->pad_offset); + break; + case MDEREF_INDEX_gvsv: +#ifdef USE_ITHREADS + S_op_clear_gv(aTHX_ o, &((++items)->pad_offset)); +#else + S_op_clear_gv(aTHX_ o, &((++items)->sv)); +#endif + break; + } + + if (actions & MDEREF_FLAG_last) + last = 1; + is_hash = FALSE; + + break; + + default: + assert(0); + last = 1; + break; + + } /* switch */ + + actions >>= MDEREF_SHIFT; + } /* while */ + + /* start of malloc is at op_aux[-1], where the length is + * stored */ + PerlMemShared_free(cUNOP_AUXo->op_aux - 1); + } + break; } if (o->op_targ > 0) { @@ -2171,7 +2275,7 @@ S_modkids(pTHX_ OP *o, I32 type) */ void -S_check_hash_fields(pTHX_ UNOP *rop, SVOP *key_op) +S_check_hash_fields_and_hekify(pTHX_ UNOP *rop, SVOP *key_op) { PADNAME *lexname; GV **fields; @@ -2379,7 +2483,7 @@ S_finalize_op(pTHX_ OP* o) check_keys: if (o->op_private & OPpLVAL_INTRO || rop->op_type != OP_RV2HV) rop = NULL; - S_check_hash_fields(aTHX_ rop, key_op); + S_check_hash_fields_and_hekify(aTHX_ rop, key_op); break; } case OP_ASLICE: @@ -4705,7 +4809,7 @@ Perl_newUNOP(pTHX_ I32 type, I32 flags, OP *first) } /* -=for apidoc +=for apidoc newUNOP_AUX Similar to C<newUNOP>, but creates an UNOP_AUX struct instead, with op_aux initialised to aux @@ -12065,6 +12169,608 @@ S_inplace_aassign(pTHX_ OP *o) { +/* S_maybe_multideref(): given an op_next chain of ops beginning at 'start' + * that potentially represent a series of one or more aggregate derefs + * (such as $a->[1]{$key}), examine the chain, and if appropriate, convert + * the whole chain to a single OP_MULTIDEREF op (maybe with a few + * additional ops left in too). + * + * The caller will have already verified that the first few ops in the + * chain following 'start' indicate a multideref candidate, and will have + * set 'orig_o' to the point further on in the chain where the first index + * expression (if any) begins. 'orig_action' specifies what type of + * beginning has already been determined by the ops between start..orig_o + * (e.g. $lex_ary[], $pkg_ary->{}, expr->[], etc). + * + * 'hints' contains any hints flags that need adding (currently just + * OPpHINT_STRICT_REFS) as found in any rv2av/hv skipped by the caller. + */ + +void +S_maybe_multideref(pTHX_ OP *start, OP *orig_o, UV orig_action, U8 hints) +{ + dVAR; + int pass; + UNOP_AUX_item *arg_buf = NULL; + bool reset_start_targ = FALSE; /* start->op_targ needs zeroing */ + int index_skip = -1; /* don't output index arg on this action */ + + /* similar to regex compiling, do two passes; the first pass + * determines whether the op chain is convertible and calculates the + * buffer size; the second pass populates the buffer and makes any + * changes necessary to ops (such as moving consts to the pad on + * threaded builds) + */ + for (pass = 0; pass < 2; pass++) { + OP *o = orig_o; + UV action = orig_action; + OP *first_elem_op = NULL; /* first seen aelem/helem */ + OP *top_op = NULL; /* highest [ah]elem/exists/del/rv2[ah]v */ + int action_count = 0; /* number of actions seen so far */ + int action_ix = 0; /* action_count % (actions per IV) */ + bool next_is_hash = FALSE; /* is the next lookup to be a hash? */ + bool is_last = FALSE; /* no more derefs to follow */ + bool maybe_aelemfast = FALSE; /* we can replace with aelemfast? */ + UNOP_AUX_item *arg = arg_buf; + UNOP_AUX_item *action_ptr = arg_buf; + + if (pass) + action_ptr->uv = 0; + arg++; + + switch (action) { + case MDEREF_HV_gvsv_vivify_rv2hv_helem: + case MDEREF_HV_gvhv_helem: + next_is_hash = TRUE; + /* FALLTHROUGH */ + case MDEREF_AV_gvsv_vivify_rv2av_aelem: + case MDEREF_AV_gvav_aelem: + if (pass) { +#ifdef USE_ITHREADS + arg->pad_offset = cPADOPx(start)->op_padix; + /* stop it being swiped when nulled */ + cPADOPx(start)->op_padix = 0; +#else + arg->sv = cSVOPx(start)->op_sv; + cSVOPx(start)->op_sv = NULL; +#endif + } + arg++; + break; + + case MDEREF_HV_padhv_helem: + case MDEREF_HV_padsv_vivify_rv2hv_helem: + next_is_hash = TRUE; + /* FALLTHROUGH */ + case MDEREF_AV_padav_aelem: + case MDEREF_AV_padsv_vivify_rv2av_aelem: + if (pass) { + arg->pad_offset = start->op_targ; + /* we skip setting op_targ = 0 for now, since the intact + * OP_PADXV is needed by S_check_hash_fields_and_hekify */ + reset_start_targ = TRUE; + } + arg++; + break; + + case MDEREF_HV_pop_rv2hv_helem: + next_is_hash = TRUE; + /* FALLTHROUGH */ + case MDEREF_AV_pop_rv2av_aelem: + break; + + default: + assert(0); + return; + } + + while (!is_last) { + /* look for another (rv2av/hv; get index; + * aelem/helem/exists/delele) sequence */ + + IV iv; + OP *kid; + bool is_deref; + bool ok; + UV index_type = MDEREF_INDEX_none; + + if (action_count) { + /* if this is not the first lookup, consume the rv2av/hv */ + + /* for N levels of aggregate lookup, we normally expect + * that the first N-1 [ah]elem ops will be flagged as + * /DEREF (so they autovivifiy if necessary), and the last + * lookup op not to be. + * For other things (like @{$h{k1}{k2}}) extra scope or + * leave ops can appear, so abandon the effort in that + * case */ + if (o->op_type != OP_RV2AV && o->op_type != OP_RV2HV) + return; + + /* rv2av or rv2hv sKR/1 */ + + assert(!(o->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_PARENS + |OPf_REF|OPf_MOD|OPf_SPECIAL))); + if (o->op_flags != (OPf_WANT_SCALAR|OPf_KIDS|OPf_REF)) + return; + + /* at this point, we wouldn't expect any of these + * possible private flags: + * OPpMAYBE_LVSUB, OPpOUR_INTRO, OPpLVAL_INTRO + * OPpTRUEBOOL, OPpMAYBE_TRUEBOOL (rv2hv only) + */ + assert(!(o->op_private & + ~(OPpHINT_STRICT_REFS|OPpARG1_MASK|OPpSLICEWARNING))); + + hints = (o->op_private & OPpHINT_STRICT_REFS); + + /* make sure the type of the previous /DEREF matches the + * type of the next lookup */ + assert(o->op_type == (next_is_hash ? OP_RV2HV : OP_RV2AV)); + top_op = o; + + action = next_is_hash + ? MDEREF_HV_vivify_rv2hv_helem + : MDEREF_AV_vivify_rv2av_aelem; + o = o->op_next; + } + + /* if this is the second pass, and we're at the depth where + * previously we encountered a non-simple index expression, + * stop processing the index at this point */ + if (action_count != index_skip) { + + /* look for one or more simple ops that return an array + * index or hash key */ + + switch (o->op_type) { + case OP_PADSV: + /* it may be a lexical var index */ + assert(!(o->op_flags & ~(OPf_WANT|OPf_PARENS + |OPf_REF|OPf_MOD|OPf_SPECIAL))); + assert(!(o->op_private & + ~(OPpPAD_STATE|OPpDEREF|OPpLVAL_INTRO))); + + if ( o->op_flags == OPf_WANT_SCALAR + && o->op_private == 0) + { + if (pass) + arg->pad_offset = o->op_targ; + arg++; + index_type = MDEREF_INDEX_padsv; + o = o->op_next; + } + break; + + case OP_CONST: + if (next_is_hash) { + /* it's a constant hash index */ + if (!(SvFLAGS(cSVOPo_sv) & (SVf_IOK|SVf_NOK|SVf_POK))) + /* "use constant foo => FOO; $h{+foo}" for + * some weird FOO, can leave you with constants + * that aren't simple strings. It's not worth + * the extra hassle for those edge cases */ + break; + + if (pass) { + UNOP *rop = NULL; + OP * helem_op = o->op_next; + + assert( helem_op->op_type == OP_HELEM + || helem_op->op_type == OP_NULL); + if (helem_op->op_type == OP_HELEM) { + rop = (UNOP*)(((BINOP*)helem_op)->op_first); + if ( helem_op->op_private & OPpLVAL_INTRO + || rop->op_type != OP_RV2HV + ) + rop = NULL; + } + S_check_hash_fields_and_hekify(aTHX_ rop, cSVOPo); + +#ifdef USE_ITHREADS + /* Relocate sv to the pad for thread safety */ + op_relocate_sv(&cSVOPo->op_sv, &o->op_targ); + arg->pad_offset = o->op_targ; + o->op_targ = 0; +#else + arg->sv = cSVOPx_sv(o); +#endif + } + } + else { + /* it's a constant array index */ + SV *ix_sv = cSVOPo->op_sv; + if (UNLIKELY(SvROK(ix_sv) && !SvGAMAGIC(ix_sv) + && ckWARN(WARN_MISC))) + Perl_warner(aTHX_ packWARN(WARN_MISC), + "Use of reference \"%"SVf"\" as array index", + SVfARG(ix_sv)); + iv = SvIV(ix_sv); + + if ( action_count == 0 + && iv >= -128 + && iv <= 127 + && ( action == MDEREF_AV_padav_aelem + || action == MDEREF_AV_gvav_aelem) + ) + maybe_aelemfast = TRUE; + + if (pass) { + arg->iv = iv; + SvREFCNT_dec_NN(cSVOPo->op_sv); + } + } + if (pass) + /* we've taken ownership of the SV */ + cSVOPo->op_sv = NULL; + arg++; + index_type = MDEREF_INDEX_const; + o = o->op_next; + break; + + case OP_GV: + /* it may be a package var index */ + + assert(!(o->op_flags & ~(OPf_WANT))); + assert(!(o->op_private & ~(OPpEARLY_CV))); + if ( o->op_flags != OPf_WANT_SCALAR + || o->op_private != 0 + ) + break; + + kid = o->op_next; + if (kid->op_type != OP_RV2SV) + break; + + assert(!(kid->op_flags & + ~(OPf_WANT|OPf_KIDS|OPf_MOD|OPf_REF|OPf_SPECIAL))); + assert(!(kid->op_private & + ~(OPpARG1_MASK + |OPpHINT_STRICT_REFS|OPpOUR_INTRO + |OPpDEREF|OPpLVAL_INTRO))); + if( kid->op_flags != (OPf_WANT_SCALAR|OPf_KIDS) + || (kid->op_private & ~(OPpARG1_MASK|HINT_STRICT_REFS)) + ) + break; + + if (pass) { +#ifdef USE_ITHREADS + arg->pad_offset = cPADOPx(o)->op_padix; + /* stop it being swiped when nulled */ + cPADOPx(o)->op_padix = 0; +#else + arg->sv = cSVOPx(o)->op_sv; + cSVOPo->op_sv = NULL; +#endif + } + arg++; + index_type = MDEREF_INDEX_gvsv; + o = kid->op_next; + break; + + } /* switch */ + } /* action_count != index_skip */ + + action |= index_type; + + + /* at this point we have either: + * * detected what looks like a simple index expression, + * and expect the next op to be an [ah]elem, or + * an nulled [ah]elem followed by a delete or exists; + * * found a more complex expression, so something other + * than the above follows. + */ + + /* possibly an optimised away [ah]elem (where op_next is + * exists or delete) */ + if (o->op_type == OP_NULL) + o = o->op_next; + + /* at this point we're looking for an OP_AELEM, OP_HELEM, + * OP_EXISTS or OP_DELETE */ + + /* if something like arybase (a.k.a $[ ) is in scope, + * abandon optimisation attempt */ + if (o->op_type == OP_AELEM && PL_check[OP_AELEM] != Perl_ck_null) + return; + + if ( o->op_type != OP_AELEM + || (o->op_private & + (OPpLVAL_INTRO|OPpLVAL_DEFER|OPpDEREF|OPpMAYBE_LVSUB)) + ) + maybe_aelemfast = FALSE; + + /* look for aelem/helem/exists/delete. If it's not the last elem + * lookup, it *must* have OPpDEREF_AV/HV, but not many other + * flags; if it's the last, then it mustn't have + * OPpDEREF_AV/HV, but may have lots of other flags, like + * OPpLVAL_INTRO etc + */ + + if ( index_type == MDEREF_INDEX_none + || ( o->op_type != OP_AELEM && o->op_type != OP_HELEM + && o->op_type != OP_EXISTS && o->op_type != OP_DELETE) + ) + ok = FALSE; + else { + /* we have aelem/helem/exists/delete with valid simple index */ + + is_deref = (o->op_type == OP_AELEM || o->op_type == OP_HELEM) + && ( (o->op_private & OPpDEREF) == OPpDEREF_AV + || (o->op_private & OPpDEREF) == OPpDEREF_HV); + + if (is_deref) { + assert(!(o->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_MOD))); + assert(!(o->op_private & ~(OPpARG2_MASK|OPpDEREF))); + + ok = o->op_flags == (OPf_WANT_SCALAR|OPf_KIDS|OPf_MOD) + && !(o->op_private & ~(OPpDEREF|OPpARG2_MASK)); + } + else if (o->op_type == OP_EXISTS) { + assert(!(o->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_PARENS + |OPf_REF|OPf_MOD|OPf_SPECIAL))); + assert(!(o->op_private & ~(OPpARG1_MASK|OPpEXISTS_SUB))); + ok = !(o->op_private & ~OPpARG1_MASK); + } + else if (o->op_type == OP_DELETE) { + assert(!(o->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_PARENS + |OPf_REF|OPf_MOD|OPf_SPECIAL))); + assert(!(o->op_private & + ~(OPpARG1_MASK|OPpSLICE|OPpLVAL_INTRO))); + /* don't handle slices or 'local delete'; the latter + * is fairly rare, and has a complex runtime */ + ok = !(o->op_private & ~OPpARG1_MASK); + if (OP_TYPE_IS_OR_WAS(cUNOPo->op_first, OP_AELEM)) + /* skip handling run-tome error */ + ok = (ok && cBOOL(o->op_flags & OPf_SPECIAL)); + } + else { + assert(o->op_type == OP_AELEM || o->op_type == OP_HELEM); + assert(!(o->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_MOD + |OPf_PARENS|OPf_REF|OPf_SPECIAL))); + assert(!(o->op_private & ~(OPpARG2_MASK|OPpMAYBE_LVSUB + |OPpLVAL_DEFER|OPpDEREF|OPpLVAL_INTRO))); + ok = (o->op_private & OPpDEREF) != OPpDEREF_SV; + } + } + + if (ok) { + if (!first_elem_op) + first_elem_op = o; + top_op = o; + if (is_deref) { + next_is_hash = cBOOL((o->op_private & OPpDEREF) == OPpDEREF_HV); + o = o->op_next; + } + else { + is_last = TRUE; + action |= MDEREF_FLAG_last; + } + } + else { + /* at this point we have something that started + * promisingly enough (with rv2av or whatever), but failed + * to find a simple index followed by an + * aelem/helem/exists/delete. If this is the first action, + * give up; but if we've already seen at least one + * aelem/helem, then keep them and add a new action with + * MDEREF_INDEX_none, which causes it to do the vivify + * from the end of the previous lookup, and do the deref, + * but stop at that point. So $a[0][expr] will do one + * av_fetch, vivify and deref, then continue executing at + * expr */ + if (!action_count) + return; + is_last = TRUE; + index_skip = action_count; + action |= MDEREF_FLAG_last; + } + + if (pass) + action_ptr->uv |= (action << (action_ix * MDEREF_SHIFT)); + action_ix++; + action_count++; + /* if there's no space for the next action, create a new slot + * for it *before* we start adding args for that action */ + if ((action_ix + 1) * MDEREF_SHIFT > UVSIZE*8) { + action_ptr = arg; + if (pass) + arg->uv = 0; + arg++; + action_ix = 0; + } + } /* while !is_last */ + + /* success! */ + + if (pass) { + OP *mderef; + OP *p; + + mderef = newUNOP_AUX(OP_MULTIDEREF, 0, NULL, arg_buf); + if (index_skip == -1) { + mderef->op_flags = o->op_flags + & (OPf_WANT|OPf_MOD|(next_is_hash ? OPf_SPECIAL : 0)); + if (o->op_type == OP_EXISTS) + mderef->op_private = OPpMULTIDEREF_EXISTS; + else if (o->op_type == OP_DELETE) + mderef->op_private = OPpMULTIDEREF_DELETE; + else + mderef->op_private = o->op_private + & (OPpMAYBE_LVSUB|OPpLVAL_DEFER|OPpLVAL_INTRO); + } + /* accumulate strictness from every level (although I don't think + * they can actually vary) */ + mderef->op_private |= hints; + + /* integrate the new multideref op into the optree and the + * op_next chain. + * + * In general an op like aelem or helem has two child + * sub-trees: the aggregate expression (a_expr) and the + * index expression (i_expr): + * + * aelem + * | + * a_expr - i_expr + * + * The a_expr returns an AV or HV, while the i-expr returns an + * index. In general a multideref replaces most or all of a + * multi-level tree, e.g. + * + * exists + * | + * ex-aelem + * | + * rv2av - i_expr1 + * | + * helem + * | + * rv2hv - i_expr2 + * | + * aelem + * | + * a_expr - i_expr3 + * + * With multideref, all the i_exprs will be simple vars or + * constants, except that i_expr1 may be arbitrary in the case + * of MDEREF_INDEX_none. + * + * The bottom-most a_expr will be either: + * 1) a simple var (so padXv or gv+rv2Xv); + * 2) a simple scalar var dereferenced (e.g. $r->[0]): + * so a simple var with an extra rv2Xv; + * 3) or an arbitrary expression. + * + * 'start', the first op in the execution chain, will point to + * 1),2): the padXv or gv op; + * 3): the rv2Xv which forms the last op in the a_expr + * execution chain, and the top-most op in the a_expr + * subtree. + * + * For all cases, the 'start' node is no longer required, + * but we can't free it since one or more external nodes + * may point to it. E.g. consider + * $h{foo} = $a ? $b : $c + * Here, both the op_next and op_other branches of the + * cond_expr point to the gv[*h] of the hash expression, so + * we can't free the 'start' op. + * + * For expr->[...], we need to save the subtree containing the + * expression; for the other cases, we just need to save the + * start node. + * So in all cases, we null the start op and keep it around by + * making it the child of the multideref op; for the expr-> + * case, the expr will be a subtree of the start node. + * + * So in the simple 1,2 case the optree above changes to + * + * ex-exists + * | + * multideref + * | + * ex-gv (or ex-padxv) + * + * with the op_next chain being + * + * -> ex-gv -> multideref -> op-following-ex-exists -> + * + * In the 3 case, we have + * + * ex-exists + * | + * multideref + * | + * ex-rv2xv + * | + * rest-of-a_expr + * subtree + * + * and + * + * -> rest-of-a_expr subtree -> + * ex-rv2xv -> multideref -> op-following-ex-exists -> + * + * + * Where the last i_expr is non-simple (i.e. MDEREF_INDEX_none, + * e.g. $a[0]{foo}[$x+1], the next rv2xv is nulled and the + * multideref attached as the child, e.g. + * + * exists + * | + * ex-aelem + * | + * ex-rv2av - i_expr1 + * | + * multideref + * | + * ex-whatever + * + */ + + /* if we free this op, don't free the pad entry */ + if (reset_start_targ) + start->op_targ = 0; + + + /* Cut the bit we need to save out of the tree and attach to + * the multideref op, then free the rest of the tree */ + + /* find parent of node to be detached (for use by splice) */ + p = first_elem_op; + if ( orig_action == MDEREF_AV_pop_rv2av_aelem + || orig_action == MDEREF_HV_pop_rv2hv_helem) + { + /* there is an arbitrary expression preceding us, e.g. + * expr->[..]? so we need to save the 'expr' subtree */ + if (p->op_type == OP_EXISTS || p->op_type == OP_DELETE) + p = cUNOPx(p)->op_first; + assert( start->op_type == OP_RV2AV + || start->op_type == OP_RV2HV); + } + else { + /* either a padXv or rv2Xv+gv, maybe with an ex-Xelem + * above for exists/delete. */ + while ( (p->op_flags & OPf_KIDS) + && cUNOPx(p)->op_first != start + ) + p = cUNOPx(p)->op_first; + } + assert(cUNOPx(p)->op_first == start); + + /* detach from main tree, and re-attach under the multideref */ + op_sibling_splice(mderef, NULL, 0, + op_sibling_splice(p, NULL, 1, NULL)); + op_null(start); + + start->op_next = mderef; + + mderef->op_next = index_skip == -1 ? o->op_next : o; + + /* excise and free the original tree, and replace with + * the multideref op */ + op_free(op_sibling_splice(top_op, NULL, -1, mderef)); + op_null(top_op); + } + else { + Size_t size = arg - arg_buf; + + if (maybe_aelemfast && action_count == 1) + return; + + arg_buf = (UNOP_AUX_item*)PerlMemShared_malloc( + sizeof(UNOP_AUX_item) * (size + 1)); + /* for dumping etc: store the length in a hidden first slot; + * we set the op_aux pointer to the second slot */ + arg_buf->uv = size; + arg_buf++; + } + } /* for (pass = ...) */ +} + + + /* mechanism for deferring recursion in rpeep() */ #define MAX_DEFERRED 4 @@ -12125,6 +12831,183 @@ Perl_rpeep(pTHX_ OP *o) o->op_opt = 1; PL_op = o; + /* look for a series of 1 or more aggregate derefs, e.g. + * $a[1]{foo}[$i]{$k} + * and replace with a single OP_MULTIDEREF op. + * Each index must be either a const, or a simple variable, + * + * First, look for likely combinations of starting ops, + * corresponding to (global and lexical variants of) + * $a[...] $h{...} + * $r->[...] $r->{...} + * (preceding expression)->[...] + * (preceding expression)->{...} + * and if so, call maybe_multideref() to do a full inspection + * of the op chain and if appropriate, replace with an + * OP_MULTIDEREF + */ + { + UV action; + OP *o2 = o; + U8 hints = 0; + + switch (o2->op_type) { + case OP_GV: + /* $pkg[..] : gv[*pkg] + * $pkg->[...]: gv[*pkg]; rv2sv sKM/DREFAV */ + + /* Fail if there are new op flag combinations that we're + * not aware of, rather than: + * * silently failing to optimise, or + * * silently optimising the flag away. + * If this assert starts failing, examine what new flag + * has been added to the op, and decide whether the + * optimisation should still occur with that flag, then + * update the code accordingly. This applies to all the + * other asserts in the block of code too. + */ + assert(!(o2->op_flags & ~(OPf_WANT|OPf_MOD))); + assert(!(o2->op_private & ~OPpEARLY_CV)); + + o2 = o2->op_next; + + if (o2->op_type == OP_RV2AV) { + action = MDEREF_AV_gvav_aelem; + goto do_deref; + } + + if (o2->op_type == OP_RV2HV) { + action = MDEREF_HV_gvhv_helem; + goto do_deref; + } + + if (o2->op_type != OP_RV2SV) + break; + + /* at this point we've seen gv,rv2sv, so the only valid + * construct left is $pkg->[] or $pkg->{} */ + + assert(!(o2->op_flags & OPf_STACKED)); + if ((o2->op_flags & (OPf_WANT|OPf_REF|OPf_MOD|OPf_SPECIAL)) + != (OPf_WANT_SCALAR|OPf_MOD)) + break; + + assert(!(o2->op_private & ~(OPpARG1_MASK|HINT_STRICT_REFS + |OPpOUR_INTRO|OPpDEREF|OPpLVAL_INTRO))); + if (o2->op_private & (OPpOUR_INTRO|OPpLVAL_INTRO)) + break; + if ( (o2->op_private & OPpDEREF) != OPpDEREF_AV + && (o2->op_private & OPpDEREF) != OPpDEREF_HV) + break; + + o2 = o2->op_next; + if (o2->op_type == OP_RV2AV) { + action = MDEREF_AV_gvsv_vivify_rv2av_aelem; + goto do_deref; + } + if (o2->op_type == OP_RV2HV) { + action = MDEREF_HV_gvsv_vivify_rv2hv_helem; + goto do_deref; + } + break; + + case OP_PADSV: + /* $lex->[...]: padsv[$lex] sM/DREFAV */ + + assert(!(o2->op_flags & + ~(OPf_WANT|OPf_PARENS|OPf_REF|OPf_MOD|OPf_SPECIAL))); + if ((o2->op_flags & + (OPf_WANT|OPf_REF|OPf_MOD|OPf_SPECIAL)) + != (OPf_WANT_SCALAR|OPf_MOD)) + break; + + assert(!(o2->op_private & + ~(OPpPAD_STATE|OPpDEREF|OPpLVAL_INTRO))); + /* skip if state or intro, or not a deref */ + if ( o2->op_private != OPpDEREF_AV + && o2->op_private != OPpDEREF_HV) + break; + + o2 = o2->op_next; + if (o2->op_type == OP_RV2AV) { + action = MDEREF_AV_padsv_vivify_rv2av_aelem; + goto do_deref; + } + if (o2->op_type == OP_RV2HV) { + action = MDEREF_HV_padsv_vivify_rv2hv_helem; + goto do_deref; + } + break; + + case OP_PADAV: + case OP_PADHV: + /* $lex[..]: padav[@lex:1,2] sR * + * or $lex{..}: padhv[%lex:1,2] sR */ + assert(!(o2->op_flags & ~(OPf_WANT|OPf_MOD|OPf_PARENS| + OPf_REF|OPf_SPECIAL))); + if ((o2->op_flags & + (OPf_WANT|OPf_REF|OPf_MOD|OPf_SPECIAL)) + != (OPf_WANT_SCALAR|OPf_REF)) + break; + if (o2->op_flags != (OPf_WANT_SCALAR|OPf_REF)) + break; + /* OPf_PARENS isn't currently used in this case; + * if that changes, let us know! */ + assert(!(o2->op_flags & OPf_PARENS)); + + /* at this point, we wouldn't expect any of the remaining + * possible private flags: + * OPpPAD_STATE, OPpLVAL_INTRO, OPpTRUEBOOL, + * OPpMAYBE_TRUEBOOL, OPpMAYBE_LVSUB + * + * OPpSLICEWARNING shouldn't affect runtime + */ + assert(!(o2->op_private & ~(OPpSLICEWARNING))); + + action = o2->op_type == OP_PADAV + ? MDEREF_AV_padav_aelem + : MDEREF_HV_padhv_helem; + o2 = o2->op_next; + S_maybe_multideref(aTHX_ o, o2, action, 0); + break; + + + case OP_RV2AV: + case OP_RV2HV: + action = o2->op_type == OP_RV2AV + ? MDEREF_AV_pop_rv2av_aelem + : MDEREF_HV_pop_rv2hv_helem; + /* FALLTHROUGH */ + do_deref: + /* (expr)->[...]: rv2av sKR/1; + * (expr)->{...}: rv2hv sKR/1; */ + + assert(o2->op_type == OP_RV2AV || o2->op_type == OP_RV2HV); + + assert(!(o2->op_flags & ~(OPf_WANT|OPf_KIDS|OPf_PARENS + |OPf_REF|OPf_MOD|OPf_STACKED|OPf_SPECIAL))); + if (o2->op_flags != (OPf_WANT_SCALAR|OPf_KIDS|OPf_REF)) + break; + + /* at this point, we wouldn't expect any of these + * possible private flags: + * OPpMAYBE_LVSUB, OPpOUR_INTRO, OPpLVAL_INTRO + * OPpTRUEBOOL, OPpMAYBE_TRUEBOOL, (rv2hv only) + */ + assert(!(o2->op_private & + ~(OPpHINT_STRICT_REFS|OPpARG1_MASK|OPpSLICEWARNING))); + hints |= (o2->op_private & OPpHINT_STRICT_REFS); + + o2 = o2->op_next; + + S_maybe_multideref(aTHX_ o, o2, action, hints); + break; + + default: + break; + } + } + switch (o->op_type) { case OP_DBSTATE: @@ -124,9 +124,10 @@ Deprecated. Use C<GIMME_V> instead. /* On OP_SMARTMATCH, an implicit smartmatch */ /* On OP_ANONHASH and OP_ANONLIST, create a reference to the new anon hash or array */ - /* On OP_HELEM and OP_HSLICE, localization will be followed - by assignment, so do not wipe the target if it is special - (e.g. a glob or a magic SV) */ + /* On OP_HELEM, OP_MULTIDEREF and OP_HSLICE, + localization will be followed by assignment, + so do not wipe the target if it is special + (e.g. a glob or a magic SV) */ /* On OP_MATCH, OP_SUBST & OP_TRANS, the operand of a logical or conditional that was optimised away, so it should @@ -177,6 +178,14 @@ typedef union { UV uv; } UNOP_AUX_item; +#ifdef USE_ITHREADS +# define UNOP_AUX_item_sv(item) PAD_SVl((item)->pad_offset); +#else +# define UNOP_AUX_item_sv(item) ((item)->sv); +#endif + + + struct op { BASEOP @@ -988,6 +997,47 @@ Sets the sibling of o to sib # define OP_CHECK_MUTEX_TERM NOOP #endif + +/* Stuff for OP_MULTDEREF/pp_multideref. */ + +/* actions */ + +/* Load another word of actions/flag bits. Must be 0 */ +#define MDEREF_reload 0 + +#define MDEREF_AV_pop_rv2av_aelem 1 +#define MDEREF_AV_gvsv_vivify_rv2av_aelem 2 +#define MDEREF_AV_padsv_vivify_rv2av_aelem 3 +#define MDEREF_AV_vivify_rv2av_aelem 4 +#define MDEREF_AV_padav_aelem 5 +#define MDEREF_AV_gvav_aelem 6 + +#define MDEREF_HV_pop_rv2hv_helem 8 +#define MDEREF_HV_gvsv_vivify_rv2hv_helem 9 +#define MDEREF_HV_padsv_vivify_rv2hv_helem 10 +#define MDEREF_HV_vivify_rv2hv_helem 11 +#define MDEREF_HV_padhv_helem 12 +#define MDEREF_HV_gvhv_helem 13 + +#define MDEREF_ACTION_MASK 0xf + +/* key / index type */ + +#define MDEREF_INDEX_none 0x00 /* run external ops to generate index */ +#define MDEREF_INDEX_const 0x10 /* index is const PV/UV */ +#define MDEREF_INDEX_padsv 0x20 /* index is lexical var */ +#define MDEREF_INDEX_gvsv 0x30 /* index is GV */ + +#define MDEREF_INDEX_MASK 0x30 + +/* bit flags */ + +#define MDEREF_FLAG_last 0x40 /* the last [ah]elem; PL_op flags apply */ + +#define MDEREF_MASK 0x7F +#define MDEREF_SHIFT 7 + + /* * Local variables: * c-indentation-style: bsd @@ -293,6 +293,7 @@ EXTCONST char* const PL_op_name[] = { "helem", "hslice", "kvhslice", + "multideref", "unpack", "pack", "split", @@ -687,6 +688,7 @@ EXTCONST char* const PL_op_desc[] = { "hash element", "hash slice", "key/value hash slice", + "array or hash lookup", "unpack", "pack", "split", @@ -1095,6 +1097,7 @@ EXT Perl_ppaddr_t PL_ppaddr[] /* or perlvars.h */ Perl_pp_helem, Perl_pp_hslice, Perl_pp_kvhslice, + Perl_pp_multideref, Perl_pp_unpack, Perl_pp_pack, Perl_pp_split, @@ -1499,6 +1502,7 @@ EXT Perl_check_t PL_check[] /* or perlvars.h */ Perl_ck_null, /* helem */ Perl_ck_null, /* hslice */ Perl_ck_null, /* kvhslice */ + Perl_ck_null, /* multideref */ Perl_ck_fun, /* unpack */ Perl_ck_fun, /* pack */ Perl_ck_split, /* split */ @@ -1897,6 +1901,7 @@ EXTCONST U32 PL_opargs[] = { 0x00014204, /* helem */ 0x00024401, /* hslice */ 0x00024401, /* kvhslice */ + 0x00000f44, /* multideref */ 0x00091480, /* unpack */ 0x0002140f, /* pack */ 0x00111418, /* split */ @@ -2190,6 +2195,7 @@ END_EXTERN_C #define OPpFT_AFTER_t 0x10 #define OPpLVREF_AV 0x10 #define OPpMAYBE_TRUEBOOL 0x10 +#define OPpMULTIDEREF_EXISTS 0x10 #define OPpOPEN_IN_RAW 0x10 #define OPpSORT_DESCEND 0x10 #define OPpSUBSTR_REPL_FIRST 0x10 @@ -2200,6 +2206,7 @@ END_EXTERN_C #define OPpHUSH_VMSISH 0x20 #define OPpLVREF_HV 0x20 #define OPpMAY_RETURN_CONSTANT 0x20 +#define OPpMULTIDEREF_DELETE 0x20 #define OPpOPEN_IN_CRLF 0x20 #define OPpSORT_QSORT 0x20 #define OPpTRANS_COMPLEMENT 0x20 @@ -2282,6 +2289,7 @@ EXTCONST char PL_op_private_labels[] = { 'D','B','G','\0', 'D','E','F','\0', 'D','E','L','\0', + 'D','E','L','E','T','E','\0', 'D','E','R','E','F','1','\0', 'D','E','R','E','F','2','\0', 'D','E','S','C','\0', @@ -2292,6 +2300,7 @@ EXTCONST char PL_op_private_labels[] = { 'E','A','R','L','Y','C','V','\0', 'E','L','E','M','\0', 'E','N','T','E','R','E','D','\0', + 'E','X','I','S','T','S','\0', 'F','A','K','E','\0', 'F','T','A','C','C','E','S','S','\0', 'F','T','A','F','T','E','R','t','\0', @@ -2366,8 +2375,8 @@ EXTCONST I16 PL_op_private_bitfields[] = { 0, 8, -1, 0, 8, -1, 0, 8, -1, - 4, -1, 1, 130, 2, 137, 3, 144, -1, - 4, -1, 0, 481, 1, 26, 2, 250, 3, 83, -1, + 4, -1, 1, 137, 2, 144, 3, 151, -1, + 4, -1, 0, 495, 1, 26, 2, 264, 3, 83, -1, }; @@ -2521,11 +2530,12 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 94, /* helem */ 99, /* hslice */ 102, /* kvhslice */ + 116, /* multideref */ 48, /* unpack */ 48, /* pack */ - 116, /* split */ + 123, /* split */ 48, /* join */ - 119, /* list */ + 126, /* list */ 12, /* lslice */ 48, /* anonlist */ 48, /* anonhash */ @@ -2534,48 +2544,48 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* pop */ 0, /* shift */ 79, /* unshift */ - 121, /* sort */ - 128, /* reverse */ - 130, /* grepstart */ - 131, /* grepwhile */ - 130, /* mapstart */ - 131, /* mapwhile */ + 128, /* sort */ + 135, /* reverse */ + 137, /* grepstart */ + 138, /* grepwhile */ + 137, /* mapstart */ + 138, /* mapwhile */ 0, /* range */ - 133, /* flip */ - 133, /* flop */ + 140, /* flip */ + 140, /* flop */ 0, /* and */ 0, /* or */ 12, /* xor */ 0, /* dor */ - 135, /* cond_expr */ + 142, /* cond_expr */ 0, /* andassign */ 0, /* orassign */ 0, /* dorassign */ 0, /* method */ - 137, /* entersub */ - 144, /* leavesub */ - 144, /* leavesublv */ - 146, /* caller */ + 144, /* entersub */ + 151, /* leavesub */ + 151, /* leavesublv */ + 153, /* caller */ 48, /* warn */ 48, /* die */ 48, /* reset */ -1, /* lineseq */ - 148, /* nextstate */ - 148, /* dbstate */ + 155, /* nextstate */ + 155, /* dbstate */ -1, /* unstack */ -1, /* enter */ - 149, /* leave */ + 156, /* leave */ -1, /* scope */ - 151, /* enteriter */ - 155, /* iter */ + 158, /* enteriter */ + 162, /* iter */ -1, /* enterloop */ - 156, /* leaveloop */ + 163, /* leaveloop */ -1, /* return */ - 158, /* last */ - 158, /* next */ - 158, /* redo */ - 158, /* dump */ - 158, /* goto */ + 165, /* last */ + 165, /* next */ + 165, /* redo */ + 165, /* dump */ + 165, /* goto */ 48, /* exit */ 0, /* method_named */ 0, /* method_super */ @@ -2587,7 +2597,7 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* leavewhen */ -1, /* break */ -1, /* continue */ - 160, /* open */ + 167, /* open */ 48, /* close */ 48, /* pipe_op */ 48, /* fileno */ @@ -2603,7 +2613,7 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 48, /* getc */ 48, /* read */ 48, /* enterwrite */ - 144, /* leavewrite */ + 151, /* leavewrite */ -1, /* prtf */ -1, /* print */ -1, /* say */ @@ -2633,33 +2643,33 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* getpeername */ 0, /* lstat */ 0, /* stat */ - 165, /* ftrread */ - 165, /* ftrwrite */ - 165, /* ftrexec */ - 165, /* fteread */ - 165, /* ftewrite */ - 165, /* fteexec */ - 170, /* ftis */ - 170, /* ftsize */ - 170, /* ftmtime */ - 170, /* ftatime */ - 170, /* ftctime */ - 170, /* ftrowned */ - 170, /* fteowned */ - 170, /* ftzero */ - 170, /* ftsock */ - 170, /* ftchr */ - 170, /* ftblk */ - 170, /* ftfile */ - 170, /* ftdir */ - 170, /* ftpipe */ - 170, /* ftsuid */ - 170, /* ftsgid */ - 170, /* ftsvtx */ - 170, /* ftlink */ - 170, /* fttty */ - 170, /* fttext */ - 170, /* ftbinary */ + 172, /* ftrread */ + 172, /* ftrwrite */ + 172, /* ftrexec */ + 172, /* fteread */ + 172, /* ftewrite */ + 172, /* fteexec */ + 177, /* ftis */ + 177, /* ftsize */ + 177, /* ftmtime */ + 177, /* ftatime */ + 177, /* ftctime */ + 177, /* ftrowned */ + 177, /* fteowned */ + 177, /* ftzero */ + 177, /* ftsock */ + 177, /* ftchr */ + 177, /* ftblk */ + 177, /* ftfile */ + 177, /* ftdir */ + 177, /* ftpipe */ + 177, /* ftsuid */ + 177, /* ftsgid */ + 177, /* ftsvtx */ + 177, /* ftlink */ + 177, /* fttty */ + 177, /* fttext */ + 177, /* ftbinary */ 79, /* chdir */ 79, /* chown */ 72, /* chroot */ @@ -2679,17 +2689,17 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* rewinddir */ 0, /* closedir */ -1, /* fork */ - 174, /* wait */ + 181, /* wait */ 79, /* waitpid */ 79, /* system */ 79, /* exec */ 79, /* kill */ - 174, /* getppid */ + 181, /* getppid */ 79, /* getpgrp */ 79, /* setpgrp */ 79, /* getpriority */ 79, /* setpriority */ - 174, /* time */ + 181, /* time */ -1, /* tms */ 0, /* localtime */ 48, /* gmtime */ @@ -2709,8 +2719,8 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* require */ 0, /* dofile */ -1, /* hintseval */ - 175, /* entereval */ - 144, /* leaveeval */ + 182, /* entereval */ + 151, /* leaveeval */ 0, /* entertry */ -1, /* leavetry */ 0, /* ghbyname */ @@ -2751,17 +2761,17 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { 0, /* reach */ 39, /* rkeys */ 0, /* rvalues */ - 181, /* coreargs */ + 188, /* coreargs */ 3, /* runcv */ 0, /* fc */ -1, /* padcv */ -1, /* introcv */ -1, /* clonecv */ - 185, /* padrange */ - 187, /* refassign */ - 193, /* lvref */ - 199, /* lvrefslice */ - 200, /* lvavref */ + 192, /* padrange */ + 194, /* refassign */ + 200, /* lvref */ + 206, /* lvrefslice */ + 207, /* lvavref */ }; @@ -2781,69 +2791,70 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { EXTCONST U16 PL_op_private_bitdefs[] = { 0x0003, /* scalar, prototype, refgen, srefgen, ref, readline, regcmaybe, regcreset, regcomp, chop, schop, defined, undef, study, preinc, i_preinc, predec, i_predec, postinc, i_postinc, postdec, i_postdec, negate, i_negate, not, complement, ucfirst, lcfirst, uc, lc, quotemeta, aeach, akeys, avalues, each, values, pop, shift, range, and, or, dor, andassign, orassign, dorassign, method, method_named, method_super, method_redir, method_redir_super, entergiven, leavegiven, enterwhen, leavewhen, untie, tied, dbmclose, getsockname, getpeername, lstat, stat, readlink, readdir, telldir, rewinddir, closedir, localtime, alarm, require, dofile, entertry, ghbyname, gnbyname, gpbyname, shostent, snetent, sprotoent, sservent, gpwnam, gpwuid, ggrnam, ggrgid, lock, once, reach, rvalues, fc */ - 0x281c, 0x3a19, /* pushmark */ + 0x29dc, 0x3bd9, /* pushmark */ 0x00bd, /* wantarray, runcv */ - 0x03b8, 0x1490, 0x3acc, 0x3588, 0x2be5, /* const */ - 0x281c, 0x2d39, /* gvsv */ - 0x12f5, /* gv */ + 0x03b8, 0x1570, 0x3c8c, 0x3748, 0x2da5, /* const */ + 0x29dc, 0x2ef9, /* gvsv */ + 0x13d5, /* gv */ 0x0067, /* gelem, lt, i_lt, gt, i_gt, le, i_le, ge, i_ge, eq, i_eq, ne, i_ne, ncmp, i_ncmp, slt, sgt, sle, sge, seq, sne, scmp, bit_and, bit_xor, bit_or, smartmatch, lslice, xor */ - 0x281c, 0x3a18, 0x0257, /* padsv */ - 0x281c, 0x3a18, 0x290c, 0x3709, /* padav */ - 0x281c, 0x3a18, 0x0534, 0x05d0, 0x290c, 0x3709, /* padhv */ - 0x34d9, /* pushre, qr */ - 0x281c, 0x1598, 0x0256, 0x290c, 0x2b08, 0x3ac4, 0x0003, /* rv2gv */ - 0x281c, 0x2d38, 0x0256, 0x3ac4, 0x0003, /* rv2sv */ - 0x290c, 0x0003, /* av2arylen, pos, keys, rkeys */ - 0x2a7c, 0x0b98, 0x08f4, 0x028c, 0x3c88, 0x3ac4, 0x0003, /* rv2cv */ + 0x29dc, 0x3bd8, 0x0257, /* padsv */ + 0x29dc, 0x3bd8, 0x2acc, 0x38c9, /* padav */ + 0x29dc, 0x3bd8, 0x0534, 0x05d0, 0x2acc, 0x38c9, /* padhv */ + 0x3699, /* pushre, qr */ + 0x29dc, 0x1758, 0x0256, 0x2acc, 0x2cc8, 0x3c84, 0x0003, /* rv2gv */ + 0x29dc, 0x2ef8, 0x0256, 0x3c84, 0x0003, /* rv2sv */ + 0x2acc, 0x0003, /* av2arylen, pos, keys, rkeys */ + 0x2c3c, 0x0b98, 0x08f4, 0x028c, 0x3e48, 0x3c84, 0x0003, /* rv2cv */ 0x012f, /* bless, glob, sprintf, formline, unpack, pack, join, anonlist, anonhash, splice, warn, die, reset, exit, close, pipe_op, fileno, umask, binmode, tie, dbmopen, sselect, select, getc, read, enterwrite, sysopen, sysseek, sysread, syswrite, eof, tell, seek, truncate, fcntl, ioctl, send, recv, socket, sockpair, bind, connect, listen, accept, shutdown, gsockopt, ssockopt, open_dir, seekdir, gmtime, shmget, shmctl, shmread, shmwrite, msgget, msgctl, msgsnd, msgrcv, semop, semget, semctl, ghbyaddr, gnbyaddr, gpbynumber, gsbyname, gsbyport, syscall */ - 0x2f1c, 0x2e38, 0x22f4, 0x2230, 0x0003, /* backtick */ - 0x34d8, 0x3d31, /* match, subst */ - 0x34d8, 0x0003, /* substcont */ - 0x0c9c, 0x1c18, 0x0834, 0x3d30, 0x384c, 0x1fa8, 0x01e4, 0x0141, /* trans, transr */ + 0x30dc, 0x2ff8, 0x24b4, 0x23f0, 0x0003, /* backtick */ + 0x3698, 0x3ef1, /* match, subst */ + 0x3698, 0x0003, /* substcont */ + 0x0c9c, 0x1dd8, 0x0834, 0x3ef0, 0x3a0c, 0x2168, 0x01e4, 0x0141, /* trans, transr */ 0x0adc, 0x0458, 0x0067, /* sassign */ - 0x0758, 0x290c, 0x0067, /* aassign */ - 0x3d30, 0x0003, /* chomp, schomp, sin, cos, exp, log, sqrt, int, hex, oct, abs, length, ord, chr, chroot, rmdir */ - 0x3d30, 0x0067, /* pow, multiply, i_multiply, divide, i_divide, modulo, i_modulo, add, i_add, subtract, i_subtract, concat, left_shift, right_shift */ - 0x0f78, 0x3d30, 0x0067, /* repeat */ - 0x3d30, 0x012f, /* stringify, atan2, rand, srand, index, rindex, crypt, push, unshift, flock, chdir, chown, unlink, chmod, utime, rename, link, symlink, mkdir, waitpid, system, exec, kill, getpgrp, setpgrp, getpriority, setpriority, sleep */ - 0x3230, 0x290c, 0x00cb, /* substr */ - 0x3d30, 0x290c, 0x0067, /* vec */ - 0x281c, 0x2d38, 0x290c, 0x3708, 0x3ac4, 0x0003, /* rv2av */ + 0x0758, 0x2acc, 0x0067, /* aassign */ + 0x3ef0, 0x0003, /* chomp, schomp, sin, cos, exp, log, sqrt, int, hex, oct, abs, length, ord, chr, chroot, rmdir */ + 0x3ef0, 0x0067, /* pow, multiply, i_multiply, divide, i_divide, modulo, i_modulo, add, i_add, subtract, i_subtract, concat, left_shift, right_shift */ + 0x1058, 0x3ef0, 0x0067, /* repeat */ + 0x3ef0, 0x012f, /* stringify, atan2, rand, srand, index, rindex, crypt, push, unshift, flock, chdir, chown, unlink, chmod, utime, rename, link, symlink, mkdir, waitpid, system, exec, kill, getpgrp, setpgrp, getpriority, setpriority, sleep */ + 0x33f0, 0x2acc, 0x00cb, /* substr */ + 0x3ef0, 0x2acc, 0x0067, /* vec */ + 0x29dc, 0x2ef8, 0x2acc, 0x38c8, 0x3c84, 0x0003, /* rv2av */ 0x01ff, /* aelemfast, aelemfast_lex */ - 0x281c, 0x2718, 0x0256, 0x290c, 0x0067, /* aelem, helem */ - 0x281c, 0x290c, 0x3709, /* aslice, hslice */ - 0x290d, /* kvaslice, kvhslice */ - 0x281c, 0x3658, 0x0003, /* delete */ - 0x3bb8, 0x0003, /* exists */ - 0x281c, 0x2d38, 0x0534, 0x05d0, 0x290c, 0x3708, 0x3ac4, 0x0003, /* rv2hv */ - 0x207c, 0x2d38, 0x3d31, /* split */ - 0x281c, 0x1cd9, /* list */ - 0x3938, 0x2fd4, 0x0ed0, 0x238c, 0x3328, 0x2484, 0x2ca1, /* sort */ - 0x238c, 0x0003, /* reverse */ - 0x1b05, /* grepstart, mapstart */ - 0x1b04, 0x0003, /* grepwhile, mapwhile */ - 0x25b8, 0x0003, /* flip, flop */ - 0x281c, 0x0003, /* cond_expr */ - 0x281c, 0x0b98, 0x0256, 0x028c, 0x3c88, 0x3ac4, 0x2141, /* entersub */ - 0x3098, 0x0003, /* leavesub, leavesublv, leavewrite, leaveeval */ + 0x29dc, 0x28d8, 0x0256, 0x2acc, 0x0067, /* aelem, helem */ + 0x29dc, 0x2acc, 0x38c9, /* aslice, hslice */ + 0x2acd, /* kvaslice, kvhslice */ + 0x29dc, 0x3818, 0x0003, /* delete */ + 0x3d78, 0x0003, /* exists */ + 0x29dc, 0x2ef8, 0x0534, 0x05d0, 0x2acc, 0x38c8, 0x3c84, 0x0003, /* rv2hv */ + 0x29dc, 0x28d8, 0x0d14, 0x1670, 0x2acc, 0x3c84, 0x0003, /* multideref */ + 0x223c, 0x2ef8, 0x3ef1, /* split */ + 0x29dc, 0x1e99, /* list */ + 0x3af8, 0x3194, 0x0fb0, 0x254c, 0x34e8, 0x2644, 0x2e61, /* sort */ + 0x254c, 0x0003, /* reverse */ + 0x1cc5, /* grepstart, mapstart */ + 0x1cc4, 0x0003, /* grepwhile, mapwhile */ + 0x2778, 0x0003, /* flip, flop */ + 0x29dc, 0x0003, /* cond_expr */ + 0x29dc, 0x0b98, 0x0256, 0x028c, 0x3e48, 0x3c84, 0x2301, /* entersub */ + 0x3258, 0x0003, /* leavesub, leavesublv, leavewrite, leaveeval */ 0x00bc, 0x012f, /* caller */ - 0x1eb5, /* nextstate, dbstate */ - 0x26bc, 0x3099, /* leave */ - 0x281c, 0x2d38, 0x0c0c, 0x33a9, /* enteriter */ - 0x33a9, /* iter */ - 0x26bc, 0x0067, /* leaveloop */ - 0x3e9c, 0x0003, /* last, next, redo, dump, goto */ - 0x2f1c, 0x2e38, 0x22f4, 0x2230, 0x012f, /* open */ - 0x1750, 0x19ac, 0x1868, 0x1624, 0x0003, /* ftrread, ftrwrite, ftrexec, fteread, ftewrite, fteexec */ - 0x1750, 0x19ac, 0x1868, 0x0003, /* ftis, ftsize, ftmtime, ftatime, ftctime, ftrowned, fteowned, ftzero, ftsock, ftchr, ftblk, ftfile, ftdir, ftpipe, ftsuid, ftsgid, ftsvtx, ftlink, fttty, fttext, ftbinary */ - 0x3d31, /* wait, getppid, time */ - 0x3134, 0x09b0, 0x068c, 0x3e08, 0x1dc4, 0x0003, /* entereval */ - 0x29dc, 0x0018, 0x0de4, 0x0d01, /* coreargs */ - 0x281c, 0x019b, /* padrange */ - 0x281c, 0x3a18, 0x0376, 0x250c, 0x13e8, 0x0067, /* refassign */ - 0x281c, 0x3a18, 0x0376, 0x250c, 0x13e8, 0x0003, /* lvref */ - 0x281d, /* lvrefslice */ - 0x281c, 0x3a18, 0x0003, /* lvavref */ + 0x2075, /* nextstate, dbstate */ + 0x287c, 0x3259, /* leave */ + 0x29dc, 0x2ef8, 0x0c0c, 0x3569, /* enteriter */ + 0x3569, /* iter */ + 0x287c, 0x0067, /* leaveloop */ + 0x405c, 0x0003, /* last, next, redo, dump, goto */ + 0x30dc, 0x2ff8, 0x24b4, 0x23f0, 0x012f, /* open */ + 0x1910, 0x1b6c, 0x1a28, 0x17e4, 0x0003, /* ftrread, ftrwrite, ftrexec, fteread, ftewrite, fteexec */ + 0x1910, 0x1b6c, 0x1a28, 0x0003, /* ftis, ftsize, ftmtime, ftatime, ftctime, ftrowned, fteowned, ftzero, ftsock, ftchr, ftblk, ftfile, ftdir, ftpipe, ftsuid, ftsgid, ftsvtx, ftlink, fttty, fttext, ftbinary */ + 0x3ef1, /* wait, getppid, time */ + 0x32f4, 0x09b0, 0x068c, 0x3fc8, 0x1f84, 0x0003, /* entereval */ + 0x2b9c, 0x0018, 0x0ec4, 0x0de1, /* coreargs */ + 0x29dc, 0x019b, /* padrange */ + 0x29dc, 0x3bd8, 0x0376, 0x26cc, 0x14c8, 0x0067, /* refassign */ + 0x29dc, 0x3bd8, 0x0376, 0x26cc, 0x14c8, 0x0003, /* lvref */ + 0x29dd, /* lvrefslice */ + 0x29dc, 0x3bd8, 0x0003, /* lvavref */ }; @@ -2997,6 +3008,7 @@ EXTCONST U8 PL_op_private_valid[] = { /* HELEM */ (OPpARG2_MASK|OPpMAYBE_LVSUB|OPpDEREF|OPpLVAL_DEFER|OPpLVAL_INTRO), /* HSLICE */ (OPpSLICEWARNING|OPpMAYBE_LVSUB|OPpLVAL_INTRO), /* KVHSLICE */ (OPpMAYBE_LVSUB), + /* MULTIDEREF */ (OPpARG1_MASK|OPpHINT_STRICT_REFS|OPpMAYBE_LVSUB|OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE|OPpLVAL_DEFER|OPpLVAL_INTRO), /* UNPACK */ (OPpARG4_MASK), /* PACK */ (OPpARG4_MASK), /* SPLIT */ (OPpTARGET_MY|OPpOUR_INTRO|OPpSPLIT_IMPLIM), @@ -159,251 +159,252 @@ typedef enum opcode { OP_HELEM = 142, OP_HSLICE = 143, OP_KVHSLICE = 144, - OP_UNPACK = 145, - OP_PACK = 146, - OP_SPLIT = 147, - OP_JOIN = 148, - OP_LIST = 149, - OP_LSLICE = 150, - OP_ANONLIST = 151, - OP_ANONHASH = 152, - OP_SPLICE = 153, - OP_PUSH = 154, - OP_POP = 155, - OP_SHIFT = 156, - OP_UNSHIFT = 157, - OP_SORT = 158, - OP_REVERSE = 159, - OP_GREPSTART = 160, - OP_GREPWHILE = 161, - OP_MAPSTART = 162, - OP_MAPWHILE = 163, - OP_RANGE = 164, - OP_FLIP = 165, - OP_FLOP = 166, - OP_AND = 167, - OP_OR = 168, - OP_XOR = 169, - OP_DOR = 170, - OP_COND_EXPR = 171, - OP_ANDASSIGN = 172, - OP_ORASSIGN = 173, - OP_DORASSIGN = 174, - OP_METHOD = 175, - OP_ENTERSUB = 176, - OP_LEAVESUB = 177, - OP_LEAVESUBLV = 178, - OP_CALLER = 179, - OP_WARN = 180, - OP_DIE = 181, - OP_RESET = 182, - OP_LINESEQ = 183, - OP_NEXTSTATE = 184, - OP_DBSTATE = 185, - OP_UNSTACK = 186, - OP_ENTER = 187, - OP_LEAVE = 188, - OP_SCOPE = 189, - OP_ENTERITER = 190, - OP_ITER = 191, - OP_ENTERLOOP = 192, - OP_LEAVELOOP = 193, - OP_RETURN = 194, - OP_LAST = 195, - OP_NEXT = 196, - OP_REDO = 197, - OP_DUMP = 198, - OP_GOTO = 199, - OP_EXIT = 200, - OP_METHOD_NAMED = 201, - OP_METHOD_SUPER = 202, - OP_METHOD_REDIR = 203, - OP_METHOD_REDIR_SUPER = 204, - OP_ENTERGIVEN = 205, - OP_LEAVEGIVEN = 206, - OP_ENTERWHEN = 207, - OP_LEAVEWHEN = 208, - OP_BREAK = 209, - OP_CONTINUE = 210, - OP_OPEN = 211, - OP_CLOSE = 212, - OP_PIPE_OP = 213, - OP_FILENO = 214, - OP_UMASK = 215, - OP_BINMODE = 216, - OP_TIE = 217, - OP_UNTIE = 218, - OP_TIED = 219, - OP_DBMOPEN = 220, - OP_DBMCLOSE = 221, - OP_SSELECT = 222, - OP_SELECT = 223, - OP_GETC = 224, - OP_READ = 225, - OP_ENTERWRITE = 226, - OP_LEAVEWRITE = 227, - OP_PRTF = 228, - OP_PRINT = 229, - OP_SAY = 230, - OP_SYSOPEN = 231, - OP_SYSSEEK = 232, - OP_SYSREAD = 233, - OP_SYSWRITE = 234, - OP_EOF = 235, - OP_TELL = 236, - OP_SEEK = 237, - OP_TRUNCATE = 238, - OP_FCNTL = 239, - OP_IOCTL = 240, - OP_FLOCK = 241, - OP_SEND = 242, - OP_RECV = 243, - OP_SOCKET = 244, - OP_SOCKPAIR = 245, - OP_BIND = 246, - OP_CONNECT = 247, - OP_LISTEN = 248, - OP_ACCEPT = 249, - OP_SHUTDOWN = 250, - OP_GSOCKOPT = 251, - OP_SSOCKOPT = 252, - OP_GETSOCKNAME = 253, - OP_GETPEERNAME = 254, - OP_LSTAT = 255, - OP_STAT = 256, - OP_FTRREAD = 257, - OP_FTRWRITE = 258, - OP_FTREXEC = 259, - OP_FTEREAD = 260, - OP_FTEWRITE = 261, - OP_FTEEXEC = 262, - OP_FTIS = 263, - OP_FTSIZE = 264, - OP_FTMTIME = 265, - OP_FTATIME = 266, - OP_FTCTIME = 267, - OP_FTROWNED = 268, - OP_FTEOWNED = 269, - OP_FTZERO = 270, - OP_FTSOCK = 271, - OP_FTCHR = 272, - OP_FTBLK = 273, - OP_FTFILE = 274, - OP_FTDIR = 275, - OP_FTPIPE = 276, - OP_FTSUID = 277, - OP_FTSGID = 278, - OP_FTSVTX = 279, - OP_FTLINK = 280, - OP_FTTTY = 281, - OP_FTTEXT = 282, - OP_FTBINARY = 283, - OP_CHDIR = 284, - OP_CHOWN = 285, - OP_CHROOT = 286, - OP_UNLINK = 287, - OP_CHMOD = 288, - OP_UTIME = 289, - OP_RENAME = 290, - OP_LINK = 291, - OP_SYMLINK = 292, - OP_READLINK = 293, - OP_MKDIR = 294, - OP_RMDIR = 295, - OP_OPEN_DIR = 296, - OP_READDIR = 297, - OP_TELLDIR = 298, - OP_SEEKDIR = 299, - OP_REWINDDIR = 300, - OP_CLOSEDIR = 301, - OP_FORK = 302, - OP_WAIT = 303, - OP_WAITPID = 304, - OP_SYSTEM = 305, - OP_EXEC = 306, - OP_KILL = 307, - OP_GETPPID = 308, - OP_GETPGRP = 309, - OP_SETPGRP = 310, - OP_GETPRIORITY = 311, - OP_SETPRIORITY = 312, - OP_TIME = 313, - OP_TMS = 314, - OP_LOCALTIME = 315, - OP_GMTIME = 316, - OP_ALARM = 317, - OP_SLEEP = 318, - OP_SHMGET = 319, - OP_SHMCTL = 320, - OP_SHMREAD = 321, - OP_SHMWRITE = 322, - OP_MSGGET = 323, - OP_MSGCTL = 324, - OP_MSGSND = 325, - OP_MSGRCV = 326, - OP_SEMOP = 327, - OP_SEMGET = 328, - OP_SEMCTL = 329, - OP_REQUIRE = 330, - OP_DOFILE = 331, - OP_HINTSEVAL = 332, - OP_ENTEREVAL = 333, - OP_LEAVEEVAL = 334, - OP_ENTERTRY = 335, - OP_LEAVETRY = 336, - OP_GHBYNAME = 337, - OP_GHBYADDR = 338, - OP_GHOSTENT = 339, - OP_GNBYNAME = 340, - OP_GNBYADDR = 341, - OP_GNETENT = 342, - OP_GPBYNAME = 343, - OP_GPBYNUMBER = 344, - OP_GPROTOENT = 345, - OP_GSBYNAME = 346, - OP_GSBYPORT = 347, - OP_GSERVENT = 348, - OP_SHOSTENT = 349, - OP_SNETENT = 350, - OP_SPROTOENT = 351, - OP_SSERVENT = 352, - OP_EHOSTENT = 353, - OP_ENETENT = 354, - OP_EPROTOENT = 355, - OP_ESERVENT = 356, - OP_GPWNAM = 357, - OP_GPWUID = 358, - OP_GPWENT = 359, - OP_SPWENT = 360, - OP_EPWENT = 361, - OP_GGRNAM = 362, - OP_GGRGID = 363, - OP_GGRENT = 364, - OP_SGRENT = 365, - OP_EGRENT = 366, - OP_GETLOGIN = 367, - OP_SYSCALL = 368, - OP_LOCK = 369, - OP_ONCE = 370, - OP_CUSTOM = 371, - OP_REACH = 372, - OP_RKEYS = 373, - OP_RVALUES = 374, - OP_COREARGS = 375, - OP_RUNCV = 376, - OP_FC = 377, - OP_PADCV = 378, - OP_INTROCV = 379, - OP_CLONECV = 380, - OP_PADRANGE = 381, - OP_REFASSIGN = 382, - OP_LVREF = 383, - OP_LVREFSLICE = 384, - OP_LVAVREF = 385, + OP_MULTIDEREF = 145, + OP_UNPACK = 146, + OP_PACK = 147, + OP_SPLIT = 148, + OP_JOIN = 149, + OP_LIST = 150, + OP_LSLICE = 151, + OP_ANONLIST = 152, + OP_ANONHASH = 153, + OP_SPLICE = 154, + OP_PUSH = 155, + OP_POP = 156, + OP_SHIFT = 157, + OP_UNSHIFT = 158, + OP_SORT = 159, + OP_REVERSE = 160, + OP_GREPSTART = 161, + OP_GREPWHILE = 162, + OP_MAPSTART = 163, + OP_MAPWHILE = 164, + OP_RANGE = 165, + OP_FLIP = 166, + OP_FLOP = 167, + OP_AND = 168, + OP_OR = 169, + OP_XOR = 170, + OP_DOR = 171, + OP_COND_EXPR = 172, + OP_ANDASSIGN = 173, + OP_ORASSIGN = 174, + OP_DORASSIGN = 175, + OP_METHOD = 176, + OP_ENTERSUB = 177, + OP_LEAVESUB = 178, + OP_LEAVESUBLV = 179, + OP_CALLER = 180, + OP_WARN = 181, + OP_DIE = 182, + OP_RESET = 183, + OP_LINESEQ = 184, + OP_NEXTSTATE = 185, + OP_DBSTATE = 186, + OP_UNSTACK = 187, + OP_ENTER = 188, + OP_LEAVE = 189, + OP_SCOPE = 190, + OP_ENTERITER = 191, + OP_ITER = 192, + OP_ENTERLOOP = 193, + OP_LEAVELOOP = 194, + OP_RETURN = 195, + OP_LAST = 196, + OP_NEXT = 197, + OP_REDO = 198, + OP_DUMP = 199, + OP_GOTO = 200, + OP_EXIT = 201, + OP_METHOD_NAMED = 202, + OP_METHOD_SUPER = 203, + OP_METHOD_REDIR = 204, + OP_METHOD_REDIR_SUPER = 205, + OP_ENTERGIVEN = 206, + OP_LEAVEGIVEN = 207, + OP_ENTERWHEN = 208, + OP_LEAVEWHEN = 209, + OP_BREAK = 210, + OP_CONTINUE = 211, + OP_OPEN = 212, + OP_CLOSE = 213, + OP_PIPE_OP = 214, + OP_FILENO = 215, + OP_UMASK = 216, + OP_BINMODE = 217, + OP_TIE = 218, + OP_UNTIE = 219, + OP_TIED = 220, + OP_DBMOPEN = 221, + OP_DBMCLOSE = 222, + OP_SSELECT = 223, + OP_SELECT = 224, + OP_GETC = 225, + OP_READ = 226, + OP_ENTERWRITE = 227, + OP_LEAVEWRITE = 228, + OP_PRTF = 229, + OP_PRINT = 230, + OP_SAY = 231, + OP_SYSOPEN = 232, + OP_SYSSEEK = 233, + OP_SYSREAD = 234, + OP_SYSWRITE = 235, + OP_EOF = 236, + OP_TELL = 237, + OP_SEEK = 238, + OP_TRUNCATE = 239, + OP_FCNTL = 240, + OP_IOCTL = 241, + OP_FLOCK = 242, + OP_SEND = 243, + OP_RECV = 244, + OP_SOCKET = 245, + OP_SOCKPAIR = 246, + OP_BIND = 247, + OP_CONNECT = 248, + OP_LISTEN = 249, + OP_ACCEPT = 250, + OP_SHUTDOWN = 251, + OP_GSOCKOPT = 252, + OP_SSOCKOPT = 253, + OP_GETSOCKNAME = 254, + OP_GETPEERNAME = 255, + OP_LSTAT = 256, + OP_STAT = 257, + OP_FTRREAD = 258, + OP_FTRWRITE = 259, + OP_FTREXEC = 260, + OP_FTEREAD = 261, + OP_FTEWRITE = 262, + OP_FTEEXEC = 263, + OP_FTIS = 264, + OP_FTSIZE = 265, + OP_FTMTIME = 266, + OP_FTATIME = 267, + OP_FTCTIME = 268, + OP_FTROWNED = 269, + OP_FTEOWNED = 270, + OP_FTZERO = 271, + OP_FTSOCK = 272, + OP_FTCHR = 273, + OP_FTBLK = 274, + OP_FTFILE = 275, + OP_FTDIR = 276, + OP_FTPIPE = 277, + OP_FTSUID = 278, + OP_FTSGID = 279, + OP_FTSVTX = 280, + OP_FTLINK = 281, + OP_FTTTY = 282, + OP_FTTEXT = 283, + OP_FTBINARY = 284, + OP_CHDIR = 285, + OP_CHOWN = 286, + OP_CHROOT = 287, + OP_UNLINK = 288, + OP_CHMOD = 289, + OP_UTIME = 290, + OP_RENAME = 291, + OP_LINK = 292, + OP_SYMLINK = 293, + OP_READLINK = 294, + OP_MKDIR = 295, + OP_RMDIR = 296, + OP_OPEN_DIR = 297, + OP_READDIR = 298, + OP_TELLDIR = 299, + OP_SEEKDIR = 300, + OP_REWINDDIR = 301, + OP_CLOSEDIR = 302, + OP_FORK = 303, + OP_WAIT = 304, + OP_WAITPID = 305, + OP_SYSTEM = 306, + OP_EXEC = 307, + OP_KILL = 308, + OP_GETPPID = 309, + OP_GETPGRP = 310, + OP_SETPGRP = 311, + OP_GETPRIORITY = 312, + OP_SETPRIORITY = 313, + OP_TIME = 314, + OP_TMS = 315, + OP_LOCALTIME = 316, + OP_GMTIME = 317, + OP_ALARM = 318, + OP_SLEEP = 319, + OP_SHMGET = 320, + OP_SHMCTL = 321, + OP_SHMREAD = 322, + OP_SHMWRITE = 323, + OP_MSGGET = 324, + OP_MSGCTL = 325, + OP_MSGSND = 326, + OP_MSGRCV = 327, + OP_SEMOP = 328, + OP_SEMGET = 329, + OP_SEMCTL = 330, + OP_REQUIRE = 331, + OP_DOFILE = 332, + OP_HINTSEVAL = 333, + OP_ENTEREVAL = 334, + OP_LEAVEEVAL = 335, + OP_ENTERTRY = 336, + OP_LEAVETRY = 337, + OP_GHBYNAME = 338, + OP_GHBYADDR = 339, + OP_GHOSTENT = 340, + OP_GNBYNAME = 341, + OP_GNBYADDR = 342, + OP_GNETENT = 343, + OP_GPBYNAME = 344, + OP_GPBYNUMBER = 345, + OP_GPROTOENT = 346, + OP_GSBYNAME = 347, + OP_GSBYPORT = 348, + OP_GSERVENT = 349, + OP_SHOSTENT = 350, + OP_SNETENT = 351, + OP_SPROTOENT = 352, + OP_SSERVENT = 353, + OP_EHOSTENT = 354, + OP_ENETENT = 355, + OP_EPROTOENT = 356, + OP_ESERVENT = 357, + OP_GPWNAM = 358, + OP_GPWUID = 359, + OP_GPWENT = 360, + OP_SPWENT = 361, + OP_EPWENT = 362, + OP_GGRNAM = 363, + OP_GGRGID = 364, + OP_GGRENT = 365, + OP_SGRENT = 366, + OP_EGRENT = 367, + OP_GETLOGIN = 368, + OP_SYSCALL = 369, + OP_LOCK = 370, + OP_ONCE = 371, + OP_CUSTOM = 372, + OP_REACH = 373, + OP_RKEYS = 374, + OP_RVALUES = 375, + OP_COREARGS = 376, + OP_RUNCV = 377, + OP_FC = 378, + OP_PADCV = 379, + OP_INTROCV = 380, + OP_CLONECV = 381, + OP_PADRANGE = 382, + OP_REFASSIGN = 383, + OP_LVREF = 384, + OP_LVREFSLICE = 385, + OP_LVAVREF = 386, OP_max } opcode; -#define MAXO 386 +#define MAXO 387 #define OP_FREED MAXO /* the OP_IS_* macros are optimized to a simple range check because @@ -4605,12 +4605,13 @@ EXTCONST char PL_warn_nl[] INIT("Unsuccessful %s on filename containing newline"); EXTCONST char PL_no_wrongref[] INIT("Can't use %s ref as %s ref"); -/* The core no longer needs these here. If you require the string constant, +/* The core no longer needs this here. If you require the string constant, please inline a copy into your own code. */ EXTCONST char PL_no_symref[] __attribute__deprecated__ INIT("Can't use string (\"%.32s\") as %s ref while \"strict refs\" in use"); -EXTCONST char PL_no_symref_sv[] __attribute__deprecated__ - INIT("Can't use string (\"%" SVf32 "\") as %s ref while \"strict refs\" in use"); +EXTCONST char PL_no_symref_sv[] + INIT("Can't use string (\"%" SVf32 "\"%s) as %s ref while \"strict refs\" in use"); + EXTCONST char PL_no_usym[] INIT("Can't use an undefined value as %s reference"); EXTCONST char PL_no_aelem[] @@ -195,9 +195,6 @@ PP(pp_clonecv) /* Translations. */ -static const char S_no_symref_sv[] = - "Can't use string (\"%" SVf32 "\"%s) as %s ref while \"strict refs\" in use"; - /* In some cases this function inspects PL_op. If this function is called for new op types, more bool parameters may need to be added in place of the checks. @@ -274,7 +271,7 @@ S_rv2gv(pTHX_ SV *sv, const bool vivify_sv, const bool strict, else { if (strict) { Perl_die(aTHX_ - S_no_symref_sv, + PL_no_symref_sv, sv, (SvPOKp(sv) && SvCUR(sv)>32 ? "..." : ""), "a symbol" @@ -329,7 +326,7 @@ Perl_softref2xv(pTHX_ SV *const sv, const char *const what, if (PL_op->op_private & HINT_STRICT_REFS) { if (SvOK(sv)) - Perl_die(aTHX_ S_no_symref_sv, sv, + Perl_die(aTHX_ PL_no_symref_sv, sv, (SvPOKp(sv) && SvCUR(sv)>32 ? "..." : ""), what); else Perl_die(aTHX_ PL_no_usym, what); @@ -1857,6 +1857,442 @@ PP(pp_helem) RETURN; } + +/* a stripped-down version of Perl_softref2xv() for use by + * pp_multideref(), which doesn't use PL_op->op_flags */ + +GV * +S_softref2xv_lite(pTHX_ SV *const sv, const char *const what, + const svtype type) +{ + if (PL_op->op_private & HINT_STRICT_REFS) { + if (SvOK(sv)) + Perl_die(aTHX_ PL_no_symref_sv, sv, + (SvPOKp(sv) && SvCUR(sv)>32 ? "..." : ""), what); + else + Perl_die(aTHX_ PL_no_usym, what); + } + if (!SvOK(sv)) + Perl_die(aTHX_ PL_no_usym, what); + return gv_fetchsv_nomg(sv, GV_ADD, type); +} + + +/* handle one or more derefs and array/hash indexings, e.g. + * $h->{foo} or $a[0]{$key}[$i] or f()->[1] + * + * op_aux points to an array of unions of UV / IV / SV* / PADOFFSET. + * Each of these either contains an action, or an argument, such as + * a UV to use as an array index, or a lexical var to retrieve. + * In fact, several actions re stored per UV; we keep shifting new actions + * of the one UV, and only reload when it becomes zero. + */ + +PP(pp_multideref) +{ + SV *sv = NULL; /* init to avoid spurious 'may be used uninitialized' */ + UNOP_AUX_item *items = cUNOP_AUXx(PL_op)->op_aux; + UV actions = items->uv; + + assert(actions); + /* this tells find_uninit_var() where we're up to */ + PL_multideref_pc = items; + + while (1) { + /* there are three main classes of action; the first retrieve + * the initial AV or HV from a variable or the stack; the second + * does the equivalent of an unrolled (/DREFAV, rv2av, aelem), + * the third an unrolled (/DREFHV, rv2hv, helem). + */ + switch (actions & MDEREF_ACTION_MASK) { + + case MDEREF_reload: + actions = (++items)->uv; + continue; + + case MDEREF_AV_padav_aelem: /* $lex[...] */ + sv = PAD_SVl((++items)->pad_offset); + goto do_AV_aelem; + + case MDEREF_AV_gvav_aelem: /* $pkg[...] */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = (SV*)GvAVn((GV*)sv); + goto do_AV_aelem; + + case MDEREF_AV_pop_rv2av_aelem: /* expr->[...] */ + { + dSP; + sv = POPs; + PUTBACK; + goto do_AV_rv2av_aelem; + } + + case MDEREF_AV_gvsv_vivify_rv2av_aelem: /* $pkg->[...] */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = GvSVn((GV*)sv); + goto do_AV_vivify_rv2av_aelem; + + case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */ + sv = PAD_SVl((++items)->pad_offset); + /* FALLTHROUGH */ + + do_AV_vivify_rv2av_aelem: + case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + /* this is the OPpDEREF action normally found at the end of + * ops like aelem, helem, rv2sv */ + sv = vivify_ref(sv, OPpDEREF_AV); + /* FALLTHROUGH */ + + do_AV_rv2av_aelem: + /* this is basically a copy of pp_rv2av when it just has the + * sKR/1 flags */ + SvGETMAGIC(sv); + if (LIKELY(SvROK(sv))) { + if (UNLIKELY(SvAMAGIC(sv))) { + sv = amagic_deref_call(sv, to_av_amg); + } + sv = SvRV(sv); + if (UNLIKELY(SvTYPE(sv) != SVt_PVAV)) + DIE(aTHX_ "Not an ARRAY reference"); + } + else if (SvTYPE(sv) != SVt_PVAV) { + if (!isGV_with_GP(sv)) + sv = (SV*)S_softref2xv_lite(aTHX_ sv, "an ARRAY", SVt_PVAV); + sv = MUTABLE_SV(GvAVn((GV*)sv)); + } + /* FALLTHROUGH */ + + do_AV_aelem: + { + /* retrieve the key; this may be either a lexical or package + * var (whose index/ptr is stored as an item) or a signed + * integer constant stored as an item. + */ + SV *elemsv; + IV elem = 0; /* to shut up stupid compiler warnings */ + + + assert(SvTYPE(sv) == SVt_PVAV); + + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + case MDEREF_INDEX_const: + elem = (++items)->iv; + break; + case MDEREF_INDEX_padsv: + elemsv = PAD_SVl((++items)->pad_offset); + goto check_elem; + case MDEREF_INDEX_gvsv: + elemsv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(elemsv)); + elemsv = GvSVn((GV*)elemsv); + check_elem: + if (UNLIKELY(SvROK(elemsv) && !SvGAMAGIC(elemsv) + && ckWARN(WARN_MISC))) + Perl_warner(aTHX_ packWARN(WARN_MISC), + "Use of reference \"%"SVf"\" as array index", + SVfARG(elemsv)); + /* the only time that S_find_uninit_var() needs this + * is to determine which index value triggered the + * undef warning. So just update it here. Note that + * since we don't save and restore this var (e.g. for + * tie or overload execution), its value will be + * meaningless apart from just here */ + PL_multideref_pc = items; + elem = SvIV(elemsv); + break; + } + + + /* this is basically a copy of pp_aelem with OPpDEREF skipped */ + + if (!(actions & MDEREF_FLAG_last)) { + SV** svp = av_fetch((AV*)sv, elem, 1); + if (!svp || ! (sv=*svp)) + DIE(aTHX_ PL_no_aelem, elem); + break; + } + + if (PL_op->op_private & + (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE)) + { + if (PL_op->op_private & OPpMULTIDEREF_EXISTS) { + sv = av_exists((AV*)sv, elem) ? &PL_sv_yes : &PL_sv_no; + } + else { + I32 discard = (GIMME_V == G_VOID) ? G_DISCARD : 0; + sv = av_delete((AV*)sv, elem, discard); + if (discard) + return NORMAL; + if (!sv) + sv = &PL_sv_undef; + } + } + else { + const U32 lval = PL_op->op_flags & OPf_MOD || LVRET; + const U32 defer = PL_op->op_private & OPpLVAL_DEFER; + const bool localizing = PL_op->op_private & OPpLVAL_INTRO; + bool preeminent = TRUE; + AV *const av = (AV*)sv; + SV** svp; + + if (UNLIKELY(localizing)) { + MAGIC *mg; + HV *stash; + + /* If we can determine whether the element exist, + * Try to preserve the existenceness of a tied array + * element by using EXISTS and DELETE if possible. + * Fallback to FETCH and STORE otherwise. */ + if (SvCANEXISTDELETE(av)) + preeminent = av_exists(av, elem); + } + + svp = av_fetch(av, elem, lval && !defer); + + if (lval) { + if (!svp || !(sv = *svp)) { + IV len; + if (!defer) + DIE(aTHX_ PL_no_aelem, elem); + len = av_tindex(av); + sv = sv_2mortal(newSVavdefelem(av, + /* Resolve a negative index now, unless it points + * before the beginning of the array, in which + * case record it for error reporting in + * magic_setdefelem. */ + elem < 0 && len + elem >= 0 + ? len + elem : elem, 1)); + } + else { + if (UNLIKELY(localizing)) { + if (preeminent) { + save_aelem(av, elem, svp); + sv = *svp; /* may have changed */ + } + else + SAVEADELETE(av, elem); + } + } + } + else { + sv = (svp ? *svp : &PL_sv_undef); + /* see note in pp_helem() */ + if (SvRMAGICAL(av) && SvGMAGICAL(sv)) + mg_get(sv); + } + } + + } + finish: + { + dSP; + XPUSHs(sv); + RETURN; + } + /* NOTREACHED */ + + + + + case MDEREF_HV_padhv_helem: /* $lex{...} */ + sv = PAD_SVl((++items)->pad_offset); + goto do_HV_helem; + + case MDEREF_HV_gvhv_helem: /* $pkg{...} */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = (SV*)GvHVn((GV*)sv); + goto do_HV_helem; + + case MDEREF_HV_pop_rv2hv_helem: /* expr->{...} */ + { + dSP; + sv = POPs; + PUTBACK; + goto do_HV_rv2hv_helem; + } + + case MDEREF_HV_gvsv_vivify_rv2hv_helem: /* $pkg->{...} */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = GvSVn((GV*)sv); + goto do_HV_vivify_rv2hv_helem; + + case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */ + sv = PAD_SVl((++items)->pad_offset); + /* FALLTHROUGH */ + + do_HV_vivify_rv2hv_helem: + case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + /* this is the OPpDEREF action normally found at the end of + * ops like aelem, helem, rv2sv */ + sv = vivify_ref(sv, OPpDEREF_HV); + /* FALLTHROUGH */ + + do_HV_rv2hv_helem: + /* this is basically a copy of pp_rv2hv when it just has the + * sKR/1 flags (and pp_rv2hv is aliased to pp_rv2av) */ + + SvGETMAGIC(sv); + if (LIKELY(SvROK(sv))) { + if (UNLIKELY(SvAMAGIC(sv))) { + sv = amagic_deref_call(sv, to_hv_amg); + } + sv = SvRV(sv); + if (UNLIKELY(SvTYPE(sv) != SVt_PVHV)) + DIE(aTHX_ "Not a HASH reference"); + } + else if (SvTYPE(sv) != SVt_PVHV) { + if (!isGV_with_GP(sv)) + sv = (SV*)S_softref2xv_lite(aTHX_ sv, "a HASH", SVt_PVHV); + sv = MUTABLE_SV(GvHVn((GV*)sv)); + } + /* FALLTHROUGH */ + + do_HV_helem: + { + /* retrieve the key; this may be either a lexical / package + * var or a string constant, whose index/ptr is stored as an + * item + */ + SV *keysv = NULL; /* to shut up stupid compiler warnings */ + + assert(SvTYPE(sv) == SVt_PVHV); + + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + + case MDEREF_INDEX_const: + keysv = UNOP_AUX_item_sv(++items); + break; + + case MDEREF_INDEX_padsv: + keysv = PAD_SVl((++items)->pad_offset); + break; + + case MDEREF_INDEX_gvsv: + keysv = UNOP_AUX_item_sv(++items); + keysv = GvSVn((GV*)keysv); + break; + } + + /* see comment above about setting this var */ + PL_multideref_pc = items; + + + /* ensure that candidate CONSTs have been HEKified */ + assert( ((actions & MDEREF_INDEX_MASK) != MDEREF_INDEX_const) + || SvTYPE(keysv) >= SVt_PVMG + || !SvOK(keysv) + || SvROK(keysv) + || SvIsCOW_shared_hash(keysv)); + + /* this is basically a copy of pp_helem with OPpDEREF skipped */ + + if (!(actions & MDEREF_FLAG_last)) { + HE *he = hv_fetch_ent((HV*)sv, keysv, 1, 0); + if (!he || !(sv=HeVAL(he)) || sv == &PL_sv_undef) + DIE(aTHX_ PL_no_helem_sv, SVfARG(keysv)); + break; + } + + if (PL_op->op_private & + (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE)) + { + if (PL_op->op_private & OPpMULTIDEREF_EXISTS) { + sv = hv_exists_ent((HV*)sv, keysv, 0) + ? &PL_sv_yes : &PL_sv_no; + } + else { + I32 discard = (GIMME_V == G_VOID) ? G_DISCARD : 0; + sv = hv_delete_ent((HV*)sv, keysv, discard, 0); + if (discard) + return NORMAL; + if (!sv) + sv = &PL_sv_undef; + } + } + else { + const U32 lval = PL_op->op_flags & OPf_MOD || LVRET; + const U32 defer = PL_op->op_private & OPpLVAL_DEFER; + const bool localizing = PL_op->op_private & OPpLVAL_INTRO; + bool preeminent = TRUE; + SV **svp; + HV * const hv = (HV*)sv; + HE* he; + + if (UNLIKELY(localizing)) { + MAGIC *mg; + HV *stash; + + /* If we can determine whether the element exist, + * Try to preserve the existenceness of a tied hash + * element by using EXISTS and DELETE if possible. + * Fallback to FETCH and STORE otherwise. */ + if (SvCANEXISTDELETE(hv)) + preeminent = hv_exists_ent(hv, keysv, 0); + } + + he = hv_fetch_ent(hv, keysv, lval && !defer, 0); + svp = he ? &HeVAL(he) : NULL; + + + if (lval) { + if (!svp || !(sv = *svp) || sv == &PL_sv_undef) { + SV* lv; + SV* key2; + if (!defer) + DIE(aTHX_ PL_no_helem_sv, SVfARG(keysv)); + lv = sv_newmortal(); + sv_upgrade(lv, SVt_PVLV); + LvTYPE(lv) = 'y'; + sv_magic(lv, key2 = newSVsv(keysv), + PERL_MAGIC_defelem, NULL, 0); + /* sv_magic() increments refcount */ + SvREFCNT_dec_NN(key2); + LvTARG(lv) = SvREFCNT_inc_simple(hv); + LvTARGLEN(lv) = 1; + sv = lv; + } + else { + if (localizing) { + if (HvNAME_get(hv) && isGV(sv)) + save_gp(MUTABLE_GV(sv), + !(PL_op->op_flags & OPf_SPECIAL)); + else if (preeminent) { + save_helem_flags(hv, keysv, svp, + (PL_op->op_flags & OPf_SPECIAL) + ? 0 : SAVEf_SETMAGIC); + sv = *svp; /* may have changed */ + } + else + SAVEHDELETE(hv, keysv); + } + } + } + else { + sv = (svp && *svp ? *svp : &PL_sv_undef); + /* see note in pp_helem() */ + if (SvRMAGICAL(hv) && SvGMAGICAL(sv)) + mg_get(sv); + } + } + goto finish; + } + + } /* switch */ + + actions >>= MDEREF_SHIFT; + } /* while */ + /* NOTREACHED */ +} + + PP(pp_iter) { dSP; diff --git a/pp_proto.h b/pp_proto.h index 6959357dd2..074f4ab8a3 100644 --- a/pp_proto.h +++ b/pp_proto.h @@ -157,6 +157,7 @@ PERL_CALLCONV OP *Perl_pp_method_redir_super(pTHX); PERL_CALLCONV OP *Perl_pp_method_super(pTHX); PERL_CALLCONV OP *Perl_pp_mkdir(pTHX); PERL_CALLCONV OP *Perl_pp_modulo(pTHX); +PERL_CALLCONV OP *Perl_pp_multideref(pTHX); PERL_CALLCONV OP *Perl_pp_multiply(pTHX); PERL_CALLCONV OP *Perl_pp_ncmp(pTHX); PERL_CALLCONV OP *Perl_pp_ne(pTHX); @@ -4946,6 +4946,12 @@ PERL_CALLCONV UV Perl_to_utf8_case(pTHX_ const U8 *p, U8* ustrp, STRLEN *lenp, S PERL_CALLCONV bool Perl_try_amagic_bin(pTHX_ int method, int flags); PERL_CALLCONV bool Perl_try_amagic_un(pTHX_ int method, int flags); +PERL_CALLCONV SV* Perl_unop_aux_stringify(pTHX_ const OP* o, CV *cv) + __attribute__nonnull__(pTHX_1) + __attribute__nonnull__(pTHX_2); +#define PERL_ARGS_ASSERT_UNOP_AUX_STRINGIFY \ + assert(o); assert(cv) + PERL_CALLCONV I32 Perl_unpack_str(pTHX_ const char *pat, const char *patend, const char *s, const char *strbeg, const char *strend, char **new_s, I32 ocnt, U32 flags) __attribute__nonnull__(pTHX_1) __attribute__nonnull__(pTHX_2) @@ -7543,7 +7549,11 @@ STATIC SV * S_find_hash_subscript(pTHX_ const HV *const hv, const SV *const val) #define PERL_ARGS_ASSERT_FIND_HASH_SUBSCRIPT \ assert(val) -STATIC SV* S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, bool top); +STATIC SV* S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, bool match, const char **desc_p) + __attribute__nonnull__(pTHX_4); +#define PERL_ARGS_ASSERT_FIND_UNINIT_VAR \ + assert(desc_p) + STATIC bool S_glob_2number(pTHX_ GV* const gv) __attribute__nonnull__(pTHX_1); #define PERL_ARGS_ASSERT_GLOB_2NUMBER \ diff --git a/regen/op_private b/regen/op_private index 731c4fb490..4b7c42522e 100644 --- a/regen/op_private +++ b/regen/op_private @@ -299,7 +299,7 @@ for (qw(nextstate dbstate)) { addbits($_, 7 => qw(OPpLVAL_INTRO LVINTRO)) for qw(gvsv rv2sv rv2hv rv2gv rv2av aelem helem aslice hslice delete padsv padav padhv enteriter entersub padrange - pushmark cond_expr refassign lvref lvrefslice lvavref), + pushmark cond_expr refassign lvref lvrefslice lvavref multideref), 'list', # this gets set in my_attrs() for some reason ; @@ -418,7 +418,7 @@ for (qw(rv2gv rv2sv padsv aelem helem entersub)) { # Defer creation of array/hash elem -addbits($_, 6 => qw(OPpLVAL_DEFER LVDEFER)) for qw(aelem helem); +addbits($_, 6 => qw(OPpLVAL_DEFER LVDEFER)) for qw(aelem helem multideref); @@ -437,7 +437,7 @@ addbits($_, 6 => qw(OPpOUR_INTRO OURINTR)) # Variable was in an our() # We might be an lvalue to return addbits($_, 3 => qw(OPpMAYBE_LVSUB LVSUB)) for qw(aassign rv2av rv2gv rv2hv padav padhv aelem helem aslice hslice - av2arylen keys rkeys kvaslice kvhslice substr pos vec); + av2arylen keys rkeys kvaslice kvhslice substr pos vec multideref); @@ -450,7 +450,8 @@ for (qw(rv2hv padhv)) { -addbits($_, 1 => qw(OPpHINT_STRICT_REFS STRICT)) for qw(rv2sv rv2av rv2hv rv2gv); +addbits($_, 1 => qw(OPpHINT_STRICT_REFS STRICT)) + for qw(rv2sv rv2av rv2hv rv2gv multideref); @@ -734,6 +735,13 @@ addbits($_, #7 => qw(OPpLVAL_INTRO LVINTRO), ) for 'refassign', 'lvref'; + + +addbits('multideref', + 4 => qw(OPpMULTIDEREF_EXISTS EXISTS), # deref is actually exists + 5 => qw(OPpMULTIDEREF_DELETE DELETE), # deref is actually delete +); + 1; # ex: set ts=8 sts=4 sw=4 et: diff --git a/regen/opcodes b/regen/opcodes index 4731fa7b56..49e6c297cc 100644 --- a/regen/opcodes +++ b/regen/opcodes @@ -236,6 +236,10 @@ helem hash element ck_null s2 H S hslice hash slice ck_null m@ H L kvhslice key/value hash slice ck_null m@ H L +# mixed array and hash access + +multideref array or hash lookup ck_null ds+ + # Explosives and implosives. unpack unpack ck_fun u@ S S? @@ -15456,6 +15456,8 @@ warning, then following the direct child of the op may yield an OP_PADSV or OP_GV that gives the name of the undefined variable. On the other hand, with OP_ADD there are two branches to follow, so we only print the variable name if we get an exact match. +desc_p points to a string pointer holding the description of the op. +This may be updated if needed. The name is returned as a mortal SV. @@ -15467,13 +15469,15 @@ PL_comppad/PL_curpad points to the currently executing pad. STATIC SV * S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, - bool match) + bool match, const char **desc_p) { dVAR; SV *sv; const GV *gv; const OP *o, *o2, *kid; + PERL_ARGS_ASSERT_FIND_UNINIT_VAR; + if (!obase || (match && (!uninit_sv || uninit_sv == &PL_sv_undef || uninit_sv == &PL_sv_placeholder))) return NULL; @@ -15513,7 +15517,7 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, } else if (obase == PL_op) /* @{expr}, %{expr} */ return find_uninit_var(cUNOPx(obase)->op_first, - uninit_sv, match); + uninit_sv, match, desc_p); else /* @{expr}, %{expr} as a sub-expression */ return NULL; } @@ -15548,7 +15552,7 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, return varname(gv, '$', 0, NULL, 0, FUV_SUBSCRIPT_NONE); } /* ${expr} */ - return find_uninit_var(cUNOPx(obase)->op_first, uninit_sv, 1); + return find_uninit_var(cUNOPx(obase)->op_first, uninit_sv, 1, desc_p); case OP_PADSV: if (match && PAD_SVl(obase->op_targ) != uninit_sv) @@ -15598,7 +15602,7 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, if (!o || o->op_type != OP_NULL || ! (o->op_targ == OP_AELEM || o->op_targ == OP_HELEM)) break; - return find_uninit_var(cBINOPo->op_last, uninit_sv, match); + return find_uninit_var(cBINOPo->op_last, uninit_sv, match, desc_p); case OP_AELEM: case OP_HELEM: @@ -15607,7 +15611,8 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, if (PL_op == obase) /* $a[uninit_expr] or $h{uninit_expr} */ - return find_uninit_var(cBINOPx(obase)->op_last, uninit_sv, match); + return find_uninit_var(cBINOPx(obase)->op_last, + uninit_sv, match, desc_p); gv = NULL; o = cBINOPx(obase)->op_first; @@ -15696,9 +15701,205 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, NOT_REACHED; /* NOTREACHED */ } + case OP_MULTIDEREF: { + /* If we were executing OP_MULTIDEREF when the undef warning + * triggered, then it must be one of the index values within + * that triggered it. If not, then the only possibility is that + * the value retrieved by the last aggregate lookup might be the + * culprit. For the former, we set PL_multideref_pc each time before + * using an index, so work though the item list until we reach + * that point. For the latter, just work through the entire item + * list; the last aggregate retrieved will be the candidate. + */ + + /* the named aggregate, if any */ + PADOFFSET agg_targ = 0; + GV *agg_gv = NULL; + /* the last-seen index */ + UV index_type; + PADOFFSET index_targ; + GV *index_gv; + IV index_const_iv = 0; /* init for spurious compiler warn */ + SV *index_const_sv; + int depth = 0; /* how many array/hash lookups we've done */ + + UNOP_AUX_item *items = cUNOP_AUXx(obase)->op_aux; + UNOP_AUX_item *last = NULL; + UV actions = items->uv; + bool is_hv; + + if (PL_op == obase) { + last = PL_multideref_pc; + assert(last >= items && last <= items + items[-1].uv); + } + + assert(actions); + + while (1) { + is_hv = FALSE; + switch (actions & MDEREF_ACTION_MASK) { + + case MDEREF_reload: + actions = (++items)->uv; + continue; + + case MDEREF_HV_padhv_helem: /* $lex{...} */ + is_hv = TRUE; + /* FALLTHROUGH */ + case MDEREF_AV_padav_aelem: /* $lex[...] */ + agg_targ = (++items)->pad_offset; + agg_gv = NULL; + break; + + case MDEREF_HV_gvhv_helem: /* $pkg{...} */ + is_hv = TRUE; + /* FALLTHROUGH */ + case MDEREF_AV_gvav_aelem: /* $pkg[...] */ + agg_targ = 0; + agg_gv = (GV*)UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(agg_gv)); + break; + + case MDEREF_HV_gvsv_vivify_rv2hv_helem: /* $pkg->{...} */ + case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */ + ++items; + /* FALLTHROUGH */ + case MDEREF_HV_pop_rv2hv_helem: /* expr->{...} */ + case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + agg_targ = 0; + agg_gv = NULL; + is_hv = TRUE; + break; + + case MDEREF_AV_gvsv_vivify_rv2av_aelem: /* $pkg->[...] */ + case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */ + ++items; + /* FALLTHROUGH */ + case MDEREF_AV_pop_rv2av_aelem: /* expr->[...] */ + case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + agg_targ = 0; + agg_gv = NULL; + } /* switch */ + + index_targ = 0; + index_gv = NULL; + index_const_sv = NULL; + + index_type = (actions & MDEREF_INDEX_MASK); + switch (index_type) { + case MDEREF_INDEX_none: + break; + case MDEREF_INDEX_const: + if (is_hv) + index_const_sv = UNOP_AUX_item_sv(++items) + else + index_const_iv = (++items)->iv; + break; + case MDEREF_INDEX_padsv: + index_targ = (++items)->pad_offset; + break; + case MDEREF_INDEX_gvsv: + index_gv = (GV*)UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(index_gv)); + break; + } + + if (index_type != MDEREF_INDEX_none) + depth++; + + if ( index_type == MDEREF_INDEX_none + || (actions & MDEREF_FLAG_last) + || (last && items == last) + ) + break; + + actions >>= MDEREF_SHIFT; + } /* while */ + + if (PL_op == obase) { + /* index was undef */ + + *desc_p = ( (actions & MDEREF_FLAG_last) + && (obase->op_private + & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE))) + ? + (obase->op_private & OPpMULTIDEREF_EXISTS) + ? "exists" + : "delete" + : is_hv ? "hash element" : "array element"; + assert(index_type != MDEREF_INDEX_none); + if (index_gv) + return varname(index_gv, '$', 0, NULL, 0, FUV_SUBSCRIPT_NONE); + if (index_targ) + return varname(NULL, '$', index_targ, + NULL, 0, FUV_SUBSCRIPT_NONE); + assert(is_hv); /* AV index is an IV and can't be undef */ + /* can a const HV index ever be undef? */ + return NULL; + } + + /* the SV returned by pp_multideref() was undef, if anything was */ + + if (depth != 1) + break; + + if (agg_targ) + sv = PAD_SV(agg_targ); + else if (agg_gv) + sv = is_hv ? MUTABLE_SV(GvHV(agg_gv)) : MUTABLE_SV(GvAV(agg_gv)); + else + break; + + if (index_type == MDEREF_INDEX_const) { + if (match) { + if (SvMAGICAL(sv)) + break; + if (is_hv) { + HE* he = hv_fetch_ent(MUTABLE_HV(sv), index_const_sv, 0, 0); + if (!he || HeVAL(he) != uninit_sv) + break; + } + else { + SV * const * const svp = + av_fetch(MUTABLE_AV(sv), index_const_iv, FALSE); + if (!svp || *svp != uninit_sv) + break; + } + } + return is_hv + ? varname(agg_gv, '%', agg_targ, + index_const_sv, 0, FUV_SUBSCRIPT_HASH) + : varname(agg_gv, '@', agg_targ, + NULL, index_const_iv, FUV_SUBSCRIPT_ARRAY); + } + else { + /* index is an var */ + if (is_hv) { + SV * const keysv = find_hash_subscript((const HV*)sv, uninit_sv); + if (keysv) + return varname(agg_gv, '%', agg_targ, + keysv, 0, FUV_SUBSCRIPT_HASH); + } + else { + const I32 index + = find_array_subscript((const AV *)sv, uninit_sv); + if (index >= 0) + return varname(agg_gv, '@', agg_targ, + NULL, index, FUV_SUBSCRIPT_ARRAY); + } + if (match) + break; + return varname(agg_gv, + is_hv ? '%' : '@', + agg_targ, NULL, 0, FUV_SUBSCRIPT_WITHIN); + } + NOT_REACHED; /* NOTREACHED */ + } + case OP_AASSIGN: /* only examine RHS */ - return find_uninit_var(cBINOPx(obase)->op_first, uninit_sv, match); + return find_uninit_var(cBINOPx(obase)->op_first, uninit_sv, + match, desc_p); case OP_OPEN: o = cUNOPx(obase)->op_first; @@ -15897,11 +16098,11 @@ S_find_uninit_var(pTHX_ const OP *const obase, const SV *const uninit_sv, o2 = kid; } if (o2) - return find_uninit_var(o2, uninit_sv, match); + return find_uninit_var(o2, uninit_sv, match, desc_p); /* scan all args */ while (o) { - sv = find_uninit_var(o, uninit_sv, 1); + sv = find_uninit_var(o, uninit_sv, 1, desc_p); if (sv) return sv; o = OP_SIBLING(o); @@ -15926,14 +16127,15 @@ Perl_report_uninit(pTHX_ const SV *uninit_sv) if (PL_op) { SV* varname = NULL; const char *desc; + + desc = PL_op->op_type == OP_STRINGIFY && PL_op->op_folded + ? "join or string" + : OP_DESC(PL_op); if (uninit_sv && PL_curpad) { - varname = find_uninit_var(PL_op, uninit_sv,0); + varname = find_uninit_var(PL_op, uninit_sv, 0, &desc); if (varname) sv_insert(varname, 0, 0, " ", 1); } - desc = PL_op->op_type == OP_STRINGIFY && PL_op->op_folded - ? "join or string" - : OP_DESC(PL_op); /* PL_warn_uninit_sv is constant */ GCC_DIAG_IGNORE(-Wformat-nonliteral); /* diag_listed_as: Use of uninitialized value%s */ diff --git a/t/lib/warnings/9uninit b/t/lib/warnings/9uninit index c1a1dfcfc6..d26d6ca0ba 100644 --- a/t/lib/warnings/9uninit +++ b/t/lib/warnings/9uninit @@ -2091,3 +2091,80 @@ tie $t, ""; $v = 1.1 * $t; # sv_2nv on a tied regexp EXPECT +######## +# multi-level uninitialised array/hash indexes +use warnings 'uninitialized'; + +our ($i0, $i2, $i4, $i6, $i8, $i10, $i12); +my ($i1, $i3, $i5, $i7, $i9, $i11, $i13); + +my (@a,%h); +my $v; + + +# use enough depth that OP_MULTIDEREF needs more than one action word + +$v = $a[$i0]{$i1}[$i2]{$i3}[$i4]{$i5}[$i6]{$i7}[$i8]{$i9}[$i10]{$i11}[$i12]{$i13}; +$v = $h{$i0}[$i1]{$i2}[$i3]{$i4}[$i5]{$i6}[$i7]{$i8}[$i9]{$i10}[$i11]{$i12}[$i13]; + +EXPECT +Use of uninitialized value $i0 in array element at - line 13. +Use of uninitialized value $i1 in hash element at - line 13. +Use of uninitialized value $i2 in array element at - line 13. +Use of uninitialized value $i3 in hash element at - line 13. +Use of uninitialized value $i4 in array element at - line 13. +Use of uninitialized value $i5 in hash element at - line 13. +Use of uninitialized value $i6 in array element at - line 13. +Use of uninitialized value $i7 in hash element at - line 13. +Use of uninitialized value $i8 in array element at - line 13. +Use of uninitialized value $i9 in hash element at - line 13. +Use of uninitialized value $i10 in array element at - line 13. +Use of uninitialized value $i11 in hash element at - line 13. +Use of uninitialized value $i12 in array element at - line 13. +Use of uninitialized value $i13 in hash element at - line 13. +Use of uninitialized value $i0 in hash element at - line 14. +Use of uninitialized value $i1 in array element at - line 14. +Use of uninitialized value $i2 in hash element at - line 14. +Use of uninitialized value $i3 in array element at - line 14. +Use of uninitialized value $i4 in hash element at - line 14. +Use of uninitialized value $i5 in array element at - line 14. +Use of uninitialized value $i6 in hash element at - line 14. +Use of uninitialized value $i7 in array element at - line 14. +Use of uninitialized value $i8 in hash element at - line 14. +Use of uninitialized value $i9 in array element at - line 14. +Use of uninitialized value $i10 in hash element at - line 14. +Use of uninitialized value $i11 in array element at - line 14. +Use of uninitialized value $i12 in hash element at - line 14. +Use of uninitialized value $i13 in array element at - line 14. +######## +# misc multideref +use warnings 'uninitialized'; +my ($i,$j,$k); +my @a; +my @ra = \@a; +my $v; +$v = exists $a[$i]{$k}; +$v = delete $a[$i]{$k}; +$v = local $a[$i]{$k}; +delete $a[$i]{$k}; +$v = $ra->[$i+$j]{$k}; +$v = ($ra//0)->[$i]{$k}; +$v = $a[length $i]{$k} +EXPECT +Use of uninitialized value $i in array element at - line 7. +Use of uninitialized value $k in exists at - line 7. +Use of uninitialized value $i in array element at - line 8. +Use of uninitialized value $k in delete at - line 8. +Use of uninitialized value $i in array element at - line 9. +Use of uninitialized value $k in hash element at - line 9. +Use of uninitialized value $k in hash element at - line 9. +Use of uninitialized value $k in hash element at - line 9. +Use of uninitialized value $i in array element at - line 10. +Use of uninitialized value $k in delete at - line 10. +Use of uninitialized value $j in addition (+) at - line 11. +Use of uninitialized value $i in addition (+) at - line 11. +Use of uninitialized value $k in hash element at - line 11. +Use of uninitialized value $i in array element at - line 12. +Use of uninitialized value $k in hash element at - line 12. +Use of uninitialized value $i in array element at - line 13. +Use of uninitialized value $k in hash element at - line 13. diff --git a/t/op/multideref.t b/t/op/multideref.t new file mode 100644 index 0000000000..1ae0843aa8 --- /dev/null +++ b/t/op/multideref.t @@ -0,0 +1,187 @@ +#!./perl +# +# test OP_MULTIDEREF. +# +# This optimising op is used when one or more array or hash aggregate +# lookups / derefs are performed, and where each key/index is a simple +# constant or scalar var; e.g. +# +# $r->{foo}[0]{$k}[$i] + + +BEGIN { + chdir 't'; + require './test.pl'; + set_up_inc("../lib"); +} + +use warnings; +use strict; + +plan 56; + + +# check that strict refs hint is handled + +{ + package strict_refs; + + our %foo; + my @a = ('foo'); + eval { + $a[0]{k} = 7; + }; + ::like($@, qr/Can't use string/, "strict refs"); + ::ok(!exists $foo{k}, "strict refs, not exist"); + + no strict 'refs'; + + $a[0]{k} = 13; + ::is($foo{k}, 13, "no strict refs, exist"); +} + +# check the basics of multilevel lookups + +{ + package basic; + + # build up the multi-level structure piecemeal to try and avoid + # relying on what we're testing + + my @a; + my $r = \@a; + my $rh = {}; + my $ra = []; + my %h = qw(a 1 b 2 c 3 d 4 e 5 f 6); + push @a, 66, 77, 'abc', $rh; + %$rh = (foo => $ra, bar => 'BAR'); + push @$ra, 'def', \%h; + + our ($i1, $i2, $k1, $k2) = (3, 1, 'foo', 'c'); + my ($li1, $li2, $lk1, $lk2) = (3, 1, 'foo', 'c'); + my $z = 0; + + # fetch + + ::is($a[3]{foo}[1]{c}, 3, 'fetch: const indices'); + ::is($a[$i1]{$k1}[$i2]{$k2}, 3, 'fetch: pkg indices'); + ::is($r->[$i1]{$k1}[$i2]{$k2}, 3, 'fetch: deref pkg indices'); + ::is($a[$li1]{$lk1}[$li2]{$lk2}, 3, 'fetch: lexical indices'); + ::is($r->[$li1]{$lk1}[$li2]{$lk2}, 3, 'fetch: deref lexical indices'); + ::is(($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2}, 3, + 'fetch: general expression and index'); + + + # store + + ::is($a[3]{foo}[1]{c} = 5, 5, 'store: const indices'); + ::is($a[3]{foo}[1]{c}, 5, 'store: const indices 2'); + ::is($a[$i1]{$k1}[$i2]{$k2} = 7, 7, 'store: pkg indices'); + ::is($a[$i1]{$k1}[$i2]{$k2}, 7, 'store: pkg indices 2'); + ::is($r->[$i1]{$k1}[$i2]{$k2} = 9, 9, 'store: deref pkg indices'); + ::is($r->[$i1]{$k1}[$i2]{$k2}, 9, 'store: deref pkg indices 2'); + ::is($a[$li1]{$lk1}[$li2]{$lk2} = 11, 11, 'store: lexical indices'); + ::is($a[$li1]{$lk1}[$li2]{$lk2}, 11, 'store: lexical indices 2'); + ::is($r->[$li1]{$lk1}[$li2]{$lk2} = 13, 13, 'store: deref lexical indices'); + ::is($r->[$li1]{$lk1}[$li2]{$lk2}, 13, 'store: deref lexical indices 2'); + ::is(($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2} = 15, 15, + 'store: general expression and index'); + ::is(($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2}, 15, + 'store: general expression and index 2'); + + + # local + + { + ::is(local $a[3]{foo}[1]{c} = 19, 19, 'local const indices'); + ::is($a[3]{foo}[1]{c}, 19, 'local const indices 2'); + } + ::is($a[3]{foo}[1]{c}, 15, 'local const indices 3'); + { + ::is(local $a[$i1]{$k1}[$i2]{$k2} = 21, 21, 'local pkg indices'); + ::is($a[$i1]{$k1}[$i2]{$k2}, 21, 'local pkg indices 2'); + } + ::is($a[$i1]{$k1}[$i2]{$k2}, 15, 'local pkg indices 3'); + { + ::is(local $a[$li1]{$lk1}[$li2]{$lk2} = 23, 23, 'local lexical indices'); + ::is($a[$li1]{$lk1}[$li2]{$lk2}, 23, 'local lexical indices 2'); + } + ::is($a[$li1]{$lk1}[$li2]{$lk2}, 15, 'local lexical indices 3'); + { + ::is(local+($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2} = 25, 25, + 'local general'); + ::is(($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2}, 25, 'local general 2'); + } + ::is(($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2}, 15, 'local general 3'); + + + # exists + + ::ok(exists $a[3]{foo}[1]{c}, 'exists: const indices'); + ::ok(exists $a[$i1]{$k1}[$i2]{$k2}, 'exists: pkg indices'); + ::ok(exists $r->[$i1]{$k1}[$i2]{$k2}, 'exists: deref pkg indices'); + ::ok(exists $a[$li1]{$lk1}[$li2]{$lk2}, 'exists: lexical indices'); + ::ok(exists $r->[$li1]{$lk1}[$li2]{$lk2}, 'exists: deref lexical indices'); + ::ok(exists +($r//0)->[$li1]{$lk1}[$li2+$z]{$lk2}, 'exists: general'); + + # delete + + our $k3 = 'a'; + my $lk4 = 'b'; + ::is(delete $a[3]{foo}[1]{c}, 15, 'delete: const indices'); + ::is(delete $a[$i1]{$k1}[$i2]{$k3}, 1, 'delete: pkg indices'); + ::is(delete $r->[$i1]{$k1}[$i2]{d}, 4, 'delete: deref pkg indices'); + ::is(delete $a[$li1]{$lk1}[$li2]{$lk4}, 2, 'delete: lexical indices'); + ::is(delete $r->[$li1]{$lk1}[$li2]{e}, 5, 'delete: deref lexical indices'); + ::is(delete +($r//0)->[$li1]{$lk1}[$li2+$z]{f}, 6, 'delete: general'); + + # !exists + + ::ok(!exists $a[3]{foo}[1]{c}, '!exists: const indices'); + ::ok(!exists $a[$i1]{$k1}[$i2]{$k3}, '!exists: pkg indices'); + ::ok(!exists $r->[$i1]{$k1}[$i2]{$k3}, '!exists: deref pkg indices'); + ::ok(!exists $a[$li1]{$lk1}[$li2]{$lk4}, '!exists: lexical indices'); + ::ok(!exists $r->[$li1]{$lk1}[$li2]{$lk4},'!exists: deref lexical indices'); + ::ok(!exists +($r//0)->[$li1]{$lk1}[$li2+$z]{$lk4},'!exists: general'); +} + + +# weird "constant" keys + +{ + use constant my_undef => undef; + use constant my_ref => []; + no warnings 'uninitialized'; + my %h1; + $h1{+my_undef} = 1; + is(join(':', keys %h1), '', "+my_undef"); + my %h2; + $h2{+my_ref} = 1; + like(join(':', keys %h2), qr/x/, "+my_ref"); +} + + + +{ + # test that multideref is marked OA_DANGEROUS, i.e. its one of the ops + # that should set the OPpASSIGN_COMMON flag in list assignments + + my $x = {}; + $x->{a} = [ 1 ]; + $x->{b} = [ 2 ]; + ($x->{a}, $x->{b}) = ($x->{b}, $x->{a}); + is($x->{a}[0], 2, "OA_DANGEROUS a"); + is($x->{b}[0], 1, "OA_DANGEROUS b"); +} + +# defer + + +sub defer {} + +{ + my %h; + $h{foo} = {}; + defer($h{foo}{bar}); + ok(!exists $h{foo}{bar}, "defer"); +} diff --git a/t/op/svleak.t b/t/op/svleak.t index 8d42265b8f..076f2bfdaf 100644 --- a/t/op/svleak.t +++ b/t/op/svleak.t @@ -15,7 +15,7 @@ BEGIN { use Config; -plan tests => 128; +plan tests => 129; # run some code N times. If the number of SVs at the end of loop N is # greater than (N-1)*delta at the end of loop 1, we've got a leak @@ -479,3 +479,17 @@ leak(2,0,sub{eval{require untohunothu}}, 'requiring nonexistent module'); # [perl #120939] use constant const_av_xsub_leaked => 1 .. 3; leak(5, 0, sub { scalar &const_av_xsub_leaked }, "const_av_sub in scalar context"); + +# check that OP_MULTIDEREF doesn't leak when compiled and then freed + +eleak(2, 0, <<'EOF', 'OP_MULTIDEREF'); +no strict; +no warnings; +my ($x, @a, %h, $r, $k, $i); +$x = $a[0]{foo}{$k}{$i}; +$x = $h[0]{foo}{$k}{$i}; +$x = $r->[0]{foo}{$k}{$i}; +$x = $mdr::a[0]{foo}{$mdr::k}{$mdr::i}; +$x = $mdr::h[0]{foo}{$mdr::k}{$mdr::i}; +$x = $mdr::r->[0]{foo}{$mdr::k}{$mdr::i}; +EOF diff --git a/t/perf/benchmarks b/t/perf/benchmarks index 52e2af9400..1e4cd72533 100644 --- a/t/perf/benchmarks +++ b/t/perf/benchmarks @@ -59,6 +59,95 @@ code => 'f(1,2,3)', }, + + 'expr::array::lex_1const_0' => { + desc => 'lexical $array[0]', + setup => 'my @a = (1)', + code => '$a[0]', + }, + 'expr::array::lex_1const_m1' => { + desc => 'lexical $array[-1]', + setup => 'my @a = (1)', + code => '$a[-1]', + }, + 'expr::array::lex_2const' => { + desc => 'lexical $array[const][const]', + setup => 'my @a = ([1,2])', + code => '$a[0][1]', + }, + 'expr::array::lex_2var' => { + desc => 'lexical $array[$i1][$i2]', + setup => 'my ($i1,$i2) = (0,1); my @a = ([1,2])', + code => '$a[$i1][$i2]', + }, + 'expr::array::ref_lex_2var' => { + desc => 'lexical $arrayref->[$i1][$i2]', + setup => 'my ($i1,$i2) = (0,1); my $r = [[1,2]]', + code => '$r->[$i1][$i2]', + }, + 'expr::array::ref_lex_3const' => { + desc => 'lexical $arrayref->[const][const][const]', + setup => 'my $r = [[[1,2]]]', + code => '$r->[0][0][0]', + }, + 'expr::array::ref_expr_lex_3const' => { + desc => '(lexical expr)->[const][const][const]', + setup => 'my $r = [[[1,2]]]', + code => '($r//0)->[0][0][0]', + }, + + + 'expr::array::pkg_1const_0' => { + desc => 'package $array[0]', + setup => 'our @a = (1)', + code => '$a[0]', + }, + 'expr::array::pkg_1const_m1' => { + desc => 'package $array[-1]', + setup => 'our @a = (1)', + code => '$a[-1]', + }, + 'expr::array::pkg_2const' => { + desc => 'package $array[const][const]', + setup => 'our @a = ([1,2])', + code => '$a[0][1]', + }, + 'expr::array::pkg_2var' => { + desc => 'package $array[$i1][$i2]', + setup => 'our ($i1,$i2) = (0,1); our @a = ([1,2])', + code => '$a[$i1][$i2]', + }, + 'expr::array::ref_pkg_2var' => { + desc => 'package $arrayref->[$i1][$i2]', + setup => 'our ($i1,$i2) = (0,1); our $r = [[1,2]]', + code => '$r->[$i1][$i2]', + }, + 'expr::array::ref_pkg_3const' => { + desc => 'package $arrayref->[const][const][const]', + setup => 'our $r = [[[1,2]]]', + code => '$r->[0][0][0]', + }, + 'expr::array::ref_expr_pkg_3const' => { + desc => '(package expr)->[const][const][const]', + setup => 'our $r = [[[1,2]]]', + code => '($r//0)->[0][0][0]', + }, + + + 'expr::arrayhash::lex_3var' => { + desc => 'lexical $h{$k1}[$i]{$k2}', + setup => 'my ($i, $k1, $k2) = (0,"foo","bar");' + . 'my %h = (foo => [ { bar => 1 } ])', + code => '$h{$k1}[$i]{$k2}', + }, + 'expr::arrayhash::pkg_3var' => { + desc => 'package $h{$k1}[$i]{$k2}', + setup => 'our ($i, $k1, $k2) = (0,"foo","bar");' + . 'our %h = (foo => [ { bar => 1 } ])', + code => '$h{$k1}[$i]{$k2}', + }, + + 'expr::assign::scalar_lex' => { desc => 'lexical $x = 1', setup => 'my $x', @@ -70,10 +159,87 @@ code => '($x, $y) = (1, 2)', }, + + 'expr::hash::lex_1const' => { + desc => 'lexical $hash{const}', + setup => 'my %h = ("foo" => 1)', + code => '$h{foo}', + }, + 'expr::hash::lex_2const' => { + desc => 'lexical $hash{const}{const}', + setup => 'my %h = (foo => { bar => 1 })', + code => '$h{foo}{bar}', + }, + 'expr::hash::lex_2var' => { + desc => 'lexical $hash{$k1}{$k2}', + setup => 'my ($k1,$k2) = qw(foo bar); my %h = ($k1 => { $k2 => 1 })', + code => '$h{$k1}{$k2}', + }, + 'expr::hash::ref_lex_2var' => { + desc => 'lexical $hashref->{$k1}{$k2}', + setup => 'my ($k1,$k2) = qw(foo bar); my $r = {$k1 => { $k2 => 1 }}', + code => '$r->{$k1}{$k2}', + }, + 'expr::hash::ref_lex_3const' => { + desc => 'lexical $hashref->{const}{const}{const}', + setup => 'my $r = {foo => { bar => { baz => 1 }}}', + code => '$r->{foo}{bar}{baz}', + }, + 'expr::hash::ref_expr_lex_3const' => { + desc => '(lexical expr)->{const}{const}{const}', + setup => 'my $r = {foo => { bar => { baz => 1 }}}', + code => '($r//0)->{foo}{bar}{baz}', + }, + + + 'expr::hash::pkg_1const' => { + desc => 'package $hash{const}', + setup => 'our %h = ("foo" => 1)', + code => '$h{foo}', + }, + 'expr::hash::pkg_2const' => { + desc => 'package $hash{const}{const}', + setup => 'our %h = (foo => { bar => 1 })', + code => '$h{foo}{bar}', + }, + 'expr::hash::pkg_2var' => { + desc => 'package $hash{$k1}{$k2}', + setup => 'our ($k1,$k2) = qw(foo bar); our %h = ($k1 => { $k2 => 1 })', + code => '$h{$k1}{$k2}', + }, + 'expr::hash::ref_pkg_2var' => { + desc => 'package $hashref->{$k1}{$k2}', + setup => 'our ($k1,$k2) = qw(foo bar); our $r = {$k1 => { $k2 => 1 }}', + code => '$r->{$k1}{$k2}', + }, + 'expr::hash::ref_pkg_3const' => { + desc => 'package $hashref->{const}{const}{const}', + setup => 'our $r = {foo => { bar => { baz => 1 }}}', + code => '$r->{foo}{bar}{baz}', + }, + 'expr::hash::ref_expr_pkg_3const' => { + desc => '(package expr)->{const}{const}{const}', + setup => 'our $r = {foo => { bar => { baz => 1 }}}', + code => '($r//0)->{foo}{bar}{baz}', + }, + + + 'expr::hash::exists_lex_2var' => { + desc => 'lexical exists $hash{$k1}{$k2}', + setup => 'my ($k1,$k2) = qw(foo bar); my %h = ($k1 => { $k2 => 1 });', + code => 'exists $h{$k1}{$k2}', + }, + 'expr::hash::delete_lex_2var' => { + desc => 'lexical delete $hash{$k1}{$k2}', + setup => 'my ($k1,$k2) = qw(foo bar); my %h = ($k1 => { $k2 => 1 });', + code => 'delete $h{$k1}{$k2}', + }, + + 'expr::index::utf8_postion_1' => { desc => 'index of a utf8 string, matching at position 1', setup => 'utf8::upgrade my $x = "abc"', code => 'index $x, "b"', }, -]; +]; diff --git a/t/perf/opcount.t b/t/perf/opcount.t index 659a80ee12..f3c0badcb6 100644 --- a/t/perf/opcount.t +++ b/t/perf/opcount.t @@ -17,7 +17,10 @@ BEGIN { @INC = '../lib'; } -plan 28; +use warnings; +use strict; + +plan 2249; use B (); @@ -56,8 +59,16 @@ use B (); note(sprintf "%3d %s", $counts{$_}, $_) for sort keys %counts; } + my @exp; for (sort keys %$expected_counts) { - is ($counts{$_}//0, $expected_counts->{$_}, "$desc: $_"); + my ($c, $e) = ($counts{$_}//0, $expected_counts->{$_}); + if ($c != $e) { + push @exp, "expected $e, got $c: $_"; + } + } + ok(!@exp, $desc); + if (@exp) { + diag($_) for @exp; } } } @@ -65,7 +76,7 @@ use B (); # aelem => aelemfast: a basic test that this test file works test_opcount(0, "basic aelemfast", - sub { $a[0] = 1 }, + sub { our @a; $a[0] = 1 }, { aelem => 0, aelemfast => 1, @@ -96,6 +107,7 @@ test_opcount(0, "basic aelemfast", } ); + no warnings 'void'; test_opcount(0, "bench.pl active loop", sub { for my $x (1..$ARGV[0]) { $x; } }, { @@ -115,3 +127,136 @@ test_opcount(0, "basic aelemfast", } ); } + +# +# multideref +# +# try many permutations of aggregate lookup expressions + +{ + package Foo; + + my (@agg_lex, %agg_lex, $i_lex, $r_lex); + our (@agg_pkg, %agg_pkg, $i_pkg, $r_pkg); + + my $f; + my @bodies = ('[0]', '[128]', '[$i_lex]', '[$i_pkg]', + '{foo}', '{$i_lex}', '{$i_pkg}', + ); + + for my $prefix ('$f->()->', '$agg_lex', '$agg_pkg', '$r_lex->', '$r_pkg->') + { + for my $mod ('', 'local', 'exists', 'delete') { + for my $body0 (@bodies) { + for my $body1 ('', @bodies) { + for my $body2 ('', '[2*$i_lex]') { + my $code = "$mod $prefix$body0$body1$body2"; + my $sub = "sub { $code }"; + my $coderef = eval $sub + or die "eval '$sub': $@"; + + my %c = (aelem => 0, + aelemfast => 0, + aelemfast_lex => 0, + exists => 0, + delete => 0, + helem => 0, + multideref => 0, + ); + + my $top = 'aelem'; + if ($code =~ /^\s*\$agg_...\[0\]$/) { + # we should expect aelemfast rather than multideref + $top = $code =~ /lex/ ? 'aelemfast_lex' + : 'aelemfast'; + $c{$top} = 1; + } + else { + $c{multideref} = 1; + } + + if ($body2 ne '') { + # trailing index; top aelem/exists/whatever + # node is kept + $top = $mod unless $mod eq '' or $mod eq 'local'; + $c{$top} = 1 + } + + ::test_opcount(0, $sub, $coderef, \%c); + } + } + } + } + } +} + + +# multideref: ensure that the prefix expression and trailing index +# expression are optimised (include aelemfast in those expressions) + + +test_opcount(0, 'multideref expressions', + sub { ($_[0] // $_)->[0]{2*$_[0]} }, + { + aelemfast => 2, + helem => 1, + multideref => 1, + }, + ); + +# multideref with interesting constant indices + + +test_opcount(0, 'multideref const index', + sub { $_->{1}{1.1} }, + { + helem => 0, + multideref => 1, + }, + ); + +use constant my_undef => undef; +test_opcount(0, 'multideref undef const index', + sub { $_->{+my_undef} }, + { + helem => 1, + multideref => 0, + }, + ); + +# multideref when its the first op in a subchain + +test_opcount(0, 'multideref op_other etc', + sub { $_{foo} = $_ ? $_{bar} : $_{baz} }, + { + helem => 0, + multideref => 3, + }, + ); + +# multideref without hints + +{ + no strict; + no warnings; + + test_opcount(0, 'multideref no hints', + sub { $_{foo}[0] }, + { + aelem => 0, + helem => 0, + multideref => 1, + }, + ); +} + +# exists shouldn't clash with aelemfast + +test_opcount(0, 'multideref exists', + sub { exists $_[0] }, + { + aelem => 0, + aelemfast => 0, + multideref => 1, + }, + ); |