]> git.vpit.fr Git - perl/modules/autovivification.git/commitdiff
Don't autovivify in keys/values
authorVincent Pit <vince@profvince.com>
Wed, 17 Jun 2009 13:06:24 +0000 (15:06 +0200)
committerVincent Pit <vince@profvince.com>
Wed, 17 Jun 2009 13:06:24 +0000 (15:06 +0200)
MANIFEST
autovivification.xs
lib/autovivification.pm
t/23-hash-specific.t [new file with mode: 0644]

index 9af2be8a924180cfcbc45792b381fe1380cb0821..0b50ebdc463a03202a086cbba0416af5ede806c1 100644 (file)
--- 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
index c0ff27c00b878c689784945dcb42a3475fa1d3af..b307fcbaf205f58c0e9d9a934e6ee0ea73f55a29 100644 (file)
@@ -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));
index 825eb9976dceefb7d1cff06d9b101fc3b1091e22..ac9bd98f0231308244a657d081563fcb0838bc45 100644 (file)
@@ -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<undef> 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<undef> is returned for a plain fetch, while C<keys> and C<values> 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 (file)
index 0000000..06263bb
--- /dev/null
@@ -0,0 +1,89 @@
+#!perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 6 * 3 * 56;
+
+use lib 't/lib';
+use autovivification::TestCases;
+
+while (<DATA>) {
+ 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