Devel::EnforceEncapsulation headed to CPAN

ChrisDolan on 2006-10-09T20:33:48

Thanks greatly to input from Joshua ben Jore, my new module Devel::EnforceEncapsulation is headed for CPAN. This module employs a hack to block raw access to a blessed reference from outside its class. An example:

use BankAccount;
use Devel::EnforceEncapsulation;

Devel::EnforceEncapsulation->apply_to( 'BankAccount' ); my $acct = BankAccount->new(); print $acct->balance(),"\n"; # ok print $acct->{balance},"\n"; # dies


The original idea came from Ovid and Adrian Howard. Ovid has expressed an interest in creating a Class::Encapsulate which would be a production-quality version of this enforcement.


-apply_to( $obj )?

jjore on 2006-10-09T21:06:34

Have you considered applying this directly to objects? Use Symbol::gensym to get a new package, make the object ISA the new class. Potentially you have to rebless the object higher or something. I'm guessing chromatic and Ovid did something similar with Class::Trait.

Re:-apply_to( $obj )?

ChrisDolan on 2006-10-10T04:28:51

That's possible. Another way is simply:

    Devel::EnforceEncapsulation->apply_to($pkg);
    my $obj = $pkg->new();
    Devel::EnforceEncapsulation->remove_from($pkg);

The instance keeps the overload, but subsequent instantiations are not affected. The only downside to that approach is that it might blow away any deref overloads that may exist in $pkg. But that's a pretty small hazard.

Re:-apply_to( $obj )?

jjore on 2006-10-10T13:46:50

It's also not reentrant since you're fiddling with a global.

Re:-apply_to( $obj )?

ChrisDolan on 2006-10-10T13:55:39

Good point.

Changing my mind

Ovid on 2006-10-09T22:36:55

After giving it a lot of thought, I'm thinking now that Class::Encapsulate might seem like a good idea, but it's probably more trouble than it's worth. I think that your plan of applying it at runtime for development is probably a cleaner way to go.

Re:Changing my mind

ChrisDolan on 2006-10-10T04:44:06

I appreciate that. In our discussions, I've been frequently reminded of Linus Torvalds' opinion on hacks to protect the Linux userspace from stack smashing exploits:
"In short, anybody who thinks that the non-executable stack gives them any real security is very very much living in a dream world. It may catch a few attacks for old binaries that have security problems, but the basic problem is that the binaries allow you to overwrite their stacks."

In typical Linus style that statement is harsher than it needs to be, but I consider the protection of hash refs to be a similar scenario: a gesture toward encapsulation that only partially solves the problem. However, also like non-executable stacks, hash encapsulation is useful for finding cases where non-malicious code is violating convention.

Like the Linux kernel and non-executable stacks, Perl objects may someday get good protection without the encumberance of inside-outness. But unless Class::Encapsulate takes off, that might have to wait for Perl 6.

deja vu, kind of...

jhi on 2006-10-10T12:23:39

http://search.cpan.org/user/jhi/Class-Privacy-0.03/lib/Class/Privacy.pm

Re:deja vu, kind of...

ChrisDolan on 2006-10-10T16:07:33

Wow, that's very similar! I had done some searches for "overload" and "'%{}'" before writing mine and your module did not show up.

The key difference between our implementations are:
  * Mine applies externally post-facto, yours from inside the code
  * Mine allows access from sub/superclasses, yours does not (deliberately!)
  * Mine supports all of overload.pm's dereferencers, yours supports %,@,$, and &.
  * Mine allows access from anything in the same package, yours allows access only from matching package AND file
To generalize, your implementation is stricter.

I'll document all that in my SEE ALSO.

I like your comment about foolproofness. :-)