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 181     181 0 250 sub evall { my $global = shift; my $r = eval 'local *_;' . shift;
  181         15795  
5 181 50       744 $@ and die; $r }
  181         509  
6              
7             our $VERSION = '0.065';
8              
9 101     101   37728 use strict;
  101         122  
  101         3288  
10 101     101   399 use warnings;
  101         702  
  101         5769  
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   48430 bool => sub { 1 };
  101     74141   76911  
  101         1188  
  74141         165432  
18              
19 101     101   9906 use Scalar::Util qw'refaddr blessed';
  101         149  
  101         5705  
20 101     101   460 use List::Util 'first';
  101         116  
  101         5276  
21 101     101   433 use B 'svref_2object';
  101         110  
  101         236121  
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 274 my $str = shift;
36 231   100     1682 shift eq $str and return 1 while @_;
37 219         858 !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 19491     19491 1 22926 my($class, $global, $value) = @_;
138              
139 19491 100 100     52004 if (defined blessed $value
140             and can $value 'to_object') {
141 9         23 return to_object $value;
142             }
143            
144 19482         16575 my $p;
145             my %hash;
146 0         0 my %opts;
147              
148 19482 100       60790 ref $value eq 'HASH' and (%opts = %$value), $value = $opts{value};
149            
150 19482         20973 local $@;
151 19482 100 66     45170 if (!defined $value || !defined eval{$value->value} && $@ eq '') {
  7 50 66     53  
152 19479 100       48436 $p = exists $opts{prototype} ? $opts{prototype}
153             : $global->prototype_for("Object");
154             }
155             elsif(ref $value eq 'HASH') {
156 3         7 %hash = %$value;
157 3         10 $p = $global->prototype_for("Object");
158             }
159             else {
160 0         0 return $global->upgrade($value);
161             }
162              
163 19482         98683 my $self =
164             bless \{ prototype => $p,
165             global => $global,
166             props => \%hash,
167             keys => [keys %hash] }, $class;
168              
169 19482 50       35867 $JE::Destroyer && JE::Destroyer'register($self);
170              
171 19482         54178 $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 4160 my $self = shift;
193 707 50       1343 my $f = JE::Object::Function->new({
194             scope => $self->global,
195             function => pop,
196             function_args => ['args'],
197             @_ ? (name => $_[0]) : ()
198             });
199 707 50       3344 @_ and $self->prop({
200             name => shift,
201             value=>$f,
202             });
203 707         1815 $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 31 my $self = shift;
225 24 50       45 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       119 @_ and $self->prop({
232             name => shift,
233             value=>$f,
234             dontenum=>1
235             });
236 24         101 $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 164303     164303 1 228287 my ($self, $opts) = (shift, shift);
309 164303         212675 my $guts = $$self;
310              
311 164303 100       289389 if(ref $opts eq 'HASH') { # special use
312 34097         39813 my $name = $$opts{name};
313 34097         44303 for (qw< dontdel readonly >) {
314 68194 100       165804 exists $$opts{$_}
315             and $$guts{"prop_$_"}{$name} = $$opts{$_};
316             }
317              
318 34097         40163 my $props = $$guts{props};
319              
320 34097         26009 my $dontenum;
321 34097 100       55725 if(exists $$opts{dontenum}) {
    100          
322 27255 50       38422 if($$opts{dontenum}) {
323 27255         41320 @{$$guts{keys}} =
  27255         35931  
324 27255         23164 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 6474         5343 push @{ $$guts{keys} }, $name
  6474         11046  
333             }
334              
335 34097 100       58348 if(exists $$opts{fetch}) {
336 111         170 $$guts{fetch_handler}{$name} = $$opts{fetch};
337 111 50       251 $$props{$name} = undef if !exists $$props{$name};
338             }
339 34097 100       51730 if(exists $$opts{store}) {
340 104         166 $$guts{store_handler}{$name} = $$opts{store};
341 104 100       194 $$props{$name} = undef if !exists $$props{$name};
342             }
343 34097 100 100     54534 if(exists $$opts{value}) {
    100          
344 31393         97808 return $$props{$name} = $$opts{value};
345             }
346             elsif(!exists $$opts{fetch} && exists $$opts{autoload}) {
347 2051         2087 my $auto = $$opts{autoload};
348 2051 100       6994 $$props{$name} = ref $auto eq 'CODE' ? $auto :
349             "package " . caller() . "; $auto";
350             return # ~~~ Figure out what this should
351             # return, if anything
352 2051         4426 }
353              
354             # ~~~ what should we return if fetch is given,
355             # but not value?
356              
357 653 100       2940 return exists $$opts{fetch} ? () :
    100          
358             exists $$props{$name} ? $$props{$name} : undef;
359             }
360              
361             else { # normal use
362 130206         135465 my $name = $opts;
363 130206         162414 my $props = $$guts{props};
364 130206 100       322833 if (@_) { # we is doing a assignment
    100          
365 23506         27484 my($new_val) = shift;
366              
367 23506 100       37912 return $new_val if $self->is_readonly($name);
368              
369             # Make sure we don't change attributes if the
370             # property already exists
371 23384   100     79695 my $exists = exists $$props{$name} &&
372             defined $$props{$name};
373              
374 23384 100       50652 exists $$guts{store_handler}{$name}
375             ? $$guts{store_handler}{$name}->(
376             $self, $new_val, $$props{$name})
377             : ($$props{$name} = $new_val);
378              
379 23384 100       48333 push @{ $$guts{keys} }, $name
  2100         3412  
380             unless $exists;
381              
382 23384         78044 return $new_val;
383             }
384             elsif (exists $$props{$name}) {
385 99123 100       196482 if(exists $$guts{fetch_handler}{$name}) {
386 68         230 return $$guts{fetch_handler}{$name}-> (
387             $self, $$props{$name}
388             );
389             }
390              
391 99055         120828 my $val = $$props{$name};
392 99055 100 66     430065 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 99055         248835 return $val;
399             }
400             else {
401 7577         12262 my $proto = $self->prototype;
402 7577 100       16565 return $proto ?
403             $proto->prop($name) :
404             undef;
405             }
406             }
407              
408             }
409              
410              
411             sub exists { # = hasOwnProperty
412 100125     100125 0 108489 my($self,$name) = @_;
413 100125         415013 return exists $$$self{props}{$name}
414             }
415              
416              
417             sub is_readonly { # See JE::Types for a description of this.
418 26621     26621 0 31589 my ($self,$name) = (shift,@_); # leave $name in @_
419              
420 26621         28129 my $guts = $$self;
421              
422 26621         28549 my $props = $$guts{props};
423 26621 100       56841 if( exists $$props{$name}) {
424 21460         24791 my $read_only_list = $$guts{prop_readonly};
425 21460 100       74899 return exists $$read_only_list{$name} ?
426             $$read_only_list{$name} : !1;
427             }
428              
429 5161 100       6605 if(my $proto = $self->prototype) {
430 3097         5177 return $proto->is_readonly(@_);
431             }
432              
433 2064         6884 return !1;
434             }
435              
436              
437              
438              
439             sub is_enum {
440 231     231 0 293 my ($self, $name) = @_;
441 231         388 $self = $$self;
442 231         249 in_list $name, @{ $$self{keys} };
  231         694  
443             }
444              
445              
446              
447              
448             sub keys {
449 255     255 0 743 my $self = shift;
450 255         550 my $proto = $self->prototype;
451 255 100       237 @{ $$self->{keys} }, defined $proto ? $proto->keys : ();
  255         1389  
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 478 my ($self, $name) = @_;
474 291         468 my $guts = $$self;
475              
476 291 100       658 unless($_[2]) { # second arg means always delete
477 176         316 my $dontdel_list = $$guts{prop_dontdel};
478 176 100 66     1332 exists $$dontdel_list{$name} and $$dontdel_list{$name}
479             and return !1;
480             }
481            
482 145         329 delete $$guts{prop_dontdel }{$name};
483 145         218 delete $$guts{prop_dontenum}{$name};
484 145         232 delete $$guts{prop_readonly}{$name};
485 145         278 delete $$guts{props}{$name};
486 145         178 $$guts{keys} = [ grep $_ ne $name, @{$$guts{keys}} ];
  145         1165  
487 145         481 return 1;
488             }
489              
490              
491              
492              
493             sub method {
494 28     28 0 66 my($self,$method) = (shift,shift);
495              
496 28         59 $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 328 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 1753 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 145521     145521 0 314071 refaddr shift;
541             }
542              
543 1359     1359 0 4118 sub primitive { !1 };
544              
545             sub prototype {
546 15840 100   15840 0 21937 @_ > 1 ? (${+shift}->{prototype} = $_[1]) : ${+shift}->{prototype};
  469         3973  
  15371         33669  
547             }
548              
549              
550              
551              
552             sub to_primitive {
553 987     987 0 1434 my($self, $hint) = @_;
554              
555 987         1830 my @methods = ('valueOf','toString');
556 987 100 100     3985 defined $hint && $hint eq 'string' and @methods = reverse @methods;
557              
558 987         1041 my $method; my $prim;
559 987         1622 for (@methods) {
560 1327 100       2516 defined($method = $self->prop($_)) || next;
561 1315 100       3737 ($prim = $method->apply($self))->primitive || next;
562 973         3702 return $prim;
563             }
564              
565             die new JE::Object::Error::TypeError $self->global,
566             add_line_number "An object of type " .
567 8   33     17 (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 528 JE::Boolean->new( $${+shift}{global}, 1 );
  42         180  
576             }
577              
578             sub to_string {
579 326     326 0 7973 shift->to_primitive('string')->to_string;
580             }
581              
582              
583             sub to_number {
584 406     406 0 2021 shift->to_primitive('number')->to_number;
585             }
586              
587 1902     1902 0 6950 sub to_object { $_[0] }
588              
589 1644     1644 0 1432 sub global { ${+shift}->{global} }
  1644         6297  
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 106     106   187 my $proto = shift;
605 106         253 my $global = $$proto->{global};
606              
607             # E 15.2.4
608              
609 106         336 $proto->prop({
610             dontenum => 1,
611             name => 'constructor',
612             value => $global->prop('Object'),
613             });
614              
615             my $toString_sub = sub {
616 566     566   665 my $self = shift;
617 566         1502 JE::String->new($global,
618             '[object ' . $self->class . ']');
619 106         602 };
620              
621 106         1256 $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   30 function => sub { shift->method('toString') },
642 106         1216 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   996 function => sub { $_[0] },
655 106         1188 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   140 JE::Boolean->new($global,
669             shift->exists(
670             defined $_[0] ? $_[0] : 'undefined'
671             )
672             );
673             },
674 106         1327 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   27 my ($self, $obj) = @_;
688              
689 15 100 100     98 !defined $obj || $obj->primitive and return
690             JE::Boolean->new($global, 0);
691              
692 13         36 my $id = $self->id;
693 13         19 my $proto = $obj;
694              
695 13         27 while (defined($proto = $proto->prototype))
696             {
697 13 100       32 $proto->id eq $id and return
698             JE::Boolean->new($global, 1);
699             }
700              
701 1         6 return JE::Boolean->new($global, 0);
702             },
703 106         1642 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   964 return JE::Boolean->new($global,
717             shift->is_enum(
718             defined $_[0] ? $_[0] : 'undefined'
719             )
720             );
721             },
722 106         1392 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   3065 my $self = shift;
739 893         1012 my $guts = $$self;
740 893 100       1751 $$guts{tie} or tie %{ $$guts{tie} }, __PACKAGE__, $self;
  761         2691  
