Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery

Managing objects/references across system boundaries

by Corion (Pope)
on Oct 23, 2009 at 14:58 UTC ( #802912=perlquestion: print w/replies, xml ) Need Help??
Corion has asked for the wisdom of the Perl Monks concerning the following question:

I'm currently implementing an object/application bridge between Perl and Javascript, using the MozRepl plugin for Mozilla FireFox and the MozRepl module. The goal is to implement a transparent bridge that lets you work with the Javascript objects as if they were native Perl objects, just like you can treat Win32::OLE objects as if they were native Perl objects.

The setup to achieve this is very simple. The bridge module I wrote is tentatively named MozRepl::RemoteObject. For the purpose of this discussion, the JS side of the bridge returns an integer when it would return an object. It looks like this in Javascript: = function(obj) { if (! repl.linkedVars) { repl.linkedVars = {}; repl.linkedIdNext = 1; }; if (obj) { repl.linkedVars[ repl.linkedIdNext ] = obj; return repl.linkedIdNext++; } else { return undefined }

It could be written like this in Perl:

sub link { my ($repl,$obj) = @_; if (! $repl->{linkedVars}) { $repl->{linkedVars} = {}; $repl->{linkedIdNext} = 1; }; if (defined $obj) { $repl->{linkedVars}->{ $repl->{linkedIdNext} } = obj; return $repl->{linkedIdNext}++; } else { return undef }; };

The Perl implementation of the objects is very simple:

package MozRepl::RemoteObject; sub new { my ($class, $id) = @_; my $self = { id => $id, }; bless $self, $class; }; sub expr { my ($class, $js) = @_; return $class->new( javascript_eval($js)); # assuming that it retu +rns an object };

Releasing objects is easily done by making the DESTROY method of the object release the Javascript object of the same id:

sub DESTROY { my ($self) = @_; if ($self->{id}) { $self->expr("repl.breakLink($self->{id})"); }; };

and the JS side of the bridge implements repl.breakLink() as:

repl.breakLink = function(id) { delete repl.linkedVars[ id ]; }

which can be translated to Perl as

sub breakLink { my ($repl,$id) = @_; delete $repl->{linkedVars}->{id}; };

This all works fine and dandy, objects get created, handed around within Perl space, and released once all Perl code is done with them.

But (and you knew there was this small niggle), there is one small problem:

my $doc_a = MozRepl::RemoteObject->expr('window.document'); # id 42 my $doc_b = MozRepl::RemoteObject->expr('window.document'); # id 43

creates two different references to the same object, and refaddr claims I have two different objects. During the writing of this node, I've come up with three solutions, one I find good, one I find bad and one I find ugly. I'd like your input on the three solutions and potentially other solutions that allow me to emulate checking the object identity in Perl without having to reimplement reference counting.

  1. The Good:
    I just overload == to use the Javascript === operator, which denotes object identity. This will mean one roundtrip per comparison, but such is life. It also doesn't solve the problem of Scalar::Util::refaddr claiming that $doc_a and $doc_b are different objects when on the JS side of things, they are identical.
  2. The Bad:
    I maintain a second mapping on the Javascript side from objects to ids, and return the same id for an object whenever it is still alive and requested again. This means that I also have to (re)implement reference counting on either the Javascript or the Perl side of things so I only break the link when the last returned reference has been released. As my history of working with Delphi, COM and Perl has taught me, getting reference counting right is very, very hard.
  3. The Ugly:
    I implement the id-to-object mapping on the Javascript side of things and on the Perl side of things, and hand out references to the same object whenever my Javascript hands me the same number. This makes refaddr happy, at the cost of me having to implement refcounting on the Perl side of things and maintaining two object <-> id mappings, one on each side of the bridge.

My current plan is to go with the first option resp. to leave object identity well alone, as I currently see few reasons to actually use object identity. Of course, this stance makes it impossible to "inherit" from such a Javascript object by using an inside-out object approach, because the object identity is not maintained.

PS: If you want to look at the real code instead of my mock example, it lives in a github repository, and the main code I write this for is WWW::Mechanize::FireFox. Both are WWW::Mechanize::FireFox is not yet on CPAN because they haven't even reached CPAN release quality.

Update: Linked to released module, fixed markup

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://802912]
Approved by marto
Front-paged by marto
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (4)
As of 2018-04-21 07:08 GMT
Find Nodes?
    Voting Booth?