Skip to content
Browse files

Reset overloading magic in the references after reblessing an instance.

Before 5.10.0 and 5.8.9 the overloading flag was stored on the reference rather
than the referent. Despite the fact that objects can only be accessed via
references (even internally), the referent actually knows that it's blessed,
not the references. So taking a new, unrelated, reference to it gives an
object. However, the overloading-or-not flag was on the reference prior to
5.10, and taking a new reference didn't (use to) copy it (prior to 5.8.9).

To fix that on 5.8.8 and older, we hook in just after the role application
reblessed the instance (thereby changing the overloading state of the passed in
reference) and brute force search for the other references to the same object
to perform the same change on them.
  • Loading branch information...
1 parent 66dfc4e commit 172eeb18b3b41e79c38a193da4f631657d46b2e4 @rafl committed
View
61 WithOverloading.xs
@@ -0,0 +1,61 @@
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+static void
+S_reset_amagic (pTHX_ SV *rv, const bool on)
+{
+ /* It is assumed that you've already turned magic on/off on rv */
+
+ SV *sva;
+ SV *const target = SvRV (rv);
+
+ /* Less 1 for the reference we've already dealt with. */
+ U32 how_many = SvREFCNT (target) - 1;
+ MAGIC *mg;
+
+ if (SvMAGICAL (target) && (mg = mg_find (target, PERL_MAGIC_backref))) {
+ /* Back referneces also need to be found, but aren't part of the target's reference count. */
+ how_many += 1 + av_len ((AV *)mg->mg_obj);
+ }
+
+ if (!how_many) {
+ /* There was only 1 reference to this object. */
+ return;
+ }
+
+ for (sva = PL_sv_arenaroot; sva; sva = (SV *)SvANY (sva)) {
+ register const SV *const svend = &sva[SvREFCNT (sva)];
+ register SV *sv;
+ for (sv = sva + 1; sv < svend; ++sv) {
+ if (SvTYPE (sv) != SVTYPEMASK
+ && ((sv->sv_flags & SVf_ROK) == SVf_ROK)
+ && SvREFCNT (sv)
+ && SvRV (sv) == target
+ && sv != rv) {
+ if (on) {
+ SvAMAGIC_on (sv);
+ }
+ else {
+ SvAMAGIC_off (sv);
+ }
+
+ if (--how_many == 0) {
+ /* We have found them all. */
+ return;
+ }
+ }
+ }
+ }
+}
+
+MODULE = MooseX::Role::WithOverloading PACKAGE = MooseX::Role::WithOverloading::Meta::Role::Application::FixOverloadedRefs
+
+void
+reset_amagic (rv)
+ SV *rv
+ CODE:
+ if (Gv_AMG (SvSTASH (SvRV (rv))) && !SvAMAGIC (rv)) {
+ SvAMAGIC_on (rv);
+ S_reset_amagic (rv, TRUE);
+ }
View
1 dist.ini
@@ -9,6 +9,7 @@ copyright_holder = Florian Ragwitz
Moose = 0.90
MooseX::Types = 0
aliased = 0
+XSLoader = 0
namespace::clean = 0
namespace::autoclean = 0.09
View
3 lib/MooseX/Role/WithOverloading.pm
@@ -1,6 +1,7 @@
package MooseX::Role::WithOverloading;
# ABSTRACT: Roles which support overloading
+use XSLoader;
use Moose::Role ();
use Moose::Exporter;
use Moose::Util::MetaRole;
@@ -11,6 +12,8 @@ use aliased 'MooseX::Role::WithOverloading::Meta::Role::Application::ToInstance'
use namespace::clean;
+XSLoader::load(__PACKAGE__, $VERSION);
+
Moose::Exporter->setup_import_methods(also => 'Moose::Role');
sub init_meta {
View
2 lib/MooseX/Role/WithOverloading/Meta/Role.pm
@@ -10,5 +10,3 @@ has '+composition_class_roles' => (
);
1;
-
-
View
5 lib/MooseX/Role/WithOverloading/Meta/Role/Application/Composite/ToInstance.pm
@@ -4,6 +4,9 @@ package MooseX::Role::WithOverloading::Meta::Role::Application::Composite::ToIns
use Moose::Role;
use namespace::autoclean;
-with 'MooseX::Role::WithOverloading::Meta::Role::Application::Composite';
+with qw(
+ MooseX::Role::WithOverloading::Meta::Role::Application::Composite
+ MooseX::Role::WithOverloading::Meta::Role::Application::FixOverloadedRefs
+);
1;
View
12 lib/MooseX/Role/WithOverloading/Meta/Role/Application/FixOverloadedRefs.pm
@@ -0,0 +1,12 @@
+package MooseX::Role::WithOverloading::Meta::Role::Application::FixOverloadedRefs;
+
+use Moose::Role;
+use namespace::autoclean;
+
+if ($] < 5.008009) {
+ after apply => sub {
+ reset_amagic($_[2]);
+ };
+}
+
+1;
View
5 lib/MooseX/Role/WithOverloading/Meta/Role/Application/ToInstance.pm
@@ -4,6 +4,9 @@ package MooseX::Role::WithOverloading::Meta::Role::Application::ToInstance;
use Moose::Role;
use namespace::autoclean;
-with 'MooseX::Role::WithOverloading::Meta::Role::Application';
+with qw(
+ MooseX::Role::WithOverloading::Meta::Role::Application
+ MooseX::Role::WithOverloading::Meta::Role::Application::FixOverloadedRefs
+);
1;

0 comments on commit 172eeb1

Please sign in to comment.
Something went wrong with that request. Please try again.