741 893         3679 $$guts{tie};
742             }
743              
744 764     764   1444 sub TIEHASH { $_[1] }
745 885     885   5036 sub FETCH { $_[0]->prop($_[1]) }
746             sub STORE {
747 735     735   1749 my($self, $key, $val) = @_;
748 735         1247 my $global = $self->global;
749 735 100 66     3720 if(ref $val eq 'HASH' && !blessed $val
    100 66        
      66        
      66        
      66        
      66        
750             && !%$val && svref_2object($val)->REFCNT == 2) {
751 3         14 $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         20 require JE::Object::Array;
756 3         18 $val = tie @$val, 'JE::Object::Array',
757             JE::Object::Array->new($global);
758             }
759 735         1855 $self->prop($key => $global->upgrade($val))
760             }
761             #sub CLEAR { }
762             # ~~~ have yet to implement this
763             sub DELETE {
764 18     18   57 my $val = $_[0]->prop($_[1]);
765 18         63 $_[0]->delete($_[1]);
766 18         52 $val;
767             }
768 12     12   40 sub EXISTS { $_[0]->exists($_[1]) }
769 11     11   41 sub FIRSTKEY { ($_[0]->keys)[0] }
770             sub NEXTKEY {
771 29     29   60 my @keys = $_[0]->keys;
772 29         36 my $last = $_[1];
773 29         48 for (0..$#keys) {
774 58 100       102 if ($last eq $keys[$_]) {
775 29         129 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