From 0b4f5fe61d03463f0f998f13f28008e61e2fe868 Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Thu, 18 Mar 2021 14:37:01 +1100 Subject: [PATCH 1/2] copy cop_features to the COP inserted into the OP tree All current features are compile-time, so this hasn't been an issue, but my current implementation of the autovivification issue checks features at runtime, which failed to work correctly if non-bundle features were set. --- cop.h | 2 ++ op.c | 1 + 2 files changed, 3 insertions(+) diff --git a/cop.h b/cop.h index b5f30bd0415f..777fb3bf7f5b 100644 --- a/cop.h +++ b/cop.h @@ -576,6 +576,8 @@ string C

, creating the package if necessary. #define CopHINTHASH_get(c) ((COPHH*)((c)->cop_hints_hash)) #define CopHINTHASH_set(c,h) ((c)->cop_hints_hash = (h)) +#define CopFEATURES_setfrom(c, o) ((c)->cop_features = (o)->cop_features) + /* =for apidoc Am|SV *|cop_hints_fetch_pvn|const COP *cop|const char *keypv|STRLEN keylen|U32 hash|U32 flags diff --git a/op.c b/op.c index 594d4ee9c329..2d2f9f0f60d7 100644 --- a/op.c +++ b/op.c @@ -9423,6 +9423,7 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o) cop->cop_seq = seq; cop->cop_warnings = DUP_WARNINGS(PL_curcop->cop_warnings); CopHINTHASH_set(cop, cophh_copy(CopHINTHASH_get(PL_curcop))); + CopFEATURES_setfrom(cop, PL_curcop); if (label) { Perl_cop_store_label(aTHX_ cop, label, strlen(label), utf8); From 76365c82b2d4d128babe60028d385cb406bd1de1 Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Thu, 18 Mar 2021 14:38:54 +1100 Subject: [PATCH 2/2] add a default-enabled autovivification feature This throws an error instead of performing autovivification when the feature is disabled. --- MANIFEST | 1 + feature.h | 52 +++++++++------ lib/feature.pm | 111 ++++++++++++++++++--------------- pp_hot.c | 16 +++++ regen/feature.pl | 14 ++++- t/lib/feature/autovivification | 50 +++++++++++++++ 6 files changed, 175 insertions(+), 69 deletions(-) create mode 100644 t/lib/feature/autovivification diff --git a/MANIFEST b/MANIFEST index fc6052425dfc..c4d2fac61f4d 100644 --- a/MANIFEST +++ b/MANIFEST @@ -5631,6 +5631,7 @@ t/lib/Devel/nodb.pm Module for t/run/switchd.t t/lib/Devel/switchd.pm Module for t/run/switchd.t t/lib/Devel/switchd_empty.pm Module for t/run/switchd.t t/lib/Devel/switchd_goto.pm Module for t/run/switchd.t +t/lib/feature/autovivification Tests for the autovivification feature t/lib/feature/bareword_filehandles Tests for enabling/disabling bareword_filehandles feature t/lib/feature/bits Tests for feature bit handling t/lib/feature/bundle Tests for feature bundles diff --git a/feature.h b/feature.h index 501bc3a3b180..540d8ff12b1b 100644 --- a/feature.h +++ b/feature.h @@ -12,24 +12,25 @@ #define HINT_FEATURE_SHIFT 26 -#define FEATURE_BAREWORD_FILEHANDLES_BIT 0x0001 -#define FEATURE_BITWISE_BIT 0x0002 -#define FEATURE___SUB___BIT 0x0004 -#define FEATURE_MYREF_BIT 0x0008 -#define FEATURE_EVALBYTES_BIT 0x0010 -#define FEATURE_FC_BIT 0x0020 -#define FEATURE_INDIRECT_BIT 0x0040 -#define FEATURE_ISA_BIT 0x0080 -#define FEATURE_MULTIDIMENSIONAL_BIT 0x0100 -#define FEATURE_POSTDEREF_QQ_BIT 0x0200 -#define FEATURE_REFALIASING_BIT 0x0400 -#define FEATURE_SAY_BIT 0x0800 -#define FEATURE_SIGNATURES_BIT 0x1000 -#define FEATURE_STATE_BIT 0x2000 -#define FEATURE_SWITCH_BIT 0x4000 -#define FEATURE_TRY_BIT 0x8000 -#define FEATURE_UNIEVAL_BIT 0x10000 -#define FEATURE_UNICODE_BIT 0x20000 +#define FEATURE_AUTOVIVIFICATION_BIT 0x0001 +#define FEATURE_BAREWORD_FILEHANDLES_BIT 0x0002 +#define FEATURE_BITWISE_BIT 0x0004 +#define FEATURE___SUB___BIT 0x0008 +#define FEATURE_MYREF_BIT 0x0010 +#define FEATURE_EVALBYTES_BIT 0x0020 +#define FEATURE_FC_BIT 0x0040 +#define FEATURE_INDIRECT_BIT 0x0080 +#define FEATURE_ISA_BIT 0x0100 +#define FEATURE_MULTIDIMENSIONAL_BIT 0x0200 +#define FEATURE_POSTDEREF_QQ_BIT 0x0400 +#define FEATURE_REFALIASING_BIT 0x0800 +#define FEATURE_SAY_BIT 0x1000 +#define FEATURE_SIGNATURES_BIT 0x2000 +#define FEATURE_STATE_BIT 0x4000 +#define FEATURE_SWITCH_BIT 0x8000 +#define FEATURE_TRY_BIT 0x10000 +#define FEATURE_UNIEVAL_BIT 0x20000 +#define FEATURE_UNICODE_BIT 0x40000 #define FEATURE_BUNDLE_DEFAULT 0 #define FEATURE_BUNDLE_510 1 @@ -167,6 +168,13 @@ FEATURE_IS_ENABLED_MASK(FEATURE_UNICODE_BIT)) \ ) +#define FEATURE_AUTOVIVIFICATION_IS_ENABLED \ + ( \ + CURRENT_FEATURE_BUNDLE <= FEATURE_BUNDLE_527 \ + || (CURRENT_FEATURE_BUNDLE == FEATURE_BUNDLE_CUSTOM && \ + FEATURE_IS_ENABLED_MASK(FEATURE_AUTOVIVIFICATION_BIT)) \ + ) + #define FEATURE_MULTIDIMENSIONAL_IS_ENABLED \ ( \ CURRENT_FEATURE_BUNDLE <= FEATURE_BUNDLE_527 \ @@ -250,6 +258,14 @@ S_magic_sethint_feature(pTHX_ SV *keysv, const char *keypv, STRLEN keylen, } return; + case 'a': + if (keylen == sizeof("feature_autovivification")-1 + && memcmp(subf+1, "utovivification", keylen - sizeof("feature_")) == 0) { + mask = FEATURE_AUTOVIVIFICATION_BIT; + break; + } + return; + case 'b': if (keylen == sizeof("feature_bareword_filehandles")-1 && memcmp(subf+1, "areword_filehandles", keylen - sizeof("feature_")) == 0) { diff --git a/lib/feature.pm b/lib/feature.pm index c57c75da8c95..7bfd2b42cf5a 100644 --- a/lib/feature.pm +++ b/lib/feature.pm @@ -24,18 +24,19 @@ our %feature = ( unicode_eval => 'feature_unieval', declared_refs => 'feature_myref', unicode_strings => 'feature_unicode', + autovivification => 'feature_autovivification', multidimensional => 'feature_multidimensional', bareword_filehandles => 'feature_bareword_filehandles', ); our %feature_bundle = ( - "5.10" => [qw(bareword_filehandles indirect multidimensional say state switch)], - "5.11" => [qw(bareword_filehandles indirect multidimensional say state switch unicode_strings)], - "5.15" => [qw(bareword_filehandles current_sub evalbytes fc indirect multidimensional say state switch unicode_eval unicode_strings)], - "5.23" => [qw(bareword_filehandles current_sub evalbytes fc indirect multidimensional postderef_qq say state switch unicode_eval unicode_strings)], - "5.27" => [qw(bareword_filehandles bitwise current_sub evalbytes fc indirect multidimensional postderef_qq say state switch unicode_eval unicode_strings)], - "all" => [qw(bareword_filehandles bitwise current_sub declared_refs evalbytes fc indirect isa multidimensional postderef_qq refaliasing say signatures state switch try unicode_eval unicode_strings)], - "default" => [qw(bareword_filehandles indirect multidimensional)], + "5.10" => [qw(autovivification bareword_filehandles indirect multidimensional say state switch)], + "5.11" => [qw(autovivification bareword_filehandles indirect multidimensional say state switch unicode_strings)], + "5.15" => [qw(autovivification bareword_filehandles current_sub evalbytes fc indirect multidimensional say state switch unicode_eval unicode_strings)], + "5.23" => [qw(autovivification bareword_filehandles current_sub evalbytes fc indirect multidimensional postderef_qq say state switch unicode_eval unicode_strings)], + "5.27" => [qw(autovivification bareword_filehandles bitwise current_sub evalbytes fc indirect multidimensional postderef_qq say state switch unicode_eval unicode_strings)], + "all" => [qw(autovivification bareword_filehandles bitwise current_sub declared_refs evalbytes fc indirect isa multidimensional postderef_qq refaliasing say signatures state switch try unicode_eval unicode_strings)], + "default" => [qw(autovivification bareword_filehandles indirect multidimensional)], ); $feature_bundle{"5.12"} = $feature_bundle{"5.11"}; @@ -418,6 +419,14 @@ C are caught by executing the body of the C block. For more information, see L. +=head2 The 'autovivification' feature. + +This feature enables autovivification of references. It is enabled by +default, but can be turned off to disable it. + +This feature is available under this name from Perl 5.34 onwards, in +previous versions it was simple on all the time. + =head1 FEATURE BUNDLES It's possible to load multiple features together, using @@ -431,64 +440,68 @@ The following feature bundles are available: bundle features included --------- ----------------- :default indirect multidimensional - bareword_filehandles + bareword_filehandles autovivification + + :5.10 autovivification bareword_filehandles + indirect multidimensional say state switch + + :5.12 autovivification bareword_filehandles + indirect multidimensional say state switch + unicode_strings - :5.10 bareword_filehandles indirect + :5.14 autovivification bareword_filehandles + indirect multidimensional say state switch + unicode_strings + + :5.16 autovivification bareword_filehandles + current_sub evalbytes fc indirect multidimensional say state switch + unicode_eval unicode_strings - :5.12 bareword_filehandles indirect + :5.18 autovivification bareword_filehandles + current_sub evalbytes fc indirect multidimensional say state switch - unicode_strings + unicode_eval unicode_strings - :5.14 bareword_filehandles indirect + :5.20 autovivification bareword_filehandles + current_sub evalbytes fc indirect multidimensional say state switch - unicode_strings + unicode_eval unicode_strings - :5.16 bareword_filehandles current_sub evalbytes - fc indirect multidimensional say state - switch unicode_eval unicode_strings + :5.22 autovivification bareword_filehandles + current_sub evalbytes fc indirect + multidimensional say state switch + unicode_eval unicode_strings - :5.18 bareword_filehandles current_sub evalbytes - fc indirect multidimensional say state + :5.24 autovivification bareword_filehandles + current_sub evalbytes fc indirect + multidimensional postderef_qq say state switch unicode_eval unicode_strings - :5.20 bareword_filehandles current_sub evalbytes - fc indirect multidimensional say state + :5.26 autovivification bareword_filehandles + current_sub evalbytes fc indirect + multidimensional postderef_qq say state switch unicode_eval unicode_strings - :5.22 bareword_filehandles current_sub evalbytes - fc indirect multidimensional say state + :5.28 autovivification bareword_filehandles + bitwise current_sub evalbytes fc indirect + multidimensional postderef_qq say state switch unicode_eval unicode_strings - :5.24 bareword_filehandles current_sub evalbytes - fc indirect multidimensional postderef_qq - say state switch unicode_eval - unicode_strings - - :5.26 bareword_filehandles current_sub evalbytes - fc indirect multidimensional postderef_qq - say state switch unicode_eval - unicode_strings - - :5.28 bareword_filehandles bitwise current_sub - evalbytes fc indirect multidimensional - postderef_qq say state switch unicode_eval - unicode_strings - - :5.30 bareword_filehandles bitwise current_sub - evalbytes fc indirect multidimensional - postderef_qq say state switch unicode_eval - unicode_strings + :5.30 autovivification bareword_filehandles + bitwise current_sub evalbytes fc indirect + multidimensional postderef_qq say state + switch unicode_eval unicode_strings - :5.32 bareword_filehandles bitwise current_sub - evalbytes fc indirect multidimensional - postderef_qq say state switch unicode_eval - unicode_strings + :5.32 autovivification bareword_filehandles + bitwise current_sub evalbytes fc indirect + multidimensional postderef_qq say state + switch unicode_eval unicode_strings - :5.34 bareword_filehandles bitwise current_sub - evalbytes fc indirect multidimensional - postderef_qq say state switch unicode_eval - unicode_strings + :5.34 autovivification bareword_filehandles + bitwise current_sub evalbytes fc indirect + multidimensional postderef_qq say state + switch unicode_eval unicode_strings The C<:default> bundle represents the feature set that is enabled before any C or C declaration. diff --git a/pp_hot.c b/pp_hot.c index 5119638b9ff6..86e4ffcefe9a 100644 --- a/pp_hot.c +++ b/pp_hot.c @@ -35,6 +35,7 @@ #define PERL_IN_PP_HOT_C #include "perl.h" #include "regcomp.h" +#include "feature.h" /* Hot code. */ @@ -5434,6 +5435,21 @@ Perl_vivify_ref(pTHX_ SV *sv, U32 to_what) if (!SvOK(sv)) { if (SvREADONLY(sv)) Perl_croak_no_modify(); + if (!FEATURE_AUTOVIVIFICATION_IS_ENABLED) { + const char *type = "an unknown"; + switch (to_what) { + case OPpDEREF_SV: + type = "a scalar"; + break; + case OPpDEREF_AV: + type = "an array"; + break; + case OPpDEREF_HV: + type = "a hash"; + break; + } + Perl_croak(aTHX_ "Attempt to autovivify %s reference with autovivification disabled", type); + } prepare_SV_for_RV(sv); switch (to_what) { case OPpDEREF_SV: diff --git a/regen/feature.pl b/regen/feature.pl index 2db99ef95736..7aab7b97a1a0 100755 --- a/regen/feature.pl +++ b/regen/feature.pl @@ -41,6 +41,7 @@ BEGIN multidimensional => 'multidimensional', bareword_filehandles => 'bareword_filehandles', try => 'try', + autovivification => 'autovivification', ); # NOTE: If a feature is ever enabled in a non-contiguous range of Perl @@ -50,7 +51,8 @@ BEGIN # 5.odd implies the next 5.even, but an explicit 5.even can override it. # features bundles -use constant V5_9_5 => sort qw{say state switch indirect multidimensional bareword_filehandles}; +use constant V5_9_5 => + sort qw{say state switch indirect multidimensional bareword_filehandles autovivification}; use constant V5_11 => sort ( +V5_9_5, qw{unicode_strings} ); use constant V5_15 => sort ( +V5_11, qw{unicode_eval evalbytes current_sub fc} ); use constant V5_23 => sort ( +V5_15, qw{postderef_qq} ); @@ -58,7 +60,7 @@ BEGIN my %feature_bundle = ( all => [ sort keys %feature ], - default => [ qw{indirect multidimensional bareword_filehandles} ], + default => [ qw{indirect multidimensional bareword_filehandles autovivification} ], # using 5.9.5 features bundle "5.9.5" => [ +V5_9_5 ], "5.10" => [ +V5_9_5 ], @@ -824,6 +826,14 @@ =head2 The 'try' feature. For more information, see L. +=head2 The 'autovivification' feature. + +This feature enables autovivification of references. It is enabled by +default, but can be turned off to disable it. + +This feature is available under this name from Perl 5.34 onwards, in +previous versions it was simple on all the time. + =head1 FEATURE BUNDLES It's possible to load multiple features together, using diff --git a/t/lib/feature/autovivification b/t/lib/feature/autovivification new file mode 100644 index 000000000000..f36f5d6cd986 --- /dev/null +++ b/t/lib/feature/autovivification @@ -0,0 +1,50 @@ +Test the autovivification feature + +__END__ +# NAME hash autovivification +use feature 'say'; # ensure the autoviv test depends on cop_warnings, not cop_hints +my $x; +$x->{foo} = 1; +no feature 'autovivification'; +undef $x; +$x->{foo} = 1; +EXPECT +Attempt to autovivify a hash reference with autovivification disabled at - line 6. +######## +# NAME hash autovivification (subhash) +use feature 'say'; # ensure the autoviv test depends on cop_warnings, not cop_hints +my $x = {}; +$x->{foo} = 1; +$x->{foo}{bar} = 1; +no feature 'autovivification'; +$x = {}; +$x->{foo}{bar} = 1; +EXPECT +Attempt to autovivify a hash reference with autovivification disabled at - line 7. +######## +# NAME array autovivification +my $x; +$x->[0] = 1; +undef $x; +no feature 'autovivification'; +$x->[0] = 2; +EXPECT +Attempt to autovivify an array reference with autovivification disabled at - line 5. +######## +# NAME array autovivification (sub array) +my $x = {}; +$x->{foo}[0] = 1; +$x = {}; +no feature 'autovivification'; +$x->{foo}[0] = 2; +EXPECT +Attempt to autovivify an array reference with autovivification disabled at - line 5. +######## +# NAME scalar autovivification +my $x; +$$x = 1; +undef $x; +no feature 'autovivification'; +$$x = 2; +EXPECT +Attempt to autovivify a scalar reference with autovivification disabled at - line 5.