File Coverage

blib/lib/JE/Object.pm
Criterion Covered Total %
statement 197 209 94.2
branch 93 104 89.4
condition 38 50 76.0
subroutine 46 49 93.8
pod 8 26 30.7
total 382 438 87.2


line stmt bran cond sub pod time code
1             package JE::Object;
2              
3             # This has to come before any pragmas and sub declarations.
4 189     189 0 304 sub evall { my $global = shift; my $r = eval 'local *_;' . shift;
  189         18021  
5 189 50       894 $@ and die; $r }
  189         622  
6              
7             our $VERSION = '0.066';
8              
9 101     101   38702 use strict;
  101         149  
  101         3558  
10 101     101   449 use warnings;
  101         128  
  101         6565  
11              
12             use overload fallback => 1,
13             '%{}'=> \&_get_tie,
14             '""' => 'to_string',
15             '0+' => 'to_number',
16             # cmp => sub { "$_[0]" cmp $_[1] },
17 101     101   54262 bool => sub { 1 };
  101     74210   84765  
  101         1379  
  74210         192474  
18              
19 101     101   10475 use Scalar::Util qw'refaddr blessed';
  101         162  
  101         6745  
20 101     101   551 use List::Util 'first';
  101         125  
  101         5837  
21 101     101   506 use B 'svref_2object';
  101         148  
  101         261519  
