From: Vincent Pit Date: Wed, 25 Feb 2009 10:33:50 +0000 (+0100) Subject: Make the package and method names passed to callbacks readonly X-Git-Tag: v0.02~4 X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FLexical-Types.git;a=commitdiff_plain;h=09c7a37f3be8034a8c04c2011208724e04441d83 Make the package and method names passed to callbacks readonly --- diff --git a/MANIFEST b/MANIFEST index 0d80d12..d556b64 100644 --- a/MANIFEST +++ b/MANIFEST @@ -10,6 +10,7 @@ t/00-load.t t/10-args.t t/11-integrate.t t/12-padsv.t +t/13-ro.t t/20-object.t t/21-tie.t t/22-magic.t diff --git a/Types.xs b/Types.xs index 9baa8da..2e8bda4 100644 --- a/Types.xs +++ b/Types.xs @@ -121,9 +121,9 @@ STATIC OP *lt_pp_padsv(pTHX) { PUSHMARK(SP); EXTEND(SP, 3); - PUSHs(sv_2mortal(newSVsv(oi->type_pkg))); + PUSHs(oi->type_pkg); PUSHs(sv); - PUSHs(sv_2mortal(newSVsv(oi->orig_pkg))); + PUSHs(oi->orig_pkg); PUTBACK; items = call_sv(oi->type_meth, G_ARRAY | G_METHOD); @@ -160,7 +160,7 @@ STATIC OP *lt_pp_padsv(pTHX) { * pp_padsv, but much less than if we would have set PL_ppaddr[OP_PADSV] * globally. */ -STATIC U32 lt_TYPEDSCALAR_hash = 0; +STATIC SV *lt_default_meth = NULL; STATIC OP *(*lt_old_ck_padany)(pTHX_ OP *) = 0; @@ -175,13 +175,12 @@ STATIC OP *lt_ck_padany(pTHX_ OP *o) { stash = PL_in_my_stash; if (stash && (hint = lt_hint())) { SV *orig_pkg = newSVpvn(HvNAME_get(stash), HvNAMELEN_get(stash)); - SV *orig_meth = newSVpvn_share("TYPEDSCALAR", 11, lt_TYPEDSCALAR_hash); + SV *orig_meth = lt_default_meth; SV *type_pkg = orig_pkg; SV *type_meth = orig_meth; SV *code = INT2PTR(SV *, SvUVX(hint)); SvREADONLY_on(orig_pkg); - SvREADONLY_on(orig_meth); if (code) { int items; @@ -209,12 +208,16 @@ STATIC OP *lt_ck_padany(pTHX_ OP *o) { SV *rsv; if (items > 1) { rsv = POPs; - if (SvOK(rsv)) + if (SvOK(rsv)) { type_meth = newSVsv(rsv); + SvREADONLY_on(type_meth); + } } rsv = POPs; - if (SvOK(rsv)) + if (SvOK(rsv)) { type_pkg = newSVsv(rsv); + SvREADONLY_on(type_pkg); + } } PUTBACK; @@ -222,8 +225,11 @@ STATIC OP *lt_ck_padany(pTHX_ OP *o) { LEAVE; } - lt_old_pp_padsv = PL_ppaddr[OP_PADSV]; + if (type_meth == orig_meth) + SvREFCNT_inc(orig_meth); lt_map_store(o, orig_pkg, type_pkg, type_meth, lt_old_pp_padsv); + + lt_old_pp_padsv = PL_ppaddr[OP_PADSV]; PL_ppaddr[OP_PADSV] = lt_pp_padsv; } @@ -249,7 +255,8 @@ PROTOTYPES: DISABLE BOOT: { if (!lt_initialized++) { - PERL_HASH(lt_TYPEDSCALAR_hash, "TYPEDSCALAR", 11); + lt_default_meth = newSVpvn("TYPEDSCALAR", 11); + SvREADONLY_on(lt_default_meth); PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__); lt_op_map = newHV(); diff --git a/t/13-ro.t b/t/13-ro.t new file mode 100644 index 0000000..c8e9f7d --- /dev/null +++ b/t/13-ro.t @@ -0,0 +1,67 @@ +#!perl -T + +use strict; +use warnings; + +use Test::More tests => 4; + +sub Str::TYPEDSCALAR { } + +sub Str1::TYPEDSCALAR { + $_[0] = 'dongs'; + (); +} + +sub Str2::TYPEDSCALAR { + $_[2] = 'hlagh'; + (); +} + +sub ro_re { + my ($file, $line) = map quotemeta, @_; + $file = '\\(eval \\d+\\)' unless $file; + return qr/^Modification of a read-only value attempted at $file line $line/; +} + +sub maybe_warn { + diag 'This will throw two warnings' if $] >= 5.008008 and $] < 5.009; +} + +{ + maybe_warn(); + local $@; + eval q! + use Lexical::Types as => sub { $_[0] = 'dongs'; () }; + my Str $x; + !; + like $@, ro_re('', 2), '$_[0] in initializer is read only'; +} + +SKIP: { + skip 'Kinda broken on old 5.8.x' => 1 if $] <= 5.008006; + maybe_warn(); + local $@; + eval q! + use Lexical::Types as => sub { $_[1] = 'hlagh'; () }; + my Str $x; + !; + like $@, ro_re('', 2), '$_[1] in initializer is read only'; +} + +{ + local $@; + eval q[ + use Lexical::Types; + my Str1 $x; + ]; + like $@, ro_re($0, 11), '$_[0] in initializer is read only'; +} + +{ + local $@; + eval q[ + use Lexical::Types; + my Str2 $x; + ]; + like $@, ro_re($0, 16), '$_[2] in initializer is read only'; +}