From: Vincent Pit Date: Wed, 17 Jun 2009 13:06:24 +0000 (+0200) Subject: Don't autovivify in keys/values X-Git-Tag: v0.02~7 X-Git-Url: http://git.vpit.fr/?a=commitdiff_plain;h=66a75622a9e10465ef5117c5c68a78e493406234;p=perl%2Fmodules%2Fautovivification.git Don't autovivify in keys/values --- diff --git a/MANIFEST b/MANIFEST index 9af2be8..0b50ebd 100644 --- a/MANIFEST +++ b/MANIFEST @@ -10,6 +10,7 @@ samples/hash2array.pl t/00-load.t t/20-hash.t t/21-array.t +t/23-hash-specific.t t/30-scope.t t/91-pod.t t/92-pod-coverage.t diff --git a/autovivification.xs b/autovivification.xs index c0ff27c..b307fcb 100644 --- a/autovivification.xs +++ b/autovivification.xs @@ -378,9 +378,28 @@ deref: return CALL_FPTR(oi.old_pp)(aTHX); } -/* ... pp_root (exists,delete) ............................................. */ +/* ... pp_root (exists,delete,keys,values) ................................. */ -STATIC OP *a_pp_root(pTHX) { +STATIC OP *a_pp_root_unop(pTHX) { + a_op_info oi; + dSP; + + if (!a_defined(TOPs)) { + POPs; + /* Can only be reached by keys or values */ + if (GIMME_V == G_SCALAR) { + dTARGET; + PUSHi(0); + } + RETURN; + } + + a_map_fetch(PL_op, &oi); + + return CALL_FPTR(oi.old_pp)(aTHX); +} + +STATIC OP *a_pp_root_binop(pTHX) { a_op_info oi; dSP; @@ -530,21 +549,36 @@ STATIC OP *a_ck_rv2xv(pTHX_ OP *o) { STATIC OP *(*a_old_ck_exists)(pTHX_ OP *) = 0; STATIC OP *(*a_old_ck_delete)(pTHX_ OP *) = 0; +STATIC OP *(*a_old_ck_keys) (pTHX_ OP *) = 0; +STATIC OP *(*a_old_ck_values)(pTHX_ OP *) = 0; STATIC OP *a_ck_root(pTHX_ OP *o) { OP * (*old_ck)(pTHX_ OP *o) = 0; + OP * (*new_pp)(pTHX) = 0; bool enabled = FALSE; UV hint = a_hint(); switch (o->op_type) { case OP_EXISTS: old_ck = a_old_ck_exists; + new_pp = a_pp_root_binop; enabled = hint & A_HINT_EXISTS; break; case OP_DELETE: old_ck = a_old_ck_delete; + new_pp = a_pp_root_binop; enabled = hint & A_HINT_DELETE; break; + case OP_KEYS: + old_ck = a_old_ck_keys; + new_pp = a_pp_root_unop; + enabled = hint & A_HINT_FETCH; + break; + case OP_VALUES: + old_ck = a_old_ck_values; + new_pp = a_pp_root_unop; + enabled = hint & A_HINT_FETCH; + break; } o = CALL_FPTR(old_ck)(aTHX_ o); @@ -552,7 +586,7 @@ STATIC OP *a_ck_root(pTHX_ OP *o) { if (enabled) { a_map_set_root(o, hint | A_HINT_DEREF); a_map_store(o, o->op_ppaddr, hint); - o->op_ppaddr = a_pp_root; + o->op_ppaddr = new_pp; } else { a_map_set_root(o, 0); } @@ -586,20 +620,27 @@ BOOT: PL_check[OP_PADANY] = MEMBER_TO_FPTR(a_ck_padany); a_old_ck_padsv = PL_check[OP_PADSV]; PL_check[OP_PADSV] = MEMBER_TO_FPTR(a_ck_padsv); + a_old_ck_aelem = PL_check[OP_AELEM]; PL_check[OP_AELEM] = MEMBER_TO_FPTR(a_ck_deref); a_old_ck_helem = PL_check[OP_HELEM]; PL_check[OP_HELEM] = MEMBER_TO_FPTR(a_ck_deref); a_old_ck_rv2sv = PL_check[OP_RV2SV]; PL_check[OP_RV2SV] = MEMBER_TO_FPTR(a_ck_deref); + a_old_ck_rv2av = PL_check[OP_RV2AV]; PL_check[OP_RV2AV] = MEMBER_TO_FPTR(a_ck_rv2xv); a_old_ck_rv2hv = PL_check[OP_RV2HV]; PL_check[OP_RV2HV] = MEMBER_TO_FPTR(a_ck_rv2xv); + a_old_ck_exists = PL_check[OP_EXISTS]; PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root); a_old_ck_delete = PL_check[OP_DELETE]; PL_check[OP_DELETE] = MEMBER_TO_FPTR(a_ck_root); + a_old_ck_keys = PL_check[OP_KEYS]; + PL_check[OP_KEYS] = MEMBER_TO_FPTR(a_ck_root); + a_old_ck_values = PL_check[OP_VALUES]; + PL_check[OP_VALUES] = MEMBER_TO_FPTR(a_ck_root); stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1); newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT)); diff --git a/lib/autovivification.pm b/lib/autovivification.pm index 825eb99..ac9bd98 100644 --- a/lib/autovivification.pm +++ b/lib/autovivification.pm @@ -64,8 +64,8 @@ Enables the features given in C<@opts>, which can be : C<'fetch'> -Turn off autovivification for rvalue dereferencing expressions, such as C<< $value = $hashref->{key}[$idx]{$field} >>. -C is returned when the expression would have autovivified. +Turn off autovivification for rvalue dereferencing expressions, such as C<< $value = $hashref->{key}[$idx]{$field} >>, C<< keys %{$hashref->{key}} >> or C<< values %{$hashref->{key}} >>. +When the expression would have autovivified, C is returned for a plain fetch, while C and C return C<0> in scalar context and the empty list in list context. =item * diff --git a/t/23-hash-specific.t b/t/23-hash-specific.t new file mode 100644 index 0000000..06263bb --- /dev/null +++ b/t/23-hash-specific.t @@ -0,0 +1,89 @@ +#!perl + +use strict; +use warnings; + +use Test::More tests => 6 * 3 * 56; + +use lib 't/lib'; +use autovivification::TestCases; + +while () { + 1 while chomp; + next unless /#/; + testcase_ok($_, '%'); +} + +__DATA__ + +--- keys --- + +$x # keys %$x # '', 0, { } +$x # keys %$x # '', 0, undef # +$x # keys %$x # '', 0, undef # +fetch +$x # keys %$x # '', 0, { } # +exists +$x # keys %$x # '', 0, { } # +delete +$x # keys %$x # '', 0, { } # +store + +$x # keys %$x # qr/^Reference vivification forbidden/, undef, undef # +strict +fetch +$x # keys %$x # '', 0, { } # +strict +exists +$x # keys %$x # '', 0, { } # +strict +delete +$x # keys %$x # '', 0, { } # +strict +store + +$x # [ keys %$x ] # '', [ ], { } +$x # [ keys %$x ] # '', [ ], undef # +$x # [ keys %$x ] # '', [ ], undef # +fetch +$x # [ keys %$x ] # '', [ ], { } # +exists +delete +store + +$x # keys %{$x->{a}} # '', 0, { a => { } } +$x # keys %{$x->{a}} # '', 0, undef # +$x # keys %{$x->{a}} # '', 0, undef # +fetch +$x # keys %{$x->{a}} # '', 0, { a => { } } # +exists +$x # keys %{$x->{a}} # '', 0, { a => { } } # +delete +$x # keys %{$x->{a}} # '', 0, { a => { } } # +store + +$x # keys %{$x->{a}} # qr/^Reference vivification forbidden/, undef, undef # +strict +fetch +$x # keys %{$x->{a}} # '', 0, { a => { } } # +strict +exists +$x # keys %{$x->{a}} # '', 0, { a => { } } # +strict +delete +$x # keys %{$x->{a}} # '', 0, { a => { } } # +strict +store + +$x # [ keys %{$x->{a}} ] # '', [ ], { a => { } } +$x # [ keys %{$x->{a}} ] # '', [ ], undef # +$x # [ keys %{$x->{a}} ] # '', [ ], undef # +fetch +$x # [ keys %{$x->{a}} ] # '', [ ], { a => { } } # +exists +delete +store + +--- values --- + +$x # values %$x # '', 0, { } +$x # values %$x # '', 0, undef # +$x # values %$x # '', 0, undef # +fetch +$x # values %$x # '', 0, { } # +exists +$x # values %$x # '', 0, { } # +delete +$x # values %$x # '', 0, { } # +store + +$x # values %$x # qr/^Reference vivification forbidden/, undef, undef # +strict +fetch +$x # values %$x # '', 0, { } # +strict +exists +$x # values %$x # '', 0, { } # +strict +delete +$x # values %$x # '', 0, { } # +strict +store + +$x # [ values %$x ] # '', [ ], { } +$x # [ values %$x ] # '', [ ], undef # +$x # [ values %$x ] # '', [ ], undef # +fetch +$x # [ values %$x ] # '', [ ], { } # +exists +delete +store + +$x # values %{$x->{a}} # '', 0, { a => { } } +$x # values %{$x->{a}} # '', 0, undef # +$x # values %{$x->{a}} # '', 0, undef # +fetch +$x # values %{$x->{a}} # '', 0, { a => { } } # +exists +$x # values %{$x->{a}} # '', 0, { a => { } } # +delete +$x # values %{$x->{a}} # '', 0, { a => { } } # +store + +$x # values %{$x->{a}} # qr/^Reference vivification forbidden/, undef, undef # +strict +fetch +$x # values %{$x->{a}} # '', 0, { a => { } } # +strict +exists +$x # values %{$x->{a}} # '', 0, { a => { } } # +strict +delete +$x # values %{$x->{a}} # '', 0, { a => { } } # +strict +store + +$x # [ values %{$x->{a}} ] # '', [ ], { a => { } } +$x # [ values %{$x->{a}} ] # '', [ ], undef # +$x # [ values %{$x->{a}} ] # '', [ ], undef # +fetch +$x # [ values %{$x->{a}} ] # '', [ ], { a => { } } # +exists +delete +store