22             #use Data::Dumper;
23              
24              
25             require JE::Code;
26             require JE::Object::Error::TypeError;
27             require JE::Object::Function;
28             require JE::Boolean;
29             require JE::String;
30              
31             import JE::Code 'add_line_number';
32             sub add_line_number;
33              
34             sub in_list {
35 231     231 0 431 my $str = shift;
36 231   100     2102 shift eq $str and return 1 while @_;
37 219         1251 !1;
38             }
39              
40              
41             =head1 NAME
42              
43             JE::Object - Base class for all JavaScript objects
44              
45             =head1 SYNOPSIS
46              
47             use JE;
48             use JE::Object;
49              
50             $j = new JE;
51              
52             $obj = new JE::Object $j;
53              
54             $obj->prop('property1', $new_value); # sets the property
55             $obj->prop('property1'); # returns $new_value;
56             $obj->{property1} = $new_value; # or use it as a hash
57             $obj->{property1}; # ref like this
58              
59             $obj->keys; # returns a list of the names of enumerable property
60             keys %$obj;
61              
62             $obj->delete('property_name');
63             delete $obj->{property_name};
64              
65             $obj->method('method_name', 'arg1', 'arg2');
66             # calls a method with the given arguments
67              
68             $obj->value ; # returns a value useful in Perl (a hashref)
69              
70             "$obj"; # "[object Object]" -- same as $obj->to_string->value
71             0+$obj"; # nan -- same as $obj->to_number->value
72             # etc.
73              
74             =head1 DESCRIPTION
75              
76             This module implements JavaScript objects for JE. It serves as a base
77             class
78             for all other JavaScript objects.
79              
80             A JavaScript object is an associative array, the elements of which are
81             its properties. A method is a property that happens to be an instance
82             of the
83             C class (C).
84              
85             JE::Object objects can be used in Perl as a number, string or boolean. The
86             result will be the same as in JavaScript. The C<%{}> (hashref) operator is
87             also overloaded and returns a hash that can be used to modify the object.
88             See L<"USING AN OBJECT AS A HASH">.
89              
90             See also L for descriptions of most of the methods. Only what
91             is specific to JE::Object is explained here.
92              
93             =head1 METHODS
94              
95             =over 4
96              
97             =item $obj = JE::Object->new( $global_obj )
98              
99             =item $obj = JE::Object->new( $global_obj, $value )
100              
101             =item $obj = JE::Object->new( $global_obj, \%options )
102              
103             This class method constructs and returns a new JavaScript object, unless
104             C<$value> is
105             already a JS object, in which case it just returns it. The behaviour is
106             the
107             same as the C constructor in JavaScript.
108              
109             The C<%options> are as follows:
110              
111             prototype the object to be used as the prototype for this
112             object (Object.prototype is the default)
113             value the value to be turned into an object
114              
115             C only applies when C is omitted, undef, undefined
116             or null.
117              
118             To convert a hash into an object, you can use the hash ref syntax like
119             this:
120              
121             new JE::Object $j, { value => \%hash }
122              
123             Though it may be easier to write:
124              
125             $j->upgrade(\%hash)
126              
127             The former is what C itself uses.
128              
129             =cut
130              
131             # ~~~ Perhaps I should eliminate the hash ref syntax and have new()
132             # check to see if $j->exists($class->class), and use that as the
133             # prototype. That would make the other constructors simpler, but would
134             # it make it harder to control JE and customise host objects?
135              
136             sub new {
137 19516     19516 1 28479 my($class, $global, $value) = @_;
138              
139 19516 100 100     62038 if (defined blessed $value
140             and can $value 'to_object') {
141 9         27 return to_object $value;
142             }
143            
144 19507         19930 my $p;
145             my %hash;
146 0         0 my %opts;
147              
148 19507 100       72317 ref $value eq 'HASH' and (%opts = %$value), $value = $opts{value};
149            
150 19507         23657 local $@;
151 19507 100 66     54025 if (!defined $value || !defined eval{$value->value} && $@ eq '') {
  7 50 66     63  
152 19504 100       60185 $p = exists $opts{prototype} ? $opts{prototype}
153             : $global->prototype_for("Object");
154             }
155             elsif(ref $value eq 'HASH') {
156 3         11 %hash = %$value;
157 3         11 $p = $global->prototype_for("Object");
158             }
159             else {
160 0         0 return $global->upgrade($value);
161             }
162              
163 19507         120965 my $self =
164             bless \{ prototype => $p,
165             global => $global,
166             props => \%hash,
167             keys => [keys %hash] }, $class;
168              
169 19507 50       39610 $JE::Destroyer && JE::Destroyer'register($self);
170              
171 19507         63688 $self;
172             }
173              
174             sub destroy { # not DESTROY; called by JE::Destroyer
175 0     0 0 0 undef ${$_[0]};
  0         0  
176             }
177              
178              
179             =item $obj->new_function($name, sub { ... })
180              
181             =item $obj->new_function(sub { ... })
182              
183             This creates and returns a new function object. If $name is given,
184             it will become a property of the object. The function is enumerable, like
185             C I in web browsers.
186              
187             For more ways to create functions, see L.
188              
189             =cut
190              
191             sub new_function {
192 707     707 1 4728 my $self = shift;
193 707 50       1478 my $f = JE::Object::Function->new({
194             scope => $self->global,
195             function => pop,
196             function_args => ['args'],
197             @_ ? (name => $_[0]) : ()
198             });
199 707 50       3923 @_ and $self->prop({
200             name => shift,
201             value=>$f,
202             });
203 707         2349 $f;
204             }
205              
206              
207              
208              
209             =item $obj->new_method($name, sub { ... })
210              
211             =item $obj->new_method(sub { ... })
212              
213             This is the same as C, except that the subroutine's first
214             argument will be the object with which the function is called, and that the
215             property created will not be enumerable. This allows one to add methods to
216             C, for instance, without making every for-in loop list
217             that method.
218              
219             For more ways to create functions, see L.
220              
221             =cut
222              
223             sub new_method {
224 24     24 1 25 my $self = shift;
225 24 50       42 my $f = JE::Object::Function->new({
226             scope => $self->global,
227             function => pop,
228             function_args => ['this','args'],
229             @_ ? (name => $_[0]) : ()
230             });
231 24 50       118 @_ and $self->prop({
232             name => shift,
233             value=>$f,
234             dontenum=>1
235             });
236 24         94 $f;
237             }
238              
239             =item $obj->prop( $name )
240              
241             =item $obj->prop( $name => $value )
242              
243             =item $obj->prop({ ... })
244              
245             See C for the first two uses.
246              
247             When the C method is called with a hash ref as its argument, the
248             prototype chain is I searched.
249             The elements of the hash are as follows:
250              
251             name property name
252             value new value
253             dontenum whether this property is unenumerable
254             dontdel whether this property is undeletable
255             readonly whether this property is read-only
256             fetch subroutine called when the property is fetched
257             store subroutine called when the property is set
258             autoload see below
259              
260             If C, C or C is given, the attribute in
261             question will be set.
262             If C is given, the value of the property will be set, regardless of
263             the attributes.
264              
265             C and C, if specified, must be subroutines for
266             fetching/setting the value of the property. The 'fetch' subroutine will be
267             called with ($object, $storage_space) as the arguments, where
268             C<$storage_space> is a hash key inside the object that the two subroutines
269             can use for storing the value (they can ignore it if they like). The
270             'store' subroutine will be call with
271             ($object, $new_value, $storage_space) as
272             the arguments. Values assigned to the storage space from within these
273             routines are I
274             upgraded, neither is the return value of C. C and C do
275             not necessarily have to go
276             together. If you only specify C, then the value will be set as
277             usual, but C will be able to mangle the value when it is retrieved.
278             Likewise, if you only specify C, the value will be retrieved the
279             usual way, so you can use this for validating or normalising the assigned
280             value, for
281             instance. B Currently, a simple scalar or unblessed coderef in the
282             storage space will cause autoloading, but that is subject to change.
283              
284             C can be a string or a coderef. It will be called/evalled the
285             first time the property is accessed (accessing it with a hash ref as
286             described here does not count). If it is a string, it will be
287             evaluated in the calling package (see warning below), in a scope that has a
288             variable named
289             C<$global> that refers to the global object. The result will become the
290             property's value. The value returned is not currently upgraded. The behaviour when a simple scalar or unblessed reference is returned is
291             undefined. C will be
292             ignored completely if C or C is also given. B The
293             'calling package' may not be what you think it is if a subclass overrides
294             C. It may be the subclass in such cases. To be on the safe side,
295             always begin the string of code with an explicit C statement. (If
296             anyone knows of a clean solution to this, please let the author know.)
297              
298             This hash ref calling convention does not work on Array
299             objects when the property name is C or an array index (a
300             non-negative integer
301             below
302             4294967295). It does not work on String objects if the
303             property name is C.
304              
305             =cut
306              
307             sub prop {
308 164421     164421 1 257977 my ($self, $opts) = (shift, shift);
309 164421         238995 my $guts = $$self;
310              
311 164421 100       319843 if(ref $opts eq 'HASH') { # special use
312 34195         47163 my $name = $$opts{name};
313 34195         50299 for (qw< dontdel readonly >) {
314 68390 100       189893 exists $$opts{$_}
315             and $$guts{"prop_$_"}{$name} = $$opts{$_};
316             }
317              
318 34195         46785 my $props = $$guts{props};
319              
320 34195         29047 my $dontenum;
321 34195 100       63457 if(exists $$opts{dontenum}) {
    100          
322 27331 50       42326 if($$opts{dontenum}) {
323 27331         47630 @{$$guts{keys}} =
  27331         40751  
324 27331         25208 grep $_ ne $name, @{$$guts{keys}};
325             }
326             else {
327 0     0   0 push @{ $$guts{keys} }, $name
  0         0  
328 0 0       0 unless first {$_ eq $name} @{$$guts{keys}};
  0         0  
329             }
330             }
331             elsif(!exists $$props{$name}) { # new property
332 6496         5955 push @{ $$guts{keys} }, $name
  6496         12607  
333             }
334              
335 34195 100       68902 if(exists $$opts{fetch}) {
336 111         180 $$guts{fetch_handler}{$name} = $$opts{fetch};
337 111 50       251 $$props{$name} = undef if !exists $$props{$name};
338             }
339 34195 100       59118 if(exists $$opts{store}) {
340 104         170 $$guts{store_handler}{$name} = $$opts{store};
341 104 100       201 $$props{$name} = undef if !exists $$props{$name};
342             }
343 34195 100 100     60852 if(exists $$opts{value}) {
    100          
344 31469         113253 return $$props{$name} = $$opts{value};
345             }
346             elsif(!exists $$opts{fetch} && exists $$opts{autoload}) {
347 2070         2469 my $auto = $$opts{autoload};
348 2070 100       8376 $$props{$name} = ref $auto eq 'CODE' ? $auto :
349             "package " . caller() . "; $auto";
350             return # ~~~ Figure out what this should
351             # return, if anything
352 2070         4722 }
353              
354             # ~~~ what should we return if fetch is given,
355             # but not value?
356              
357 656 100       3467 return exists $$opts{fetch} ? () :
    100          
358             exists $$props{$name} ? $$props{$name} : undef;
359             }
360              
361             else { # normal use
362 130226         148613 my $name = $opts;
363 130226         178717 my $props = $$guts{props};
364 130226 100       356556 if (@_) { # we is doing a assignment
    100          
365 23508         30428 my($new_val) = shift;
366              
367 23508 100       47235 return $new_val if $self->is_readonly($name);
368              
369             # Make sure we don't change attributes if the
370             # property already exists
371 23386   100     93878 my $exists = exists $$props{$name} &&
372             defined $$props{$name};
373              
374 23386 100       59981 exists $$guts{store_handler}{$name}
375             ? $$guts{store_handler}{$name}->(
376             $self, $new_val, $$props{$name})
377             : ($$props{$name} = $new_val);
378              
379 23386 100       53863 push @{ $$guts{keys} }, $name
  2101         4217  
380             unless $exists;
381              
382 23386         93767 return $new_val;
383             }
384             elsif (exists $$props{$name}) {
385 99141 100       228898 if(exists $$guts{fetch_handler}{$name}) {
386 68         269 return $$guts{fetch_handler}{$name}-> (
387             $self, $$props{$name}
388             );
389             }
390              
391 99073         145462 my $val = $$props{$name};
392 99073 100 66     490406 ref $val eq 'CODE' ?
393             $val = $$props{$name} = &$val() :
394             defined $val && ref $val eq '' &&
395             ($val = $$props{$name} =
396             evall $$guts{global}, $val
397             );
398 99073         293495 return $val;
399             }
400             else {
401 7577         15192 my $proto = $self->prototype;
402 7577 100       20155 return $proto ?
403             $proto->prop($name) :
404             undef;
405             }
406             }
407              
408             }
409              
410              
411             sub exists { # = hasOwnProperty
412 100125     100125 0 114211 my($self,$name) = @_;
413 100125         465690 return exists $$$self{props}{$name}
414             }
415              
416              
417             sub is_readonly { # See JE::Types for a description of this.
418 26624     26624 0 35558 my ($self,$name) = (shift,@_); # leave $name in @_
419              
420 26624         32489 my $guts = $$self;
421              
422 26624         31635 my $props = $$guts{props};
423 26624 100       69120 if( exists $$props{$name}) {
424 21461         31135 my $read_only_list = $$guts{prop_readonly};
425 21461 100       87112 return exists $$read_only_list{$name} ?
426             $$read_only_list{$name} : !1;
427             }
428              
429 5163 100       7816 if(my $proto = $self->prototype) {
430 3098         6329 return $proto->is_readonly(@_);
431             }
432              
433 2065         8294 return !1;
434             }
435              
436              
437              
438              
439             sub is_enum {
440 231     231 0 430 my ($self, $name) = @_;
441 231         557 $self = $$self;
442 231         320 in_list $name, @{ $$self{keys} };
  231         904  
443             }
444              
445              
446              
447              
448             sub keys {
449 255     255 0 649 my $self = shift;
450 255         640 my $proto = $self->prototype;
451 255 100       252 @{ $$self->{keys} }, defined $proto ? $proto->keys : ();
  255         1457  
452             }
453              
454              
455              
456              
457             =item $obj->delete($property_name, $even_if_it's_undeletable)
458              
459             Deletes the property named $name, if it is deletable. If the property did
460             not exist or it was deletable, then
461             true is returned. If the property exists and could not be deleted, false
462             is returned.
463              
464             If the second argument is given and is true, the property will be deleted
465             even if it is marked is undeletable. A subclass may override this,
466             however.
467             For instance, Array and String objects always have a 'length' property
468             which cannot be deleted.
469              
470             =cut
471              
472             sub delete {
473 291     291 1 609 my ($self, $name) = @_;
474 291         521 my $guts = $$self;
475              
476 291 100       800 unless($_[2]) { # second arg means always delete
477 176         405 my $dontdel_list = $$guts{prop_dontdel};
478 176 100 66     1837 exists $$dontdel_list{$name} and $$dontdel_list{$name}
479             and return !1;
480             }
481            
482 145         381 delete $$guts{prop_dontdel }{$name};
483 145         255 delete $$guts{prop_dontenum}{$name};
484 145         294 delete $$guts{prop_readonly}{$name};
485 145         328 delete $$guts{props}{$name};
486 145         189 $$guts{keys} = [ grep $_ ne $name, @{$$guts{keys}} ];
  145         1325  
487 145         490 return 1;
488             }
489              
490              
491              
492              
493             sub method {
494 28     28 0 80 my($self,$method) = (shift,shift);
495              
496 28         70 $self->prop($method)->apply($self, $self->global->upgrade(@_));
497             }
498              
499             =item $obj->typeof
500              
501             This returns the string 'object'.
502              
503             =cut
504              
505 112     112 1 361 sub typeof { 'object' }
506              
507              
508              
509              
510             =item $obj->class
511              
512             Returns the string 'Object'.
513              
514             =cut
515              
516 440     440 1 2057 sub class { 'Object' }
517              
518              
519              
520              
521             =item $obj->value
522              
523             This returns a hash ref of the object's enumerable properties. This is a
524             copy of the object's properties. Modifying it does not modify the object
525             itself.
526              
527             =cut
528              
529             sub value {
530 1     1 1 2 my $self = shift;
531 1         4 +{ map +($_ => $self->prop($_)), $self->keys };
532             }
533              
534             *TO_JSON=*value;
535              
536              
537              
538              
539             sub id {
540 145522     145522 0 355362 refaddr shift;
541             }
542              
543 1359     1359 0 4860 sub primitive { !1 };
544              
545             sub prototype {
546 15848 100   15848 0 26350 @_ > 1 ? (${+shift}->{prototype} = $_[1]) : ${+shift}->{prototype};
  475         4957  
  15373         40238  
547             }
548              
549              
550              
551              
552             sub to_primitive {
553 987     987 0 1644 my($self, $hint) = @_;
554              
555 987         2186 my @methods = ('valueOf','toString');
556 987 100 100     4650 defined $hint && $hint eq 'string' and @methods = reverse @methods;
557              
558 987         1173 my $method; my $prim;
559 987         1955 for (@methods) {
560 1327 100       3009 defined($method = $self->prop($_)) || next;
561 1315 100       4519 ($prim = $method->apply($self))->primitive || next;
562 973         4272 return $prim;
563             }
564              
565             die new JE::Object::Error::TypeError $self->global,
566             add_line_number "An object of type " .
567 8   33     25 (eval {$self->class} || ref $self) .
568             " cannot be converted to a primitive";
569             }
570              
571              
572              
573              
574             sub to_boolean {
575 42     42 0 532 JE::Boolean->new( $${+shift}{global}, 1 );
  42         214  
576             }
577              
578             sub to_string {
579 326     326 0 9495 shift->to_primitive('string')->to_string;
580             }
581              
582              
583             sub to_number {
584 406     406 0 2165 shift->to_primitive('number')->to_number;
585             }
586              
587 1902     1902 0 8469 sub to_object { $_[0] }
588              
589 1644     1644 0 1615 sub global { ${+shift}->{global} }
  1644         7462  
590              
591             =back
592              
593             =cut
594              
595              
596              
597              
598             #----------- PRIIVATE ROUTIES ---------------#
599              
600             # _init_proto takes the Object prototype (Object.prototype) as its sole
601             # arg and adds all the default properties thereto.
602              
603             sub _init_proto {
604 107     107   218 my $proto = shift;
605 107         273 my $global = $$proto->{global};
606              
607             # E 15.2.4
608              
609 107         396 $proto->prop({
610             dontenum => 1,
611             name => 'constructor',
612             value => $global->prop('Object'),
613             });
614              
615             my $toString_sub = sub {
616 566     566   874 my $self = shift;
617 566         1937 JE::String->new($global,
618             '[object ' . $self->class . ']');
619 107         670 };
620              
621 107         1459 $proto->prop({
622             name => 'toString',
623             value => JE::Object::Function->new({
624             scope => $global,
625             name => 'toString',
626             length => 0,
627             function_args => ['this'],
628             function => $toString_sub,
629             no_proto => 1,
630             }),
631             dontenum => 1,
632             });
633              
634             $proto->prop({
635             name => 'toLocaleString',
636             value => JE::Object::Function->new({
637             scope => $global,
638             name => 'toLocaleString',
639             length => 0,
640             function_args => ['this'],
641 7     7   34 function => sub { shift->method('toString') },
642 107         1369 no_proto => 1,
643             }),
644             dontenum => 1,
645             });
646              
647             $proto->prop({
648             name => 'valueOf',
649             value => JE::Object::Function->new({
650             scope => $global,
651             name => 'valueOf',
652             length => 0,
653             function_args => ['this'],
654 325     325   1148 function => sub { $_[0] },
655 107         1403 no_proto => 1,
656             }),
657             dontenum => 1,
658             });
659              
660             $proto->prop({
661             name => 'hasOwnProperty',
662             value => JE::Object::Function->new({
663             scope => $global,
664             name => 'hasOwnProperty',
665             argnames => ['V'],
666             function_args => ['this', 'args'],
667             function => sub {
668 24 100   24   198 JE::Boolean->new($global,
669             shift->exists(
670             defined $_[0] ? $_[0] : 'undefined'
671             )
672             );
673             },
674 107         1483 no_proto => 1,
675             }),
676             dontenum => 1,
677             });
678              
679             $proto->prop({
680             name => 'isPrototypeOf',
681             value => JE::Object::Function->new({
682             scope => $global,
683             name => 'isPrototypeOf',
684             argnames => ['V'],
685             function_args => ['this', 'args'],
686             function => sub {
687 15     15   31 my ($self, $obj) = @_;
688              
689 15 100 100     112 !defined $obj || $obj->primitive and return
690             JE::Boolean->new($global, 0);
691              
692 13         36 my $id = $self->id;
693 13         29 my $proto = $obj;
694              
695 13         37 while (defined($proto = $proto->prototype))
696             {
697 13 100       28 $proto->id eq $id and return
698             JE::Boolean->new($global, 1);
699             }
700              
701 1         7 return JE::Boolean->new($global, 0);
702             },
703 107         1767 no_proto => 1,
704             }),
705             dontenum => 1,
706             });
707              
708             $proto->prop({
709             name => 'propertyIsEnumerable',
710             value => JE::Object::Function->new({
711             scope => $global,
712             name => 'propertyIsEnumerable',
713             argnames => ['V'],
714             function_args => ['this', 'args'],
715             function => sub {
716 222 100   222   1466 return JE::Boolean->new($global,
717             shift->is_enum(
718             defined $_[0] ? $_[0] : 'undefined'
719             )
720             );
721             },
722 107         1469 no_proto => 1,
723             }),
724             dontenum => 1,
725             });
726             }
727              
728              
729              
730             #----------- TYING MAGIC ---------------#
731              
732             # I'm putting the object itself behind the tied hash, so that no new object
733             # has to be created.
734             # That means that tied %$obj returns $obj.
735              
736              
737             sub _get_tie {
738 893     893   3122 my $self = shift;
739 893         1115 my $guts = $$self;
740 893 100       2646 $$guts{tie} or tie %{ $$guts{tie} }, __PACKAGE__, $self;
  761         3156  
741 893         4153 $$guts{tie};
742             }
743              
744 764     764   1567 sub TIEHASH { $_[1] }
745 885     885   5039 sub FETCH { $_[0]->prop($_[1]) }
746             sub STORE {
747 735     735   1942 my($self, $key, $val) = @_;
748 735         1430 my $global = $self->global;
749 735 100 66     4179 if(ref $val eq 'HASH' && !blessed $val
    100 66        
      66        
      66        
      66        
      66        
750             && !%$val && svref_2object($val)->REFCNT == 2) {
751 3         15 $val = tie %$val, __PACKAGE__, __PACKAGE__->new(
752             $global);
753             } elsif (ref $val eq 'ARRAY' && !blessed $val && !@$val &&
754             svref_2object($val)->REFCNT == 2) {
755 3         23 require JE::Object::Array;
756 3         18 $val = tie @$val, 'JE::Object::Array',
757             JE::Object::Array->new($global);
758             }
759 735         2162 $self->prop($key => $global->upgrade($val))
760             }
761             #sub CLEAR { }
762             # ~~~ have yet to implement this
763             sub DELETE {
764 18     18   58 my $val = $_[0]->prop($_[1]);
765 18         54 $_[0]->delete($_[1]);
766 18         48 $val;
767             }
768 12     12   44 sub EXISTS { $_[0]->exists($_[1]) }
769 11     11   38 sub FIRSTKEY { ($_[0]->keys)[0] }
770             sub NEXTKEY {
771 29     29   53 my @keys = $_[0]->keys;
772 29         33 my $last = $_[1];
773 29         48 for (0..$#keys) {
774 58 100       123 if ($last eq $keys[$_]) {
775 29         136 return $keys[$_+1]
776             }
777             }
778              
779             # ~~~ What *should* we do if the property has been
780             # deleted?
781             # I think this means the iterator should have been reset (from the
782             # user's point of view), so we'll start from the beginning.
783              
784 0           return $keys[0];
785             }
786              
787 0     0 0   sub DDS_freeze { my $self = shift; delete $$$self{tie}; $self }
  0            
  0            
788              
789              
790             #----------- THE REST OF THE DOCUMENTATION ---------------#
791              
792             =head1 USING AN OBJECT AS A HASH
793              
794             Note first of all that C<\%$obj> is I the same as C<< $obj->value >>.
795             The C method creates a new hash containing just the enumerable
796             properties of the object and its prototypes. It's just a plain hash--no
797             ties, no magic. C<%$obj>, on the other hand, is another creature...
798              
799             C<%$obj> returns a magic hash which only lists enumerable properties
800             when you write C, but still provides access to the rest.
801              
802             Using C on this hash will check to see whether it is the object's
803             I property, and not a prototype's.
804              
805             Assignment to the hash itself currently
806             throws an error:
807              
808             %$obj = (); # no good!
809              
810             This is simply because I have not yet figured out what it should do. If
811             anyone has any ideas, please let me know.
812              
813             Autovivification works, so you can write
814              
815             $obj->{a}{b} = 3;
816              
817             and the 'a' element will be created if did not already exist. Note that,
818             if the property C exist but was undefined (from JS's point of view),
819             this throws an error.
820              
821             =begin paranoia
822              
823             One potential problem with this is that, when perl autovivifies in the
824             example
825             above, it first calls C and, when it sees that the result is not
826             defined, then calls C with C<{}> as the value. It then uses that
827             same hash that it passed to C, and does I make a second call to
828             C. This means that, for autovivification to work, the empty hash
829             that perl automatically assigns has to be tied to the new JE::Object that
830             is created. Now, the same sequence of calls to tie
831             handlers can be triggered by the following lines:
832              
833             my %h;
834             $obj->{a};
835             $h{b} = 3;
836              
837             And, of course, you don't want your %h hash transmogrified and tied to a
838             JE::Object, do you? (Normally
839             hashes and arrays are copied by STORE.) So the only feasible way (I can
840             think of) to
841             make the distinction is to use reference counts (which is what I'm using),
842             but I don't know whether they will change
843             between versions of Perl.
844              
845             =end paranoia
846              
847             =head1 INNARDS
848              
849             Each C instance is a blessed reference to a hash ref. The
850             contents of the hash
851             are as follows:
852              
853             $$self->{global} a reference to the global object
854             $$self->{props} a hash ref of properties, the values being
855             JavaScript objects
856             $$self->{prop_readonly} a hash ref with property names for the keys
857             and booleans (that indicate whether prop-
858             erties are read-only) for the values
859             $$self->{prop_dontdel} a hash ref in the same format as
860             prop_readonly that indicates whether proper-
861             ties are undeletable
862             $$self->{keys} an array of the names of enumerable
863             properties
864             $$self->{prototype} a reference to this object's prototype
865              
866             In derived classes, if you need to store extra information, begin the hash
867             keys with an underscore or use at least one capital letter in each key.
868             Such keys
869             will never be used by the
870             classes that come with the JE distribution.
871              
872             =head1 SEE ALSO
873              
874             L
875              
876             L
877              
878             =cut
879              
880              
881             1;
882